z3-4.4.0/0000755000175000017500000000000012540347414011645 5ustar michaelmichaelz3-4.4.0/RELEASE_NOTES0000644000175000017500000010255112540347414013624 0ustar michaelmichaelRELEASE NOTES Version 4.4.0 ============= - New feature: Support for the theory of floating-point numbers. This comes in the form of logics (QF_FP and QF_FPBV), tactics (qffp and qffpbv), as well as a theory plugin that allows theory combinations. Z3 supports the official SMT theory definition of FP (see http://smtlib.cs.uiowa.edu/theories/FloatingPoint.smt2) in SMT2 files, as well as all APIs. - New feature: Stochastic local search engine for bit-vector formulas (see the qfbv-sls tactic). See also: Froehlich, Biere, Wintersteiger, Hamadi: Stochastic Local Search for Satisfiability Modulo Theories, AAAI 2015. - Upgrade: This release includes a brand new OCaml/ML API that is much better integrated with the build system, and hopefully also easier to use than the previous one. - Fixed various bugs reported by Marc Brockschmidt, Venkatesh-Prasad Ranganath, Enric Carbonell, Morgan Deters, Tom Ball, Malte Schwerhoff, Amir Ebrahimi, Codeplex users rsas, clockish, Heizmann, susmitj, steimann, and Stackoverflow users user297886. Version 4.3.2 ============= - Added preliminary support for the theory of floating point numbers (tactics qffpa, qffpabv, and logics QF_FPA, QF_FPABV). - Added the interpolation features of iZ3, which are now integrated into Z3. - Fixed a multitude of bugs and inconsistencies that were reported to us either in person, by email, or on Codeplex. Of those that we do have records of, we would like to express our gratitude to: Vladimir Klebanov, Konrad Jamrozik, Nuno Lopes, Carsten Ruetz, Esteban Pavese, Tomer Weiss, Ilya Mironov, Gabriele Paganelli, Levent Erkok, Fabian Emmes, David Cok, Etienne Kneuss, Arlen Cox, Matt Lewis, Carsten Otto, Paul Jackson, David Monniaux, Markus Rabe, Martin Pluecker, Jasmin Blanchette, Jules Villard, Andrew Gacek, George Karpenkov, Joerg Pfaehler, and Pablo Aledo as well as the following Codeplex users that either reported bugs or took part in discussions: xor88, parno, gario, Bauna, GManNickG, hanwentao, dinu09, fhowar, Cici, chinissai, barak_cohen, tvalentyn, krikunts, sukyoung, daramos, snedunuri, rajtendulkar, sonertari, nick8325, dvitek, amdragon, Beatgodes, dmonniaux, nickolai, DameNingen, mangpo, ttsiodras, blurium, sbrickey, pcodemod, indranilsaha, apanda, hougaardj, yoff, EfForEffort, Ansotegui, scottgw, viorelpreoteasa, idudka, c2855337, gario, jnfoster, omarmrivas, switicus, vosandi, foens, yzwwf, Heizmann, znajem, ilyagri, hougaardj, cliguda, rgrig, 92c849c1ccc707173, edmcman, cipher1024, MichaelvW, hellok, n00b42, ic3guy, Adorf, tvcsantos, zilongwang, Elarnon, immspw, jbridge99, danliew, zverlov, petross, jmh93, dradorf, fniksic, Heyji, cxcfan, henningg, wxlfrank, rvprasad, MovGP0, jackie1015, cowang, ffaghih, sanpra1989, gzchenyin, baitman, xjtulixiangyang, andreis, trucnguyenlam, erizzi, hanhchi, qsp, windypan, vadave, gradanne, SamWot, gsingh93, manjeetdahiya, zverlov, RaLa, and regehr. - New parameter setting infrastructure. Now, it is possible to set parameter for Z3 internal modules. Several parameter names changed. Execute `z3 -p` for the new parameter list. - Added get_version() and get_version_string() to Z3Py - Added support for FreeBSD. Z3 can be compiled on FreeBSD using g++. - Added support for Python 3.x. - Reverted to `(set-option :global-decls false)` as the default. In Z3 4.3.0 and Z3 4.3.1, this option was set to true. Thanks to Julien Henry for reporting this problem. - Added `doc` directory and scripts for automatically generating the API documentation. - Removed 'autoconf' dependency. We do not need to execute 'autoconf' and './configure' anymore to build Z3. - Fixed incorrect result returned by Z3_solver_get_num_scopes. (Thanks to Herman Venter). This bug was introduced in Z3 4.3.0 - Java bindings. To enable them, we must use the option `--java` when executing the `mk_make.py` script. Example: `python scripts/mk_make.py --java` - Fixed crash when parsing incorrect formulas. The crash was introduced when support for "arithmetic coercions" was added in Z3 4.3.0. - Added new option to mk_make to allow users to specify where python bindings (Z3Py) will be installed. (Thanks to Dejan Jovanovic for reporting the problem). - Fixed crash reported at http://z3.codeplex.com/workitem/10 - Removed auxiliary constants created by the nnf tactic from Z3 models. - Fixed problem in the pretty printer. It was not introducing quotes for attribute names such as |foo:10|. - Fixed bug when using assumptions (Thanks to Philippe Suter and Etienne Kneuss) Consider the following example: (assert F) (check-sat a) (check-sat) If 'F' is unstatisfiable independently of the assumption 'a', and the inconsistenty can be detected by just performing propagation, Then, version <= 4.3.1 may return unsat sat instead of unsat unsat We say may because 'F' may have other unsatisfiable cores. - Fixed bug reported at http://stackoverflow.com/questions/13923316/unprintable-solver-model - Fixed timers on Linux and FreeBSD. - Fixed crash reported at http://z3.codeplex.com/workitem/11. - Fixed bug reported at http://stackoverflow.com/questions/14307692/unknown-when-using-defs - Relax check_logic procedure. Now, it accepts coercions (to_real) automatically introduced by Z3. (Thanks to Paul Jackson). This is a fix for http://z3.codeplex.com/workitem/19. - Fixed http://stackoverflow.com/questions/14524316/z3-4-3-get-complete-model. - Fixed bugs in the C++ API (Thanks to Andrey Kupriyanov). - Fixed bug reported at http://z3.codeplex.com/workitem/23 (Thanks to Paul Jackson). - Fixed bug reported at http://stackoverflow.com/questions/15226944/segmentation-fault-in-z3 (Thanks to Tianhai Liu). Version 4.3.1 ============= - Added support for compiling Z3 using clang++ on Linux and OSX - Added missing compilation option (-D _EXTERNAL_RELEASE) in release mode. Version 4.3.0 ============= - Fixed bug during model construction reported by Heizmann (http://z3.codeplex.com/workitem/5) - Remark: We skipped version 4.2 due to a mistake when releasing 4.1.2. Version 4.1.2 was accidentally tagged as 4.2. Thanks to Claude Marche for reporting this issue. From now on, we are also officially moving to a 3 number naming convention for version numbers. The idea is to have more frequent releases containing bug fixes. - The Z3 codebase was reorganized, we also have a new build system. In all platforms, we need Python 2.7.x installed. On Windows, you can build using Visual Studio Command Prompt. On Linux, OSX, Cygwin, you can build using g++. See README for compilation instructions. - Removed tactic mip. It was based on code that was deleted during the code reorganization. - Remark: We skipped version 4.2 due to a mistake when releasing 4.1.2. It accidentatly - Fixed compilation problems with clang/llvm. Many thanks to Xi Wang for finding the problem, and suggesting the fix. - Now, Z3 automatically adds arithmetic coercions: to_real and to_int. Option (set-option :int-real-coercions false) disables this feature. If SMTLIB2_COMPLIANT=true in the command line, then :int-real-coercions is also set to false. - SMTLIB2_COMPLIANT is false by default. Use command line option SMTLIB2_COMPLIANT=true to enable it back. - Added "make install" and "make uninstall" to Makefile.in. - Added "make install-z3py" and "make uninstall-z3py" to Makefile.in. - Fixed crash/bug in the simplifier. The crash occurred when option ":sort-sums true" was used. - Added "--with-python=" option to configure script. - Cleanned c++, maxsat, test_mapi examples. - Move RELEASE_NOTES files to source code distribution. - Removed unnecessary files from source code distribution. - Removed unnecessary compilation modes from z3-prover.sln. - Added Xor procedure to Z3Py. - Z3 by default switches to an incremental solver when a Solver object is used to solve many queries. In the this version, we switch back to the tactic framework if the incremental solver returns "unknown". - Allow negative numerals in the SMT 2.0 frontend. That is, Z3 SMT 2.0 parser now accepts numerals such as "-2". It is not needed to encode them as "(- 2)" anymore. The parser still accepts -foo as a symbol. That is, it is *not* a shorthand for (- foo). This feature is disabled when SMTLIB2_COMPLIANT=true is set in the command line. - Now, Z3 can be compiled inside cygwin using gcc. - Fixed bug in the unsat core generation. First source code release (October 2, 2012) =========================================== - Fixed bug in Z3Py. The method that builds Z3 applications could crash if one of the arguments have to be "casted" into the correct sort (Thanks to Dennis Yurichev). - Fixed bug in datatype theory (Thanks to Ayrat). - Fixed bug in the definition of MkEmptySet and MkFullSet in the .Net API. - Display warning message and ignore option CASE_SPLIT=3,4 or 5 when auto configuration is enabled (AUTO_CONFIG=true) (Thanks Tobias from StackOverflow). - Made the predicates <, <=, > and >= chainable as defined in the SMT 2.0 standard (Thanks to Matthias Weiler). - Added missing Z3_decl_kind's for datatypes: Z3_OP_DT_CONSTRUCTOR, Z3_OP_DT_ACCESSOR, Z3_OP_DT_RECOGNISER. - Added support for numbers in scientific notation at Z3_ast Z3_mk_numeral(__in Z3_context c, __in Z3_string numeral, __in Z3_sort ty). - New builtin symbols in the arithmetic theory: pi, euler, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh. The first two are constants, and the others are unary functions. These symbols are not available if the a SMT 2.0 logic is specified (e.g., QF_LRA, QF_NRA, QF_LIA, etc) because these symbols are not defined in these logics. That is, the new symbols are only available if the logic is not specified. Version 4.1 =========== - New OCAML API (Many thanks to Josh Berdine) - CodeContracts in the .NET API (Many thanks to Francesco Logozzo). Users can now check whether they are using the .NET API correctly using Clousot. - Added option :error-behavior. The default value is continued-execution. Now, users can force the Z3 SMT 2.0 frontend to exit whenever an error is reported. They just have to use the command (set-option :error-behavior immediate-exit). - Fixed bug in term-if-then-else elimination (Thanks to Artur Niewiadomski). - Fixed bug in difference logic detection code (Thanks to Dejan Jovanovic). - Fixed bug in the pseudo-boolean preprocessor (Thanks to Adrien Champion). - Fixed bug in bvsmod preprocessing rules (Thanks to Dejan Jovanovic). - Fixed bug in Tactic tseitin-cnf (Thanks to Georg Hofferek). - Added missing simplification step in nlsat. - Fixed bug in model construction for linear real arithmetic (Thanks to Marcello Bersani). - Fixed bug in preprocessor that eliminated rational powers (e.g., (^ x (/ 1.0 2.0))), the bug affected only problems where the denominator was even (Thanks to Johannes Eriksson). - Fixed bug in the k-th root operation in the algebraic number package. The result was correct, but the resulting polynomial could be incorrectly tagged as minimal and trigger nontermination on comparison operations. (Thanks to Johannes Eriksson). - Fixed bug affecting problems containing patterns with n-ary arithmetic terms such as (p (+ x y 2)). This bug was introduced in Z3 4.0. (Thanks to Paul Jackson). - Fixed crash when running out of memory. - Fixed crash reported by Alex Summers. The crash was happening on scripts that contain quantifiers, and use boolean formulas inside terms. - Fixed crash in the MBQI module (Thanks to Stephan Falke). - Fixed bug in the E-matching engine. It was missing instances of multi-patterns (Thanks Alex Summers). - Fixed bug in Z3Py pretty printer. - The pattern inference module does not generate warning messages by default anymore. This module was responsible for producing messages such as: "WARNING: failed to find a pattern for quantifier (quantifier id: k!199)". The option PI_WARNINGS=true can be used to enable these warning messages. - Added missing return statements in z3++.h (Thanks to Daniel Neider). - Removed support for TPTP5 and Simplify input formats. - Removed support for Z3 (low-level) input format. It is still available in the API. - Removed support for "SMT 1.5" input format (aka .smtc files). This was a hybrid input format that was implemented while the SMT 2.0 standard was being designed. Users should move to SMT 2.0 format. Note that SMT 1.0 format is still available. - Made tseitin-cnf tactic more "user friendly". It automatically applies required transformations needed to eliminate operators such as: and, distinct, etc. - Implemented new PSC (principal subresultant coefficient) algorithm. This was one of the bottlenecks in the new nlsat solver/tactic. Version 4.0 =========== Z3 4.0 is a major release. The main new features are: - New C API, and it is backwards compatible, but several methods are marked as deprecated. In the new API, many solvers can be created in the same context. It also includes support for user defined strategies using Tactics. It also exposes a new interface for browsing models. - A thin C++ layer around the C API that illustrates how to leverage reference counting of ast objects. Several examples can be found in the directory 'examples/c++'. - New .NET API together with updated version of the legacy .NET API. The new .NET API supports the new features, Tactics, Solvers, Goals, and integration of with reference counting. Terms and sorts life-times no longer requires a scoping discipline. - Z3Py: Python interface for Z3. It covers all main features in the Z3 API. - NLSAT solver for nonlinear arithmetic. - The PDR algorithm in muZ. - iZ3: an interpolating theorem prover built on top of Z3 (\ref iz3documentation). iZ3 is only available for Windows and Linux. - New logging infrastructure. Z3 logs are used to record every Z3 API call performed by your application. If you find a bug, just the log need to be sent to the Z3 team. The following APIs were removed: Z3_trace_to_file, Z3_trace_to_stderr, Z3_trace_to_stdout, Z3_trace_off. The APIs: Z3_open_log, Z3_append_log and Z3_close_log do not receive a Z3_context anymore. When creating a log, you must invoke Z3_open_log before any other Z3 function call. The new logs are much more precise. However, they still have two limitations. They are not useful for logging applications that use callbacks (e.g., theory plugins) because the log interpreter does not have access to these callbacks. They are not precise for applications that are using multiple threads for processing multiple Z3 contexts. - Z3 (for Linux and OSX) does not depend on GMP anymore. - Z3 1.x backwards compatibility macros are defined in z3_v1.h. If you still use them, you have to explicitly include this file. - Fixed all bugs reported at Stackoverflow. Temporarily disabled features: - User theories cannot be used with the new Solver API yet. Users may still use them with the deprecated solver API. - Parallel Z3 is also disabled in this release. However, we have parallel combinators for creating stragegies (See tutorial). The two features above will return in future releases. Here is a list of all deprecated functions. Version 3.2 =========== This is a bug-fix refresh that fixes reported problems with 3.1. - Added support for chainable and right associative attributes. - Fixed model generation for QBVF (aka UFBV) logic. Now, Z3 officially supports the logics BV and UFBV. These are essentially QF_BV and QF_UFBV with quantifiers. - Fixed bug in eval and get-value commands. Thanks to Levent Erkok. - Fixed performance bug that was affecting VCC and Slayer. Thanks to Michal Moskal. - Fixed time measurement on Linux. Thanks to Ayrat Khalimov. - Fixed bug in destructive equality resolution (DER=true). - Fixed bug in map operator in the theory of arrays. Thanks to Shaz Quadeer. - Improved OCaml build scripts for Windows. Thanks to Josh Berdine. - Fixed crash in MBQI (when Real variables were used). - Fixed bugs in quantifier elimination. Thanks to Josh Berdine. - Fixed crash when an invalid datatype declaration is used. - Fixed bug in the SMT2 parser. - Fixed crash in quick checker for quantified formulas. Thanks to Swen Jacobs. - Fixed bug in the bvsmod simplifier. Thanks to Trevor Hansen. - New APIs: \c Z3_substitute and \c Z3_substitute_vars. - Fixed crash in MBQI. Thanks to Dejan Jovanovic. Version 3.1 =========== This is a bug-fix refresh that fixes reported problems with 3.0. - Fixed a bug in model generation. Thanks to Arlen Cox and Gordon Fraser. - Fixed a bug in Z3_check_assumptions that prevented it from being used between satisfiable instances. Thanks to Krystof Hoder. - Fixed two bugs in quantifier elimination. Thanks to Josh Berdine. - Fixed bugs in the preprocessor. - Fixed performance bug in MBQI. Thanks to Kathryn Stolee. - Improved strategy for QBVF (aka UFBV) logic. - Added support for negative assumptions in the check-sat command. Version 3.0 =========== - Fully compliant SMT-LIB 2.0 (SMT2) front-end. The old front-end is still available (command line option -smtc). The Z3 Guide describes the new front-end. - Parametric inductive datatypes, and parametric user defined types. - New SAT solver. Z3 can also read dimacs input formulas. - New Bitvector (QF_BV) solver. The new solver is only available when using the new SMT2 front-end. - Major performace improvements. - New preprocessing stack. - Performance improvements for linear and nonlinear arithmetic. The improvements are only available when using the the SMT2 front-end. - Added API for parsing SMT2 files. - Fixed bug in AUTO_CONFIG=true. Thanks to Alberto Griggio. - Fixed bug in the Z3 simplifier cache. It was not being reset during backtracking. Thanks to Alberto Griggio. - Fixed many other bugs reported by users. - Improved model-based quantifier instantiation (MBQI). - New solver for Quantified Bitvector Logic (QBVF). - Z3 checks the user specified logic. - TPTP 5 front-end. Version 2.19 ============ - In the SMT-LIB 1.0 frontend, Z3 will only display the model when requested by the user (MODEL=true). - Fixed bug in the variable elimination preprocessor. Thanks to Alberto Griggio. - Fixed bug in the expression strong simplifier. Thanks to Marko. - Fixed bug in the Z3 auto configuration mode. Thanks to Vladimir Klebanov. - Fixed bug when model generation is used in the context of user-defined-theories. Thanks to Philippe Suter. - Fixed bug in quantifier elimination procedure. Thanks to Mikkel Larsen Pedersen. - Improved speed of Z3 lexer for SMT-LIB frontend. - Added a sample under examples/fixedpoints to illustrate using the API for pluggable relations. - Added an API method \c Z3_get_param_value for retrieving a configuration value given a configuration parameter name. Version 2.18 ============ - Z3 has a new mode for solving fixed-point queries. It allows formulating Datalogish queries combined with constraints. Try it online. - Fixed bug that affects the array theory over the API using RELEVANCY=0. Thanks to Josh Berdine. Version 2.17 ============ - Z3 has new model finding capabilities for Quantified SMT formulas. The new features are enabled with MBQI=true. (Model Based Quantifier Instantiation). MBQI implements a counter-example based refinement loop, where candidate models are built and checked. When the model checking step fails, it creates new quantifier instantiations. The models are returned as simple functional programs. The new feature is also a decision procedure for many known decidable fragments such as: EPR (Effectively Propositional), Bradley&Manna&Sipma's Array Property Fragment (VMCAI'06), Almost Uninterpreted Fragment (Complete instantiation for quantified SMT formulas, CAV'09), McPeak&Necula's list fragment (CAV'05), QBVF (Quantified Bit-Vector Formulas FMCAD'10), to cite a few. MBQI is useful for checking the consistency of background axiomatizations, synthesizing functions, and building real counterexamples for verification tools. Users can constrain the search space by providing templates for function symbols, and constraints on the size of the universe and range of functions. - Fixed bug in the command (simplify [expr]) SMT-LIB 2.0 frontend. - New model pretty printer. The old style is still available (option MODEL_V2=true). Z3 1.x style is also available (option MODEL_V1=true). - Removed \c ARRAY_PROPERTY option. It is subsumed by MBQI=true. - Z3 uses the (set-logic [name]) to configure itself. - Assumptions can be provided to the \c check-sat command. The command (check-sat [assumptions]) checks the satisfiability of the logical context modulo the given set of assumptions. The assumptions must be Boolean constants or the negation of Boolean constants. When the logical context is unsatisfiable modulo the given assumptions, Z3 will display a subset of the \c assumptions that contributed to the conflict. Lemmas learned during the execution of \c check-sat are preserved. - Added command (echo [string]) to the SMT-LIB 2.0 frontend. - Z3 models explicitly include an interpretation for uninterpreted sorts. The interpretation is presented using the \c define-sort primitive. For example, \code (define-sort S e_1 ... e_n) \endcode states that the interpretation of the uninterpreted sort S is finite, and its universe is composed by values \c e_1, ..., \c e_n. - Options \c WARNING and \c VERBOSE can be set in the SMT-LIB 2.0 frontend using the commands (set-option WARNING ) (set-option VERBOSE ). - Fixed unintentional side-effects in the Z3 pretty printer. Thanks to Swen Jacobs. - Added interpreted constants of the form as-array[f]. The constants are used in models produced by Z3 to encode the interpretation of arrays. The following axiom scheme axiomatizes the new constants: \code (forall (x1 S1) ... (xn Sn) (= (select as-array[f] x1 ... xn) (f x1 ... xn))) \endcode - Fixed bug in the option MACRO_FINDER=true. - Fixed bug in the (eval [expr]) command in the SMT-LIB 2.0 frontend. - Soundness bug in solver for array property fragment. Thanks to Trevor Hansen. Version 2.16 ============ The following bugs are fixed in this release: - Bugs in quantifier elimination. Thanks to Mikkel Larsen Pedersen. - Crash in non-linear arithmetic. Thanks to Trevor Hansen. - Unsoundness in mixed integer-linear version using to_real. Thanks to Hirai. - A crash and bugs in check_assumptions feature. Thanks to Akash Lal and Shaz Qadeer. Version 2.15 ============ The following bugs are fixed in this release: - A bug in the quantifier elimination that affects nested alternating quantifiers that cannot be fully eliminated. - A crash in proof generation. Thanks to Sascha Boehme. Version 2.14 ============ The following bugs are fixed in this release: - A crash in arithmetic simplification. Thanks to Trevor Hansen. - An unsoundness bug in the quantifier elimination. It affects the equivalence of answers that are computed in some cases. - Incorrect printing of parameters and other values in SMT-LIB2 mode. Thanks to Tjark Weber. Version 2.13 ============ The following bugs are fixed in this release: - Soundness bug in solver for array property fragment. Thanks to Trevor Hansen. - Soundness bug introduced in macro expansion utilities. Thanks to Wintersteiger. - Incorrect handling of QF_NRA. Thanks to Trevor Hansen. - Mixup between SMT2 and SMT1 pretty printing formats. Thanks to Alvin Cheung and Tjark Weber. Version 2.12 ============ News: - Philippe Suter made a JNI binding available. There is also an existing Python binding by Sascha Boehme. See \ref contrib. The following features are added in this release: - Enable check_assumptions without enclosing push/pop. This resolves the limitation described in \ref sub_release_limitations_2_0. - Expose coefficients used in arithmetical proofs. - Allow quantified theory axioms. The following bugs are fixed in this release: - Fixes to the SMT-LIB 2.0 pretty printing mode. - Detect miss-annotated SMT-LIB benchmarks to avoid crashes when using the wrong solvers. Thanks to Trevor Hansen. - A digression in the managed API from 2.10 when passing null parameters. - Crash/incorrect handling of inequalities over the reals during quantifier elimination. Thanks to Mikkel Larsen Pedersen. - Bug in destructive equality resolution. Thanks to Sascha Boehme. - Bug in initialization for x64_mt executable on SMT benchmarks. Thanks to Alvin Cheung. Version 2.11 ============ The following features are added in this release: - SMT-LIB 2.0 parsing support for (! ..) in quantifiers and (_ ..). - Allow passing strings to function and sort declarations in the .NET Theory builders. - Add a parameter to the proof construct for theory lemmas to indicate which theory provided the lemma. - More detailed proof production in rewrite steps. The following bugs are fixed in this release: - A bug in BV propagation. Thanks to Trevor Hansen. Version 2.10 ============ The following bugs are fixed in this release: - Inconsistent printing of integer and real types from the low level and SMT-LIB pretty printers. Thanks to Sascha Boehme. - Missing relevancy propagation and memory smash in user-theory plugins. Thanks to Stan Rosenberg. Version 2.9 =========== The following bugs are fixed in this release: - Incorrect constant folding of extraction for large bit-vectors. Thanks to Alvin. - Z3 crashed when using patterns that are variables. Thanks to Michael Emmi. - Unsound array property fragment handling of non-integer types. Thanks to Juergen Christ. - The quantifier elimination procedure for data-types has been replaced. Thanks to Josh Berdine. - Refresh 2.9.1: Add missing AssumeEq to the .NET managed API. Thanks to Stan Rosenberg. Version 2.8 =========== The following features have been added: - User theories: The user can add theory solvers that get invoked by Z3's core during search. See also \ref theory_plugin_ex. - SMT2 features: parse smt2 let bindings. The following bugs are fixed in this release: - Incorrect semantics of constant folding for (bvsmod 0 x), where x is positive, incorrect constant folding for bvsdiv, incorrect simplification of bvnor, bvnand, incorrect compilation of bvshl when using a shift amount that evaluates to the length of the bit-vector. Thanks to Trevor Hansen and Robert Brummayer. - Incorrect NNF conversion in linear quantifier elimniation routines. Thanks to Josh Berdine. - Missing constant folding of extraction for large bit-vectors. Thanks to Alvin. - Missing APIs for bvredand and bvredor. Version 2.7 =========== The following features have been added: - Partial support for SMT-LIB 2.0 format: Added declare-fun, define-fun, declare-sort, define-sort, get-value - Added coercion function to_int and testing function is_int. To coerce from reals to integers and to test whether a real is an integer. The function to_real was already supported. - Added Z3_repeat to create the repetition of bit-vectors. The following bugs are fixed in this release: - Incorrect semantics of constant folding for bvsmod. - Incorrect semantics of constant folding for div/mod. Thanks to Sascha Boehme. - Non-termination problem associated with option LOOKAHEAD=true. It gets set for QF_UF in auto-configuration mode. Thanks to Pierre-Christophe Bué. - Incorrect axioms created for injective functions. Thanks to Sascha Boehme. - Stack overflow during simplification of large nested bit-vector terms. Thanks to David Molnar. - Crash in unsat-core generation when enabling SOLVER=true. Thanks to Lucas Cordeiro. - Unlimited cache growth while simplifying bit-vectors. Thanks to Eric Landers. - Crash when solving array property formulas using non-standard array operators. Thanks to Sascha Boehme. Version 2.6 =========== This release fixes a few bugs. Thanks to Marko Kääramees for reporting a bug in the strong context simplifier and to Josh Berdine. This release also introduces some new preprocessing features: - More efficient destructive equality resolution DER=true. - DISTRIBUTE_FORALL=true (distributes universal quatifiers over conjunctions, this transformation may affect pattern inference). - Rewriter that uses universally quantified equations PRE_DEMODULATOR=true (yes, the option name is not good, we will change it in a future release). - REDUCE_ARGS=true (this transformation is essentially a partial ackermannization for functions where a particular argument is always an interpreted value). - Better support for macro detection (a macro is a universally quantified formula of the form Forall X. F(X) = T[X]). We also change the option name, now it is called MACRO_FINDER=true. - ELIM_QUANTIFIERS=true enables quantifier elimination methods. Previous variants called QUANT_ARITH are deprecated. Version 2.5 =========== This release introduces the following features: - STRONG_CONTEXT_SIMPLIFIER=true allows simplifying sub-formulas to true/false depending on context-dependent information. The approach that we use is described on the Microsoft Z3 forum. - Some parameter values can be updated over the API. This functionality is called Z3_update_param_value in the C API. This is particularly useful for turning the strong context simplifier on and off. It also fixes bugs reported by Enric Rodríguez Carbonell, Nuno Lopes, Josh Berdine, Ethan Jackson, Rob Quigley and Lucas Cordeiro. Version 2.4 =========== This release introduces the following features: - Labeled literals for the SMT-LIB format. The Simplify format has supported labeled formulas to simplify displaying counter-examples. Section \ref smtlib_labels explains how labels are now supported in the SMT-LIB format. - Preliminary support for SMT-LIB2 It fixes the following bugs: - Bug in non-linear arithmetic routines. - Crash observed a class of modular integer arithmetic formulas. - Incomplete saturation leading to incorrectly sat labeling. - Crash in the bit-vector procedure when using int2bv and bv2int. Thanks to Michal Moskal, Sascha Boehme and Ethan Jackson. Version 2.3 =========== This release introduces the following features: - F# Quotation utilities. The release contains a new directory 'utils'. It contains utilities built on top of Z3. The main one is support for translating F# quoted expressions into Z3 formulas. - QUANT_ARITH configuration. Complete quantifier-elimination simplification for linear real and linear integer arithmetic. QUANT_ARITH=1 uses Ferrante/Rackhoff for reals and Cooper's method for integers. QUANT_ARITH=2 uses Fourier-Motzkin for reals and the Omega test for integers. It fixes the following bugs: - Incorrect simplification of map over store in the extendted array theory. Reported by Catalin Hritcu. - Incomplete handling of equality propagation with constant arrays. Reported by Catalin Hritcu. - Crash in bit-vector theory. - Incorrectness in proof reconstruction for quantifier manipulation. Thanks to Catalin Hritcu, Nikolai Tillmann and Sascha Boehme. Version 2.2 =========== This release fixes minor bugs. It introduces some additional features in the SMT-LIB front-end to make it easier to parse new operators in the theory of arrays. These are described in \ref smtlibext. Version 2.1 =========== This is a bug fix release. Many thanks to Robert Brummayer, Carine Pascal, François Remy, Rajesh K Karmani, Roberto Lublinerman and numerous others for their feedback and bug reports. Version 2.0 =========== - Parallel Z3. Thanks to Christoph Wintersteiger there is a binary supporting running multiple instances of Z3 from different threads, but more interestingly, also making use of multiple cores for a single formula. - Check Assumptions. The binary API exposes a new call #Z3_check_assumptions, which allows passing in additional assumptions while checking for consistency of the already asserted formulas. The API function returns a subset of the assumptions that were used in an unsatisfiable core. It also returns an optional proof object. - Proof Objects. The #Z3_check_assumptions retuns a proof object if the configuration flag PROOF_MODE is set to 1 or 2. - Partial support for non-linear arithmetic. The support uses support for computing Groebner bases. It allows solving some, but far from all, formulas using polynomials over the reals. Uses should be aware that the support for non-linear arithmetic (over the reals) is not complete in Z3. - Recursive data-types. The theory of well-founded recursive data-types is supported over the binary APIs. It supports ground satisfiability checking for tuples, enumeration types (scalars), lists and mututally recursive data-types. z3-4.4.0/scripts/0000755000175000017500000000000012540347414013334 5ustar michaelmichaelz3-4.4.0/scripts/mk_exception.py0000644000175000017500000000050012540347414016366 0ustar michaelmichael############################################ # Copyright (c) 2012 Microsoft Corporation # # Author: Leonardo de Moura (leonardo) ############################################ class MKException(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) z3-4.4.0/scripts/mk_make.py0000644000175000017500000000070612540347414015315 0ustar michaelmichael############################################ # Copyright (c) 2012 Microsoft Corporation # # Scripts for generating Makefiles and Visual # Studio project files. # # Author: Leonardo de Moura (leonardo) ############################################ from mk_util import * from mk_project import * parse_options() check_eol() API_files = init_project_def() update_version() mk_auto_src() mk_bindings(API_files) mk_vs_proj('z3', ['shell']) mk_makefile() z3-4.4.0/scripts/mk_project.py0000644000175000017500000001425612540347414016053 0ustar michaelmichael############################################ # Copyright (c) 2012 Microsoft Corporation # # Z3 project configuration files # # Author: Leonardo de Moura (leonardo) ############################################ from mk_util import * # Z3 Project definition def init_project_def(): set_version(4, 4, 1, 0) add_lib('util', []) add_lib('polynomial', ['util'], 'math/polynomial') add_lib('sat', ['util']) add_lib('nlsat', ['polynomial', 'sat']) add_lib('hilbert', ['util'], 'math/hilbert') add_lib('simplex', ['util'], 'math/simplex') 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') add_lib('normal_forms', ['rewriter'], 'ast/normal_forms') add_lib('model', ['rewriter']) add_lib('tactic', ['ast', 'model']) add_lib('substitution', ['ast', 'rewriter'], 'ast/substitution') add_lib('parser_util', ['ast'], 'parsers/util') add_lib('grobner', ['ast'], 'math/grobner') add_lib('euclid', ['util'], 'math/euclid') add_lib('core_tactics', ['tactic', 'normal_forms'], 'tactic/core') add_lib('sat_tactic', ['tactic', 'sat'], 'sat/tactic') add_lib('arith_tactics', ['core_tactics', 'sat'], 'tactic/arith') add_lib('nlsat_tactic', ['nlsat', 'sat_tactic', 'arith_tactics'], 'nlsat/tactic') add_lib('subpaving_tactic', ['core_tactics', 'subpaving'], 'math/subpaving/tactic') add_lib('aig_tactic', ['tactic'], 'tactic/aig') add_lib('solver', ['model', 'tactic']) add_lib('interp', ['solver']) add_lib('cmd_context', ['solver', 'rewriter', 'interp']) add_lib('extra_cmds', ['cmd_context', 'subpaving_tactic', 'arith_tactics'], 'cmd_context/extra_cmds') add_lib('smt2parser', ['cmd_context', 'parser_util'], 'parsers/smt2') add_lib('proof_checker', ['rewriter'], 'ast/proof_checker') # Simplifier module will be deleted in the future. # It has been replaced with rewriter module. add_lib('simplifier', ['rewriter'], 'ast/simplifier') add_lib('fpa', ['ast', 'util', 'simplifier'], 'ast/fpa') add_lib('macros', ['simplifier'], 'ast/macros') add_lib('pattern', ['normal_forms', 'smt2parser', 'simplifier'], 'ast/pattern') add_lib('bit_blaster', ['rewriter', 'simplifier'], 'ast/rewriter/bit_blaster') add_lib('smt_params', ['ast', 'simplifier', 'pattern', 'bit_blaster'], 'smt/params') add_lib('proto_model', ['model', 'simplifier', 'smt_params'], 'smt/proto_model') add_lib('smt', ['bit_blaster', 'macros', 'normal_forms', 'cmd_context', 'proto_model', 'substitution', 'grobner', 'euclid', 'simplex', 'proof_checker', 'pattern', 'parser_util', 'fpa']) add_lib('user_plugin', ['smt'], 'smt/user_plugin') add_lib('bv_tactics', ['tactic', 'bit_blaster'], 'tactic/bv') add_lib('fuzzing', ['ast'], 'test/fuzzing') add_lib('smt_tactic', ['smt'], 'smt/tactic') add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') add_lib('qe', ['smt','sat'], 'qe') add_lib('duality', ['smt', 'interp', 'qe']) add_lib('muz', ['smt', 'sat', 'smt2parser', 'aig_tactic', 'qe'], 'muz/base') add_lib('transforms', ['muz', 'hilbert'], 'muz/transforms') add_lib('rel', ['muz', 'transforms'], 'muz/rel') add_lib('pdr', ['muz', 'transforms', 'arith_tactics', 'core_tactics', 'smt_tactic'], 'muz/pdr') add_lib('clp', ['muz', 'transforms'], 'muz/clp') add_lib('tab', ['muz', 'transforms'], 'muz/tab') add_lib('bmc', ['muz', 'transforms'], 'muz/bmc') add_lib('ddnf', ['muz', 'transforms', 'rel'], 'muz/ddnf') add_lib('duality_intf', ['muz', 'transforms', 'duality'], 'muz/duality') add_lib('fp', ['muz', 'pdr', 'clp', 'tab', 'rel', 'bmc', 'duality_intf', 'ddnf'], 'muz/fp') add_lib('nlsat_smt_tactic', ['nlsat_tactic', 'smt_tactic'], 'tactic/nlsat_smt') add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'fp', 'muz','qe','nlsat_smt_tactic'], 'tactic/smtlogics') add_lib('fpa_tactics', ['fpa', 'core_tactics', 'bv_tactics', 'sat_tactic', 'smt_tactic', 'arith_tactics', 'smtlogic_tactics'], 'tactic/fpa') add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa_tactics', 'aig_tactic', 'fp', 'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') add_lib('smtparser', ['portfolio'], 'parsers/smt') add_lib('opt', ['smt', 'smtlogic_tactics', 'sls_tactic'], 'opt') # add_dll('foci2', ['util'], 'interp/foci2stub', # dll_name='foci2', # export_files=['foci2stub.cpp']) # add_lib('interp', ['solver','foci2']) API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h', 'z3_interp.h', 'z3_fpa.h'] add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure', 'interp', 'opt'], includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files) add_exe('shell', ['api', 'sat', 'extra_cmds','opt'], exe_name='z3') add_exe('test', ['api', 'fuzzing', 'simplex'], exe_name='test-z3', install=False) add_dll('api_dll', ['api', 'sat', 'extra_cmds'], 'api/dll', reexports=['api'], dll_name='libz3', static=build_static_lib(), export_files=API_files) add_dot_net_dll('dotnet', ['api_dll'], 'api/dotnet', dll_name='Microsoft.Z3', assembly_info_dir='Properties') add_java_dll('java', ['api_dll'], 'api/java', dll_name='libz3java', package_name="com.microsoft.z3", manifest_file='manifest') add_ml_lib('ml', ['api_dll'], 'api/ml', lib_name='libz3ml') add_hlib('cpp', 'api/c++', includes2install=['z3++.h']) set_z3py_dir('api/python') # Examples add_cpp_example('cpp_example', 'c++') add_cpp_example('iz3', 'interp') add_cpp_example('z3_tptp', 'tptp') add_c_example('c_example', 'c') add_c_example('maxsat') add_dotnet_example('dotnet_example', 'dotnet') add_java_example('java_example', 'java') add_ml_example('ml_example', 'ml') add_z3py_example('py_example', 'python') return API_files z3-4.4.0/scripts/mk_util.py0000644000175000017500000036436512540347414015373 0ustar michaelmichael############################################ # Copyright (c) 2012 Microsoft Corporation # # Auxiliary scripts for generating Makefiles # and Visual Studio project files. # # Author: Leonardo de Moura (leonardo) ############################################ import sys import os import glob import re import getopt import shutil from mk_exception import * from fnmatch import fnmatch import distutils.sysconfig import compileall import subprocess import string def getenv(name, default): try: return os.environ[name].strip(' "\'') except: return default CXX=getenv("CXX", None) CC=getenv("CC", None) CPPFLAGS=getenv("CPPFLAGS", "") CXXFLAGS=getenv("CXXFLAGS", "") EXAMP_DEBUG_FLAG='' LDFLAGS=getenv("LDFLAGS", "") JNI_HOME=getenv("JNI_HOME", None) OCAMLC=getenv("OCAMLC", "ocamlc") OCAMLOPT=getenv("OCAMLOPT", "ocamlopt") OCAML_LIB=getenv("OCAML_LIB", None) OCAMLFIND=getenv("OCAMLFIND", "ocamlfind") CXX_COMPILERS=['g++', 'clang++'] C_COMPILERS=['gcc', 'clang'] JAVAC=None JAR=None PYTHON_PACKAGE_DIR=distutils.sysconfig.get_python_lib() BUILD_DIR='build' REV_BUILD_DIR='..' SRC_DIR='src' EXAMPLE_DIR='examples' # Required Components Z3_DLL_COMPONENT='api_dll' PATTERN_COMPONENT='pattern' UTIL_COMPONENT='util' API_COMPONENT='api' DOTNET_COMPONENT='dotnet' JAVA_COMPONENT='java' ML_COMPONENT='ml' CPP_COMPONENT='cpp' ##################### IS_WINDOWS=False IS_LINUX=False IS_OSX=False IS_FREEBSD=False VERBOSE=True DEBUG_MODE=False SHOW_CPPS = True VS_X64 = False LINUX_X64 = True ONLY_MAKEFILES = False Z3PY_SRC_DIR=None VS_PROJ = False TRACE = False DOTNET_ENABLED=False JAVA_ENABLED=False ML_ENABLED=False STATIC_LIB=False VER_MAJOR=None VER_MINOR=None VER_BUILD=None VER_REVISION=None PREFIX=os.path.split(os.path.split(os.path.split(PYTHON_PACKAGE_DIR)[0])[0])[0] GMP=False FOCI2=False FOCI2LIB='' VS_PAR=False VS_PAR_NUM=8 GPROF=False GIT_HASH=False OPTIMIZE=False FPMATH="Default" FPMATH_FLAGS="-mfpmath=sse -msse -msse2" def check_output(cmd): return str(subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0]).rstrip('\r\n') def git_hash(): try: branch = check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']) r = check_output(['git', 'show-ref', '--abbrev=12', 'refs/heads/%s' % branch]) except: raise MKException("Failed to retrieve git hash") ls = r.split(' ') if len(ls) != 2: raise MKException("Unexpected git output") return ls[0] def is_windows(): return IS_WINDOWS def is_linux(): return IS_LINUX def is_freebsd(): return IS_FREEBSD def is_osx(): return IS_OSX def norm_path(p): # We use '/' on mk_project for convenience return os.path.join(*(p.split('/'))) def which(program): import os def is_exe(fpath): return os.path.isfile(fpath) and os.access(fpath, os.X_OK) fpath, fname = os.path.split(program) if fpath: if is_exe(program): return program else: for path in getenv("PATH", "").split(os.pathsep): exe_file = os.path.join(path, program) if is_exe(exe_file): return exe_file class TempFile: def __init__(self, name): try: self.name = name self.fname = open(name, 'w') except: raise MKException("Failed to create temporary file '%s'" % self.name) def add(self, s): self.fname.write(s) def commit(self): self.fname.close() def __del__(self): self.fname.close() try: os.remove(self.name) except: pass def exec_cmd(cmd): if isinstance(cmd, str): cmd = cmd.split(' ') new_cmd = [] first = True for e in cmd: if first: first = False new_cmd.append(e) else: if e != "": se = e.split(' ') if len(se) > 1: for e2 in se: if e2 != "": new_cmd.append(e2) else: new_cmd.append(e) cmd = new_cmd null = open(os.devnull, 'wb') try: return subprocess.call(cmd, stdout=null, stderr=null) except: # Failed to create process return 1 # rm -f fname def rmf(fname): if os.path.exists(fname): os.remove(fname) def exec_compiler_cmd(cmd): r = exec_cmd(cmd) rmf('a.out') return r def test_cxx_compiler(cc): if is_verbose(): print("Testing %s..." % cc) t = TempFile('tst.cpp') t.add('#include\nint main() { return 0; }\n') t.commit() return exec_compiler_cmd([cc, CPPFLAGS, CXXFLAGS, 'tst.cpp', LDFLAGS]) == 0 def test_c_compiler(cc): if is_verbose(): print("Testing %s..." % cc) t = TempFile('tst.c') t.add('#include\nint main() { return 0; }\n') t.commit() return exec_compiler_cmd([cc, CPPFLAGS, 'tst.c', LDFLAGS]) == 0 def test_gmp(cc): if is_verbose(): print("Testing GMP...") t = TempFile('tstgmp.cpp') t.add('#include\nint main() { mpz_t t; mpz_init(t); mpz_clear(t); return 0; }\n') t.commit() return exec_compiler_cmd([cc, CPPFLAGS, 'tstgmp.cpp', LDFLAGS, '-lgmp']) == 0 def test_foci2(cc,foci2lib): if is_verbose(): print("Testing FOCI2...") t = TempFile('tstfoci2.cpp') t.add('#include\nint main() { foci2 *f = foci2::create("lia"); return 0; }\n') t.commit() return exec_compiler_cmd([cc, CPPFLAGS, '-Isrc/interp', 'tstfoci2.cpp', LDFLAGS, foci2lib]) == 0 def test_openmp(cc): if is_verbose(): print("Testing OpenMP...") t = TempFile('tstomp.cpp') t.add('#include\nint main() { return omp_in_parallel() ? 1 : 0; }\n') t.commit() if IS_WINDOWS: r = exec_compiler_cmd([cc, CPPFLAGS, 'tstomp.cpp', LDFLAGS, '/openmp']) == 0 try: rmf('tstomp.obj') rmf('tstomp.exe') except: pass return r else: return exec_compiler_cmd([cc, CPPFLAGS, 'tstomp.cpp', LDFLAGS, '-fopenmp']) == 0 def test_fpmath(cc): global FPMATH_FLAGS if is_verbose(): print("Testing floating point support...") t = TempFile('tstsse.cpp') t.add('int main() { return 42; }\n') t.commit() if exec_compiler_cmd([cc, CPPFLAGS, 'tstsse.cpp', LDFLAGS, '-mfpmath=sse -msse -msse2']) == 0: FPMATH_FLAGS="-mfpmath=sse -msse -msse2" return "SSE2-GCC" elif exec_compiler_cmd([cc, CPPFLAGS, 'tstsse.cpp', LDFLAGS, '-msse -msse2']) == 0: FPMATH_FLAGS="-msse -msse2" return "SSE2-CLANG" elif exec_compiler_cmd([cc, CPPFLAGS, 'tstsse.cpp', LDFLAGS, '-mfpu=vfp -mfloat-abi=hard']) == 0: FPMATH_FLAGS="-mfpu=vfp -mfloat-abi=hard" return "ARM-VFP" else: FPMATH_FLAGS="" return "UNKNOWN" def find_jni_h(path): for root, dirs, files in os.walk(path): for f in files: if f == 'jni.h': return root return False def check_java(): global JNI_HOME global JAVAC global JAR JDK_HOME = getenv('JDK_HOME', None) # we only need to check this locally. if is_verbose(): print("Finding javac ...") if JDK_HOME != None: if IS_WINDOWS: JAVAC = os.path.join(JDK_HOME, 'bin', 'javac.exe') else: JAVAC = os.path.join(JDK_HOME, 'bin', 'javac') if not os.path.exists(JAVAC): raise MKException("Failed to detect javac at '%s/bin'; the environment variable JDK_HOME is probably set to the wrong path." % os.path.join(JDK_HOME)) else: # Search for javac in the path. ind = 'javac'; if IS_WINDOWS: ind = ind + '.exe' paths = os.getenv('PATH', None) if paths: spaths = paths.split(os.pathsep) for i in range(0, len(spaths)): cmb = os.path.join(spaths[i], ind) if os.path.exists(cmb): JAVAC = cmb break if JAVAC == None: raise MKException('No java compiler in the path, please adjust your PATH or set JDK_HOME to the location of the JDK.') if is_verbose(): print("Finding jar ...") if IS_WINDOWS: JAR = os.path.join(os.path.dirname(JAVAC), 'jar.exe') else: JAR = os.path.join(os.path.dirname(JAVAC), 'jar') if not os.path.exists(JAR): raise MKException("Failed to detect jar at '%s'; the environment variable JDK_HOME is probably set to the wrong path." % os.path.join(JDK_HOME)) if is_verbose(): print("Testing %s..." % JAVAC) t = TempFile('Hello.java') t.add('public class Hello { public static void main(String[] args) { System.out.println("Hello, World"); }}\n') t.commit() oo = TempFile('output') eo = TempFile('errout') try: subprocess.call([JAVAC, 'Hello.java', '-verbose'], stdout=oo.fname, stderr=eo.fname) oo.commit() eo.commit() except: raise MKException('Found, but failed to run Java compiler at %s' % (JAVAC)) os.remove('Hello.class') if is_verbose(): print("Finding jni.h...") if JNI_HOME != None: if not os.path.exists(os.path.join(JNI_HOME, 'jni.h')): raise MKException("Failed to detect jni.h '%s'; the environment variable JNI_HOME is probably set to the wrong path." % os.path.join(JNI_HOME)) else: # Search for jni.h in the library directories... t = open('errout', 'r') open_pat = re.compile("\[search path for class files: (.*)\]") cdirs = [] for line in t: m = open_pat.match(line) if m: libdirs = m.group(1).split(',') for libdir in libdirs: q = os.path.dirname(libdir) if cdirs.count(q) == 0: cdirs.append(q) # ... plus some heuristic ones. extra_dirs = [] # For the libraries, even the JDK usually uses a JRE that comes with it. To find the # headers we have to go a little bit higher up. for dir in cdirs: extra_dirs.append(os.path.abspath(os.path.join(dir, '..'))) if IS_OSX: # Apparently Apple knows best where to put stuff... extra_dirs.append('/System/Library/Frameworks/JavaVM.framework/Headers/') cdirs[len(cdirs):] = extra_dirs for dir in cdirs: q = find_jni_h(dir) if q != False: JNI_HOME = q if JNI_HOME == None: raise MKException("Failed to detect jni.h. Possible solution: set JNI_HOME with the path to JDK.") def check_ml(): t = TempFile('hello.ml') t.add('print_string "Hello world!\n";;') t.commit() if is_verbose(): print ('Testing %s...' % OCAMLC) r = exec_cmd([OCAMLC, '-o', 'a.out', 'hello.ml']) if r != 0: raise MKException('Failed testing ocamlc compiler. Set environment variable OCAMLC with the path to the Ocaml compiler') if is_verbose(): print ('Testing %s...' % OCAMLOPT) r = exec_cmd([OCAMLOPT, '-o', 'a.out', 'hello.ml']) if r != 0: raise MKException('Failed testing ocamlopt compiler. Set environment variable OCAMLOPT with the path to the Ocaml native compiler. Note that ocamlopt may require flexlink to be in your path.') try: rmf('hello.cmi') rmf('hello.cmo') rmf('hello.cmx') rmf('a.out') rmf('hello.o') except: pass find_ml_lib() find_ocaml_find() def find_ocaml_find(): global OCAMLFIND if is_verbose(): print ("Testing %s..." % OCAMLFIND) r = exec_cmd([OCAMLFIND, 'printconf']) if r != 0: OCAMLFIND='' def find_ml_lib(): global OCAML_LIB if is_verbose(): print ('Finding OCAML_LIB...') t = TempFile('output') null = open(os.devnull, 'wb') try: subprocess.call([OCAMLC, '-where'], stdout=t.fname, stderr=null) t.commit() except: raise MKException('Failed to find Ocaml library; please set OCAML_LIB') t = open('output', 'r') for line in t: OCAML_LIB = line[:-1] if is_verbose(): print ('OCAML_LIB=%s' % OCAML_LIB) t.close() rmf('output') return def is64(): global LINUX_X64 return LINUX_X64 and sys.maxsize >= 2**32 def check_ar(): if is_verbose(): print("Testing ar...") if which('ar')== None: raise MKException('ar (archive tool) was not found') def find_cxx_compiler(): global CXX, CXX_COMPILERS if CXX != None: if test_cxx_compiler(CXX): return CXX for cxx in CXX_COMPILERS: if test_cxx_compiler(cxx): CXX = cxx return CXX raise MKException('C++ compiler was not found. Try to set the environment variable CXX with the C++ compiler available in your system.') def find_c_compiler(): global CC, C_COMPILERS if CC != None: if test_c_compiler(CC): return CC for c in C_COMPILERS: if test_c_compiler(c): CC = c return CC raise MKException('C compiler was not found. Try to set the environment variable CC with the C compiler available in your system.') def set_version(major, minor, build, revision): global VER_MAJOR, VER_MINOR, VER_BUILD, VER_REVISION VER_MAJOR = major VER_MINOR = minor VER_BUILD = build VER_REVISION = revision def get_version(): return (VER_MAJOR, VER_MINOR, VER_BUILD, VER_REVISION) def build_static_lib(): return STATIC_LIB def is_cr_lf(fname): # Check whether text files use cr/lf f = open(fname, 'r') line = f.readline() sz = len(line) return sz >= 2 and line[sz-2] == '\r' and line[sz-1] == '\n' # dos2unix in python # cr/lf --> lf def dos2unix(fname): if is_cr_lf(fname): fin = open(fname, 'r') fname_new = '%s.new' % fname fout = open(fname_new, 'w') for line in fin: line = line.rstrip('\r\n') fout.write(line) fout.write('\n') fin.close() fout.close() shutil.move(fname_new, fname) if is_verbose(): print("dos2unix '%s'" % fname) def dos2unix_tree_core(pattern, dir, files): for filename in files: if fnmatch(filename, pattern): fname = os.path.join(dir, filename) if not os.path.isdir(fname): dos2unix(fname) def dos2unix_tree(): os.path.walk('src', dos2unix_tree_core, '*') def check_eol(): if not IS_WINDOWS: # Linux/OSX/BSD check if the end-of-line is cr/lf if is_cr_lf('LICENSE.txt'): if is_verbose(): print("Fixing end of line...") dos2unix_tree() if os.name == 'nt': IS_WINDOWS=True # Visual Studio already displays the files being compiled SHOW_CPPS=False # Enable .Net bindings by default on windows DOTNET_ENABLED=True elif os.name == 'posix': if os.uname()[0] == 'Darwin': IS_OSX=True elif os.uname()[0] == 'Linux': IS_LINUX=True elif os.uname()[0] == 'FreeBSD': IS_FREEBSD=True def display_help(exit_code): print("mk_make.py: Z3 Makefile generator\n") print("This script generates the Makefile for the Z3 theorem prover.") print("It must be executed from the Z3 root directory.") print("\nOptions:") print(" -h, --help display this message.") print(" -s, --silent do not print verbose messages.") if not IS_WINDOWS: print(" -p , --prefix= installation prefix (default: %s)." % PREFIX) else: print(" --parallel=num use cl option /MP with 'num' parallel processes") print(" -b , --build= subdirectory where Z3 will be built (default: build).") print(" --githash=hash include the given hash in the binaries.") print(" -d, --debug compile Z3 in debug mode.") print(" -t, --trace enable tracing in release mode.") if IS_WINDOWS: print(" -x, --x64 create 64 binary when using Visual Studio.") else: print(" --x86 force 32-bit x86 build on x64 systems.") print(" -m, --makefiles generate only makefiles.") if IS_WINDOWS: print(" -v, --vsproj generate Visual Studio Project Files.") if IS_WINDOWS: print(" -n, --nodotnet do not generate Microsoft.Z3.dll make rules.") if IS_WINDOWS: print(" --optimize generate optimized code during linking.") print(" -j, --java generate Java bindings.") print(" --ml generate OCaml bindings.") print(" --staticlib build Z3 static library.") if not IS_WINDOWS: print(" -g, --gmp use GMP.") print(" --gprof enable gprof") print(" -f --foci2= use foci2 library at path") print("") print("Some influential environment variables:") if not IS_WINDOWS: print(" CXX C++ compiler") print(" CC C compiler") print(" LDFLAGS Linker flags, e.g., -L if you have libraries in a non-standard directory") print(" CPPFLAGS Preprocessor flags, e.g., -I if you have header files in a non-standard directory") print(" CXXFLAGS C++ compiler flags") print(" JDK_HOME JDK installation directory (only relevant if -j or --java option is provided)") print(" JNI_HOME JNI bindings directory (only relevant if -j or --java option is provided)") print(" OCAMLC Ocaml byte-code compiler (only relevant with --ml)") print(" OCAMLOPT Ocaml native compiler (only relevant with --ml)") print(" OCAML_LIB Ocaml library directory (only relevant with --ml)") exit(exit_code) # Parse configuration option for mk_make script def parse_options(): global VERBOSE, DEBUG_MODE, IS_WINDOWS, VS_X64, ONLY_MAKEFILES, SHOW_CPPS, VS_PROJ, TRACE, VS_PAR, VS_PAR_NUM global DOTNET_ENABLED, JAVA_ENABLED, ML_ENABLED, STATIC_LIB, PREFIX, GMP, FOCI2, FOCI2LIB, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH global LINUX_X64, OPTIMIZE try: options, remainder = getopt.gnu_getopt(sys.argv[1:], 'b:df:sxhmcvtnp:gj', ['build=', 'debug', 'silent', 'x64', 'help', 'makefiles', 'showcpp', 'vsproj', 'trace', 'nodotnet', 'staticlib', 'prefix=', 'gmp', 'foci2=', 'java', 'parallel=', 'gprof', 'githash=', 'x86', 'ml', 'optimize']) except: print("ERROR: Invalid command line option") display_help(1) for opt, arg in options: print('opt = %s, arg = %s' % (opt, arg)) if opt in ('-b', '--build'): if arg == 'src': raise MKException('The src directory should not be used to host the Makefile') set_build_dir(arg) elif opt in ('-s', '--silent'): VERBOSE = False elif opt in ('-d', '--debug'): DEBUG_MODE = True elif opt in ('-x', '--x64'): if not IS_WINDOWS: raise MKException('x64 compilation mode can only be specified when using Visual Studio') VS_X64 = True elif opt in ('--x86'): LINUX_X64=False elif opt in ('-h', '--help'): display_help(0) elif opt in ('-m', '--makefiles'): ONLY_MAKEFILES = True elif opt in ('-c', '--showcpp'): SHOW_CPPS = True elif opt in ('-v', '--vsproj'): VS_PROJ = True elif opt in ('-t', '--trace'): TRACE = True elif opt in ('-n', '--nodotnet'): DOTNET_ENABLED = False elif opt in ('--staticlib'): STATIC_LIB = True elif opt in ('--optimize'): OPTIMIZE = True elif not IS_WINDOWS and opt in ('-p', '--prefix'): PREFIX = arg PYTHON_PACKAGE_DIR = os.path.join(PREFIX, 'lib', 'python%s' % distutils.sysconfig.get_python_version(), 'dist-packages') mk_dir(PYTHON_PACKAGE_DIR) if sys.version >= "3": mk_dir(os.path.join(PYTHON_PACKAGE_DIR, '__pycache__')) elif IS_WINDOWS and opt == '--parallel': VS_PAR = True VS_PAR_NUM = int(arg) elif opt in ('-g', '--gmp'): GMP = True elif opt in ('-f', '--foci2'): FOCI2 = True FOCI2LIB = arg elif opt in ('-j', '--java'): JAVA_ENABLED = True elif opt == '--gprof': GPROF = True elif opt == '--githash': GIT_HASH=arg elif opt in ('', '--ml'): ML_ENABLED = True else: print("ERROR: Invalid command line option '%s'" % opt) display_help(1) # Return a list containing a file names included using '#include' in # the given C/C++ file named fname. def extract_c_includes(fname): result = [] # We look for well behaved #include directives std_inc_pat = re.compile("[ \t]*#include[ \t]*\"(.*)\"[ \t]*") system_inc_pat = re.compile("[ \t]*#include[ \t]*\<.*\>[ \t]*") # We should generate and error for any occurrence of #include that does not match the previous pattern. non_std_inc_pat = re.compile(".*#include.*") f = open(fname, 'r') linenum = 1 for line in f: m1 = std_inc_pat.match(line) if m1: result.append(m1.group(1)) elif not system_inc_pat.match(line) and non_std_inc_pat.match(line): raise MKException("Invalid #include directive at '%s':%s" % (fname, line)) linenum = linenum + 1 return result # Given a path dir1/subdir2/subdir3 returns ../../.. def reverse_path(p): l = p.split(os.sep) n = len(l) r = '..' for i in range(1, n): r = os.path.join(r, '..') return r def mk_dir(d): if not os.path.exists(d): os.makedirs(d) def set_build_dir(d): global BUILD_DIR, REV_BUILD_DIR BUILD_DIR = norm_path(d) REV_BUILD_DIR = reverse_path(d) def set_z3py_dir(p): global SRC_DIR, Z3PY_SRC_DIR p = norm_path(p) full = os.path.join(SRC_DIR, p) if not os.path.exists(full): raise MKException("Python bindings directory '%s' does not exist" % full) Z3PY_SRC_DIR = full if VERBOSE: print("Python bindings directory was detected.") _UNIQ_ID = 0 def mk_fresh_name(prefix): global _UNIQ_ID r = '%s_%s' % (prefix, _UNIQ_ID) _UNIQ_ID = _UNIQ_ID + 1 return r _Id = 0 _Components = [] _ComponentNames = set() _Name2Component = {} _Processed_Headers = set() # Return the Component object named name def get_component(name): return _Name2Component[name] def get_components(): return _Components # Return the directory where the python bindings are located. def get_z3py_dir(): return Z3PY_SRC_DIR # Return true if in verbose mode def is_verbose(): return VERBOSE def is_java_enabled(): return JAVA_ENABLED def is_ml_enabled(): return ML_ENABLED def is_compiler(given, expected): """ Return True if the 'given' compiler is the expected one. >>> is_compiler('g++', 'g++') True >>> is_compiler('/home/g++', 'g++') True >>> is_compiler(os.path.join('home', 'g++'), 'g++') True >>> is_compiler('clang++', 'g++') False >>> is_compiler(os.path.join('home', 'clang++'), 'clang++') True """ if given == expected: return True if len(expected) < len(given): return given[len(given) - len(expected) - 1] == os.sep and given[len(given) - len(expected):] == expected return False def is_CXX_gpp(): return is_compiler(CXX, 'g++') def is_clang_in_gpp_form(cc): version_string = check_output([cc, '--version']) return str(version_string).find('clang') != -1 def is_CXX_clangpp(): if is_compiler(CXX, 'g++'): return is_clang_in_gpp_form(CXX) return is_compiler(CXX, 'clang++') def get_cpp_files(path): return filter(lambda f: f.endswith('.cpp'), os.listdir(path)) def get_c_files(path): return filter(lambda f: f.endswith('.c'), os.listdir(path)) def get_cs_files(path): return filter(lambda f: f.endswith('.cs'), os.listdir(path)) def get_java_files(path): return filter(lambda f: f.endswith('.java'), os.listdir(path)) def get_ml_files(path): return filter(lambda f: f.endswith('.ml'), os.listdir(path)) def find_all_deps(name, deps): new_deps = [] for dep in deps: if dep in _ComponentNames: if not (dep in new_deps): new_deps.append(dep) for dep_dep in get_component(dep).deps: if not (dep_dep in new_deps): new_deps.append(dep_dep) else: raise MKException("Unknown component '%s' at '%s'." % (dep, name)) return new_deps class Component: def __init__(self, name, path, deps): global BUILD_DIR, SRC_DIR, REV_BUILD_DIR if name in _ComponentNames: raise MKException("Component '%s' was already defined." % name) if path == None: path = name self.name = name path = norm_path(path) self.path = path self.deps = find_all_deps(name, deps) self.build_dir = path self.src_dir = os.path.join(SRC_DIR, path) self.to_src_dir = os.path.join(REV_BUILD_DIR, self.src_dir) def get_link_name(self): return os.path.join(self.build_dir, self.name) + '$(LIB_EXT)' # Find fname in the include paths for the given component. # ownerfile is only used for creating error messages. # That is, we were looking for fname when processing ownerfile def find_file(self, fname, ownerfile): full_fname = os.path.join(self.src_dir, fname) if os.path.exists(full_fname): return self for dep in self.deps: c_dep = get_component(dep) full_fname = os.path.join(c_dep.src_dir, fname) if os.path.exists(full_fname): return c_dep raise MKException("Failed to find include file '%s' for '%s' when processing '%s'." % (fname, ownerfile, self.name)) # Display all dependencies of file basename located in the given component directory. # The result is displayed at out def add_cpp_h_deps(self, out, basename): includes = extract_c_includes(os.path.join(self.src_dir, basename)) out.write(os.path.join(self.to_src_dir, basename)) for include in includes: owner = self.find_file(include, basename) out.write(' %s.node' % os.path.join(owner.build_dir, include)) # Add a rule for each #include directive in the file basename located at the current component. def add_rule_for_each_include(self, out, basename): fullname = os.path.join(self.src_dir, basename) includes = extract_c_includes(fullname) for include in includes: owner = self.find_file(include, fullname) owner.add_h_rule(out, include) # Display a Makefile rule for an include file located in the given component directory. # 'include' is something of the form: ast.h, polynomial.h # The rule displayed at out is of the form # ast/ast_pp.h.node : ../src/util/ast_pp.h util/util.h.node ast/ast.h.node # @echo "done" > ast/ast_pp.h.node def add_h_rule(self, out, include): include_src_path = os.path.join(self.to_src_dir, include) if include_src_path in _Processed_Headers: return _Processed_Headers.add(include_src_path) self.add_rule_for_each_include(out, include) include_node = '%s.node' % os.path.join(self.build_dir, include) out.write('%s: ' % include_node) self.add_cpp_h_deps(out, include) out.write('\n') out.write('\t@echo done > %s\n' % include_node) def add_cpp_rules(self, out, include_defs, cppfile): self.add_rule_for_each_include(out, cppfile) objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0]) srcfile = os.path.join(self.to_src_dir, cppfile) out.write('%s: ' % objfile) self.add_cpp_h_deps(out, cppfile) out.write('\n') if SHOW_CPPS: out.write('\t@echo %s\n' % os.path.join(self.src_dir, cppfile)) out.write('\t@$(CXX) $(CXXFLAGS) $(%s) $(CXX_OUT_FLAG)%s %s\n' % (include_defs, objfile, srcfile)) def mk_makefile(self, out): include_defs = mk_fresh_name('includes') out.write('%s =' % include_defs) for dep in self.deps: out.write(' -I%s' % get_component(dep).to_src_dir) out.write('\n') mk_dir(os.path.join(BUILD_DIR, self.build_dir)) if VS_PAR and IS_WINDOWS: cppfiles = list(get_cpp_files(self.src_dir)) dependencies = set() for cppfile in cppfiles: dependencies.add(os.path.join(self.to_src_dir, cppfile)) self.add_rule_for_each_include(out, cppfile) includes = extract_c_includes(os.path.join(self.src_dir, cppfile)) for include in includes: owner = self.find_file(include, cppfile) dependencies.add('%s.node' % os.path.join(owner.build_dir, include)) for cppfile in cppfiles: out.write('%s$(OBJ_EXT) ' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0])) out.write(': ') for dep in dependencies: out.write(dep) out.write(' ') out.write('\n') out.write('\t@$(CXX) $(CXXFLAGS) /MP%s $(%s)' % (VS_PAR_NUM, include_defs)) for cppfile in cppfiles: out.write(' ') out.write(os.path.join(self.to_src_dir, cppfile)) out.write('\n') out.write('\tmove *.obj %s\n' % self.build_dir) else: for cppfile in get_cpp_files(self.src_dir): self.add_cpp_rules(out, include_defs, cppfile) # Return true if the component should be included in the all: rule def main_component(self): return False # Return true if the component contains an AssemblyInfo.cs file that needs to be updated. def has_assembly_info(self): return False # Return true if the component needs builder to generate an install_tactics.cpp file def require_install_tactics(self): return False # Return true if the component needs a def file def require_def_file(self): return False # Return true if the component needs builder to generate a mem_initializer.cpp file with mem_initialize() and mem_finalize() functions. def require_mem_initializer(self): return False def mk_install_deps(self, out): return def mk_install(self, out): return def mk_uninstall(self, out): return def is_example(self): return False # Invoked when creating a (windows) distribution package using components at build_path, and # storing them at dist_path def mk_win_dist(self, build_path, dist_path): return def mk_unix_dist(self, build_path, dist_path): return class LibComponent(Component): def __init__(self, name, path, deps, includes2install): Component.__init__(self, name, path, deps) self.includes2install = includes2install def mk_makefile(self, out): Component.mk_makefile(self, out) # generate rule for lib objs = [] for cppfile in get_cpp_files(self.src_dir): objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) libfile = '%s$(LIB_EXT)' % os.path.join(self.build_dir, self.name) out.write('%s:' % libfile) for obj in objs: out.write(' ') out.write(obj) out.write('\n') out.write('\t@$(AR) $(AR_FLAGS) $(AR_OUTFLAG)%s' % libfile) for obj in objs: out.write(' ') out.write(obj) out.write('\n') out.write('%s: %s\n\n' % (self.name, libfile)) def mk_install_dep(self, out): out.write('%s' % libfile) def mk_install(self, out): for include in self.includes2install: out.write('\t@cp %s %s\n' % (os.path.join(self.to_src_dir, include), os.path.join('$(PREFIX)', 'include', include))) def mk_uninstall(self, out): for include in self.includes2install: out.write('\t@rm -f %s\n' % os.path.join('$(PREFIX)', 'include', include)) def mk_win_dist(self, build_path, dist_path): mk_dir(os.path.join(dist_path, 'include')) for include in self.includes2install: shutil.copy(os.path.join(self.src_dir, include), os.path.join(dist_path, 'include', include)) def mk_unix_dist(self, build_path, dist_path): self.mk_win_dist(build_path, dist_path) # "Library" containing only .h files. This is just a placeholder for includes files to be installed. class HLibComponent(LibComponent): def __init__(self, name, path, includes2install): LibComponent.__init__(self, name, path, [], includes2install) def mk_makefile(self, out): return # Auxiliary function for sort_components def comp_components(c1, c2): id1 = get_component(c1).id id2 = get_component(c2).id return id2 - id1 # Sort components based on (reverse) definition time def sort_components(cnames): return sorted(cnames, key=lambda c: get_component(c).id, reverse=True) class ExeComponent(Component): def __init__(self, name, exe_name, path, deps, install): Component.__init__(self, name, path, deps) if exe_name == None: exe_name = name self.exe_name = exe_name self.install = install def mk_makefile(self, out): Component.mk_makefile(self, out) # generate rule for exe exefile = '%s$(EXE_EXT)' % self.exe_name out.write('%s:' % exefile) deps = sort_components(self.deps) objs = [] for cppfile in get_cpp_files(self.src_dir): objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) for obj in objs: out.write(' ') out.write(obj) for dep in deps: c_dep = get_component(dep) out.write(' ' + c_dep.get_link_name()) out.write('\n') out.write('\t$(LINK) $(LINK_OUT_FLAG)%s $(LINK_FLAGS)' % exefile) for obj in objs: out.write(' ') out.write(obj) for dep in deps: c_dep = get_component(dep) out.write(' ' + c_dep.get_link_name()) out.write(' ' + FOCI2LIB) out.write(' $(LINK_EXTRA_FLAGS)\n') out.write('%s: %s\n\n' % (self.name, exefile)) def require_install_tactics(self): return ('tactic' in self.deps) and ('cmd_context' in self.deps) def require_mem_initializer(self): return True # All executables (to be installed) are included in the all: rule def main_component(self): return self.install def mk_install_dep(self, out): out.write('%s' % exefile) def mk_install(self, out): if self.install: exefile = '%s$(EXE_EXT)' % self.exe_name out.write('\t@cp %s %s\n' % (exefile, os.path.join('$(PREFIX)', 'bin', exefile))) def mk_uninstall(self, out): exefile = '%s$(EXE_EXT)' % self.exe_name out.write('\t@rm -f %s\n' % os.path.join('$(PREFIX)', 'bin', exefile)) def mk_win_dist(self, build_path, dist_path): if self.install: mk_dir(os.path.join(dist_path, 'bin')) shutil.copy('%s.exe' % os.path.join(build_path, self.exe_name), '%s.exe' % os.path.join(dist_path, 'bin', self.exe_name)) def mk_unix_dist(self, build_path, dist_path): if self.install: mk_dir(os.path.join(dist_path, 'bin')) shutil.copy(os.path.join(build_path, self.exe_name), os.path.join(dist_path, 'bin', self.exe_name)) class ExtraExeComponent(ExeComponent): def __init__(self, name, exe_name, path, deps, install): ExeComponent.__init__(self, name, exe_name, path, deps, install) def main_component(self): return False def require_mem_initializer(self): return False def get_so_ext(): sysname = os.uname()[0] if sysname == 'Darwin': return 'dylib' elif sysname == 'Linux' or sysname == 'FreeBSD': return 'so' elif sysname == 'CYGWIN': return 'dll' else: assert(False) return 'dll' class DLLComponent(Component): def __init__(self, name, dll_name, path, deps, export_files, reexports, install, static): Component.__init__(self, name, path, deps) if dll_name == None: dll_name = name self.dll_name = dll_name self.export_files = export_files self.reexports = reexports self.install = install self.static = static def get_link_name(self): if self.static: return os.path.join(self.build_dir, self.name) + '$(LIB_EXT)' else: return self.name + '$(SO_EXT)' def mk_makefile(self, out): Component.mk_makefile(self, out) # generate rule for (SO_EXT) dllfile = '%s$(SO_EXT)' % self.dll_name out.write('%s:' % dllfile) deps = sort_components(self.deps) objs = [] for cppfile in get_cpp_files(self.src_dir): objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) # Explicitly include obj files of reexport. This fixes problems with exported symbols on Linux and OSX. for reexport in self.reexports: reexport = get_component(reexport) for cppfile in get_cpp_files(reexport.src_dir): objfile = '%s$(OBJ_EXT)' % os.path.join(reexport.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) for obj in objs: out.write(' ') out.write(obj) for dep in deps: if not dep in self.reexports: c_dep = get_component(dep) out.write(' ' + c_dep.get_link_name()) out.write('\n') out.write('\t$(LINK) $(SLINK_OUT_FLAG)%s $(SLINK_FLAGS)' % dllfile) for obj in objs: out.write(' ') out.write(obj) for dep in deps: if not dep in self.reexports: c_dep = get_component(dep) out.write(' ' + c_dep.get_link_name()) out.write(' ' + FOCI2LIB) out.write(' $(SLINK_EXTRA_FLAGS)') if IS_WINDOWS: out.write(' /DEF:%s.def' % os.path.join(self.to_src_dir, self.name)) out.write('\n') if self.static: self.mk_static(out) libfile = '%s$(LIB_EXT)' % self.dll_name out.write('%s: %s %s\n\n' % (self.name, dllfile, libfile)) else: out.write('%s: %s\n\n' % (self.name, dllfile)) def mk_static(self, out): # generate rule for lib objs = [] for cppfile in get_cpp_files(self.src_dir): objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) # we have to "reexport" all object files for dep in self.deps: dep = get_component(dep) for cppfile in get_cpp_files(dep.src_dir): objfile = '%s$(OBJ_EXT)' % os.path.join(dep.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) libfile = '%s$(LIB_EXT)' % self.dll_name out.write('%s:' % libfile) for obj in objs: out.write(' ') out.write(obj) out.write('\n') out.write('\t@$(AR) $(AR_FLAGS) $(AR_OUTFLAG)%s' % libfile) for obj in objs: out.write(' ') out.write(obj) out.write('\n') def main_component(self): return self.install def require_install_tactics(self): return ('tactic' in self.deps) and ('cmd_context' in self.deps) def require_mem_initializer(self): return True def require_def_file(self): return IS_WINDOWS and self.export_files def mk_install_dep(self, out): out.write('%s$(SO_EXT)' % self.dll_name) if self.static: out.write(' %s$(LIB_EXT)' % self.dll_name) def mk_install(self, out): if self.install: dllfile = '%s$(SO_EXT)' % self.dll_name out.write('\t@cp %s %s\n' % (dllfile, os.path.join('$(PREFIX)', 'lib', dllfile))) out.write('\t@cp %s %s\n' % (dllfile, os.path.join(PYTHON_PACKAGE_DIR, dllfile))) if self.static: libfile = '%s$(LIB_EXT)' % self.dll_name out.write('\t@cp %s %s\n' % (libfile, os.path.join('$(PREFIX)', 'lib', libfile))) def mk_uninstall(self, out): dllfile = '%s$(SO_EXT)' % self.dll_name out.write('\t@rm -f %s\n' % os.path.join('$(PREFIX)', 'lib', dllfile)) out.write('\t@rm -f %s\n' % os.path.join(PYTHON_PACKAGE_DIR, dllfile)) libfile = '%s$(LIB_EXT)' % self.dll_name out.write('\t@rm -f %s\n' % os.path.join('$(PREFIX)', 'lib', libfile)) def mk_win_dist(self, build_path, dist_path): if self.install: mk_dir(os.path.join(dist_path, 'bin')) shutil.copy('%s.dll' % os.path.join(build_path, self.dll_name), '%s.dll' % os.path.join(dist_path, 'bin', self.dll_name)) shutil.copy('%s.lib' % os.path.join(build_path, self.dll_name), '%s.lib' % os.path.join(dist_path, 'bin', self.dll_name)) def mk_unix_dist(self, build_path, dist_path): if self.install: mk_dir(os.path.join(dist_path, 'bin')) so = get_so_ext() shutil.copy('%s.%s' % (os.path.join(build_path, self.dll_name), so), '%s.%s' % (os.path.join(dist_path, 'bin', self.dll_name), so)) shutil.copy('%s.a' % os.path.join(build_path, self.dll_name), '%s.a' % os.path.join(dist_path, 'bin', self.dll_name)) class DotNetDLLComponent(Component): def __init__(self, name, dll_name, path, deps, assembly_info_dir): Component.__init__(self, name, path, deps) if dll_name == None: dll_name = name if assembly_info_dir == None: assembly_info_dir = "." self.dll_name = dll_name self.assembly_info_dir = assembly_info_dir def mk_makefile(self, out): if DOTNET_ENABLED: cs_fp_files = [] cs_files = [] for cs_file in get_cs_files(self.src_dir): cs_fp_files.append(os.path.join(self.to_src_dir, cs_file)) cs_files.append(cs_file) if self.assembly_info_dir != '.': for cs_file in get_cs_files(os.path.join(self.src_dir, self.assembly_info_dir)): cs_fp_files.append(os.path.join(self.to_src_dir, self.assembly_info_dir, cs_file)) cs_files.append(os.path.join(self.assembly_info_dir, cs_file)) dllfile = '%s.dll' % self.dll_name out.write('%s: %s$(SO_EXT)' % (dllfile, get_component(Z3_DLL_COMPONENT).dll_name)) for cs_file in cs_fp_files: out.write(' ') out.write(cs_file) out.write('\n') out.write(' csc /noconfig /unsafe+ /nowarn:1701,1702 /nostdlib+ /errorreport:prompt /warn:4 /reference:mscorlib.dll /reference:System.Core.dll /reference:System.dll /reference:System.Numerics.dll /filealign:512 /linkresource:%s.dll /out:%s.dll /target:library /doc:%s.xml' % (get_component(Z3_DLL_COMPONENT).dll_name, self.dll_name, self.dll_name)) if DEBUG_MODE: out.write(' /define:DEBUG;TRACE /debug+ /debug:full /optimize-') else: out.write(' /optimize+') if VS_X64: out.write(' /platform:x64') else: out.write(' /platform:x86') for cs_file in cs_files: out.write(' %s' % os.path.join(self.to_src_dir, cs_file)) out.write('\n') out.write('%s: %s\n\n' % (self.name, dllfile)) return def main_component(self): return DOTNET_ENABLED def has_assembly_info(self): return True def mk_win_dist(self, build_path, dist_path): if DOTNET_ENABLED: # Assuming all DotNET dll should be in the distribution mk_dir(os.path.join(dist_path, 'bin')) shutil.copy('%s.dll' % os.path.join(build_path, self.dll_name), '%s.dll' % os.path.join(dist_path, 'bin', self.dll_name)) shutil.copy('%s.xml' % os.path.join(build_path, self.dll_name), '%s.xml' % os.path.join(dist_path, 'bin', self.dll_name)) if DEBUG_MODE: shutil.copy('%s.pdb' % os.path.join(build_path, self.dll_name), '%s.pdb' % os.path.join(dist_path, 'bin', self.dll_name)) def mk_unix_dist(self, build_path, dist_path): # Do nothing return class JavaDLLComponent(Component): def __init__(self, name, dll_name, package_name, manifest_file, path, deps): Component.__init__(self, name, path, deps) if dll_name == None: dll_name = name self.dll_name = dll_name self.package_name = package_name self.manifest_file = manifest_file self.install = not is_windows() def mk_makefile(self, out): global JAVAC global JAR if is_java_enabled(): mk_dir(os.path.join(BUILD_DIR, 'api', 'java', 'classes')) dllfile = '%s$(SO_EXT)' % self.dll_name out.write('libz3java$(SO_EXT): libz3$(SO_EXT) %s\n' % os.path.join(self.to_src_dir, 'Native.cpp')) t = '\t$(CXX) $(CXXFLAGS) $(CXX_OUT_FLAG)api/java/Native$(OBJ_EXT) -I"%s" -I"%s/PLATFORM" -I%s %s/Native.cpp\n' % (JNI_HOME, JNI_HOME, get_component('api').to_src_dir, self.to_src_dir) if IS_OSX: t = t.replace('PLATFORM', 'darwin') elif IS_LINUX: t = t.replace('PLATFORM', 'linux') elif IS_FREEBSD: t = t.replace('PLATFORM', 'freebsd') else: t = t.replace('PLATFORM', 'win32') out.write(t) if IS_WINDOWS: # On Windows, CL creates a .lib file to link against. out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) %s$(OBJ_EXT) libz3$(LIB_EXT)\n' % os.path.join('api', 'java', 'Native')) else: out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) %s$(OBJ_EXT) libz3$(SO_EXT)\n' % os.path.join('api', 'java', 'Native')) out.write('%s.jar: libz3java$(SO_EXT) ' % self.package_name) deps = '' for jfile in get_java_files(self.src_dir): deps += ('%s ' % os.path.join(self.to_src_dir, jfile)) for jfile in get_java_files(os.path.join(self.src_dir, "enumerations")): deps += '%s ' % os.path.join(self.to_src_dir, 'enumerations', jfile) out.write(deps) out.write('\n') #if IS_WINDOWS: JAVAC = '"%s"' % JAVAC JAR = '"%s"' % JAR t = ('\t%s %s.java -d %s\n' % (JAVAC, os.path.join(self.to_src_dir, 'enumerations', '*'), os.path.join('api', 'java', 'classes'))) out.write(t) t = ('\t%s -cp %s %s.java -d %s\n' % (JAVAC, os.path.join('api', 'java', 'classes'), os.path.join(self.to_src_dir, '*'), os.path.join('api', 'java', 'classes'))) out.write(t) out.write('\t%s cfm %s.jar %s -C %s .\n' % (JAR, self.package_name, os.path.join(self.to_src_dir, 'manifest'), os.path.join('api', 'java', 'classes'))) out.write('java: %s.jar\n\n' % self.package_name) def main_component(self): return is_java_enabled() def mk_win_dist(self, build_path, dist_path): if JAVA_ENABLED: mk_dir(os.path.join(dist_path, 'bin')) shutil.copy('%s.jar' % os.path.join(build_path, self.package_name), '%s.jar' % os.path.join(dist_path, 'bin', self.package_name)) shutil.copy(os.path.join(build_path, 'libz3java.dll'), os.path.join(dist_path, 'bin', 'libz3java.dll')) shutil.copy(os.path.join(build_path, 'libz3java.lib'), os.path.join(dist_path, 'bin', 'libz3java.lib')) def mk_unix_dist(self, build_path, dist_path): if JAVA_ENABLED: mk_dir(os.path.join(dist_path, 'bin')) shutil.copy('%s.jar' % os.path.join(build_path, self.package_name), '%s.jar' % os.path.join(dist_path, 'bin', self.package_name)) so = get_so_ext() shutil.copy(os.path.join(build_path, 'libz3java.%s' % so), os.path.join(dist_path, 'bin', 'libz3java.%s' % so)) def mk_install(self, out): if is_java_enabled() and self.install: dllfile = '%s$(SO_EXT)' % self.dll_name out.write('\t@cp %s %s\n' % (dllfile, os.path.join('$(PREFIX)', 'lib', dllfile))) out.write('\t@cp %s.jar %s.jar\n' % (self.package_name, os.path.join('$(PREFIX)', 'lib', self.package_name))) def mk_uninstall(self, out): if is_java_enabled() and self.install: dllfile = '%s$(SO_EXT)' % self.dll_name out.write('\t@rm %s\n' % (os.path.join('$(PREFIX)', 'lib', dllfile))) out.write('\t@rm %s.jar\n' % (os.path.join('$(PREFIX)', 'lib', self.package_name))) class MLComponent(Component): def __init__(self, name, lib_name, path, deps): Component.__init__(self, name, path, deps) if lib_name == None: lib_name = name self.lib_name = lib_name def mk_ml_meta(self, ml_meta_in, ml_meta_out, major, minor, build, revision): ver_pat = re.compile('version = "VERSION"*') fin = open(ml_meta_in, 'r') fout = open(ml_meta_out, 'w') num_updates = 0 for line in fin: if ver_pat.match(line): fout.write('version = "%s.%s.%s.%s"\n' % (major, minor, build, revision)) num_updates = num_updates + 1 else: fout.write(line) assert num_updates == 1, "unexpected number of version number updates" fin.close() fout.close() if VERBOSE: print("Updated '%s'" % ml_meta_out) def mk_makefile(self, out): if is_ml_enabled(): CP_CMD = "cp" if IS_WINDOWS: CP_CMD = "copy" src_dir = self.to_src_dir sub_dir = os.path.join('api', 'ml') mk_dir(os.path.join(BUILD_DIR, sub_dir)) api_src = get_component(API_COMPONENT).to_src_dir out.write('CXXFLAGS_OCAML=$(CXXFLAGS:/GL=)\n') # remove /GL; the ocaml tools don't like it. for f in filter(lambda f: f.endswith('.ml'), os.listdir(self.src_dir)): out.write('%s: %s\n' % (os.path.join(sub_dir,f),os.path.join(src_dir,f))) str = '\t%s %s %s\n' % (CP_CMD,os.path.join(src_dir,f),os.path.join(sub_dir,f)) out.write(str) for f in filter(lambda f: f.endswith('.mli'), os.listdir(self.src_dir)): out.write('%s: %s\n' % (os.path.join(sub_dir,f),os.path.join(src_dir,f))) str = '\t%s %s %s\n' % (CP_CMD,os.path.join(src_dir,f),os.path.join(sub_dir,f)) out.write(str) for f in filter(lambda f: f.endswith('.c'), os.listdir(self.src_dir)): out.write('%s: %s\n' % (os.path.join(sub_dir,f),os.path.join(src_dir,f))) str = '\t%s %s %s\n' % (CP_CMD,os.path.join(src_dir,f),os.path.join(sub_dir,f)) out.write(str) modules = ["z3enums", "z3native", "z3"] # dependencies in this order! mls = '' mlis = '' cmis = '' archives = '' for m in modules: fn = os.path.join(self.src_dir, ('%s.mli' % m)) if not os.path.exists(fn): out.write('%s.mli: %s.ml%s\n' % (os.path.join(sub_dir,m),os.path.join(sub_dir,m),mlis)) out.write('\t%s -I %s -i -c %s.ml > %s.mli\n' % (OCAMLC,sub_dir,os.path.join(sub_dir, m),os.path.join(sub_dir, m))) out.write('%s.cmi: %s.mli%s\n' % (os.path.join(sub_dir,m),os.path.join(sub_dir,m), cmis)) out.write('\t%s -I %s -c %s.mli\n' % (OCAMLC,sub_dir,os.path.join(sub_dir,m))) out.write('%s.cma: %s.ml %s.cmi%s\n' % (os.path.join(sub_dir,m),os.path.join(sub_dir,m),os.path.join(sub_dir,m), archives)) out.write('\t%s -a -o %s.ml -o %s.cma\n' % (OCAMLC,os.path.join(sub_dir,m), os.path.join(sub_dir,m))) mlis = mlis + ' ' + os.path.join(sub_dir, m) + '.mli' cmis = cmis + ' ' + os.path.join(sub_dir,m) + '.cmi' archives = archives + ' ' + os.path.join(sub_dir,m) + '.cma' mls = mls + ' ' + os.path.join(sub_dir, m) + '.ml' out.write('%s: %s %s\n' % (os.path.join(sub_dir, 'z3native_stubs$(OBJ_EXT)'), os.path.join(sub_dir, 'z3native_stubs.c'), get_component(Z3_DLL_COMPONENT).dll_name+'$(SO_EXT)')); out.write('\t$(CC) $(CXXFLAGS_OCAML) -I %s -I %s %s $(CXX_OUT_FLAG)%s$(OBJ_EXT)\n' % (OCAML_LIB, api_src, os.path.join(sub_dir, 'z3native_stubs.c'), os.path.join(sub_dir, 'z3native_stubs'))) out.write('%s: %s %s %s$(SO_EXT)' % ( os.path.join(sub_dir, "z3ml.cmxa"), cmis, archives, get_component(Z3_DLL_COMPONENT).dll_name)) out.write(' %s\n' % (os.path.join(sub_dir, 'z3native_stubs$(OBJ_EXT)'))) out.write('\tocamlmklib -o %s -I %s -ldopt \"-L. -lz3\" ' % (os.path.join(sub_dir, 'z3ml'), sub_dir)) for m in modules: out.write(' %s' % (os.path.join(sub_dir, m+'.ml'))) out.write(' %s\n' % (os.path.join(sub_dir, 'z3native_stubs$(OBJ_EXT)'))) out.write('ml: %s\n' % (os.path.join(sub_dir, 'z3ml.cmxa'))) self.mk_ml_meta(os.path.join('src/api/ml/META'), os.path.join(BUILD_DIR, sub_dir, 'META'), VER_MAJOR, VER_MINOR, VER_BUILD, VER_REVISION) if OCAMLFIND != '': out.write('\nocamlfind_install: %s %s %s\n' % ( get_component(Z3_DLL_COMPONENT).dll_name + '$(SO_EXT)', os.path.join(sub_dir, 'z3ml.cmxa'), os.path.join(sub_dir, 'META'))) out.write('\t%s remove Z3\n' % (OCAMLFIND)) out.write('\t%s install Z3 %s' % (OCAMLFIND, (os.path.join(sub_dir, 'META')))) for m in modules: out.write(' %s.cma' % (os.path.join(sub_dir, m))) out.write(' %s.cmx' % (os.path.join(sub_dir, m))) out.write(' %s.cmi' % (os.path.join(sub_dir, m))) out.write(' %s.cmo' % (os.path.join(sub_dir, m))) out.write(' %s.ml' % (os.path.join(sub_dir, m))) out.write(' %s.mli' % (os.path.join(sub_dir, m))) out.write(' %s$(OBJ_EXT)' % (os.path.join(sub_dir, m))) out.write(' %s' % ((os.path.join(sub_dir, 'z3ml$(LIB_EXT)')))) out.write(' %s' % ((os.path.join(sub_dir, 'z3ml.cma')))) out.write(' %s' % ((os.path.join(sub_dir, 'z3ml.cmxa')))) out.write(' %s' % ((os.path.join(sub_dir, 'libz3ml$(LIB_EXT)')))) out.write(' %s' % ((os.path.join(sub_dir, 'dllz3ml')))) if IS_WINDOWS: out.write('.dll') else: out.write('.so') # .so also on OSX! out.write(' ' + get_component(Z3_DLL_COMPONENT).dll_name + '$(SO_EXT)') if IS_WINDOWS: out.write(' ' + get_component(Z3_DLL_COMPONENT).dll_name + '$(LIB_EXT)') out.write('\n\n') def main_component(self): return is_ml_enabled() class ExampleComponent(Component): def __init__(self, name, path): Component.__init__(self, name, path, []) self.ex_dir = os.path.join(EXAMPLE_DIR, self.path) self.to_ex_dir = os.path.join(REV_BUILD_DIR, self.ex_dir) def is_example(self): return True class CppExampleComponent(ExampleComponent): def __init__(self, name, path): ExampleComponent.__init__(self, name, path) def compiler(self): return "$(CXX)" def src_files(self): return get_cpp_files(self.ex_dir) def mk_makefile(self, out): dll_name = get_component(Z3_DLL_COMPONENT).dll_name dll = '%s$(SO_EXT)' % dll_name exefile = '%s$(EXE_EXT)' % self.name out.write('%s: %s' % (exefile, dll)) for cppfile in self.src_files(): out.write(' ') out.write(os.path.join(self.to_ex_dir, cppfile)) out.write('\n') out.write('\t%s $(OS_DEFINES) $(EXAMP_DEBUG_FLAG) $(LINK_OUT_FLAG)%s $(LINK_FLAGS)' % (self.compiler(), exefile)) # Add include dir components out.write(' -I%s' % get_component(API_COMPONENT).to_src_dir) out.write(' -I%s' % get_component(CPP_COMPONENT).to_src_dir) for cppfile in self.src_files(): out.write(' ') out.write(os.path.join(self.to_ex_dir, cppfile)) out.write(' ') if IS_WINDOWS: out.write('%s.lib' % dll_name) else: out.write(dll) out.write(' $(LINK_EXTRA_FLAGS)\n') out.write('_ex_%s: %s\n\n' % (self.name, exefile)) class CExampleComponent(CppExampleComponent): def __init__(self, name, path): CppExampleComponent.__init__(self, name, path) def compiler(self): return "$(CC)" def src_files(self): return get_c_files(self.ex_dir) class DotNetExampleComponent(ExampleComponent): def __init__(self, name, path): ExampleComponent.__init__(self, name, path) def is_example(self): return IS_WINDOWS def mk_makefile(self, out): if DOTNET_ENABLED: dll_name = get_component(DOTNET_COMPONENT).dll_name dll = '%s.dll' % dll_name exefile = '%s$(EXE_EXT)' % self.name out.write('%s: %s' % (exefile, dll)) for csfile in get_cs_files(self.ex_dir): out.write(' ') out.write(os.path.join(self.to_ex_dir, csfile)) out.write('\n') out.write('\tcsc /out:%s /reference:%s /debug:full /reference:System.Numerics.dll' % (exefile, dll)) if VS_X64: out.write(' /platform:x64') else: out.write(' /platform:x86') for csfile in get_cs_files(self.ex_dir): out.write(' ') # HACK win_ex_dir = self.to_ex_dir.replace('/', '\\') out.write(os.path.join(win_ex_dir, csfile)) out.write('\n') out.write('_ex_%s: %s\n\n' % (self.name, exefile)) class JavaExampleComponent(ExampleComponent): def __init__(self, name, path): ExampleComponent.__init__(self, name, path) def is_example(self): return JAVA_ENABLED def mk_makefile(self, out): if JAVA_ENABLED: pkg = get_component(JAVA_COMPONENT).package_name + '.jar' out.write('JavaExample.class: %s' % (pkg)) deps = '' for jfile in get_java_files(self.ex_dir): out.write(' %s' % os.path.join(self.to_ex_dir, jfile)) if IS_WINDOWS: deps = deps.replace('/', '\\') out.write('%s\n' % deps) out.write('\t%s -cp %s ' % (JAVAC, pkg)) win_ex_dir = self.to_ex_dir for javafile in get_java_files(self.ex_dir): out.write(' ') out.write(os.path.join(win_ex_dir, javafile)) out.write(' -d .\n') out.write('_ex_%s: JavaExample.class\n\n' % (self.name)) class MLExampleComponent(ExampleComponent): def __init__(self, name, path): ExampleComponent.__init__(self, name, path) def is_example(self): return ML_ENABLED def mk_makefile(self, out): if ML_ENABLED: out.write('ml_example.byte: api/ml/z3ml.cmxa ') for mlfile in get_ml_files(self.ex_dir): out.write(' %s' % os.path.join(self.to_ex_dir, mlfile)) out.write('\n') out.write('\t%s ' % OCAMLC) if DEBUG_MODE: out.write('-g ') out.write('-custom -o ml_example.byte -I api/ml -cclib "-L. -lz3" nums.cma z3ml.cma') for mlfile in get_ml_files(self.ex_dir): out.write(' %s/%s' % (self.to_ex_dir, mlfile)) out.write('\n') out.write('ml_example$(EXE_EXT): api/ml/z3ml.cmxa ml_example.byte') for mlfile in get_ml_files(self.ex_dir): out.write(' %s' % os.path.join(self.to_ex_dir, mlfile)) out.write('\n') out.write('\t%s ' % OCAMLOPT) if DEBUG_MODE: out.write('-g ') out.write('-o ml_example$(EXE_EXT) -I api/ml -cclib "-L. -lz3" nums.cmxa z3ml.cmxa') for mlfile in get_ml_files(self.ex_dir): out.write(' %s/%s' % (self.to_ex_dir, mlfile)) out.write('\n') out.write('_ex_%s: ml_example.byte ml_example$(EXE_EXT)\n\n' % self.name) class PythonExampleComponent(ExampleComponent): def __init__(self, name, path): ExampleComponent.__init__(self, name, path) # Python examples are just placeholders, we just copy the *.py files when mk_makefile is invoked. # We don't need to include them in the :examples rule def mk_makefile(self, out): full = os.path.join(EXAMPLE_DIR, self.path) for py in filter(lambda f: f.endswith('.py'), os.listdir(full)): shutil.copyfile(os.path.join(full, py), os.path.join(BUILD_DIR, py)) if is_verbose(): print("Copied Z3Py example '%s' to '%s'" % (py, BUILD_DIR)) out.write('_ex_%s: \n\n' % self.name) def reg_component(name, c): global _Id, _Components, _ComponentNames, _Name2Component c.id = _Id _Id = _Id + 1 _Components.append(c) _ComponentNames.add(name) _Name2Component[name] = c if VERBOSE: print("New component: '%s'" % name) def add_lib(name, deps=[], path=None, includes2install=[]): c = LibComponent(name, path, deps, includes2install) reg_component(name, c) def add_hlib(name, path=None, includes2install=[]): c = HLibComponent(name, path, includes2install) reg_component(name, c) def add_exe(name, deps=[], path=None, exe_name=None, install=True): c = ExeComponent(name, exe_name, path, deps, install) reg_component(name, c) def add_extra_exe(name, deps=[], path=None, exe_name=None, install=True): c = ExtraExeComponent(name, exe_name, path, deps, install) reg_component(name, c) def add_dll(name, deps=[], path=None, dll_name=None, export_files=[], reexports=[], install=True, static=False): c = DLLComponent(name, dll_name, path, deps, export_files, reexports, install, static) reg_component(name, c) def add_dot_net_dll(name, deps=[], path=None, dll_name=None, assembly_info_dir=None): c = DotNetDLLComponent(name, dll_name, path, deps, assembly_info_dir) reg_component(name, c) def add_java_dll(name, deps=[], path=None, dll_name=None, package_name=None, manifest_file=None): c = JavaDLLComponent(name, dll_name, package_name, manifest_file, path, deps) reg_component(name, c) def add_ml_lib(name, deps=[], path=None, lib_name=None): c = MLComponent(name, lib_name, path, deps) reg_component(name, c) def add_cpp_example(name, path=None): c = CppExampleComponent(name, path) reg_component(name, c) def add_c_example(name, path=None): c = CExampleComponent(name, path) reg_component(name, c) def add_dotnet_example(name, path=None): c = DotNetExampleComponent(name, path) reg_component(name, c) def add_java_example(name, path=None): c = JavaExampleComponent(name, path) reg_component(name, c) def add_ml_example(name, path=None): c = MLExampleComponent(name, path) reg_component(name, c) def add_z3py_example(name, path=None): c = PythonExampleComponent(name, path) reg_component(name, c) def mk_config(): if ONLY_MAKEFILES: return config = open(os.path.join(BUILD_DIR, 'config.mk'), 'w') if IS_WINDOWS: config.write( 'CC=cl\n' 'CXX=cl\n' 'CXX_OUT_FLAG=/Fo\n' 'OBJ_EXT=.obj\n' 'LIB_EXT=.lib\n' 'AR=lib\n' 'AR_OUTFLAG=/OUT:\n' 'EXE_EXT=.exe\n' 'LINK=cl\n' 'LINK_OUT_FLAG=/Fe\n' 'SO_EXT=.dll\n' 'SLINK=cl\n' 'SLINK_OUT_FLAG=/Fe\n' 'OS_DEFINES=/D _WINDOWS\n') extra_opt = '' HAS_OMP = test_openmp('cl') if HAS_OMP: extra_opt = ' /openmp' else: extra_opt = ' -D_NO_OMP_' if GIT_HASH: extra_opt = ' %s /D Z3GITHASH=%s' % (extra_opt, GIT_HASH) if DEBUG_MODE: config.write( 'AR_FLAGS=/nologo\n' 'LINK_FLAGS=/nologo /MDd\n' 'SLINK_FLAGS=/nologo /LDd\n') if not VS_X64: config.write( 'CXXFLAGS=/c /Zi /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _DEBUG /D Z3DEBUG %s /D _CONSOLE /D _TRACE /D _WINDOWS /Gm- /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /analyze- /arch:SSE2\n' % extra_opt) config.write( 'LINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT\n' 'SLINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE:NO\n') else: config.write( 'CXXFLAGS=/c /Zi /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _AMD64_ /D _DEBUG /D Z3DEBUG %s /D _CONSOLE /D _TRACE /D _WINDOWS /Gm- /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /analyze-\n' % extra_opt) config.write( 'LINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT\n' 'SLINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE:NO\n') else: # Windows Release mode if OPTIMIZE: config.write('AR_FLAGS=/nologo /LTCG\n') config.write( 'LINK_FLAGS=/nologo /MD\n' 'SLINK_FLAGS=/nologo /LD\n') if TRACE: extra_opt = '%s /D _TRACE ' % extra_opt if not VS_X64: config.write( 'CXXFLAGS=/nologo /c /GL /Zi /W3 /WX- /O2 /Oy- /D _EXTERNAL_RELEASE /D WIN32 /D NDEBUG %s /D _CONSOLE /D _WINDOWS /D ASYNC_COMMANDS /Gm- /EHsc /MD /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /analyze- /arch:SSE2\n' % extra_opt) config.write( 'LINK_EXTRA_FLAGS=/link /LTCG /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT\n' 'SLINK_EXTRA_FLAGS=/link /LTCG /DEBUG /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE:NO\n') else: config.write( 'CXXFLAGS=/c /GL /Zi /nologo /W3 /WX- /O2 /D _EXTERNAL_RELEASE /D WIN32 /D NDEBUG %s /D _LIB /D _WINDOWS /D _AMD64_ /D _UNICODE /D UNICODE /Gm- /EHsc /MD /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /TP\n' % extra_opt) config.write( 'LINK_EXTRA_FLAGS=/link /LTCG /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608\n' 'SLINK_EXTRA_FLAGS=/link /LTCG /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608\n') # End of Windows VS config.mk if is_verbose(): print('64-bit: %s' % is64()) print('OpenMP: %s' % HAS_OMP) if is_java_enabled(): print('JNI Bindings: %s' % JNI_HOME) print('Java Compiler: %s' % JAVAC) if is_ml_enabled(): print('OCaml Compiler: %s' % OCAMLC) print('OCaml Native: %s' % OCAMLOPT) print('OCaml Library: %s' % OCAML_LIB) else: global CXX, CC, GMP, FOCI2, CPPFLAGS, CXXFLAGS, LDFLAGS, EXAMP_DEBUG_FLAG, FPMATH_FLAGS OS_DEFINES = "" ARITH = "internal" check_ar() CXX = find_cxx_compiler() CC = find_c_compiler() SLIBEXTRAFLAGS = '' if GPROF: CXXFLAGS = '%s -pg' % CXXFLAGS LDFLAGS = '%s -pg' % LDFLAGS 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 if FOCI2: if test_foci2(CXX,FOCI2LIB): LDFLAGS = '%s %s' % (LDFLAGS,FOCI2LIB) SLIBEXTRAFLAGS = '%s %s' % (SLIBEXTRAFLAGS,FOCI2LIB) CPPFLAGS = '%s -D_FOCI2' % CPPFLAGS else: print("FAILED\n") FOCI2 = False if GIT_HASH: CPPFLAGS = '%s -DZ3GITHASH=%s' % (CPPFLAGS, GIT_HASH) CXXFLAGS = '%s -fvisibility=hidden -c' % CXXFLAGS FPMATH = test_fpmath(CXX) CXXFLAGS = '%s %s' % (CXXFLAGS, FPMATH_FLAGS) HAS_OMP = test_openmp(CXX) if HAS_OMP: CXXFLAGS = '%s -fopenmp' % CXXFLAGS LDFLAGS = '%s -fopenmp' % LDFLAGS SLIBEXTRAFLAGS = '%s -fopenmp' % SLIBEXTRAFLAGS else: CXXFLAGS = '%s -D_NO_OMP_' % CXXFLAGS if DEBUG_MODE: CXXFLAGS = '%s -g -Wall' % CXXFLAGS EXAMP_DEBUG_FLAG = '-g' else: if GPROF: CXXFLAGS = '%s -O3 -D _EXTERNAL_RELEASE' % CXXFLAGS else: CXXFLAGS = '%s -O3 -D _EXTERNAL_RELEASE -fomit-frame-pointer' % CXXFLAGS if is_CXX_clangpp(): CXXFLAGS = '%s -Wno-unknown-pragmas -Wno-overloaded-virtual -Wno-unused-value' % CXXFLAGS sysname = os.uname()[0] if sysname == 'Darwin': SO_EXT = '.dylib' SLIBFLAGS = '-dynamiclib' elif sysname == 'Linux': CXXFLAGS = '%s -fno-strict-aliasing -D_LINUX_' % CXXFLAGS OS_DEFINES = '-D_LINUX' SO_EXT = '.so' LDFLAGS = '%s -lrt' % LDFLAGS SLIBFLAGS = '-shared' SLIBEXTRAFLAGS = '%s -lrt' % SLIBEXTRAFLAGS elif sysname == 'FreeBSD': CXXFLAGS = '%s -fno-strict-aliasing -D_FREEBSD_' % CXXFLAGS OS_DEFINES = '-D_FREEBSD_' SO_EXT = '.so' LDFLAGS = '%s -lrt' % LDFLAGS SLIBFLAGS = '-shared' SLIBEXTRAFLAGS = '%s -lrt' % SLIBEXTRAFLAGS elif sysname[:6] == 'CYGWIN': CXXFLAGS = '%s -D_CYGWIN -fno-strict-aliasing' % CXXFLAGS OS_DEFINES = '-D_CYGWIN' SO_EXT = '.dll' SLIBFLAGS = '-shared' else: raise MKException('Unsupported platform: %s' % sysname) if is64(): if sysname[:6] != 'CYGWIN': CXXFLAGS = '%s -fPIC' % CXXFLAGS CPPFLAGS = '%s -D_AMD64_' % CPPFLAGS if sysname == 'Linux': CPPFLAGS = '%s -D_USE_THREAD_LOCAL' % CPPFLAGS elif not LINUX_X64: CXXFLAGS = '%s -m32' % CXXFLAGS LDFLAGS = '%s -m32' % LDFLAGS SLIBFLAGS = '%s -m32' % SLIBFLAGS if DEBUG_MODE: CPPFLAGS = '%s -DZ3DEBUG' % CPPFLAGS if TRACE or DEBUG_MODE: CPPFLAGS = '%s -D_TRACE' % CPPFLAGS config.write('PREFIX=%s\n' % PREFIX) config.write('CC=%s\n' % CC) config.write('CXX=%s\n' % CXX) config.write('CXXFLAGS=%s %s\n' % (CPPFLAGS, CXXFLAGS)) config.write('EXAMP_DEBUG_FLAG=%s\n' % EXAMP_DEBUG_FLAG) config.write('CXX_OUT_FLAG=-o \n') config.write('OBJ_EXT=.o\n') config.write('LIB_EXT=.a\n') config.write('AR=ar\n') config.write('AR_FLAGS=rcs\n') config.write('AR_OUTFLAG=\n') config.write('EXE_EXT=\n') config.write('LINK=%s\n' % CXX) config.write('LINK_FLAGS=\n') config.write('LINK_OUT_FLAG=-o \n') config.write('LINK_EXTRA_FLAGS=-lpthread %s\n' % LDFLAGS) config.write('SO_EXT=%s\n' % SO_EXT) config.write('SLINK=%s\n' % CXX) config.write('SLINK_FLAGS=%s\n' % SLIBFLAGS) config.write('SLINK_EXTRA_FLAGS=%s\n' % SLIBEXTRAFLAGS) config.write('SLINK_OUT_FLAG=-o \n') config.write('OS_DEFINES=%s\n' % OS_DEFINES) if is_verbose(): print('Host platform: %s' % sysname) print('C++ Compiler: %s' % CXX) print('C Compiler : %s' % CC) print('Arithmetic: %s' % ARITH) print('OpenMP: %s' % HAS_OMP) print('Prefix: %s' % PREFIX) print('64-bit: %s' % is64()) print('FP math: %s' % FPMATH) if GPROF: print('gprof: enabled') print('Python version: %s' % distutils.sysconfig.get_python_version()) if is_java_enabled(): print('JNI Bindings: %s' % JNI_HOME) print('Java Compiler: %s' % JAVAC) if is_ml_enabled(): print('OCaml Compiler: %s' % OCAMLC) print('OCaml Native: %s' % OCAMLOPT) print('OCaml Library: %s' % OCAML_LIB) def mk_install(out): out.write('install: ') for c in get_components(): c.mk_install_deps(out) out.write(' ') if is_ml_enabled() and OCAMLFIND != '': out.write('ocamlfind_install') out.write('\n') out.write('\t@mkdir -p %s\n' % os.path.join('$(PREFIX)', 'bin')) out.write('\t@mkdir -p %s\n' % os.path.join('$(PREFIX)', 'include')) out.write('\t@mkdir -p %s\n' % os.path.join('$(PREFIX)', 'lib')) for c in get_components(): c.mk_install(out) out.write('\t@cp z3*.py %s\n' % PYTHON_PACKAGE_DIR) if sys.version >= "3": out.write('\t@cp %s*.pyc %s\n' % (os.path.join('__pycache__', 'z3'), os.path.join(PYTHON_PACKAGE_DIR, '__pycache__'))) else: out.write('\t@cp z3*.pyc %s\n' % PYTHON_PACKAGE_DIR) out.write('\t@echo Z3 was successfully installed.\n') if PYTHON_PACKAGE_DIR != distutils.sysconfig.get_python_lib(): if os.uname()[0] == 'Darwin': LD_LIBRARY_PATH = "DYLD_LIBRARY_PATH" else: LD_LIBRARY_PATH = "LD_LIBRARY_PATH" out.write('\t@echo Z3 shared libraries were installed at \'%s\', make sure this directory is in your %s environment variable.\n' % (os.path.join(PREFIX, 'lib'), LD_LIBRARY_PATH)) out.write('\t@echo Z3Py was installed at \'%s\', make sure this directory is in your PYTHONPATH environment variable.' % PYTHON_PACKAGE_DIR) out.write('\n') def mk_uninstall(out): out.write('uninstall:\n') for c in get_components(): c.mk_uninstall(out) out.write('\t@rm -f %s*.py\n' % os.path.join(PYTHON_PACKAGE_DIR, 'z3')) out.write('\t@rm -f %s*.pyc\n' % os.path.join(PYTHON_PACKAGE_DIR, 'z3')) out.write('\t@rm -f %s*.pyc\n' % os.path.join(PYTHON_PACKAGE_DIR, '__pycache__', 'z3')) out.write('\t@echo Z3 was successfully uninstalled.\n') out.write('\n') # Generate the Z3 makefile def mk_makefile(): mk_dir(BUILD_DIR) mk_config() if VERBOSE: print("Writing %s" % os.path.join(BUILD_DIR, 'Makefile')) out = open(os.path.join(BUILD_DIR, 'Makefile'), 'w') out.write('# Automatically generated file.\n') out.write('include config.mk\n') # Generate :all rule out.write('all:') for c in get_components(): if c.main_component(): out.write(' %s' % c.name) out.write('\n\t@echo Z3 was successfully built.\n') out.write("\t@echo \"Z3Py scripts can already be executed in the \'%s\' directory.\"\n" % BUILD_DIR) out.write("\t@echo \"Z3Py scripts stored in arbitrary directories can be also executed if \'%s\' directory is added to the PYTHONPATH environment variable.\"\n" % BUILD_DIR) if not IS_WINDOWS: out.write("\t@echo Use the following command to install Z3 at prefix $(PREFIX).\n") out.write('\t@echo " sudo make install"\n') # Generate :examples rule out.write('examples:') for c in get_components(): if c.is_example(): out.write(' _ex_%s' % c.name) out.write('\n\t@echo Z3 examples were successfully built.\n') # Generate components for c in get_components(): c.mk_makefile(out) # Generate install/uninstall rules if not WINDOWS if not IS_WINDOWS: mk_install(out) mk_uninstall(out) # Finalize if VERBOSE: print("Makefile was successfully generated.") if not IS_WINDOWS: print(" python packages dir: %s" % PYTHON_PACKAGE_DIR) if DEBUG_MODE: print(" compilation mode: Debug") else: print(" compilation mode: Release") if IS_WINDOWS: if VS_X64: print(" platform: x64\n") print("To build Z3, open a [Visual Studio x64 Command Prompt], then") else: print(" platform: x86") print("To build Z3, open a [Visual Studio Command Prompt], then") print("type 'cd %s && nmake'\n" % os.path.join(os.getcwd(), BUILD_DIR)) print('Remark: to open a Visual Studio Command Prompt, go to: "Start > All Programs > Visual Studio > Visual Studio Tools"') else: print("Type 'cd %s; make' to build Z3" % BUILD_DIR) # Generate automatically generated source code def mk_auto_src(): if not ONLY_MAKEFILES: exec_pyg_scripts() mk_pat_db() mk_all_install_tactic_cpps() mk_all_mem_initializer_cpps() mk_all_gparams_register_modules() UINT = 0 BOOL = 1 DOUBLE = 2 STRING = 3 SYMBOL = 4 UINT_MAX = 4294967295 CURR_PYG = None def get_curr_pyg(): return CURR_PYG TYPE2CPK = { UINT : 'CPK_UINT', BOOL : 'CPK_BOOL', DOUBLE : 'CPK_DOUBLE', STRING : 'CPK_STRING', SYMBOL : 'CPK_SYMBOL' } TYPE2CTYPE = { UINT : 'unsigned', BOOL : 'bool', DOUBLE : 'double', STRING : 'char const *', SYMBOL : 'symbol' } TYPE2GETTER = { UINT : 'get_uint', BOOL : 'get_bool', DOUBLE : 'get_double', STRING : 'get_str', SYMBOL : 'get_sym' } def pyg_default(p): if p[1] == BOOL: if p[2]: return "true" else: return "false" return p[2] def pyg_default_as_c_literal(p): if p[1] == BOOL: if p[2]: return "true" else: return "false" elif p[1] == STRING: return '"%s"' % p[2] elif p[1] == SYMBOL: return 'symbol("%s")' % p[2] elif p[1] == UINT: return '%su' % p[2] else: return p[2] def to_c_method(s): return s.replace('.', '_') def def_module_params(module_name, export, params, class_name=None, description=None): pyg = get_curr_pyg() dirname = os.path.split(get_curr_pyg())[0] if class_name == None: class_name = '%s_params' % module_name hpp = os.path.join(dirname, '%s.hpp' % class_name) out = open(hpp, 'w') out.write('// Automatically generated file\n') out.write('#ifndef __%s_HPP_\n' % class_name.upper()) out.write('#define __%s_HPP_\n' % class_name.upper()) out.write('#include"params.h"\n') if export: out.write('#include"gparams.h"\n') out.write('struct %s {\n' % class_name) out.write(' params_ref const & p;\n') if export: out.write(' params_ref g;\n') out.write(' %s(params_ref const & _p = params_ref::get_empty()):\n' % class_name) out.write(' p(_p)') if export: out.write(', g(gparams::get_module("%s"))' % module_name) out.write(' {}\n') out.write(' static void collect_param_descrs(param_descrs & d) {\n') for param in params: out.write(' d.insert("%s", %s, "%s", "%s","%s");\n' % (param[0], TYPE2CPK[param[1]], param[3], pyg_default(param), module_name)) out.write(' }\n') if export: out.write(' /*\n') out.write(" REG_MODULE_PARAMS('%s', '%s::collect_param_descrs')\n" % (module_name, class_name)) if description != None: out.write(" REG_MODULE_DESCRIPTION('%s', '%s')\n" % (module_name, description)) out.write(' */\n') # Generated accessors for param in params: if export: out.write(' %s %s() const { return p.%s("%s", g, %s); }\n' % (TYPE2CTYPE[param[1]], to_c_method(param[0]), TYPE2GETTER[param[1]], param[0], pyg_default_as_c_literal(param))) else: out.write(' %s %s() const { return p.%s("%s", %s); }\n' % (TYPE2CTYPE[param[1]], to_c_method(param[0]), TYPE2GETTER[param[1]], param[0], pyg_default_as_c_literal(param))) out.write('};\n') out.write('#endif\n') if is_verbose(): print("Generated '%s'" % hpp) def max_memory_param(): return ('max_memory', UINT, UINT_MAX, 'maximum amount of memory in megabytes') def max_steps_param(): return ('max_steps', UINT, UINT_MAX, 'maximum number of steps') PYG_GLOBALS = { 'UINT' : UINT, 'BOOL' : BOOL, 'DOUBLE' : DOUBLE, 'STRING' : STRING, 'SYMBOL' : SYMBOL, 'UINT_MAX' : UINT_MAX, 'max_memory_param' : max_memory_param, 'max_steps_param' : max_steps_param, 'def_module_params' : def_module_params } def _execfile(file, globals=globals(), locals=locals()): if sys.version < "2.7": execfile(file, globals, locals) else: with open(file, "r") as fh: exec(fh.read()+"\n", globals, locals) # Execute python auxiliary scripts that generate extra code for Z3. def exec_pyg_scripts(): global CURR_PYG for root, dirs, files in os.walk('src'): for f in files: if f.endswith('.pyg'): script = os.path.join(root, f) CURR_PYG = script _execfile(script, PYG_GLOBALS) # TODO: delete after src/ast/pattern/expr_pattern_match # database.smt ==> database.h def mk_pat_db(): c = get_component(PATTERN_COMPONENT) fin = open(os.path.join(c.src_dir, 'database.smt2'), 'r') fout = open(os.path.join(c.src_dir, 'database.h'), 'w') fout.write('static char const g_pattern_database[] =\n') for line in fin: fout.write('"%s\\n"\n' % line.strip('\n')) fout.write(';\n') if VERBOSE: print("Generated '%s'" % os.path.join(c.src_dir, 'database.h')) # Update version numbers def update_version(): major = VER_MAJOR minor = VER_MINOR build = VER_BUILD revision = VER_REVISION if major == None or minor == None or build == None or revision == None: raise MKException("set_version(major, minor, build, revision) must be used before invoking update_version()") if not ONLY_MAKEFILES: mk_version_dot_h(major, minor, build, revision) mk_all_assembly_infos(major, minor, build, revision) mk_def_files() # Update files with the version number def mk_version_dot_h(major, minor, build, revision): c = get_component(UTIL_COMPONENT) fout = open(os.path.join(c.src_dir, 'version.h'), 'w') fout.write('// automatically generated file.\n') fout.write('#define Z3_MAJOR_VERSION %s\n' % major) fout.write('#define Z3_MINOR_VERSION %s\n' % minor) fout.write('#define Z3_BUILD_NUMBER %s\n' % build) fout.write('#define Z3_REVISION_NUMBER %s\n' % revision) if VERBOSE: print("Generated '%s'" % os.path.join(c.src_dir, 'version.h')) # Generate AssemblyInfo.cs files with the right version numbers by using AssemblyInfo files as a template def mk_all_assembly_infos(major, minor, build, revision): for c in get_components(): if c.has_assembly_info(): assembly = os.path.join(c.src_dir, c.assembly_info_dir, 'AssemblyInfo') if os.path.exists(assembly): # It is a CS file mk_assembly_info_version(assembly, major, minor, build, revision) else: raise MKException("Failed to find assembly info file 'AssemblyInfo' at '%s'" % os.path.join(c.src_dir, c.assembly_info_dir)) # Generate version number in the given 'AssemblyInfo.cs' file using 'AssemblyInfo' as a template. def mk_assembly_info_version(assemblyinfo, major, minor, build, revision): ver_pat = re.compile('[assembly: AssemblyVersion\("[\.\d]*"\) *') fver_pat = re.compile('[assembly: AssemblyFileVersion\("[\.\d]*"\) *') fin = open(assemblyinfo, 'r') tmp = '%s.cs' % assemblyinfo fout = open(tmp, 'w') num_updates = 0 for line in fin: if ver_pat.match(line): fout.write('[assembly: AssemblyVersion("%s.%s.%s.%s")]\n' % (major, minor, build, revision)) num_updates = num_updates + 1 elif fver_pat.match(line): fout.write('[assembly: AssemblyFileVersion("%s.%s.%s.%s")]\n' % (major, minor, build, revision)) num_updates = num_updates + 1 else: fout.write(line) # if VERBOSE: # print("%s version numbers updated at '%s'" % (num_updates, assemblyinfo)) assert num_updates == 2, "unexpected number of version number updates" fin.close() fout.close() if VERBOSE: print("Updated '%s'" % assemblyinfo) ADD_TACTIC_DATA=[] ADD_PROBE_DATA=[] def ADD_TACTIC(name, descr, cmd): global ADD_TACTIC_DATA ADD_TACTIC_DATA.append((name, descr, cmd)) def ADD_PROBE(name, descr, cmd): global ADD_PROBE_DATA ADD_PROBE_DATA.append((name, descr, cmd)) # Generate an install_tactics.cpp at path. # This file implements the procedure # void install_tactics(tactic_manager & ctx) # It installs all tactics in the given component (name) list cnames # The procedure looks for ADD_TACTIC commands in the .h files of these components. def mk_install_tactic_cpp(cnames, path): global ADD_TACTIC_DATA, ADD_PROBE_DATA ADD_TACTIC_DATA = [] ADD_PROBE_DATA = [] fullname = os.path.join(path, 'install_tactic.cpp') fout = open(fullname, 'w') fout.write('// Automatically generated file.\n') fout.write('#include"tactic.h"\n') fout.write('#include"tactic_cmds.h"\n') fout.write('#include"cmd_context.h"\n') tactic_pat = re.compile('[ \t]*ADD_TACTIC\(.*\)') probe_pat = re.compile('[ \t]*ADD_PROBE\(.*\)') for cname in cnames: c = get_component(cname) h_files = filter(lambda f: f.endswith('.h') or f.endswith('.hpp'), os.listdir(c.src_dir)) for h_file in h_files: added_include = False fin = open(os.path.join(c.src_dir, h_file), 'r') for line in fin: if tactic_pat.match(line): if not added_include: added_include = True fout.write('#include"%s"\n' % h_file) try: exec(line.strip('\n '), globals()) except: raise MKException("Failed processing ADD_TACTIC command at '%s'\n%s" % (fullname, line)) if probe_pat.match(line): if not added_include: added_include = True fout.write('#include"%s"\n' % h_file) try: exec(line.strip('\n '), globals()) except: raise MKException("Failed processing ADD_PROBE command at '%s'\n%s" % (fullname, line)) # First pass will just generate the tactic factories idx = 0 for data in ADD_TACTIC_DATA: fout.write('MK_SIMPLE_TACTIC_FACTORY(__Z3_local_factory_%s, %s);\n' % (idx, data[2])) idx = idx + 1 fout.write('#define ADD_TACTIC_CMD(NAME, DESCR, FACTORY) ctx.insert(alloc(tactic_cmd, symbol(NAME), DESCR, alloc(FACTORY)))\n') fout.write('#define ADD_PROBE(NAME, DESCR, PROBE) ctx.insert(alloc(probe_info, symbol(NAME), DESCR, PROBE))\n') fout.write('void install_tactics(tactic_manager & ctx) {\n') idx = 0 for data in ADD_TACTIC_DATA: fout.write(' ADD_TACTIC_CMD("%s", "%s", __Z3_local_factory_%s);\n' % (data[0], data[1], idx)) idx = idx + 1 for data in ADD_PROBE_DATA: fout.write(' ADD_PROBE("%s", "%s", %s);\n' % data) fout.write('}\n') if VERBOSE: print("Generated '%s'" % fullname) def mk_all_install_tactic_cpps(): if not ONLY_MAKEFILES: for c in get_components(): if c.require_install_tactics(): cnames = [] cnames.extend(c.deps) cnames.append(c.name) mk_install_tactic_cpp(cnames, c.src_dir) # Generate an mem_initializer.cpp at path. # This file implements the procedures # void mem_initialize() # void mem_finalize() # These procedures are invoked by the Z3 memory_manager def mk_mem_initializer_cpp(cnames, path): initializer_cmds = [] finalizer_cmds = [] fullname = os.path.join(path, 'mem_initializer.cpp') fout = open(fullname, 'w') fout.write('// Automatically generated file.\n') initializer_pat = re.compile('[ \t]*ADD_INITIALIZER\(\'([^\']*)\'\)') # ADD_INITIALIZER with priority initializer_prio_pat = re.compile('[ \t]*ADD_INITIALIZER\(\'([^\']*)\',[ \t]*(-?[0-9]*)\)') finalizer_pat = re.compile('[ \t]*ADD_FINALIZER\(\'([^\']*)\'\)') for cname in cnames: c = get_component(cname) h_files = filter(lambda f: f.endswith('.h') or f.endswith('.hpp'), os.listdir(c.src_dir)) for h_file in h_files: added_include = False fin = open(os.path.join(c.src_dir, h_file), 'r') for line in fin: m = initializer_pat.match(line) if m: if not added_include: added_include = True fout.write('#include"%s"\n' % h_file) initializer_cmds.append((m.group(1), 0)) m = initializer_prio_pat.match(line) if m: if not added_include: added_include = True fout.write('#include"%s"\n' % h_file) initializer_cmds.append((m.group(1), int(m.group(2)))) m = finalizer_pat.match(line) if m: if not added_include: added_include = True fout.write('#include"%s"\n' % h_file) finalizer_cmds.append(m.group(1)) initializer_cmds.sort(key=lambda tup: tup[1]) fout.write('void mem_initialize() {\n') for (cmd, prio) in initializer_cmds: fout.write(cmd) fout.write('\n') fout.write('}\n') fout.write('void mem_finalize() {\n') for cmd in finalizer_cmds: fout.write(cmd) fout.write('\n') fout.write('}\n') if VERBOSE: print("Generated '%s'" % fullname) def mk_all_mem_initializer_cpps(): if not ONLY_MAKEFILES: for c in get_components(): if c.require_mem_initializer(): cnames = [] cnames.extend(c.deps) cnames.append(c.name) mk_mem_initializer_cpp(cnames, c.src_dir) # Generate an mem_initializer.cpp at path. # This file implements the procedure # void gparams_register_modules() # This procedure is invoked by gparams::init() def mk_gparams_register_modules(cnames, path): cmds = [] mod_cmds = [] mod_descrs = [] fullname = os.path.join(path, 'gparams_register_modules.cpp') fout = open(fullname, 'w') fout.write('// Automatically generated file.\n') fout.write('#include"gparams.h"\n') reg_pat = re.compile('[ \t]*REG_PARAMS\(\'([^\']*)\'\)') reg_mod_pat = re.compile('[ \t]*REG_MODULE_PARAMS\(\'([^\']*)\', *\'([^\']*)\'\)') reg_mod_descr_pat = re.compile('[ \t]*REG_MODULE_DESCRIPTION\(\'([^\']*)\', *\'([^\']*)\'\)') for cname in cnames: c = get_component(cname) h_files = filter(lambda f: f.endswith('.h') or f.endswith('.hpp'), os.listdir(c.src_dir)) for h_file in h_files: added_include = False fin = open(os.path.join(c.src_dir, h_file), 'r') for line in fin: m = reg_pat.match(line) if m: if not added_include: added_include = True fout.write('#include"%s"\n' % h_file) cmds.append((m.group(1))) m = reg_mod_pat.match(line) if m: if not added_include: added_include = True fout.write('#include"%s"\n' % h_file) mod_cmds.append((m.group(1), m.group(2))) m = reg_mod_descr_pat.match(line) if m: mod_descrs.append((m.group(1), m.group(2))) fout.write('void gparams_register_modules() {\n') for code in cmds: fout.write('{ param_descrs d; %s(d); gparams::register_global(d); }\n' % code) for (mod, code) in mod_cmds: fout.write('{ param_descrs * d = alloc(param_descrs); %s(*d); gparams::register_module("%s", d); }\n' % (code, mod)) for (mod, descr) in mod_descrs: fout.write('gparams::register_module_descr("%s", "%s");\n' % (mod, descr)) fout.write('}\n') if VERBOSE: print("Generated '%s'" % fullname) def mk_all_gparams_register_modules(): if not ONLY_MAKEFILES: for c in get_components(): if c.require_mem_initializer(): cnames = [] cnames.extend(c.deps) cnames.append(c.name) mk_gparams_register_modules(cnames, c.src_dir) # Generate a .def based on the files at c.export_files slot. def mk_def_file(c): pat1 = re.compile(".*Z3_API.*") defname = '%s.def' % os.path.join(c.src_dir, c.name) fout = open(defname, 'w') fout.write('LIBRARY "%s"\nEXPORTS\n' % c.dll_name) num = 1 for dot_h in c.export_files: dot_h_c = c.find_file(dot_h, c.name) api = open(os.path.join(dot_h_c.src_dir, dot_h), 'r') for line in api: m = pat1.match(line) if m: words = re.split('\W+', line) i = 0 for w in words: if w == 'Z3_API': f = words[i+1] fout.write('\t%s @%s\n' % (f, num)) i = i + 1 num = num + 1 if VERBOSE: print("Generated '%s'" % defname) def mk_def_files(): if not ONLY_MAKEFILES: for c in get_components(): if c.require_def_file(): mk_def_file(c) def cp_z3py_to_build(): mk_dir(BUILD_DIR) # Erase existing .pyc files for root, dirs, files in os.walk(Z3PY_SRC_DIR): for f in files: if f.endswith('.pyc'): rmf(os.path.join(root, f)) # Compile Z3Py files if compileall.compile_dir(Z3PY_SRC_DIR, force=1) != 1: raise MKException("failed to compile Z3Py sources") # Copy sources to build for py in filter(lambda f: f.endswith('.py'), os.listdir(Z3PY_SRC_DIR)): shutil.copyfile(os.path.join(Z3PY_SRC_DIR, py), os.path.join(BUILD_DIR, py)) if is_verbose(): print("Copied '%s'" % py) # Python 2.x support for pyc in filter(lambda f: f.endswith('.pyc'), os.listdir(Z3PY_SRC_DIR)): shutil.copyfile(os.path.join(Z3PY_SRC_DIR, pyc), os.path.join(BUILD_DIR, pyc)) if is_verbose(): print("Generated '%s'" % pyc) # Python 3.x support src_pycache = os.path.join(Z3PY_SRC_DIR, '__pycache__') if os.path.exists(src_pycache): for pyc in filter(lambda f: f.endswith('.pyc'), os.listdir(src_pycache)): target_pycache = os.path.join(BUILD_DIR, '__pycache__') mk_dir(target_pycache) shutil.copyfile(os.path.join(src_pycache, pyc), os.path.join(target_pycache, pyc)) if is_verbose(): print("Generated '%s'" % pyc) def mk_bindings(api_files): if not ONLY_MAKEFILES: mk_z3consts_py(api_files) mk_z3consts_dotnet(api_files) new_api_files = [] api = get_component(API_COMPONENT) for api_file in api_files: api_file_path = api.find_file(api_file, api.name) new_api_files.append(os.path.join(api_file_path.src_dir, api_file)) g = globals() g["API_FILES"] = new_api_files if is_java_enabled(): check_java() mk_z3consts_java(api_files) _execfile(os.path.join('scripts', 'update_api.py'), g) # HACK cp_z3py_to_build() if is_ml_enabled(): check_ml() mk_z3consts_ml(api_files) # Extract enumeration types from API files, and add python definitions. def mk_z3consts_py(api_files): if Z3PY_SRC_DIR == None: raise MKException("You must invoke set_z3py_dir(path):") blank_pat = re.compile("^ *$") comment_pat = re.compile("^ *//.*$") typedef_pat = re.compile("typedef enum *") typedef2_pat = re.compile("typedef enum { *") openbrace_pat = re.compile("{ *") closebrace_pat = re.compile("}.*;") z3consts = open(os.path.join(Z3PY_SRC_DIR, 'z3consts.py'), 'w') z3consts.write('# Automatically generated file\n\n') api_dll = get_component(Z3_DLL_COMPONENT) for api_file in api_files: api_file_c = api_dll.find_file(api_file, api_dll.name) api_file = os.path.join(api_file_c.src_dir, api_file) api = open(api_file, 'r') SEARCHING = 0 FOUND_ENUM = 1 IN_ENUM = 2 mode = SEARCHING decls = {} idx = 0 linenum = 1 for line in api: m1 = blank_pat.match(line) m2 = comment_pat.match(line) if m1 or m2: # skip blank lines and comments linenum = linenum + 1 elif mode == SEARCHING: m = typedef_pat.match(line) if m: mode = FOUND_ENUM m = typedef2_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 elif mode == FOUND_ENUM: m = openbrace_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 else: assert False, "Invalid %s, line: %s" % (api_file, linenum) else: assert mode == IN_ENUM words = re.split('[^\-a-zA-Z0-9_]+', line) m = closebrace_pat.match(line) if m: name = words[1] z3consts.write('# enum %s\n' % name) for k in decls: i = decls[k] z3consts.write('%s = %s\n' % (k, i)) z3consts.write('\n') mode = SEARCHING else: if words[2] != '': if len(words[2]) > 1 and words[2][1] == 'x': idx = int(words[2], 16) else: idx = int(words[2]) decls[words[1]] = idx idx = idx + 1 linenum = linenum + 1 if VERBOSE: print("Generated '%s'" % os.path.join(Z3PY_SRC_DIR, 'z3consts.py')) # Extract enumeration types from z3_api.h, and add .Net definitions def mk_z3consts_dotnet(api_files): blank_pat = re.compile("^ *$") comment_pat = re.compile("^ *//.*$") typedef_pat = re.compile("typedef enum *") typedef2_pat = re.compile("typedef enum { *") openbrace_pat = re.compile("{ *") closebrace_pat = re.compile("}.*;") dotnet = get_component(DOTNET_COMPONENT) DeprecatedEnums = [ 'Z3_search_failure' ] z3consts = open(os.path.join(dotnet.src_dir, 'Enumerations.cs'), 'w') z3consts.write('// Automatically generated file\n\n') z3consts.write('using System;\n\n' '#pragma warning disable 1591\n\n' 'namespace Microsoft.Z3\n' '{\n'); for api_file in api_files: api_file_c = dotnet.find_file(api_file, dotnet.name) api_file = os.path.join(api_file_c.src_dir, api_file) api = open(api_file, 'r') SEARCHING = 0 FOUND_ENUM = 1 IN_ENUM = 2 mode = SEARCHING decls = {} idx = 0 linenum = 1 for line in api: m1 = blank_pat.match(line) m2 = comment_pat.match(line) if m1 or m2: # skip blank lines and comments linenum = linenum + 1 elif mode == SEARCHING: m = typedef_pat.match(line) if m: mode = FOUND_ENUM m = typedef2_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 elif mode == FOUND_ENUM: m = openbrace_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 else: assert False, "Invalid %s, line: %s" % (api_file, linenum) else: assert mode == IN_ENUM words = re.split('[^\-a-zA-Z0-9_]+', line) m = closebrace_pat.match(line) if m: name = words[1] if name not in DeprecatedEnums: z3consts.write(' /// %s\n' % name) z3consts.write(' public enum %s {\n' % name) z3consts.write for k in decls: i = decls[k] z3consts.write(' %s = %s,\n' % (k, i)) z3consts.write(' }\n\n') mode = SEARCHING else: if words[2] != '': if len(words[2]) > 1 and words[2][1] == 'x': idx = int(words[2], 16) else: idx = int(words[2]) decls[words[1]] = idx idx = idx + 1 linenum = linenum + 1 z3consts.write('}\n'); if VERBOSE: print("Generated '%s'" % os.path.join(dotnet.src_dir, 'Enumerations.cs')) # Extract enumeration types from z3_api.h, and add Java definitions def mk_z3consts_java(api_files): blank_pat = re.compile("^ *$") comment_pat = re.compile("^ *//.*$") typedef_pat = re.compile("typedef enum *") typedef2_pat = re.compile("typedef enum { *") openbrace_pat = re.compile("{ *") closebrace_pat = re.compile("}.*;") java = get_component(JAVA_COMPONENT) DeprecatedEnums = [ 'Z3_search_failure' ] gendir = os.path.join(java.src_dir, "enumerations") if not os.path.exists(gendir): os.mkdir(gendir) for api_file in api_files: api_file_c = java.find_file(api_file, java.name) api_file = os.path.join(api_file_c.src_dir, api_file) api = open(api_file, 'r') SEARCHING = 0 FOUND_ENUM = 1 IN_ENUM = 2 mode = SEARCHING decls = {} idx = 0 linenum = 1 for line in api: m1 = blank_pat.match(line) m2 = comment_pat.match(line) if m1 or m2: # skip blank lines and comments linenum = linenum + 1 elif mode == SEARCHING: m = typedef_pat.match(line) if m: mode = FOUND_ENUM m = typedef2_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 elif mode == FOUND_ENUM: m = openbrace_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 else: assert False, "Invalid %s, line: %s" % (api_file, linenum) else: assert mode == IN_ENUM words = re.split('[^\-a-zA-Z0-9_]+', line) m = closebrace_pat.match(line) if m: name = words[1] if name not in DeprecatedEnums: efile = open('%s.java' % os.path.join(gendir, name), 'w') efile.write('/**\n * Automatically generated file\n **/\n\n') efile.write('package %s.enumerations;\n\n' % java.package_name); efile.write('/**\n') efile.write(' * %s\n' % name) efile.write(' **/\n') efile.write('public enum %s {\n' % name) efile.write first = True for k in decls: i = decls[k] if first: first = False else: efile.write(',\n') efile.write(' %s (%s)' % (k, i)) efile.write(";\n") efile.write('\n private final int intValue;\n\n') efile.write(' %s(int v) {\n' % name) efile.write(' this.intValue = v;\n') efile.write(' }\n\n') efile.write(' public static final %s fromInt(int v) {\n' % name) efile.write(' for (%s k: values()) \n' % name) efile.write(' if (k.intValue == v) return k;\n') efile.write(' return values()[0];\n') efile.write(' }\n\n') efile.write(' public final int toInt() { return this.intValue; }\n') # efile.write(';\n %s(int v) {}\n' % name) efile.write('}\n\n') efile.close() mode = SEARCHING else: if words[2] != '': if len(words[2]) > 1 and words[2][1] == 'x': idx = int(words[2], 16) else: idx = int(words[2]) decls[words[1]] = idx idx = idx + 1 linenum = linenum + 1 if VERBOSE: print("Generated '%s'" % ('%s' % gendir)) # Extract enumeration types from z3_api.h, and add ML definitions def mk_z3consts_ml(api_files): blank_pat = re.compile("^ *$") comment_pat = re.compile("^ *//.*$") typedef_pat = re.compile("typedef enum *") typedef2_pat = re.compile("typedef enum { *") openbrace_pat = re.compile("{ *") closebrace_pat = re.compile("}.*;") ml = get_component(ML_COMPONENT) DeprecatedEnums = [ 'Z3_search_failure' ] gendir = ml.src_dir if not os.path.exists(gendir): os.mkdir(gendir) efile = open('%s.ml' % os.path.join(gendir, "z3enums"), 'w') efile.write('(* Automatically generated file *)\n\n') efile.write('(** The enumeration types of Z3. *)\n\n') for api_file in api_files: api_file_c = ml.find_file(api_file, ml.name) api_file = os.path.join(api_file_c.src_dir, api_file) api = open(api_file, 'r') SEARCHING = 0 FOUND_ENUM = 1 IN_ENUM = 2 mode = SEARCHING decls = {} idx = 0 linenum = 1 for line in api: m1 = blank_pat.match(line) m2 = comment_pat.match(line) if m1 or m2: # skip blank lines and comments linenum = linenum + 1 elif mode == SEARCHING: m = typedef_pat.match(line) if m: mode = FOUND_ENUM m = typedef2_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 elif mode == FOUND_ENUM: m = openbrace_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 else: assert False, "Invalid %s, line: %s" % (api_file, linenum) else: assert mode == IN_ENUM words = re.split('[^\-a-zA-Z0-9_]+', line) m = closebrace_pat.match(line) if m: name = words[1] if name not in DeprecatedEnums: efile.write('(** %s *)\n' % name[3:]) efile.write('type %s =\n' % name[3:]) # strip Z3_ for k, i in decls.items(): efile.write(' | %s \n' % k[3:]) # strip Z3_ efile.write('\n') efile.write('(** Convert %s to int*)\n' % name[3:]) efile.write('let int_of_%s x : int =\n' % (name[3:])) # strip Z3_ efile.write(' match x with\n') for k, i in decls.items(): efile.write(' | %s -> %d\n' % (k[3:], i)) efile.write('\n') efile.write('(** Convert int to %s*)\n' % name[3:]) efile.write('let %s_of_int x : %s =\n' % (name[3:],name[3:])) # strip Z3_ efile.write(' match x with\n') for k, i in decls.items(): efile.write(' | %d -> %s\n' % (i, k[3:])) # use Z3.Exception? efile.write(' | _ -> raise (Failure "undefined enum value")\n\n') mode = SEARCHING else: if words[2] != '': if len(words[2]) > 1 and words[2][1] == 'x': idx = int(words[2], 16) else: idx = int(words[2]) decls[words[1]] = idx idx = idx + 1 linenum = linenum + 1 if VERBOSE: print ('Generated "%s/z3enums.ml"' % ('%s' % gendir)) efile = open('%s.mli' % os.path.join(gendir, "z3enums"), 'w') efile.write('(* Automatically generated file *)\n\n') efile.write('(** The enumeration types of Z3. *)\n\n') for api_file in api_files: api_file_c = ml.find_file(api_file, ml.name) api_file = os.path.join(api_file_c.src_dir, api_file) api = open(api_file, 'r') SEARCHING = 0 FOUND_ENUM = 1 IN_ENUM = 2 mode = SEARCHING decls = {} idx = 0 linenum = 1 for line in api: m1 = blank_pat.match(line) m2 = comment_pat.match(line) if m1 or m2: # skip blank lines and comments linenum = linenum + 1 elif mode == SEARCHING: m = typedef_pat.match(line) if m: mode = FOUND_ENUM m = typedef2_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 elif mode == FOUND_ENUM: m = openbrace_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 else: assert False, "Invalid %s, line: %s" % (api_file, linenum) else: assert mode == IN_ENUM words = re.split('[^\-a-zA-Z0-9_]+', line) m = closebrace_pat.match(line) if m: name = words[1] if name not in DeprecatedEnums: efile.write('(** %s *)\n' % name[3:]) efile.write('type %s =\n' % name[3:]) # strip Z3_ for k, i in decls.items(): efile.write(' | %s \n' % k[3:]) # strip Z3_ efile.write('\n') efile.write('(** Convert %s to int*)\n' % name[3:]) efile.write('val int_of_%s : %s -> int\n' % (name[3:], name[3:])) # strip Z3_ efile.write('(** Convert int to %s*)\n' % name[3:]) efile.write('val %s_of_int : int -> %s\n' % (name[3:],name[3:])) # strip Z3_ efile.write('\n') mode = SEARCHING else: if words[2] != '': if len(words[2]) > 1 and words[2][1] == 'x': idx = int(words[2], 16) else: idx = int(words[2]) decls[words[1]] = idx idx = idx + 1 linenum = linenum + 1 if VERBOSE: print ('Generated "%s/z3enums.mli"' % ('%s' % gendir)) def mk_gui_str(id): return '4D2F40D8-E5F9-473B-B548-%012d' % id def mk_vs_proj(name, components): if not VS_PROJ: return proj_name = '%s.vcxproj' % os.path.join(BUILD_DIR, name) modes=['Debug', 'Release'] PLATFORMS=['Win32'] f = open(proj_name, 'w') f.write('\n') f.write('\n') f.write(' \n') f.write(' \n') f.write(' Debug\n') f.write(' Win32\n') f.write(' \n') f.write(' \n') f.write(' Release\n') f.write(' Win32\n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' {%s}\n' % mk_gui_str(0)) f.write(' %s\n' % name) f.write(' Win32Proj\n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' Application\n') f.write(' Unicode\n') f.write(' false\n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' $(SolutionDir)$(Configuration)\\n') f.write(' %s\n' % name) f.write(' .exe\n') f.write(' $(SolutionDir)$(Configuration)\\n') f.write(' %s\n' % name) f.write(' .exe\n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' Disabled\n') f.write(' WIN32;_DEBUG;Z3DEBUG;_TRACE;_MP_INTERNAL;_WINDOWS;%(PreprocessorDefinitions)\n') if VS_PAR: f.write(' false\n') f.write(' true\n') else: f.write(' true\n') f.write(' EnableFastChecks\n') f.write(' Level3\n') f.write(' MultiThreadedDebugDLL\n') f.write(' true\n') f.write(' ProgramDatabase\n') f.write(' ') deps = find_all_deps(name, components) first = True for dep in deps: if first: first = False else: f.write(';') f.write(get_component(dep).to_src_dir) f.write('\n') f.write(' \n') f.write(' \n') f.write(' $(OutDir)%s.exe\n' % name) f.write(' true\n') f.write(' Console\n') f.write(' 8388608\n') f.write(' false\n') f.write(' \n') f.write(' \n') f.write(' MachineX86\n') f.write(' %(AdditionalLibraryDirectories)\n') f.write('psapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)\n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' Disabled\n') f.write(' WIN32;_NDEBUG;_MP_INTERNAL;_WINDOWS;%(PreprocessorDefinitions)\n') if VS_PAR: f.write(' false\n') f.write(' true\n') else: f.write(' true\n') f.write(' EnableFastChecks\n') f.write(' Level3\n') f.write(' MultiThreadedDLL\n') f.write(' true\n') f.write(' ProgramDatabase\n') f.write(' ') deps = find_all_deps(name, components) first = True for dep in deps: if first: first = False else: f.write(';') f.write(get_component(dep).to_src_dir) f.write('\n') f.write(' \n') f.write(' \n') f.write(' $(OutDir)%s.exe\n' % name) f.write(' true\n') f.write(' Console\n') f.write(' 8388608\n') f.write(' false\n') f.write(' \n') f.write(' \n') f.write(' MachineX86\n') f.write(' %(AdditionalLibraryDirectories)\n') f.write('psapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)\n') f.write(' \n') f.write(' \n') f.write(' \n') for dep in deps: dep = get_component(dep) for cpp in filter(lambda f: f.endswith('.cpp'), os.listdir(dep.src_dir)): f.write(' \n' % os.path.join(dep.to_src_dir, cpp)) f.write(' \n') f.write(' \n') f.write(' \n') f.write(' \n') f.write('\n') if is_verbose(): print("Generated '%s'" % proj_name) def mk_win_dist(build_path, dist_path): for c in get_components(): c.mk_win_dist(build_path, dist_path) # Add Z3Py to bin directory print("Adding to %s\n" % dist_path) for pyc in filter(lambda f: f.endswith('.pyc') or f.endswith('.py'), os.listdir(build_path)): shutil.copy(os.path.join(build_path, pyc), os.path.join(dist_path, 'bin', pyc)) def mk_unix_dist(build_path, dist_path): for c in get_components(): c.mk_unix_dist(build_path, dist_path) # Add Z3Py to bin directory for pyc in filter(lambda f: f.endswith('.pyc') or f.endswith('.py'), os.listdir(build_path)): shutil.copy(os.path.join(build_path, pyc), os.path.join(dist_path, 'bin', pyc)) if __name__ == '__main__': import doctest doctest.testmod() z3-4.4.0/scripts/mk_copyright.py0000644000175000017500000000213312540347414016404 0ustar michaelmichael# Copyright (c) 2015 Microsoft Corporation import os import re cr = re.compile("Copyright") aut = re.compile("Automatically generated") cr_notice = """ /*++ Copyright (c) 2015 Microsoft Corporation --*/ """ def has_cr(file): ins = open(file) lines = 0 line = ins.readline() while line and lines < 20: m = cr.search(line) if m: ins.close() return True m = aut.search(line) if m: ins.close() return True line = ins.readline() ins.close() return False def add_cr(file): tmp = "%s.tmp" % file ins = open(file) ous = open(tmp,'w') ous.write(cr_notice) line = ins.readline() while line: ous.write(line) line = ins.readline() ins.close() ous.close() os.system("move %s %s" % (tmp, file)) def add_missing_cr(dir): for root, dirs, files in os.walk(dir): for f in files: if f.endswith('.cpp') or f.endswith('.h') or f.endswith('.c') or f.endswith('.cs'): path = "%s\\%s" % (root, f) if not has_cr(path): print "Missing CR for %s" % path add_cr(path) add_missing_cr('src') add_missing_cr('examples') z3-4.4.0/scripts/README0000644000175000017500000000060712540347414014217 0ustar michaelmichaelInstructions for updating external Z3 API ----------------------------------------- The python "macros": def_Type() and def_API() are used to add new types and function definitions to the Z3 API. The .h files provided to `mk_bindings(API_files)` contain these definitions. See src\api\z3_api.h for many examples. The bindings for .Net and Python are generated when mk_make.py is invoked. z3-4.4.0/scripts/mk_unix_dist.py0000644000175000017500000001467712540347414016422 0ustar michaelmichael############################################ # Copyright (c) 2013 Microsoft Corporation # # Scripts for automatically generating # Linux/OSX/BSD distribution zip files. # # Author: Leonardo de Moura (leonardo) ############################################ import os import glob import re import getopt import sys import shutil import subprocess import zipfile from mk_exception import * from mk_project import * import mk_util BUILD_DIR='build-dist' VERBOSE=True DIST_DIR='dist' FORCE_MK=False JAVA_ENABLED=True GIT_HASH=False def set_verbose(flag): global VERBOSE VERBOSE = flag def is_verbose(): return VERBOSE def mk_dir(d): if not os.path.exists(d): os.makedirs(d) def set_build_dir(path): global BUILD_DIR BUILD_DIR = path mk_dir(BUILD_DIR) def display_help(): print "mk_unix_dist.py: Z3 Linux/OSX/BSD distribution generator\n" print "This script generates the zip files containing executables, shared objects, header files for Linux/OSX/BSD." print "It must be executed from the Z3 root directory." print "\nOptions:" print " -h, --help display this message." print " -s, --silent do not print verbose messages." print " -b , --build= subdirectory where x86 and x64 Z3 versions will be built (default: build-dist)." print " -f, --force force script to regenerate Makefiles." print " --nojava do not include Java bindings in the binary distribution files." print " --githash include git hash in the Zip file." exit(0) # Parse configuration option for mk_make script def parse_options(): global FORCE_MK, JAVA_ENABLED, GIT_HASH path = BUILD_DIR options, remainder = getopt.gnu_getopt(sys.argv[1:], 'b:hsf', ['build=', 'help', 'silent', 'force', 'nojava', 'githash' ]) for opt, arg in options: if opt in ('-b', '--build'): if arg == 'src': raise MKException('The src directory should not be used to host the Makefile') path = arg elif opt in ('-s', '--silent'): set_verbose(False) elif opt in ('-h', '--help'): display_help() elif opt in ('-f', '--force'): FORCE_MK = True elif opt == '--nojava': JAVA_ENABLED = False elif opt == '--githash': GIT_HASH = True else: raise MKException("Invalid command line option '%s'" % opt) set_build_dir(path) # Check whether build directory already exists or not def check_build_dir(path): return os.path.exists(path) and os.path.exists(os.path.join(path, 'Makefile')) # Create a build directory using mk_make.py def mk_build_dir(path): if not check_build_dir(path) or FORCE_MK: opts = ["python", os.path.join('scripts', 'mk_make.py'), "-b", path, "--static"] if JAVA_ENABLED: opts.append('--java') if GIT_HASH: opts.append('--githash=%s' % mk_util.git_hash()) if subprocess.call(opts) != 0: raise MKException("Failed to generate build directory at '%s'" % path) # Create build directories def mk_build_dirs(): mk_build_dir(BUILD_DIR) class cd: def __init__(self, newPath): self.newPath = newPath def __enter__(self): self.savedPath = os.getcwd() os.chdir(self.newPath) def __exit__(self, etype, value, traceback): os.chdir(self.savedPath) def mk_z3(): with cd(BUILD_DIR): try: return subprocess.call(['make', '-j', '8']) except: return 1 def get_os_name(): import platform basic = os.uname()[0].lower() if basic == 'linux': dist = platform.linux_distribution() if len(dist) == 3 and len(dist[0]) > 0 and len(dist[1]) > 0: return '%s-%s' % (dist[0].lower(), dist[1].lower()) else: return basic elif basic == 'darwin': ver = platform.mac_ver() if len(ver) == 3 and len(ver[0]) > 0: return 'osx-%s' % ver[0] else: return 'osx' elif basic == 'freebsd': ver = platform.version() idx1 = ver.find(' ') idx2 = ver.find('-') if idx1 < 0 or idx2 < 0 or idx1 >= idx2: return basic else: return 'freebsd-%s' % ver[(idx1+1):idx2] else: return basic def get_z3_name(): major, minor, build, revision = get_version() if sys.maxsize >= 2**32: platform = "x64" else: platform = "x86" osname = get_os_name() if GIT_HASH: return 'z3-%s.%s.%s.%s-%s-%s' % (major, minor, build, mk_util.git_hash(), platform, osname) else: return 'z3-%s.%s.%s-%s-%s' % (major, minor, build, platform, osname) def mk_dist_dir(): build_path = BUILD_DIR dist_path = os.path.join(DIST_DIR, get_z3_name()) mk_dir(dist_path) if JAVA_ENABLED: # HACK: Propagate JAVA_ENABLED flag to mk_util # TODO: fix this hack mk_util.JAVA_ENABLED = JAVA_ENABLED mk_unix_dist(build_path, dist_path) if is_verbose(): print "Generated distribution folder at '%s'" % dist_path ZIPOUT = None def mk_zip_visitor(pattern, dir, files): for filename in files: if fnmatch(filename, pattern): fname = os.path.join(dir, filename) if not os.path.isdir(fname): ZIPOUT.write(fname) def get_dist_path(): return get_z3_name() def mk_zip(): global ZIPOUT dist_path = get_dist_path() old = os.getcwd() try: os.chdir(DIST_DIR) zfname = '%s.zip' % dist_path ZIPOUT = zipfile.ZipFile(zfname, 'w', zipfile.ZIP_DEFLATED) os.path.walk(dist_path, mk_zip_visitor, '*') if is_verbose(): print "Generated '%s'" % zfname except: pass ZIPOUT = None os.chdir(old) def cp_license(): shutil.copy("LICENSE.txt", os.path.join(DIST_DIR, get_dist_path())) # Entry point def main(): parse_options() mk_build_dirs() mk_z3() init_project_def() mk_dist_dir() cp_license() mk_zip() main() z3-4.4.0/scripts/update_api.py0000644000175000017500000017610012540347414016026 0ustar michaelmichael ############################################ # Copyright (c) 2012 Microsoft Corporation # # Scripts for generating Makefiles and Visual # Studio project files. # # Author: Leonardo de Moura (leonardo) ############################################ from mk_util import * from mk_exception import * ########################################################## # TODO: rewrite this file without using global variables. # This file is a big HACK. # It started as small simple script. # Now, it is too big, and is invoked from mk_make.py # The communication uses # ########################################################## # # Generate logging support and bindings # api_dir = get_component('api').src_dir dotnet_dir = get_component('dotnet').src_dir log_h = open(os.path.join(api_dir, 'api_log_macros.h'), 'w') log_c = open(os.path.join(api_dir, 'api_log_macros.cpp'), 'w') exe_c = open(os.path.join(api_dir, 'api_commands.cpp'), 'w') core_py = open(os.path.join(get_z3py_dir(), 'z3core.py'), 'w') dotnet_fileout = os.path.join(dotnet_dir, 'Native.cs') ## log_h.write('// Automatically generated file\n') log_h.write('#include\"z3.h\"\n') log_h.write('#ifdef __GNUC__\n') log_h.write('#define _Z3_UNUSED __attribute__((unused))\n') log_h.write('#else\n') log_h.write('#define _Z3_UNUSED\n') log_h.write('#endif\n') ## log_c.write('// Automatically generated file\n') log_c.write('#include\n') log_c.write('#include\"z3.h\"\n') log_c.write('#include\"api_log_macros.h\"\n') log_c.write('#include\"z3_logger.h\"\n') ## exe_c.write('// Automatically generated file\n') exe_c.write('#include\"z3.h\"\n') exe_c.write('#include\"z3_replayer.h\"\n') ## log_h.write('extern std::ostream * g_z3_log;\n') log_h.write('extern bool g_z3_log_enabled;\n') log_h.write('class z3_log_ctx { bool m_prev; public: z3_log_ctx():m_prev(g_z3_log_enabled) { g_z3_log_enabled = false; } ~z3_log_ctx() { g_z3_log_enabled = m_prev; } bool enabled() const { return m_prev; } };\n') log_h.write('inline void SetR(void * obj) { *g_z3_log << "= " << obj << "\\n"; }\ninline void SetO(void * obj, unsigned pos) { *g_z3_log << "* " << obj << " " << pos << "\\n"; } \ninline void SetAO(void * obj, unsigned pos, unsigned idx) { *g_z3_log << "@ " << obj << " " << pos << " " << idx << "\\n"; }\n') log_h.write('#define RETURN_Z3(Z3RES) if (_LOG_CTX.enabled()) { SetR(Z3RES); } return Z3RES\n') log_h.write('void _Z3_append_log(char const * msg);\n') ## exe_c.write('void Z3_replacer_error_handler(Z3_context ctx, Z3_error_code c) { printf("[REPLAYER ERROR HANDLER]: %s\\n", Z3_get_error_msg_ex(ctx, c)); }\n') ## core_py.write('# Automatically generated file\n') core_py.write('import sys, os\n') core_py.write('import ctypes\n') core_py.write('from z3types import *\n') core_py.write('from z3consts import *\n') core_py.write(""" _lib = None def lib(): global _lib if _lib == None: _dir = os.path.dirname(os.path.abspath(__file__)) for ext in ['dll', 'so', 'dylib']: try: init('libz3.%s' % ext) break except: pass try: init(os.path.join(_dir, 'libz3.%s' % ext)) break except: pass if _lib == None: raise Z3Exception("init(Z3_LIBRARY_PATH) must be invoked before using Z3-python") return _lib def _to_ascii(s): if isinstance(s, str): return s.encode('ascii') else: return s if sys.version < '3': def _to_pystr(s): return s else: def _to_pystr(s): return s.decode('utf-8') def init(PATH): global _lib _lib = ctypes.CDLL(PATH) """) IN = 0 OUT = 1 INOUT = 2 IN_ARRAY = 3 OUT_ARRAY = 4 INOUT_ARRAY = 5 OUT_MANAGED_ARRAY = 6 # Primitive Types VOID = 0 VOID_PTR = 1 INT = 2 UINT = 3 INT64 = 4 UINT64 = 5 STRING = 6 STRING_PTR = 7 BOOL = 8 SYMBOL = 9 PRINT_MODE = 10 ERROR_CODE = 11 DOUBLE = 12 FLOAT = 13 FIRST_OBJ_ID = 100 def is_obj(ty): return ty >= FIRST_OBJ_ID Type2Str = { VOID : 'void', VOID_PTR : 'void*', INT : 'int', UINT : 'unsigned', INT64 : '__int64', UINT64 : '__uint64', DOUBLE : 'double', FLOAT : 'float', STRING : 'Z3_string', STRING_PTR : 'Z3_string_ptr', BOOL : 'Z3_bool', SYMBOL : 'Z3_symbol', PRINT_MODE : 'Z3_ast_print_mode', ERROR_CODE : 'Z3_error_code' } Type2PyStr = { VOID_PTR : 'ctypes.c_void_p', INT : 'ctypes.c_int', UINT : 'ctypes.c_uint', INT64 : 'ctypes.c_longlong', UINT64 : 'ctypes.c_ulonglong', DOUBLE : 'ctypes.c_double', FLOAT : 'ctypes.c_float', STRING : 'ctypes.c_char_p', STRING_PTR : 'ctypes.POINTER(ctypes.c_char_p)', BOOL : 'ctypes.c_bool', SYMBOL : 'Symbol', PRINT_MODE : 'ctypes.c_uint', ERROR_CODE : 'ctypes.c_uint' } # Mapping to .NET types Type2Dotnet = { VOID : 'void', VOID_PTR : 'IntPtr', INT : 'int', UINT : 'uint', INT64 : 'Int64', UINT64 : 'UInt64', DOUBLE : 'double', FLOAT : 'float', STRING : 'string', STRING_PTR : 'byte**', BOOL : 'int', SYMBOL : 'IntPtr', PRINT_MODE : 'uint', ERROR_CODE : 'uint' } # Mapping to Java types Type2Java = { VOID : 'void', VOID_PTR : 'long', INT : 'int', UINT : 'int', INT64 : 'long', UINT64 : 'long', DOUBLE : 'double', FLOAT : 'float', STRING : 'String', STRING_PTR : 'StringPtr', BOOL : 'boolean', SYMBOL : 'long', PRINT_MODE : 'int', ERROR_CODE : 'int'} Type2JavaW = { VOID : 'void', VOID_PTR : 'jlong', INT : 'jint', UINT : 'jint', INT64 : 'jlong', UINT64 : 'jlong', DOUBLE : 'jdouble', FLOAT : 'jfloat', STRING : 'jstring', STRING_PTR : 'jobject', BOOL : 'jboolean', SYMBOL : 'jlong', PRINT_MODE : 'jint', ERROR_CODE : 'jint'} # Mapping to ML types Type2ML = { VOID : 'unit', VOID_PTR : 'VOIDP', INT : 'int', UINT : 'int', INT64 : 'int', UINT64 : 'int', DOUBLE : 'float', FLOAT : 'float', STRING : 'string', STRING_PTR : 'char**', BOOL : 'bool', SYMBOL : 'z3_symbol', PRINT_MODE : 'int', ERROR_CODE : 'int' } next_type_id = FIRST_OBJ_ID def def_Type(var, c_type, py_type): global next_type_id exec('%s = %s' % (var, next_type_id), globals()) Type2Str[next_type_id] = c_type Type2PyStr[next_type_id] = py_type next_type_id = next_type_id + 1 def def_Types(): import re pat1 = re.compile(" *def_Type\(\'(.*)\',[^\']*\'(.*)\',[^\']*\'(.*)\'\)[ \t]*") for api_file in API_FILES: api = open(api_file, 'r') for line in api: m = pat1.match(line) if m: def_Type(m.group(1), m.group(2), m.group(3)) for k in Type2Str: v = Type2Str[k] if is_obj(k): Type2Dotnet[k] = v Type2ML[k] = v.lower() def type2str(ty): global Type2Str return Type2Str[ty] def type2pystr(ty): global Type2PyStr return Type2PyStr[ty] def type2dotnet(ty): global Type2Dotnet return Type2Dotnet[ty] def type2java(ty): global Type2Java if (ty >= FIRST_OBJ_ID): return 'long' else: return Type2Java[ty] def type2javaw(ty): global Type2JavaW if (ty >= FIRST_OBJ_ID): return 'jlong' else: return Type2JavaW[ty] def type2ml(ty): global Type2ML return Type2ML[ty] def _in(ty): return (IN, ty); def _in_array(sz, ty): return (IN_ARRAY, ty, sz); def _out(ty): return (OUT, ty); def _out_array(sz, ty): return (OUT_ARRAY, ty, sz, sz); # cap contains the position of the argument that stores the capacity of the array # sz contains the position of the output argument that stores the (real) size of the array def _out_array2(cap, sz, ty): return (OUT_ARRAY, ty, cap, sz) def _inout_array(sz, ty): return (INOUT_ARRAY, ty, sz, sz); def _out_managed_array(sz,ty): return (OUT_MANAGED_ARRAY, ty, 0, sz) def param_kind(p): return p[0] def param_type(p): return p[1] def param_array_capacity_pos(p): return p[2] def param_array_size_pos(p): return p[3] def param2str(p): if param_kind(p) == IN_ARRAY: return "%s const *" % type2str(param_type(p)) elif param_kind(p) == OUT_ARRAY or param_kind(p) == IN_ARRAY or param_kind(p) == INOUT_ARRAY: return "%s*" % type2str(param_type(p)) elif param_kind(p) == OUT: return "%s*" % type2str(param_type(p)) else: return type2str(param_type(p)) def param2dotnet(p): k = param_kind(p) if k == OUT: if param_type(p) == STRING: return "out IntPtr" else: return "[In, Out] ref %s" % type2dotnet(param_type(p)) elif k == IN_ARRAY: return "[In] %s[]" % type2dotnet(param_type(p)) elif k == INOUT_ARRAY: return "[In, Out] %s[]" % type2dotnet(param_type(p)) elif k == OUT_ARRAY: return "[Out] %s[]" % type2dotnet(param_type(p)) elif k == OUT_MANAGED_ARRAY: return "[Out] out %s[]" % type2dotnet(param_type(p)) else: return type2dotnet(param_type(p)) def param2java(p): k = param_kind(p) if k == OUT: if param_type(p) == INT or param_type(p) == UINT: return "IntPtr" elif param_type(p) == INT64 or param_type(p) == UINT64 or param_type(p) == VOID_PTR or param_type(p) >= FIRST_OBJ_ID: return "LongPtr" elif param_type(p) == STRING: return "StringPtr" else: print("ERROR: unreachable code") assert(False) exit(1) elif k == IN_ARRAY or k == INOUT_ARRAY or k == OUT_ARRAY: return "%s[]" % type2java(param_type(p)) elif k == OUT_MANAGED_ARRAY: if param_type(p) == UINT: return "UIntArrayPtr" else: return "ObjArrayPtr" else: return type2java(param_type(p)) def param2javaw(p): k = param_kind(p) if k == OUT: return "jobject" elif k == IN_ARRAY or k == INOUT_ARRAY or k == OUT_ARRAY: if param_type(p) == INT or param_type(p) == UINT: return "jintArray" else: return "jlongArray" elif k == OUT_MANAGED_ARRAY: return "jlong"; else: return type2javaw(param_type(p)) def param2pystr(p): if param_kind(p) == IN_ARRAY or param_kind(p) == OUT_ARRAY or param_kind(p) == IN_ARRAY or param_kind(p) == INOUT_ARRAY or param_kind(p) == OUT: return "ctypes.POINTER(%s)" % type2pystr(param_type(p)) else: return type2pystr(param_type(p)) def param2ml(p): k = param_kind(p) if k == OUT: if param_type(p) == INT or param_type(p) == UINT or param_type(p) == INT64 or param_type(p) == UINT64: return "int" elif param_type(p) == STRING: return "string" else: return "ptr" elif k == IN_ARRAY or k == INOUT_ARRAY or k == OUT_ARRAY: return "%s array" % type2ml(param_type(p)) elif k == OUT_MANAGED_ARRAY: return "%s array" % type2ml(param_type(p)); else: return type2ml(param_type(p)) # Save name, result, params to generate wrapper _API2PY = [] def mk_py_binding(name, result, params): global core_py global _API2PY _API2PY.append((name, result, params)) if result != VOID: core_py.write(" _lib.%s.restype = %s\n" % (name, type2pystr(result))) core_py.write(" _lib.%s.argtypes = [" % name) first = True for p in params: if first: first = False else: core_py.write(", ") core_py.write(param2pystr(p)) core_py.write("]\n") def extra_API(name, result, params): mk_py_binding(name, result, params) reg_dotnet(name, result, params) def display_args(num): for i in range(num): if i > 0: core_py.write(", ") core_py.write("a%s" % i) def display_args_to_z3(params): i = 0 for p in params: if i > 0: core_py.write(", ") if param_type(p) == STRING: core_py.write("_to_ascii(a%s)" % i) else: core_py.write("a%s" % i) i = i + 1 def mk_py_wrappers(): core_py.write("\n") for sig in _API2PY: name = sig[0] result = sig[1] params = sig[2] num = len(params) core_py.write("def %s(" % name) display_args(num) core_py.write("):\n") if result != VOID: core_py.write(" r = lib().%s(" % name) else: core_py.write(" lib().%s(" % name) display_args_to_z3(params) core_py.write(")\n") if len(params) > 0 and param_type(params[0]) == CONTEXT: core_py.write(" err = lib().Z3_get_error_code(a0)\n") core_py.write(" if err != Z3_OK:\n") core_py.write(" raise Z3Exception(lib().Z3_get_error_msg_ex(a0, err))\n") if result == STRING: core_py.write(" return _to_pystr(r)\n") elif result != VOID: core_py.write(" return r\n") core_py.write("\n") ## .NET API native interface _dotnet_decls = [] def reg_dotnet(name, result, params): global _dotnet_decls _dotnet_decls.append((name, result, params)) def mk_dotnet(): global Type2Str global dotnet_fileout dotnet = open(dotnet_fileout, 'w') dotnet.write('// Automatically generated file\n') dotnet.write('using System;\n') dotnet.write('using System.Collections.Generic;\n') dotnet.write('using System.Text;\n') dotnet.write('using System.Runtime.InteropServices;\n\n') dotnet.write('#pragma warning disable 1591\n\n'); dotnet.write('namespace Microsoft.Z3\n') dotnet.write('{\n') for k in Type2Str: v = Type2Str[k] if is_obj(k): dotnet.write(' using %s = System.IntPtr;\n' % v) dotnet.write('\n'); dotnet.write(' public class Native\n') dotnet.write(' {\n\n') dotnet.write(' [UnmanagedFunctionPointer(CallingConvention.Cdecl)]\n') dotnet.write(' public delegate void Z3_error_handler(Z3_context c, Z3_error_code e);\n\n') dotnet.write(' public unsafe class LIB\n') dotnet.write(' {\n') dotnet.write(' const string Z3_DLL_NAME = \"libz3.dll\";\n' ' \n'); dotnet.write(' [DllImport(Z3_DLL_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]\n') dotnet.write(' public extern static void Z3_set_error_handler(Z3_context a0, Z3_error_handler a1);\n\n') for name, result, params in _dotnet_decls: dotnet.write(' [DllImport(Z3_DLL_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]\n') dotnet.write(' ') if result == STRING: dotnet.write('public extern static IntPtr %s(' % (name)) else: dotnet.write('public extern static %s %s(' % (type2dotnet(result), name)) first = True i = 0; for param in params: if first: first = False else: dotnet.write(', ') dotnet.write('%s a%d' % (param2dotnet(param), i)) i = i + 1 dotnet.write(');\n\n') dotnet.write(' }\n') NULLWrapped = [ 'Z3_mk_context', 'Z3_mk_context_rc' ] Unwrapped = [ 'Z3_del_context', 'Z3_get_error_code' ] def mk_dotnet_wrappers(): global Type2Str global dotnet_fileout dotnet = open(dotnet_fileout, 'a') dotnet.write("\n") dotnet.write(" public static void Z3_set_error_handler(Z3_context a0, Z3_error_handler a1) {\n") dotnet.write(" LIB.Z3_set_error_handler(a0, a1);\n") dotnet.write(" Z3_error_code err = (Z3_error_code)LIB.Z3_get_error_code(a0);\n") dotnet.write(" if (err != Z3_error_code.Z3_OK)\n") dotnet.write(" throw new Z3Exception(Marshal.PtrToStringAnsi(LIB.Z3_get_error_msg_ex(a0, (uint)err)));\n") dotnet.write(" }\n\n") for name, result, params in _dotnet_decls: if result == STRING: dotnet.write(' public static string %s(' % (name)) else: dotnet.write(' public static %s %s(' % (type2dotnet(result), name)) first = True i = 0; for param in params: if first: first = False else: dotnet.write(', ') dotnet.write('%s a%d' % (param2dotnet(param), i)) i = i + 1 dotnet.write(') {\n') dotnet.write(' ') if result == STRING: dotnet.write('IntPtr r = '); elif result != VOID: dotnet.write('%s r = ' % type2dotnet(result)); dotnet.write('LIB.%s(' % (name)) first = True i = 0 for param in params: if first: first = False else: dotnet.write(', ') if param_kind(param) == OUT: if param_type(param) == STRING: dotnet.write('out '); else: dotnet.write('ref ') elif param_kind(param) == OUT_MANAGED_ARRAY: dotnet.write('out '); dotnet.write('a%d' % i) i = i + 1 dotnet.write(');\n'); if name not in Unwrapped: if name in NULLWrapped: dotnet.write(" if (r == IntPtr.Zero)\n") dotnet.write(" throw new Z3Exception(\"Object allocation failed.\");\n") else: if len(params) > 0 and param_type(params[0]) == CONTEXT: dotnet.write(" Z3_error_code err = (Z3_error_code)LIB.Z3_get_error_code(a0);\n") dotnet.write(" if (err != Z3_error_code.Z3_OK)\n") dotnet.write(" throw new Z3Exception(Marshal.PtrToStringAnsi(LIB.Z3_get_error_msg_ex(a0, (uint)err)));\n") if result == STRING: dotnet.write(" return Marshal.PtrToStringAnsi(r);\n") elif result != VOID: dotnet.write(" return r;\n") dotnet.write(" }\n\n") dotnet.write(" }\n\n") dotnet.write("}\n\n") def java_method_name(name): result = '' name = name[3:] # Remove Z3_ n = len(name) i = 0 while i < n: if name[i] == '_': i = i + 1 if i < n: result += name[i].upper() else: result += name[i] i = i + 1 return result # Return the type of the java array elements def java_array_element_type(p): if param_type(p) == INT or param_type(p) == UINT: return 'jint' else: return 'jlong' def mk_java(): if not is_java_enabled(): return java_dir = get_component('java').src_dir java_nativef = os.path.join(java_dir, 'Native.java') java_wrapperf = os.path.join(java_dir, 'Native.cpp') java_native = open(java_nativef, 'w') java_native.write('// Automatically generated file\n') java_native.write('package %s;\n' % get_component('java').package_name) java_native.write('import %s.enumerations.*;\n' % get_component('java').package_name) java_native.write('public final class Native {\n') java_native.write(' public static class IntPtr { public int value; }\n') java_native.write(' public static class LongPtr { public long value; }\n') java_native.write(' public static class StringPtr { public String value; }\n') java_native.write(' public static class ObjArrayPtr { public long[] value; }\n') java_native.write(' public static class UIntArrayPtr { public int[] value; }\n') java_native.write(' public static native void setInternalErrorHandler(long ctx);\n\n') java_native.write(' static {\n') java_native.write(' try { System.loadLibrary("z3java"); }\n') java_native.write(' catch (UnsatisfiedLinkError ex) { System.loadLibrary("libz3java"); }\n') java_native.write(' }\n') java_native.write('\n') for name, result, params in _dotnet_decls: java_native.write(' protected static native %s INTERNAL%s(' % (type2java(result), java_method_name(name))) first = True i = 0; for param in params: if first: first = False else: java_native.write(', ') java_native.write('%s a%d' % (param2java(param), i)) i = i + 1 java_native.write(');\n') java_native.write('\n\n') # Exception wrappers for name, result, params in _dotnet_decls: java_native.write(' public static %s %s(' % (type2java(result), java_method_name(name))) first = True i = 0; for param in params: if first: first = False else: java_native.write(', ') java_native.write('%s a%d' % (param2java(param), i)) i = i + 1 java_native.write(')') if (len(params) > 0 and param_type(params[0]) == CONTEXT) or name in NULLWrapped: java_native.write(' throws Z3Exception') java_native.write('\n') java_native.write(' {\n') java_native.write(' ') if result != VOID: java_native.write('%s res = ' % type2java(result)) java_native.write('INTERNAL%s(' % (java_method_name(name))) first = True i = 0; for param in params: if first: first = False else: java_native.write(', ') java_native.write('a%d' % i) i = i + 1 java_native.write(');\n') if name not in Unwrapped: if name in NULLWrapped: java_native.write(" if (res == 0)\n") java_native.write(" throw new Z3Exception(\"Object allocation failed.\");\n") else: if len(params) > 0 and param_type(params[0]) == CONTEXT: java_native.write(' Z3_error_code err = Z3_error_code.fromInt(INTERNALgetErrorCode(a0));\n') java_native.write(' if (err != Z3_error_code.Z3_OK)\n') java_native.write(' throw new Z3Exception(INTERNALgetErrorMsgEx(a0, err.toInt()));\n') if result != VOID: java_native.write(' return res;\n') java_native.write(' }\n\n') java_native.write('}\n') java_wrapper = open(java_wrapperf, 'w') pkg_str = get_component('java').package_name.replace('.', '_') java_wrapper.write('// Automatically generated file\n') java_wrapper.write('#ifdef _CYGWIN\n') java_wrapper.write('typedef long long __int64;\n') java_wrapper.write('#endif\n') java_wrapper.write('#include\n') java_wrapper.write('#include\n') java_wrapper.write('#include"z3.h"\n') java_wrapper.write('#ifdef __cplusplus\n') java_wrapper.write('extern "C" {\n') java_wrapper.write('#endif\n\n') java_wrapper.write('#ifdef __GNUC__\n#if __GNUC__ >= 4\n#define DLL_VIS __attribute__ ((visibility ("default")))\n#else\n#define DLL_VIS\n#endif\n#else\n#define DLL_VIS\n#endif\n\n') java_wrapper.write('#if defined(_M_X64) || defined(_AMD64_)\n\n') java_wrapper.write('#define GETLONGAELEMS(T,OLD,NEW) \\\n') java_wrapper.write(' T * NEW = (OLD == 0) ? 0 : (T*) jenv->GetLongArrayElements(OLD, NULL);\n') java_wrapper.write('#define RELEASELONGAELEMS(OLD,NEW) \\\n') java_wrapper.write(' if (OLD != 0) jenv->ReleaseLongArrayElements(OLD, (jlong *) NEW, JNI_ABORT); \n\n') java_wrapper.write('#define GETLONGAREGION(T,OLD,Z,SZ,NEW) \\\n') java_wrapper.write(' jenv->GetLongArrayRegion(OLD,Z,(jsize)SZ,(jlong*)NEW); \n') java_wrapper.write('#define SETLONGAREGION(OLD,Z,SZ,NEW) \\\n') java_wrapper.write(' jenv->SetLongArrayRegion(OLD,Z,(jsize)SZ,(jlong*)NEW) \n\n') java_wrapper.write('#else\n\n') java_wrapper.write('#define GETLONGAELEMS(T,OLD,NEW) \\\n') java_wrapper.write(' T * NEW = 0; { \\\n') java_wrapper.write(' jlong * temp = (OLD == 0) ? 0 : jenv->GetLongArrayElements(OLD, NULL); \\\n') java_wrapper.write(' unsigned int size = (OLD == 0) ? 0 :jenv->GetArrayLength(OLD); \\\n') java_wrapper.write(' if (OLD != 0) { \\\n') java_wrapper.write(' NEW = (T*) (new int[size]); \\\n') java_wrapper.write(' for (unsigned i=0; i < size; i++) \\\n') java_wrapper.write(' NEW[i] = reinterpret_cast(temp[i]); \\\n') java_wrapper.write(' jenv->ReleaseLongArrayElements(OLD, temp, JNI_ABORT); \\\n') java_wrapper.write(' } \\\n') java_wrapper.write(' } \n\n') java_wrapper.write('#define RELEASELONGAELEMS(OLD,NEW) \\\n') java_wrapper.write(' delete [] NEW; \n\n') java_wrapper.write('#define GETLONGAREGION(T,OLD,Z,SZ,NEW) \\\n') java_wrapper.write(' { \\\n') java_wrapper.write(' jlong * temp = new jlong[SZ]; \\\n') java_wrapper.write(' jenv->GetLongArrayRegion(OLD,Z,(jsize)SZ,(jlong*)temp); \\\n') java_wrapper.write(' for (int i = 0; i < (SZ); i++) \\\n') java_wrapper.write(' NEW[i] = reinterpret_cast(temp[i]); \\\n') java_wrapper.write(' delete [] temp; \\\n') java_wrapper.write(' }\n\n') java_wrapper.write('#define SETLONGAREGION(OLD,Z,SZ,NEW) \\\n') java_wrapper.write(' { \\\n') java_wrapper.write(' jlong * temp = new jlong[SZ]; \\\n') java_wrapper.write(' for (int i = 0; i < (SZ); i++) \\\n') java_wrapper.write(' temp[i] = reinterpret_cast(NEW[i]); \\\n') java_wrapper.write(' jenv->SetLongArrayRegion(OLD,Z,(jsize)SZ,temp); \\\n') java_wrapper.write(' delete [] temp; \\\n') java_wrapper.write(' }\n\n') java_wrapper.write('#endif\n\n') java_wrapper.write('void Z3JavaErrorHandler(Z3_context c, Z3_error_code e)\n') java_wrapper.write('{\n') java_wrapper.write(' // Internal do-nothing error handler. This is required to avoid that Z3 calls exit()\n') java_wrapper.write(' // upon errors, but the actual error handling is done by throwing exceptions in the\n') java_wrapper.write(' // wrappers below.\n') java_wrapper.write('}\n\n') java_wrapper.write('DLL_VIS JNIEXPORT void JNICALL Java_%s_Native_setInternalErrorHandler(JNIEnv * jenv, jclass cls, jlong a0)\n' % pkg_str) java_wrapper.write('{\n') java_wrapper.write(' Z3_set_error_handler((Z3_context)a0, Z3JavaErrorHandler);\n') java_wrapper.write('}\n\n') java_wrapper.write('') for name, result, params in _dotnet_decls: java_wrapper.write('DLL_VIS JNIEXPORT %s JNICALL Java_%s_Native_INTERNAL%s(JNIEnv * jenv, jclass cls' % (type2javaw(result), pkg_str, java_method_name(name))) i = 0; for param in params: java_wrapper.write(', ') java_wrapper.write('%s a%d' % (param2javaw(param), i)) i = i + 1 java_wrapper.write(') {\n') # preprocess arrays, strings, in/out arguments i = 0 for param in params: k = param_kind(param) if k == OUT or k == INOUT: java_wrapper.write(' %s _a%s;\n' % (type2str(param_type(param)), i)) elif k == IN_ARRAY or k == INOUT_ARRAY: if param_type(param) == INT or param_type(param) == UINT: java_wrapper.write(' %s * _a%s = (%s*) jenv->GetIntArrayElements(a%s, NULL);\n' % (type2str(param_type(param)), i, type2str(param_type(param)), i)) else: java_wrapper.write(' GETLONGAELEMS(%s, a%s, _a%s);\n' % (type2str(param_type(param)), i, i)) elif k == OUT_ARRAY: java_wrapper.write(' %s * _a%s = (%s *) malloc(((unsigned)a%s) * sizeof(%s));\n' % (type2str(param_type(param)), i, type2str(param_type(param)), param_array_capacity_pos(param), type2str(param_type(param)))) if param_type(param) == INT or param_type(param) == UINT: java_wrapper.write(' jenv->GetIntArrayRegion(a%s, 0, (jsize)a%s, (jint*)_a%s);\n' % (i, param_array_capacity_pos(param), i)) else: java_wrapper.write(' GETLONGAREGION(%s, a%s, 0, a%s, _a%s);\n' % (type2str(param_type(param)), i, param_array_capacity_pos(param), i)) elif k == IN and param_type(param) == STRING: java_wrapper.write(' Z3_string _a%s = (Z3_string) jenv->GetStringUTFChars(a%s, NULL);\n' % (i, i)) elif k == OUT_MANAGED_ARRAY: java_wrapper.write(' %s * _a%s = 0;\n' % (type2str(param_type(param)), i)) i = i + 1 # invoke procedure java_wrapper.write(' ') if result != VOID: java_wrapper.write('%s result = ' % type2str(result)) java_wrapper.write('%s(' % name) i = 0 first = True for param in params: if first: first = False else: java_wrapper.write(', ') k = param_kind(param) if k == OUT or k == INOUT: java_wrapper.write('&_a%s' % i) elif k == OUT_ARRAY or k == IN_ARRAY or k == INOUT_ARRAY: java_wrapper.write('_a%s' % i) elif k == OUT_MANAGED_ARRAY: java_wrapper.write('&_a%s' % i) elif k == IN and param_type(param) == STRING: java_wrapper.write('_a%s' % i) else: java_wrapper.write('(%s)a%i' % (param2str(param), i)) i = i + 1 java_wrapper.write(');\n') # cleanup i = 0 for param in params: k = param_kind(param) if k == OUT_ARRAY: if param_type(param) == INT or param_type(param) == UINT: java_wrapper.write(' jenv->SetIntArrayRegion(a%s, 0, (jsize)a%s, (jint*)_a%s);\n' % (i, param_array_capacity_pos(param), i)) else: java_wrapper.write(' SETLONGAREGION(a%s, 0, a%s, _a%s);\n' % (i, param_array_capacity_pos(param), i)) java_wrapper.write(' free(_a%s);\n' % i) elif k == IN_ARRAY or k == OUT_ARRAY: if param_type(param) == INT or param_type(param) == UINT: java_wrapper.write(' jenv->ReleaseIntArrayElements(a%s, (jint*)_a%s, JNI_ABORT);\n' % (i, i)) else: java_wrapper.write(' RELEASELONGAELEMS(a%s, _a%s);\n' % (i, i)) elif k == OUT or k == INOUT: if param_type(param) == INT or param_type(param) == UINT: java_wrapper.write(' {\n') java_wrapper.write(' jclass mc = jenv->GetObjectClass(a%s);\n' % i) java_wrapper.write(' jfieldID fid = jenv->GetFieldID(mc, "value", "I");\n') java_wrapper.write(' jenv->SetIntField(a%s, fid, (jint) _a%s);\n' % (i, i)) java_wrapper.write(' }\n') else: java_wrapper.write(' {\n') java_wrapper.write(' jclass mc = jenv->GetObjectClass(a%s);\n' % i) java_wrapper.write(' jfieldID fid = jenv->GetFieldID(mc, "value", "J");\n') java_wrapper.write(' jenv->SetLongField(a%s, fid, (jlong) _a%s);\n' % (i, i)) java_wrapper.write(' }\n') elif k == OUT_MANAGED_ARRAY: java_wrapper.write(' *(jlong**)a%s = (jlong*)_a%s;\n' % (i, i)) i = i + 1 # return if result == STRING: java_wrapper.write(' return jenv->NewStringUTF(result);\n') elif result != VOID: java_wrapper.write(' return (%s) result;\n' % type2javaw(result)) java_wrapper.write('}\n') java_wrapper.write('#ifdef __cplusplus\n') java_wrapper.write('}\n') java_wrapper.write('#endif\n') if is_verbose(): print("Generated '%s'" % java_nativef) def mk_log_header(file, name, params): file.write("void log_%s(" % name) i = 0 for p in params: if i > 0: file.write(", "); file.write("%s a%s" % (param2str(p), i)) i = i + 1 file.write(")"); def log_param(p): kind = param_kind(p) ty = param_type(p) return is_obj(ty) and (kind == OUT or kind == INOUT or kind == OUT_ARRAY or kind == INOUT_ARRAY) def log_result(result, params): for p in params: if log_param(p): return True return False def mk_log_macro(file, name, params): file.write("#define LOG_%s(" % name) i = 0 for p in params: if i > 0: file.write(", ") file.write("_ARG%s" % i) i = i + 1 file.write(") z3_log_ctx _LOG_CTX; ") auxs = set() i = 0 for p in params: if log_param(p): kind = param_kind(p) if kind == OUT_ARRAY or kind == INOUT_ARRAY: cap = param_array_capacity_pos(p) if cap not in auxs: auxs.add(cap) file.write("unsigned _Z3_UNUSED Z3ARG%s; " % cap) sz = param_array_size_pos(p) if sz not in auxs: auxs.add(sz) file.write("unsigned * _Z3_UNUSED Z3ARG%s; " % sz) file.write("%s _Z3_UNUSED Z3ARG%s; " % (param2str(p), i)) i = i + 1 file.write("if (_LOG_CTX.enabled()) { log_%s(" % name) i = 0 for p in params: if (i > 0): file.write(', ') file.write("_ARG%s" %i) i = i + 1 file.write("); ") auxs = set() i = 0 for p in params: if log_param(p): kind = param_kind(p) if kind == OUT_ARRAY or kind == INOUT_ARRAY: cap = param_array_capacity_pos(p) if cap not in auxs: auxs.add(cap) file.write("Z3ARG%s = _ARG%s; " % (cap, cap)) sz = param_array_size_pos(p) if sz not in auxs: auxs.add(sz) file.write("Z3ARG%s = _ARG%s; " % (sz, sz)) file.write("Z3ARG%s = _ARG%s; " % (i, i)) i = i + 1 file.write("}\n") def mk_log_result_macro(file, name, result, params): file.write("#define RETURN_%s" % name) if is_obj(result): file.write("(Z3RES)") file.write(" ") file.write("if (_LOG_CTX.enabled()) { ") if is_obj(result): file.write("SetR(Z3RES); ") i = 0 for p in params: if log_param(p): kind = param_kind(p) if kind == OUT_ARRAY or kind == INOUT_ARRAY: cap = param_array_capacity_pos(p) sz = param_array_size_pos(p) if cap == sz: file.write("for (unsigned i = 0; i < Z3ARG%s; i++) { SetAO(Z3ARG%s[i], %s, i); } " % (sz, i, i)) else: file.write("for (unsigned i = 0; Z3ARG%s && i < *Z3ARG%s; i++) { SetAO(Z3ARG%s[i], %s, i); } " % (sz, sz, i, i)) if kind == OUT or kind == INOUT: file.write("SetO((Z3ARG%s == 0 ? 0 : *Z3ARG%s), %s); " % (i, i, i)) i = i + 1 file.write("} ") if is_obj(result): file.write("return Z3RES\n") else: file.write("return\n") def mk_exec_header(file, name): file.write("void exec_%s(z3_replayer & in)" % name) def error(msg): sys.stderr.write(msg) exit(-1) next_id = 0 API2Id = {} def def_API(name, result, params): global API2Id, next_id global log_h, log_c mk_py_binding(name, result, params) reg_dotnet(name, result, params) API2Id[next_id] = name mk_log_header(log_h, name, params) log_h.write(';\n') mk_log_header(log_c, name, params) log_c.write(' {\n R();\n') mk_exec_header(exe_c, name) exe_c.write(' {\n') # Create Log function & Function call i = 0 exe_c.write(" ") if is_obj(result): exe_c.write("%s result = " % type2str(result)) exe_c.write("%s(\n " % name) for p in params: kind = param_kind(p) ty = param_type(p) if (i > 0): exe_c.write(",\n ") if kind == IN: if is_obj(ty): log_c.write(" P(a%s);\n" % i) exe_c.write("reinterpret_cast<%s>(in.get_obj(%s))" % (param2str(p), i)) elif ty == STRING: log_c.write(" S(a%s);\n" % i) exe_c.write("in.get_str(%s)" % i) elif ty == SYMBOL: log_c.write(" Sy(a%s);\n" % i) exe_c.write("in.get_symbol(%s)" % i) elif ty == UINT: log_c.write(" U(a%s);\n" % i) exe_c.write("in.get_uint(%s)" % i) elif ty == UINT64: log_c.write(" U(a%s);\n" % i) exe_c.write("in.get_uint64(%s)" % i) elif ty == INT: log_c.write(" I(a%s);\n" % i) exe_c.write("in.get_int(%s)" % i) elif ty == INT64: log_c.write(" I(a%s);\n" % i) exe_c.write("in.get_int64(%s)" % i) elif ty == DOUBLE: log_c.write(" D(a%s);\n" % i) exe_c.write("in.get_double(%s)" % i) elif ty == FLOAT: log_c.write(" D(a%s);\n" % i) exe_c.write("in.get_float(%s)" % i) elif ty == BOOL: log_c.write(" I(a%s);\n" % i) exe_c.write("in.get_bool(%s)" % i) elif ty == PRINT_MODE or ty == ERROR_CODE: log_c.write(" U(static_cast(a%s));\n" % i); exe_c.write("static_cast<%s>(in.get_uint(%s))" % (type2str(ty), i)) else: error("unsupported parameter for %s, %s" % (name, p)) elif kind == INOUT: error("unsupported parameter for %s, %s" % (name, p)) elif kind == OUT: if is_obj(ty): log_c.write(" P(0);\n") exe_c.write("reinterpret_cast<%s>(in.get_obj_addr(%s))" % (param2str(p), i)) elif ty == STRING: log_c.write(" S(\"\");\n") exe_c.write("in.get_str_addr(%s)" % i) elif ty == UINT: log_c.write(" U(0);\n") exe_c.write("in.get_uint_addr(%s)" % i) elif ty == UINT64: log_c.write(" U(0);\n") exe_c.write("in.get_uint64_addr(%s)" % i) elif ty == INT: log_c.write(" I(0);\n") exe_c.write("in.get_int_addr(%s)" % i) elif ty == INT64: log_c.write(" I(0);\n") exe_c.write("in.get_int64_addr(%s)" % i) elif ty == VOID_PTR: log_c.write(" P(0);\n") exe_c.write("in.get_obj_addr(%s)" % i) else: error("unsupported parameter for %s, %s" % (name, p)) elif kind == IN_ARRAY or kind == INOUT_ARRAY: sz = param_array_capacity_pos(p) log_c.write(" for (unsigned i = 0; i < a%s; i++) { " % sz) if is_obj(ty): log_c.write("P(a%s[i]);" % i) log_c.write(" }\n") log_c.write(" Ap(a%s);\n" % sz) exe_c.write("reinterpret_cast<%s*>(in.get_obj_array(%s))" % (type2str(ty), i)) elif ty == SYMBOL: log_c.write("Sy(a%s[i]);" % i) log_c.write(" }\n") log_c.write(" Asy(a%s);\n" % sz) exe_c.write("in.get_symbol_array(%s)" % i) elif ty == UINT: log_c.write("U(a%s[i]);" % i) log_c.write(" }\n") log_c.write(" Au(a%s);\n" % sz) exe_c.write("in.get_uint_array(%s)" % i) elif ty == INT: log_c.write("U(a%s[i]);" % i) log_c.write(" }\n") log_c.write(" Au(a%s);\n" % sz) exe_c.write("in.get_int_array(%s)" % i) else: error ("unsupported parameter for %s, %s" % (ty, name, p)) elif kind == OUT_ARRAY: sz = param_array_capacity_pos(p) sz_p = params[sz] sz_p_k = param_kind(sz_p) tstr = type2str(ty) if sz_p_k == OUT or sz_p_k == INOUT: sz_e = ("(*a%s)" % sz) else: sz_e = ("a%s" % sz) log_c.write(" for (unsigned i = 0; i < %s; i++) { " % sz_e) if is_obj(ty): log_c.write("P(0);") log_c.write(" }\n") log_c.write(" Ap(%s);\n" % sz_e) exe_c.write("reinterpret_cast<%s*>(in.get_obj_array(%s))" % (tstr, i)) elif ty == UINT: log_c.write("U(0);") log_c.write(" }\n") log_c.write(" Au(%s);\n" % sz_e) exe_c.write("in.get_uint_array(%s)" % i) else: error ("unsupported parameter for %s, %s" % (name, p)) elif kind == OUT_MANAGED_ARRAY: sz = param_array_size_pos(p) sz_p = params[sz] sz_p_k = param_kind(sz_p) tstr = type2str(ty) if sz_p_k == OUT or sz_p_k == INOUT: sz_e = ("(*a%s)" % sz) else: sz_e = ("a%s" % sz) log_c.write(" for (unsigned i = 0; i < %s; i++) { " % sz_e) log_c.write("P(0);") log_c.write(" }\n") log_c.write(" Ap(%s);\n" % sz_e) exe_c.write("reinterpret_cast<%s**>(in.get_obj_array(%s))" % (tstr, i)) else: error ("unsupported parameter for %s, %s" % (name, p)) i = i + 1 log_c.write(" C(%s);\n" % next_id) exe_c.write(");\n") if is_obj(result): exe_c.write(" in.store_result(result);\n") if name == 'Z3_mk_context' or name == 'Z3_mk_context_rc': exe_c.write(" Z3_set_error_handler(result, Z3_replacer_error_handler);") log_c.write('}\n') exe_c.write('}\n') mk_log_macro(log_h, name, params) if log_result(result, params): mk_log_result_macro(log_h, name, result, params) next_id = next_id + 1 def mk_bindings(): exe_c.write("void register_z3_replayer_cmds(z3_replayer & in) {\n") for key, val in API2Id.items(): exe_c.write(" in.register_cmd(%s, exec_%s);\n" % (key, val)) exe_c.write("}\n") def ml_method_name(name): return name[3:] # Remove Z3_ def is_out_param(p): if param_kind(p) == OUT or param_kind(p) == INOUT or param_kind(p) == OUT_ARRAY or param_kind(p) == INOUT_ARRAY or param_kind(p) == OUT_MANAGED_ARRAY: return True else: return False def outparams(params): op = [] for param in params: if is_out_param(param): op.append(param) return op def is_in_param(p): if param_kind(p) == IN or param_kind(p) == INOUT or param_kind(p) == IN_ARRAY or param_kind(p) == INOUT_ARRAY: return True else: return False def inparams(params): ip = [] for param in params: if is_in_param(param): ip.append(param) return ip def is_array_param(p): if param_kind(p) == IN_ARRAY or param_kind(p) == INOUT_ARRAY or param_kind(p) == OUT_ARRAY: return True else: return False def arrayparams(params): op = [] for param in params: if is_array_param(param): op.append(param) return op def ml_unwrap(t, ts, s): if t == STRING: return '(' + ts + ') String_val(' + s + ')' elif t == BOOL: return '(' + ts + ') Bool_val(' + s + ')' elif t == INT or t == PRINT_MODE or t == ERROR_CODE: return '(' + ts + ') Int_val(' + s + ')' elif t == UINT: return '(' + ts + ') Unsigned_int_val(' + s + ')' elif t == INT64: return '(' + ts + ') Long_val(' + s + ')' elif t == UINT64: return '(' + ts + ') Unsigned_long_val(' + s + ')' elif t == DOUBLE: return '(' + ts + ') Double_val(' + s + ')' else: return '* (' + ts + '*) Data_custom_val(' + s + ')' def ml_set_wrap(t, d, n): if t == VOID: return d + ' = Val_unit;' elif t == BOOL: return d + ' = Val_bool(' + n + ');' elif t == INT or t == UINT or t == PRINT_MODE or t == ERROR_CODE: return d + ' = Val_int(' + n + ');' elif t == INT64 or t == UINT64: return d + ' = Val_long(' + n + ');' elif t == DOUBLE: return 'Store_double_val(' + d + ', ' + n + ');' elif t == STRING: return d + ' = caml_copy_string((const char*) ' + n + ');' else: ts = type2str(t) return d + ' = caml_alloc_custom(&default_custom_ops, sizeof(' + ts + '), 0, 1); memcpy( Data_custom_val(' + d + '), &' + n + ', sizeof(' + ts + '));' def mk_ml(): global Type2Str if not is_ml_enabled(): return ml_dir = get_component('ml').src_dir ml_nativef = os.path.join(ml_dir, 'z3native.ml') ml_nativefi = os.path.join(ml_dir, 'z3native.mli') ml_wrapperf = os.path.join(ml_dir, 'z3native_stubs.c') ml_native = open(ml_nativef, 'w') ml_i = open(ml_nativefi, 'w') ml_native.write('(* Automatically generated file *)\n\n') ml_native.write('(** The native (raw) interface to the dynamic Z3 library. *)\n\n') ml_i.write('(* Automatically generated file *)\n\n') ml_i.write('(** The native (raw) interface to the dynamic Z3 library. *)\n\n') ml_i.write('(**/**)\n\n'); ml_native.write('open Z3enums\n\n') ml_native.write('(**/**)\n') ml_native.write('type ptr\n') ml_i.write('type ptr\n') ml_native.write('and z3_symbol = ptr\n') ml_i.write('and z3_symbol = ptr\n') for k, v in Type2Str.items(): if is_obj(k): ml_native.write('and %s = ptr\n' % v.lower()) ml_i.write('and %s = ptr\n' % v.lower()) ml_native.write('\n') ml_i.write('\n') ml_native.write('external is_null : ptr -> bool\n = "n_is_null"\n\n') ml_native.write('external mk_null : unit -> ptr\n = "n_mk_null"\n\n') ml_native.write('external set_internal_error_handler : ptr -> unit\n = "n_set_internal_error_handler"\n\n') ml_native.write('exception Exception of string\n\n') ml_i.write('val is_null : ptr -> bool\n') ml_i.write('val mk_null : unit -> ptr\n') ml_i.write('val set_internal_error_handler : ptr -> unit\n\n') ml_i.write('exception Exception of string\n\n') # ML declarations ml_native.write('module ML2C = struct\n\n') for name, result, params in _dotnet_decls: ml_native.write(' external n_%s : ' % ml_method_name(name)) ml_i.write('val %s : ' % ml_method_name(name)) ip = inparams(params) op = outparams(params) if len(ip) == 0: ml_native.write(' unit -> ') ml_i.write(' unit -> ') for p in ip: ml_native.write('%s -> ' % param2ml(p)) ml_i.write('%s -> ' % param2ml(p)) if len(op) > 0: ml_native.write('(') ml_i.write('(') first = True if result != VOID or len(op) == 0: ml_native.write('%s' % type2ml(result)) ml_i.write('%s' % type2ml(result)) first = False for p in op: if first: first = False else: ml_native.write(' * ') ml_i.write(' * ') ml_native.write('%s' % param2ml(p)) ml_i.write('%s' % param2ml(p)) if len(op) > 0: ml_native.write(')') ml_i.write(')') ml_native.write('\n') ml_i.write('\n') if len(ip) > 5: ml_native.write(' = "n_%s_bytecode"\n' % ml_method_name(name)) ml_native.write(' "n_%s"\n' % ml_method_name(name)) else: ml_native.write(' = "n_%s"\n' % ml_method_name(name)) ml_native.write('\n') ml_native.write(' end\n\n') ml_i.write('\n(**/**)\n'); # Exception wrappers for name, result, params in _dotnet_decls: ip = inparams(params) op = outparams(params) ml_native.write(' let %s ' % ml_method_name(name)) first = True i = 0; for p in params: if is_in_param(p): if first: first = False; else: ml_native.write(' ') ml_native.write('a%d' % i) i = i + 1 if len(ip) == 0: ml_native.write('()') ml_native.write(' = \n') ml_native.write(' ') if result == VOID and len(op) == 0: ml_native.write('let _ = ') else: ml_native.write('let res = ') ml_native.write('(ML2C.n_%s' % (ml_method_name(name))) if len(ip) == 0: ml_native.write(' ()') first = True i = 0; for p in params: if is_in_param(p): ml_native.write(' a%d' % i) i = i + 1 ml_native.write(') in\n') if name not in Unwrapped and len(params) > 0 and param_type(params[0]) == CONTEXT: ml_native.write(' let err = (error_code_of_int (ML2C.n_get_error_code a0)) in \n') ml_native.write(' if err <> OK then\n') ml_native.write(' raise (Exception (ML2C.n_get_error_msg_ex a0 (int_of_error_code err)))\n') ml_native.write(' else\n') if result == VOID and len(op) == 0: ml_native.write(' ()\n') else: ml_native.write(' res\n') ml_native.write('\n') ml_native.write('(**/**)\n') # C interface ml_wrapper = open(ml_wrapperf, 'w') ml_wrapper.write('// Automatically generated file\n\n') ml_wrapper.write('#include \n') ml_wrapper.write('#include \n\n') ml_wrapper.write('#ifdef __cplusplus\n') ml_wrapper.write('extern "C" {\n') ml_wrapper.write('#endif\n') ml_wrapper.write('#include \n') ml_wrapper.write('#include \n') ml_wrapper.write('#include \n') ml_wrapper.write('#include \n') ml_wrapper.write('#include \n') ml_wrapper.write('#ifdef Custom_tag\n') ml_wrapper.write('#include \n') ml_wrapper.write('#include \n') ml_wrapper.write('#endif\n') ml_wrapper.write('#ifdef __cplusplus\n') ml_wrapper.write('}\n') ml_wrapper.write('#endif\n\n') ml_wrapper.write('#include \n\n') ml_wrapper.write('#define CAMLlocal6(X1,X2,X3,X4,X5,X6) \\\n') ml_wrapper.write(' CAMLlocal5(X1,X2,X3,X4,X5); \\\n') ml_wrapper.write(' CAMLlocal1(X6) \n') ml_wrapper.write('#define CAMLlocal7(X1,X2,X3,X4,X5,X6,X7) \\\n') ml_wrapper.write(' CAMLlocal5(X1,X2,X3,X4,X5); \\\n') ml_wrapper.write(' CAMLlocal2(X6,X7) \n') ml_wrapper.write('#define CAMLlocal8(X1,X2,X3,X4,X5,X6,X7,X8) \\\n') ml_wrapper.write(' CAMLlocal5(X1,X2,X3,X4,X5); \\\n') ml_wrapper.write(' CAMLlocal3(X6,X7,X8) \n') ml_wrapper.write('\n') ml_wrapper.write('#define CAMLparam7(X1,X2,X3,X4,X5,X6,X7) \\\n') ml_wrapper.write(' CAMLparam5(X1,X2,X3,X4,X5); \\\n') ml_wrapper.write(' CAMLxparam2(X6,X7) \n') ml_wrapper.write('#define CAMLparam8(X1,X2,X3,X4,X5,X6,X7,X8) \\\n') ml_wrapper.write(' CAMLparam5(X1,X2,X3,X4,X5); \\\n') ml_wrapper.write(' CAMLxparam3(X6,X7,X8) \n') ml_wrapper.write('#define CAMLparam9(X1,X2,X3,X4,X5,X6,X7,X8,X9) \\\n') ml_wrapper.write(' CAMLparam5(X1,X2,X3,X4,X5); \\\n') ml_wrapper.write(' CAMLxparam4(X6,X7,X8,X9) \n') ml_wrapper.write('#define CAMLparam12(X1,X2,X3,X4,X5,X6,X7,X8,X9,X10,X11,X12) \\\n') ml_wrapper.write(' CAMLparam5(X1,X2,X3,X4,X5); \\\n') ml_wrapper.write(' CAMLxparam5(X6,X7,X8,X9,X10); \\\n') ml_wrapper.write(' CAMLxparam2(X11,X12) \n') ml_wrapper.write('#define CAMLparam13(X1,X2,X3,X4,X5,X6,X7,X8,X9,X10,X11,X12,X13) \\\n') ml_wrapper.write(' CAMLparam5(X1,X2,X3,X4,X5); \\\n') ml_wrapper.write(' CAMLxparam5(X6,X7,X8,X9,X10); \\\n') ml_wrapper.write(' CAMLxparam3(X11,X12,X13) \n') ml_wrapper.write('\n\n') ml_wrapper.write('static struct custom_operations default_custom_ops = {\n') ml_wrapper.write(' (char*) "default handling",\n') ml_wrapper.write(' custom_finalize_default,\n') ml_wrapper.write(' custom_compare_default,\n') ml_wrapper.write(' custom_hash_default,\n') ml_wrapper.write(' custom_serialize_default,\n') ml_wrapper.write(' custom_deserialize_default\n') ml_wrapper.write('};\n\n') ml_wrapper.write('#ifdef __cplusplus\n') ml_wrapper.write('extern "C" {\n') ml_wrapper.write('#endif\n\n') ml_wrapper.write('CAMLprim value n_is_null(value p) {\n') ml_wrapper.write(' void * t = * (void**) Data_custom_val(p);\n') ml_wrapper.write(' return Val_bool(t == 0);\n') ml_wrapper.write('}\n\n') ml_wrapper.write('CAMLprim value n_mk_null( void ) {\n') ml_wrapper.write(' CAMLparam0();\n') ml_wrapper.write(' CAMLlocal1(result);\n') ml_wrapper.write(' void * z3_result = 0;\n') ml_wrapper.write(' result = caml_alloc_custom(&default_custom_ops, sizeof(void*), 0, 1);\n') ml_wrapper.write(' memcpy( Data_custom_val(result), &z3_result, sizeof(void*));\n') ml_wrapper.write(' CAMLreturn (result);\n') ml_wrapper.write('}\n\n') ml_wrapper.write('void MLErrorHandler(Z3_context c, Z3_error_code e)\n') ml_wrapper.write('{\n') ml_wrapper.write(' // Internal do-nothing error handler. This is required to avoid that Z3 calls exit()\n') ml_wrapper.write(' // upon errors, but the actual error handling is done by throwing exceptions in the\n') ml_wrapper.write(' // wrappers below.\n') ml_wrapper.write('}\n\n') ml_wrapper.write('void n_set_internal_error_handler(value a0)\n') ml_wrapper.write('{\n') ml_wrapper.write(' Z3_context _a0 = * (Z3_context*) Data_custom_val(a0);\n') ml_wrapper.write(' Z3_set_error_handler(_a0, MLErrorHandler);\n') ml_wrapper.write('}\n\n') for name, result, params in _dotnet_decls: ip = inparams(params) op = outparams(params) ap = arrayparams(params) ret_size = len(op) if result != VOID: ret_size = ret_size + 1 # Setup frame ml_wrapper.write('CAMLprim value n_%s(' % ml_method_name(name)) first = True i = 0 for p in params: if is_in_param(p): if first: first = False else: ml_wrapper.write(', ') ml_wrapper.write('value a%d' % i) i = i + 1 ml_wrapper.write(') {\n') ml_wrapper.write(' CAMLparam%d(' % len(ip)) i = 0 first = True for p in params: if is_in_param(p): if first: first = False else: ml_wrapper.write(', ') ml_wrapper.write('a%d' % i) i = i + 1 ml_wrapper.write(');\n') i = 0 if len(op) + len(ap) == 0: ml_wrapper.write(' CAMLlocal1(result);\n') else: c = 0 for p in params: if is_out_param(p) or is_array_param(p): c = c + 1 ml_wrapper.write(' CAMLlocal%s(result, res_val' % (c+2)) for p in params: if is_out_param(p) or is_array_param(p): ml_wrapper.write(', _a%s_val' % i) i = i + 1 ml_wrapper.write(');\n') if len(ap) != 0: ml_wrapper.write(' unsigned _i;\n') # declare locals, preprocess arrays, strings, in/out arguments i = 0 for param in params: k = param_kind(param) if k == OUT_ARRAY: ml_wrapper.write(' %s * _a%s = (%s*) malloc(sizeof(%s) * (_a%s));\n' % ( type2str(param_type(param)), i, type2str(param_type(param)), type2str(param_type(param)), param_array_capacity_pos(param))) elif k == OUT_MANAGED_ARRAY: ml_wrapper.write(' %s * _a%s = 0;\n' % (type2str(param_type(param)), i)) elif k == IN_ARRAY or k == INOUT_ARRAY: t = param_type(param) ts = type2str(t) ml_wrapper.write(' %s * _a%s = (%s*) malloc(sizeof(%s) * _a%s);\n' % (ts, i, ts, ts, param_array_capacity_pos(param))) elif k == IN: t = param_type(param) ml_wrapper.write(' %s _a%s = %s;\n' % (type2str(t), i, ml_unwrap(t, type2str(t), 'a' + str(i)))) elif k == OUT: ml_wrapper.write(' %s _a%s;\n' % (type2str(param_type(param)), i)) elif k == INOUT: ml_wrapper.write(' %s _a%s = a%s;\n' % (type2str(param_type(param)), i, i)) i = i + 1 if result != VOID: ml_wrapper.write(' %s z3_result;\n' % type2str(result)) i = 0 for param in params: k = param_kind(param) if k == IN_ARRAY or k == INOUT_ARRAY: t = param_type(param) ts = type2str(t) ml_wrapper.write(' for (_i = 0; _i < _a%s; _i++) { _a%s[_i] = %s; }\n' % (param_array_capacity_pos(param), i, ml_unwrap(t, ts, 'Field(a' + str(i) + ', _i)'))) i = i + 1 # invoke procedure ml_wrapper.write(' ') if result != VOID: ml_wrapper.write('z3_result = ') ml_wrapper.write('%s(' % name) i = 0 first = True for param in params: if first: first = False else: ml_wrapper.write(', ') k = param_kind(param) if k == OUT or k == INOUT or k == OUT_MANAGED_ARRAY: ml_wrapper.write('&_a%s' % i) else: ml_wrapper.write('_a%i' % i) i = i + 1 ml_wrapper.write(');\n') # convert output params if len(op) > 0: if result != VOID: ml_wrapper.write(' %s\n' % ml_set_wrap(result, "res_val", "z3_result")) i = 0; for p in params: if param_kind(p) == OUT_ARRAY or param_kind(p) == INOUT_ARRAY: ml_wrapper.write(' _a%s_val = caml_alloc(_a%s, 0);\n' % (i, param_array_capacity_pos(p))) ml_wrapper.write(' for (_i = 0; _i < _a%s; _i++) { value t; %s Store_field(_a%s_val, _i, t); }\n' % (param_array_capacity_pos(p), ml_set_wrap(param_type(p), 't', '_a' + str(i) + '[_i]'), i)) elif param_kind(p) == OUT_MANAGED_ARRAY: ml_wrapper.write(' %s\n' % ml_set_wrap(param_type(p), "_a" + str(i) + "_val", "_a" + str(i) )) elif is_out_param(p): ml_wrapper.write(' %s\n' % ml_set_wrap(param_type(p), "_a" + str(i) + "_val", "_a" + str(i) )) i = i + 1 # return tuples if len(op) == 0: ml_wrapper.write(' %s\n' % ml_set_wrap(result, "result", "z3_result")) else: ml_wrapper.write(' result = caml_alloc(%s, 0);\n' % ret_size) i = j = 0 if result != VOID: ml_wrapper.write(' Store_field(result, 0, res_val);\n') j = j + 1 for p in params: if is_out_param(p): ml_wrapper.write(' Store_field(result, %s, _a%s_val);\n' % (j, i)) j = j + 1; i = i + 1 # local array cleanup i = 0 for p in params: k = param_kind(p) if k == OUT_ARRAY or k == IN_ARRAY or k == INOUT_ARRAY: ml_wrapper.write(' free(_a%s);\n' % i) i = i + 1 # return ml_wrapper.write(' CAMLreturn(result);\n') ml_wrapper.write('}\n\n') if len(ip) > 5: ml_wrapper.write('CAMLprim value n_%s_bytecode(value * argv, int argn) {\n' % ml_method_name(name)) ml_wrapper.write(' return n_%s(' % ml_method_name(name)) i = 0 while i < len(ip): if i == 0: ml_wrapper.write('argv[0]') else: ml_wrapper.write(', argv[%s]' % i) i = i + 1 ml_wrapper.write(');\n}\n') ml_wrapper.write('\n\n') ml_wrapper.write('#ifdef __cplusplus\n') ml_wrapper.write('}\n') ml_wrapper.write('#endif\n') if is_verbose(): print ('Generated "%s"' % ml_nativef) # Collect API(...) commands from def def_APIs(): pat1 = re.compile(" *def_API.*") pat2 = re.compile(" *extra_API.*") for api_file in API_FILES: api = open(api_file, 'r') for line in api: line = line.strip('\r\n\t ') try: m = pat1.match(line) if m: eval(line) m = pat2.match(line) if m: eval(line) except Exception: raise MKException("Failed to process API definition: %s" % line) def_Types() def_APIs() mk_bindings() mk_py_wrappers() mk_dotnet() mk_dotnet_wrappers() mk_java() mk_ml() log_h.close() log_c.close() exe_c.close() core_py.close() if is_verbose(): print("Generated '%s'" % os.path.join(api_dir, 'api_log_macros.h')) print("Generated '%s'" % os.path.join(api_dir, 'api_log_macros.cpp')) print("Generated '%s'" % os.path.join(api_dir, 'api_commands.cpp')) print("Generated '%s'" % os.path.join(get_z3py_dir(), 'z3core.py')) print("Generated '%s'" % os.path.join(dotnet_dir, 'Native.cs')) z3-4.4.0/scripts/trackall.sh0000755000175000017500000000054112540347414015470 0ustar michaelmichael#!/bin/bash # Copyright (c) 2015 Microsoft Corporation # Script for "cloning" (and tracking) all branches at codeplex. # On Windows, this script must be executed in the "git Bash" console. for branch in `git branch -a | grep remotes | grep -v HEAD | grep -v master`; do git branch --track ${branch##*/} $branch done git fetch --all git pull --all z3-4.4.0/scripts/mk_win_dist.py0000644000175000017500000002075012540347414016221 0ustar michaelmichael############################################ # Copyright (c) 2012 Microsoft Corporation # # Scripts for automatically generating # Window distribution zip files. # # Author: Leonardo de Moura (leonardo) ############################################ import os import glob import re import getopt import sys import shutil import subprocess import zipfile from mk_exception import * from mk_project import * import mk_util BUILD_DIR='build-dist' BUILD_X64_DIR=os.path.join('build-dist', 'x64') BUILD_X86_DIR=os.path.join('build-dist', 'x86') VERBOSE=True DIST_DIR='dist' FORCE_MK=False JAVA_ENABLED=True GIT_HASH=False def set_verbose(flag): global VERBOSE VERBOSE = flag def is_verbose(): return VERBOSE def mk_dir(d): if not os.path.exists(d): os.makedirs(d) def set_build_dir(path): global BUILD_DIR, BUILD_X86_DIR, BUILD_X64_DIR BUILD_DIR = path BUILD_X86_DIR = os.path.join(path, 'x86') BUILD_X64_DIR = os.path.join(path, 'x64') mk_dir(BUILD_X86_DIR) mk_dir(BUILD_X64_DIR) def display_help(): print("mk_win_dist.py: Z3 Windows distribution generator\n") print("This script generates the zip files containing executables, dlls, header files for Windows.") print("It must be executed from the Z3 root directory.") print("\nOptions:") print(" -h, --help display this message.") print(" -s, --silent do not print verbose messages.") print(" -b , --build= subdirectory where x86 and x64 Z3 versions will be built (default: build-dist).") print(" -f, --force force script to regenerate Makefiles.") print(" --nojava do not include Java bindings in the binary distribution files.") print(" --githash include git hash in the Zip file.") exit(0) # Parse configuration option for mk_make script def parse_options(): global FORCE_MK, JAVA_ENABLED, GIT_HASH path = BUILD_DIR options, remainder = getopt.gnu_getopt(sys.argv[1:], 'b:hsf', ['build=', 'help', 'silent', 'force', 'nojava', 'githash' ]) for opt, arg in options: if opt in ('-b', '--build'): if arg == 'src': raise MKException('The src directory should not be used to host the Makefile') path = arg elif opt in ('-s', '--silent'): set_verbose(False) elif opt in ('-h', '--help'): display_help() elif opt in ('-f', '--force'): FORCE_MK = True elif opt == '--nojava': JAVA_ENABLED = False elif opt == '--githash': GIT_HASH = True else: raise MKException("Invalid command line option '%s'" % opt) set_build_dir(path) # Check whether build directory already exists or not def check_build_dir(path): return os.path.exists(path) and os.path.exists(os.path.join(path, 'Makefile')) # Create a build directory using mk_make.py def mk_build_dir(path, x64): if not check_build_dir(path) or FORCE_MK: opts = ["python", os.path.join('scripts', 'mk_make.py'), "--parallel=24", "-b", path] if JAVA_ENABLED: opts.append('--java') if x64: opts.append('-x') if GIT_HASH: opts.append('--githash=%s' % mk_util.git_hash()) if subprocess.call(opts) != 0: raise MKException("Failed to generate build directory at '%s'" % path) # Create build directories def mk_build_dirs(): mk_build_dir(BUILD_X86_DIR, False) mk_build_dir(BUILD_X64_DIR, True) # Check if on Visual Studio command prompt def check_vc_cmd_prompt(): try: DEVNULL = open(os.devnull, 'wb') subprocess.call(['cl'], stdout=DEVNULL, stderr=DEVNULL) except: raise MKException("You must execute the mk_win_dist.py script on a Visual Studio Command Prompt") def exec_cmds(cmds): cmd_file = 'z3_tmp.cmd' f = open(cmd_file, 'w') for cmd in cmds: f.write(cmd) f.write('\n') f.close() res = 0 try: res = subprocess.call(cmd_file, shell=True) except: res = 1 try: os.erase(cmd_file) except: pass return res # Compile Z3 (if x64 == True, then it builds it in x64 mode). def mk_z3_core(x64): cmds = [] if x64: cmds.append('call "%VCINSTALLDIR%vcvarsall.bat" amd64') cmds.append('cd %s' % BUILD_X64_DIR) else: cmds.append('call "%VCINSTALLDIR%vcvarsall.bat" x86') cmds.append('cd %s' % BUILD_X86_DIR) cmds.append('nmake') if exec_cmds(cmds) != 0: raise MKException("Failed to make z3, x64: %s" % x64) def mk_z3(): mk_z3_core(False) mk_z3_core(True) def get_z3_name(x64): major, minor, build, revision = get_version() if x64: platform = "x64" else: platform = "x86" if GIT_HASH: return 'z3-%s.%s.%s.%s-%s-win' % (major, minor, build, mk_util.git_hash(), platform) else: return 'z3-%s.%s.%s-%s-win' % (major, minor, build, platform) def mk_dist_dir_core(x64): if x64: platform = "x64" build_path = BUILD_X64_DIR else: platform = "x86" build_path = BUILD_X86_DIR dist_path = os.path.join(DIST_DIR, get_z3_name(x64)) mk_dir(dist_path) if JAVA_ENABLED: # HACK: Propagate JAVA_ENABLED flag to mk_util # TODO: fix this hack mk_util.JAVA_ENABLED = JAVA_ENABLED mk_win_dist(build_path, dist_path) if is_verbose(): print("Generated %s distribution folder at '%s'" % (platform, dist_path)) def mk_dist_dir(): mk_dist_dir_core(False) mk_dist_dir_core(True) ZIPOUT = None def mk_zip_visitor(pattern, dir, files): for filename in files: if fnmatch(filename, pattern): fname = os.path.join(dir, filename) if not os.path.isdir(fname): ZIPOUT.write(fname) def get_dist_path(x64): return get_z3_name(x64) def mk_zip_core(x64): global ZIPOUT dist_path = get_dist_path(x64) old = os.getcwd() try: os.chdir(DIST_DIR) zfname = '%s.zip' % dist_path ZIPOUT = zipfile.ZipFile(zfname, 'w', zipfile.ZIP_DEFLATED) os.path.walk(dist_path, mk_zip_visitor, '*') if is_verbose(): print("Generated '%s'" % zfname) except: pass ZIPOUT = None os.chdir(old) # Create a zip file for each platform def mk_zip(): mk_zip_core(False) mk_zip_core(True) VS_RUNTIME_PATS = [re.compile('vcomp.*\.dll'), re.compile('msvcp.*\.dll'), re.compile('msvcr.*\.dll')] VS_RUNTIME_FILES = [] def cp_vs_runtime_visitor(pattern, dir, files): global VS_RUNTIME_FILES for filename in files: for pat in VS_RUNTIME_PATS: if pat.match(filename): if fnmatch(filename, pattern): fname = os.path.join(dir, filename) if not os.path.isdir(fname): VS_RUNTIME_FILES.append(fname) break # Copy Visual Studio Runtime libraries def cp_vs_runtime_core(x64): global VS_RUNTIME_FILES if x64: platform = "x64" else: platform = "x86" vcdir = subprocess.check_output(['echo', '%VCINSTALLDIR%'], shell=True).rstrip('\r\n') path = '%sredist\\%s' % (vcdir, platform) VS_RUNTIME_FILES = [] os.path.walk(path, cp_vs_runtime_visitor, '*.dll') bin_dist_path = os.path.join(DIST_DIR, get_dist_path(x64), 'bin') for f in VS_RUNTIME_FILES: shutil.copy(f, bin_dist_path) if is_verbose(): print("Copied '%s' to '%s'" % (f, bin_dist_path)) def cp_vs_runtime(): cp_vs_runtime_core(True) cp_vs_runtime_core(False) def cp_license(): shutil.copy("LICENSE.txt", os.path.join(DIST_DIR, get_dist_path(True))) shutil.copy("LICENSE.txt", os.path.join(DIST_DIR, get_dist_path(False))) # Entry point def main(): if os.name != 'nt': raise MKException("This script is for Windows only") parse_options() check_vc_cmd_prompt() mk_build_dirs() mk_z3() init_project_def() mk_dist_dir() cp_license() cp_vs_runtime() mk_zip() main() z3-4.4.0/README0000644000175000017500000000240212540347414012523 0ustar michaelmichaelZ3 is a theorem prover from Microsoft Research. Z3 is licensed under the MIT license. Z3 can be built using Visual Studio Command Prompt and make/g++. 1) Building Z3 on Windows using Visual Studio Command Prompt python scripts/mk_make.py cd build nmake 2) Building Z3 using make/g++ and Python Execute: python scripts/mk_make.py cd build make sudo make install By default, it will install z3 executable at PREFIX/bin, libraries at PREFIX/lib, and include files at PREFIX/include, where PREFIX is the installation prefix used for installing Python in your system. It is usually /usr for most Linux distros, and /usr/local for FreeBSD. Use the following commands to install in a different prefix (e.g., /home/leo) python scripts/mk_make.py --prefix=/home/leo cd build make make install In this example, the Z3 Python bindings will be stored at /home/leo/lib/pythonX.Y/dist-packages, where X.Y corresponds to the python version in your system. To uninstall Z3, use sudo make uninstall 4) Building Z3 using clang and clang++ on Linux/OSX Remark: clang does not support OpenMP yet. CXX=clang++ CC=clang python scripts/mk_make.py cd build make To clean Z3 you can delete the build directory and run the mk_make.py script again. z3-4.4.0/examples/0000755000175000017500000000000012540347414013463 5ustar michaelmichaelz3-4.4.0/examples/ml/0000755000175000017500000000000012540347414014073 5ustar michaelmichaelz3-4.4.0/examples/ml/ml_example.ml0000644000175000017500000003132012540347414016547 0ustar michaelmichael(* Copyright (C) 2012 Microsoft Corporation Author: CM Wintersteiger (cwinter) 2012-12-17 *) open Z3 open Z3.Symbol open Z3.Sort open Z3.Expr open Z3.Boolean open Z3.FuncDecl open Z3.Goal open Z3.Tactic open Z3.Tactic.ApplyResult open Z3.Probe open Z3.Solver open Z3.Arithmetic open Z3.Arithmetic.Integer open Z3.Arithmetic.Real open Z3.BitVector exception TestFailedException of string (** Model Converter test *) let model_converter_test ( ctx : context ) = Printf.printf "ModelConverterTest\n"; let xr = (Expr.mk_const ctx (Symbol.mk_string ctx "x") (Real.mk_sort ctx)) in let yr = (Expr.mk_const ctx (Symbol.mk_string ctx "y") (Real.mk_sort ctx)) in let g4 = (mk_goal ctx true false false ) in (Goal.add g4 [ (mk_gt ctx xr (Real.mk_numeral_nd ctx 10 1)) ]) ; (Goal.add g4 [ (mk_eq ctx yr (Arithmetic.mk_add ctx [ xr; (Real.mk_numeral_nd ctx 1 1) ])) ]) ; (Goal.add g4 [ (mk_gt ctx yr (Real.mk_numeral_nd ctx 1 1)) ]) ; ( let ar = (Tactic.apply (mk_tactic ctx "simplify") g4 None) in if ((get_num_subgoals ar) == 1 && ((is_decided_sat (get_subgoal ar 0)) || (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); ( let ar = (Tactic.apply (and_then ctx (mk_tactic ctx ("simplify")) (mk_tactic ctx "solve-eqs") []) g4 None) in if ((get_num_subgoals ar) == 1 && ((is_decided_sat (get_subgoal ar 0)) || (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ; let solver = (mk_solver ctx None) in let f e = (Solver.add solver [ e ]) in ignore (List.map f (get_formulas (get_subgoal ar 0))) ; let q = (check solver []) in if q != SATISFIABLE then raise (TestFailedException "") else let m = (get_model solver) in match m with | None -> raise (TestFailedException "") | Some (m) -> Printf.printf "Solver says: %s\n" (string_of_status q) ; Printf.printf "Model: \n%s\n" (Model.to_string m) ; Printf.printf "Converted Model: \n%s\n" (Model.to_string (convert_model ar 0 m)) ) (** Some basic tests. *) let basic_tests ( ctx : context ) = Printf.printf "BasicTests\n" ; let fname = (mk_string ctx "f") in let x = (mk_string ctx "x") in let y = (mk_string ctx "y") in let bs = (Boolean.mk_sort ctx) in let domain = [ bs; bs ] in let f = (FuncDecl.mk_func_decl ctx fname domain bs) in let fapp = (mk_app ctx f [ (Expr.mk_const ctx x bs); (Expr.mk_const ctx y bs) ]) in let fargs2 = [ (mk_fresh_const ctx "cp" bs) ] in let domain2 = [ bs ] in let fapp2 = (mk_app ctx (mk_fresh_func_decl ctx "fp" domain2 bs) fargs2) in let trivial_eq = (mk_eq ctx fapp fapp) in let nontrivial_eq = (mk_eq ctx fapp fapp2) in let g = (mk_goal ctx true false false) in (Goal.add g [ trivial_eq ]) ; (Goal.add g [ nontrivial_eq ]) ; Printf.printf "%s\n" ("Goal: " ^ (Goal.to_string g)) ; ( let solver = (mk_solver ctx None) in (List.iter (fun a -> (Solver.add solver [ a ])) (get_formulas g)) ; if (check solver []) != SATISFIABLE then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); ( let ar = (Tactic.apply (mk_tactic ctx "simplify") g None) in if ((get_num_subgoals ar) == 1 && ((is_decided_sat (get_subgoal ar 0)) || (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); ( let ar = (Tactic.apply (mk_tactic ctx "smt") g None) in if ((get_num_subgoals ar) == 1 && (not (is_decided_sat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); (Goal.add g [ (mk_eq ctx (mk_numeral_int ctx 1 (BitVector.mk_sort ctx 32)) (mk_numeral_int ctx 2 (BitVector.mk_sort ctx 32))) ] ) ; ( let ar = (Tactic.apply (mk_tactic ctx "smt") g None) in if ((get_num_subgoals ar) == 1 && (not (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); ( let g2 = (mk_goal ctx true true false) in let ar = (Tactic.apply (mk_tactic ctx "smt") g2 None) in if ((get_num_subgoals ar) == 1 && (not (is_decided_sat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); ( let g2 = (mk_goal ctx true true false) in (Goal.add g2 [ (Boolean.mk_false ctx) ]) ; let ar = (Tactic.apply (mk_tactic ctx "smt") g2 None) in if ((get_num_subgoals ar) == 1 && (not (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); ( let g3 = (mk_goal ctx true true false) in let xc = (Expr.mk_const ctx (Symbol.mk_string ctx "x") (Integer.mk_sort ctx)) in let yc = (Expr.mk_const ctx (Symbol.mk_string ctx "y") (Integer.mk_sort ctx)) in (Goal.add g3 [ (mk_eq ctx xc (mk_numeral_int ctx 1 (Integer.mk_sort ctx))) ]) ; (Goal.add g3 [ (mk_eq ctx yc (mk_numeral_int ctx 2 (Integer.mk_sort ctx))) ]) ; let constr = (mk_eq ctx xc yc) in (Goal.add g3 [ constr ] ) ; let ar = (Tactic.apply (mk_tactic ctx "smt") g3 None) in if ((get_num_subgoals ar) == 1 && (not (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ) ; model_converter_test ctx ; (* Real num/den test. *) let rn = Real.mk_numeral_nd ctx 42 43 in let inum = (get_numerator rn) in let iden = get_denominator rn in Printf.printf "Numerator: %s Denominator: %s\n" (Real.numeral_to_string inum) (Real.numeral_to_string iden) ; if ((Real.numeral_to_string inum) <> "42" || (Real.numeral_to_string iden) <> "43") then raise (TestFailedException "") else Printf.printf "Test passed.\n" ; if ((to_decimal_string rn 3) <> "0.976?") then raise (TestFailedException "") else Printf.printf "Test passed.\n" ; if (to_decimal_string (Real.mk_numeral_s ctx "-1231231232/234234333") 5 <> "-5.25640?") then raise (TestFailedException "") else if (to_decimal_string (Real.mk_numeral_s ctx "-123123234234234234231232/234234333") 5 <> "-525641278361333.28170?") then raise (TestFailedException "") else if (to_decimal_string (Real.mk_numeral_s ctx "-234234333") 5 <> "-234234333") then raise (TestFailedException "") else if (to_decimal_string (Real.mk_numeral_s ctx "234234333/2") 5 <> "117117166.5") then raise (TestFailedException "") ; (* Error handling test. *) try ( let i = Integer.mk_numeral_s ctx "1/2" in raise (TestFailedException (numeral_to_string i)) (* unreachable *) ) with Z3native.Exception(_) -> ( Printf.printf "Exception caught, OK.\n" ) (** A basic example of how to use quantifiers. **) let quantifier_example1 ( ctx : context ) = Printf.printf "QuantifierExample\n" ; let is = (Integer.mk_sort ctx) in let types = [ is; is; is ] in let names = [ (Symbol.mk_string ctx "x_0"); (Symbol.mk_string ctx "x_1"); (Symbol.mk_string ctx "x_2") ] in let vars = [ (Quantifier.mk_bound ctx 2 (List.nth types 0)); (Quantifier.mk_bound ctx 2 (List.nth types 1)); (Quantifier.mk_bound ctx 2 (List.nth types 2)) ] in let xs = [ (Integer.mk_const ctx (List.nth names 0)); (Integer.mk_const ctx (List.nth names 1)); (Integer.mk_const ctx (List.nth names 2)) ] in let body_vars = (Boolean.mk_and ctx [ (mk_eq ctx (Arithmetic.mk_add ctx [ (List.nth vars 0) ; (Integer.mk_numeral_i ctx 1)]) (Integer.mk_numeral_i ctx 2)) ; (mk_eq ctx (Arithmetic.mk_add ctx [ (List.nth vars 1); (Integer.mk_numeral_i ctx 2)]) (Arithmetic.mk_add ctx [ (List.nth vars 2); (Integer.mk_numeral_i ctx 3)])) ]) in let body_const = (Boolean.mk_and ctx [ (mk_eq ctx (Arithmetic.mk_add ctx [ (List.nth xs 0); (Integer.mk_numeral_i ctx 1)]) (Integer.mk_numeral_i ctx 2)) ; (mk_eq ctx (Arithmetic.mk_add ctx [ (List.nth xs 1); (Integer.mk_numeral_i ctx 2)]) (Arithmetic.mk_add ctx [ (List.nth xs 2); (Integer.mk_numeral_i ctx 3)])) ]) in let x = (Quantifier.mk_forall ctx types names body_vars (Some 1) [] [] (Some (Symbol.mk_string ctx "Q1")) (Some (Symbol.mk_string ctx "skid1"))) in Printf.printf "Quantifier X: %s\n" (Quantifier.to_string x) ; let y = (Quantifier.mk_forall_const ctx xs body_const (Some 1) [] [] (Some (Symbol.mk_string ctx "Q2")) (Some (Symbol.mk_string ctx "skid2"))) in Printf.printf "Quantifier Y: %s\n" (Quantifier.to_string y) ; if (is_true (Quantifier.expr_of_quantifier x)) then raise (TestFailedException "") (* unreachable *) else if (is_false (Quantifier.expr_of_quantifier x)) then raise (TestFailedException "") (* unreachable *) else if (is_const (Quantifier.expr_of_quantifier x)) then raise (TestFailedException "") (* unreachable *) open Z3.FloatingPoint (** A basic example of floating point arithmetic **) let fpa_example ( ctx : context ) = Printf.printf "FPAExample\n" ; (* let str = ref "" in *) (* (read_line ()) ; *) let double_sort = (FloatingPoint.mk_sort_double ctx) in let rm_sort = (FloatingPoint.RoundingMode.mk_sort ctx) in (** Show that there are x, y s.t. (x + y) = 42.0 (with rounding mode). *) let s_rm = (mk_string ctx "rm") in let rm = (mk_const ctx s_rm rm_sort) in let s_x = (mk_string ctx "x") in let s_y = (mk_string ctx "y") in let x = (mk_const ctx s_x double_sort) in let y = (mk_const ctx s_y double_sort)in let n = (FloatingPoint.mk_numeral_f ctx 42.0 double_sort) in let s_x_plus_y = (mk_string ctx "x_plus_y") in let x_plus_y = (mk_const ctx s_x_plus_y double_sort) in let c1 = (mk_eq ctx x_plus_y (mk_add ctx rm x y)) in let args = [ c1 ; (mk_eq ctx x_plus_y n) ] in let c2 = (Boolean.mk_and ctx args) in let args2 = [ c2 ; (Boolean.mk_not ctx (Boolean.mk_eq ctx rm (RoundingMode.mk_rtz ctx))) ] in let c3 = (Boolean.mk_and ctx args2) in let and_args = [ (Boolean.mk_not ctx (mk_is_zero ctx y)) ; (Boolean.mk_not ctx (mk_is_nan ctx y)) ; (Boolean.mk_not ctx (mk_is_infinite ctx y)) ] in let args3 = [ c3 ; (Boolean.mk_and ctx and_args) ] in let c4 = (Boolean.mk_and ctx args3) in (Printf.printf "c4: %s\n" (Expr.to_string c4)) ; ( let solver = (mk_solver ctx None) in (Solver.add solver [ c4 ]) ; if (check solver []) != SATISFIABLE then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); (* Show that the following are equal: *) (* (fp #b0 #b10000000001 #xc000000000000) *) (* ((_ to_fp 11 53) #x401c000000000000)) *) (* ((_ to_fp 11 53) RTZ 1.75 2))) *) (* ((_ to_fp 11 53) RTZ 7.0))) *) let c1 = (mk_fp ctx (mk_numeral_string ctx "0" (BitVector.mk_sort ctx 1)) (mk_numeral_string ctx "3377699720527872" (BitVector.mk_sort ctx 52)) (mk_numeral_string ctx "1025" (BitVector.mk_sort ctx 11))) in let c2 = (mk_to_fp_bv ctx (mk_numeral_string ctx "4619567317775286272" (BitVector.mk_sort ctx 64)) (mk_sort ctx 11 53)) in let c3 = (mk_to_fp_int_real ctx (RoundingMode.mk_rtz ctx) (mk_numeral_string ctx "2" (Integer.mk_sort ctx)) (mk_numeral_string ctx "1.75" (Real.mk_sort ctx)) (FloatingPoint.mk_sort ctx 11 53)) in let c4 = (mk_to_fp_real ctx (RoundingMode.mk_rtz ctx) (mk_numeral_string ctx "7.0" (Real.mk_sort ctx)) (FloatingPoint.mk_sort ctx 11 53)) in let args3 = [ (mk_eq ctx c1 c2) ; (mk_eq ctx c1 c3) ; (mk_eq ctx c1 c4) ] in let c5 = (Boolean.mk_and ctx args3) in (Printf.printf "c5: %s\n" (Expr.to_string c5)) ; ( let solver = (mk_solver ctx None) in (Solver.add solver [ c5 ]) ; if (check solver []) != SATISFIABLE then raise (TestFailedException "") else Printf.printf "Test passed.\n" ) let _ = try ( if not (Log.open_ "z3.log") then raise (TestFailedException "Log couldn't be opened.") else ( Printf.printf "Running Z3 version %s\n" Version.to_string ; let cfg = [("model", "true"); ("proof", "false")] in let ctx = (mk_context cfg) in let is = (Symbol.mk_int ctx 42) in let ss = (Symbol.mk_string ctx "mySymbol") in let bs = (Boolean.mk_sort ctx) in let ints = (Integer.mk_sort ctx) in let rs = (Real.mk_sort ctx) in Printf.printf "int symbol: %s\n" (Symbol.to_string is); Printf.printf "string symbol: %s\n" (Symbol.to_string ss); Printf.printf "bool sort: %s\n" (Sort.to_string bs); Printf.printf "int sort: %s\n" (Sort.to_string ints); Printf.printf "real sort: %s\n" (Sort.to_string rs); basic_tests ctx ; quantifier_example1 ctx ; fpa_example ctx ; Printf.printf "Disposing...\n"; Gc.full_major () ); Printf.printf "Exiting.\n" ; exit 0 ) with Z3native.Exception(msg) -> ( Printf.printf "Z3 EXCEPTION: %s\n" msg ; exit 1 ) ;; z3-4.4.0/examples/ml/README0000644000175000017500000000150512540347414014754 0ustar michaelmichaelSmall example using the Z3 ML bindings. To build the example execute make examples in the build directory. It will create ml_example in the build directory, which can be run in the build directory via LD_LIBRARY_PATH=. ./ml_example or LD_LIBRARY_PATH=. ./ml_example.byte for the byte-code version. If Z3 was installed into the ocamlfind package repository (see src/api/ml/README), then we can compile this example as follows: ocamlfind ocamlc -o ml_example.byte -package Z3 -linkpkg ml_example.ml ocamlfind ocamlopt -o ml_example -package Z3 -linkpkg ml_example.ml Note that the resulting binaries depend on the shared z3 library, which needs to be in the PATH (Windows), LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (OSX). If Z3 was installed into ocamlfind, the path that should be added is `ocamlfind printconf destdir`/Z3 z3-4.4.0/examples/maxsat/0000755000175000017500000000000012540347414014760 5ustar michaelmichaelz3-4.4.0/examples/maxsat/README0000644000175000017500000000100112540347414015630 0ustar michaelmichaelSmall example using the c++ bindings. To build the example execute make examples in the build directory. This command will create the executable maxsat. On Windows, you can just execute it. On OSX and Linux, you must install z3 first using sudo make install OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (OSX) with the build directory. You need that to be able to find the Z3 shared library. This directory contains a test file (ex.smt) that can be use as input for the maxsat test application. z3-4.4.0/examples/maxsat/ex.smt0000644000175000017500000000037512540347414016126 0ustar michaelmichael(benchmark ex :logic AUFLIA :extrafuns ((x Int) (y Int) (z Int)) :assumption (> x 0) :assumption (<= x -1) :assumption (or (> x 0) (< y 1)) :assumption (> y 2) :assumption (> y 3) :assumption (<= y -1) :formula (= z (+ x y))) z3-4.4.0/examples/maxsat/maxsat.c0000644000175000017500000004732412540347414016433 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ /* Simple MAXSAT solver on top of the Z3 API. */ #include #include #include #include /** \defgroup maxsat_ex MaxSAT/MaxSMT examples */ /*@{*/ /** \brief Exit gracefully in case of error. */ void error(char * msg) { fprintf(stderr, "%s\n", msg); exit(1); } /** \brief Create a logical context. Enable model construction only. */ Z3_context mk_context() { Z3_config cfg; Z3_context ctx; cfg = Z3_mk_config(); Z3_set_param_value(cfg, "MODEL", "true"); ctx = Z3_mk_context(cfg); Z3_del_config(cfg); return ctx; } /** \brief Create a variable using the given name and type. */ Z3_ast mk_var(Z3_context ctx, const char * name, Z3_sort ty) { Z3_symbol s = Z3_mk_string_symbol(ctx, name); return Z3_mk_const(ctx, s, ty); } /** \brief Create a boolean variable using the given name. */ Z3_ast mk_bool_var(Z3_context ctx, const char * name) { Z3_sort ty = Z3_mk_bool_sort(ctx); return mk_var(ctx, name, ty); } /** \brief Create a fresh boolean variable. */ Z3_ast mk_fresh_bool_var(Z3_context ctx) { return Z3_mk_fresh_const(ctx, "k", Z3_mk_bool_sort(ctx)); } /** \brief Create an array with \c num_vars fresh boolean variables. */ Z3_ast * mk_fresh_bool_var_array(Z3_context ctx, unsigned num_vars) { Z3_ast * result = (Z3_ast *) malloc(sizeof(Z3_ast) * num_vars); unsigned i; for (i = 0; i < num_vars; i++) { result[i] = mk_fresh_bool_var(ctx); } return result; } /** \brief Delete array of boolean variables. */ void del_bool_var_array(Z3_ast * arr) { free(arr); } /** \brief Create a binary OR. */ Z3_ast mk_binary_or(Z3_context ctx, Z3_ast in_1, Z3_ast in_2) { Z3_ast args[2] = { in_1, in_2 }; return Z3_mk_or(ctx, 2, args); } /** \brief Create a ternary OR. */ Z3_ast mk_ternary_or(Z3_context ctx, Z3_ast in_1, Z3_ast in_2, Z3_ast in_3) { Z3_ast args[3] = { in_1, in_2, in_3 }; return Z3_mk_or(ctx, 3, args); } /** \brief Create a binary AND. */ Z3_ast mk_binary_and(Z3_context ctx, Z3_ast in_1, Z3_ast in_2) { Z3_ast args[2] = { in_1, in_2 }; return Z3_mk_and(ctx, 2, args); } /** \brief Get hard constraints from a SMT-LIB file. We assume hard constraints are formulas preceeded with the keyword :formula. Return an array containing all formulas read by the last Z3_parse_smtlib_file invocation. It will store the number of formulas in num_cnstrs. */ Z3_ast * get_hard_constraints(Z3_context ctx, unsigned * num_cnstrs) { Z3_ast * result; unsigned i; *num_cnstrs = Z3_get_smtlib_num_formulas(ctx); result = (Z3_ast *) malloc(sizeof(Z3_ast) * (*num_cnstrs)); for (i = 0; i < *num_cnstrs; i++) { result[i] = Z3_get_smtlib_formula(ctx, i); } return result; } /** \brief Get soft constraints from a SMT-LIB file. We assume soft constraints are formulas preceeded with the keyword :assumption. Return an array containing all assumptions read by the last Z3_parse_smtlib_file invocation. It will store the number of formulas in num_cnstrs. */ Z3_ast * get_soft_constraints(Z3_context ctx, unsigned * num_cnstrs) { Z3_ast * result; unsigned i; *num_cnstrs = Z3_get_smtlib_num_assumptions(ctx); result = (Z3_ast *) malloc(sizeof(Z3_ast) * (*num_cnstrs)); for (i = 0; i < *num_cnstrs; i++) { result[i] = Z3_get_smtlib_assumption(ctx, i); } return result; } /** \brief Free the given array of cnstrs that was allocated using get_hard_constraints or get_soft_constraints. */ void free_cnstr_array(Z3_ast * cnstrs) { free(cnstrs); } /** \brief Assert hard constraints stored in the given array. */ void assert_hard_constraints(Z3_context ctx, unsigned num_cnstrs, Z3_ast * cnstrs) { unsigned i; for (i = 0; i < num_cnstrs; i++) { Z3_assert_cnstr(ctx, cnstrs[i]); } } /** \brief Assert soft constraints stored in the given array. This funtion will assert each soft-constraint C_i as (C_i or k_i) where k_i is a fresh boolean variable. It will also return an array containing these fresh variables. */ Z3_ast * assert_soft_constraints(Z3_context ctx, unsigned num_cnstrs, Z3_ast * cnstrs) { unsigned i; Z3_ast * aux_vars; aux_vars = mk_fresh_bool_var_array(ctx, num_cnstrs); for (i = 0; i < num_cnstrs; i++) { Z3_ast assumption = cnstrs[i]; Z3_assert_cnstr(ctx, mk_binary_or(ctx, assumption, aux_vars[i])); } return aux_vars; } /** \brief Create a full adder with inputs \c in_1, \c in_2 and \c cin. The output of the full adder is stored in \c out, and the carry in \c c_out. */ void mk_full_adder(Z3_context ctx, Z3_ast in_1, Z3_ast in_2, Z3_ast cin, Z3_ast * out, Z3_ast * cout) { *cout = mk_ternary_or(ctx, mk_binary_and(ctx, in_1, in_2), mk_binary_and(ctx, in_1, cin), mk_binary_and(ctx, in_2, cin)); *out = Z3_mk_xor(ctx, Z3_mk_xor(ctx, in_1, in_2), cin); } /** \brief Create an adder for inputs of size \c num_bits. The arguments \c in1 and \c in2 are arrays of bits of size \c num_bits. \remark \c result must be an array of size \c num_bits + 1. */ void mk_adder(Z3_context ctx, unsigned num_bits, Z3_ast * in_1, Z3_ast * in_2, Z3_ast * result) { Z3_ast cin, cout, out; unsigned i; cin = Z3_mk_false(ctx); for (i = 0; i < num_bits; i++) { mk_full_adder(ctx, in_1[i], in_2[i], cin, &out, &cout); result[i] = out; cin = cout; } result[num_bits] = cout; } /** \brief Given \c num_ins "numbers" of size \c num_bits stored in \c in. Create floor(num_ins/2) adder circuits. Each circuit is adding two consecutive "numbers". The numbers are stored one after the next in the array \c in. That is, the array \c in has size num_bits * num_ins. Return an array of bits containing \c ceil(num_ins/2) numbers of size \c (num_bits + 1). If num_ins/2 is not an integer, then the last "number" in the output, is the last "number" in \c in with an appended "zero". */ void mk_adder_pairs(Z3_context ctx, unsigned num_bits, unsigned num_ins, Z3_ast * in, unsigned * out_num_ins, Z3_ast * out) { unsigned out_num_bits = num_bits + 1; unsigned i = 0; Z3_ast * _in = in; Z3_ast * _out = out; *out_num_ins = (num_ins % 2 == 0) ? (num_ins / 2) : (num_ins / 2) + 1; for (i = 0; i < num_ins / 2; i++) { mk_adder(ctx, num_bits, _in, _in + num_bits, _out); _in += num_bits; _in += num_bits; _out += out_num_bits; } if (num_ins % 2 != 0) { for (i = 0; i < num_bits; i++) { _out[i] = _in[i]; } _out[num_bits] = Z3_mk_false(ctx); } } /** \brief Create a counter circuit to count the number of "ones" in lits. The function returns an array of bits (i.e. boolean expressions) containing the output of the circuit. The size of the array is stored in out_sz. */ Z3_ast * mk_counter_circuit(Z3_context ctx, unsigned n, Z3_ast * lits, unsigned * out_sz) { unsigned num_ins = n; unsigned num_bits = 1; Z3_ast * aux_1; Z3_ast * aux_2; if (n == 0) return 0; aux_1 = (Z3_ast *) malloc(sizeof(Z3_ast) * (n + 1)); aux_2 = (Z3_ast *) malloc(sizeof(Z3_ast) * (n + 1)); memcpy(aux_1, lits, sizeof(Z3_ast) * n); while (num_ins > 1) { unsigned new_num_ins; Z3_ast * tmp; mk_adder_pairs(ctx, num_bits, num_ins, aux_1, &new_num_ins, aux_2); num_ins = new_num_ins; num_bits++; #ifdef MAXSAT_DEBUG { unsigned i; printf("num_bits: %d, num_ins: %d \n", num_bits, num_ins); for (i = 0; i < num_ins * num_bits; i++) { printf("bit %d:\n%s\n", i, Z3_ast_to_string(ctx, aux_2[i])); } printf("-----------\n"); } #endif tmp = aux_1; aux_1 = aux_2; aux_2 = tmp; } *out_sz = num_bits; free(aux_2); return aux_1; } /** \brief Return the \c idx bit of \c val. */ int get_bit(unsigned val, unsigned idx) { unsigned mask = 1 << (idx & 31); return (val & mask) != 0; } /** \brief Given an integer val encoded in n bits (boolean variables), assert the constraint that val <= k. */ void assert_le_k(Z3_context ctx, unsigned n, Z3_ast * val, unsigned k) { Z3_ast i1, i2, not_val, out; unsigned idx; not_val = Z3_mk_not(ctx, val[0]); if (get_bit(k, 0)) out = Z3_mk_true(ctx); else out = not_val; for (idx = 1; idx < n; idx++) { not_val = Z3_mk_not(ctx, val[idx]); if (get_bit(k, idx)) { i1 = not_val; i2 = out; } else { i1 = Z3_mk_false(ctx); i2 = Z3_mk_false(ctx); } out = mk_ternary_or(ctx, i1, i2, mk_binary_and(ctx, not_val, out)); } // printf("at-most-k:\n%s\n", Z3_ast_to_string(ctx, out)); Z3_assert_cnstr(ctx, out); } /** \brief Assert that at most \c k literals in \c lits can be true, where \c n is the number of literals in lits. We use a simple encoding using an adder (counter). An interesting exercise consists in implementing more sophisticated encodings. */ void assert_at_most_k(Z3_context ctx, unsigned n, Z3_ast * lits, unsigned k) { Z3_ast * counter_bits; unsigned counter_bits_sz; if (k >= n || n <= 1) return; /* nothing to be done */ counter_bits = mk_counter_circuit(ctx, n, lits, &counter_bits_sz); assert_le_k(ctx, counter_bits_sz, counter_bits, k); del_bool_var_array(counter_bits); } /** \brief Assert that at most one literal in \c lits can be true, where \c n is the number of literals in lits. */ void assert_at_most_one(Z3_context ctx, unsigned n, Z3_ast * lits) { assert_at_most_k(ctx, n, lits, 1); } /** \brief Small test for the at-most-one constraint. */ void tst_at_most_one() { Z3_context ctx = mk_context(); Z3_ast k1 = mk_bool_var(ctx, "k1"); Z3_ast k2 = mk_bool_var(ctx, "k2"); Z3_ast k3 = mk_bool_var(ctx, "k3"); Z3_ast k4 = mk_bool_var(ctx, "k4"); Z3_ast k5 = mk_bool_var(ctx, "k5"); Z3_ast k6 = mk_bool_var(ctx, "k6"); Z3_ast args1[5] = { k1, k2, k3, k4, k5 }; Z3_ast args2[3] = { k4, k5, k6 }; Z3_model m = 0; Z3_lbool result; printf("testing at-most-one constraint\n"); assert_at_most_one(ctx, 5, args1); assert_at_most_one(ctx, 3, args2); printf("it must be sat...\n"); result = Z3_check_and_get_model(ctx, &m); if (result != Z3_L_TRUE) error("BUG"); printf("model:\n%s\n", Z3_model_to_string(ctx, m)); if (m) { Z3_del_model(ctx, m); } Z3_assert_cnstr(ctx, mk_binary_or(ctx, k2, k3)); Z3_assert_cnstr(ctx, mk_binary_or(ctx, k1, k6)); printf("it must be sat...\n"); result = Z3_check_and_get_model(ctx, &m); if (result != Z3_L_TRUE) error("BUG"); printf("model:\n%s\n", Z3_model_to_string(ctx, m)); if (m) { Z3_del_model(ctx, m); } Z3_assert_cnstr(ctx, mk_binary_or(ctx, k4, k5)); printf("it must be unsat...\n"); result = Z3_check_and_get_model(ctx, &m); if (result != Z3_L_FALSE) error("BUG"); Z3_del_context(ctx); } /** \brief Return the number of soft-constraints that were disable by the given model. A soft-constraint was disabled if the associated auxiliary variable was assigned to true. */ unsigned get_num_disabled_soft_constraints(Z3_context ctx, Z3_model m, unsigned num_soft_cnstrs, Z3_ast * aux_vars) { unsigned i; unsigned num_disabled = 0; Z3_ast t = Z3_mk_true(ctx); for (i = 0; i < num_soft_cnstrs; i++) { Z3_ast val; if (Z3_eval(ctx, m, aux_vars[i], &val) == Z3_TRUE) { // printf("%s", Z3_ast_to_string(ctx, aux_vars[i])); // printf(" -> %s\n", Z3_ast_to_string(ctx, val)); if (Z3_is_eq_ast(ctx, val, t)) { num_disabled++; } } } return num_disabled; } /** \brief Naive maxsat procedure based on linear search and at-most-k constraint. Return the number of soft-constraints that can be satisfied. Return -1 if the hard-constraints cannot be satisfied. That is, the formula cannot be satisfied even if all soft-constraints are ignored. Exercise: use binary search to implement MaxSAT. Hint: you will need to use an answer literal to retract the at-most-k constraint. */ int naive_maxsat(Z3_context ctx, unsigned num_hard_cnstrs, Z3_ast * hard_cnstrs, unsigned num_soft_cnstrs, Z3_ast * soft_cnstrs) { Z3_ast * aux_vars; Z3_lbool is_sat; unsigned r, k; assert_hard_constraints(ctx, num_hard_cnstrs, hard_cnstrs); printf("checking whether hard constraints are satisfiable...\n"); is_sat = Z3_check(ctx); if (is_sat == Z3_L_FALSE) { // It is not possible to make the formula satisfiable even when ignoring all soft constraints. return -1; } if (num_soft_cnstrs == 0) return 0; // nothing to be done... aux_vars = assert_soft_constraints(ctx, num_soft_cnstrs, soft_cnstrs); // Perform linear search. r = 0; k = num_soft_cnstrs - 1; for (;;) { Z3_model m; unsigned num_disabled; // at most k soft-constraints can be ignored. printf("checking whether at-most %d soft-constraints can be ignored.\n", k); assert_at_most_k(ctx, num_soft_cnstrs, aux_vars, k); is_sat = Z3_check_and_get_model(ctx, &m); if (is_sat == Z3_L_FALSE) { printf("unsat\n"); return num_soft_cnstrs - k - 1; } num_disabled = get_num_disabled_soft_constraints(ctx, m, num_soft_cnstrs, aux_vars); if (num_disabled > k) { error("BUG"); } if (m) { Z3_del_model(ctx, m); } printf("sat\n"); k = num_disabled; if (k == 0) { // it was possible to satisfy all soft-constraints. return num_soft_cnstrs; } k--; } } /** \brief Implement one step of the Fu&Malik algorithm. See fu_malik_maxsat function for more details. Input: soft constraints + aux-vars (aka answer literals) Output: done/not-done when not done return updated set of soft-constraints and aux-vars. - if SAT --> terminates - if UNSAT * compute unsat core * add blocking variable to soft-constraints in the core - replace soft-constraint with the one with the blocking variable - we should also add an aux-var - replace aux-var with a new one * add at-most-one constraint with blocking */ int fu_malik_maxsat_step(Z3_context ctx, unsigned num_soft_cnstrs, Z3_ast * soft_cnstrs, Z3_ast * aux_vars) { // create assumptions Z3_ast * assumptions = (Z3_ast*) malloc(sizeof(Z3_ast) * num_soft_cnstrs); Z3_ast * core = (Z3_ast*) malloc(sizeof(Z3_ast) * num_soft_cnstrs); unsigned core_size; Z3_ast dummy_proof; // we don't care about proofs in this example Z3_model m; Z3_lbool is_sat; unsigned i = 0; for (i = 0; i < num_soft_cnstrs; i++) { // Recall that we asserted (soft_cnstrs[i] \/ aux_vars[i]) // So using (NOT aux_vars[i]) as an assumption we are actually forcing the soft_cnstrs[i] to be considered. assumptions[i] = Z3_mk_not(ctx, aux_vars[i]); core[i] = 0; } is_sat = Z3_check_assumptions(ctx, num_soft_cnstrs, assumptions, &m, &dummy_proof, &core_size, core); if (is_sat != Z3_L_FALSE) { return 1; // done } else { Z3_ast * block_vars = (Z3_ast*) malloc(sizeof(Z3_ast) * core_size); unsigned k = 0; // update soft-constraints and aux_vars for (i = 0; i < num_soft_cnstrs; i++) { unsigned j; // check whether assumption[i] is in the core or not for (j = 0; j < core_size; j++) { if (assumptions[i] == core[j]) break; } if (j < core_size) { // assumption[i] is in the unsat core... so soft_cnstrs[i] is in the unsat core Z3_ast block_var = mk_fresh_bool_var(ctx); Z3_ast new_aux_var = mk_fresh_bool_var(ctx); soft_cnstrs[i] = mk_binary_or(ctx, soft_cnstrs[i], block_var); aux_vars[i] = new_aux_var; block_vars[k] = block_var; k++; // Add new constraint containing the block variable. // Note that we are using the new auxiliary variable to be able to use it as an assumption. Z3_assert_cnstr(ctx, mk_binary_or(ctx, soft_cnstrs[i], new_aux_var)); } } assert_at_most_one(ctx, k, block_vars); return 0; // not done. } } /** \brief Fu & Malik procedure for MaxSAT. This procedure is based on unsat core extraction and the at-most-one constraint. Return the number of soft-constraints that can be satisfied. Return -1 if the hard-constraints cannot be satisfied. That is, the formula cannot be satisfied even if all soft-constraints are ignored. For more information on the Fu & Malik procedure: Z. Fu and S. Malik, On solving the partial MAX-SAT problem, in International Conference on Theory and Applications of Satisfiability Testing, 2006. */ int fu_malik_maxsat(Z3_context ctx, unsigned num_hard_cnstrs, Z3_ast * hard_cnstrs, unsigned num_soft_cnstrs, Z3_ast * soft_cnstrs) { Z3_ast * aux_vars; Z3_lbool is_sat; unsigned k; assert_hard_constraints(ctx, num_hard_cnstrs, hard_cnstrs); printf("checking whether hard constraints are satisfiable...\n"); is_sat = Z3_check(ctx); if (is_sat == Z3_L_FALSE) { // It is not possible to make the formula satisfiable even when ignoring all soft constraints. return -1; } if (num_soft_cnstrs == 0) return 0; // nothing to be done... /* Fu&Malik algorithm is based on UNSAT-core extraction. We accomplish that using auxiliary variables (aka answer literals). */ aux_vars = assert_soft_constraints(ctx, num_soft_cnstrs, soft_cnstrs); k = 0; for (;;) { printf("iteration %d\n", k); if (fu_malik_maxsat_step(ctx, num_soft_cnstrs, soft_cnstrs, aux_vars)) { return num_soft_cnstrs - k; } k++; } } #define NAIVE_MAXSAT 0 #define FU_MALIK_MAXSAT 1 /** \brief Finds the maximal number of assumptions that can be satisfied. An assumption is any formula preceeded with the :assumption keyword. "Hard" constraints can be supported by using the :formula keyword. Input: file in SMT-LIB format, and MaxSAT algorithm to be used: 0 - Naive, 1 - Fu&Malik's algo. Output: the maximum number of assumptions that can be satisfied. */ int smtlib_maxsat(char * file_name, int approach) { Z3_context ctx; unsigned num_hard_cnstrs, num_soft_cnstrs; Z3_ast * hard_cnstrs, * soft_cnstrs; unsigned result; ctx = mk_context(); Z3_parse_smtlib_file(ctx, file_name, 0, 0, 0, 0, 0, 0); hard_cnstrs = get_hard_constraints(ctx, &num_hard_cnstrs); soft_cnstrs = get_soft_constraints(ctx, &num_soft_cnstrs); switch (approach) { case NAIVE_MAXSAT: result = naive_maxsat(ctx, num_hard_cnstrs, hard_cnstrs, num_soft_cnstrs, soft_cnstrs); break; case FU_MALIK_MAXSAT: result = fu_malik_maxsat(ctx, num_hard_cnstrs, hard_cnstrs, num_soft_cnstrs, soft_cnstrs); break; default: /* Exercise: implement your own MaxSAT algorithm.*/ error("Not implemented yet."); break; } free_cnstr_array(hard_cnstrs); free_cnstr_array(soft_cnstrs); return result; } int main(int argc, char * argv[]) { #if 1 int r; if (argc != 2) { error("usage: maxsat [filename.smt]."); } r = smtlib_maxsat(argv[1], FU_MALIK_MAXSAT); printf("result: %d\n", r); #else tst_at_most_one(); #endif return 0; } /*@}*/ z3-4.4.0/examples/dotnet/0000755000175000017500000000000012540347414014760 5ustar michaelmichaelz3-4.4.0/examples/dotnet/README0000644000175000017500000000032512540347414015640 0ustar michaelmichaelSmall example using the .Net bindings. This example is only built if you have Visual Studio. To build the example execute make examples in the build directory. It will create the executable dotnet_example.exe z3-4.4.0/examples/dotnet/Program.cs0000644000175000017500000024443612540347414016733 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Program.cs Abstract: Z3 Managed API: Example program Author: Christoph Wintersteiger (cwinter) 2012-03-16 Notes: --*/ using System; using System.Collections; using System.Collections.Generic; using Microsoft.Z3; namespace test_mapi { class Program { class TestFailedException : Exception { public TestFailedException() : base("Check FAILED") { } }; /// /// Create axiom: function f is injective in the i-th argument. /// /// /// The following axiom is produced: /// /// forall (x_0, ..., x_n) finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i /// /// Where, finvis a fresh function declaration. /// public static BoolExpr InjAxiom(Context ctx, FuncDecl f, int i) { Sort[] domain = f.Domain; uint sz = f.DomainSize; if (i >= sz) { Console.WriteLine("failed to create inj axiom"); return null; } /* declare the i-th inverse of f: finv */ Sort finv_domain = f.Range; Sort finv_range = domain[i]; FuncDecl finv = ctx.MkFuncDecl("f_fresh", finv_domain, finv_range); /* allocate temporary arrays */ Expr[] xs = new Expr[sz]; Symbol[] names = new Symbol[sz]; Sort[] types = new Sort[sz]; /* fill types, names and xs */ for (uint j = 0; j < sz; j++) { types[j] = domain[j]; names[j] = ctx.MkSymbol(String.Format("x_{0}", j)); xs[j] = ctx.MkBound(j, types[j]); } Expr x_i = xs[i]; /* create f(x_0, ..., x_i, ..., x_{n-1}) */ Expr fxs = f[xs]; /* create f_inv(f(x_0, ..., x_i, ..., x_{n-1})) */ Expr finv_fxs = finv[fxs]; /* create finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i */ Expr eq = ctx.MkEq(finv_fxs, x_i); /* use f(x_0, ..., x_i, ..., x_{n-1}) as the pattern for the quantifier */ Pattern p = ctx.MkPattern(new Expr[] { fxs }); /* create & assert quantifier */ BoolExpr q = ctx.MkForall( types, /* types of quantified variables */ names, /* names of quantified variables */ eq, 1, new Pattern[] { p } /* patterns */); return q; } /// /// Create axiom: function f is injective in the i-th argument. /// /// /// The following axiom is produced: /// /// forall (x_0, ..., x_n) finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i /// /// Where, finvis a fresh function declaration. /// public static BoolExpr InjAxiomAbs(Context ctx, FuncDecl f, int i) { Sort[] domain = f.Domain; uint sz = f.DomainSize; if (i >= sz) { Console.WriteLine("failed to create inj axiom"); return null; } /* declare the i-th inverse of f: finv */ Sort finv_domain = f.Range; Sort finv_range = domain[i]; FuncDecl finv = ctx.MkFuncDecl("f_fresh", finv_domain, finv_range); /* allocate temporary arrays */ Expr[] xs = new Expr[sz]; /* fill types, names and xs */ for (uint j = 0; j < sz; j++) { xs[j] = ctx.MkConst(String.Format("x_{0}", j), domain[j]); } Expr x_i = xs[i]; /* create f(x_0, ..., x_i, ..., x_{n-1}) */ Expr fxs = f[xs]; /* create f_inv(f(x_0, ..., x_i, ..., x_{n-1})) */ Expr finv_fxs = finv[fxs]; /* create finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i */ Expr eq = ctx.MkEq(finv_fxs, x_i); /* use f(x_0, ..., x_i, ..., x_{n-1}) as the pattern for the quantifier */ Pattern p = ctx.MkPattern(new Expr[] { fxs }); /* create & assert quantifier */ BoolExpr q = ctx.MkForall( xs, /* types of quantified variables */ eq, /* names of quantified variables */ 1, new Pattern[] { p } /* patterns */); return q; } /// /// Assert the axiom: function f is commutative. /// /// /// This example uses the SMT-LIB parser to simplify the axiom construction. /// private static BoolExpr CommAxiom(Context ctx, FuncDecl f) { Sort t = f.Range; Sort[] dom = f.Domain; if (dom.Length != 2 || !t.Equals(dom[0]) || !t.Equals(dom[1])) { Console.WriteLine("{0} {1} {2} {3}", dom.Length, dom[0], dom[1], t); throw new Exception("function must be binary, and argument types must be equal to return type"); } string bench = string.Format("(benchmark comm :formula (forall (x {0}) (y {1}) (= ({2} x y) ({3} y x))))", t.Name, t.Name, f.Name, f.Name); ctx.ParseSMTLIBString(bench, new Symbol[] { t.Name }, new Sort[] { t }, new Symbol[] { f.Name }, new FuncDecl[] { f }); return ctx.SMTLIBFormulas[0]; } /// /// "Hello world" example: create a Z3 logical context, and delete it. /// public static void SimpleExample() { Console.WriteLine("SimpleExample"); using (Context ctx = new Context()) { /* do something with the context */ /* be kind to dispose manually and not wait for the GC. */ ctx.Dispose(); } } static Model Check(Context ctx, BoolExpr f, Status sat) { Solver s = ctx.MkSolver(); s.Assert(f); if (s.Check() != sat) throw new TestFailedException(); if (sat == Status.SATISFIABLE) return s.Model; else return null; } static void SolveTactical(Context ctx, Tactic t, Goal g, Status sat) { Solver s = ctx.MkSolver(t); Console.WriteLine("\nTactical solver: " + s); foreach (BoolExpr a in g.Formulas) s.Assert(a); Console.WriteLine("Solver: " + s); if (s.Check() != sat) throw new TestFailedException(); } static ApplyResult ApplyTactic(Context ctx, Tactic t, Goal g) { Console.WriteLine("\nGoal: " + g); ApplyResult res = t.Apply(g); Console.WriteLine("Application result: " + res); Status q = Status.UNKNOWN; foreach (Goal sg in res.Subgoals) if (sg.IsDecidedSat) q = Status.SATISFIABLE; else if (sg.IsDecidedUnsat) q = Status.UNSATISFIABLE; switch (q) { case Status.UNKNOWN: Console.WriteLine("Tactic result: Undecided"); break; case Status.SATISFIABLE: Console.WriteLine("Tactic result: SAT"); break; case Status.UNSATISFIABLE: Console.WriteLine("Tactic result: UNSAT"); break; } return res; } static void Prove(Context ctx, BoolExpr f, bool useMBQI = false, params BoolExpr[] assumptions) { Console.WriteLine("Proving: " + f); Solver s = ctx.MkSolver(); Params p = ctx.MkParams(); p.Add("mbqi", useMBQI); s.Parameters = p; foreach (BoolExpr a in assumptions) s.Assert(a); s.Assert(ctx.MkNot(f)); Status q = s.Check(); switch (q) { case Status.UNKNOWN: Console.WriteLine("Unknown because: " + s.ReasonUnknown); break; case Status.SATISFIABLE: throw new TestFailedException(); case Status.UNSATISFIABLE: Console.WriteLine("OK, proof: " + s.Proof); break; } } static void Disprove(Context ctx, BoolExpr f, bool useMBQI = false, params BoolExpr[] assumptions) { Console.WriteLine("Disproving: " + f); Solver s = ctx.MkSolver(); Params p = ctx.MkParams(); p.Add("mbqi", useMBQI); s.Parameters = p; foreach (BoolExpr a in assumptions) s.Assert(a); s.Assert(ctx.MkNot(f)); Status q = s.Check(); switch (q) { case Status.UNKNOWN: Console.WriteLine("Unknown because: " + s.ReasonUnknown); break; case Status.SATISFIABLE: Console.WriteLine("OK, model: " + s.Model); break; case Status.UNSATISFIABLE: throw new TestFailedException(); } } static void ModelConverterTest(Context ctx) { Console.WriteLine("ModelConverterTest"); ArithExpr xr = (ArithExpr)ctx.MkConst(ctx.MkSymbol("x"), ctx.MkRealSort()); ArithExpr yr = (ArithExpr)ctx.MkConst(ctx.MkSymbol("y"), ctx.MkRealSort()); Goal g4 = ctx.MkGoal(true); g4.Assert(ctx.MkGt(xr, ctx.MkReal(10, 1))); g4.Assert(ctx.MkEq(yr, ctx.MkAdd(xr, ctx.MkReal(1, 1)))); g4.Assert(ctx.MkGt(yr, ctx.MkReal(1, 1))); ApplyResult ar = ApplyTactic(ctx, ctx.MkTactic("simplify"), g4); if (ar.NumSubgoals == 1 && (ar.Subgoals[0].IsDecidedSat || ar.Subgoals[0].IsDecidedUnsat)) throw new TestFailedException(); ar = ApplyTactic(ctx, ctx.AndThen(ctx.MkTactic("simplify"), ctx.MkTactic("solve-eqs")), g4); if (ar.NumSubgoals == 1 && (ar.Subgoals[0].IsDecidedSat || ar.Subgoals[0].IsDecidedUnsat)) throw new TestFailedException(); Solver s = ctx.MkSolver(); foreach (BoolExpr e in ar.Subgoals[0].Formulas) s.Assert(e); Status q = s.Check(); Console.WriteLine("Solver says: " + q); Console.WriteLine("Model: \n" + s.Model); Console.WriteLine("Converted Model: \n" + ar.ConvertModel(0, s.Model)); if (q != Status.SATISFIABLE) throw new TestFailedException(); } /// /// A simple array example. /// /// static void ArrayExample1(Context ctx) { Console.WriteLine("ArrayExample1"); Goal g = ctx.MkGoal(true); ArraySort asort = ctx.MkArraySort(ctx.IntSort, ctx.MkBitVecSort(32)); ArrayExpr aex = (ArrayExpr)ctx.MkConst(ctx.MkSymbol("MyArray"), asort); Expr sel = ctx.MkSelect(aex, ctx.MkInt(0)); g.Assert(ctx.MkEq(sel, ctx.MkBV(42, 32))); Symbol xs = ctx.MkSymbol("x"); IntExpr xc = (IntExpr)ctx.MkConst(xs, ctx.IntSort); Symbol fname = ctx.MkSymbol("f"); Sort[] domain = { ctx.IntSort }; FuncDecl fd = ctx.MkFuncDecl(fname, domain, ctx.IntSort); Expr[] fargs = { ctx.MkConst(xs, ctx.IntSort) }; IntExpr fapp = (IntExpr)ctx.MkApp(fd, fargs); g.Assert(ctx.MkEq(ctx.MkAdd(xc, fapp), ctx.MkInt(123))); Solver s = ctx.MkSolver(); foreach (BoolExpr a in g.Formulas) s.Assert(a); Console.WriteLine("Solver: " + s); Status q = s.Check(); Console.WriteLine("Status: " + q); if (q != Status.SATISFIABLE) throw new TestFailedException(); Console.WriteLine("Model = " + s.Model); Console.WriteLine("Interpretation of MyArray:\n" + s.Model.FuncInterp(aex.FuncDecl)); Console.WriteLine("Interpretation of x:\n" + s.Model.ConstInterp(xc)); Console.WriteLine("Interpretation of f:\n" + s.Model.FuncInterp(fd)); Console.WriteLine("Interpretation of MyArray as Term:\n" + s.Model.FuncInterp(aex.FuncDecl)); } /// /// Prove store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3)). /// /// This example demonstrates how to use the array theory. public static void ArrayExample2(Context ctx) { Console.WriteLine("ArrayExample2"); Sort int_type = ctx.IntSort; Sort array_type = ctx.MkArraySort(int_type, int_type); ArrayExpr a1 = (ArrayExpr)ctx.MkConst("a1", array_type); ArrayExpr a2 = ctx.MkArrayConst("a2", int_type, int_type); Expr i1 = ctx.MkConst("i1", int_type); Expr i2 = ctx.MkConst("i2", int_type); Expr i3 = ctx.MkConst("i3", int_type); Expr v1 = ctx.MkConst("v1", int_type); Expr v2 = ctx.MkConst("v2", int_type); Expr st1 = ctx.MkStore(a1, i1, v1); Expr st2 = ctx.MkStore(a2, i2, v2); Expr sel1 = ctx.MkSelect(a1, i3); Expr sel2 = ctx.MkSelect(a2, i3); /* create antecedent */ BoolExpr antecedent = ctx.MkEq(st1, st2); /* create consequent: i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3) */ BoolExpr consequent = ctx.MkOr(new BoolExpr[] { ctx.MkEq(i1, i3), ctx.MkEq(i2, i3), ctx.MkEq(sel1, sel2) }); /* prove store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3)) */ BoolExpr thm = ctx.MkImplies(antecedent, consequent); Console.WriteLine("prove: store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3))"); Console.WriteLine("{0}", (thm)); Prove(ctx, thm); } /// /// Show that distinct(a_0, ... , a_n) is /// unsatisfiable when a_i's are arrays from boolean to /// boolean and n > 4. /// /// This example also shows how to use the distinct construct. public static void ArrayExample3(Context ctx) { Console.WriteLine("ArrayExample3"); for (int n = 2; n <= 5; n++) { Console.WriteLine("n = {0}", n); Sort bool_type = ctx.MkBoolSort(); Sort array_type = ctx.MkArraySort(bool_type, bool_type); Expr[] a = new Expr[n]; /* create arrays */ for (int i = 0; i < n; i++) { a[i] = ctx.MkConst(String.Format("array_{0}", i), array_type); } /* assert distinct(a[0], ..., a[n]) */ BoolExpr d = ctx.MkDistinct(a); Console.WriteLine("{0}", (d)); /* context is satisfiable if n < 5 */ Model model = Check(ctx, d, n < 5 ? Status.SATISFIABLE : Status.UNSATISFIABLE); if (n < 5) { for (int i = 0; i < n; i++) { Console.WriteLine("{0} = {1}", a[i], model.Evaluate(a[i])); } } } } /// /// Sudoku solving example. /// static void SudokuExample(Context ctx) { Console.WriteLine("SudokuExample"); // 9x9 matrix of integer variables IntExpr[][] X = new IntExpr[9][]; for (uint i = 0; i < 9; i++) { X[i] = new IntExpr[9]; for (uint j = 0; j < 9; j++) X[i][j] = (IntExpr)ctx.MkConst(ctx.MkSymbol("x_" + (i + 1) + "_" + (j + 1)), ctx.IntSort); } // each cell contains a value in {1, ..., 9} Expr[][] cells_c = new Expr[9][]; for (uint i = 0; i < 9; i++) { cells_c[i] = new BoolExpr[9]; for (uint j = 0; j < 9; j++) cells_c[i][j] = ctx.MkAnd(ctx.MkLe(ctx.MkInt(1), X[i][j]), ctx.MkLe(X[i][j], ctx.MkInt(9))); } // each row contains a digit at most once BoolExpr[] rows_c = new BoolExpr[9]; for (uint i = 0; i < 9; i++) rows_c[i] = ctx.MkDistinct(X[i]); // each column contains a digit at most once BoolExpr[] cols_c = new BoolExpr[9]; for (uint j = 0; j < 9; j++) { IntExpr[] column = new IntExpr[9]; for (uint i = 0; i < 9; i++) column[i] = X[i][j]; cols_c[j] = ctx.MkDistinct(column); } // each 3x3 square contains a digit at most once BoolExpr[][] sq_c = new BoolExpr[3][]; for (uint i0 = 0; i0 < 3; i0++) { sq_c[i0] = new BoolExpr[3]; for (uint j0 = 0; j0 < 3; j0++) { IntExpr[] square = new IntExpr[9]; for (uint i = 0; i < 3; i++) for (uint j = 0; j < 3; j++) square[3 * i + j] = X[3 * i0 + i][3 * j0 + j]; sq_c[i0][j0] = ctx.MkDistinct(square); } } BoolExpr sudoku_c = ctx.MkTrue(); foreach (BoolExpr[] t in cells_c) sudoku_c = ctx.MkAnd(ctx.MkAnd(t), sudoku_c); sudoku_c = ctx.MkAnd(ctx.MkAnd(rows_c), sudoku_c); sudoku_c = ctx.MkAnd(ctx.MkAnd(cols_c), sudoku_c); foreach (BoolExpr[] t in sq_c) sudoku_c = ctx.MkAnd(ctx.MkAnd(t), sudoku_c); // sudoku instance, we use '0' for empty cells int[,] instance = {{0,0,0,0,9,4,0,3,0}, {0,0,0,5,1,0,0,0,7}, {0,8,9,0,0,0,0,4,0}, {0,0,0,0,0,0,2,0,8}, {0,6,0,2,0,1,0,5,0}, {1,0,2,0,0,0,0,0,0}, {0,7,0,0,0,0,5,2,0}, {9,0,0,0,6,5,0,0,0}, {0,4,0,9,7,0,0,0,0}}; BoolExpr instance_c = ctx.MkTrue(); for (uint i = 0; i < 9; i++) for (uint j = 0; j < 9; j++) instance_c = ctx.MkAnd(instance_c, (BoolExpr) ctx.MkITE(ctx.MkEq(ctx.MkInt(instance[i, j]), ctx.MkInt(0)), ctx.MkTrue(), ctx.MkEq(X[i][j], ctx.MkInt(instance[i, j])))); Solver s = ctx.MkSolver(); s.Assert(sudoku_c); s.Assert(instance_c); if (s.Check() == Status.SATISFIABLE) { Model m = s.Model; Expr[,] R = new Expr[9, 9]; for (uint i = 0; i < 9; i++) for (uint j = 0; j < 9; j++) R[i, j] = m.Evaluate(X[i][j]); Console.WriteLine("Sudoku solution:"); for (uint i = 0; i < 9; i++) { for (uint j = 0; j < 9; j++) Console.Write(" " + R[i, j]); Console.WriteLine(); } } else { Console.WriteLine("Failed to solve sudoku"); throw new TestFailedException(); } } /// /// A basic example of how to use quantifiers. /// static void QuantifierExample1(Context ctx) { Console.WriteLine("QuantifierExample"); Sort[] types = new Sort[3]; IntExpr[] xs = new IntExpr[3]; Symbol[] names = new Symbol[3]; IntExpr[] vars = new IntExpr[3]; for (uint j = 0; j < 3; j++) { types[j] = ctx.IntSort; names[j] = ctx.MkSymbol(String.Format("x_{0}", j)); xs[j] = (IntExpr)ctx.MkConst(names[j], types[j]); vars[j] = (IntExpr)ctx.MkBound(2 - j, types[j]); // <-- vars reversed! } Expr body_vars = ctx.MkAnd(ctx.MkEq(ctx.MkAdd(vars[0], ctx.MkInt(1)), ctx.MkInt(2)), ctx.MkEq(ctx.MkAdd(vars[1], ctx.MkInt(2)), ctx.MkAdd(vars[2], ctx.MkInt(3)))); Expr body_const = ctx.MkAnd(ctx.MkEq(ctx.MkAdd(xs[0], ctx.MkInt(1)), ctx.MkInt(2)), ctx.MkEq(ctx.MkAdd(xs[1], ctx.MkInt(2)), ctx.MkAdd(xs[2], ctx.MkInt(3)))); Expr x = ctx.MkForall(types, names, body_vars, 1, null, null, ctx.MkSymbol("Q1"), ctx.MkSymbol("skid1")); Console.WriteLine("Quantifier X: " + x.ToString()); Expr y = ctx.MkForall(xs, body_const, 1, null, null, ctx.MkSymbol("Q2"), ctx.MkSymbol("skid2")); Console.WriteLine("Quantifier Y: " + y.ToString()); } static void QuantifierExample2(Context ctx) { Console.WriteLine("QuantifierExample2"); Expr q1, q2; FuncDecl f = ctx.MkFuncDecl("f", ctx.IntSort, ctx.IntSort); FuncDecl g = ctx.MkFuncDecl("g", ctx.IntSort, ctx.IntSort); // Quantifier with Exprs as the bound variables. { Expr x = ctx.MkConst("x", ctx.IntSort); Expr y = ctx.MkConst("y", ctx.IntSort); Expr f_x = ctx.MkApp(f, x); Expr f_y = ctx.MkApp(f, y); Expr g_y = ctx.MkApp(g, y); Pattern[] pats = new Pattern[] { ctx.MkPattern(new Expr[] { f_x, g_y }) }; Expr[] no_pats = new Expr[] { f_y }; Expr[] bound = new Expr[2] { x, y }; Expr body = ctx.MkAnd(ctx.MkEq(f_x, f_y), ctx.MkEq(f_y, g_y)); q1 = ctx.MkForall(bound, body, 1, null, no_pats, ctx.MkSymbol("q"), ctx.MkSymbol("sk")); Console.WriteLine("{0}", q1); } // Quantifier with de-Brujin indices. { Expr x = ctx.MkBound(1, ctx.IntSort); Expr y = ctx.MkBound(0, ctx.IntSort); Expr f_x = ctx.MkApp(f, x); Expr f_y = ctx.MkApp(f, y); Expr g_y = ctx.MkApp(g, y); Pattern[] pats = new Pattern[] { ctx.MkPattern(new Expr[] { f_x, g_y }) }; Expr[] no_pats = new Expr[] { f_y }; Symbol[] names = new Symbol[] { ctx.MkSymbol("x"), ctx.MkSymbol("y") }; Sort[] sorts = new Sort[] { ctx.IntSort, ctx.IntSort }; Expr body = ctx.MkAnd(ctx.MkEq(f_x, f_y), ctx.MkEq(f_y, g_y)); q2 = ctx.MkForall(sorts, names, body, 1, null, // pats, no_pats, ctx.MkSymbol("q"), ctx.MkSymbol("sk") ); Console.WriteLine("{0}", q2); } Console.WriteLine("{0}", (q1.Equals(q2))); } /// /// Prove that f(x, y) = f(w, v) implies y = v when /// f is injective in the second argument. /// public static void QuantifierExample3(Context ctx) { Console.WriteLine("QuantifierExample3"); /* If quantified formulas are asserted in a logical context, then the model produced by Z3 should be viewed as a potential model. */ /* declare function f */ Sort I = ctx.IntSort; FuncDecl f = ctx.MkFuncDecl("f", new Sort[] { I, I }, I); /* f is injective in the second argument. */ BoolExpr inj = InjAxiom(ctx, f, 1); /* create x, y, v, w, fxy, fwv */ Expr x = ctx.MkIntConst("x"); Expr y = ctx.MkIntConst("y"); Expr v = ctx.MkIntConst("v"); Expr w = ctx.MkIntConst("w"); Expr fxy = ctx.MkApp(f, x, y); Expr fwv = ctx.MkApp(f, w, v); /* f(x, y) = f(w, v) */ BoolExpr p1 = ctx.MkEq(fxy, fwv); /* prove f(x, y) = f(w, v) implies y = v */ BoolExpr p2 = ctx.MkEq(y, v); Prove(ctx, p2, false, inj, p1); /* disprove f(x, y) = f(w, v) implies x = w */ BoolExpr p3 = ctx.MkEq(x, w); Disprove(ctx, p3, false, inj, p1); } /// /// Prove that f(x, y) = f(w, v) implies y = v when /// f is injective in the second argument. /// public static void QuantifierExample4(Context ctx) { Console.WriteLine("QuantifierExample4"); /* If quantified formulas are asserted in a logical context, then the model produced by Z3 should be viewed as a potential model. */ /* declare function f */ Sort I = ctx.IntSort; FuncDecl f = ctx.MkFuncDecl("f", new Sort[] { I, I }, I); /* f is injective in the second argument. */ BoolExpr inj = InjAxiomAbs(ctx, f, 1); /* create x, y, v, w, fxy, fwv */ Expr x = ctx.MkIntConst("x"); Expr y = ctx.MkIntConst("y"); Expr v = ctx.MkIntConst("v"); Expr w = ctx.MkIntConst("w"); Expr fxy = ctx.MkApp(f, x, y); Expr fwv = ctx.MkApp(f, w, v); /* f(x, y) = f(w, v) */ BoolExpr p1 = ctx.MkEq(fxy, fwv); /* prove f(x, y) = f(w, v) implies y = v */ BoolExpr p2 = ctx.MkEq(y, v); Prove(ctx, p2, false, inj, p1); /* disprove f(x, y) = f(w, v) implies x = w */ BoolExpr p3 = ctx.MkEq(x, w); Disprove(ctx, p3, false, inj, p1); } /// /// Some basic tests. /// static void BasicTests(Context ctx) { Console.WriteLine("BasicTests"); Symbol qi = ctx.MkSymbol(1); Symbol fname = ctx.MkSymbol("f"); Symbol x = ctx.MkSymbol("x"); Symbol y = ctx.MkSymbol("y"); Sort bs = ctx.MkBoolSort(); Sort[] domain = { bs, bs }; FuncDecl f = ctx.MkFuncDecl(fname, domain, bs); Expr fapp = ctx.MkApp(f, ctx.MkConst(x, bs), ctx.MkConst(y, bs)); Expr[] fargs2 = { ctx.MkFreshConst("cp", bs) }; Sort[] domain2 = { bs }; Expr fapp2 = ctx.MkApp(ctx.MkFreshFuncDecl("fp", domain2, bs), fargs2); BoolExpr trivial_eq = ctx.MkEq(fapp, fapp); BoolExpr nontrivial_eq = ctx.MkEq(fapp, fapp2); Goal g = ctx.MkGoal(true); g.Assert(trivial_eq); g.Assert(nontrivial_eq); Console.WriteLine("Goal: " + g); Solver solver = ctx.MkSolver(); foreach (BoolExpr a in g.Formulas) solver.Assert(a); if (solver.Check() != Status.SATISFIABLE) throw new TestFailedException(); ApplyResult ar = ApplyTactic(ctx, ctx.MkTactic("simplify"), g); if (ar.NumSubgoals == 1 && (ar.Subgoals[0].IsDecidedSat || ar.Subgoals[0].IsDecidedUnsat)) throw new TestFailedException(); ar = ApplyTactic(ctx, ctx.MkTactic("smt"), g); if (ar.NumSubgoals != 1 || !ar.Subgoals[0].IsDecidedSat) throw new TestFailedException(); g.Assert(ctx.MkEq(ctx.MkNumeral(1, ctx.MkBitVecSort(32)), ctx.MkNumeral(2, ctx.MkBitVecSort(32)))); ar = ApplyTactic(ctx, ctx.MkTactic("smt"), g); if (ar.NumSubgoals != 1 || !ar.Subgoals[0].IsDecidedUnsat) throw new TestFailedException(); Goal g2 = ctx.MkGoal(true, true); ar = ApplyTactic(ctx, ctx.MkTactic("smt"), g2); if (ar.NumSubgoals != 1 || !ar.Subgoals[0].IsDecidedSat) throw new TestFailedException(); g2 = ctx.MkGoal(true, true); g2.Assert(ctx.MkFalse()); ar = ApplyTactic(ctx, ctx.MkTactic("smt"), g2); if (ar.NumSubgoals != 1 || !ar.Subgoals[0].IsDecidedUnsat) throw new TestFailedException(); Goal g3 = ctx.MkGoal(true, true); Expr xc = ctx.MkConst(ctx.MkSymbol("x"), ctx.IntSort); Expr yc = ctx.MkConst(ctx.MkSymbol("y"), ctx.IntSort); g3.Assert(ctx.MkEq(xc, ctx.MkNumeral(1, ctx.IntSort))); g3.Assert(ctx.MkEq(yc, ctx.MkNumeral(2, ctx.IntSort))); BoolExpr constr = ctx.MkEq(xc, yc); g3.Assert(constr); ar = ApplyTactic(ctx, ctx.MkTactic("smt"), g3); if (ar.NumSubgoals != 1 || !ar.Subgoals[0].IsDecidedUnsat) throw new TestFailedException(); ModelConverterTest(ctx); // Real num/den test. RatNum rn = ctx.MkReal(42, 43); Expr inum = rn.Numerator; Expr iden = rn.Denominator; Console.WriteLine("Numerator: " + inum + " Denominator: " + iden); if (inum.ToString() != "42" || iden.ToString() != "43") throw new TestFailedException(); if (rn.ToDecimalString(3) != "0.976?") throw new TestFailedException(); BigIntCheck(ctx, ctx.MkReal("-1231231232/234234333")); BigIntCheck(ctx, ctx.MkReal("-123123234234234234231232/234234333")); BigIntCheck(ctx, ctx.MkReal("-234234333")); BigIntCheck(ctx, ctx.MkReal("234234333/2")); string bn = "1234567890987654321"; if (ctx.MkInt(bn).BigInteger.ToString() != bn) throw new TestFailedException(); if (ctx.MkBV(bn, 128).BigInteger.ToString() != bn) throw new TestFailedException(); if (ctx.MkBV(bn, 32).BigInteger.ToString() == bn) throw new TestFailedException(); // Error handling test. try { IntExpr i = ctx.MkInt("1/2"); throw new TestFailedException(); // unreachable } catch (Z3Exception) { } } /// /// Some basic expression casting tests. /// static void CastingTest(Context ctx) { Console.WriteLine("CastingTest"); Sort[] domain = { ctx.BoolSort, ctx.BoolSort }; FuncDecl f = ctx.MkFuncDecl("f", domain, ctx.BoolSort); AST upcast = ctx.MkFuncDecl(ctx.MkSymbol("q"), domain, ctx.BoolSort); try { FuncDecl downcast = (FuncDecl)f; // OK } catch (InvalidCastException) { throw new TestFailedException(); } try { Expr uc = (Expr)upcast; throw new TestFailedException(); // should not be reachable! } catch (InvalidCastException) { } Symbol s = ctx.MkSymbol(42); IntSymbol si = s as IntSymbol; if (si == null) throw new TestFailedException(); try { IntSymbol si2 = (IntSymbol)s; } catch (InvalidCastException) { throw new TestFailedException(); } s = ctx.MkSymbol("abc"); StringSymbol ss = s as StringSymbol; if (ss == null) throw new TestFailedException(); try { StringSymbol ss2 = (StringSymbol)s; } catch (InvalidCastException) { throw new TestFailedException(); } try { IntSymbol si2 = (IntSymbol)s; throw new TestFailedException(); // unreachable } catch { } Sort srt = ctx.MkBitVecSort(32); BitVecSort bvs = null; try { bvs = (BitVecSort)srt; } catch (InvalidCastException) { throw new TestFailedException(); } if (bvs.Size != 32) throw new TestFailedException(); Expr q = ctx.MkAdd(ctx.MkInt(1), ctx.MkInt(2)); Expr q2 = q.Args[1]; Sort qs = q2.Sort; if (qs as IntSort == null) throw new TestFailedException(); try { IntSort isrt = (IntSort)qs; } catch (InvalidCastException) { throw new TestFailedException(); } AST a = ctx.MkInt(42); Expr ae = a as Expr; if (ae == null) throw new TestFailedException(); ArithExpr aae = a as ArithExpr; if (aae == null) throw new TestFailedException(); IntExpr aie = a as IntExpr; if (aie == null) throw new TestFailedException(); IntNum ain = a as IntNum; if (ain == null) throw new TestFailedException(); Expr[][] earr = new Expr[2][]; earr[0] = new Expr[2]; earr[1] = new Expr[2]; earr[0][0] = ctx.MkTrue(); earr[0][1] = ctx.MkTrue(); earr[1][0] = ctx.MkFalse(); earr[1][1] = ctx.MkFalse(); foreach (Expr[] ea in earr) foreach (Expr e in ea) { try { Expr ns = ctx.MkNot((BoolExpr)e); BoolExpr ens = (BoolExpr)ns; } catch (InvalidCastException) { throw new TestFailedException(); } } } /// /// Shows how to read an SMT1 file. /// static void SMT1FileTest(string filename) { Console.Write("SMT File test "); using (Context ctx = new Context(new Dictionary() { { "MODEL", "true" } })) { ctx.ParseSMTLIBFile(filename); BoolExpr a = ctx.MkAnd(ctx.SMTLIBFormulas); Console.WriteLine("read formula: " + a); } } /// /// Shows how to read an SMT2 file. /// static void SMT2FileTest(string filename) { System.DateTime before = System.DateTime.Now; Console.WriteLine("SMT2 File test "); System.GC.Collect(); using (Context ctx = new Context(new Dictionary() { { "MODEL", "true" } })) { Expr a = ctx.ParseSMTLIB2File(filename); Console.WriteLine("SMT2 file read time: " + (System.DateTime.Now - before).TotalSeconds + " sec"); // Iterate over the formula. Queue q = new Queue(); q.Enqueue(a); uint cnt = 0; while (q.Count > 0) { AST cur = (AST)q.Dequeue(); cnt++; // This here ... if (cur is Expr) if (!(cur.IsVar)) foreach (Expr c in ((Expr)cur).Args) q.Enqueue(c); // Takes the same time as this: //switch ((cur).ASTKind) //{ // case Z3_ast_kind.Z3_APP_AST: // foreach (Expr e in ((Expr)cur).Args) // q.Enqueue(e); // break; // case Z3_ast_kind.Z3_QUANTIFIER_AST: // q.Enqueue(((Quantifier)cur).Args[0]); // break; // case Z3_ast_kind.Z3_VAR_AST: break; // case Z3_ast_kind.Z3_NUMERAL_AST: break; // case Z3_ast_kind.Z3_FUNC_DECL_AST: break; // case Z3_ast_kind.Z3_SORT_AST: break; //} } Console.WriteLine(cnt + " ASTs"); } Console.WriteLine("SMT2 file test took " + (System.DateTime.Now - before).TotalSeconds + " sec"); } /// /// Shows how to use Solver(logic) /// /// static void LogicExample(Context ctx) { Console.WriteLine("LogicTest"); Microsoft.Z3.Global.ToggleWarningMessages(true); BitVecSort bvs = ctx.MkBitVecSort(32); Expr x = ctx.MkConst("x", bvs); Expr y = ctx.MkConst("y", bvs); BoolExpr eq = ctx.MkEq(x, y); // Use a solver for QF_BV Solver s = ctx.MkSolver("QF_BV"); s.Assert(eq); Status res = s.Check(); Console.WriteLine("solver result: " + res); // Or perhaps a tactic for QF_BV Goal g = ctx.MkGoal(true); g.Assert(eq); Tactic t = ctx.MkTactic("qfbv"); ApplyResult ar = t.Apply(g); Console.WriteLine("tactic result: " + ar); if (ar.NumSubgoals != 1 || !ar.Subgoals[0].IsDecidedSat) throw new TestFailedException(); } /// /// Demonstrates how to use the ParOr tactic. /// static void ParOrExample(Context ctx) { Console.WriteLine("ParOrExample"); BitVecSort bvs = ctx.MkBitVecSort(32); Expr x = ctx.MkConst("x", bvs); Expr y = ctx.MkConst("y", bvs); BoolExpr q = ctx.MkEq(x, y); Goal g = ctx.MkGoal(true); g.Assert(q); Tactic t1 = ctx.MkTactic("qfbv"); Tactic t2 = ctx.MkTactic("qfbv"); Tactic p = ctx.ParOr(t1, t2); ApplyResult ar = p.Apply(g); if (ar.NumSubgoals != 1 || !ar.Subgoals[0].IsDecidedSat) throw new TestFailedException(); } static void BigIntCheck(Context ctx, RatNum r) { Console.WriteLine("Num: " + r.BigIntNumerator); Console.WriteLine("Den: " + r.BigIntDenominator); } /// /// Find a model for x xor y. /// public static void FindModelExample1(Context ctx) { Console.WriteLine("FindModelExample1"); BoolExpr x = ctx.MkBoolConst("x"); BoolExpr y = ctx.MkBoolConst("y"); BoolExpr x_xor_y = ctx.MkXor(x, y); Model model = Check(ctx, x_xor_y, Status.SATISFIABLE); Console.WriteLine("x = {0}, y = {1}", model.Evaluate(x), model.Evaluate(y)); } /// /// Find a model for x < y + 1, x > 2. /// Then, assert not(x = y), and find another model. /// public static void FindModelExample2(Context ctx) { Console.WriteLine("FindModelExample2"); IntExpr x = ctx.MkIntConst("x"); IntExpr y = ctx.MkIntConst("y"); IntExpr one = ctx.MkInt(1); IntExpr two = ctx.MkInt(2); ArithExpr y_plus_one = ctx.MkAdd(y, one); BoolExpr c1 = ctx.MkLt(x, y_plus_one); BoolExpr c2 = ctx.MkGt(x, two); BoolExpr q = ctx.MkAnd(c1, c2); Console.WriteLine("model for: x < y + 1, x > 2"); Model model = Check(ctx, q, Status.SATISFIABLE); Console.WriteLine("x = {0}, y = {1}", (model.Evaluate(x)), (model.Evaluate(y))); /* assert not(x = y) */ BoolExpr x_eq_y = ctx.MkEq(x, y); BoolExpr c3 = ctx.MkNot(x_eq_y); q = ctx.MkAnd(q, c3); Console.WriteLine("model for: x < y + 1, x > 2, not(x = y)"); model = Check(ctx, q, Status.SATISFIABLE); Console.WriteLine("x = {0}, y = {1}", (model.Evaluate(x)), (model.Evaluate(y))); } /// /// Prove x = y implies g(x) = g(y), and /// disprove x = y implies g(g(x)) = g(y). /// /// This function demonstrates how to create uninterpreted /// types and functions. public static void ProveExample1(Context ctx) { Console.WriteLine("ProveExample1"); /* create uninterpreted type. */ Sort U = ctx.MkUninterpretedSort(ctx.MkSymbol("U")); /* declare function g */ FuncDecl g = ctx.MkFuncDecl("g", U, U); /* create x and y */ Expr x = ctx.MkConst("x", U); Expr y = ctx.MkConst("y", U); /* create g(x), g(y) */ Expr gx = g[x]; Expr gy = g[y]; /* assert x = y */ BoolExpr eq = ctx.MkEq(x, y); /* prove g(x) = g(y) */ BoolExpr f = ctx.MkEq(gx, gy); Console.WriteLine("prove: x = y implies g(x) = g(y)"); Prove(ctx, ctx.MkImplies(eq, f)); /* create g(g(x)) */ Expr ggx = g[gx]; /* disprove g(g(x)) = g(y) */ f = ctx.MkEq(ggx, gy); Console.WriteLine("disprove: x = y implies g(g(x)) = g(y)"); Disprove(ctx, ctx.MkImplies(eq, f)); /* Print the model using the custom model printer */ Model m = Check(ctx, ctx.MkNot(f), Status.SATISFIABLE); Console.WriteLine(m); } /// /// Prove not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0 . /// Then, show that z < -1 is not implied. /// /// This example demonstrates how to combine uninterpreted functions /// and arithmetic. public static void ProveExample2(Context ctx) { Console.WriteLine("ProveExample2"); /* declare function g */ Sort I = ctx.IntSort; FuncDecl g = ctx.MkFuncDecl("g", I, I); /* create x, y, and z */ IntExpr x = ctx.MkIntConst("x"); IntExpr y = ctx.MkIntConst("y"); IntExpr z = ctx.MkIntConst("z"); /* create gx, gy, gz */ Expr gx = ctx.MkApp(g, x); Expr gy = ctx.MkApp(g, y); Expr gz = ctx.MkApp(g, z); /* create zero */ IntExpr zero = ctx.MkInt(0); /* assert not(g(g(x) - g(y)) = g(z)) */ ArithExpr gx_gy = ctx.MkSub((IntExpr)gx, (IntExpr)gy); Expr ggx_gy = ctx.MkApp(g, gx_gy); BoolExpr eq = ctx.MkEq(ggx_gy, gz); BoolExpr c1 = ctx.MkNot(eq); /* assert x + z <= y */ ArithExpr x_plus_z = ctx.MkAdd(x, z); BoolExpr c2 = ctx.MkLe(x_plus_z, y); /* assert y <= x */ BoolExpr c3 = ctx.MkLe(y, x); /* prove z < 0 */ BoolExpr f = ctx.MkLt(z, zero); Console.WriteLine("prove: not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0"); Prove(ctx, f, false, c1, c2, c3); /* disprove z < -1 */ IntExpr minus_one = ctx.MkInt(-1); f = ctx.MkLt(z, minus_one); Console.WriteLine("disprove: not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < -1"); Disprove(ctx, f, false, c1, c2, c3); } /// /// Show how push & pop can be used to create "backtracking" points. /// /// This example also demonstrates how big numbers can be /// created in ctx. public static void PushPopExample1(Context ctx) { Console.WriteLine("PushPopExample1"); /* create a big number */ IntSort int_type = ctx.IntSort; IntExpr big_number = ctx.MkInt("1000000000000000000000000000000000000000000000000000000"); /* create number 3 */ IntExpr three = (IntExpr)ctx.MkNumeral("3", int_type); /* create x */ IntExpr x = ctx.MkIntConst("x"); Solver solver = ctx.MkSolver(); /* assert x >= "big number" */ BoolExpr c1 = ctx.MkGe(x, big_number); Console.WriteLine("assert: x >= 'big number'"); solver.Assert(c1); /* create a backtracking point */ Console.WriteLine("push"); solver.Push(); /* assert x <= 3 */ BoolExpr c2 = ctx.MkLe(x, three); Console.WriteLine("assert: x <= 3"); solver.Assert(c2); /* context is inconsistent at this point */ if (solver.Check() != Status.UNSATISFIABLE) throw new TestFailedException(); /* backtrack: the constraint x <= 3 will be removed, since it was asserted after the last ctx.Push. */ Console.WriteLine("pop"); solver.Pop(1); /* the context is consistent again. */ if (solver.Check() != Status.SATISFIABLE) throw new TestFailedException(); /* new constraints can be asserted... */ /* create y */ IntExpr y = ctx.MkIntConst("y"); /* assert y > x */ BoolExpr c3 = ctx.MkGt(y, x); Console.WriteLine("assert: y > x"); solver.Assert(c3); /* the context is still consistent. */ if (solver.Check() != Status.SATISFIABLE) throw new TestFailedException(); } /// /// Tuples. /// /// Check that the projection of a tuple /// returns the corresponding element. public static void TupleExample(Context ctx) { Console.WriteLine("TupleExample"); Sort int_type = ctx.IntSort; TupleSort tuple = ctx.MkTupleSort( ctx.MkSymbol("mk_tuple"), // name of tuple constructor new Symbol[] { ctx.MkSymbol("first"), ctx.MkSymbol("second") }, // names of projection operators new Sort[] { int_type, int_type } // types of projection operators ); FuncDecl first = tuple.FieldDecls[0]; // declarations are for projections FuncDecl second = tuple.FieldDecls[1]; Expr x = ctx.MkConst("x", int_type); Expr y = ctx.MkConst("y", int_type); Expr n1 = tuple.MkDecl[x, y]; Expr n2 = first[n1]; BoolExpr n3 = ctx.MkEq(x, n2); Console.WriteLine("Tuple example: {0}", n3); Prove(ctx, n3); } /// /// Simple bit-vector example. /// /// /// This example disproves that x - 10 <= 0 IFF x <= 10 for (32-bit) machine integers /// public static void BitvectorExample1(Context ctx) { Console.WriteLine("BitvectorExample1"); Sort bv_type = ctx.MkBitVecSort(32); BitVecExpr x = (BitVecExpr)ctx.MkConst("x", bv_type); BitVecNum zero = (BitVecNum)ctx.MkNumeral("0", bv_type); BitVecNum ten = ctx.MkBV(10, 32); BitVecExpr x_minus_ten = ctx.MkBVSub(x, ten); /* bvsle is signed less than or equal to */ BoolExpr c1 = ctx.MkBVSLE(x, ten); BoolExpr c2 = ctx.MkBVSLE(x_minus_ten, zero); BoolExpr thm = ctx.MkIff(c1, c2); Console.WriteLine("disprove: x - 10 <= 0 IFF x <= 10 for (32-bit) machine integers"); Disprove(ctx, thm); } /// /// Find x and y such that: x ^ y - 103 == x * y /// public static void BitvectorExample2(Context ctx) { Console.WriteLine("BitvectorExample2"); /* construct x ^ y - 103 == x * y */ Sort bv_type = ctx.MkBitVecSort(32); BitVecExpr x = ctx.MkBVConst("x", 32); BitVecExpr y = ctx.MkBVConst("y", 32); BitVecExpr x_xor_y = ctx.MkBVXOR(x, y); BitVecExpr c103 = (BitVecNum)ctx.MkNumeral("103", bv_type); BitVecExpr lhs = ctx.MkBVSub(x_xor_y, c103); BitVecExpr rhs = ctx.MkBVMul(x, y); BoolExpr ctr = ctx.MkEq(lhs, rhs); Console.WriteLine("find values of x and y, such that x ^ y - 103 == x * y"); /* find a model (i.e., values for x an y that satisfy the constraint */ Model m = Check(ctx, ctr, Status.SATISFIABLE); Console.WriteLine(m); } /// /// Demonstrates how to use the SMTLIB parser. /// public static void ParserExample1(Context ctx) { Console.WriteLine("ParserExample1"); ctx.ParseSMTLIBString("(benchmark tst :extrafuns ((x Int) (y Int)) :formula (> x y) :formula (> x 0))"); foreach (BoolExpr f in ctx.SMTLIBFormulas) Console.WriteLine("formula {0}", f); Model m = Check(ctx, ctx.MkAnd(ctx.SMTLIBFormulas), Status.SATISFIABLE); } /// /// Demonstrates how to initialize the parser symbol table. /// public static void ParserExample2(Context ctx) { Console.WriteLine("ParserExample2"); Symbol[] declNames = { ctx.MkSymbol("a"), ctx.MkSymbol("b") }; FuncDecl a = ctx.MkConstDecl(declNames[0], ctx.MkIntSort()); FuncDecl b = ctx.MkConstDecl(declNames[1], ctx.MkIntSort()); FuncDecl[] decls = new FuncDecl[] { a, b }; ctx.ParseSMTLIBString("(benchmark tst :formula (> a b))", null, null, declNames, decls); BoolExpr f = ctx.SMTLIBFormulas[0]; Console.WriteLine("formula: {0}", f); Check(ctx, f, Status.SATISFIABLE); } /// /// Demonstrates how to initialize the parser symbol table. /// public static void ParserExample3(Context ctx) { Console.WriteLine("ParserExample3"); /* declare function g */ Sort I = ctx.MkIntSort(); FuncDecl g = ctx.MkFuncDecl("g", new Sort[] { I, I }, I); BoolExpr ca = CommAxiom(ctx, g); ctx.ParseSMTLIBString("(benchmark tst :formula (forall (x Int) (y Int) (implies (= x y) (= (gg x 0) (gg 0 y)))))", null, null, new Symbol[] { ctx.MkSymbol("gg") }, new FuncDecl[] { g }); BoolExpr thm = ctx.SMTLIBFormulas[0]; Console.WriteLine("formula: {0}", thm); Prove(ctx, thm, false, ca); } /// /// Display the declarations, assumptions and formulas in a SMT-LIB string. /// public static void ParserExample4(Context ctx) { Console.WriteLine("ParserExample4"); ctx.ParseSMTLIBString ("(benchmark tst :extrafuns ((x Int) (y Int)) :assumption (= x 20) :formula (> x y) :formula (> x 0))"); foreach (var decl in ctx.SMTLIBDecls) { Console.WriteLine("Declaration: {0}", decl); } foreach (var f in ctx.SMTLIBAssumptions) { Console.WriteLine("Assumption: {0}", f); } foreach (var f in ctx.SMTLIBFormulas) { Console.WriteLine("Formula: {0}", f); } } /// /// Demonstrates how to handle parser errors using Z3 error handling support. /// /// public static void ParserExample5(Context ctx) { Console.WriteLine("ParserExample5"); try { ctx.ParseSMTLIBString( /* the following string has a parsing error: missing parenthesis */ "(benchmark tst :extrafuns ((x Int (y Int)) :formula (> x y) :formula (> x 0))"); } catch (Z3Exception e) { Console.WriteLine("Z3 error: {0}", e); } } /// /// Create an ite-Expr (if-then-else Exprs). /// public static void ITEExample(Context ctx) { Console.WriteLine("ITEExample"); BoolExpr f = ctx.MkFalse(); Expr one = ctx.MkInt(1); Expr zero = ctx.MkInt(0); Expr ite = ctx.MkITE(f, one, zero); Console.WriteLine("Expr: {0}", ite); } /// /// Create an enumeration data type. /// public static void EnumExample(Context ctx) { Console.WriteLine("EnumExample"); Symbol name = ctx.MkSymbol("fruit"); EnumSort fruit = ctx.MkEnumSort(ctx.MkSymbol("fruit"), new Symbol[] { ctx.MkSymbol("apple"), ctx.MkSymbol("banana"), ctx.MkSymbol("orange") }); Console.WriteLine("{0}", (fruit.Consts[0])); Console.WriteLine("{0}", (fruit.Consts[1])); Console.WriteLine("{0}", (fruit.Consts[2])); Console.WriteLine("{0}", (fruit.TesterDecls[0])); Console.WriteLine("{0}", (fruit.TesterDecls[1])); Console.WriteLine("{0}", (fruit.TesterDecls[2])); Expr apple = fruit.Consts[0]; Expr banana = fruit.Consts[1]; Expr orange = fruit.Consts[2]; /* Apples are different from oranges */ Prove(ctx, ctx.MkNot(ctx.MkEq(apple, orange))); /* Apples pass the apple test */ Prove(ctx, (BoolExpr)ctx.MkApp(fruit.TesterDecls[0], apple)); /* Oranges fail the apple test */ Disprove(ctx, (BoolExpr)ctx.MkApp(fruit.TesterDecls[0], orange)); Prove(ctx, (BoolExpr)ctx.MkNot((BoolExpr)ctx.MkApp(fruit.TesterDecls[0], orange))); Expr fruity = ctx.MkConst("fruity", fruit); /* If something is fruity, then it is an apple, banana, or orange */ Prove(ctx, ctx.MkOr(new BoolExpr[] { ctx.MkEq(fruity, apple), ctx.MkEq(fruity, banana), ctx.MkEq(fruity, orange) })); } /// /// Create a list datatype. /// public static void ListExample(Context ctx) { Console.WriteLine("ListExample"); Sort int_ty; ListSort int_list; Expr nil, l1, l2, x, y, u, v; BoolExpr fml, fml1; int_ty = ctx.MkIntSort(); int_list = ctx.MkListSort(ctx.MkSymbol("int_list"), int_ty); nil = ctx.MkConst(int_list.NilDecl); l1 = ctx.MkApp(int_list.ConsDecl, ctx.MkInt(1), nil); l2 = ctx.MkApp(int_list.ConsDecl, ctx.MkInt(2), nil); /* nil != cons(1, nil) */ Prove(ctx, ctx.MkNot(ctx.MkEq(nil, l1))); /* cons(2,nil) != cons(1, nil) */ Prove(ctx, ctx.MkNot(ctx.MkEq(l1, l2))); /* cons(x,nil) = cons(y, nil) => x = y */ x = ctx.MkConst("x", int_ty); y = ctx.MkConst("y", int_ty); l1 = ctx.MkApp(int_list.ConsDecl, x, nil); l2 = ctx.MkApp(int_list.ConsDecl, y, nil); Prove(ctx, ctx.MkImplies(ctx.MkEq(l1, l2), ctx.MkEq(x, y))); /* cons(x,u) = cons(x, v) => u = v */ u = ctx.MkConst("u", int_list); v = ctx.MkConst("v", int_list); l1 = ctx.MkApp(int_list.ConsDecl, x, u); l2 = ctx.MkApp(int_list.ConsDecl, y, v); Prove(ctx, ctx.MkImplies(ctx.MkEq(l1, l2), ctx.MkEq(u, v))); Prove(ctx, ctx.MkImplies(ctx.MkEq(l1, l2), ctx.MkEq(x, y))); /* is_nil(u) or is_cons(u) */ Prove(ctx, ctx.MkOr((BoolExpr)ctx.MkApp(int_list.IsNilDecl, u), (BoolExpr)ctx.MkApp(int_list.IsConsDecl, u))); /* occurs check u != cons(x,u) */ Prove(ctx, ctx.MkNot(ctx.MkEq(u, l1))); /* destructors: is_cons(u) => u = cons(head(u),tail(u)) */ fml1 = ctx.MkEq(u, ctx.MkApp(int_list.ConsDecl, ctx.MkApp(int_list.HeadDecl, u), ctx.MkApp(int_list.TailDecl, u))); fml = ctx.MkImplies((BoolExpr)ctx.MkApp(int_list.IsConsDecl, u), fml1); Console.WriteLine("Formula {0}", fml); Prove(ctx, fml); Disprove(ctx, fml1); } /// /// Create a binary tree datatype. /// public static void TreeExample(Context ctx) { Console.WriteLine("TreeExample"); Sort cell; FuncDecl nil_decl, is_nil_decl, cons_decl, is_cons_decl, car_decl, cdr_decl; Expr nil, l1, l2, x, y, u, v; BoolExpr fml, fml1; string[] head_tail = new string[] { "car", "cdr" }; Sort[] sorts = new Sort[] { null, null }; uint[] sort_refs = new uint[] { 0, 0 }; Constructor nil_con, cons_con; nil_con = ctx.MkConstructor("nil", "is_nil", null, null, null); cons_con = ctx.MkConstructor("cons", "is_cons", head_tail, sorts, sort_refs); Constructor[] constructors = new Constructor[] { nil_con, cons_con }; cell = ctx.MkDatatypeSort("cell", constructors); nil_decl = nil_con.ConstructorDecl; is_nil_decl = nil_con.TesterDecl; cons_decl = cons_con.ConstructorDecl; is_cons_decl = cons_con.TesterDecl; FuncDecl[] cons_accessors = cons_con.AccessorDecls; car_decl = cons_accessors[0]; cdr_decl = cons_accessors[1]; nil = ctx.MkConst(nil_decl); l1 = ctx.MkApp(cons_decl, nil, nil); l2 = ctx.MkApp(cons_decl, l1, nil); /* nil != cons(nil, nil) */ Prove(ctx, ctx.MkNot(ctx.MkEq(nil, l1))); /* cons(x,u) = cons(x, v) => u = v */ u = ctx.MkConst("u", cell); v = ctx.MkConst("v", cell); x = ctx.MkConst("x", cell); y = ctx.MkConst("y", cell); l1 = ctx.MkApp(cons_decl, x, u); l2 = ctx.MkApp(cons_decl, y, v); Prove(ctx, ctx.MkImplies(ctx.MkEq(l1, l2), ctx.MkEq(u, v))); Prove(ctx, ctx.MkImplies(ctx.MkEq(l1, l2), ctx.MkEq(x, y))); /* is_nil(u) or is_cons(u) */ Prove(ctx, ctx.MkOr((BoolExpr)ctx.MkApp(is_nil_decl, u), (BoolExpr)ctx.MkApp(is_cons_decl, u))); /* occurs check u != cons(x,u) */ Prove(ctx, ctx.MkNot(ctx.MkEq(u, l1))); /* destructors: is_cons(u) => u = cons(car(u),cdr(u)) */ fml1 = ctx.MkEq(u, ctx.MkApp(cons_decl, ctx.MkApp(car_decl, u), ctx.MkApp(cdr_decl, u))); fml = ctx.MkImplies((BoolExpr)ctx.MkApp(is_cons_decl, u), fml1); Console.WriteLine("Formula {0}", fml); Prove(ctx, fml); Disprove(ctx, fml1); } /// /// Create a forest of trees. /// /// /// forest ::= nil | cons(tree, forest) /// tree ::= nil | cons(forest, forest) /// public static void ForestExample(Context ctx) { Console.WriteLine("ForestExample"); Sort tree, forest; FuncDecl nil1_decl, is_nil1_decl, cons1_decl, is_cons1_decl, car1_decl, cdr1_decl; FuncDecl nil2_decl, is_nil2_decl, cons2_decl, is_cons2_decl, car2_decl, cdr2_decl; Expr nil1, nil2, t1, t2, t3, t4, f1, f2, f3, l1, l2, x, y, u, v; // // Declare the names of the accessors for cons. // Then declare the sorts of the accessors. // For this example, all sorts refer to the new types 'forest' and 'tree' // being declared, so we pass in null for both sorts1 and sorts2. // On the other hand, the sort_refs arrays contain the indices of the // two new sorts being declared. The first element in sort1_refs // points to 'tree', which has index 1, the second element in sort1_refs array // points to 'forest', which has index 0. // Symbol[] head_tail1 = new Symbol[] { ctx.MkSymbol("head"), ctx.MkSymbol("tail") }; Sort[] sorts1 = new Sort[] { null, null }; uint[] sort1_refs = new uint[] { 1, 0 }; // the first item points to a tree, the second to a forest Symbol[] head_tail2 = new Symbol[] { ctx.MkSymbol("car"), ctx.MkSymbol("cdr") }; Sort[] sorts2 = new Sort[] { null, null }; uint[] sort2_refs = new uint[] { 0, 0 }; // both items point to the forest datatype. Constructor nil1_con, cons1_con, nil2_con, cons2_con; Constructor[] constructors1 = new Constructor[2], constructors2 = new Constructor[2]; Symbol[] sort_names = { ctx.MkSymbol("forest"), ctx.MkSymbol("tree") }; /* build a forest */ nil1_con = ctx.MkConstructor(ctx.MkSymbol("nil"), ctx.MkSymbol("is_nil"), null, null, null); cons1_con = ctx.MkConstructor(ctx.MkSymbol("cons1"), ctx.MkSymbol("is_cons1"), head_tail1, sorts1, sort1_refs); constructors1[0] = nil1_con; constructors1[1] = cons1_con; /* build a tree */ nil2_con = ctx.MkConstructor(ctx.MkSymbol("nil2"), ctx.MkSymbol("is_nil2"), null, null, null); cons2_con = ctx.MkConstructor(ctx.MkSymbol("cons2"), ctx.MkSymbol("is_cons2"), head_tail2, sorts2, sort2_refs); constructors2[0] = nil2_con; constructors2[1] = cons2_con; Constructor[][] clists = new Constructor[][] { constructors1, constructors2 }; Sort[] sorts = ctx.MkDatatypeSorts(sort_names, clists); forest = sorts[0]; tree = sorts[1]; // // Now that the datatype has been created. // Query the constructors for the constructor // functions, testers, and field accessors. // nil1_decl = nil1_con.ConstructorDecl; is_nil1_decl = nil1_con.TesterDecl; cons1_decl = cons1_con.ConstructorDecl; is_cons1_decl = cons1_con.TesterDecl; FuncDecl[] cons1_accessors = cons1_con.AccessorDecls; car1_decl = cons1_accessors[0]; cdr1_decl = cons1_accessors[1]; nil2_decl = nil2_con.ConstructorDecl; is_nil2_decl = nil2_con.TesterDecl; cons2_decl = cons2_con.ConstructorDecl; is_cons2_decl = cons2_con.TesterDecl; FuncDecl[] cons2_accessors = cons2_con.AccessorDecls; car2_decl = cons2_accessors[0]; cdr2_decl = cons2_accessors[1]; nil1 = ctx.MkConst(nil1_decl); nil2 = ctx.MkConst(nil2_decl); f1 = ctx.MkApp(cons1_decl, nil2, nil1); t1 = ctx.MkApp(cons2_decl, nil1, nil1); t2 = ctx.MkApp(cons2_decl, f1, nil1); t3 = ctx.MkApp(cons2_decl, f1, f1); t4 = ctx.MkApp(cons2_decl, nil1, f1); f2 = ctx.MkApp(cons1_decl, t1, nil1); f3 = ctx.MkApp(cons1_decl, t1, f1); /* nil != cons(nil,nil) */ Prove(ctx, ctx.MkNot(ctx.MkEq(nil1, f1))); Prove(ctx, ctx.MkNot(ctx.MkEq(nil2, t1))); /* cons(x,u) = cons(x, v) => u = v */ u = ctx.MkConst("u", forest); v = ctx.MkConst("v", forest); x = ctx.MkConst("x", tree); y = ctx.MkConst("y", tree); l1 = ctx.MkApp(cons1_decl, x, u); l2 = ctx.MkApp(cons1_decl, y, v); Prove(ctx, ctx.MkImplies(ctx.MkEq(l1, l2), ctx.MkEq(u, v))); Prove(ctx, ctx.MkImplies(ctx.MkEq(l1, l2), ctx.MkEq(x, y))); /* is_nil(u) or is_cons(u) */ Prove(ctx, ctx.MkOr((BoolExpr)ctx.MkApp(is_nil1_decl, u), (BoolExpr)ctx.MkApp(is_cons1_decl, u))); /* occurs check u != cons(x,u) */ Prove(ctx, ctx.MkNot(ctx.MkEq(u, l1))); } /// /// Demonstrate how to use #Eval. /// public static void EvalExample1(Context ctx) { Console.WriteLine("EvalExample1"); IntExpr x = ctx.MkIntConst("x"); IntExpr y = ctx.MkIntConst("y"); IntExpr two = ctx.MkInt(2); Solver solver = ctx.MkSolver(); /* assert x < y */ solver.Assert(ctx.MkLt(x, y)); /* assert x > 2 */ solver.Assert(ctx.MkGt(x, two)); /* find model for the constraints above */ Model model = null; if (Status.SATISFIABLE == solver.Check()) { model = solver.Model; Console.WriteLine("{0}", model); Console.WriteLine("\nevaluating x+y"); Expr v = model.Evaluate(ctx.MkAdd(x, y)); if (v != null) { Console.WriteLine("result = {0}", (v)); } else { Console.WriteLine("Failed to evaluate: x+y"); } } else { Console.WriteLine("BUG, the constraints are satisfiable."); } } /// /// Demonstrate how to use #Eval on tuples. /// public static void EvalExample2(Context ctx) { Console.WriteLine("EvalExample2"); Sort int_type = ctx.IntSort; TupleSort tuple = ctx.MkTupleSort( ctx.MkSymbol("mk_tuple"), // name of tuple constructor new Symbol[] { ctx.MkSymbol("first"), ctx.MkSymbol("second") }, // names of projection operators new Sort[] { int_type, int_type } // types of projection operators ); FuncDecl first = tuple.FieldDecls[0]; // declarations are for projections FuncDecl second = tuple.FieldDecls[1]; Expr tup1 = ctx.MkConst("t1", tuple); Expr tup2 = ctx.MkConst("t2", tuple); Solver solver = ctx.MkSolver(); /* assert tup1 != tup2 */ solver.Assert(ctx.MkNot(ctx.MkEq(tup1, tup2))); /* assert first tup1 = first tup2 */ solver.Assert(ctx.MkEq(ctx.MkApp(first, tup1), ctx.MkApp(first, tup2))); /* find model for the constraints above */ Model model = null; if (Status.SATISFIABLE == solver.Check()) { model = solver.Model; Console.WriteLine("{0}", model); Console.WriteLine("evaluating tup1 {0}", (model.Evaluate(tup1))); Console.WriteLine("evaluating tup2 {0}", (model.Evaluate(tup2))); Console.WriteLine("evaluating second(tup2) {0}", (model.Evaluate(ctx.MkApp(second, tup2)))); } else { Console.WriteLine("BUG, the constraints are satisfiable."); } } /// /// Demonstrate how to use Pushand Popto /// control the size of models. /// /// Note: this test is specialized to 32-bit bitvectors. public static void CheckSmall(Context ctx, Solver solver, BitVecExpr[] to_minimize) { Sort bv32 = ctx.MkBitVecSort(32); int num_Exprs = to_minimize.Length; UInt32[] upper = new UInt32[num_Exprs]; UInt32[] lower = new UInt32[num_Exprs]; BitVecExpr[] values = new BitVecExpr[num_Exprs]; for (int i = 0; i < upper.Length; ++i) { upper[i] = UInt32.MaxValue; lower[i] = 0; } bool some_work = true; int last_index = -1; UInt32 last_upper = 0; while (some_work) { solver.Push(); bool check_is_sat = true; while (check_is_sat && some_work) { // Assert all feasible bounds. for (int i = 0; i < num_Exprs; ++i) { solver.Assert(ctx.MkBVULE(to_minimize[i], ctx.MkBV(upper[i], 32))); } check_is_sat = Status.SATISFIABLE == solver.Check(); if (!check_is_sat) { if (last_index != -1) { lower[last_index] = last_upper + 1; } break; } Console.WriteLine("{0}", solver.Model); // narrow the bounds based on the current model. for (int i = 0; i < num_Exprs; ++i) { Expr v = solver.Model.Evaluate(to_minimize[i]); UInt64 ui = ((BitVecNum)v).UInt64; if (ui < upper[i]) { upper[i] = (UInt32)ui; } Console.WriteLine("{0} {1} {2}", i, lower[i], upper[i]); } // find a new bound to add some_work = false; last_index = 0; for (int i = 0; i < num_Exprs; ++i) { if (lower[i] < upper[i]) { last_upper = (upper[i] + lower[i]) / 2; last_index = i; solver.Assert(ctx.MkBVULE(to_minimize[i], ctx.MkBV(last_upper, 32))); some_work = true; break; } } } solver.Pop(); } } /// /// Reduced-size model generation example. /// public static void FindSmallModelExample(Context ctx) { Console.WriteLine("FindSmallModelExample"); BitVecExpr x = ctx.MkBVConst("x", 32); BitVecExpr y = ctx.MkBVConst("y", 32); BitVecExpr z = ctx.MkBVConst("z", 32); Solver solver = ctx.MkSolver(); solver.Assert(ctx.MkBVULE(x, ctx.MkBVAdd(y, z))); CheckSmall(ctx, solver, new BitVecExpr[] { x, y, z }); } /// /// Simplifier example. /// public static void SimplifierExample(Context ctx) { Console.WriteLine("SimplifierExample"); IntExpr x = ctx.MkIntConst("x"); IntExpr y = ctx.MkIntConst("y"); IntExpr z = ctx.MkIntConst("z"); IntExpr u = ctx.MkIntConst("u"); Expr t1 = ctx.MkAdd(x, ctx.MkSub(y, ctx.MkAdd(x, z))); Expr t2 = t1.Simplify(); Console.WriteLine("{0} -> {1}", (t1), (t2)); } /// /// Extract unsatisfiable core example /// public static void UnsatCoreAndProofExample(Context ctx) { Console.WriteLine("UnsatCoreAndProofExample"); Solver solver = ctx.MkSolver(); BoolExpr pa = ctx.MkBoolConst("PredA"); BoolExpr pb = ctx.MkBoolConst("PredB"); BoolExpr pc = ctx.MkBoolConst("PredC"); BoolExpr pd = ctx.MkBoolConst("PredD"); BoolExpr p1 = ctx.MkBoolConst("P1"); BoolExpr p2 = ctx.MkBoolConst("P2"); BoolExpr p3 = ctx.MkBoolConst("P3"); BoolExpr p4 = ctx.MkBoolConst("P4"); BoolExpr[] assumptions = new BoolExpr[] { ctx.MkNot(p1), ctx.MkNot(p2), ctx.MkNot(p3), ctx.MkNot(p4) }; BoolExpr f1 = ctx.MkAnd(new BoolExpr[] { pa, pb, pc }); BoolExpr f2 = ctx.MkAnd(new BoolExpr[] { pa, ctx.MkNot(pb), pc }); BoolExpr f3 = ctx.MkOr(ctx.MkNot(pa), ctx.MkNot(pc)); BoolExpr f4 = pd; solver.Assert(ctx.MkOr(f1, p1)); solver.Assert(ctx.MkOr(f2, p2)); solver.Assert(ctx.MkOr(f3, p3)); solver.Assert(ctx.MkOr(f4, p4)); Status result = solver.Check(assumptions); if (result == Status.UNSATISFIABLE) { Console.WriteLine("unsat"); Console.WriteLine("proof: {0}", solver.Proof); Console.WriteLine("core: "); foreach (Expr c in solver.UnsatCore) { Console.WriteLine("{0}", c); } } } /// /// Extract unsatisfiable core example with AssertAndTrack /// public static void UnsatCoreAndProofExample2(Context ctx) { Console.WriteLine("UnsatCoreAndProofExample2"); Solver solver = ctx.MkSolver(); BoolExpr pa = ctx.MkBoolConst("PredA"); BoolExpr pb = ctx.MkBoolConst("PredB"); BoolExpr pc = ctx.MkBoolConst("PredC"); BoolExpr pd = ctx.MkBoolConst("PredD"); BoolExpr f1 = ctx.MkAnd(new BoolExpr[] { pa, pb, pc }); BoolExpr f2 = ctx.MkAnd(new BoolExpr[] { pa, ctx.MkNot(pb), pc }); BoolExpr f3 = ctx.MkOr(ctx.MkNot(pa), ctx.MkNot(pc)); BoolExpr f4 = pd; BoolExpr p1 = ctx.MkBoolConst("P1"); BoolExpr p2 = ctx.MkBoolConst("P2"); BoolExpr p3 = ctx.MkBoolConst("P3"); BoolExpr p4 = ctx.MkBoolConst("P4"); solver.AssertAndTrack(f1, p1); solver.AssertAndTrack(f2, p2); solver.AssertAndTrack(f3, p3); solver.AssertAndTrack(f4, p4); Status result = solver.Check(); if (result == Status.UNSATISFIABLE) { Console.WriteLine("unsat"); Console.WriteLine("core: "); foreach (Expr c in solver.UnsatCore) { Console.WriteLine("{0}", c); } } } public static void FiniteDomainExample(Context ctx) { Console.WriteLine("FiniteDomainExample"); var s = ctx.MkFiniteDomainSort("S", 10); var t = ctx.MkFiniteDomainSort("T", 10); var s1 = ctx.MkNumeral(1, s); var t1 = ctx.MkNumeral(1, t); Console.WriteLine("{0}", s); Console.WriteLine("{0}", t); Console.WriteLine("{0}", s1); Console.WriteLine("{0}", ctx.MkNumeral(2, s)); Console.WriteLine("{0}", t1); // But you cannot mix numerals of different sorts // even if the size of their domains are the same: // Console.WriteLine("{0}", ctx.MkEq(s1, t1)); } public static void FloatingPointExample1(Context ctx) { Console.WriteLine("FloatingPointExample1"); FPSort s = ctx.MkFPSort(11, 53); Console.WriteLine("Sort: {0}", s); FPNum x = (FPNum)ctx.MkNumeral("-1e1", s); /* -1 * 10^1 = -10 */ FPNum y = (FPNum)ctx.MkNumeral("-10", s); /* -10 */ FPNum z = (FPNum)ctx.MkNumeral("-1.25p3", s); /* -1.25 * 2^3 = -1.25 * 8 = -10 */ Console.WriteLine("x={0}; y={1}; z={2}", x.ToString(), y.ToString(), z.ToString()); BoolExpr a = ctx.MkAnd(ctx.MkFPEq(x, y), ctx.MkFPEq(y, z)); Check(ctx, ctx.MkNot(a), Status.UNSATISFIABLE); /* nothing is equal to NaN according to floating-point * equality, so NaN == k should be unsatisfiable. */ FPExpr k = (FPExpr)ctx.MkConst("x", s); FPExpr nan = ctx.MkFPNaN(s); /* solver that runs the default tactic for QF_FP. */ Solver slvr = ctx.MkSolver("QF_FP"); slvr.Add(ctx.MkFPEq(nan, k)); if (slvr.Check() != Status.UNSATISFIABLE) throw new TestFailedException(); Console.WriteLine("OK, unsat:" + Environment.NewLine + slvr); /* NaN is equal to NaN according to normal equality. */ slvr = ctx.MkSolver("QF_FP"); slvr.Add(ctx.MkEq(nan, nan)); if (slvr.Check() != Status.SATISFIABLE) throw new TestFailedException(); Console.WriteLine("OK, sat:" + Environment.NewLine + slvr); /* Let's prove -1e1 * -1.25e3 == +100 */ x = (FPNum)ctx.MkNumeral("-1e1", s); y = (FPNum)ctx.MkNumeral("-1.25p3", s); FPExpr x_plus_y = (FPExpr)ctx.MkConst("x_plus_y", s); FPNum r = (FPNum)ctx.MkNumeral("100", s); slvr = ctx.MkSolver("QF_FP"); slvr.Add(ctx.MkEq(x_plus_y, ctx.MkFPMul(ctx.MkFPRoundNearestTiesToAway(), x, y))); slvr.Add(ctx.MkNot(ctx.MkFPEq(x_plus_y, r))); if (slvr.Check() != Status.UNSATISFIABLE) throw new TestFailedException(); Console.WriteLine("OK, unsat:" + Environment.NewLine + slvr); } public static void FloatingPointExample2(Context ctx) { Console.WriteLine("FloatingPointExample2"); FPSort double_sort = ctx.MkFPSort(11, 53); FPRMSort rm_sort = ctx.MkFPRoundingModeSort(); FPRMExpr rm = (FPRMExpr)ctx.MkConst(ctx.MkSymbol("rm"), rm_sort); BitVecExpr x = (BitVecExpr)ctx.MkConst(ctx.MkSymbol("x"), ctx.MkBitVecSort(64)); FPExpr y = (FPExpr)ctx.MkConst(ctx.MkSymbol("y"), double_sort); FPExpr fp_val = ctx.MkFP(42, double_sort); BoolExpr c1 = ctx.MkEq(y, fp_val); BoolExpr c2 = ctx.MkEq(x, ctx.MkFPToBV(rm, y, 64, false)); BoolExpr c3 = ctx.MkEq(x, ctx.MkBV(42, 64)); BoolExpr c4 = ctx.MkEq(ctx.MkNumeral(42, ctx.RealSort), ctx.MkFPToReal(fp_val)); BoolExpr c5 = ctx.MkAnd(c1, c2, c3, c4); Console.WriteLine("c5 = " + c5); /* Generic solver */ Solver s = ctx.MkSolver(); s.Assert(c5); Console.WriteLine(s); if (s.Check() != Status.SATISFIABLE) throw new TestFailedException(); Console.WriteLine("OK, model: {0}", s.Model.ToString()); } static void Main(string[] args) { try { Microsoft.Z3.Global.ToggleWarningMessages(true); Log.Open("test.log"); Console.Write("Z3 Major Version: "); Console.WriteLine(Microsoft.Z3.Version.Major.ToString()); Console.Write("Z3 Full Version: "); Console.WriteLine(Microsoft.Z3.Version.ToString()); SimpleExample(); // These examples need model generation turned on. using (Context ctx = new Context(new Dictionary() { { "model", "true" } })) { BasicTests(ctx); CastingTest(ctx); SudokuExample(ctx); QuantifierExample1(ctx); QuantifierExample2(ctx); LogicExample(ctx); ParOrExample(ctx); FindModelExample1(ctx); FindModelExample2(ctx); PushPopExample1(ctx); ArrayExample1(ctx); ArrayExample3(ctx); BitvectorExample1(ctx); BitvectorExample2(ctx); ParserExample1(ctx); ParserExample2(ctx); ParserExample4(ctx); ParserExample5(ctx); ITEExample(ctx); EvalExample1(ctx); EvalExample2(ctx); FindSmallModelExample(ctx); SimplifierExample(ctx); FiniteDomainExample(ctx); FloatingPointExample1(ctx); FloatingPointExample2(ctx); } // These examples need proof generation turned on. using (Context ctx = new Context(new Dictionary() { { "proof", "true" } })) { ProveExample1(ctx); ProveExample2(ctx); ArrayExample2(ctx); TupleExample(ctx); ParserExample3(ctx); EnumExample(ctx); ListExample(ctx); TreeExample(ctx); ForestExample(ctx); UnsatCoreAndProofExample(ctx); UnsatCoreAndProofExample2(ctx); } // These examples need proof generation turned on and auto-config set to false. using (Context ctx = new Context(new Dictionary() { {"proof", "true" }, {"auto-config", "false" } })) { QuantifierExample3(ctx); QuantifierExample4(ctx); } Log.Close(); if (Log.isOpen()) Console.WriteLine("Log is still open!"); } catch (Z3Exception ex) { Console.WriteLine("Z3 Managed Exception: " + ex.Message); Console.WriteLine("Stack trace: " + ex.StackTrace); } catch (TestFailedException ex) { Console.WriteLine("TEST CASE FAILED: " + ex.Message); } } } } z3-4.4.0/examples/interp/0000755000175000017500000000000012540347414014764 5ustar michaelmichaelz3-4.4.0/examples/interp/iz3.cpp0000755000175000017500000003004312540347414016200 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include #include #include #include #include #include "z3.h" int usage(const char **argv){ std::cerr << "usage: " << argv[0] << " [options] file.smt" << std::endl; std::cerr << std::endl; std::cerr << "options:" << std::endl; std::cerr << " -t,--tree tree interpolation" << std::endl; std::cerr << " -c,--check check result" << std::endl; std::cerr << " -p,--profile profile execution" << std::endl; std::cerr << " -w,--weak weak interpolants" << std::endl; std::cerr << " -f,--flat ouput flat formulas" << std::endl; std::cerr << " -o ouput to SMT-LIB file" << std::endl; std::cerr << " -a,--anon anonymize" << std::endl; std::cerr << " -s,--simple simple proof mode" << std::endl; std::cerr << std::endl; return 1; } int main(int argc, const char **argv) { bool tree_mode = false; bool check_mode = false; bool profile_mode = false; bool incremental_mode = false; std::string output_file; bool flat_mode = false; bool anonymize = false; bool write = false; Z3_config cfg = Z3_mk_config(); // Z3_interpolation_options options = Z3_mk_interpolation_options(); Z3_params options = 0; /* Parse the command line */ int argn = 1; while(argn < argc-1){ std::string flag = argv[argn]; if(flag[0] == '-'){ if(flag == "-t" || flag == "--tree") tree_mode = true; else if(flag == "-c" || flag == "--check") check_mode = true; else if(flag == "-p" || flag == "--profile") profile_mode = true; #if 0 else if(flag == "-w" || flag == "--weak") Z3_set_interpolation_option(options,"weak","1"); else if(flag == "--secondary") Z3_set_interpolation_option(options,"secondary","1"); #endif else if(flag == "-i" || flag == "--incremental") incremental_mode = true; else if(flag == "-o"){ argn++; if(argn >= argc) return usage(argv); output_file = argv[argn]; } else if(flag == "-f" || flag == "--flat") flat_mode = true; else if(flag == "-a" || flag == "--anon") anonymize = true; else if(flag == "-w" || flag == "--write") write = true; else if(flag == "-s" || flag == "--simple") Z3_set_param_value(cfg,"PREPROCESS","false"); else return usage(argv); } argn++; } if(argn != argc-1) return usage(argv); const char *filename = argv[argn]; /* Create a Z3 context to contain formulas */ Z3_context ctx = Z3_mk_interpolation_context(cfg); if(write || anonymize) Z3_set_ast_print_mode(ctx,Z3_PRINT_SMTLIB2_COMPLIANT); else if(!flat_mode) Z3_set_ast_print_mode(ctx,Z3_PRINT_SMTLIB_COMPLIANT); /* Read an interpolation problem */ unsigned num; Z3_ast *constraints; unsigned *parents = 0; const char *error; bool ok; unsigned num_theory; Z3_ast *theory; ok = Z3_read_interpolation_problem(ctx, &num, &constraints, tree_mode ? &parents : 0, filename, &error, &num_theory, &theory); /* If parse failed, print the error message */ if(!ok){ std::cerr << error << "\n"; return 1; } /* if we get only one formula, and it is a conjunction, split it into conjuncts. */ if(!tree_mode && num == 1){ Z3_app app = Z3_to_app(ctx,constraints[0]); Z3_func_decl func = Z3_get_app_decl(ctx,app); Z3_decl_kind dk = Z3_get_decl_kind(ctx,func); if(dk == Z3_OP_AND){ int nconjs = Z3_get_app_num_args(ctx,app); if(nconjs > 1){ std::cout << "Splitting formula into " << nconjs << " conjuncts...\n"; num = nconjs; constraints = new Z3_ast[num]; for(int k = 0; k < num; k++) constraints[k] = Z3_get_app_arg(ctx,app,k); } } } /* Write out anonymized version. */ if(write || anonymize){ #if 0 Z3_anonymize_ast_vector(ctx,num,constraints); #endif std::string ofn = output_file.empty() ? "iz3out.smt2" : output_file; Z3_write_interpolation_problem(ctx, num, constraints, parents, ofn.c_str(), num_theory, theory); std::cout << "anonymized problem written to " << ofn << "\n"; exit(0); } /* Compute an interpolant, or get a model. */ Z3_ast *interpolants = (Z3_ast *)malloc((num-1) * sizeof(Z3_ast)); Z3_model model = 0; Z3_lbool result; if(!incremental_mode){ /* In non-incremental mode, we just pass the constraints. */ result = Z3_L_UNDEF; // FIXME: Z3_interpolate(ctx, num, constraints, parents, options, interpolants, &model, 0, false, num_theory, theory); } else { /* This is a somewhat useless demonstration of incremental mode. Here, we assert the constraints in the context, then pass them to iZ3 in an array, so iZ3 knows the sequence. Note it's safe to pass "true", even though we haven't techically asserted if. */ Z3_push(ctx); std::vector asserted(num); /* We start with nothing asserted. */ for(int i = 0; i < num; i++) asserted[i] = Z3_mk_true(ctx); /* Now we assert the constrints one at a time until UNSAT. */ for(int i = 0; i < num; i++){ asserted[i] = constraints[i]; Z3_assert_cnstr(ctx,constraints[i]); // assert one constraint result = Z3_L_UNDEF; // FIXME: Z3_interpolate(ctx, num, &asserted[0], parents, options, interpolants, &model, 0, true, 0, 0); if(result == Z3_L_FALSE){ for(unsigned j = 0; j < num-1; j++) /* Since we want the interpolant formulas to survive a "pop", we "persist" them here. */ Z3_persist_ast(ctx,interpolants[j],1); break; } } Z3_pop(ctx,1); } switch (result) { /* If UNSAT, print the interpolants */ case Z3_L_FALSE: printf("unsat\n"); if(output_file.empty()){ printf("interpolant:\n"); for(int i = 0; i < num-1; i++) printf("%s\n", Z3_ast_to_string(ctx, interpolants[i])); } else { #if 0 Z3_write_interpolation_problem(ctx,num-1,interpolants,0,output_file.c_str()); printf("interpolant written to %s\n",output_file.c_str()); #endif } #if 1 if(check_mode){ std::cout << "Checking interpolant...\n"; bool chk; chk = Z3_check_interpolant(ctx,num,constraints,parents,interpolants,&error,num_theory,theory); if(chk) std::cout << "Interpolant is correct\n"; else { std::cout << "Interpolant is incorrect\n"; std::cout << error; return 1; } } #endif break; case Z3_L_UNDEF: printf("fail\n"); break; case Z3_L_TRUE: printf("sat\n"); printf("model:\n%s\n", Z3_model_to_string(ctx, model)); break; } if(profile_mode) std::cout << Z3_interpolation_profile(ctx); /* Delete the model if there is one */ if (model) Z3_del_model(ctx, model); /* Delete logical context. */ Z3_del_context(ctx); free(interpolants); return 0; } #if 0 int test(){ int i; /* Create a Z3 context to contain formulas */ Z3_config cfg = Z3_mk_config(); Z3_context ctx = iz3_mk_context(cfg); int num = 2; Z3_ast *constraints = (Z3_ast *)malloc(num * sizeof(Z3_ast)); #if 1 Z3_sort arr = Z3_mk_array_sort(ctx,Z3_mk_int_sort(ctx),Z3_mk_bool_sort(ctx)); Z3_symbol as = Z3_mk_string_symbol(ctx, "a"); Z3_symbol bs = Z3_mk_string_symbol(ctx, "b"); Z3_symbol xs = Z3_mk_string_symbol(ctx, "x"); Z3_ast a = Z3_mk_const(ctx,as,arr); Z3_ast b = Z3_mk_const(ctx,bs,arr); Z3_ast x = Z3_mk_const(ctx,xs,Z3_mk_int_sort(ctx)); Z3_ast c1 = Z3_mk_eq(ctx,a,Z3_mk_store(ctx,b,x,Z3_mk_true(ctx))); Z3_ast c2 = Z3_mk_not(ctx,Z3_mk_select(ctx,a,x)); #else Z3_symbol xs = Z3_mk_string_symbol(ctx, "x"); Z3_ast x = Z3_mk_const(ctx,xs,Z3_mk_bool_sort(ctx)); Z3_ast c1 = Z3_mk_eq(ctx,x,Z3_mk_true(ctx)); Z3_ast c2 = Z3_mk_eq(ctx,x,Z3_mk_false(ctx)); #endif constraints[0] = c1; constraints[1] = c2; /* print out the result for grins. */ // Z3_string smtout = Z3_benchmark_to_smtlib_string (ctx, "foo", "QFLIA", "sat", "", num, constraints, Z3_mk_true(ctx)); // Z3_string smtout = Z3_ast_to_string(ctx,constraints[0]); // Z3_string smtout = Z3_context_to_string(ctx); // puts(smtout); iz3_print(ctx,num,constraints,"iZ3temp.smt"); /* Make room for interpolants. */ Z3_ast *interpolants = (Z3_ast *)malloc((num-1) * sizeof(Z3_ast)); /* Make room for the model. */ Z3_model model = 0; /* Call the prover */ Z3_lbool result = iz3_interpolate(ctx, num, constraints, interpolants, &model); switch (result) { /* If UNSAT, print the interpolants */ case Z3_L_FALSE: printf("unsat, interpolants:\n"); for(i = 0; i < num-1; i++) printf("%s\n", Z3_ast_to_string(ctx, interpolants[i])); break; case Z3_L_UNDEF: printf("fail\n"); break; case Z3_L_TRUE: printf("sat\n"); printf("model:\n%s\n", Z3_model_to_string(ctx, model)); break; } /* Delete the model if there is one */ if (model) Z3_del_model(ctx, model); /* Delete logical context (note, we call iz3_del_context, not Z3_del_context */ iz3_del_context(ctx); return 1; } struct z3_error { Z3_error_code c; z3_error(Z3_error_code _c) : c(_c) {} }; extern "C" { static void throw_z3_error(Z3_error_code c){ throw z3_error(c); } } int main(int argc, const char **argv) { /* Create a Z3 context to contain formulas */ Z3_config cfg = Z3_mk_config(); Z3_context ctx = iz3_mk_context(cfg); Z3_set_error_handler(ctx, throw_z3_error); /* Make some constraints, by parsing an smtlib formatted file given as arg 1 */ try { Z3_parse_smtlib_file(ctx, argv[1], 0, 0, 0, 0, 0, 0); } catch(const z3_error &err){ std::cerr << "Z3 error: " << Z3_get_error_msg(err.c) << "\n"; std::cerr << Z3_get_smtlib_error(ctx) << "\n"; return(1); } /* Get the constraints from the parser. */ int num = Z3_get_smtlib_num_formulas(ctx); if(num == 0){ std::cerr << "iZ3 error: File contains no formulas.\n"; return 1; } Z3_ast *constraints = (Z3_ast *)malloc(num * sizeof(Z3_ast)); int i; for (i = 0; i < num; i++) constraints[i] = Z3_get_smtlib_formula(ctx, i); /* if we get only one formula, and it is a conjunction, split it into conjuncts. */ if(num == 1){ Z3_app app = Z3_to_app(ctx,constraints[0]); Z3_func_decl func = Z3_get_app_decl(ctx,app); Z3_decl_kind dk = Z3_get_decl_kind(ctx,func); if(dk == Z3_OP_AND){ int nconjs = Z3_get_app_num_args(ctx,app); if(nconjs > 1){ std::cout << "Splitting formula into " << nconjs << " conjuncts...\n"; num = nconjs; constraints = new Z3_ast[num]; for(int k = 0; k < num; k++) constraints[k] = Z3_get_app_arg(ctx,app,k); } } } /* print out the result for grins. */ // Z3_string smtout = Z3_benchmark_to_smtlib_string (ctx, "foo", "QFLIA", "sat", "", num, constraints, Z3_mk_true(ctx)); // Z3_string smtout = Z3_ast_to_string(ctx,constraints[0]); // Z3_string smtout = Z3_context_to_string(ctx); // puts(smtout); // iz3_print(ctx,num,constraints,"iZ3temp.smt"); /* Make room for interpolants. */ Z3_ast *interpolants = (Z3_ast *)malloc((num-1) * sizeof(Z3_ast)); /* Make room for the model. */ Z3_model model = 0; /* Call the prover */ Z3_lbool result = iz3_interpolate(ctx, num, constraints, interpolants, &model); switch (result) { /* If UNSAT, print the interpolants */ case Z3_L_FALSE: printf("unsat, interpolants:\n"); for(i = 0; i < num-1; i++) printf("%s\n", Z3_ast_to_string(ctx, interpolants[i])); std::cout << "Checking interpolants...\n"; const char *error; if(iZ3_check_interpolant(ctx, num, constraints, 0, interpolants, &error)) std::cout << "Interpolant is correct\n"; else { std::cout << "Interpolant is incorrect\n"; std::cout << error << "\n"; } break; case Z3_L_UNDEF: printf("fail\n"); break; case Z3_L_TRUE: printf("sat\n"); printf("model:\n%s\n", Z3_model_to_string(ctx, model)); break; } /* Delete the model if there is one */ if (model) Z3_del_model(ctx, model); /* Delete logical context (note, we call iz3_del_context, not Z3_del_context */ iz3_del_context(ctx); return 0; } #endif z3-4.4.0/examples/c++/0000755000175000017500000000000012540347414014033 5ustar michaelmichaelz3-4.4.0/examples/c++/README0000644000175000017500000000063512540347414014717 0ustar michaelmichaelSmall example using the c++ bindings. To build the example execute make examples in the build directory. This command will create the executable cpp_example. On Windows, you can just execute it. On OSX and Linux, you must install z3 first using sudo make install OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (OSX) with the build directory. You need that to be able to find the Z3 shared library.z3-4.4.0/examples/c++/example.cpp0000644000175000017500000010004412540347414016171 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include #include"z3++.h" using namespace z3; /** Demonstration of how Z3 can be used to prove validity of De Morgan's Duality Law: {e not(x and y) <-> (not x) or ( not y) } */ void demorgan() { std::cout << "de-Morgan example\n"; context c; expr x = c.bool_const("x"); expr y = c.bool_const("y"); expr conjecture = !(x && y) == (!x || !y); solver s(c); // adding the negation of the conjecture as a constraint. s.add(!conjecture); std::cout << s << "\n"; std::cout << s.to_smt2() << "\n"; switch (s.check()) { case unsat: std::cout << "de-Morgan is valid\n"; break; case sat: std::cout << "de-Morgan is not valid\n"; break; case unknown: std::cout << "unknown\n"; break; } } /** \brief Find a model for x >= 1 and y < x + 3. */ void find_model_example1() { std::cout << "find_model_example1\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); solver s(c); s.add(x >= 1); s.add(y < x + 3); std::cout << s.check() << "\n"; model m = s.get_model(); std::cout << m << "\n"; // traversing the model for (unsigned i = 0; i < m.size(); i++) { func_decl v = m[i]; // this problem contains only constants assert(v.arity() == 0); std::cout << v.name() << " = " << m.get_const_interp(v) << "\n"; } // we can evaluate expressions in the model. std::cout << "x + y + 1 = " << m.eval(x + y + 1) << "\n"; } /** \brief Prove x = y implies g(x) = g(y), and disprove x = y implies g(g(x)) = g(y). This function demonstrates how to create uninterpreted types and functions. */ void prove_example1() { std::cout << "prove_example1\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); sort I = c.int_sort(); func_decl g = function("g", I, I); solver s(c); expr conjecture1 = implies(x == y, g(x) == g(y)); std::cout << "conjecture 1\n" << conjecture1 << "\n"; s.add(!conjecture1); if (s.check() == unsat) std::cout << "proved" << "\n"; else std::cout << "failed to prove" << "\n"; s.reset(); // remove all assertions from solver s expr conjecture2 = implies(x == y, g(g(x)) == g(y)); std::cout << "conjecture 2\n" << conjecture2 << "\n"; s.add(!conjecture2); if (s.check() == unsat) { std::cout << "proved" << "\n"; } else { std::cout << "failed to prove" << "\n"; model m = s.get_model(); std::cout << "counterexample:\n" << m << "\n"; std::cout << "g(g(x)) = " << m.eval(g(g(x))) << "\n"; std::cout << "g(y) = " << m.eval(g(y)) << "\n"; } } /** \brief Prove not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0 . Then, show that z < -1 is not implied. This example demonstrates how to combine uninterpreted functions and arithmetic. */ void prove_example2() { std::cout << "prove_example1\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); expr z = c.int_const("z"); sort I = c.int_sort(); func_decl g = function("g", I, I); expr conjecture1 = implies(g(g(x) - g(y)) != g(z) && x + z <= y && y <= x, z < 0); solver s(c); s.add(!conjecture1); std::cout << "conjecture 1:\n" << conjecture1 << "\n"; if (s.check() == unsat) std::cout << "proved" << "\n"; else std::cout << "failed to prove" << "\n"; expr conjecture2 = implies(g(g(x) - g(y)) != g(z) && x + z <= y && y <= x, z < -1); s.reset(); s.add(!conjecture2); std::cout << "conjecture 2:\n" << conjecture2 << "\n"; if (s.check() == unsat) { std::cout << "proved" << "\n"; } else { std::cout << "failed to prove" << "\n"; std::cout << "counterexample:\n" << s.get_model() << "\n"; } } /** \brief Nonlinear arithmetic example 1 */ void nonlinear_example1() { std::cout << "nonlinear example 1\n"; config cfg; cfg.set("auto_config", true); context c(cfg); expr x = c.real_const("x"); expr y = c.real_const("y"); expr z = c.real_const("z"); solver s(c); s.add(x*x + y*y == 1); // x^2 + y^2 == 1 s.add(x*x*x + z*z*z < c.real_val("1/2")); // x^3 + z^3 < 1/2 s.add(z != 0); std::cout << s.check() << "\n"; model m = s.get_model(); std::cout << m << "\n"; set_param("pp.decimal", true); // set decimal notation std::cout << "model in decimal notation\n"; std::cout << m << "\n"; set_param("pp.decimal-precision", 50); // increase number of decimal places to 50. std::cout << "model using 50 decimal places\n"; std::cout << m << "\n"; set_param("pp.decimal", false); // disable decimal notation } /** \brief Simple function that tries to prove the given conjecture using the following steps: 1- create a solver 2- assert the negation of the conjecture 3- checks if the result is unsat. */ void prove(expr conjecture) { context & c = conjecture.ctx(); solver s(c); s.add(!conjecture); std::cout << "conjecture:\n" << conjecture << "\n"; if (s.check() == unsat) { std::cout << "proved" << "\n"; } else { std::cout << "failed to prove" << "\n"; std::cout << "counterexample:\n" << s.get_model() << "\n"; } } /** \brief Simple bit-vector example. This example disproves that x - 10 <= 0 IFF x <= 10 for (32-bit) machine integers */ void bitvector_example1() { std::cout << "bitvector example 1\n"; context c; expr x = c.bv_const("x", 32); // using signed <= prove((x - 10 <= 0) == (x <= 10)); // using unsigned <= prove(ule(x - 10, 0) == ule(x, 10)); expr y = c.bv_const("y", 32); prove(implies(concat(x, y) == concat(y, x), x == y)); } /** \brief Find x and y such that: x ^ y - 103 == x * y */ void bitvector_example2() { std::cout << "bitvector example 2\n"; context c; expr x = c.bv_const("x", 32); expr y = c.bv_const("y", 32); solver s(c); // In C++, the operator == has higher precedence than ^. s.add((x ^ y) - 103 == x * y); std::cout << s << "\n"; std::cout << s.check() << "\n"; std::cout << s.get_model() << "\n"; } /** \brief Mixing C and C++ APIs. */ void capi_example() { std::cout << "capi example\n"; context c; expr x = c.bv_const("x", 32); expr y = c.bv_const("y", 32); // Invoking a C API function, and wrapping the result using an expr object. expr r = to_expr(c, Z3_mk_bvsrem(c, x, y)); std::cout << "r: " << r << "\n"; } /** \brief Demonstrate how to evaluate expressions in a model. */ void eval_example1() { std::cout << "eval example 1\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); solver s(c); /* assert x < y */ s.add(x < y); /* assert x > 2 */ s.add(x > 2); std::cout << s.check() << "\n"; model m = s.get_model(); std::cout << "Model:\n" << m << "\n"; std::cout << "x+y = " << m.eval(x+y) << "\n"; } /** \brief Several contexts can be used simultaneously. */ void two_contexts_example1() { std::cout << "two contexts example 1\n"; context c1, c2; expr x = c1.int_const("x"); expr n = x + 1; // We cannot mix expressions from different contexts, but we can copy // an expression from one context to another. // The following statement copies the expression n from c1 to c2. expr n1 = to_expr(c2, Z3_translate(c1, n, c2)); std::cout << n1 << "\n"; } /** \brief Demonstrates how to catch API usage errors. */ void error_example() { std::cout << "error example\n"; context c; expr x = c.bool_const("x"); // Error using the C API can be detected using Z3_get_error_code. // The next call fails because x is a constant. Z3_ast arg = Z3_get_app_arg(c, x, 0); if (Z3_get_error_code(c) != Z3_OK) { std::cout << "last call failed.\n"; } // The C++ layer converts API usage errors into exceptions. try { // The next call fails because x is a Boolean. expr n = x + 1; } catch (exception ex) { std::cout << "failed: " << ex << "\n"; } // The functions to_expr, to_sort and to_func_decl also convert C API errors into exceptions. try { expr arg = to_expr(c, Z3_get_app_arg(c, x, 0)); } catch (exception ex) { std::cout << "failed: " << ex << "\n"; } } /** \brief Demonstrate different ways of creating rational numbers: decimal and fractional representations. */ void numeral_example() { std::cout << "numeral example\n"; context c; expr n1 = c.real_val("1/2"); expr n2 = c.real_val("0.5"); expr n3 = c.real_val(1, 2); std::cout << n1 << " " << n2 << " " << n3 << "\n"; prove(n1 == n2 && n1 == n3); n1 = c.real_val("-1/3"); n2 = c.real_val("-0.3333333333333333333333333333333333"); std::cout << n1 << " " << n2 << "\n"; prove(n1 != n2); } /** \brief Test ite-term (if-then-else terms). */ void ite_example() { std::cout << "if-then-else example\n"; context c; expr f = c.bool_val(false); expr one = c.int_val(1); expr zero = c.int_val(0); expr ite = to_expr(c, Z3_mk_ite(c, f, one, zero)); 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. */ void quantifier_example() { std::cout << "quantifier example\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); sort I = c.int_sort(); func_decl f = function("f", I, I, I); solver s(c); // making sure model based quantifier instantiation is enabled. params p(c); p.set("mbqi", true); s.set(p); s.add(forall(x, y, f(x, y) >= 0)); expr a = c.int_const("a"); s.add(f(a, a) < a); std::cout << s << "\n"; std::cout << s.check() << "\n"; std::cout << s.get_model() << "\n"; s.add(a < 0); std::cout << s.check() << "\n"; } /** \brief Unsat core example */ void unsat_core_example1() { std::cout << "unsat core example1\n"; context c; // We use answer literals to track assertions. // An answer literal is essentially a fresh Boolean marker // that is used to track an assertion. // For example, if we want to track assertion F, we // create a fresh Boolean variable p and assert (p => F) // Then we provide p as an argument for the check method. expr p1 = c.bool_const("p1"); expr p2 = c.bool_const("p2"); expr p3 = c.bool_const("p3"); expr x = c.int_const("x"); expr y = c.int_const("y"); solver s(c); s.add(implies(p1, x > 10)); s.add(implies(p1, y > x)); s.add(implies(p2, y < 5)); s.add(implies(p3, y > 0)); expr assumptions[3] = { p1, p2, p3 }; std::cout << s.check(3, assumptions) << "\n"; expr_vector core = s.unsat_core(); std::cout << core << "\n"; std::cout << "size: " << core.size() << "\n"; for (unsigned i = 0; i < core.size(); i++) { std::cout << core[i] << "\n"; } // Trying again without p2 expr assumptions2[2] = { p1, p3 }; std::cout << s.check(2, assumptions2) << "\n"; } /** \brief Unsat core example 2 */ void unsat_core_example2() { std::cout << "unsat core example 2\n"; context c; // The answer literal mechanism, described in the previous example, // tracks assertions. An assertion can be a complicated // formula containing containing the conjunction of many subformulas. expr p1 = c.bool_const("p1"); expr x = c.int_const("x"); expr y = c.int_const("y"); solver s(c); expr F = x > 10 && y > x && y < 5 && y > 0; s.add(implies(p1, F)); expr assumptions[1] = { p1 }; std::cout << s.check(1, assumptions) << "\n"; expr_vector core = s.unsat_core(); std::cout << core << "\n"; std::cout << "size: " << core.size() << "\n"; for (unsigned i = 0; i < core.size(); i++) { std::cout << core[i] << "\n"; } // The core is not very informative, since p1 is tracking the formula F // that is a conjunction of subformulas. // Now, we use the following piece of code to break this conjunction // into individual subformulas. First, we flat the conjunctions by // using the method simplify. std::vector qs; // auxiliary vector used to store new answer literals. assert(F.is_app()); // I'm assuming F is an application. if (F.decl().decl_kind() == Z3_OP_AND) { // F is a conjunction std::cout << "F num. args (before simplify): " << F.num_args() << "\n"; F = F.simplify(); std::cout << "F num. args (after simplify): " << F.num_args() << "\n"; for (unsigned i = 0; i < F.num_args(); i++) { std::cout << "Creating answer literal q" << i << " for " << F.arg(i) << "\n"; std::stringstream qname; qname << "q" << i; expr qi = c.bool_const(qname.str().c_str()); // create a new answer literal s.add(implies(qi, F.arg(i))); qs.push_back(qi); } } // The solver s already contains p1 => F // To disable F, we add (not p1) as an additional assumption qs.push_back(!p1); std::cout << s.check(qs.size(), &qs[0]) << "\n"; expr_vector core2 = s.unsat_core(); std::cout << core2 << "\n"; std::cout << "size: " << core2.size() << "\n"; for (unsigned i = 0; i < core2.size(); i++) { std::cout << core2[i] << "\n"; } } /** \brief Unsat core example 3 */ void unsat_core_example3() { // Extract unsat core using tracked assertions std::cout << "unsat core example 3\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); solver s(c); // enabling unsat core tracking params p(c); p.set("unsat_core", true); s.set(p); // The following assertion will not be tracked. s.add(x > 0); // The following assertion will be tracked using Boolean variable p1. // The C++ wrapper will automatically create the Boolean variable. s.add(y > 0, "p1"); // Asserting other tracked assertions. s.add(x < 10, "p2"); s.add(y < 0, "p3"); std::cout << s.check() << "\n"; std::cout << s.unsat_core() << "\n"; } void tactic_example1() { /* Z3 implements a methodology for orchestrating reasoning engines where "big" symbolic reasoning steps are represented as functions known as tactics, and tactics are composed using combinators known as tacticals. Tactics process sets of formulas called Goals. When a tactic is applied to some goal G, four different outcomes are possible. The tactic succeeds in showing G to be satisfiable (i.e., feasible); succeeds in showing G to be unsatisfiable (i.e., infeasible); produces a sequence of subgoals; or fails. When reducing a goal G to a sequence of subgoals G1, ..., Gn, we face the problem of model conversion. A model converter construct a model for G using a model for some subgoal Gi. In this example, we create a goal g consisting of three formulas, and a tactic t composed of two built-in tactics: simplify and solve-eqs. The tactic simplify apply transformations equivalent to the ones found in the command simplify. The tactic solver-eqs eliminate variables using Gaussian elimination. Actually, solve-eqs is not restricted only to linear arithmetic. It can also eliminate arbitrary variables. Then, sequential composition combinator & applies simplify to the input goal and solve-eqs to each subgoal produced by simplify. In this example, only one subgoal is produced. */ std::cout << "tactic example 1\n"; context c; expr x = c.real_const("x"); expr y = c.real_const("y"); goal g(c); g.add(x > 0); g.add(y > 0); g.add(x == y + 2); std::cout << g << "\n"; tactic t1(c, "simplify"); tactic t2(c, "solve-eqs"); tactic t = t1 & t2; apply_result r = t(g); std::cout << r << "\n"; } void tactic_example2() { /* In Z3, we say a clause is any constraint of the form (f_1 || ... || f_n). The tactic split-clause will select a clause in the input goal, and split it n subgoals. One for each subformula f_i. */ std::cout << "tactic example 2\n"; context c; expr x = c.real_const("x"); expr y = c.real_const("y"); goal g(c); g.add(x < 0 || x > 0); g.add(x == y + 1); g.add(y < 0); tactic t(c, "split-clause"); apply_result r = t(g); for (unsigned i = 0; i < r.size(); i++) { std::cout << "subgoal " << i << "\n" << r[i] << "\n"; } } void tactic_example3() { /* - The choice combinator t | s first applies t to the given goal, if it fails then returns the result of s applied to the given goal. - repeat(t) Keep applying the given tactic until no subgoal is modified by it. - repeat(t, n) Keep applying the given tactic until no subgoal is modified by it, or the number of iterations is greater than n. - try_for(t, ms) Apply tactic t to the input goal, if it does not return in ms millisenconds, it fails. - with(t, params) Apply the given tactic using the given parameters. */ std::cout << "tactic example 3\n"; context c; expr x = c.real_const("x"); expr y = c.real_const("y"); expr z = c.real_const("z"); goal g(c); g.add(x == 0 || x == 1); g.add(y == 0 || y == 1); g.add(z == 0 || z == 1); g.add(x + y + z > 2); // split all clauses tactic split_all = repeat(tactic(c, "split-clause") | tactic(c, "skip")); std::cout << split_all(g) << "\n"; tactic split_at_most_2 = repeat(tactic(c, "split-clause") | tactic(c, "skip"), 1); std::cout << split_at_most_2(g) << "\n"; // In the tactic split_solver, the tactic solve-eqs discharges all but one goal. // Note that, this tactic generates one goal: the empty goal which is trivially satisfiable (i.e., feasible) tactic split_solve = split_all & tactic(c, "solve-eqs"); std::cout << split_solve(g) << "\n"; } void tactic_example4() { /* A tactic can be converted into a solver object using the method mk_solver(). If the tactic produces the empty goal, then the associated solver returns sat. If the tactic produces a single goal containing False, then the solver returns unsat. Otherwise, it returns unknown. In this example, the tactic t implements a basic bit-vector solver using equation solving, bit-blasting, and a propositional SAT solver. We use the combinator `with` to configure our little solver. We also include the tactic `aig` which tries to compress Boolean formulas using And-Inverted Graphs. */ std::cout << "tactic example 4\n"; context c; params p(c); p.set("mul2concat", true); tactic t = with(tactic(c, "simplify"), p) & tactic(c, "solve-eqs") & tactic(c, "bit-blast") & tactic(c, "aig") & tactic(c, "sat"); solver s = t.mk_solver(); expr x = c.bv_const("x", 16); expr y = c.bv_const("y", 16); s.add(x*32 + y == 13); // In C++, the operator < has higher precedence than &. s.add((x & y) < 10); s.add(y > -100); std::cout << s.check() << "\n"; model m = s.get_model(); std::cout << m << "\n"; std::cout << "x*32 + y = " << m.eval(x*32 + y) << "\n"; std::cout << "x & y = " << m.eval(x & y) << "\n"; } void tactic_example5() { /* The tactic smt wraps the main solver in Z3 as a tactic. */ std::cout << "tactic example 5\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); solver s = tactic(c, "smt").mk_solver(); s.add(x > y + 1); std::cout << s.check() << "\n"; std::cout << s.get_model() << "\n"; } void tactic_example6() { /* In this example, we show how to implement a solver for integer arithmetic using SAT. The solver is complete only for problems where every variable has a lower and upper bound. */ std::cout << "tactic example 6\n"; context c; params p(c); p.set("arith_lhs", true); p.set("som", true); // sum-of-monomials normal form solver s = (with(tactic(c, "simplify"), p) & tactic(c, "normalize-bounds") & tactic(c, "lia2pb") & tactic(c, "pb2bv") & tactic(c, "bit-blast") & tactic(c, "sat")).mk_solver(); expr x = c.int_const("x"); expr y = c.int_const("y"); expr z = c.int_const("z"); s.add(x > 0 && x < 10); s.add(y > 0 && y < 10); s.add(z > 0 && z < 10); s.add(3*y + 2*x == z); std::cout << s.check() << "\n"; std::cout << s.get_model() << "\n"; s.reset(); s.add(3*y + 2*x == z); std::cout << s.check() << "\n"; } void tactic_example7() { /* Tactics can be combined with solvers. For example, we can apply a tactic to a goal, produced a set of subgoals, then select one of the subgoals and solve it using a solver. This example demonstrates how to do that, and how to use model converters to convert a model for a subgoal into a model for the original goal. */ std::cout << "tactic example 7\n"; context c; tactic t = tactic(c, "simplify") & tactic(c, "normalize-bounds") & tactic(c, "solve-eqs"); expr x = c.int_const("x"); expr y = c.int_const("y"); expr z = c.int_const("z"); goal g(c); g.add(x > 10); g.add(y == x + 3); g.add(z > y); apply_result r = t(g); // r contains only one subgoal std::cout << r << "\n"; solver s(c); goal subgoal = r[0]; for (unsigned i = 0; i < subgoal.size(); i++) { s.add(subgoal[i]); } std::cout << s.check() << "\n"; model m = s.get_model(); std::cout << "model for subgoal:\n" << m << "\n"; std::cout << "model for original goal:\n" << r.convert_model(m) << "\n"; } void tactic_example8() { /* Probes (aka formula measures) are evaluated over goals. Boolean expressions over them can be built using relational operators and Boolean connectives. The tactic fail_if(cond) fails if the given goal does not satisfy the condition cond. Many numeric and Boolean measures are available in Z3. In this example, we build a simple tactic using fail_if. It also shows that a probe can be applied directly to a goal. */ std::cout << "tactic example 8\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); expr z = c.int_const("z"); goal g(c); g.add(x + y + z > 0); probe p(c, "num-consts"); std::cout << "num-consts: " << p(g) << "\n"; tactic t = fail_if(p > 2); try { t(g); } catch (exception) { std::cout << "tactic failed...\n"; } std::cout << "trying again...\n"; g.reset(); g.add(x + y > 0); std::cout << t(g) << "\n"; } void tactic_example9() { /* The combinator (tactical) cond(p, t1, t2) is a shorthand for: (fail_if(p) & t1) | t2 The combinator when(p, t) is a shorthand for: cond(p, t, tactic(c, "skip")) The tactic skip just returns the input goal. This example demonstrates how to use the cond combinator. */ std::cout << "tactic example 9\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); expr z = c.int_const("z"); goal g(c); g.add(x*x - y*y >= 0); probe p(c, "num-consts"); tactic t = cond(p > 2, tactic(c, "simplify"), tactic(c, "factor")); std::cout << t(g) << "\n"; g.reset(); g.add(x + x + y + z >= 0); g.add(x*x - y*y >= 0); std::cout << t(g) << "\n"; } void tactic_qe() { std::cout << "tactic example using quantifier elimination\n"; context c; // Create a solver using "qe" and "smt" tactics solver s = (tactic(c, "qe") & tactic(c, "smt")).mk_solver(); expr a = c.int_const("a"); expr b = c.int_const("b"); expr x = c.int_const("x"); expr f = implies(x <= a, x < b); expr qf = forall(x, f); std::cout << qf << "\n"; s.add(qf); std::cout << s.check() << "\n"; std::cout << s.get_model() << "\n"; } void visit(expr const & e) { if (e.is_app()) { unsigned num = e.num_args(); for (unsigned i = 0; i < num; i++) { visit(e.arg(i)); } // do something // Example: print the visited expression func_decl f = e.decl(); std::cout << "application of " << f.name() << ": " << e << "\n"; } else if (e.is_quantifier()) { visit(e.body()); // do something } else { assert(e.is_var()); // do something } } void tst_visit() { std::cout << "visit example\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); expr z = c.int_const("z"); expr f = x*x - y*y >= 0; visit(f); } void incremental_example1() { std::cout << "incremental example1\n"; context c; expr x = c.int_const("x"); solver s(c); s.add(x > 0); std::cout << s.check() << "\n"; // We can add more formulas to the solver s.add(x < 0); // and, invoke s.check() again... std::cout << s.check() << "\n"; } void incremental_example2() { // In this example, we show how push() and pop() can be used // to remove formulas added to the solver. std::cout << "incremental example2\n"; context c; expr x = c.int_const("x"); solver s(c); s.add(x > 0); std::cout << s.check() << "\n"; // push() creates a backtracking point (aka a snapshot). s.push(); // We can add more formulas to the solver s.add(x < 0); // and, invoke s.check() again... std::cout << s.check() << "\n"; // pop() will remove all formulas added between this pop() and the matching push() s.pop(); // The context is satisfiable again std::cout << s.check() << "\n"; // and contains only x > 0 std::cout << s << "\n"; } void incremental_example3() { // In this example, we show how to use assumptions to "remove" // formulas added to a solver. Actually, we disable them. std::cout << "incremental example3\n"; context c; expr x = c.int_const("x"); solver s(c); s.add(x > 0); std::cout << s.check() << "\n"; // Now, suppose we want to add x < 0 to the solver, but we also want // to be able to disable it later. // To do that, we create an auxiliary Boolean variable expr b = c.bool_const("b"); // and, assert (b implies x < 0) s.add(implies(b, x < 0)); // Now, we check whether s is satisfiable under the assumption "b" is true. expr_vector a1(c); a1.push_back(b); std::cout << s.check(a1) << "\n"; // To "disable" (x > 0), we may just ask with the assumption "not b" or not provide any assumption. std::cout << s.check() << "\n"; expr_vector a2(c); a2.push_back(!b); std::cout << s.check(a2) << "\n"; } void enum_sort_example() { std::cout << "enumeration sort example\n"; context ctx; const char * enum_names[] = { "a", "b", "c" }; func_decl_vector enum_consts(ctx); func_decl_vector enum_testers(ctx); sort s = ctx.enumeration_sort("enumT", 3, enum_names, enum_consts, enum_testers); // enum_consts[0] is a func_decl of arity 0. // we convert it to an expression using the operator() expr a = enum_consts[0](); expr b = enum_consts[1](); expr x = ctx.constant("x", s); expr test = (x==a) && (x==b); std::cout << "1: " << test << std::endl; tactic qe(ctx, "ctx-solver-simplify"); goal g(ctx); g.add(test); expr res(ctx); apply_result result_of_elimination = qe.apply(g); goal result_goal = result_of_elimination[0]; std::cout << "2: " << result_goal.as_expr() << std::endl; } void expr_vector_example() { std::cout << "expr_vector example\n"; context c; const unsigned N = 10; expr_vector x(c); for (unsigned i = 0; i < N; i++) { std::stringstream x_name; x_name << "x_" << i; x.push_back(c.int_const(x_name.str().c_str())); } solver s(c); for (unsigned i = 0; i < N; i++) { s.add(x[i] >= 1); } std::cout << s << "\n" << "solving...\n" << s.check() << "\n"; model m = s.get_model(); std::cout << "solution\n" << m; } void exists_expr_vector_example() { std::cout << "exists expr_vector example\n"; context c; const unsigned N = 10; expr_vector xs(c); expr x(c); expr b(c); b = c.bool_val(true); for (unsigned i = 0; i < N; i++) { std::stringstream x_name; x_name << "x_" << i; x = c.int_const(x_name.str().c_str()); xs.push_back(x); b = b && x >= 0; } expr ex(c); ex = exists(xs, b); std::cout << ex << std::endl; } void substitute_example() { std::cout << "substitute example\n"; context c; expr x(c); x = c.int_const("x"); expr f(c); f = (x == 2) || (x == 1); std::cout << f << std::endl; expr two(c), three(c); two = c.int_val(2); three = c.int_val(3); Z3_ast from[] = { two }; Z3_ast to[] = { three }; expr new_f(c); new_f = to_expr(c, Z3_substitute(c, f, 1, from, to)); std::cout << new_f << std::endl; } void opt_example() { context c; optimize opt(c); params p(c); p.set("priority",c.str_symbol("pareto")); opt.set(p); expr x = c.int_const("x"); expr y = c.int_const("y"); opt.add(10 >= x && x >= 0); opt.add(10 >= y && y >= 0); opt.add(x + y <= 11); optimize::handle h1 = opt.maximize(x); optimize::handle h2 = opt.maximize(y); check_result r = sat; while (true) { if (sat == opt.check()) { std::cout << x << ": " << opt.lower(h1) << " " << y << ": " << opt.lower(h2) << "\n"; } else { break; } } } void extract_example() { std::cout << "extract example\n"; context c; expr x(c); x = c.constant("x", c.bv_sort(32)); expr y = x.extract(21, 10); std::cout << y << " " << y.hi() << " " << y.lo() << "\n"; } int main() { try { demorgan(); std::cout << "\n"; find_model_example1(); std::cout << "\n"; prove_example1(); std::cout << "\n"; prove_example2(); std::cout << "\n"; nonlinear_example1(); std::cout << "\n"; bitvector_example1(); std::cout << "\n"; bitvector_example2(); std::cout << "\n"; capi_example(); std::cout << "\n"; eval_example1(); std::cout << "\n"; two_contexts_example1(); std::cout << "\n"; 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"; unsat_core_example3(); std::cout << "\n"; tactic_example1(); std::cout << "\n"; tactic_example2(); std::cout << "\n"; tactic_example3(); std::cout << "\n"; tactic_example4(); std::cout << "\n"; tactic_example5(); std::cout << "\n"; tactic_example6(); std::cout << "\n"; tactic_example7(); std::cout << "\n"; tactic_example8(); std::cout << "\n"; tactic_example9(); std::cout << "\n"; tactic_qe(); std::cout << "\n"; tst_visit(); std::cout << "\n"; incremental_example1(); std::cout << "\n"; incremental_example2(); std::cout << "\n"; incremental_example3(); std::cout << "\n"; enum_sort_example(); std::cout << "\n"; expr_vector_example(); std::cout << "\n"; exists_expr_vector_example(); std::cout << "\n"; substitute_example(); std::cout << "\n"; opt_example(); std::cout << "\n"; extract_example(); std::cout << "\n"; std::cout << "done\n"; } catch (exception & ex) { std::cout << "unexpected error: " << ex << "\n"; } return 0; } z3-4.4.0/examples/tptp/0000755000175000017500000000000012540347414014452 5ustar michaelmichaelz3-4.4.0/examples/tptp/tptp5.tab.c0000644000175000017500000050350112540347414016443 0ustar michaelmichael/* A Bison parser, made by GNU Bison 2.4.2. */ /* Skeleton implementation for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2006, 2009-2010 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "2.4.2" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 0 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Using locations. */ #define YYLSP_NEEDED 0 /* Copy the first part of user declarations. */ /* Line 189 of yacc.c */ #line 2 "tptp5.y" //----------------------------------------------------------------------------- #include #include #include //----------------------------------------------------------------------------- //----Compile with -DP_VERBOSE=1 for verbose output. #ifndef P_VERBOSE # define P_VERBOSE 0 #endif int verbose = P_VERBOSE; //----Compile with -DP_USERPROC=1 to #include p_user_proc.c. p_user_proc.c //----should #define P_ACT, P_BUILD, P_TOKEN, P_PRINT to different procedures //----from those below, and supply code. #ifdef P_USERPROC #else # define P_ACT(ss) if(verbose)printf("%7d %s\n",yylineno,ss); # define P_BUILD(sym,A,B,C,D,E,F,G,H,I,J) pBuildTree(sym,A,B,C,D,E,F,G,H,I,J) # define P_TOKEN(tok,symbolIndex) pToken(tok,symbolIndex) # define P_PRINT(ss) if(verbose){printf("\n\n");pPrintTree(ss,0);} #endif extern int yylineno; extern int yychar; extern char yytext[]; extern int tptp_store_size; extern char* tptp_lval[]; #define MAX_CHILDREN 12 typedef struct pTreeNode * pTree; struct pTreeNode { char* symbol; int symbolIndex; pTree children[MAX_CHILDREN+1]; }; //----------------------------------------------------------------------------- int yyerror( char const *s ) { fprintf( stderr, "%s in line %d at item \"%s\".\n", s, yylineno, yytext); return 0; } //----------------------------------------------------------------------------- pTree pBuildTree(char* symbol,pTree A,pTree B,pTree C,pTree D,pTree E,pTree F, pTree G, pTree H, pTree I, pTree J) { pTree ss = (pTree)calloc(1,sizeof(struct pTreeNode)); ss->symbol = symbol; ss->symbolIndex = -1; ss->children[0] = A; ss->children[1] = B; ss->children[2] = C; ss->children[3] = D; ss->children[4] = E; ss->children[5] = F; ss->children[6] = G; ss->children[7] = H; ss->children[8] = I; ss->children[9] = J; ss->children[10] = NULL; return ss; } //----------------------------------------------------------------------------- pTree pToken(char* token, int symbolIndex) { //char pTokenBuf[8240]; pTree ss; char* symbol = tptp_lval[symbolIndex]; char* safeSym = 0; //strncpy(pTokenBuf, token, 39); //strncat(pTokenBuf, symbol, 8193); //safeSym = strdup(pTokenBuf); ss = pBuildTree(safeSym,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); ss->symbolIndex = symbolIndex; return ss; } //----------------------------------------------------------------------------- void pPrintComments(int start, int depth) { int d, j; char c1[4] = "%", c2[4] = "/*"; j = start; while (tptp_lval[j] != NULL && (tptp_lval[j][0]==c1[0] || (tptp_lval[j][0]==c2[0] && tptp_lval[j][1]==c2[1]))) { for (d=0; d= 0) { pPrintComments(pPrintIdx, 0); pPrintIdx = -1; } if (ss == NULL) { return; } for (d = 0; d < depth-1; d++) { printf("| "); } printf("%1d ",depth % 10); if (ss->children[0] == NULL) { printf("%s\n", ss->symbol); } else { printf("<%s>\n", ss->symbol); } if (strcmp(ss->symbol, "PERIOD .") == 0) { pPrintIdx = (ss->symbolIndex+1) % tptp_store_size; } if (ss->symbolIndex >= 0) { pPrintComments((ss->symbolIndex+1) % tptp_store_size, depth); } i = 0; while(ss->children[i] != NULL) { pPrintTree(ss->children[i],depth+1); i++; } return; } //----------------------------------------------------------------------------- int yywrap(void) { P_PRINT(NULL); return 1; } //----------------------------------------------------------------------------- /* Line 189 of yacc.c */ #line 219 "tptp5.tab.c" /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 0 #endif /* Enabling the token table. */ #ifndef YYTOKEN_TABLE # define YYTOKEN_TABLE 0 #endif /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { AMPERSAND = 258, AT_SIGN = 259, AT_SIGN_MINUS = 260, AT_SIGN_PLUS = 261, CARET = 262, COLON = 263, COLON_EQUALS = 264, COMMA = 265, EQUALS = 266, EQUALS_GREATER = 267, EXCLAMATION = 268, EXCLAMATION_EQUALS = 269, EXCLAMATION_EXCLAMATION = 270, EXCLAMATION_GREATER = 271, LBRKT = 272, LESS_EQUALS = 273, LESS_EQUALS_GREATER = 274, LESS_TILDE_GREATER = 275, LPAREN = 276, MINUS = 277, MINUS_MINUS_GREATER = 278, PERIOD = 279, QUESTION = 280, QUESTION_QUESTION = 281, QUESTION_STAR = 282, RBRKT = 283, RPAREN = 284, STAR = 285, TILDE = 286, TILDE_AMPERSAND = 287, TILDE_VLINE = 288, VLINE = 289, _DLR_cnf = 290, _DLR_fof = 291, _DLR_fot = 292, _DLR_itef = 293, _DLR_itetf = 294, _DLR_itett = 295, _DLR_tff = 296, _DLR_thf = 297, _LIT_cnf = 298, _LIT_fof = 299, _LIT_include = 300, _LIT_tff = 301, _LIT_thf = 302, arrow = 303, comment = 304, comment_line = 305, decimal = 306, decimal_exponent = 307, decimal_fraction = 308, distinct_object = 309, dollar_dollar_word = 310, dollar_word = 311, dot_decimal = 312, integer = 313, less_sign = 314, lower_word = 315, plus = 316, positive_decimal = 317, rational = 318, real = 319, signed_integer = 320, signed_rational = 321, signed_real = 322, single_quoted = 323, star = 324, unrecognized = 325, unsigned_integer = 326, unsigned_rational = 327, unsigned_real = 328, upper_word = 329, vline = 330 }; #endif #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE { /* Line 214 of yacc.c */ #line 148 "tptp5.y" int ival; double dval; char* sval; TreeNode* pval; /* Line 214 of yacc.c */ #line 334 "tptp5.tab.c" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 #endif /* Copy the second part of user declarations. */ /* Line 264 of yacc.c */ #line 346 "tptp5.tab.c" #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #elif (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) typedef signed char yytype_int8; #else typedef short int yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(msgid) dgettext ("bison-runtime", msgid) # endif # endif # ifndef YY_ # define YY_(msgid) msgid # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(e) ((void) (e)) #else # define YYUSE(e) /* empty */ #endif /* Identity function, used to suppress warnings about constant conditions. */ #ifndef lint # define YYID(n) (n) #else #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int YYID (int yyi) #else static int YYID (yyi) int yyi; #endif { return yyi; } #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's `empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined _STDLIB_H \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss_alloc; YYSTYPE yyvs_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(To, From, Count) \ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) # else # define YYCOPY(To, From, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ while (YYID (0)) # endif # endif /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (YYID (0)) #endif /* YYFINAL -- State number of the termination state. */ #define YYFINAL 3 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 1612 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 76 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 141 /* YYNRULES -- Number of rules. */ #define YYNRULES 281 /* YYNRULES -- Number of states. */ #define YYNSTATES 523 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 330 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75 }; #if YYDEBUG /* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in YYRHS. */ static const yytype_uint16 yyprhs[] = { 0, 0, 3, 5, 8, 10, 12, 14, 16, 18, 20, 31, 42, 53, 64, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 94, 96, 98, 100, 104, 108, 112, 116, 120, 124, 126, 128, 130, 132, 134, 136, 140, 147, 149, 153, 155, 157, 161, 166, 170, 172, 174, 178, 182, 184, 186, 188, 190, 192, 196, 200, 204, 208, 212, 216, 218, 220, 223, 227, 229, 233, 240, 242, 246, 250, 254, 263, 267, 271, 273, 275, 277, 279, 281, 283, 285, 289, 291, 293, 297, 301, 305, 309, 311, 313, 315, 317, 319, 321, 325, 332, 334, 338, 340, 342, 346, 349, 351, 355, 359, 361, 363, 365, 367, 369, 373, 375, 377, 381, 385, 389, 393, 397, 404, 406, 410, 414, 419, 423, 432, 436, 440, 442, 444, 446, 448, 450, 452, 456, 458, 460, 464, 468, 472, 476, 478, 480, 482, 484, 486, 488, 492, 499, 501, 505, 508, 510, 517, 519, 523, 527, 532, 536, 545, 549, 553, 557, 559, 561, 565, 567, 570, 572, 574, 576, 578, 582, 584, 586, 588, 590, 592, 594, 596, 598, 600, 602, 604, 606, 609, 611, 613, 615, 617, 619, 621, 623, 625, 627, 629, 631, 633, 635, 637, 639, 641, 643, 645, 647, 649, 653, 655, 657, 659, 661, 663, 665, 667, 669, 671, 673, 675, 680, 682, 684, 686, 688, 690, 692, 694, 696, 701, 703, 705, 707, 712, 714, 716, 718, 720, 724, 733, 742, 744, 747, 749, 751, 758, 763, 765, 767, 771, 773, 777, 779, 781, 786, 788, 790, 792, 794, 799, 804, 809, 814, 819, 822, 826, 828, 832, 834, 836, 838, 840, 842, 844, 846, 848, 850, 852 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int16 yyrhs[] = { 77, 0, -1, 216, -1, 77, 78, -1, 79, -1, 202, -1, 80, -1, 81, -1, 82, -1, 83, -1, 47, 21, 210, 10, 85, 10, 86, 84, 29, 24, -1, 46, 21, 210, 10, 85, 10, 117, 84, 29, 24, -1, 44, 21, 210, 10, 85, 10, 142, 84, 29, 24, -1, 43, 21, 210, 10, 85, 10, 158, 84, 29, 24, -1, 10, 199, 200, -1, 216, -1, 60, -1, 87, -1, 116, -1, 88, -1, 94, -1, 100, -1, 102, -1, 89, -1, 90, -1, 105, -1, 94, 164, 94, -1, 91, -1, 92, -1, 93, -1, 94, 34, 94, -1, 91, 34, 94, -1, 94, 3, 94, -1, 92, 3, 94, -1, 94, 4, 94, -1, 93, 4, 94, -1, 95, -1, 99, -1, 109, -1, 110, -1, 112, -1, 115, -1, 21, 87, 29, -1, 163, 17, 96, 28, 8, 94, -1, 97, -1, 97, 10, 96, -1, 98, -1, 196, -1, 196, 8, 103, -1, 165, 21, 87, 29, -1, 101, 8, 103, -1, 109, -1, 110, -1, 21, 87, 29, -1, 185, 166, 185, -1, 87, -1, 94, -1, 106, -1, 107, -1, 108, -1, 104, 48, 104, -1, 104, 48, 106, -1, 104, 30, 104, -1, 107, 30, 104, -1, 104, 61, 104, -1, 108, 61, 104, -1, 182, -1, 161, -1, 17, 28, -1, 17, 111, 28, -1, 94, -1, 94, 10, 111, -1, 9, 17, 113, 28, 8, 94, -1, 114, -1, 114, 10, 113, -1, 97, 9, 87, -1, 21, 114, 29, -1, 38, 21, 87, 10, 87, 10, 87, 29, -1, 110, 171, 110, -1, 21, 116, 29, -1, 118, -1, 130, -1, 141, -1, 119, -1, 124, -1, 120, -1, 121, -1, 124, 168, 124, -1, 122, -1, 123, -1, 124, 34, 124, -1, 122, 34, 124, -1, 124, 3, 124, -1, 123, 3, 124, -1, 125, -1, 129, -1, 173, -1, 137, -1, 196, -1, 140, -1, 21, 118, 29, -1, 167, 17, 126, 28, 8, 124, -1, 127, -1, 127, 10, 126, -1, 128, -1, 196, -1, 196, 8, 134, -1, 170, 124, -1, 162, -1, 131, 8, 132, -1, 21, 130, 29, -1, 186, -1, 195, -1, 134, -1, 135, -1, 134, -1, 21, 136, 29, -1, 211, -1, 172, -1, 133, 48, 134, -1, 21, 135, 29, -1, 134, 30, 134, -1, 136, 30, 134, -1, 21, 136, 29, -1, 9, 17, 138, 28, 8, 124, -1, 139, -1, 139, 10, 138, -1, 196, 9, 118, -1, 196, 8, 22, 182, -1, 21, 139, 29, -1, 38, 21, 118, 10, 118, 10, 118, 29, -1, 118, 171, 118, -1, 21, 141, 29, -1, 143, -1, 157, -1, 144, -1, 149, -1, 145, -1, 146, -1, 149, 168, 149, -1, 147, -1, 148, -1, 149, 34, 149, -1, 147, 34, 149, -1, 149, 3, 149, -1, 148, 3, 149, -1, 150, -1, 152, -1, 173, -1, 153, -1, 196, -1, 156, -1, 21, 143, 29, -1, 167, 17, 151, 28, 8, 149, -1, 196, -1, 196, 10, 151, -1, 170, 149, -1, 162, -1, 9, 17, 154, 28, 8, 149, -1, 155, -1, 155, 10, 154, -1, 196, 9, 143, -1, 196, 8, 22, 182, -1, 21, 155, 29, -1, 38, 21, 143, 10, 143, 10, 143, 29, -1, 143, 171, 143, -1, 21, 157, 29, -1, 21, 159, 29, -1, 159, -1, 160, -1, 159, 34, 160, -1, 173, -1, 31, 173, -1, 162, -1, 164, -1, 169, -1, 165, -1, 182, 180, 182, -1, 167, -1, 7, -1, 16, -1, 27, -1, 6, -1, 5, -1, 179, -1, 180, -1, 168, -1, 170, -1, 15, -1, 26, -1, 59, 59, -1, 13, -1, 25, -1, 19, -1, 12, -1, 18, -1, 20, -1, 33, -1, 32, -1, 34, -1, 3, -1, 31, -1, 23, -1, 212, -1, 174, -1, 175, -1, 181, -1, 184, -1, 176, -1, 177, -1, 190, -1, 182, 178, 182, -1, 179, -1, 11, -1, 14, -1, 193, -1, 183, -1, 196, -1, 198, -1, 184, -1, 187, -1, 193, -1, 185, -1, 186, 21, 197, 29, -1, 186, -1, 211, -1, 188, -1, 189, -1, 214, -1, 54, -1, 190, -1, 191, -1, 192, 21, 197, 29, -1, 192, -1, 212, -1, 194, -1, 195, 21, 197, 29, -1, 195, -1, 213, -1, 74, -1, 182, -1, 182, 10, 197, -1, 40, 21, 118, 10, 182, 10, 182, 29, -1, 39, 21, 143, 10, 182, 10, 182, 29, -1, 205, -1, 10, 201, -1, 216, -1, 208, -1, 45, 21, 215, 203, 29, 24, -1, 10, 17, 204, 28, -1, 216, -1, 210, -1, 210, 10, 204, -1, 206, -1, 206, 8, 205, -1, 208, -1, 211, -1, 211, 21, 209, 29, -1, 196, -1, 214, -1, 54, -1, 207, -1, 42, 21, 86, 29, -1, 41, 21, 117, 29, -1, 36, 21, 142, 29, -1, 35, 21, 158, 29, -1, 37, 21, 182, 29, -1, 17, 28, -1, 17, 209, 28, -1, 205, -1, 205, 10, 209, -1, 211, -1, 58, -1, 60, -1, 68, -1, 56, -1, 55, -1, 58, -1, 63, -1, 64, -1, 68, -1, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { 0, 225, 225, 226, 229, 230, 233, 234, 235, 236, 239, 242, 245, 248, 251, 252, 255, 258, 259, 262, 263, 264, 265, 268, 269, 270, 273, 276, 277, 278, 281, 282, 285, 286, 289, 290, 293, 294, 295, 296, 297, 298, 299, 302, 305, 306, 309, 310, 313, 316, 319, 322, 323, 324, 327, 330, 333, 336, 337, 338, 341, 342, 345, 346, 349, 350, 353, 354, 357, 358, 361, 362, 365, 368, 369, 372, 373, 376, 379, 380, 383, 384, 385, 388, 389, 392, 393, 396, 399, 400, 403, 404, 407, 408, 411, 412, 413, 414, 415, 416, 417, 420, 423, 424, 427, 428, 431, 434, 435, 438, 439, 442, 443, 446, 447, 450, 451, 454, 455, 458, 459, 462, 463, 464, 467, 470, 471, 474, 475, 476, 479, 482, 483, 486, 487, 490, 491, 494, 495, 498, 501, 502, 505, 506, 509, 510, 513, 514, 515, 516, 517, 518, 519, 522, 525, 526, 529, 530, 533, 536, 537, 540, 541, 542, 545, 548, 549, 552, 553, 556, 557, 560, 561, 562, 565, 566, 567, 570, 573, 574, 575, 576, 577, 578, 581, 582, 583, 586, 587, 588, 591, 594, 595, 598, 599, 600, 601, 602, 603, 606, 607, 610, 613, 616, 619, 620, 621, 624, 627, 628, 631, 634, 637, 640, 643, 646, 649, 650, 651, 654, 655, 656, 659, 660, 663, 666, 669, 670, 673, 674, 677, 680, 681, 684, 687, 690, 691, 694, 697, 700, 703, 704, 707, 708, 711, 714, 715, 718, 721, 724, 725, 728, 729, 732, 733, 734, 737, 738, 739, 740, 741, 742, 745, 746, 747, 748, 749, 752, 753, 756, 757, 760, 761, 764, 765, 768, 771, 774, 775, 776, 779, 782 }; #endif #if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "AMPERSAND", "AT_SIGN", "AT_SIGN_MINUS", "AT_SIGN_PLUS", "CARET", "COLON", "COLON_EQUALS", "COMMA", "EQUALS", "EQUALS_GREATER", "EXCLAMATION", "EXCLAMATION_EQUALS", "EXCLAMATION_EXCLAMATION", "EXCLAMATION_GREATER", "LBRKT", "LESS_EQUALS", "LESS_EQUALS_GREATER", "LESS_TILDE_GREATER", "LPAREN", "MINUS", "MINUS_MINUS_GREATER", "PERIOD", "QUESTION", "QUESTION_QUESTION", "QUESTION_STAR", "RBRKT", "RPAREN", "STAR", "TILDE", "TILDE_AMPERSAND", "TILDE_VLINE", "VLINE", "_DLR_cnf", "_DLR_fof", "_DLR_fot", "_DLR_itef", "_DLR_itetf", "_DLR_itett", "_DLR_tff", "_DLR_thf", "_LIT_cnf", "_LIT_fof", "_LIT_include", "_LIT_tff", "_LIT_thf", "arrow", "comment", "comment_line", "decimal", "decimal_exponent", "decimal_fraction", "distinct_object", "dollar_dollar_word", "dollar_word", "dot_decimal", "integer", "less_sign", "lower_word", "plus", "positive_decimal", "rational", "real", "signed_integer", "signed_rational", "signed_real", "single_quoted", "star", "unrecognized", "unsigned_integer", "unsigned_rational", "unsigned_real", "upper_word", "vline", "$accept", "TPTP_file", "TPTP_input", "annotated_formula", "thf_annotated", "tff_annotated", "fof_annotated", "cnf_annotated", "annotations", "formula_role", "thf_formula", "thf_logic_formula", "thf_binary_formula", "thf_binary_pair", "thf_binary_tuple", "thf_or_formula", "thf_and_formula", "thf_apply_formula", "thf_unitary_formula", "thf_quantified_formula", "thf_variable_list", "thf_variable", "thf_typed_variable", "thf_unary_formula", "thf_type_formula", "thf_typeable_formula", "thf_subtype", "thf_top_level_type", "thf_unitary_type", "thf_binary_type", "thf_mapping_type", "thf_xprod_type", "thf_union_type", "thf_atom", "thf_tuple", "thf_tuple_list", "thf_let", "thf_let_list", "thf_defined_var", "thf_conditional", "thf_sequent", "tff_formula", "tff_logic_formula", "tff_binary_formula", "tff_binary_nonassoc", "tff_binary_assoc", "tff_or_formula", "tff_and_formula", "tff_unitary_formula", "tff_quantified_formula", "tff_variable_list", "tff_variable", "tff_typed_variable", "tff_unary_formula", "tff_typed_atom", "tff_untyped_atom", "tff_top_level_type", "tff_unitary_type", "tff_atomic_type", "tff_mapping_type", "tff_xprod_type", "tff_let", "tff_let_list", "tff_defined_var", "tff_conditional", "tff_sequent", "fof_formula", "fof_logic_formula", "fof_binary_formula", "fof_binary_nonassoc", "fof_binary_assoc", "fof_or_formula", "fof_and_formula", "fof_unitary_formula", "fof_quantified_formula", "fof_variable_list", "fof_unary_formula", "fof_let", "fof_let_list", "fof_defined_var", "fof_conditional", "fof_sequent", "cnf_formula", "disjunction", "literal", "thf_conn_term", "fol_infix_unary", "thf_quantifier", "thf_pair_connective", "thf_unary_connective", "subtype_sign", "fol_quantifier", "binary_connective", "assoc_connective", "unary_connective", "gentzen_arrow", "defined_type", "atomic_formula", "plain_atomic_formula", "defined_atomic_formula", "defined_plain_formula", "defined_infix_formula", "defined_infix_pred", "infix_equality", "infix_inequality", "system_atomic_formula", "term", "function_term", "plain_term", "constant", "functor", "defined_term", "defined_atom", "defined_atomic_term", "defined_plain_term", "defined_constant", "defined_functor", "system_term", "system_constant", "system_functor", "variable", "arguments", "conditional_term", "source", "optional_info", "useful_info", "include", "formula_selection", "name_list", "general_term", "general_data", "formula_data", "general_list", "general_terms", "name", "atomic_word", "atomic_defined_word", "atomic_system_word", "number", "file_name", "null", 0 }; #endif # ifdef YYPRINT /* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to token YYLEX-NUM. */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 76, 77, 77, 78, 78, 79, 79, 79, 79, 80, 81, 82, 83, 84, 84, 85, 86, 86, 87, 87, 87, 87, 88, 88, 88, 89, 90, 90, 90, 91, 91, 92, 92, 93, 93, 94, 94, 94, 94, 94, 94, 94, 95, 96, 96, 97, 97, 98, 99, 100, 101, 101, 101, 102, 103, 104, 105, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 113, 113, 114, 114, 115, 116, 116, 117, 117, 117, 118, 118, 119, 119, 120, 121, 121, 122, 122, 123, 123, 124, 124, 124, 124, 124, 124, 124, 125, 126, 126, 127, 127, 128, 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, 134, 134, 135, 135, 136, 136, 136, 137, 138, 138, 139, 139, 139, 140, 141, 141, 142, 142, 143, 143, 144, 144, 145, 146, 146, 147, 147, 148, 148, 149, 149, 149, 149, 149, 149, 149, 150, 151, 151, 152, 152, 153, 154, 154, 155, 155, 155, 156, 157, 157, 158, 158, 159, 159, 160, 160, 160, 161, 161, 161, 162, 163, 163, 163, 163, 163, 163, 164, 164, 164, 165, 165, 165, 166, 167, 167, 168, 168, 168, 168, 168, 168, 169, 169, 170, 171, 172, 173, 173, 173, 174, 175, 175, 176, 177, 178, 179, 180, 181, 182, 182, 182, 183, 183, 183, 184, 184, 185, 186, 187, 187, 188, 188, 189, 190, 190, 191, 192, 193, 193, 194, 195, 196, 197, 197, 198, 198, 199, 200, 200, 201, 202, 203, 203, 204, 204, 205, 205, 205, 206, 206, 206, 206, 206, 206, 207, 207, 207, 207, 207, 208, 208, 209, 209, 210, 210, 211, 211, 212, 213, 214, 214, 214, 215, 216 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 1, 2, 1, 1, 1, 1, 1, 1, 10, 10, 10, 10, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 3, 6, 1, 3, 1, 1, 3, 4, 3, 1, 1, 3, 3, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 1, 1, 2, 3, 1, 3, 6, 1, 3, 3, 3, 8, 3, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 3, 6, 1, 3, 1, 1, 3, 2, 1, 3, 3, 1, 1, 1, 1, 1, 3, 1, 1, 3, 3, 3, 3, 3, 6, 1, 3, 3, 4, 3, 8, 3, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 3, 6, 1, 3, 2, 1, 6, 1, 3, 3, 4, 3, 8, 3, 3, 3, 1, 1, 3, 1, 2, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 4, 1, 1, 1, 1, 3, 8, 8, 1, 2, 1, 1, 6, 4, 1, 1, 3, 1, 3, 1, 1, 4, 1, 1, 1, 1, 4, 4, 4, 4, 4, 2, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state STATE-NUM when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint16 yydefact[] = { 281, 0, 2, 1, 0, 0, 0, 0, 0, 3, 4, 6, 7, 8, 9, 5, 0, 0, 0, 0, 0, 272, 273, 274, 0, 271, 0, 280, 281, 0, 0, 0, 0, 0, 0, 250, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 248, 0, 0, 0, 0, 0, 0, 229, 276, 275, 277, 278, 279, 239, 281, 168, 169, 173, 171, 204, 205, 208, 209, 206, 0, 216, 207, 222, 224, 220, 226, 227, 210, 231, 233, 215, 235, 237, 217, 218, 225, 234, 238, 228, 0, 191, 0, 192, 201, 0, 281, 133, 135, 137, 138, 140, 141, 136, 146, 147, 149, 151, 134, 157, 0, 0, 148, 150, 249, 0, 0, 0, 0, 281, 80, 83, 85, 86, 88, 89, 84, 94, 95, 81, 0, 97, 99, 82, 108, 0, 0, 96, 224, 237, 98, 200, 183, 182, 179, 0, 213, 194, 214, 188, 180, 0, 195, 193, 196, 0, 189, 181, 198, 197, 199, 0, 281, 17, 19, 23, 24, 27, 28, 29, 20, 36, 37, 21, 0, 22, 0, 25, 57, 58, 59, 38, 39, 40, 41, 18, 67, 0, 174, 176, 178, 186, 175, 187, 184, 185, 66, 219, 222, 230, 221, 0, 172, 0, 0, 0, 0, 0, 15, 0, 0, 212, 0, 0, 0, 0, 0, 0, 0, 0, 0, 202, 0, 0, 0, 0, 0, 0, 0, 0, 156, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 68, 70, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 167, 0, 0, 0, 0, 0, 0, 0, 0, 260, 258, 281, 244, 253, 261, 255, 256, 259, 0, 170, 211, 177, 240, 0, 0, 0, 0, 0, 159, 0, 152, 166, 0, 0, 165, 143, 145, 144, 142, 139, 0, 154, 0, 0, 0, 125, 0, 100, 110, 132, 0, 0, 131, 91, 93, 92, 90, 87, 0, 109, 0, 113, 114, 118, 117, 203, 0, 102, 104, 105, 0, 0, 0, 46, 0, 73, 47, 0, 0, 39, 0, 69, 42, 79, 0, 0, 31, 33, 35, 32, 34, 30, 26, 55, 50, 56, 62, 60, 61, 64, 63, 65, 78, 0, 44, 0, 190, 54, 224, 0, 0, 267, 269, 0, 0, 0, 0, 0, 0, 0, 14, 246, 0, 0, 13, 0, 223, 232, 236, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 71, 0, 10, 0, 0, 49, 0, 0, 0, 268, 0, 0, 0, 0, 0, 245, 247, 254, 0, 241, 163, 0, 160, 0, 161, 0, 0, 155, 129, 0, 126, 0, 127, 0, 0, 0, 120, 116, 0, 119, 0, 103, 106, 76, 75, 0, 74, 48, 0, 0, 45, 0, 0, 270, 265, 264, 266, 263, 262, 257, 158, 162, 0, 153, 124, 128, 0, 123, 121, 122, 101, 72, 0, 43, 0, 0, 0, 0, 0, 243, 242, 164, 130, 77 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { -1, 1, 9, 10, 11, 12, 13, 14, 210, 39, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 383, 352, 353, 175, 176, 177, 178, 374, 179, 180, 181, 182, 183, 255, 256, 257, 186, 354, 355, 187, 188, 122, 123, 124, 125, 126, 127, 128, 129, 130, 346, 347, 348, 131, 132, 133, 339, 340, 426, 427, 428, 134, 324, 325, 135, 136, 99, 100, 101, 102, 103, 104, 105, 106, 107, 320, 108, 109, 307, 308, 110, 111, 63, 64, 65, 189, 112, 190, 191, 192, 279, 193, 194, 195, 196, 225, 343, 115, 68, 69, 70, 71, 213, 197, 198, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 303, 88, 291, 400, 454, 15, 34, 47, 392, 293, 294, 295, 393, 48, 89, 90, 91, 92, 28, 211 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ #define YYPACT_NINF -388 static const yytype_int16 yypact[] = { -388, 110, -388, -388, 12, 14, 26, 41, 46, -388, -388, -388, -388, -388, -388, -388, -12, -12, 10, -12, -12, -388, -388, -388, 72, -388, 74, -388, 84, 89, 91, 52, 52, 104, 101, -388, 52, 52, -388, 132, 141, -12, 145, 155, 164, 750, 32, 148, 174, -388, 790, 1403, 594, 471, 177, 179, -388, -388, -388, -388, -388, -388, -388, 201, 178, -388, -388, -388, -388, -388, -388, -388, -388, 50, -388, 111, -388, 192, -388, -388, -388, 127, -388, 193, 152, -388, 195, -388, -388, -388, -388, -388, -388, 216, -388, 32, -388, -388, 214, 201, 217, -388, -388, -388, 205, 238, 298, -388, -388, -388, -388, -388, -388, 227, 1158, -388, 153, -388, -12, 228, 790, 226, 201, 217, -388, -388, -388, 231, 259, 316, -388, -388, -388, 258, -388, -388, -388, -388, 251, 1241, -388, 19, 22, 153, -388, -388, -388, -388, 254, -388, -388, -388, -388, -388, 1339, -388, -388, -388, 1403, -388, -388, -388, -388, -388, 252, 201, -388, -388, -388, -388, 240, 269, 272, 566, -388, -388, -388, 270, -388, -6, -388, -388, 250, 220, 275, 21, -388, -388, -388, -388, 267, -388, 268, -388, -388, -388, -388, -388, -388, -388, -388, 234, -388, -388, 2, -388, 279, 1158, 1241, 1058, 266, -388, 594, 471, -388, 471, 471, 471, 471, -1, 3, 271, 1158, 274, -388, 1158, 1158, 1158, 1158, 1158, 1158, 222, 1158, -388, -388, 0, 36, 276, 282, 1241, 284, 1241, 1241, 1241, 1241, 1241, 1241, 29, 222, 1241, -388, 1, 1467, -388, 287, -388, -388, 295, 285, 296, 1467, 297, 1531, 1531, 1531, 1531, 1531, 1531, 1531, 1467, 1531, 1531, 1531, 1531, 1531, 281, 222, 1467, 245, 23, -388, 289, 314, 1538, 306, 308, 312, 317, 318, -388, -388, 330, -388, 334, -388, -388, 326, -388, 331, -388, -388, -388, 346, 329, 332, 335, -1, 337, 349, 183, -388, -388, 353, 342, -388, -388, -388, -388, -388, -388, 339, 358, 340, 0, 343, 360, 186, -388, -388, -388, 362, 354, -388, -388, -388, -388, -388, -388, 77, -388, 333, 345, -388, -388, -388, -388, 355, 369, -388, 380, 365, 1, 387, -388, 370, 390, 389, 1467, 372, 396, 1531, -388, 397, -388, 400, 382, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, 359, -388, -388, -388, -388, -388, 383, 402, 385, -388, -388, -388, 471, 471, -388, 408, 391, 750, 32, 471, 790, 1403, 404, -388, -388, 1058, 1058, -388, 471, -388, -388, -388, 393, 416, -1, 405, 1158, 1158, -388, 420, 222, 401, 421, 0, 411, 1241, 1241, -388, 77, 412, 409, 167, -2, 433, 222, -2, 418, 1467, 441, 1, 1467, -388, -388, 1467, -388, 442, 222, -388, 443, 445, 1058, -388, 423, 430, 431, 434, 435, -388, -388, -388, 436, -388, -388, 1158, -388, 471, -388, 452, 1158, -388, -388, 1241, -388, 471, -388, 457, 180, -2, -388, -388, -2, -388, 1241, -388, -388, -388, -388, 1531, -388, -388, 458, 1531, -388, 471, 471, -388, -388, -388, -388, -388, -388, -388, -388, -388, 1158, -388, -388, -388, 1241, 424, -388, -388, -388, -388, 1467, -388, 440, 444, 446, 449, 451, -388, -388, -388, -388, -388 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { -388, -388, -388, -388, -388, -388, -388, -388, -84, 99, 96, -149, -388, -388, -388, -388, -388, -388, -85, -388, 55, -253, -388, -388, -388, -388, -388, 58, -112, -388, 235, -388, -388, 465, 17, 154, -388, 76, 162, -388, 357, 120, -115, -388, -388, -388, -388, -388, -127, -388, 87, -388, -388, -388, 399, -388, -388, -388, -168, 273, 97, -388, 103, 198, -388, 410, 129, -93, -388, -388, -388, -388, -388, -80, -388, 115, -388, -388, 122, 230, -388, 447, 143, 488, 350, -388, 516, -388, 368, -388, -388, 781, -78, -388, 842, -109, -388, 455, -388, -388, -388, -388, -388, -54, 470, -388, -45, -388, -14, 351, -43, -388, -388, -388, 219, -388, -388, 286, -388, -40, 722, -200, -388, -388, -388, -388, -388, -388, 426, -196, -388, -388, 165, -387, 88, -16, -195, -388, -160, -388, 11 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -231 static const yytype_int16 yytable[] = { 25, 25, 220, 25, 25, 236, 199, 141, 206, 258, 142, 2, 250, 292, 241, 223, 457, 304, 305, 214, 306, 323, 351, 384, 270, 25, 224, -111, 230, -52, -112, 280, 310, 16, 233, 17, 212, 200, 240, 35, 216, 93, 271, 218, 224, 94, 21, 18, 22, 297, 338, 246, 345, 95, 58, 272, 23, 96, 22, 224, 492, 149, 19, 97, 151, 327, 23, 20, 185, 254, 98, 54, 55, 62, 62, 62, 275, 141, 27, 341, 142, 261, 31, 22, 32, 58, 56, 57, 58, 22, 59, 23, 22, 282, 33, 60, 61, 23, 425, 36, 23, 37, 25, 358, 24, 26, 62, 29, 30, 199, 3, 364, 38, 199, 281, 333, 334, 335, 336, 337, 373, 41, -219, 297, 330, -219, 332, 241, 385, 312, 42, 40, 314, 58, 350, 43, 44, 22, -230, 322, 200, -230, 45, 345, 200, 23, 315, 316, 317, 318, 319, 46, 214, 4, 5, 6, 7, 8, 376, 377, 379, 380, 381, -221, -217, 50, -221, -217, 300, 49, 301, 302, 302, 302, 51, 185, 117, 366, 367, 368, 369, 370, 371, 372, 118, 375, 375, 375, 375, 375, 384, 412, 413, 296, 421, 422, 476, 477, 207, 200, 208, 200, 200, 200, 200, 458, 456, 199, 258, 506, 477, 209, 212, 216, 217, 199, 218, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 345, 344, 199, 219, 345, 222, 388, 345, 200, 226, 224, 227, 297, 297, 231, 235, 200, 239, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 478, 243, 200, 481, 242, 247, 296, 248, 359, 202, 251, 263, 260, 262, 254, 264, 359, 269, 345, 273, 274, 345, -51, 276, 483, 359, 297, 373, 277, 149, 487, 382, 278, 359, 298, 62, 360, 154, 389, 311, 228, 401, 313, 386, 328, 507, 471, 472, 508, 150, 329, 199, 331, 362, 199, 155, 156, 157, 244, 463, 464, 344, 361, 390, 363, 365, 394, 150, 395, 161, 162, 229, 396, 155, 156, 157, 203, 397, 398, 399, 503, 402, 200, 445, 446, 200, 403, 161, 162, 245, 451, 509, 199, 141, 404, 405, 142, 406, 411, 302, 407, 517, 414, 408, 410, 415, 416, 417, 310, 420, 419, 423, 202, 359, 200, 200, 202, 424, 431, 499, 429, 200, 430, 200, 502, 296, 296, 432, 199, 516, 200, 199, -115, 327, 199, 434, 437, 435, 510, 436, 438, 201, 512, -52, -53, 441, 271, 515, 344, 440, 442, 443, 344, 444, 185, 344, 500, 447, 448, 200, 283, 459, 200, 460, 504, 200, 462, 465, 468, 467, 296, 202, 470, 202, 202, 202, 202, 475, 199, 203, 479, 474, 199, 203, 513, 514, 482, 200, 484, 488, 359, 493, 490, 359, 491, 200, 359, 344, 494, 495, 344, 501, 496, 497, 498, 199, 505, 511, 518, 200, 202, -116, 519, 200, 520, 200, 200, 521, 202, 522, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 453, 486, 202, 200, 489, 203, 67, 203, 203, 203, 203, 140, 378, 67, 205, 201, 54, 55, 485, 433, 439, 259, 184, 452, 480, 237, 342, 418, 473, 469, 450, 56, 57, 58, 359, 59, 238, 22, 466, 461, 60, 61, 409, 449, 203, 23, 204, 268, 221, 215, 234, 62, 203, 0, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 66, 299, 203, 455, 0, 137, 0, 66, 265, 266, 0, 0, 0, 0, 140, 202, 149, 150, 202, 151, 0, 0, 0, 155, 156, 157, 0, 0, 0, 0, 0, 0, 0, 140, 0, -56, 0, 161, 162, 267, 0, 0, 201, 0, 0, 0, 0, 202, 202, 0, 201, 0, 0, -56, 202, 0, 202, 0, 0, 201, 0, 0, 184, 202, 53, 0, -56, 201, 0, 387, 0, 0, 54, 55, 0, 137, 0, 0, 0, 0, 0, 0, 203, 0, 0, 203, 0, 56, 57, 58, 0, 59, 202, 22, 137, 202, 60, 61, 202, 0, 0, 23, 140, 0, 0, 0, 67, 62, 0, 0, 0, 0, 0, 0, 203, 203, 0, 0, 0, 0, 202, 203, 0, 203, 0, 0, 0, 0, 202, 0, 203, 0, 0, 140, 0, 140, 140, 140, 140, 140, 140, 0, 202, 140, 0, 0, 202, 201, 202, 202, 0, 0, 0, 0, 0, 0, 184, 0, 0, 203, 0, 0, 203, 137, 184, 203, 0, 66, 0, 202, 0, 0, 0, 184, 0, 0, 0, 0, 0, 0, 0, 184, 0, 0, 0, 0, 0, 203, 201, 0, 0, 0, 0, 0, 137, 203, 137, 137, 137, 137, 137, 137, 0, 0, 137, 0, 0, 116, 0, 203, 52, 143, 0, 203, 0, 203, 203, 0, 0, 0, 53, 0, 0, 0, 201, 0, 0, 201, 54, 55, 201, 0, 0, 0, 0, 0, 203, 0, 119, 0, 0, 0, 94, 56, 57, 58, 0, 59, 0, 22, 120, 0, 60, 61, 96, 0, 116, 23, 0, 0, 97, 184, 0, 62, 0, 0, 113, 121, 54, 55, 138, 0, 0, 0, 0, 116, 0, 0, 0, 0, 0, 143, 0, 56, 57, 58, 0, 59, 67, 22, 0, 140, 60, 61, 0, 0, 0, 23, 0, 0, 143, 201, 184, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 140, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, 139, 0, 0, 113, 0, 0, 0, 184, 0, 138, 184, 0, 0, 184, 0, 0, 0, 0, 66, 0, 0, 137, 0, 0, 0, 0, 0, 0, 138, 0, 0, 140, 0, 0, 0, 0, 0, 116, 143, 290, 0, 0, 140, 0, 0, 114, 137, 137, 0, 309, 0, 0, 116, 0, 0, 116, 116, 116, 116, 116, 116, 321, 116, 0, 114, 326, 0, 0, 140, 143, 139, 143, 143, 143, 143, 143, 143, 0, 349, 143, 0, 356, 0, 0, 184, 0, 0, 0, 0, 139, 0, 0, 137, 0, 0, 0, 113, 138, 0, 0, 0, 0, 0, 137, 0, 0, 356, 0, 0, 0, 0, 113, 0, 290, 113, 113, 113, 113, 113, 113, 0, 113, 0, 0, 0, 0, 0, 0, 138, 137, 138, 138, 138, 138, 138, 138, 309, 0, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 326, 0, 0, 0, 114, 139, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 114, 114, 114, 114, 114, 114, 356, 114, 283, 0, 0, 0, 0, 0, 139, 0, 139, 139, 139, 139, 139, 139, 0, 0, 139, 0, 284, 285, 286, 0, 0, 0, 287, 288, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 289, 0, 0, 0, 59, 116, 22, 143, 0, 60, 61, 0, 290, 290, 23, 0, 0, 0, 0, 0, 62, 309, 0, 116, 116, 0, 0, 321, 0, 0, 326, 0, 143, 143, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 356, 0, 0, 0, 0, 0, 0, 356, 0, 93, 0, 290, 0, 94, 0, 0, 0, 0, 113, 0, 138, 232, 0, 0, 116, 96, 0, 0, 0, 116, 0, 97, 143, 0, 0, 0, 113, 113, 98, 54, 55, 0, 0, 143, 0, 138, 138, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 0, 59, 0, 22, 0, 0, 60, 61, 116, 0, 0, 23, 143, 0, 0, 0, 0, 62, 0, 0, 0, 0, 114, 0, 139, 0, 113, 0, 0, 0, 0, 113, 0, 0, 138, 119, 0, 0, 0, 94, 114, 114, 0, 0, 0, 138, 0, 249, 0, 139, 139, 96, 0, 0, 0, 0, 0, 97, 0, 0, 0, 0, 0, 0, 121, 54, 55, 113, 0, 0, 0, 138, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 0, 59, 0, 22, 114, 0, 60, 61, 0, 114, 0, 23, 139, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 139, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 114, 145, 146, 147, 139, 148, 0, 149, 150, 94, 151, 152, 153, 154, 155, 156, 157, 252, 0, 0, 0, 96, 159, 160, 253, 0, 0, 97, 161, 162, 163, 0, 0, 0, 164, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 0, 59, 0, 22, 0, 0, 60, 61, 0, 0, 144, 23, 145, 146, 147, 0, 148, 62, 149, 150, 94, 151, 152, 153, 154, 155, 156, 157, 158, 0, 0, 0, 96, 159, 160, 0, 0, 0, 97, 161, 162, 163, 0, 0, 0, 164, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 0, 59, 0, 22, 0, 0, 60, 61, 0, 0, 144, 23, 145, 146, 147, 0, 148, 62, 149, 150, 94, 151, 152, 153, 154, 155, 156, 157, 357, 0, 0, 0, 96, 159, 160, 0, 0, 0, 97, 161, 162, 163, 0, 0, 0, 164, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 0, 59, 0, 22, 0, 0, 60, 61, 0, 0, 144, 23, 145, 146, 147, 0, 148, 62, 149, 150, 94, 151, 152, 153, 154, 155, 156, 157, 252, 0, 0, 283, 96, 159, 160, 0, 0, 0, 97, 161, 162, 163, 391, 0, 0, 164, 54, 55, 0, 284, 285, 286, 0, 0, 0, 287, 288, 0, 0, 0, 0, 56, 57, 58, 0, 59, 0, 22, 289, 0, 60, 61, 59, 0, 22, 23, 0, 60, 61, 0, 0, 62, 23, 0, 0, 0, 0, 0, 62 }; static const yytype_int16 yycheck[] = { 16, 17, 95, 19, 20, 120, 51, 50, 53, 158, 50, 0, 139, 209, 123, 99, 403, 217, 218, 73, 21, 21, 21, 276, 30, 41, 23, 8, 106, 8, 8, 29, 29, 21, 114, 21, 34, 51, 122, 28, 21, 9, 48, 21, 23, 13, 58, 21, 60, 209, 21, 129, 247, 21, 56, 61, 68, 25, 60, 23, 447, 11, 21, 31, 14, 29, 68, 21, 51, 154, 38, 39, 40, 74, 74, 74, 185, 120, 68, 247, 120, 165, 10, 60, 10, 56, 54, 55, 56, 60, 58, 68, 60, 208, 10, 63, 64, 68, 21, 10, 68, 10, 118, 252, 16, 17, 74, 19, 20, 154, 0, 260, 60, 158, 207, 242, 243, 244, 245, 246, 269, 17, 11, 283, 239, 14, 241, 236, 277, 222, 29, 32, 225, 56, 249, 36, 37, 60, 11, 232, 154, 14, 10, 338, 158, 68, 226, 227, 228, 229, 230, 10, 206, 43, 44, 45, 46, 47, 270, 271, 272, 273, 274, 11, 11, 10, 14, 14, 213, 24, 215, 216, 217, 218, 10, 158, 28, 262, 263, 264, 265, 266, 267, 268, 10, 270, 271, 272, 273, 274, 443, 8, 9, 209, 8, 9, 29, 30, 21, 213, 21, 215, 216, 217, 218, 405, 402, 252, 357, 29, 30, 10, 34, 21, 21, 260, 21, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 425, 247, 277, 17, 429, 21, 279, 432, 252, 34, 23, 3, 402, 403, 17, 17, 260, 21, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 429, 3, 277, 432, 34, 8, 283, 17, 252, 51, 17, 3, 21, 34, 360, 4, 260, 8, 474, 30, 61, 477, 8, 17, 434, 269, 447, 437, 21, 11, 440, 275, 59, 277, 29, 74, 10, 17, 10, 29, 3, 291, 29, 59, 29, 474, 422, 423, 477, 12, 29, 357, 29, 29, 360, 18, 19, 20, 3, 413, 414, 338, 28, 10, 29, 29, 21, 12, 21, 32, 33, 34, 21, 18, 19, 20, 51, 21, 21, 10, 468, 8, 357, 389, 390, 360, 21, 32, 33, 34, 396, 479, 398, 397, 24, 10, 397, 29, 10, 405, 29, 511, 10, 29, 28, 24, 28, 10, 29, 10, 28, 10, 154, 357, 389, 390, 158, 24, 10, 460, 48, 396, 28, 398, 465, 402, 403, 8, 434, 505, 405, 437, 48, 29, 440, 9, 8, 28, 484, 10, 29, 51, 488, 8, 8, 24, 48, 501, 425, 10, 28, 10, 429, 29, 398, 432, 462, 10, 28, 434, 17, 29, 437, 8, 470, 440, 22, 8, 8, 29, 447, 213, 22, 215, 216, 217, 218, 29, 484, 154, 8, 30, 488, 158, 490, 491, 29, 462, 8, 8, 434, 29, 10, 437, 10, 470, 440, 474, 29, 29, 477, 10, 29, 29, 29, 511, 10, 10, 29, 484, 252, 48, 29, 488, 29, 490, 491, 29, 260, 29, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 398, 437, 277, 511, 443, 213, 45, 215, 216, 217, 218, 50, 271, 52, 53, 158, 39, 40, 436, 351, 360, 158, 51, 397, 431, 120, 247, 323, 425, 420, 395, 54, 55, 56, 511, 58, 120, 60, 417, 411, 63, 64, 306, 394, 252, 68, 52, 173, 95, 73, 118, 74, 260, -1, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 45, 212, 277, 399, -1, 50, -1, 52, 3, 4, -1, -1, -1, -1, 120, 357, 11, 12, 360, 14, -1, -1, -1, 18, 19, 20, -1, -1, -1, -1, -1, -1, -1, 139, -1, 30, -1, 32, 33, 34, -1, -1, 252, -1, -1, -1, -1, 389, 390, -1, 260, -1, -1, 48, 396, -1, 398, -1, -1, 269, -1, -1, 158, 405, 31, -1, 61, 277, -1, 279, -1, -1, 39, 40, -1, 120, -1, -1, -1, -1, -1, -1, 357, -1, -1, 360, -1, 54, 55, 56, -1, 58, 434, 60, 139, 437, 63, 64, 440, -1, -1, 68, 208, -1, -1, -1, 212, 74, -1, -1, -1, -1, -1, -1, 389, 390, -1, -1, -1, -1, 462, 396, -1, 398, -1, -1, -1, -1, 470, -1, 405, -1, -1, 239, -1, 241, 242, 243, 244, 245, 246, -1, 484, 249, -1, -1, 488, 357, 490, 491, -1, -1, -1, -1, -1, -1, 252, -1, -1, 434, -1, -1, 437, 208, 260, 440, -1, 212, -1, 511, -1, -1, -1, 269, -1, -1, -1, -1, -1, -1, -1, 277, -1, -1, -1, -1, -1, 462, 398, -1, -1, -1, -1, -1, 239, 470, 241, 242, 243, 244, 245, 246, -1, -1, 249, -1, -1, 46, -1, 484, 21, 50, -1, 488, -1, 490, 491, -1, -1, -1, 31, -1, -1, -1, 434, -1, -1, 437, 39, 40, 440, -1, -1, -1, -1, -1, 511, -1, 9, -1, -1, -1, 13, 54, 55, 56, -1, 58, -1, 60, 21, -1, 63, 64, 25, -1, 95, 68, -1, -1, 31, 357, -1, 74, -1, -1, 46, 38, 39, 40, 50, -1, -1, -1, -1, 114, -1, -1, -1, -1, -1, 120, -1, 54, 55, 56, -1, 58, 394, 60, -1, 397, 63, 64, -1, -1, -1, 68, -1, -1, 139, 511, 398, 74, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 95, 422, 423, -1, -1, -1, -1, -1, -1, -1, -1, -1, 46, -1, -1, -1, 50, -1, -1, 114, -1, -1, -1, 434, -1, 120, 437, -1, -1, 440, -1, -1, -1, -1, 394, -1, -1, 397, -1, -1, -1, -1, -1, -1, 139, -1, -1, 468, -1, -1, -1, -1, -1, 207, 208, 209, -1, -1, 479, -1, -1, 95, 422, 423, -1, 219, -1, -1, 222, -1, -1, 225, 226, 227, 228, 229, 230, 231, 232, -1, 114, 235, -1, -1, 505, 239, 120, 241, 242, 243, 244, 245, 246, -1, 248, 249, -1, 251, -1, -1, 511, -1, -1, -1, -1, 139, -1, -1, 468, -1, -1, -1, 207, 208, -1, -1, -1, -1, -1, 479, -1, -1, 276, -1, -1, -1, -1, 222, -1, 283, 225, 226, 227, 228, 229, 230, -1, 232, -1, -1, -1, -1, -1, -1, 239, 505, 241, 242, 243, 244, 245, 246, 306, -1, 249, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 323, -1, -1, -1, 207, 208, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 222, -1, -1, 225, 226, 227, 228, 229, 230, 351, 232, 17, -1, -1, -1, -1, -1, 239, -1, 241, 242, 243, 244, 245, 246, -1, -1, 249, -1, 35, 36, 37, -1, -1, -1, 41, 42, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 54, -1, -1, -1, 58, 395, 60, 397, -1, 63, 64, -1, 402, 403, 68, -1, -1, -1, -1, -1, 74, 411, -1, 413, 414, -1, -1, 417, -1, -1, 420, -1, 422, 423, -1, -1, -1, -1, -1, -1, -1, 431, -1, -1, -1, -1, 436, -1, -1, -1, -1, -1, -1, 443, -1, 9, -1, 447, -1, 13, -1, -1, -1, -1, 395, -1, 397, 21, -1, -1, 460, 25, -1, -1, -1, 465, -1, 31, 468, -1, -1, -1, 413, 414, 38, 39, 40, -1, -1, 479, -1, 422, 423, -1, -1, -1, -1, -1, -1, -1, 54, 55, 56, -1, 58, -1, 60, -1, -1, 63, 64, 501, -1, -1, 68, 505, -1, -1, -1, -1, 74, -1, -1, -1, -1, 395, -1, 397, -1, 460, -1, -1, -1, -1, 465, -1, -1, 468, 9, -1, -1, -1, 13, 413, 414, -1, -1, -1, 479, -1, 21, -1, 422, 423, 25, -1, -1, -1, -1, -1, 31, -1, -1, -1, -1, -1, -1, 38, 39, 40, 501, -1, -1, -1, 505, -1, -1, -1, -1, -1, -1, -1, -1, 54, 55, 56, -1, 58, -1, 60, 460, -1, 63, 64, -1, 465, -1, 68, 468, -1, -1, -1, -1, 74, -1, -1, -1, -1, -1, 479, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 501, 5, 6, 7, 505, 9, -1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, -1, -1, -1, 25, 26, 27, 28, -1, -1, 31, 32, 33, 34, -1, -1, -1, 38, 39, 40, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 54, 55, 56, -1, 58, -1, 60, -1, -1, 63, 64, -1, -1, 3, 68, 5, 6, 7, -1, 9, 74, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, -1, -1, -1, 25, 26, 27, -1, -1, -1, 31, 32, 33, 34, -1, -1, -1, 38, 39, 40, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 54, 55, 56, -1, 58, -1, 60, -1, -1, 63, 64, -1, -1, 3, 68, 5, 6, 7, -1, 9, 74, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, -1, -1, -1, 25, 26, 27, -1, -1, -1, 31, 32, 33, 34, -1, -1, -1, 38, 39, 40, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 54, 55, 56, -1, 58, -1, 60, -1, -1, 63, 64, -1, -1, 3, 68, 5, 6, 7, -1, 9, 74, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, -1, -1, 17, 25, 26, 27, -1, -1, -1, 31, 32, 33, 34, 28, -1, -1, 38, 39, 40, -1, 35, 36, 37, -1, -1, -1, 41, 42, -1, -1, -1, -1, 54, 55, 56, -1, 58, -1, 60, 54, -1, 63, 64, 58, -1, 60, 68, -1, 63, 64, -1, -1, 74, 68, -1, -1, -1, -1, -1, 74 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 77, 216, 0, 43, 44, 45, 46, 47, 78, 79, 80, 81, 82, 83, 202, 21, 21, 21, 21, 21, 58, 60, 68, 210, 211, 210, 68, 215, 210, 210, 10, 10, 10, 203, 216, 10, 10, 60, 85, 85, 17, 29, 85, 85, 10, 10, 204, 210, 24, 10, 10, 21, 31, 39, 40, 54, 55, 56, 58, 63, 64, 74, 158, 159, 160, 162, 173, 174, 175, 176, 177, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 198, 211, 212, 213, 214, 9, 13, 21, 25, 31, 38, 142, 143, 144, 145, 146, 147, 148, 149, 150, 152, 153, 156, 157, 162, 167, 170, 173, 196, 28, 10, 9, 21, 38, 117, 118, 119, 120, 121, 122, 123, 124, 125, 129, 130, 131, 137, 140, 141, 162, 167, 170, 173, 186, 195, 196, 3, 5, 6, 7, 9, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 26, 27, 32, 33, 34, 38, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 99, 100, 101, 102, 104, 105, 106, 107, 108, 109, 110, 112, 115, 116, 161, 163, 164, 165, 167, 168, 169, 170, 179, 180, 182, 184, 185, 190, 193, 159, 173, 182, 21, 21, 10, 84, 216, 34, 178, 179, 180, 21, 21, 21, 17, 143, 157, 21, 84, 23, 171, 34, 3, 3, 34, 168, 17, 21, 149, 204, 17, 118, 130, 141, 21, 84, 171, 34, 3, 3, 34, 168, 8, 17, 21, 124, 17, 21, 28, 94, 109, 110, 111, 87, 116, 21, 84, 34, 3, 4, 3, 4, 34, 164, 8, 30, 48, 61, 30, 61, 171, 17, 21, 59, 166, 29, 143, 118, 17, 35, 36, 37, 41, 42, 54, 196, 199, 205, 206, 207, 208, 211, 214, 29, 160, 182, 182, 182, 197, 197, 197, 21, 154, 155, 196, 29, 29, 143, 29, 143, 149, 149, 149, 149, 149, 151, 196, 143, 21, 138, 139, 196, 29, 29, 29, 118, 29, 118, 124, 124, 124, 124, 124, 21, 132, 133, 134, 135, 172, 211, 212, 126, 127, 128, 196, 118, 21, 97, 98, 113, 114, 196, 21, 87, 110, 10, 28, 29, 29, 87, 29, 94, 94, 94, 94, 94, 94, 94, 87, 103, 94, 104, 104, 106, 104, 104, 104, 110, 96, 97, 87, 59, 185, 186, 10, 10, 28, 205, 209, 21, 21, 21, 21, 21, 10, 200, 216, 8, 21, 24, 10, 29, 29, 29, 155, 28, 10, 8, 9, 10, 24, 28, 10, 139, 28, 10, 8, 9, 10, 24, 21, 134, 135, 136, 48, 28, 10, 8, 114, 9, 28, 10, 8, 29, 111, 10, 24, 28, 10, 29, 182, 182, 10, 28, 158, 142, 182, 117, 86, 201, 208, 205, 209, 197, 29, 8, 154, 22, 143, 143, 8, 151, 29, 8, 138, 22, 118, 118, 136, 30, 29, 29, 30, 134, 8, 126, 134, 29, 87, 8, 113, 103, 87, 8, 96, 10, 10, 209, 29, 29, 29, 29, 29, 29, 149, 182, 10, 149, 124, 182, 10, 29, 134, 134, 124, 94, 10, 94, 182, 182, 143, 118, 87, 29, 29, 29, 29, 29 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. Once GCC version 2 has supplanted version 1, this can go. However, YYFAIL appears to be in use. Nevertheless, it is formally deprecated in Bison 2.4.2's NEWS entry, where a plan to phase it out is discussed. */ #define YYFAIL goto yyerrlab #if defined YYFAIL /* This is here to suppress warnings from the GCC cpp's -Wunused-macros. Normally we don't worry about that warning, but some users do, and we want to make it easy for users to remove YYFAIL uses, which will produce warnings from Bison 2.5. */ #endif #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY && yylen == 1) \ { \ yychar = (Token); \ yylval = (Value); \ yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ else \ { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #define YYRHSLOC(Rhs, K) ((Rhs)[K]) #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (YYID (N)) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (YYID (0)) #endif /* YY_LOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL # define YY_LOCATION_PRINT(File, Loc) \ fprintf (File, "%d.%d-%d.%d", \ (Loc).first_line, (Loc).first_column, \ (Loc).last_line, (Loc).last_column) # else # define YY_LOCATION_PRINT(File, Loc) ((void) 0) # endif #endif /* YYLEX -- calling `yylex' with the right arguments. */ #ifdef YYLEX_PARAM # define YYLEX yylex (YYLEX_PARAM) #else # define YYLEX yylex () #endif /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (YYID (0)) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value); \ YYFPRINTF (stderr, "\n"); \ } \ } while (YYID (0)) /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) #else static void yy_symbol_value_print (yyoutput, yytype, yyvaluep) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; #endif { if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # else YYUSE (yyoutput); # endif switch (yytype) { default: break; } } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) #else static void yy_symbol_print (yyoutput, yytype, yyvaluep) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; #endif { if (yytype < YYNTOKENS) YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); else YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); yy_symbol_value_print (yyoutput, yytype, yyvaluep); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) #else static void yy_stack_print (yybottom, yytop) yytype_int16 *yybottom; yytype_int16 *yytop; #endif { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (YYID (0)) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_reduce_print (YYSTYPE *yyvsp, int yyrule) #else static void yy_reduce_print (yyvsp, yyrule) YYSTYPE *yyvsp; int yyrule; #endif { int yynrhs = yyr2[yyrule]; int yyi; unsigned long int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) ); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyvsp, Rule); \ } while (YYID (0)) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static YYSIZE_T yystrlen (const char *yystr) #else static YYSIZE_T yystrlen (yystr) const char *yystr; #endif { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static char * yystpcpy (char *yydest, const char *yysrc) #else static char * yystpcpy (yydest, yysrc) char *yydest; const char *yysrc; #endif { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into YYRESULT an error message about the unexpected token YYCHAR while in state YYSTATE. Return the number of bytes copied, including the terminating null byte. If YYRESULT is null, do not copy anything; just return the number of bytes that would be copied. As a special case, return 0 if an ordinary "syntax error" message will do. Return YYSIZE_MAXIMUM if overflow occurs during size calculation. */ static YYSIZE_T yysyntax_error (char *yyresult, int yystate, int yychar) { int yyn = yypact[yystate]; if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) return 0; else { int yytype = YYTRANSLATE (yychar); YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); YYSIZE_T yysize = yysize0; YYSIZE_T yysize1; int yysize_overflow = 0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; int yyx; # if 0 /* This is so xgettext sees the translatable formats that are constructed on the fly. */ YY_("syntax error, unexpected %s"); YY_("syntax error, unexpected %s, expecting %s"); YY_("syntax error, unexpected %s, expecting %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); # endif char *yyfmt; char const *yyf; static char const yyunexpected[] = "syntax error, unexpected %s"; static char const yyexpecting[] = ", expecting %s"; static char const yyor[] = " or %s"; char yyformat[sizeof yyunexpected + sizeof yyexpecting - 1 + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) * (sizeof yyor - 1))]; char const *yyprefix = yyexpecting; /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yycount = 1; yyarg[0] = yytname[yytype]; yyfmt = yystpcpy (yyformat, yyunexpected); for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; yyformat[sizeof yyunexpected - 1] = '\0'; break; } yyarg[yycount++] = yytname[yyx]; yysize1 = yysize + yytnamerr (0, yytname[yyx]); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; yyfmt = yystpcpy (yyfmt, yyprefix); yyprefix = yyor; } yyf = YY_(yyformat); yysize1 = yysize + yystrlen (yyf); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; if (yysize_overflow) return YYSIZE_MAXIMUM; if (yyresult) { /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ char *yyp = yyresult; int yyi = 0; while ((*yyp = *yyf) != '\0') { if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyf += 2; } else { yyp++; yyf++; } } } return yysize; } } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) #else static void yydestruct (yymsg, yytype, yyvaluep) const char *yymsg; int yytype; YYSTYPE *yyvaluep; #endif { YYUSE (yyvaluep); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); #if 0 switch (yytype) { default: break; } #endif } /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); #else int yyparse (); #endif #else /* ! YYPARSE_PARAM */ #if defined __STDC__ || defined __cplusplus int yyparse (void); #else int yyparse (); #endif #endif /* ! YYPARSE_PARAM */ /* The lookahead symbol. */ int yychar; /* The semantic value of the lookahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; /*-------------------------. | yyparse or yypush_parse. | `-------------------------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void *YYPARSE_PARAM) #else int yyparse (YYPARSE_PARAM) void *YYPARSE_PARAM; #endif #else /* ! YYPARSE_PARAM */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void) #else int yyparse () #endif #endif { int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: `yyss': related to states. `yyvs': related to semantic values. Refer to the stacks thru separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; YYSIZE_T yystacksize; int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ int yytoken; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; yytoken = 0; yyss = yyssa; yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ yyssp = yyss; yyvsp = yyvs; goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yyn == 0 || yyn == YYTABLE_NINF) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token. */ yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: `$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; YY_REDUCE_PRINT (yyn); switch (yyn) { case 2: /* Line 1464 of yacc.c */ #line 225 "tptp5.y" {;} break; case 3: /* Line 1464 of yacc.c */ #line 226 "tptp5.y" {;} break; case 4: /* Line 1464 of yacc.c */ #line 229 "tptp5.y" {P_PRINT((yyval.pval));;} break; case 5: /* Line 1464 of yacc.c */ #line 230 "tptp5.y" {P_PRINT((yyval.pval));;} break; case 6: /* Line 1464 of yacc.c */ #line 233 "tptp5.y" {(yyval.pval) = P_BUILD("annotated_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 7: /* Line 1464 of yacc.c */ #line 234 "tptp5.y" {(yyval.pval) = P_BUILD("annotated_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 8: /* Line 1464 of yacc.c */ #line 235 "tptp5.y" {(yyval.pval) = P_BUILD("annotated_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 9: /* Line 1464 of yacc.c */ #line 236 "tptp5.y" {(yyval.pval) = P_BUILD("annotated_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 10: /* Line 1464 of yacc.c */ #line 239 "tptp5.y" {(yyval.pval) = P_BUILD("thf_annotated", P_TOKEN("_LIT_thf ", (yyvsp[(1) - (10)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (10)].ival)), (yyvsp[(3) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (10)].ival)), (yyvsp[(5) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (10)].ival)), (yyvsp[(7) - (10)].pval), (yyvsp[(8) - (10)].pval), P_TOKEN("RPAREN ", (yyvsp[(9) - (10)].ival)), P_TOKEN("PERIOD ", (yyvsp[(10) - (10)].ival)));;} break; case 11: /* Line 1464 of yacc.c */ #line 242 "tptp5.y" {(yyval.pval) = P_BUILD("tff_annotated", P_TOKEN("_LIT_tff ", (yyvsp[(1) - (10)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (10)].ival)), (yyvsp[(3) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (10)].ival)), (yyvsp[(5) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (10)].ival)), (yyvsp[(7) - (10)].pval), (yyvsp[(8) - (10)].pval), P_TOKEN("RPAREN ", (yyvsp[(9) - (10)].ival)), P_TOKEN("PERIOD ", (yyvsp[(10) - (10)].ival)));;} break; case 12: /* Line 1464 of yacc.c */ #line 245 "tptp5.y" {(yyval.pval) = P_BUILD("fof_annotated", P_TOKEN("_LIT_fof ", (yyvsp[(1) - (10)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (10)].ival)), (yyvsp[(3) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (10)].ival)), (yyvsp[(5) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (10)].ival)), (yyvsp[(7) - (10)].pval), (yyvsp[(8) - (10)].pval), P_TOKEN("RPAREN ", (yyvsp[(9) - (10)].ival)), P_TOKEN("PERIOD ", (yyvsp[(10) - (10)].ival)));;} break; case 13: /* Line 1464 of yacc.c */ #line 248 "tptp5.y" {(yyval.pval) = P_BUILD("cnf_annotated", P_TOKEN("_LIT_cnf ", (yyvsp[(1) - (10)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (10)].ival)), (yyvsp[(3) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (10)].ival)), (yyvsp[(5) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (10)].ival)), (yyvsp[(7) - (10)].pval), (yyvsp[(8) - (10)].pval), P_TOKEN("RPAREN ", (yyvsp[(9) - (10)].ival)), P_TOKEN("PERIOD ", (yyvsp[(10) - (10)].ival)));;} break; case 14: /* Line 1464 of yacc.c */ #line 251 "tptp5.y" {(yyval.pval) = P_BUILD("annotations", P_TOKEN("COMMA ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 15: /* Line 1464 of yacc.c */ #line 252 "tptp5.y" {(yyval.pval) = P_BUILD("annotations", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 16: /* Line 1464 of yacc.c */ #line 255 "tptp5.y" {(yyval.pval) = P_BUILD("formula_role", P_TOKEN("lower_word ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 17: /* Line 1464 of yacc.c */ #line 258 "tptp5.y" {(yyval.pval) = P_BUILD("thf_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 18: /* Line 1464 of yacc.c */ #line 259 "tptp5.y" {(yyval.pval) = P_BUILD("thf_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 19: /* Line 1464 of yacc.c */ #line 262 "tptp5.y" {(yyval.pval) = P_BUILD("thf_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 20: /* Line 1464 of yacc.c */ #line 263 "tptp5.y" {(yyval.pval) = P_BUILD("thf_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 21: /* Line 1464 of yacc.c */ #line 264 "tptp5.y" {(yyval.pval) = P_BUILD("thf_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 22: /* Line 1464 of yacc.c */ #line 265 "tptp5.y" {(yyval.pval) = P_BUILD("thf_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 23: /* Line 1464 of yacc.c */ #line 268 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 24: /* Line 1464 of yacc.c */ #line 269 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 25: /* Line 1464 of yacc.c */ #line 270 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 26: /* Line 1464 of yacc.c */ #line 273 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_pair", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 27: /* Line 1464 of yacc.c */ #line 276 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_tuple", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 28: /* Line 1464 of yacc.c */ #line 277 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_tuple", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 29: /* Line 1464 of yacc.c */ #line 278 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_tuple", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 30: /* Line 1464 of yacc.c */ #line 281 "tptp5.y" {(yyval.pval) = P_BUILD("thf_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 31: /* Line 1464 of yacc.c */ #line 282 "tptp5.y" {(yyval.pval) = P_BUILD("thf_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 32: /* Line 1464 of yacc.c */ #line 285 "tptp5.y" {(yyval.pval) = P_BUILD("thf_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 33: /* Line 1464 of yacc.c */ #line 286 "tptp5.y" {(yyval.pval) = P_BUILD("thf_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 34: /* Line 1464 of yacc.c */ #line 289 "tptp5.y" {(yyval.pval) = P_BUILD("thf_apply_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AT_SIGN ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 35: /* Line 1464 of yacc.c */ #line 290 "tptp5.y" {(yyval.pval) = P_BUILD("thf_apply_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AT_SIGN ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 36: /* Line 1464 of yacc.c */ #line 293 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 37: /* Line 1464 of yacc.c */ #line 294 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 38: /* Line 1464 of yacc.c */ #line 295 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 39: /* Line 1464 of yacc.c */ #line 296 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 40: /* Line 1464 of yacc.c */ #line 297 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 41: /* Line 1464 of yacc.c */ #line 298 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 42: /* Line 1464 of yacc.c */ #line 299 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unitary_formula", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 43: /* Line 1464 of yacc.c */ #line 302 "tptp5.y" {(yyval.pval) = P_BUILD("thf_quantified_formula", (yyvsp[(1) - (6)].pval), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} break; case 44: /* Line 1464 of yacc.c */ #line 305 "tptp5.y" {(yyval.pval) = P_BUILD("thf_variable_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 45: /* Line 1464 of yacc.c */ #line 306 "tptp5.y" {(yyval.pval) = P_BUILD("thf_variable_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 46: /* Line 1464 of yacc.c */ #line 309 "tptp5.y" {(yyval.pval) = P_BUILD("thf_variable", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 47: /* Line 1464 of yacc.c */ #line 310 "tptp5.y" {(yyval.pval) = P_BUILD("thf_variable", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 48: /* Line 1464 of yacc.c */ #line 313 "tptp5.y" {(yyval.pval) = P_BUILD("thf_typed_variable", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 49: /* Line 1464 of yacc.c */ #line 316 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unary_formula", (yyvsp[(1) - (4)].pval), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 50: /* Line 1464 of yacc.c */ #line 319 "tptp5.y" {(yyval.pval) = P_BUILD("thf_type_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 51: /* Line 1464 of yacc.c */ #line 322 "tptp5.y" {(yyval.pval) = P_BUILD("thf_typeable_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 52: /* Line 1464 of yacc.c */ #line 323 "tptp5.y" {(yyval.pval) = P_BUILD("thf_typeable_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 53: /* Line 1464 of yacc.c */ #line 324 "tptp5.y" {(yyval.pval) = P_BUILD("thf_typeable_formula", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 54: /* Line 1464 of yacc.c */ #line 327 "tptp5.y" {(yyval.pval) = P_BUILD("thf_subtype", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 55: /* Line 1464 of yacc.c */ #line 330 "tptp5.y" {(yyval.pval) = P_BUILD("thf_top_level_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 56: /* Line 1464 of yacc.c */ #line 333 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unitary_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 57: /* Line 1464 of yacc.c */ #line 336 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 58: /* Line 1464 of yacc.c */ #line 337 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 59: /* Line 1464 of yacc.c */ #line 338 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 60: /* Line 1464 of yacc.c */ #line 341 "tptp5.y" {(yyval.pval) = P_BUILD("thf_mapping_type", (yyvsp[(1) - (3)].pval), P_TOKEN("arrow ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 61: /* Line 1464 of yacc.c */ #line 342 "tptp5.y" {(yyval.pval) = P_BUILD("thf_mapping_type", (yyvsp[(1) - (3)].pval), P_TOKEN("arrow ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 62: /* Line 1464 of yacc.c */ #line 345 "tptp5.y" {(yyval.pval) = P_BUILD("thf_xprod_type", (yyvsp[(1) - (3)].pval), P_TOKEN("STAR ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 63: /* Line 1464 of yacc.c */ #line 346 "tptp5.y" {(yyval.pval) = P_BUILD("thf_xprod_type", (yyvsp[(1) - (3)].pval), P_TOKEN("STAR ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 64: /* Line 1464 of yacc.c */ #line 349 "tptp5.y" {(yyval.pval) = P_BUILD("thf_union_type", (yyvsp[(1) - (3)].pval), P_TOKEN("plus ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 65: /* Line 1464 of yacc.c */ #line 350 "tptp5.y" {(yyval.pval) = P_BUILD("thf_union_type", (yyvsp[(1) - (3)].pval), P_TOKEN("plus ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 66: /* Line 1464 of yacc.c */ #line 353 "tptp5.y" {(yyval.pval) = P_BUILD("thf_atom", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 67: /* Line 1464 of yacc.c */ #line 354 "tptp5.y" {(yyval.pval) = P_BUILD("thf_atom", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 68: /* Line 1464 of yacc.c */ #line 357 "tptp5.y" {(yyval.pval) = P_BUILD("thf_tuple", P_TOKEN("LBRKT ", (yyvsp[(1) - (2)].ival)), P_TOKEN("RBRKT ", (yyvsp[(2) - (2)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 69: /* Line 1464 of yacc.c */ #line 358 "tptp5.y" {(yyval.pval) = P_BUILD("thf_tuple", P_TOKEN("LBRKT ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RBRKT ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 70: /* Line 1464 of yacc.c */ #line 361 "tptp5.y" {(yyval.pval) = P_BUILD("thf_tuple_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 71: /* Line 1464 of yacc.c */ #line 362 "tptp5.y" {(yyval.pval) = P_BUILD("thf_tuple_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 72: /* Line 1464 of yacc.c */ #line 365 "tptp5.y" {(yyval.pval) = P_BUILD("thf_let", P_TOKEN("COLON_EQUALS ", (yyvsp[(1) - (6)].ival)), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} break; case 73: /* Line 1464 of yacc.c */ #line 368 "tptp5.y" {(yyval.pval) = P_BUILD("thf_let_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 74: /* Line 1464 of yacc.c */ #line 369 "tptp5.y" {(yyval.pval) = P_BUILD("thf_let_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 75: /* Line 1464 of yacc.c */ #line 372 "tptp5.y" {(yyval.pval) = P_BUILD("thf_defined_var", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON_EQUALS ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 76: /* Line 1464 of yacc.c */ #line 373 "tptp5.y" {(yyval.pval) = P_BUILD("thf_defined_var", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 77: /* Line 1464 of yacc.c */ #line 376 "tptp5.y" {(yyval.pval) = P_BUILD("thf_conditional", P_TOKEN("_DLR_itef ", (yyvsp[(1) - (8)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (8)].ival)), (yyvsp[(3) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (8)].ival)), (yyvsp[(5) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (8)].ival)), (yyvsp[(7) - (8)].pval), P_TOKEN("RPAREN ", (yyvsp[(8) - (8)].ival)),NULL,NULL);;} break; case 78: /* Line 1464 of yacc.c */ #line 379 "tptp5.y" {(yyval.pval) = P_BUILD("thf_sequent", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 79: /* Line 1464 of yacc.c */ #line 380 "tptp5.y" {(yyval.pval) = P_BUILD("thf_sequent", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 80: /* Line 1464 of yacc.c */ #line 383 "tptp5.y" {(yyval.pval) = P_BUILD("tff_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 81: /* Line 1464 of yacc.c */ #line 384 "tptp5.y" {(yyval.pval) = P_BUILD("tff_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 82: /* Line 1464 of yacc.c */ #line 385 "tptp5.y" {(yyval.pval) = P_BUILD("tff_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 83: /* Line 1464 of yacc.c */ #line 388 "tptp5.y" {(yyval.pval) = P_BUILD("tff_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 84: /* Line 1464 of yacc.c */ #line 389 "tptp5.y" {(yyval.pval) = P_BUILD("tff_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 85: /* Line 1464 of yacc.c */ #line 392 "tptp5.y" {(yyval.pval) = P_BUILD("tff_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 86: /* Line 1464 of yacc.c */ #line 393 "tptp5.y" {(yyval.pval) = P_BUILD("tff_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 87: /* Line 1464 of yacc.c */ #line 396 "tptp5.y" {(yyval.pval) = P_BUILD("tff_binary_nonassoc", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 88: /* Line 1464 of yacc.c */ #line 399 "tptp5.y" {(yyval.pval) = P_BUILD("tff_binary_assoc", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 89: /* Line 1464 of yacc.c */ #line 400 "tptp5.y" {(yyval.pval) = P_BUILD("tff_binary_assoc", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 90: /* Line 1464 of yacc.c */ #line 403 "tptp5.y" {(yyval.pval) = P_BUILD("tff_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 91: /* Line 1464 of yacc.c */ #line 404 "tptp5.y" {(yyval.pval) = P_BUILD("tff_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 92: /* Line 1464 of yacc.c */ #line 407 "tptp5.y" {(yyval.pval) = P_BUILD("tff_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 93: /* Line 1464 of yacc.c */ #line 408 "tptp5.y" {(yyval.pval) = P_BUILD("tff_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 94: /* Line 1464 of yacc.c */ #line 411 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 95: /* Line 1464 of yacc.c */ #line 412 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 96: /* Line 1464 of yacc.c */ #line 413 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 97: /* Line 1464 of yacc.c */ #line 414 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 98: /* Line 1464 of yacc.c */ #line 415 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 99: /* Line 1464 of yacc.c */ #line 416 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 100: /* Line 1464 of yacc.c */ #line 417 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_formula", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 101: /* Line 1464 of yacc.c */ #line 420 "tptp5.y" {(yyval.pval) = P_BUILD("tff_quantified_formula", (yyvsp[(1) - (6)].pval), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} break; case 102: /* Line 1464 of yacc.c */ #line 423 "tptp5.y" {(yyval.pval) = P_BUILD("tff_variable_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 103: /* Line 1464 of yacc.c */ #line 424 "tptp5.y" {(yyval.pval) = P_BUILD("tff_variable_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 104: /* Line 1464 of yacc.c */ #line 427 "tptp5.y" {(yyval.pval) = P_BUILD("tff_variable", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 105: /* Line 1464 of yacc.c */ #line 428 "tptp5.y" {(yyval.pval) = P_BUILD("tff_variable", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 106: /* Line 1464 of yacc.c */ #line 431 "tptp5.y" {(yyval.pval) = P_BUILD("tff_typed_variable", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 107: /* Line 1464 of yacc.c */ #line 434 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unary_formula", (yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 108: /* Line 1464 of yacc.c */ #line 435 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 109: /* Line 1464 of yacc.c */ #line 438 "tptp5.y" {(yyval.pval) = P_BUILD("tff_typed_atom", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 110: /* Line 1464 of yacc.c */ #line 439 "tptp5.y" {(yyval.pval) = P_BUILD("tff_typed_atom", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 111: /* Line 1464 of yacc.c */ #line 442 "tptp5.y" {(yyval.pval) = P_BUILD("tff_untyped_atom", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 112: /* Line 1464 of yacc.c */ #line 443 "tptp5.y" {(yyval.pval) = P_BUILD("tff_untyped_atom", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 113: /* Line 1464 of yacc.c */ #line 446 "tptp5.y" {(yyval.pval) = P_BUILD("tff_top_level_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 114: /* Line 1464 of yacc.c */ #line 447 "tptp5.y" {(yyval.pval) = P_BUILD("tff_top_level_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 115: /* Line 1464 of yacc.c */ #line 450 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 116: /* Line 1464 of yacc.c */ #line 451 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_type", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 117: /* Line 1464 of yacc.c */ #line 454 "tptp5.y" {(yyval.pval) = P_BUILD("tff_atomic_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 118: /* Line 1464 of yacc.c */ #line 455 "tptp5.y" {(yyval.pval) = P_BUILD("tff_atomic_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 119: /* Line 1464 of yacc.c */ #line 458 "tptp5.y" {(yyval.pval) = P_BUILD("tff_mapping_type", (yyvsp[(1) - (3)].pval), P_TOKEN("arrow ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 120: /* Line 1464 of yacc.c */ #line 459 "tptp5.y" {(yyval.pval) = P_BUILD("tff_mapping_type", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 121: /* Line 1464 of yacc.c */ #line 462 "tptp5.y" {(yyval.pval) = P_BUILD("tff_xprod_type", (yyvsp[(1) - (3)].pval), P_TOKEN("STAR ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 122: /* Line 1464 of yacc.c */ #line 463 "tptp5.y" {(yyval.pval) = P_BUILD("tff_xprod_type", (yyvsp[(1) - (3)].pval), P_TOKEN("STAR ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 123: /* Line 1464 of yacc.c */ #line 464 "tptp5.y" {(yyval.pval) = P_BUILD("tff_xprod_type", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 124: /* Line 1464 of yacc.c */ #line 467 "tptp5.y" {(yyval.pval) = P_BUILD("tff_let", P_TOKEN("COLON_EQUALS ", (yyvsp[(1) - (6)].ival)), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} break; case 125: /* Line 1464 of yacc.c */ #line 470 "tptp5.y" {(yyval.pval) = P_BUILD("tff_let_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 126: /* Line 1464 of yacc.c */ #line 471 "tptp5.y" {(yyval.pval) = P_BUILD("tff_let_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 127: /* Line 1464 of yacc.c */ #line 474 "tptp5.y" {(yyval.pval) = P_BUILD("tff_defined_var", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON_EQUALS ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 128: /* Line 1464 of yacc.c */ #line 475 "tptp5.y" {(yyval.pval) = P_BUILD("tff_defined_var", (yyvsp[(1) - (4)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (4)].ival)), P_TOKEN("MINUS ", (yyvsp[(3) - (4)].ival)), (yyvsp[(4) - (4)].pval),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 129: /* Line 1464 of yacc.c */ #line 476 "tptp5.y" {(yyval.pval) = P_BUILD("tff_defined_var", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 130: /* Line 1464 of yacc.c */ #line 479 "tptp5.y" {(yyval.pval) = P_BUILD("tff_conditional", P_TOKEN("_DLR_itef ", (yyvsp[(1) - (8)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (8)].ival)), (yyvsp[(3) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (8)].ival)), (yyvsp[(5) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (8)].ival)), (yyvsp[(7) - (8)].pval), P_TOKEN("RPAREN ", (yyvsp[(8) - (8)].ival)),NULL,NULL);;} break; case 131: /* Line 1464 of yacc.c */ #line 482 "tptp5.y" {(yyval.pval) = P_BUILD("tff_sequent", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 132: /* Line 1464 of yacc.c */ #line 483 "tptp5.y" {(yyval.pval) = P_BUILD("tff_sequent", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 133: /* Line 1464 of yacc.c */ #line 486 "tptp5.y" {(yyval.pval) = P_BUILD("fof_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 134: /* Line 1464 of yacc.c */ #line 487 "tptp5.y" {(yyval.pval) = P_BUILD("fof_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 135: /* Line 1464 of yacc.c */ #line 490 "tptp5.y" {(yyval.pval) = P_BUILD("fof_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 136: /* Line 1464 of yacc.c */ #line 491 "tptp5.y" {(yyval.pval) = P_BUILD("fof_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 137: /* Line 1464 of yacc.c */ #line 494 "tptp5.y" {(yyval.pval) = P_BUILD("fof_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 138: /* Line 1464 of yacc.c */ #line 495 "tptp5.y" {(yyval.pval) = P_BUILD("fof_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 139: /* Line 1464 of yacc.c */ #line 498 "tptp5.y" {(yyval.pval) = P_BUILD("fof_binary_nonassoc", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 140: /* Line 1464 of yacc.c */ #line 501 "tptp5.y" {(yyval.pval) = P_BUILD("fof_binary_assoc", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 141: /* Line 1464 of yacc.c */ #line 502 "tptp5.y" {(yyval.pval) = P_BUILD("fof_binary_assoc", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 142: /* Line 1464 of yacc.c */ #line 505 "tptp5.y" {(yyval.pval) = P_BUILD("fof_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 143: /* Line 1464 of yacc.c */ #line 506 "tptp5.y" {(yyval.pval) = P_BUILD("fof_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 144: /* Line 1464 of yacc.c */ #line 509 "tptp5.y" {(yyval.pval) = P_BUILD("fof_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 145: /* Line 1464 of yacc.c */ #line 510 "tptp5.y" {(yyval.pval) = P_BUILD("fof_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 146: /* Line 1464 of yacc.c */ #line 513 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 147: /* Line 1464 of yacc.c */ #line 514 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 148: /* Line 1464 of yacc.c */ #line 515 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 149: /* Line 1464 of yacc.c */ #line 516 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 150: /* Line 1464 of yacc.c */ #line 517 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 151: /* Line 1464 of yacc.c */ #line 518 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 152: /* Line 1464 of yacc.c */ #line 519 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unitary_formula", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 153: /* Line 1464 of yacc.c */ #line 522 "tptp5.y" {(yyval.pval) = P_BUILD("fof_quantified_formula", (yyvsp[(1) - (6)].pval), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} break; case 154: /* Line 1464 of yacc.c */ #line 525 "tptp5.y" {(yyval.pval) = P_BUILD("fof_variable_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 155: /* Line 1464 of yacc.c */ #line 526 "tptp5.y" {(yyval.pval) = P_BUILD("fof_variable_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 156: /* Line 1464 of yacc.c */ #line 529 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unary_formula", (yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 157: /* Line 1464 of yacc.c */ #line 530 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 158: /* Line 1464 of yacc.c */ #line 533 "tptp5.y" {(yyval.pval) = P_BUILD("fof_let", P_TOKEN("COLON_EQUALS ", (yyvsp[(1) - (6)].ival)), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} break; case 159: /* Line 1464 of yacc.c */ #line 536 "tptp5.y" {(yyval.pval) = P_BUILD("fof_let_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 160: /* Line 1464 of yacc.c */ #line 537 "tptp5.y" {(yyval.pval) = P_BUILD("fof_let_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 161: /* Line 1464 of yacc.c */ #line 540 "tptp5.y" {(yyval.pval) = P_BUILD("fof_defined_var", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON_EQUALS ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 162: /* Line 1464 of yacc.c */ #line 541 "tptp5.y" {(yyval.pval) = P_BUILD("fof_defined_var", (yyvsp[(1) - (4)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (4)].ival)), P_TOKEN("MINUS ", (yyvsp[(3) - (4)].ival)), (yyvsp[(4) - (4)].pval),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 163: /* Line 1464 of yacc.c */ #line 542 "tptp5.y" {(yyval.pval) = P_BUILD("fof_defined_var", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 164: /* Line 1464 of yacc.c */ #line 545 "tptp5.y" {(yyval.pval) = P_BUILD("fof_conditional", P_TOKEN("_DLR_itef ", (yyvsp[(1) - (8)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (8)].ival)), (yyvsp[(3) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (8)].ival)), (yyvsp[(5) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (8)].ival)), (yyvsp[(7) - (8)].pval), P_TOKEN("RPAREN ", (yyvsp[(8) - (8)].ival)),NULL,NULL);;} break; case 165: /* Line 1464 of yacc.c */ #line 548 "tptp5.y" {(yyval.pval) = P_BUILD("fof_sequent", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 166: /* Line 1464 of yacc.c */ #line 549 "tptp5.y" {(yyval.pval) = P_BUILD("fof_sequent", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 167: /* Line 1464 of yacc.c */ #line 552 "tptp5.y" {(yyval.pval) = P_BUILD("cnf_formula", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 168: /* Line 1464 of yacc.c */ #line 553 "tptp5.y" {(yyval.pval) = P_BUILD("cnf_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 169: /* Line 1464 of yacc.c */ #line 556 "tptp5.y" {(yyval.pval) = P_BUILD("disjunction", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 170: /* Line 1464 of yacc.c */ #line 557 "tptp5.y" {(yyval.pval) = P_BUILD("disjunction", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 171: /* Line 1464 of yacc.c */ #line 560 "tptp5.y" {(yyval.pval) = P_BUILD("literal", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 172: /* Line 1464 of yacc.c */ #line 561 "tptp5.y" {(yyval.pval) = P_BUILD("literal", P_TOKEN("TILDE ", (yyvsp[(1) - (2)].ival)), (yyvsp[(2) - (2)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 173: /* Line 1464 of yacc.c */ #line 562 "tptp5.y" {(yyval.pval) = P_BUILD("literal", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 174: /* Line 1464 of yacc.c */ #line 565 "tptp5.y" {(yyval.pval) = P_BUILD("thf_conn_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 175: /* Line 1464 of yacc.c */ #line 566 "tptp5.y" {(yyval.pval) = P_BUILD("thf_conn_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 176: /* Line 1464 of yacc.c */ #line 567 "tptp5.y" {(yyval.pval) = P_BUILD("thf_conn_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 177: /* Line 1464 of yacc.c */ #line 570 "tptp5.y" {(yyval.pval) = P_BUILD("fol_infix_unary", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 178: /* Line 1464 of yacc.c */ #line 573 "tptp5.y" {(yyval.pval) = P_BUILD("thf_quantifier", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 179: /* Line 1464 of yacc.c */ #line 574 "tptp5.y" {(yyval.pval) = P_BUILD("thf_quantifier", P_TOKEN("CARET ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 180: /* Line 1464 of yacc.c */ #line 575 "tptp5.y" {(yyval.pval) = P_BUILD("thf_quantifier", P_TOKEN("EXCLAMATION_GREATER ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 181: /* Line 1464 of yacc.c */ #line 576 "tptp5.y" {(yyval.pval) = P_BUILD("thf_quantifier", P_TOKEN("QUESTION_STAR ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 182: /* Line 1464 of yacc.c */ #line 577 "tptp5.y" {(yyval.pval) = P_BUILD("thf_quantifier", P_TOKEN("AT_SIGN_PLUS ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 183: /* Line 1464 of yacc.c */ #line 578 "tptp5.y" {(yyval.pval) = P_BUILD("thf_quantifier", P_TOKEN("AT_SIGN_MINUS ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 184: /* Line 1464 of yacc.c */ #line 581 "tptp5.y" {(yyval.pval) = P_BUILD("thf_pair_connective", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 185: /* Line 1464 of yacc.c */ #line 582 "tptp5.y" {(yyval.pval) = P_BUILD("thf_pair_connective", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 186: /* Line 1464 of yacc.c */ #line 583 "tptp5.y" {(yyval.pval) = P_BUILD("thf_pair_connective", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 187: /* Line 1464 of yacc.c */ #line 586 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unary_connective", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 188: /* Line 1464 of yacc.c */ #line 587 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unary_connective", P_TOKEN("EXCLAMATION_EXCLAMATION ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 189: /* Line 1464 of yacc.c */ #line 588 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unary_connective", P_TOKEN("QUESTION_QUESTION ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 190: /* Line 1464 of yacc.c */ #line 591 "tptp5.y" {(yyval.pval) = P_BUILD("subtype_sign", P_TOKEN("less_sign ", (yyvsp[(1) - (2)].ival)), P_TOKEN("less_sign ", (yyvsp[(2) - (2)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 191: /* Line 1464 of yacc.c */ #line 594 "tptp5.y" {(yyval.pval) = P_BUILD("fol_quantifier", P_TOKEN("EXCLAMATION ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 192: /* Line 1464 of yacc.c */ #line 595 "tptp5.y" {(yyval.pval) = P_BUILD("fol_quantifier", P_TOKEN("QUESTION ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 193: /* Line 1464 of yacc.c */ #line 598 "tptp5.y" {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("LESS_EQUALS_GREATER ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 194: /* Line 1464 of yacc.c */ #line 599 "tptp5.y" {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("EQUALS_GREATER ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 195: /* Line 1464 of yacc.c */ #line 600 "tptp5.y" {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("LESS_EQUALS ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 196: /* Line 1464 of yacc.c */ #line 601 "tptp5.y" {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("LESS_TILDE_GREATER ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 197: /* Line 1464 of yacc.c */ #line 602 "tptp5.y" {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("TILDE_VLINE ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 198: /* Line 1464 of yacc.c */ #line 603 "tptp5.y" {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("TILDE_AMPERSAND ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 199: /* Line 1464 of yacc.c */ #line 606 "tptp5.y" {(yyval.pval) = P_BUILD("assoc_connective", P_TOKEN("VLINE ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 200: /* Line 1464 of yacc.c */ #line 607 "tptp5.y" {(yyval.pval) = P_BUILD("assoc_connective", P_TOKEN("AMPERSAND ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 201: /* Line 1464 of yacc.c */ #line 610 "tptp5.y" {(yyval.pval) = P_BUILD("unary_connective", P_TOKEN("TILDE ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 202: /* Line 1464 of yacc.c */ #line 613 "tptp5.y" {(yyval.pval) = P_BUILD("gentzen_arrow", P_TOKEN("MINUS_MINUS_GREATER ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 203: /* Line 1464 of yacc.c */ #line 616 "tptp5.y" {(yyval.pval) = P_BUILD("defined_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 204: /* Line 1464 of yacc.c */ #line 619 "tptp5.y" {(yyval.pval) = P_BUILD("atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 205: /* Line 1464 of yacc.c */ #line 620 "tptp5.y" {(yyval.pval) = P_BUILD("atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 206: /* Line 1464 of yacc.c */ #line 621 "tptp5.y" {(yyval.pval) = P_BUILD("atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 207: /* Line 1464 of yacc.c */ #line 624 "tptp5.y" {(yyval.pval) = P_BUILD("plain_atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 208: /* Line 1464 of yacc.c */ #line 627 "tptp5.y" {(yyval.pval) = P_BUILD("defined_atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 209: /* Line 1464 of yacc.c */ #line 628 "tptp5.y" {(yyval.pval) = P_BUILD("defined_atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 210: /* Line 1464 of yacc.c */ #line 631 "tptp5.y" {(yyval.pval) = P_BUILD("defined_plain_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 211: /* Line 1464 of yacc.c */ #line 634 "tptp5.y" {(yyval.pval) = P_BUILD("defined_infix_formula", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 212: /* Line 1464 of yacc.c */ #line 637 "tptp5.y" {(yyval.pval) = P_BUILD("defined_infix_pred", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 213: /* Line 1464 of yacc.c */ #line 640 "tptp5.y" {(yyval.pval) = P_BUILD("infix_equality", P_TOKEN("EQUALS ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 214: /* Line 1464 of yacc.c */ #line 643 "tptp5.y" {(yyval.pval) = P_BUILD("infix_inequality", P_TOKEN("EXCLAMATION_EQUALS ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 215: /* Line 1464 of yacc.c */ #line 646 "tptp5.y" {(yyval.pval) = P_BUILD("system_atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 216: /* Line 1464 of yacc.c */ #line 649 "tptp5.y" {(yyval.pval) = P_BUILD("term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 217: /* Line 1464 of yacc.c */ #line 650 "tptp5.y" {(yyval.pval) = P_BUILD("term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 218: /* Line 1464 of yacc.c */ #line 651 "tptp5.y" {(yyval.pval) = P_BUILD("term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 219: /* Line 1464 of yacc.c */ #line 654 "tptp5.y" {(yyval.pval) = P_BUILD("function_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 220: /* Line 1464 of yacc.c */ #line 655 "tptp5.y" {(yyval.pval) = P_BUILD("function_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 221: /* Line 1464 of yacc.c */ #line 656 "tptp5.y" {(yyval.pval) = P_BUILD("function_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 222: /* Line 1464 of yacc.c */ #line 659 "tptp5.y" {(yyval.pval) = P_BUILD("plain_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 223: /* Line 1464 of yacc.c */ #line 660 "tptp5.y" {(yyval.pval) = P_BUILD("plain_term", (yyvsp[(1) - (4)].pval), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 224: /* Line 1464 of yacc.c */ #line 663 "tptp5.y" {(yyval.pval) = P_BUILD("constant", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 225: /* Line 1464 of yacc.c */ #line 666 "tptp5.y" {(yyval.pval) = P_BUILD("functor", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 226: /* Line 1464 of yacc.c */ #line 669 "tptp5.y" {(yyval.pval) = P_BUILD("defined_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 227: /* Line 1464 of yacc.c */ #line 670 "tptp5.y" {(yyval.pval) = P_BUILD("defined_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 228: /* Line 1464 of yacc.c */ #line 673 "tptp5.y" {(yyval.pval) = P_BUILD("defined_atom", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 229: /* Line 1464 of yacc.c */ #line 674 "tptp5.y" {(yyval.pval) = P_BUILD("defined_atom", P_TOKEN("distinct_object ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 230: /* Line 1464 of yacc.c */ #line 677 "tptp5.y" {(yyval.pval) = P_BUILD("defined_atomic_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 231: /* Line 1464 of yacc.c */ #line 680 "tptp5.y" {(yyval.pval) = P_BUILD("defined_plain_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 232: /* Line 1464 of yacc.c */ #line 681 "tptp5.y" {(yyval.pval) = P_BUILD("defined_plain_term", (yyvsp[(1) - (4)].pval), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 233: /* Line 1464 of yacc.c */ #line 684 "tptp5.y" {(yyval.pval) = P_BUILD("defined_constant", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 234: /* Line 1464 of yacc.c */ #line 687 "tptp5.y" {(yyval.pval) = P_BUILD("defined_functor", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 235: /* Line 1464 of yacc.c */ #line 690 "tptp5.y" {(yyval.pval) = P_BUILD("system_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 236: /* Line 1464 of yacc.c */ #line 691 "tptp5.y" {(yyval.pval) = P_BUILD("system_term", (yyvsp[(1) - (4)].pval), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 237: /* Line 1464 of yacc.c */ #line 694 "tptp5.y" {(yyval.pval) = P_BUILD("system_constant", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 238: /* Line 1464 of yacc.c */ #line 697 "tptp5.y" {(yyval.pval) = P_BUILD("system_functor", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 239: /* Line 1464 of yacc.c */ #line 700 "tptp5.y" {(yyval.pval) = P_BUILD("variable", P_TOKEN("upper_word ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 240: /* Line 1464 of yacc.c */ #line 703 "tptp5.y" {(yyval.pval) = P_BUILD("arguments", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 241: /* Line 1464 of yacc.c */ #line 704 "tptp5.y" {(yyval.pval) = P_BUILD("arguments", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 242: /* Line 1464 of yacc.c */ #line 707 "tptp5.y" {(yyval.pval) = P_BUILD("conditional_term", P_TOKEN("_DLR_itett ", (yyvsp[(1) - (8)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (8)].ival)), (yyvsp[(3) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (8)].ival)), (yyvsp[(5) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (8)].ival)), (yyvsp[(7) - (8)].pval), P_TOKEN("RPAREN ", (yyvsp[(8) - (8)].ival)),NULL,NULL);;} break; case 243: /* Line 1464 of yacc.c */ #line 708 "tptp5.y" {(yyval.pval) = P_BUILD("conditional_term", P_TOKEN("_DLR_itetf ", (yyvsp[(1) - (8)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (8)].ival)), (yyvsp[(3) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (8)].ival)), (yyvsp[(5) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (8)].ival)), (yyvsp[(7) - (8)].pval), P_TOKEN("RPAREN ", (yyvsp[(8) - (8)].ival)),NULL,NULL);;} break; case 244: /* Line 1464 of yacc.c */ #line 711 "tptp5.y" {(yyval.pval) = P_BUILD("source", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 245: /* Line 1464 of yacc.c */ #line 714 "tptp5.y" {(yyval.pval) = P_BUILD("optional_info", P_TOKEN("COMMA ", (yyvsp[(1) - (2)].ival)), (yyvsp[(2) - (2)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 246: /* Line 1464 of yacc.c */ #line 715 "tptp5.y" {(yyval.pval) = P_BUILD("optional_info", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 247: /* Line 1464 of yacc.c */ #line 718 "tptp5.y" {(yyval.pval) = P_BUILD("useful_info", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 248: /* Line 1464 of yacc.c */ #line 721 "tptp5.y" {(yyval.pval) = P_BUILD("include", P_TOKEN("_LIT_include ", (yyvsp[(1) - (6)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), (yyvsp[(4) - (6)].pval), P_TOKEN("RPAREN ", (yyvsp[(5) - (6)].ival)), P_TOKEN("PERIOD ", (yyvsp[(6) - (6)].ival)),NULL,NULL,NULL,NULL);;} break; case 249: /* Line 1464 of yacc.c */ #line 724 "tptp5.y" {(yyval.pval) = P_BUILD("formula_selection", P_TOKEN("COMMA ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LBRKT ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 250: /* Line 1464 of yacc.c */ #line 725 "tptp5.y" {(yyval.pval) = P_BUILD("formula_selection", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 251: /* Line 1464 of yacc.c */ #line 728 "tptp5.y" {(yyval.pval) = P_BUILD("name_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 252: /* Line 1464 of yacc.c */ #line 729 "tptp5.y" {(yyval.pval) = P_BUILD("name_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 253: /* Line 1464 of yacc.c */ #line 732 "tptp5.y" {(yyval.pval) = P_BUILD("general_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 254: /* Line 1464 of yacc.c */ #line 733 "tptp5.y" {(yyval.pval) = P_BUILD("general_term", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 255: /* Line 1464 of yacc.c */ #line 734 "tptp5.y" {(yyval.pval) = P_BUILD("general_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 256: /* Line 1464 of yacc.c */ #line 737 "tptp5.y" {(yyval.pval) = P_BUILD("general_data", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 257: /* Line 1464 of yacc.c */ #line 738 "tptp5.y" {(yyval.pval) = P_BUILD("general_data", (yyvsp[(1) - (4)].pval), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 258: /* Line 1464 of yacc.c */ #line 739 "tptp5.y" {(yyval.pval) = P_BUILD("general_data", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 259: /* Line 1464 of yacc.c */ #line 740 "tptp5.y" {(yyval.pval) = P_BUILD("general_data", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 260: /* Line 1464 of yacc.c */ #line 741 "tptp5.y" {(yyval.pval) = P_BUILD("general_data", P_TOKEN("distinct_object ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 261: /* Line 1464 of yacc.c */ #line 742 "tptp5.y" {(yyval.pval) = P_BUILD("general_data", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 262: /* Line 1464 of yacc.c */ #line 745 "tptp5.y" {(yyval.pval) = P_BUILD("formula_data", P_TOKEN("_DLR_thf ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 263: /* Line 1464 of yacc.c */ #line 746 "tptp5.y" {(yyval.pval) = P_BUILD("formula_data", P_TOKEN("_DLR_tff ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 264: /* Line 1464 of yacc.c */ #line 747 "tptp5.y" {(yyval.pval) = P_BUILD("formula_data", P_TOKEN("_DLR_fof ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 265: /* Line 1464 of yacc.c */ #line 748 "tptp5.y" {(yyval.pval) = P_BUILD("formula_data", P_TOKEN("_DLR_cnf ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 266: /* Line 1464 of yacc.c */ #line 749 "tptp5.y" {(yyval.pval) = P_BUILD("formula_data", P_TOKEN("_DLR_fot ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 267: /* Line 1464 of yacc.c */ #line 752 "tptp5.y" {(yyval.pval) = P_BUILD("general_list", P_TOKEN("LBRKT ", (yyvsp[(1) - (2)].ival)), P_TOKEN("RBRKT ", (yyvsp[(2) - (2)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 268: /* Line 1464 of yacc.c */ #line 753 "tptp5.y" {(yyval.pval) = P_BUILD("general_list", P_TOKEN("LBRKT ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RBRKT ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 269: /* Line 1464 of yacc.c */ #line 756 "tptp5.y" {(yyval.pval) = P_BUILD("general_terms", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 270: /* Line 1464 of yacc.c */ #line 757 "tptp5.y" {(yyval.pval) = P_BUILD("general_terms", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 271: /* Line 1464 of yacc.c */ #line 760 "tptp5.y" {(yyval.pval) = P_BUILD("name", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 272: /* Line 1464 of yacc.c */ #line 761 "tptp5.y" {(yyval.pval) = P_BUILD("name", P_TOKEN("integer ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 273: /* Line 1464 of yacc.c */ #line 764 "tptp5.y" {(yyval.pval) = P_BUILD("atomic_word", P_TOKEN("lower_word ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 274: /* Line 1464 of yacc.c */ #line 765 "tptp5.y" {(yyval.pval) = P_BUILD("atomic_word", P_TOKEN("single_quoted ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 275: /* Line 1464 of yacc.c */ #line 768 "tptp5.y" {(yyval.pval) = P_BUILD("atomic_defined_word", P_TOKEN("dollar_word ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 276: /* Line 1464 of yacc.c */ #line 771 "tptp5.y" {(yyval.pval) = P_BUILD("atomic_system_word", P_TOKEN("dollar_dollar_word ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 277: /* Line 1464 of yacc.c */ #line 774 "tptp5.y" {(yyval.pval) = P_BUILD("number", P_TOKEN("integer ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 278: /* Line 1464 of yacc.c */ #line 775 "tptp5.y" {(yyval.pval) = P_BUILD("number", P_TOKEN("rational ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 279: /* Line 1464 of yacc.c */ #line 776 "tptp5.y" {(yyval.pval) = P_BUILD("number", P_TOKEN("real ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 280: /* Line 1464 of yacc.c */ #line 779 "tptp5.y" {(yyval.pval) = P_BUILD("file_name", P_TOKEN("single_quoted ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 281: /* Line 1464 of yacc.c */ #line 782 "tptp5.y" {(yyval.pval) = P_BUILD("null",NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; /* Line 1464 of yacc.c */ #line 4264 "tptp5.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*------------------------------------. | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else { YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) { YYSIZE_T yyalloc = 2 * yysize; if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) yyalloc = YYSTACK_ALLOC_MAXIMUM; if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yyalloc); if (yymsg) yymsg_alloc = yyalloc; else { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; } } if (0 < yysize && yysize <= yymsg_alloc) { (void) yysyntax_error (yymsg, yystate, yychar); yyerror (yymsg); } else { yyerror (YY_("syntax error")); if (yysize != 0) goto yyexhaustedlab; } } #endif } if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; /* Do not reclaim the symbols of the rule which action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (yyn != YYPACT_NINF) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct ("Error: popping", yystos[yystate], yyvsp); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } *++yyvsp = yylval; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #if !defined(yyoverflow) || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEMPTY) yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval); /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif /* Make sure YYID is used. */ return YYID (yyresult); } z3-4.4.0/examples/tptp/README0000644000175000017500000000116212540347414015332 0ustar michaelmichaelTPTP front-end and utilities as a sample using the C++ bindings. To build the example execute make examples in the build directory. This command will create the executable tptp. On Windows, you can just execute it. On OSX and Linux, you must install z3 first using sudo make install OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (OSX) with the build directory. You need that to be able to find the Z3 shared library. The sample illustrates using Z3 from the TPTP language. The TPTP language is documented on http://tptp.org It also exposes utilities for converting between SMT-LIB and TPTP format. z3-4.4.0/examples/tptp/tptp5.cpp0000644000175000017500000024000612540347414016234 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "z3++.h" struct alloc_region { std::list m_alloc; void * allocate(size_t s) { char * res = new char[s]; m_alloc.push_back(res); return res; } ~alloc_region() { std::list::iterator it = m_alloc.begin(), end = m_alloc.end(); for (; it != end; ++it) { delete *it; } } }; template class flet { T & m_ref; T m_old; public: flet(T& x, T const& y): m_ref(x), m_old(x) { x = y; } ~flet() { m_ref = m_old; } }; struct symbol_compare { bool operator()(z3::symbol const& s1, z3::symbol const& s2) const { return s1 < s2; }; }; template struct symbol_table { typedef std::map map; map m_map; void insert(z3::symbol s, T val) { m_map.insert(std::pair(s, val)); } bool find(z3::symbol const& s, T& val) { typename map::iterator it = m_map.find(s); if (it == m_map.end()) { return false; } else { val = it->second; return true; } } }; typedef std::set symbol_set; struct named_formulas { std::vector m_formulas; std::vector m_names; std::vector m_files; bool m_has_conjecture; named_formulas(): m_has_conjecture(false) {} void push_back(z3::expr fml, char const * name, char const* file) { m_formulas.push_back(fml); m_names.push_back(name); m_files.push_back(file); } void set_has_conjecture() { m_has_conjecture = true; } bool has_conjecture() const { return m_has_conjecture; } }; inline void * operator new(size_t s, alloc_region & r) { return r.allocate(s); } inline void * operator new[](size_t s, alloc_region & r) { return r.allocate(s); } inline void operator delete(void *, alloc_region & ) { /* do nothing */ } inline void operator delete[](void *, alloc_region & ) { /* do nothing */ } struct failure_ex { std::string msg; failure_ex(char const* m):msg(m) {} }; extern char* tptp_lval[]; extern int yylex(); static char* strdup(alloc_region& r, char const* s) { size_t l = strlen(s) + 1; char* result = new (r) char[l]; memcpy(result, s, l); return result; } class TreeNode { char const* m_symbol; int m_symbol_index; TreeNode** m_children; public: TreeNode(alloc_region& r, char const* sym, TreeNode* A, TreeNode* B, TreeNode* C, TreeNode* D, TreeNode* E, TreeNode* F, TreeNode* G, TreeNode* H, TreeNode* I, TreeNode* J): m_symbol(strdup(r, sym)), m_symbol_index(-1) { m_children = new (r) TreeNode*[10]; m_children[0] = A; m_children[1] = B; m_children[2] = C; m_children[3] = D; m_children[4] = E; m_children[5] = F; m_children[6] = G; m_children[7] = H; m_children[8] = I; m_children[9] = J; } char const* symbol() const { return m_symbol; } TreeNode *const* children() const { return m_children; } TreeNode* child(unsigned i) const { return m_children[i]; } int index() const { return m_symbol_index; } void set_index(int idx) { m_symbol_index = idx; } }; TreeNode* MkToken(alloc_region& r, char const* token, int symbolIndex) { TreeNode* ss; char* symbol = tptp_lval[symbolIndex]; ss = new (r) TreeNode(r, symbol, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); ss->set_index(symbolIndex); return ss; } // ------------------------------------------------------ // Build Z3 formulas. class env { z3::context& m_context; z3::expr_vector m_bound; // vector of bound constants. z3::sort m_univ; symbol_table m_decls; symbol_table m_defined_sorts; static std::vector* m_nodes; static alloc_region* m_region; char const* m_filename; enum binary_connective { IFF, IMPLIES, IMPLIED, LESS_TILDE_GREATER, TILDE_VLINE }; void mk_error(TreeNode* f, char const* msg) { std::ostringstream strm; strm << "expected: " << msg << "\n"; strm << "got: " << f->symbol(); throw failure_ex(strm.str().c_str()); } void mk_not_handled(TreeNode* f, char const* msg) { std::ostringstream strm; strm << "Construct " << f->symbol() << " not handled: " << msg; throw failure_ex(strm.str().c_str()); } void mk_input(TreeNode* f, named_formulas& fmls) { if (!strcmp(f->symbol(),"annotated_formula")) { mk_annotated_formula(f->child(0), fmls); } else if (!strcmp(f->symbol(),"include")) { mk_include(f->child(2), f->child(3), fmls); } else { mk_error(f, "annotated formula or include"); } } void mk_annotated_formula(TreeNode* f, named_formulas& fmls) { if (!strcmp(f->symbol(),"fof_annotated")) { fof_annotated(f->child(2), f->child(4), f->child(6), f->child(7), fmls); } else if (!strcmp(f->symbol(),"tff_annotated")) { fof_annotated(f->child(2), f->child(4), f->child(6), f->child(7), fmls); } else if (!strcmp(f->symbol(),"cnf_annotated")) { cnf_annotated(f->child(2), f->child(4), f->child(6), f->child(7), fmls); } else if (!strcmp(f->symbol(),"thf_annotated")) { mk_error(f, "annotated formula (not thf)"); } else { mk_error(f, "annotated formula"); } } void check_arity(unsigned num_args, unsigned arity) { if (num_args != arity) { throw failure_ex("arity missmatch"); } } void mk_include(TreeNode* file_name, TreeNode* formula_selection, named_formulas& fmls) { char const* fn = file_name->child(0)->symbol(); TreeNode* name_list = formula_selection->child(2); if (name_list && !strcmp("null",name_list->symbol())) { name_list = 0; } std::string inc_name; bool f_exists = false; for (unsigned i = 1; !f_exists && i <= 3; ++i) { inc_name.clear(); f_exists = mk_filename(fn, i, inc_name); } if (!f_exists) { inc_name.clear(); f_exists = mk_env_filename(fn, inc_name); } if (!f_exists) { inc_name = fn; } parse(inc_name.c_str(), fmls); while (name_list) { return mk_error(name_list, "name list (not handled)"); char const* name = name_list->child(0)->symbol(); name_list = name_list->child(2); } } #define CHECK(_node_) if (0 != strcmp(_node_->symbol(),#_node_)) return mk_error(_node_,#_node_); const char* get_name(TreeNode* name) { if (!name->child(0)) { mk_error(name, "node with a child"); } if (!name->child(0)->child(0)) { return name->child(0)->symbol(); } return name->child(0)->child(0)->symbol(); } z3::expr mk_forall(z3::expr_vector& bound, z3::expr body) { return mk_quantifier(true, bound, body); } z3::expr mk_quantifier(bool is_forall, z3::expr_vector& bound, z3::expr body) { Z3_app* vars = new Z3_app[bound.size()]; for (unsigned i = 0; i < bound.size(); ++i) { vars[i] = (Z3_app) bound[i]; } Z3_ast r = Z3_mk_quantifier_const(m_context, is_forall, 1, bound.size(), vars, 0, 0, body); delete[] vars; return z3::expr(m_context, r); } void cnf_annotated(TreeNode* name, TreeNode* formula_role, TreeNode* formula, TreeNode* annotations, named_formulas& fmls) { symbol_set st; get_cnf_variables(formula, st); symbol_set::iterator it = st.begin(), end = st.end(); std::vector names; m_bound.resize(0); for(; it != end; ++it) { names.push_back(*it); m_bound.push_back(m_context.constant(names.back(), m_univ)); } z3::expr r(m_context); cnf_formula(formula, r); if (!m_bound.empty()) { r = mk_forall(m_bound, r); } char const* role = formula_role->child(0)->symbol(); if (!strcmp(role,"conjecture")) { fmls.set_has_conjecture(); r = !r; } fmls.push_back(r, get_name(name), m_filename); m_bound.resize(0); } void cnf_formula(TreeNode* formula, z3::expr& r) { std::vector disj; if (formula->child(1)) { disjunction(formula->child(1), disj); } else { disjunction(formula->child(0), disj); } if (disj.size() > 0) { r = disj[0]; } else { r = m_context.bool_val(false); } for (unsigned i = 1; i < disj.size(); ++i) { r = r || disj[i]; } } void disjunction(TreeNode* d, std::vector& r) { z3::expr lit(m_context); if (d->child(2)) { disjunction(d->child(0), r); literal(d->child(2), lit); r.push_back(lit); } else { literal(d->child(0), lit); r.push_back(lit); } } void literal(TreeNode* l, z3::expr& lit) { if (!strcmp(l->child(0)->symbol(),"~")) { fof_formula(l->child(1), lit); lit = !lit; } else { fof_formula(l->child(0), lit); } } void fof_annotated(TreeNode* name, TreeNode* formula_role, TreeNode* formula, TreeNode* annotations, named_formulas& fmls) { z3::expr fml(m_context); //CHECK(fof_formula); CHECK(formula_role); fof_formula(formula->child(0), fml); char const* role = formula_role->child(0)->symbol(); if (!strcmp(role,"conjecture")) { fmls.set_has_conjecture(); fmls.push_back(!fml, get_name(name), m_filename); } else if (!strcmp(role,"type")) { } else { fmls.push_back(fml, get_name(name), m_filename); } } void fof_formula(TreeNode* f, z3::expr& fml) { z3::expr f1(m_context); char const* name = f->symbol(); if (!strcmp(name,"fof_logic_formula") || !strcmp(name,"fof_binary_assoc") || !strcmp(name,"fof_binary_formula") || !strcmp(name,"tff_logic_formula") || !strcmp(name,"tff_binary_assoc") || !strcmp(name,"tff_binary_formula") || !strcmp(name,"atomic_formula") || !strcmp(name,"defined_atomic_formula")) { fof_formula(f->child(0), fml); } else if (!strcmp(name, "fof_sequent") || !strcmp(name, "tff_sequent")) { fof_formula(f->child(0), f1); fof_formula(f->child(2), fml); fml = implies(f1, fml); } else if (!strcmp(name, "fof_binary_nonassoc") || !strcmp(name, "tff_binary_nonassoc")) { fof_formula(f->child(0), f1); fof_formula(f->child(2), fml); //SASSERT(!strcmp("binary_connective",f->child(1)->symbol())); char const* conn = f->child(1)->child(0)->symbol(); if (!strcmp(conn, "<=>")) { fml = (f1 == fml); } else if (!strcmp(conn, "=>")) { fml = implies(f1, fml); } else if (!strcmp(conn, "<=")) { fml = implies(fml, f1); } else if (!strcmp(conn, "<~>")) { fml = ! (f1 == fml); } else if (!strcmp(conn, "~|")) { fml = !(f1 || fml); } else if (!strcmp(conn, "~&")) { fml = ! (f1 && fml); } else { mk_error(f->child(1)->child(0), "connective"); } } else if (!strcmp(name,"fof_or_formula") || !strcmp(name,"tff_or_formula")) { fof_formula(f->child(0), f1); fof_formula(f->child(2), fml); fml = f1 || fml; } else if (!strcmp(name,"fof_and_formula") || !strcmp(name,"tff_and_formula")) { fof_formula(f->child(0), f1); fof_formula(f->child(2), fml); fml = f1 && fml; } else if (!strcmp(name,"fof_unitary_formula") || !strcmp(name,"tff_unitary_formula")) { if (f->child(1)) { // parenthesis fof_formula(f->child(1), fml); } else { fof_formula(f->child(0), fml); } } else if (!strcmp(name,"fof_quantified_formula") || !strcmp(name,"tff_quantified_formula")) { fof_quantified_formula(f->child(0), f->child(2), f->child(5), fml); } else if (!strcmp(name,"fof_unary_formula") || !strcmp(name,"tff_unary_formula")) { if (!f->child(1)) { fof_formula(f->child(0), fml); } else { fof_formula(f->child(1), fml); char const* conn = f->child(0)->child(0)->symbol(); if (!strcmp(conn,"~")) { fml = !fml; } else { mk_error(f->child(0)->child(0), "fof_unary_formula"); } } } else if (!strcmp(name,"fof_let")) { mk_let(f->child(2), f->child(5), fml); } else if (!strcmp(name,"variable")) { char const* v = f->child(0)->symbol(); if (!find_bound(v, fml)) { mk_error(f->child(0), "variable"); } } else if (!strcmp(name,"fof_conditional")) { z3::expr f2(m_context); fof_formula(f->child(2), f1); fof_formula(f->child(4), f2); fof_formula(f->child(6), fml); fml = ite(f1, f2, fml); } else if (!strcmp(name,"plain_atomic_formula") || !strcmp(name,"defined_plain_formula") || !strcmp(name,"system_atomic_formula")) { z3::sort srt(m_context.bool_sort()); term(f->child(0), srt, fml); } else if (!strcmp(name,"defined_infix_formula") || !strcmp(name,"fol_infix_unary")) { z3::expr t1(m_context), t2(m_context); term(f->child(0), m_univ, t1); term(f->child(2), m_univ, t2); TreeNode* inf = f->child(1); while (inf && strcmp(inf->symbol(),"=") && strcmp(inf->symbol(),"!=")) { inf = inf->child(0); } if (!inf) { mk_error(f->child(1), "defined_infix_formula"); } char const* conn = inf->symbol(); if (!strcmp(conn,"=")) { fml = t1 == t2; } else if (!strcmp(conn,"!=")) { fml = ! (t1 == t2); } else { mk_error(inf, "defined_infix_formula"); } } else if (!strcmp(name, "tff_typed_atom")) { while (!strcmp(f->child(0)->symbol(),"(")) { f = f->child(1); } char const* id = 0; z3::sort s(m_context); z3::sort_vector sorts(m_context); mk_id(f->child(0), id); if (is_ttype(f->child(2))) { s = mk_sort(id); m_defined_sorts.insert(symbol(id), s); } else { mk_mapping_sort(f->child(2), sorts, s); z3::func_decl fd(m_context.function(id, sorts, s)); m_decls.insert(symbol(id), fd); } } else { mk_error(f, "fof_formula"); } } bool is_ttype(TreeNode* t) { char const* name = t->symbol(); if (!strcmp(name,"atomic_defined_word")) { return !strcmp("$tType", t->child(0)->symbol()); } return false; } void fof_quantified_formula(TreeNode* fol_quantifier, TreeNode* vl, TreeNode* formula, z3::expr& fml) { unsigned l = m_bound.size(); mk_variable_list(vl); fof_formula(formula, fml); bool is_forall = !strcmp(fol_quantifier->child(0)->symbol(),"!"); z3::expr_vector bound(m_context); for (unsigned i = l; i < m_bound.size(); ++i) { bound.push_back(m_bound[i]); } fml = mk_quantifier(is_forall, bound, fml); m_bound.resize(l); } void mk_variable_list(TreeNode* variable_list) { while (variable_list) { TreeNode* var = variable_list->child(0); if (!strcmp(var->symbol(),"tff_variable")) { var = var->child(0); } if (!strcmp(var->symbol(),"variable")) { char const* name = var->child(0)->symbol(); m_bound.push_back(m_context.constant(name, m_univ)); } else if (!strcmp(var->symbol(),"tff_typed_variable")) { z3::sort s(m_context); char const* name = var->child(0)->child(0)->symbol(); mk_sort(var->child(2), s); m_bound.push_back(m_context.constant(name, s)); } else { mk_error(var, "variable_list"); } variable_list = variable_list->child(2); } } void mk_sort(TreeNode* t, z3::sort& s) { char const* name = t->symbol(); if (!strcmp(name, "tff_atomic_type") || !strcmp(name, "defined_type")) { mk_sort(t->child(0), s); } else if (!strcmp(name, "atomic_defined_word")) { z3::symbol sname = symbol(t->child(0)->symbol()); z3::sort srt(m_context); if (!strcmp("$tType", t->child(0)->symbol())) { char const* id = 0; s = mk_sort(id); m_defined_sorts.insert(symbol(id), s); } else if (m_defined_sorts.find(sname, srt)) { s = srt; } else { s = mk_sort(sname); if (sname == symbol("$rat")) { throw failure_ex("rational sorts are not handled\n"); } mk_error(t, sname.str().c_str()); } } else if (!strcmp(name,"atomic_word")) { name = t->child(0)->symbol(); z3::symbol symname = symbol(name); s = mk_sort(symname); } else { mk_error(t, "sort"); } } void mk_mapping_sort(TreeNode* t, z3::sort_vector& domain, z3::sort& s) { char const* name = t->symbol(); char const* id = 0; if (!strcmp(name,"tff_top_level_type")) { mk_mapping_sort(t->child(0), domain, s); } else if (!strcmp(name,"tff_atomic_type")) { mk_sort(t->child(0), s); } else if (!strcmp(name,"tff_mapping_type")) { TreeNode* t1 = t->child(0); if (t1->child(1)) { mk_xprod_sort(t1->child(1), domain); } else { mk_sort(t1->child(0), s); domain.push_back(s); } mk_sort(t->child(2), s); } else { mk_error(t, "mapping sort"); } } void mk_xprod_sort(TreeNode* t, z3::sort_vector& sorts) { char const* name = t->symbol(); z3::sort s1(m_context), s2(m_context); if (!strcmp(name, "tff_atomic_type")) { mk_sort(t->child(0), s1); sorts.push_back(s1); } else if (!strcmp(name, "tff_xprod_type")) { name = t->child(0)->symbol(); if (!strcmp(name, "tff_atomic_type") || !strcmp(name, "tff_xprod_type")) { mk_xprod_sort(t->child(0), sorts); mk_xprod_sort(t->child(2), sorts); } else if (t->child(1)) { mk_xprod_sort(t->child(1), sorts); } else { mk_error(t, "xprod sort"); } } else { mk_error(t, "xprod sort"); } } void term(TreeNode* t, z3::sort const& s, z3::expr& r) { char const* name = t->symbol(); if (!strcmp(name, "defined_plain_term") || !strcmp(name, "system_term") || !strcmp(name, "plain_term")) { if (!t->child(1)) { term(t->child(0), s, r); } else { apply_term(t->child(0), t->child(2), s, r); } } else if (!strcmp(name, "constant") || !strcmp(name, "functor") || !strcmp(name, "defined_plain_formula") || !strcmp(name, "defined_functor") || !strcmp(name, "defined_constant") || !strcmp(name, "system_constant") || !strcmp(name, "defined_atomic_term") || !strcmp(name, "system_functor") || !strcmp(name, "function_term") || !strcmp(name, "term") || !strcmp(name, "defined_term")) { term(t->child(0), s, r); } else if (!strcmp(name, "defined_atom")) { char const* name0 = t->child(0)->symbol(); if (!strcmp(name0,"number")) { name0 = t->child(0)->child(0)->symbol(); char const* per = strchr(name0, '.'); bool is_real = 0 != per; bool is_rat = 0 != strchr(name0, '/'); bool is_int = !is_real && !is_rat; if (is_int) { r = m_context.int_val(name0); } else { r = m_context.real_val(name0); } } else if (!strcmp(name0, "distinct_object")) { throw failure_ex("distinct object not handled"); } else { mk_error(t->child(0), "number or distinct object"); } } else if (!strcmp(name, "atomic_defined_word")) { char const* ch = t->child(0)->symbol(); z3::symbol s = symbol(ch); z3::func_decl fd(m_context); if (!strcmp(ch, "$true")) { r = m_context.bool_val(true); } else if (!strcmp(ch, "$false")) { r = m_context.bool_val(false); } else if (m_decls.find(s, fd)) { r = fd(0,0); } else { mk_error(t->child(0), "atomic_defined_word"); } } else if (!strcmp(name, "atomic_word")) { z3::func_decl f(m_context); z3::symbol sym = symbol(t->child(0)->symbol()); if (m_decls.find(sym, f)) { r = f(0,0); } else { r = m_context.constant(sym, s); } } else if (!strcmp(name, "variable")) { char const* v = t->child(0)->symbol(); if (!find_bound(v, r)) { mk_error(t->child(0), "variable not bound"); } } else { mk_error(t, "term not recognized"); } } void apply_term(TreeNode* f, TreeNode* args, z3::sort const& s, z3::expr& r) { z3::expr_vector terms(m_context); z3::sort_vector sorts(m_context); mk_args(args, terms); for (unsigned i = 0; i < terms.size(); ++i) { sorts.push_back(terms[i].get_sort()); } if (!strcmp(f->symbol(),"functor") || !strcmp(f->symbol(),"system_functor") || !strcmp(f->symbol(),"defined_functor")) { f = f->child(0); } bool atomic_word = !strcmp(f->symbol(),"atomic_word"); if (atomic_word || !strcmp(f->symbol(),"atomic_defined_word") || !strcmp(f->symbol(),"atomic_system_word")) { char const* ch = f->child(0)->symbol(); z3::symbol fn = symbol(ch); z3::func_decl fun(m_context); z3::context& ctx = r.ctx(); if (!strcmp(ch,"$less")) { check_arity(terms.size(), 2); r = terms[0] < terms[1]; } else if (!strcmp(ch,"$lesseq")) { check_arity(terms.size(), 2); r = terms[0] <= terms[1]; } else if (!strcmp(ch,"$greater")) { check_arity(terms.size(), 2); r = terms[0] > terms[1]; } else if (!strcmp(ch,"$greatereq")) { check_arity(terms.size(), 2); r = terms[0] >= terms[1]; } else if (!strcmp(ch,"$uminus")) { check_arity(terms.size(), 1); r = -terms[0]; } else if (!strcmp(ch,"$sum")) { check_arity(terms.size(), 2); r = terms[0] + terms[1]; } else if (!strcmp(ch,"$plus")) { check_arity(terms.size(), 2); r = terms[0] + terms[1]; } else if (!strcmp(ch,"$difference")) { check_arity(terms.size(), 2); r = terms[0] - terms[1]; } else if (!strcmp(ch,"$product")) { check_arity(terms.size(), 2); r = terms[0] * terms[1]; } else if (!strcmp(ch,"$quotient")) { check_arity(terms.size(), 2); r = terms[0] / terms[1]; } else if (!strcmp(ch,"$quotient_e")) { check_arity(terms.size(), 2); r = terms[0] / terms[1]; } else if (!strcmp(ch,"$distinct")) { if (terms.size() == 2) { r = terms[0] != terms[1]; } else { r = distinct(terms); } } else if (!strcmp(ch,"$floor") || !strcmp(ch,"$to_int")) { check_arity(terms.size(), 1); r = to_real(to_int(terms[0])); } else if (!strcmp(ch,"$to_real")) { check_arity(terms.size(), 1); r = to_real(terms[0]); } else if (!strcmp(ch,"$is_int")) { check_arity(terms.size(), 1); r = z3::expr(ctx, Z3_mk_is_int(ctx, terms[0])); } else if (!strcmp(ch,"$true")) { r = ctx.bool_val(true); } else if (!strcmp(ch,"$false")) { r = ctx.bool_val(false); } // ceiling(x) = -floor(-x) else if (!strcmp(ch,"$ceiling")) { check_arity(terms.size(), 1); r = ceiling(terms[0]); } // truncate - The nearest integral value with magnitude not greater than the absolute value of the argument. // if x >= 0 floor(x) else ceiling(x) else if (!strcmp(ch,"$truncate")) { check_arity(terms.size(), 1); r = truncate(terms[0]); } // The nearest integral number to the argument. When the argument // is halfway between two integral numbers, the nearest even integral number to the argument. else if (!strcmp(ch,"$round")) { check_arity(terms.size(), 1); z3::expr t = terms[0]; z3::expr i = to_int(t); z3::expr i2 = i + ctx.real_val(1,2); r = ite(t > i2, i + 1, ite(t == i2, ite(is_even(i), i, i+1), i)); } // $quotient_e(N,D) - the Euclidean quotient, which has a non-negative remainder. // If D is positive then $quotient_e(N,D) is the floor (in the type of N and D) of // the real division N/D, and if D is negative then $quotient_e(N,D) is the ceiling of N/D. // $quotient_t(N,D) - the truncation of the real division N/D. else if (!strcmp(ch,"$quotient_t")) { check_arity(terms.size(), 2); r = truncate(terms[0] / terms[1]); } // $quotient_f(N,D) - the floor of the real division N/D. else if (!strcmp(ch,"$quotient_f")) { check_arity(terms.size(), 2); r = to_real(to_int(terms[0] / terms[1])); } // For t in {$int,$rat, $real}, x in {e, t,f}, $quotient_x and $remainder_x are related by // ! [N:t,D:t] : $sum($product($quotient_x(N,D),D),$remainder_x(N,D)) = N // For zero divisors the result is not specified. else if (!strcmp(ch,"$remainder_t")) { mk_not_handled(f, ch); } else if (!strcmp(ch,"$remainder_e")) { check_arity(terms.size(), 2); r = z3::expr(ctx, Z3_mk_mod(ctx, terms[0], terms[1])); } else if (!strcmp(ch,"$remainder_r")) { mk_not_handled(f, ch); } else if (!strcmp(ch,"$to_rat") || !strcmp(ch,"$is_rat")) { mk_not_handled(f, ch); } else if (m_decls.find(fn, fun)) { r = fun(terms); } else if (true) { z3::func_decl func(m_context); func = m_context.function(fn, sorts, s); r = func(terms); } else { mk_error(f->child(0), "atomic, defined or system word"); } return; } mk_error(f, "function"); } z3::expr to_int(z3::expr e) { return z3::expr(e.ctx(), Z3_mk_real2int(e.ctx(), e)); } z3::expr to_real(z3::expr e) { return z3::expr(e.ctx(), Z3_mk_int2real(e.ctx(), e)); } z3::expr ceiling(z3::expr e) { return -to_real(to_int(-e)); } z3::expr is_even(z3::expr e) { z3::context& ctx = e.ctx(); z3::expr two = ctx.int_val(2); z3::expr m = z3::expr(ctx, Z3_mk_mod(ctx, e, two)); return m == 0; } z3::expr truncate(z3::expr e) { return ite(e >= 0, to_int(e), ceiling(e)); } bool check_app(z3::func_decl& f, unsigned num, z3::expr const* args) { if (f.arity() == num) { for (unsigned i = 0; i < num; ++i) { if (!eq(args[i].get_sort(), f.domain(i))) { return false; } } return true; } else { return true; } } void mk_args(TreeNode* args, z3::expr_vector& result) { z3::expr t(m_context); while (args) { term(args->child(0), m_univ, t); result.push_back(t); args = args->child(2); } } bool find_bound(char const* v, z3::expr& b) { for (unsigned l = m_bound.size(); l > 0; ) { --l; if (v == m_bound[l].decl().name().str()) { b = m_bound[l]; return true; } } return false; } void mk_id(TreeNode* f, char const*& sym) { char const* name = f->symbol(); if (!strcmp(name, "tff_untyped_atom") || !strcmp(name, "functor") || !strcmp(name, "system_functor")) { mk_id(f->child(0), sym); } else if (!strcmp(name, "atomic_word") || !strcmp(name, "atomic_system_word")) { sym = f->child(0)->symbol(); } else { mk_error(f, "atom"); } } void mk_let(TreeNode* let_vars, TreeNode* f, z3::expr& fml) { mk_error(f, "let construct is not handled"); } FILE* open_file(char const* filename) { FILE* fp = 0; #ifdef _WINDOWS if (0 > fopen_s(&fp, filename, "r") || fp == 0) { fp = 0; } #else fp = fopen(filename, "r"); #endif return fp; } bool is_sep(char s) { return s == '/' || s == '\\'; } void add_separator(const char* rel_name, std::string& inc_name) { size_t sz = inc_name.size(); if (sz == 0) return; if (sz > 0 && is_sep(inc_name[sz-1])) return; if (is_sep(rel_name[0])) return; inc_name += "/"; } void append_rel_name(const char * rel_name, std::string& inc_name) { if (rel_name[0] == '\'') { add_separator(rel_name+1, inc_name); inc_name.append(rel_name+1); inc_name.resize(inc_name.size()-1); } else { add_separator(rel_name, inc_name); inc_name.append(rel_name); } } bool mk_filename(const char *rel_name, unsigned num_sep, std::string& inc_name) { unsigned sep1 = 0, sep2 = 0, sep3 = 0; size_t len = strlen(m_filename); for (unsigned i = 0; i < len; ++i) { if (is_sep(m_filename[i])) { sep3 = sep2; sep2 = sep1; sep1 = i; } } if ((num_sep == 3) && sep3 > 0) { inc_name.append(m_filename,sep3+1); } if ((num_sep == 2) && sep2 > 0) { inc_name.append(m_filename,sep2+1); } if ((num_sep == 1) && sep1 > 0) { inc_name.append(m_filename,sep1+1); } append_rel_name(rel_name, inc_name); return file_exists(inc_name.c_str()); } bool file_exists(char const* filename) { FILE* fp = open_file(filename); if (!fp) { return false; } fclose(fp); return true; } bool mk_env_filename(const char* rel_name, std::string& inc_name) { #ifdef _WINDOWS char buffer[1024]; size_t sz; errno_t err = getenv_s( &sz, buffer, "$TPTP"); if (err != 0) { return false; } #else char const* buffer = getenv("$TPTP"); if (!buffer) { return false; } #endif inc_name = buffer; append_rel_name(rel_name, inc_name); return file_exists(inc_name.c_str()); } void get_cnf_variables(TreeNode* t, symbol_set& symbols) { std::vector todo; todo.push_back(t); while (!todo.empty()) { t = todo.back(); todo.pop_back(); if (!t) continue; if (!strcmp(t->symbol(),"variable")) { z3::symbol sym = symbol(t->child(0)->symbol()); symbols.insert(sym); } else { for (unsigned i = 0; i < 10; ++i) { todo.push_back(t->child(i)); } } } } z3::symbol symbol(char const* s) { return m_context.str_symbol(s); } z3::sort mk_sort(char const* s) { return m_context.uninterpreted_sort(s); } z3::sort mk_sort(z3::symbol& s) { return m_context.uninterpreted_sort(s); } public: env(z3::context& ctx): m_context(ctx), m_bound(ctx), m_univ(mk_sort("$i")), m_filename(0) { m_nodes = 0; m_region = new alloc_region(); m_defined_sorts.insert(symbol("$i"), m_univ); m_defined_sorts.insert(symbol("$o"), m_context.bool_sort()); m_defined_sorts.insert(symbol("$real"), m_context.real_sort()); m_defined_sorts.insert(symbol("$int"), m_context.int_sort()); } ~env() { delete m_region; m_region = 0; } void parse(const char* filename, named_formulas& fmls); static void register_node(TreeNode* t) { m_nodes->push_back(t); } static alloc_region& r() { return *m_region; } }; std::vector* env::m_nodes = 0; alloc_region* env::m_region = 0; # define P_USERPROC # define P_ACT(ss) if(verbose)printf("%7d %s\n",yylineno,ss); # define P_BUILD(sym,A,B,C,D,E,F,G,H,I,J) new (env::r()) TreeNode(env::r(), sym,A,B,C,D,E,F,G,H,I,J) # define P_TOKEN(tok,symbolIndex) MkToken(env::r(), tok,symbolIndex) # define P_PRINT(ss) env::register_node(ss) // ------------------------------------------------------ // created by YACC. #include "tptp5.tab.c" extern FILE* yyin; void env::parse(const char* filename, named_formulas& fmls) { std::vector nodes; flet fn(m_filename, filename); flet*> fnds(m_nodes, &nodes); FILE* fp = open_file(filename); if (!fp) { std::stringstream strm; strm << "Could not open file " << filename << "\n"; throw failure_ex(strm.str().c_str()); } yyin = fp; int result = yyparse(); fclose(fp); if (result != 0) { throw failure_ex("could not parse input"); } for (unsigned i = 0; i < nodes.size(); ++i) { TreeNode* cl = nodes[i]; if (cl) { mk_input(cl, fmls); } } } class pp_tptp { z3::context& ctx; std::vector names; std::vector sorts; std::vector funs; std::vector todo; std::set seen_ids; unsigned m_formula_id; unsigned m_node_number; std::map m_proof_ids; std::map > m_proof_hypotheses; std::map m_axiom_ids; named_formulas* m_named_formulas; public: pp_tptp(z3::context& ctx): ctx(ctx), m_formula_id(0) {} void display_func_decl(std::ostream& out, z3::func_decl& f) { std::string name = lower_case_fun(f.name()); out << "tff(" << name << "_type, type, (\n " << name << ": "; unsigned na = f.arity(); switch(na) { case 0: break; case 1: { z3::sort s(f.domain(0)); display_sort(out, s); out << " > "; break; } default: out << "( "; for (unsigned j = 0; j < na; ++j) { z3::sort s(f.domain(j)); display_sort(out, s); if (j + 1 < na) { out << " * "; } } out << " ) > "; } z3::sort srt(f.range()); display_sort(out, srt); out << ")).\n"; } void display_axiom(std::ostream& out, z3::expr e) { out << "tff(formula" << (++m_formula_id) << ", axiom,\n "; display(out, e); out << ").\n"; } void display(std::ostream& out, z3::expr e) { if (e.is_numeral()) { __int64 num, den; if (Z3_get_numeral_small(ctx, e, &num, &den)) { if (num < 0 && den == 1 && num != std::numeric_limits<__int64>::min()) { out << "-" << (-num); return; } } // potential incompatibility: prints negative numbers with a space. out << e; } else if (e.is_var()) { unsigned idx = Z3_get_index_value(ctx, e); out << names[names.size()-1-idx]; } else if (e.is_app()) { switch(e.decl().decl_kind()) { case Z3_OP_TRUE: out << "$true"; break; case Z3_OP_FALSE: out << "$false"; break; case Z3_OP_AND: display_infix(out, "&", e); break; case Z3_OP_OR: display_infix(out, "|", e); break; case Z3_OP_IMPLIES: display_infix(out, "=>", e); break; case Z3_OP_NOT: out << "(~"; display(out, e.arg(0)); out << ")"; break; case Z3_OP_EQ: if (e.arg(0).is_bool()) { display_infix(out, "<=>", e); } else { display_infix(out, "=", e); } break; case Z3_OP_IFF: display_infix(out, "<=>", e); break; case Z3_OP_XOR: display_infix(out, "<~>", e); break; case Z3_OP_MUL: display_binary(out, "$product", e); break; case Z3_OP_ADD: display_binary(out, "$sum", e); break; case Z3_OP_SUB: display_prefix(out, "$difference", e); break; case Z3_OP_LE: display_prefix(out, "$lesseq", e); break; case Z3_OP_GE: display_prefix(out, "$greatereq", e); break; case Z3_OP_LT: display_prefix(out, "$less", e); break; case Z3_OP_GT: display_prefix(out, "$greater", e); break; case Z3_OP_UMINUS: display_prefix(out, "$uminus", e); break; case Z3_OP_DIV: display_prefix(out, "$quotient", e); break; case Z3_OP_IS_INT: display_prefix(out, "$is_int", e); break; case Z3_OP_TO_REAL: display_prefix(out, "$to_real", e); break; case Z3_OP_TO_INT: display_prefix(out, "$to_int", e); break; case Z3_OP_IDIV: display_prefix(out, "$quotient_e", e); break; case Z3_OP_MOD: display_prefix(out, "$remainder_e", e); break; case Z3_OP_ITE: display_prefix(out, e.is_bool()?"ite_f":"ite_t", e); break; case Z3_OP_DISTINCT: display_prefix(out, "$distinct", e); break; case Z3_OP_REM: throw failure_ex("rem is not handled"); break; case Z3_OP_OEQ: display_prefix(out, "$oeq", e); break; default: display_app(out, e); break; } } else if (e.is_quantifier()) { Z3_bool is_forall = Z3_is_quantifier_forall(ctx, e); unsigned nb = Z3_get_quantifier_num_bound(ctx, e); out << (is_forall?"!":"?") << "["; for (unsigned i = 0; i < nb; ++i) { Z3_symbol n = Z3_get_quantifier_bound_name(ctx, e, i); names.push_back(upper_case_var(z3::symbol(ctx, n))); z3::sort srt(ctx, Z3_get_quantifier_bound_sort(ctx, e, i)); out << names.back() << ": "; display_sort(out, srt); if (i + 1 < nb) { out << ", "; } } out << "] : "; display(out, e.body()); for (unsigned i = 0; i < nb; ++i) { names.pop_back(); } } } void display_app(std::ostream& out, z3::expr e) { if (e.is_const()) { out << e; return; } out << lower_case_fun(e.decl().name()) << "("; unsigned n = e.num_args(); for(unsigned i = 0; i < n; ++i) { display(out, e.arg(i)); if (i + 1 < n) { out << ", "; } } out << ")"; } void display_sort(std::ostream& out, z3::sort const& s) { if (s.is_int()) { out << "$int"; } else if (s.is_real()) { out << "$real"; } else if (s.is_bool()) { out << "$o"; } else { out << s; } } void display_infix(std::ostream& out, char const* conn, z3::expr& e) { out << "("; unsigned sz = e.num_args(); for (unsigned i = 0; i < sz; ++i) { display(out, e.arg(i)); if (i + 1 < sz) { out << " " << conn << " "; } } out << ")"; } void display_prefix(std::ostream& out, char const* conn, z3::expr& e) { out << conn << "("; unsigned sz = e.num_args(); for (unsigned i = 0; i < sz; ++i) { display(out, e.arg(i)); if (i + 1 < sz) { out << ", "; } } out << ")"; } void display_binary(std::ostream& out, char const* conn, z3::expr& e) { out << conn << "("; unsigned sz = e.num_args(); unsigned np = 1; for (unsigned i = 0; i < sz; ++i) { display(out, e.arg(i)); if (i + 1 < sz) { out << ", "; } if (i + 2 < sz) { out << conn << "("; ++np; } } for (unsigned i = 0; i < np; ++i) { out << ")"; } } void collect_axiom_ids(named_formulas& axioms) { m_named_formulas = &axioms; m_axiom_ids.clear(); for (unsigned i = 0; i < axioms.m_formulas.size(); ++i) { z3::expr& e = axioms.m_formulas[i]; unsigned id = Z3_get_ast_id(ctx, e); m_axiom_ids.insert(std::make_pair(id, i)); } } void display_proof(std::ostream& out, named_formulas& fmls, z3::solver& solver) { m_node_number = 0; m_proof_ids.clear(); m_proof_hypotheses.clear(); z3::expr proof = solver.proof(); collect_axiom_ids(fmls); collect_decls(proof); collect_hypotheses(proof); display_sort_decls(out); display_func_decls(out); display_proof_rec(out, proof); } /** \brief collect hypotheses for each proof node. */ void collect_hypotheses(z3::expr& proof) { Z3_sort proof_sort = proof.get_sort(); size_t todo_size = todo.size(); todo.push_back(proof); while (todo_size != todo.size()) { z3::expr p = todo.back(); unsigned id = Z3_get_ast_id(ctx, p); if (m_proof_hypotheses.find(id) != m_proof_hypotheses.end()) { todo.pop_back(); continue; } bool all_visited = true; for (unsigned i = 0; i < p.num_args(); ++i) { z3::expr arg = p.arg(i); if (arg.get_sort() == proof_sort) { if (m_proof_hypotheses.find(Z3_get_ast_id(ctx,arg)) == m_proof_hypotheses.end()) { all_visited = false; todo.push_back(arg); } } } if (!all_visited) { continue; } todo.pop_back(); std::set hyps; if (p.decl().decl_kind() == Z3_OP_PR_LEMMA) { // we assume here that all hypotheses get consumed in lemmas. } else { for (unsigned i = 0; i < p.num_args(); ++i) { z3::expr arg = p.arg(i); if (arg.get_sort() == proof_sort) { unsigned arg_id = Z3_get_ast_id(ctx,arg); std::set const& arg_hyps = m_proof_hypotheses.find(arg_id)->second; std::set::iterator it = arg_hyps.begin(), end = arg_hyps.end(); for (; it != end; ++it) { hyps.insert(*it); } } } } m_proof_hypotheses.insert(std::make_pair(id, hyps)); } } unsigned display_proof_rec(std::ostream& out, z3::expr proof) { Z3_sort proof_sort = proof.get_sort(); size_t todo_size = todo.size(); todo.push_back(proof); while (todo_size != todo.size()) { z3::expr p = todo.back(); unsigned id = Z3_get_ast_id(ctx, p); if (m_proof_ids.find(id) != m_proof_ids.end()) { todo.pop_back(); continue; } switch (p.decl().decl_kind()) { case Z3_OP_PR_MODUS_PONENS_OEQ: { unsigned hyp = display_proof_rec(out, p.arg(0)); unsigned num = display_proof_hyp(out, hyp, p.arg(1)); m_proof_ids.insert(std::make_pair(id, num)); todo.pop_back(); continue; } default: break; } bool all_visited = true; for (unsigned i = 0; i < p.num_args(); ++i) { z3::expr arg = p.arg(i); if (arg.get_sort() == proof_sort) { if (m_proof_ids.find(Z3_get_ast_id(ctx,arg)) == m_proof_ids.end()) { all_visited = false; todo.push_back(arg); } } } if (!all_visited) { continue; } todo.pop_back(); unsigned num = ++m_node_number; m_proof_ids.insert(std::make_pair(id, num)); switch (p.decl().decl_kind()) { case Z3_OP_PR_ASSERTED: { std::string formula_name; std::string formula_file; unsigned id = Z3_get_ast_id(ctx, p.arg(0)); std::map::iterator it = m_axiom_ids.find(id); if (it != m_axiom_ids.end()) { formula_name = m_named_formulas->m_names[it->second]; formula_file = m_named_formulas->m_files[it->second]; } else { std::ostringstream str; str << "axiom_" << id; formula_name = str.str(); formula_file = "unknown"; } out << "tff(" << m_node_number << ",axiom,("; display(out, get_proof_formula(p)); out << "), file('" << formula_file << "','"; out << formula_name << "')).\n"; break; } case Z3_OP_PR_UNDEF: throw failure_ex("undef rule not handled"); case Z3_OP_PR_TRUE: display_inference(out, "true", "thm", p); break; case Z3_OP_PR_GOAL: display_inference(out, "goal", "thm", p); break; case Z3_OP_PR_MODUS_PONENS: display_inference(out, "modus_ponens", "thm", p); break; case Z3_OP_PR_REFLEXIVITY: display_inference(out, "reflexivity", "thm", p); break; case Z3_OP_PR_SYMMETRY: display_inference(out, "symmetry", "thm", p); break; case Z3_OP_PR_TRANSITIVITY: case Z3_OP_PR_TRANSITIVITY_STAR: display_inference(out, "transitivity", "thm", p); break; case Z3_OP_PR_MONOTONICITY: display_inference(out, "monotonicity", "thm", p); break; case Z3_OP_PR_QUANT_INTRO: display_inference(out, "quant_intro", "thm", p); break; case Z3_OP_PR_DISTRIBUTIVITY: display_inference(out, "distributivity", "thm", p); break; case Z3_OP_PR_AND_ELIM: display_inference(out, "and_elim", "thm", p); break; case Z3_OP_PR_NOT_OR_ELIM: display_inference(out, "or_elim", "thm", p); break; case Z3_OP_PR_REWRITE: case Z3_OP_PR_REWRITE_STAR: display_inference(out, "rewrite", "thm", p); break; case Z3_OP_PR_PULL_QUANT: case Z3_OP_PR_PULL_QUANT_STAR: display_inference(out, "pull_quant", "thm", p); break; case Z3_OP_PR_PUSH_QUANT: display_inference(out, "push_quant", "thm", p); break; case Z3_OP_PR_ELIM_UNUSED_VARS: display_inference(out, "elim_unused_vars", "thm", p); break; case Z3_OP_PR_DER: display_inference(out, "destructive_equality_resolution", "thm", p); break; case Z3_OP_PR_QUANT_INST: display_inference(out, "quant_inst", "thm", p); break; case Z3_OP_PR_HYPOTHESIS: out << "tff(" << m_node_number << ",assumption,("; display(out, get_proof_formula(p)); out << "), introduced(assumption)).\n"; break; case Z3_OP_PR_LEMMA: { out << "tff(" << m_node_number << ",plain,("; display(out, get_proof_formula(p)); out << "), inference(lemma,lemma(discharge,"; unsigned parent_id = Z3_get_ast_id(ctx, p.arg(0)); std::set const& hyps = m_proof_hypotheses.find(parent_id)->second; print_hypotheses(out, hyps); out << "))).\n"; break; } case Z3_OP_PR_UNIT_RESOLUTION: display_inference(out, "unit_resolution", "thm", p); break; case Z3_OP_PR_IFF_TRUE: display_inference(out, "iff_true", "thm", p); break; case Z3_OP_PR_IFF_FALSE: display_inference(out, "iff_false", "thm", p); break; case Z3_OP_PR_COMMUTATIVITY: display_inference(out, "commutativity", "thm", p); break; case Z3_OP_PR_DEF_AXIOM: display_inference(out, "tautology", "thm", p); break; case Z3_OP_PR_DEF_INTRO: display_inference(out, "def_intro", "sab", p); break; case Z3_OP_PR_APPLY_DEF: display_inference(out, "apply_def", "sab", p); break; case Z3_OP_PR_IFF_OEQ: display_inference(out, "iff_oeq", "sab", p); break; case Z3_OP_PR_NNF_POS: display_inference(out, "nnf_pos", "sab", p); break; case Z3_OP_PR_NNF_NEG: display_inference(out, "nnf_neg", "sab", p); break; case Z3_OP_PR_NNF_STAR: display_inference(out, "nnf", "sab", p); break; case Z3_OP_PR_CNF_STAR: display_inference(out, "cnf", "sab", p); break; case Z3_OP_PR_SKOLEMIZE: display_inference(out, "skolemize", "sab", p); break; case Z3_OP_PR_MODUS_PONENS_OEQ: display_inference(out, "modus_ponens_sab", "sab", p); break; case Z3_OP_PR_TH_LEMMA: display_inference(out, "theory_lemma", "thm", p); break; case Z3_OP_PR_HYPER_RESOLVE: display_inference(out, "hyper_resolve", "thm", p); break; default: out << "TBD: " << m_node_number << "\n" << p << "\n"; throw failure_ex("rule not handled"); } } return m_proof_ids.find(Z3_get_ast_id(ctx, proof))->second; } unsigned display_proof_hyp(std::ostream& out, unsigned hyp, z3::expr p) { z3::expr fml = p.arg(p.num_args()-1); z3::expr conclusion = fml.arg(1); switch (p.decl().decl_kind()) { case Z3_OP_PR_REFLEXIVITY: return display_hyp_inference(out, "reflexivity", "sab", conclusion, hyp); case Z3_OP_PR_IFF_OEQ: { unsigned hyp2 = display_proof_rec(out, p.arg(0)); return display_hyp_inference(out, "modus_ponens", "thm", conclusion, hyp, hyp2); } case Z3_OP_PR_NNF_POS: case Z3_OP_PR_NNF_STAR: return display_hyp_inference(out, "nnf", "sab", conclusion, hyp); case Z3_OP_PR_CNF_STAR: return display_hyp_inference(out, "cnf", "sab", conclusion, hyp); case Z3_OP_PR_SKOLEMIZE: return display_hyp_inference(out, "skolemize", "sab", conclusion, hyp); case Z3_OP_PR_TRANSITIVITY: case Z3_OP_PR_TRANSITIVITY_STAR: { unsigned na = p.num_args(); for (unsigned i = 0; i + 1 < na; ++i) { if (p.arg(i).num_args() != 2) { // cop-out: Z3 produces transitivity proofs that are not a chain of equivalences/equi-sats. // the generated proof is (most likely) not going to be checkable. continue; } z3::expr conclusion = p.arg(i).arg(1); hyp = display_hyp_inference(out, "transitivity", "sab", conclusion, hyp); } return hyp; } case Z3_OP_PR_MONOTONICITY: throw failure_ex("monotonicity rule is not handled"); default: unsigned hyp2 = 0; if (p.num_args() == 2) { hyp2 = display_proof_rec(out, p.arg(0)); } if (p.num_args() > 2) { std::cout << "unexpected number of arguments: " << p << "\n"; throw failure_ex("unexpected number of arguments"); } return display_hyp_inference(out, p.decl().name().str().c_str(), "sab", conclusion, hyp, hyp2); } return 0; } void display_inference(std::ostream& out, char const* name, char const* status, z3::expr p) { unsigned id = Z3_get_ast_id(ctx, p); std::set const& hyps = m_proof_hypotheses.find(id)->second; out << "tff(" << m_node_number << ",plain,\n ("; display(out, get_proof_formula(p)); out << "),\n inference(" << name << ",[status(" << status << ")"; if (!hyps.empty()) { out << ", assumptions("; print_hypotheses(out, hyps); out << ")"; } out << "],"; display_hypotheses(out, p); out << ")).\n"; } void print_hypotheses(std::ostream& out, std::set const& hyps) { std::set::iterator it = hyps.begin(), end = hyps.end(); bool first = true; out << "["; for (; it != end; ++it) { if (!first) { out << ", "; } first = false; out << m_proof_ids.find(*it)->second; } out << "]"; } unsigned display_hyp_inference(std::ostream& out, char const* name, char const* status, z3::expr conclusion, unsigned hyp1, unsigned hyp2 = 0) { ++m_node_number; out << "tff(" << m_node_number << ",plain,(\n "; display(out, conclusion); out << "),\n inference(" << name << ",[status(" << status << ")],"; out << "[" << hyp1; if (hyp2) { out << ", " << hyp2; } out << "])).\n"; return m_node_number; } void get_free_vars(z3::expr const& e, std::vector& vars) { std::set seen; size_t sz = todo.size(); todo.push_back(e); while (todo.size() != sz) { z3::expr e = todo.back(); todo.pop_back(); unsigned id = Z3_get_ast_id(e.ctx(), e); if (seen.find(id) != seen.end()) { continue; } seen.insert(id); if (e.is_var()) { unsigned idx = Z3_get_index_value(ctx, e); while (idx >= vars.size()) { vars.push_back(e.get_sort()); } vars[idx] = e.get_sort(); } else if (e.is_app()) { unsigned sz = e.num_args(); for (unsigned i = 0; i < sz; ++i) { todo.push_back(e.arg(i)); } } else { // e is a quantifier std::vector fv; get_free_vars(e.body(), fv); unsigned nb = Z3_get_quantifier_num_bound(e.ctx(), e); for (unsigned i = nb; i < fv.size(); ++i) { if (vars.size() <= i - nb) { vars.push_back(fv[i]); } } } } } z3::expr get_proof_formula(z3::expr proof) { unsigned na = proof.num_args(); z3::expr result = proof.arg(proof.num_args()-1); std::vector vars; get_free_vars(result, vars); if (vars.empty()) { return result; } Z3_sort* sorts = new Z3_sort[vars.size()]; Z3_symbol* names = new Z3_symbol[vars.size()]; for (unsigned i = 0; i < vars.size(); ++i) { std::ostringstream str; str << "X" << (i+1); sorts[vars.size()-i-1] = vars[i]; names[vars.size()-i-1] = Z3_mk_string_symbol(ctx, str.str().c_str()); } result = z3::expr(ctx, Z3_mk_forall(ctx, 1, 0, 0, static_cast(vars.size()), sorts, names, result)); delete[] sorts; delete[] names; return result; } void display_hypotheses(std::ostream& out, z3::expr p) { unsigned na = p.num_args(); out << "["; for (unsigned i = 0; i + 1 < na; ++i) { out << m_proof_ids.find(Z3_get_ast_id(p.ctx(), p.arg(i)))->second; if (i + 2 < na) { out << ", "; } } out << "]"; } void display_sort_decls(std::ostream& out) { for (unsigned i = 0; i < sorts.size(); ++i) { display_sort_decl(out, sorts[i]); } } void display_sort_decl(std::ostream& out, z3::sort& s) { out << "tff(" << s << "_type, type, (" << s << ": $tType)).\n"; } void display_func_decls(std::ostream& out) { for (size_t i = 0; i < funs.size(); ++i) { display_func_decl(out, funs[i]); } } bool contains_id(unsigned id) const { return seen_ids.find(id) != seen_ids.end(); } void collect_decls(z3::expr e) { todo.push_back(e); while (!todo.empty()) { z3::expr e = todo.back(); todo.pop_back(); unsigned id = Z3_get_ast_id(ctx, e); if (contains_id(id)) { continue; } seen_ids.insert(id); if (e.is_app()) { collect_fun(e.decl()); unsigned sz = e.num_args(); for (unsigned i = 0; i < sz; ++i) { todo.push_back(e.arg(i)); } } else if (e.is_quantifier()) { unsigned nb = Z3_get_quantifier_num_bound(e.ctx(), e); for (unsigned i = 0; i < nb; ++i) { z3::sort srt(ctx, Z3_get_quantifier_bound_sort(e.ctx(), e, i)); collect_sort(srt); } todo.push_back(e.body()); } else if (e.is_var()) { collect_sort(e.get_sort()); } } } void collect_sort(z3::sort s) { unsigned id = Z3_get_sort_id(ctx, s); if (s.sort_kind() == Z3_UNINTERPRETED_SORT && contains_id(id)) { seen_ids.insert(id); sorts.push_back(s); } } void collect_fun(z3::func_decl f) { unsigned id = Z3_get_func_decl_id(ctx, f); if (contains_id(id)) { return; } seen_ids.insert(id); if (f.decl_kind() == Z3_OP_UNINTERPRETED) { funs.push_back(f); } for (unsigned i = 0; i < f.arity(); ++i) { collect_sort(f.domain(i)); } collect_sort(f.range()); } std::string upper_case_var(z3::symbol const& sym) { std::string result = sanitize(sym); char ch = result[0]; if ('A' <= ch && ch <= 'Z') { return result; } return "X" + result; } std::string lower_case_fun(z3::symbol const& sym) { std::string result = sanitize(sym); char ch = result[0]; if ('a' <= ch && ch <= 'z') { return result; } else { return "tptp_fun_" + result; } } std::string sanitize(z3::symbol const& sym) { std::ostringstream str; if (sym.kind() == Z3_INT_SYMBOL) { str << sym; return str.str(); } std::string s = sym.str(); size_t sz = s.size(); for (size_t i = 0; i < sz; ++i) { char ch = s[i]; if ('a' <= ch && ch <= 'z') { str << ch; } else if ('A' <= ch && ch <= 'Z') { str << ch; } else if ('0' <= ch && ch <= '9') { str << ch; } else if ('_' == ch) { str << ch; } else { str << "_"; } } return str.str(); } }; static char* g_input_file = 0; static bool g_display_smt2 = false; static bool g_generate_model = false; static bool g_generate_proof = false; static bool g_generate_core = false; static bool g_display_statistics = false; static bool g_first_interrupt = true; static bool g_smt2status = false; static bool g_check_status = false; static int g_timeout = 0; static double g_start_time = 0; static z3::solver* g_solver = 0; static z3::context* g_context = 0; static std::ostream* g_out = &std::cout; static void display_usage() { unsigned major, minor, build_number, revision_number; Z3_get_version(&major, &minor, &build_number, &revision_number); std::cout << "Z3tptp [" << major << "." << minor << "." << build_number << "." << revision_number << "] (c) 2006-20**. Microsoft Corp.\n"; std::cout << "Usage: tptp [options] [-file:]file\n"; std::cout << " -h, -? prints this message.\n"; std::cout << " -smt2 print SMT-LIB2 benchmark.\n"; std::cout << " -m, -model generate model.\n"; std::cout << " -p, -proof generate proof.\n"; std::cout << " -c, -core generate unsat core of named formulas.\n"; std::cout << " -st, -statistics display statistics.\n"; std::cout << " -t:timeout set timeout (in second).\n"; std::cout << " -smt2status display status in smt2 format instead of SZS.\n"; std::cout << " -check_status check the status produced by Z3 against annotation in benchmark.\n"; std::cout << " -: configuration parameter and value.\n"; std::cout << " -o: file to place output in.\n"; } static void display_statistics() { if (g_solver && g_display_statistics) { std::cout.flush(); std::cerr.flush(); double end_time = static_cast(clock()); z3::stats stats = g_solver->statistics(); std::cout << stats << "\n"; std::cout << "time: " << (end_time - g_start_time)/CLOCKS_PER_SEC << " secs\n"; } } static void on_ctrl_c(int) { if (g_context && g_first_interrupt) { Z3_interrupt(*g_context); g_first_interrupt = false; } else { signal (SIGINT, SIG_DFL); display_statistics(); raise(SIGINT); } } bool parse_token(char const*& line, char const* token) { char const* result = line; while (result[0] == ' ') ++result; while (token[0] && result[0] == token[0]) { ++token; ++result; } if (!token[0]) { line = result; return true; } else { return false; } } bool parse_is_sat_line(char const* line, bool& is_sat) { if (!parse_token(line, "%")) return false; if (!parse_token(line, "Status")) return false; if (!parse_token(line, ":")) return false; if (parse_token(line, "Unsatisfiable")) { is_sat = false; return true; } if (parse_token(line, "Theorem")) { is_sat = false; return true; } if (parse_token(line, "Theorem")) { is_sat = false; return true; } if (parse_token(line, "CounterSatisfiable")) { is_sat = true; return true; } if (parse_token(line, "Satisfiable")) { is_sat = true; return true; } return false; } bool parse_is_sat(char const* filename, bool& is_sat) { std::ifstream is(filename); if (is.bad() || is.fail()) { std::stringstream strm; strm << "Could not open file " << filename << "\n"; throw failure_ex(strm.str().c_str()); } for (unsigned i = 0; !is.eof() && i < 200; ++i) { std::string line; std::getline(is, line); if (parse_is_sat_line(line.c_str(), is_sat)) { return true; } } return false; } void parse_cmd_line_args(int argc, char ** argv) { g_input_file = 0; g_display_smt2 = false; int i = 1; while (i < argc) { char* arg = argv[i]; char * eq = 0; char * opt_arg = 0; if (arg[0] == '-' || arg[0] == '/') { ++arg; while (*arg == '-') { ++arg; } char * colon = strchr(arg, ':'); if (colon) { opt_arg = colon + 1; *colon = 0; } if (!strcmp(arg,"h") || !strcmp(arg,"help") || !strcmp(arg,"?")) { display_usage(); exit(0); } if (!strcmp(arg,"p") || !strcmp(arg,"proof")) { g_generate_proof = true; } else if (!strcmp(arg,"m") || !strcmp(arg,"model")) { g_generate_model = true; } else if (!strcmp(arg,"c") || !strcmp(arg,"core")) { g_generate_core = true; } else if (!strcmp(arg,"st") || !strcmp(arg,"statistics")) { g_display_statistics = true; } else if (!strcmp(arg,"check_status")) { g_check_status = true; } else if (!strcmp(arg,"t") || !strcmp(arg,"timeout")) { if (!opt_arg) { display_usage(); exit(0); } g_timeout = atoi(opt_arg); } else if (!strcmp(arg,"smt2status")) { g_smt2status = true; } else if (!strcmp(arg,"o")) { if (opt_arg) { g_out = new std::ofstream(opt_arg); if (g_out->bad() || g_out->fail()) { std::cout << "Could not open file of output: " << opt_arg << "\n"; exit(0); } } else { display_usage(); exit(0); } } else if (!strcmp(arg,"smt2")) { g_display_smt2 = true; } else if (!strcmp(arg, "file")) { g_input_file = opt_arg; } else if (opt_arg && arg[0] != '"') { Z3_global_param_set(arg, opt_arg); } else { std::cerr << "parameter " << arg << " was not recognized\n"; display_usage(); exit(0); } } else { g_input_file = arg; } ++i; } if (!g_input_file) { display_usage(); exit(0); } } static bool is_smt2_file(char const* filename) { size_t len = strlen(filename); return (len > 4 && !strcmp(filename + len - 5,".smt2")); } static void check_error(z3::context& ctx) { Z3_error_code e = Z3_get_error_code(ctx); if (e != Z3_OK) { std::cout << Z3_get_error_msg_ex(ctx, e) << "\n"; exit(1); } } static void display_tptp(std::ostream& out) { // run SMT2 parser, pretty print TFA format. z3::context ctx; Z3_ast _fml = Z3_parse_smtlib2_file(ctx, g_input_file, 0, 0, 0, 0, 0, 0); check_error(ctx); z3::expr fml(ctx, _fml); pp_tptp pp(ctx); pp.collect_decls(fml); pp.display_sort_decls(out); pp.display_func_decls(out); if (fml.decl().decl_kind() == Z3_OP_AND) { for (unsigned i = 0; i < fml.num_args(); ++i) { pp.display_axiom(out, fml.arg(i)); } } else { pp.display_axiom(out, fml); } } static void display_proof(z3::context& ctx, named_formulas& fmls, z3::solver& solver) { pp_tptp pp(ctx); pp.display_proof(std::cout, fmls, solver); } static void display_model(z3::context& ctx, z3::model model) { unsigned nc = model.num_consts(); unsigned nf = model.num_funcs(); z3::expr_vector fmls(ctx); for (unsigned i = 0; i < nc; ++i) { z3::func_decl f = model.get_const_decl(i); z3::expr e = model.get_const_interp(f); fmls.push_back(f() == e); } for (unsigned i = 0; i < nf; ++i) { z3::func_decl f = model.get_func_decl(i); z3::func_interp fi = model.get_func_interp(f); unsigned arity = f.arity(); z3::expr_vector args(ctx); for (unsigned j = 0; j < arity; ++j) { std::ostringstream str; str << "X" << j; z3::symbol sym(ctx, Z3_mk_string_symbol(ctx, str.str().c_str())); args.push_back(ctx.constant(sym, f.domain(j))); } unsigned ne = fi.num_entries(); Z3_ast* conds = new Z3_ast[arity]; Z3_ast* conds_match = new Z3_ast[ne]; z3::expr_vector conds_matchv(ctx); z3::expr els = fi.else_value(); unsigned num_cases = 0; for (unsigned k = 0; k < ne; ++k) { z3::func_entry e = fi.entry(k); z3::expr_vector condv(ctx), args_e(ctx); if (((Z3_ast)els) && (Z3_get_ast_id(ctx, els) == Z3_get_ast_id(ctx, e.value()))) { continue; } for (unsigned j = 0; j < arity; ++j) { args_e.push_back(e.arg(j)); condv.push_back(e.arg(j) == args[j]); conds[j] = condv.back(); } z3::expr cond(ctx, Z3_mk_and(ctx, arity, conds)); conds_matchv.push_back(cond); conds_match[num_cases] = cond; fmls.push_back(f(args_e) == e.value()); ++num_cases; } if (els) { els = f(args) == els; switch (num_cases) { case 0: els = forall(args, els); break; case 1: els = forall(args, implies(!z3::expr(ctx, conds_match[0]), els)); break; default: els = forall(args, implies(!z3::expr(ctx, Z3_mk_or(ctx, num_cases, conds_match)), els)); break; } fmls.push_back(els); } delete[] conds; delete[] conds_match; } pp_tptp pp(ctx); for (unsigned i = 0; i < fmls.size(); ++i) { pp.collect_decls(fmls[i]); } pp.display_sort_decls(std::cout); pp.display_func_decls(std::cout); for (unsigned i = 0; i < fmls.size(); ++i) { pp.display_axiom(std::cout, fmls[i]); } } static void display_smt2(std::ostream& out) { z3::config config; z3::context ctx(config); named_formulas fmls; env env(ctx); try { env.parse(g_input_file, fmls); } catch (failure_ex& ex) { std::cerr << ex.msg << "\n"; return; } size_t num_assumptions = fmls.m_formulas.size(); Z3_ast* assumptions = new Z3_ast[num_assumptions]; for (size_t i = 0; i < num_assumptions; ++i) { assumptions[i] = fmls.m_formulas[i]; } Z3_string s = Z3_benchmark_to_smtlib_string( ctx, "Benchmark generated from TPTP", // comment 0, // no logic is set "unknown", // no status annotation "", // attributes static_cast(num_assumptions), assumptions, ctx.bool_val(true)); out << s << "\n"; delete[] assumptions; } static void prove_tptp() { z3::config config; if (g_generate_proof) { config.set("proof", true); z3::set_param("proof", true); } z3::context ctx(config); z3::solver solver(ctx); g_solver = &solver; g_context = &ctx; if (g_timeout) { // TBD overflow check z3::set_param("timeout", g_timeout*1000); z3::params params(ctx); params.set("timeout", static_cast(g_timeout*1000)); solver.set(params); } named_formulas fmls; env env(ctx); try { env.parse(g_input_file, fmls); } catch (failure_ex& ex) { std::cerr << ex.msg << "\n"; std::cout << "SZS status GaveUp\n"; return; } size_t num_assumptions = fmls.m_formulas.size(); z3::check_result result; if (g_generate_core) { z3::expr_vector assumptions(ctx); for (size_t i = 0; i < num_assumptions; ++i) { z3::expr pred = ctx.constant(fmls.m_names[i].c_str(), ctx.bool_sort()); z3::expr def = fmls.m_formulas[i] == pred; solver.add(def); assumptions.push_back(pred); } result = solver.check(assumptions); } else { for (unsigned i = 0; i < num_assumptions; ++i) { solver.add(fmls.m_formulas[i]); } result = solver.check(); } switch(result) { case z3::unsat: if (g_smt2status) { std::cout << result << "\n"; } else if (fmls.has_conjecture()) { std::cout << "SZS status Theorem\n"; } else { std::cout << "SZS status Unsatisfiable\n"; } if (g_generate_proof) { try { display_proof(ctx, fmls, solver); } catch (failure_ex& ex) { std::cerr << "Proof display could not be completed: " << ex.msg << "\n"; } } if (g_generate_core) { z3::expr_vector core = solver.unsat_core(); std::cout << "SZS core "; for (unsigned i = 0; i < core.size(); ++i) { std::cout << core[i] << " "; } std::cout << "\n"; } break; case z3::sat: if (g_smt2status) { std::cout << result << "\n"; } else if (fmls.has_conjecture()) { std::cout << "SZS status CounterSatisfiable\n"; } else { std::cout << "SZS status Satisfiable\n"; } if (g_generate_model) { display_model(ctx, solver.get_model()); } break; case z3::unknown: if (g_smt2status) { std::cout << result << "\n"; } else if (!g_first_interrupt) { std::cout << "SZS status Interrupted\n"; } else { std::cout << "SZS status GaveUp\n"; std::string reason = solver.reason_unknown(); std::cout << "SZS reason " << reason << "\n"; } break; } bool is_sat = true; if (g_check_status && result != z3::unknown && parse_is_sat(g_input_file, is_sat)) { if (is_sat && result == z3::unsat) { std::cout << "BUG!! expected result is Satisfiable, returned result is Unsat\n"; } if (!is_sat && result == z3::sat) { std::cout << "BUG!! expected result is Unsatisfiable, returned result is Satisfiable\n"; } } display_statistics(); } int main(int argc, char** argv) { std::ostream* out = &std::cout; g_start_time = static_cast(clock()); signal(SIGINT, on_ctrl_c); parse_cmd_line_args(argc, argv); if (is_smt2_file(g_input_file)) { display_tptp(*g_out); } else if (g_display_smt2) { display_smt2(*g_out); } else { prove_tptp(); } return 0; } z3-4.4.0/examples/tptp/tptp5.h0000644000175000017500000000210412540347414015674 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifndef TPTP5_H_ #define TPTP5_H_ class TreeNode; #if 0 class named_formulas { expr_ref_vector m_fmls; svector m_names; bool m_has_conjecture; unsigned m_conjecture_index; public: named_formulas(ast_manager& m) : m_fmls(m), m_has_conjecture(false), m_conjecture_index(0) {} void push_back(expr* fml, char const* name) { m_fmls.push_back(fml); m_names.push_back(symbol(name)); } unsigned size() const { return m_fmls.size(); } expr*const* c_ptr() const { return m_fmls.c_ptr(); } expr* operator[](unsigned i) { return m_fmls[i].get(); } symbol const& name(unsigned i) { return m_names[i]; } void set_has_conjecture() { m_has_conjecture = true; m_conjecture_index = m_fmls.size(); } bool has_conjecture() const { return m_has_conjecture; } unsigned conjecture_index() const { return m_conjecture_index; } }; bool tptp5_parse(ast_manager& m, char const* filename, named_formulas& fmls); #endif #endif z3-4.4.0/examples/tptp/tptp5.tab.h0000644000175000017500000000742112540347414016450 0ustar michaelmichael/* A Bison parser, made by GNU Bison 2.4.2. */ /* Skeleton interface for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2006, 2009-2010 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { AMPERSAND = 258, AT_SIGN = 259, AT_SIGN_MINUS = 260, AT_SIGN_PLUS = 261, CARET = 262, COLON = 263, COLON_EQUALS = 264, COMMA = 265, EQUALS = 266, EQUALS_GREATER = 267, EXCLAMATION = 268, EXCLAMATION_EQUALS = 269, EXCLAMATION_EXCLAMATION = 270, EXCLAMATION_GREATER = 271, LBRKT = 272, LESS_EQUALS = 273, LESS_EQUALS_GREATER = 274, LESS_TILDE_GREATER = 275, LPAREN = 276, MINUS = 277, MINUS_MINUS_GREATER = 278, PERIOD = 279, QUESTION = 280, QUESTION_QUESTION = 281, QUESTION_STAR = 282, RBRKT = 283, RPAREN = 284, STAR = 285, TILDE = 286, TILDE_AMPERSAND = 287, TILDE_VLINE = 288, VLINE = 289, _DLR_cnf = 290, _DLR_fof = 291, _DLR_fot = 292, _DLR_itef = 293, _DLR_itetf = 294, _DLR_itett = 295, _DLR_tff = 296, _DLR_thf = 297, _LIT_cnf = 298, _LIT_fof = 299, _LIT_include = 300, _LIT_tff = 301, _LIT_thf = 302, arrow = 303, comment = 304, comment_line = 305, decimal = 306, decimal_exponent = 307, decimal_fraction = 308, distinct_object = 309, dollar_dollar_word = 310, dollar_word = 311, dot_decimal = 312, integer = 313, less_sign = 314, lower_word = 315, plus = 316, positive_decimal = 317, rational = 318, real = 319, signed_integer = 320, signed_rational = 321, signed_real = 322, single_quoted = 323, star = 324, unrecognized = 325, unsigned_integer = 326, unsigned_rational = 327, unsigned_real = 328, upper_word = 329, vline = 330 }; #endif #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE { /* Line 1685 of yacc.c */ #line 148 "tptp5.y" int ival; double dval; char* sval; TreeNode* pval; /* Line 1685 of yacc.c */ #line 130 "tptp5.tab.h" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 #endif extern YYSTYPE yylval; z3-4.4.0/examples/tptp/tptp5.lex.cpp0000644000175000017500000020507612540347414017033 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #line 2 "tptp5.lex.cpp" #line 4 "tptp5.lex.cpp" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 35 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; #endif /* ! C99 */ /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ /* C99 requires __STDC__ to be defined as 1. */ #if defined (__STDC__) #define YY_USE_CONST #endif /* defined (__STDC__) */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN (yy_start) = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START (((yy_start) - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE yyrestart(yyin ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #define YY_BUF_SIZE 16384 #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif extern int yyleng; extern FILE *yyin, *yyout; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires * access to the local variable yy_act. Since yyless() is a macro, it would break * existing scanners that call yyless() from OUTSIDE yylex. * One obvious solution it to make yy_act a global. I tried that, and saw * a 5% performance hit in a non-yylineno scanner, because yy_act is * normally declared as a register variable-- so it is not worth it. */ #define YY_LESS_LINENO(n) \ do { \ int yyl;\ for ( yyl = n; yyl < yyleng; ++yyl )\ if ( yytext[yyl] == '\n' )\ --yylineno;\ }while(0) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = (yy_hold_char); \ YY_RESTORE_YY_MORE_OFFSET \ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, (yytext_ptr) ) #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* Stack of input buffers. */ static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] /* yy_hold_char holds the character lost when yytext is formed. */ static char yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ int yyleng; /* Points to current character in buffer. */ static char *yy_c_buf_p = (char *) 0; static int yy_init = 0; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* Flag which is used to allow yywrap()'s to do buffer switches * instead of setting up a fresh yyin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; void yyrestart (FILE *input_file ); void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); void yy_delete_buffer (YY_BUFFER_STATE b ); void yy_flush_buffer (YY_BUFFER_STATE b ); void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); void yypop_buffer_state (void ); static void yyensure_buffer_stack (void ); static void yy_load_buffer_state (void ); static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); #define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); void *yyalloc (yy_size_t ); void *yyrealloc (void *,yy_size_t ); void yyfree (void * ); #define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ yyensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer(yyin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ yyensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer(yyin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ typedef unsigned char YY_CHAR; FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; typedef int yy_state_type; #define YY_FLEX_LEX_COMPAT extern int yylineno; int yylineno = 1; extern char yytext[]; static yy_state_type yy_get_previous_state (void ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); static int yy_get_next_buffer (void ); static void yy_fatal_error (yyconst char msg[] ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ (yytext_ptr) = yy_bp; \ yyleng = (int) (yy_cp - yy_bp); \ (yy_hold_char) = *yy_cp; \ *yy_cp = '\0'; \ if ( yyleng + (yy_more_offset) >= YYLMAX ) \ YY_FATAL_ERROR( "token too large, exceeds YYLMAX" ); \ yy_flex_strncpy( &yytext[(yy_more_offset)], (yytext_ptr), yyleng + 1 ); \ yyleng += (yy_more_offset); \ (yy_prev_more_offset) = (yy_more_offset); \ (yy_more_offset) = 0; \ (yy_c_buf_p) = yy_cp; #define YY_NUM_RULES 75 #define YY_END_OF_BUFFER 76 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_acclist[228] = { 0, 76, 73, 75, 72, 73, 75, 11, 74, 75, 74, 75, 74, 75, 74, 75, 71, 74, 75, 1, 74, 75, 74, 75, 19, 74, 75, 27, 74, 75, 28, 53, 74, 75, 54, 74, 75, 8, 74, 75, 20, 74, 75, 22, 74, 75, 74, 75, 63, 65, 66, 74, 75, 63, 65, 66, 67, 74, 75, 6, 74, 75, 56, 74, 75, 9, 74, 75, 55, 74, 75, 23, 74, 75, 2, 74, 75, 50, 74, 75, 15, 74, 75, 26, 74, 75, 5, 74, 75, 51, 74, 75, 51, 74, 75, 51, 74, 75, 51, 74, 75, 51, 74, 75, 32, 52, 74, 75, 29, 74, 75, 75, 13, 12, 14, 47, 48, 48, 48, 48, 48, 71, 63, 64, 63, 64, 70, 63, 65, 66, 67, 7, 16, 10, 25, 24, 4, 3, 50, 51, 51, 51, 51, 51, 51, 30, 31, 49, 48, 48, 48, 48, 48, 48, 46, 63, 64, 21, 70, 57, 59, 69, 60, 62, 57, 59, 68, 57, 59, 68, 17, 18, 41, 51, 42, 51, 51, 44, 51, 45, 51, 49, 33, 48, 34, 48, 35, 48, 48, 39, 48, 40, 48, 57, 58, 60, 61, 57, 58, 57, 58, 71, 57, 59, 69, 60, 62, 57, 59, 68, 51, 36, 48, 48, 57, 58, 60, 61, 57, 58, 51, 37, 48, 38, 48, 51, 43, 51 } ; static yyconst flex_int16_t yy_accept[153] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 7, 10, 12, 14, 16, 19, 22, 24, 27, 30, 34, 37, 40, 43, 46, 48, 53, 59, 62, 65, 68, 71, 74, 77, 80, 83, 86, 89, 92, 95, 98, 101, 104, 108, 111, 112, 113, 114, 115, 115, 116, 116, 116, 117, 118, 119, 120, 121, 122, 122, 122, 124, 126, 126, 127, 127, 127, 127, 127, 131, 132, 133, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 154, 155, 155, 155, 155, 155, 157, 158, 159, 159, 159, 162, 164, 167, 170, 171, 172, 174, 176, 177, 179, 181, 182, 184, 186, 188, 189, 191, 193, 195, 197, 199, 201, 201, 201, 202, 205, 207, 210, 211, 213, 214, 216, 218, 220, 221, 223, 225, 226, 228, 228 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 7, 23, 24, 25, 26, 27, 28, 28, 28, 28, 29, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 30, 31, 32, 33, 34, 7, 35, 35, 36, 37, 38, 39, 35, 40, 41, 35, 35, 42, 35, 43, 44, 35, 35, 35, 35, 45, 46, 35, 35, 35, 35, 35, 7, 47, 7, 48, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50 } ; static yyconst flex_int32_t yy_meta[51] = { 0, 1, 1, 2, 3, 3, 3, 3, 4, 3, 3, 5, 3, 3, 3, 3, 3, 3, 3, 3, 6, 6, 3, 3, 3, 3, 3, 3, 6, 6, 3, 3, 3, 3, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, 3, 1, 1 } ; static yyconst flex_int16_t yy_base[164] = { 0, 0, 0, 15, 16, 23, 30, 31, 38, 45, 46, 53, 60, 61, 68, 302, 303, 303, 90, 47, 303, 80, 0, 303, 270, 303, 303, 303, 90, 303, 103, 97, 286, 108, 110, 275, 84, 273, 303, 108, 48, 0, 303, 303, 303, 0, 254, 252, 252, 96, 303, 93, 303, 303, 303, 303, 127, 303, 132, 0, 0, 222, 213, 209, 102, 0, 62, 133, 131, 133, 228, 135, 231, 145, 223, 147, 154, 303, 218, 217, 303, 303, 303, 303, 303, 0, 0, 202, 201, 202, 197, 196, 303, 303, 0, 0, 180, 131, 171, 157, 143, 146, 303, 148, 160, 157, 164, 168, 303, 170, 147, 179, 174, 179, 303, 181, 303, 303, 0, 0, 105, 0, 0, 0, 0, 0, 0, 165, 0, 0, 187, 193, 303, 197, 131, 201, 303, 201, 203, 206, 97, 0, 166, 208, 211, 213, 75, 0, 0, 42, 0, 303, 244, 248, 255, 260, 262, 264, 51, 266, 271, 278, 280, 287 } ; static yyconst flex_int16_t yy_def[164] = { 0, 151, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 151, 151, 151, 151, 152, 151, 153, 154, 151, 155, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 156, 151, 151, 151, 157, 157, 157, 157, 157, 151, 151, 151, 151, 151, 151, 152, 151, 151, 158, 159, 159, 159, 159, 159, 154, 160, 151, 151, 151, 151, 151, 161, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 156, 157, 157, 157, 157, 157, 157, 151, 151, 162, 159, 159, 159, 159, 159, 159, 160, 151, 151, 151, 151, 151, 151, 151, 151, 161, 163, 151, 151, 151, 151, 151, 151, 157, 157, 157, 157, 157, 162, 159, 159, 159, 159, 159, 159, 151, 151, 151, 151, 161, 163, 151, 151, 151, 151, 157, 159, 159, 151, 151, 151, 157, 159, 159, 157, 157, 0, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151 } ; static yyconst flex_int16_t yy_nxt[354] = { 0, 16, 17, 17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 41, 42, 20, 43, 44, 20, 45, 46, 45, 45, 47, 45, 48, 45, 45, 45, 49, 45, 50, 51, 16, 52, 45, 45, 57, 45, 45, 45, 45, 94, 45, 45, 45, 45, 83, 45, 84, 45, 45, 45, 45, 45, 45, 45, 102, 45, 45, 45, 45, 58, 45, 150, 45, 45, 45, 45, 45, 45, 45, 59, 45, 45, 45, 45, 103, 45, 53, 45, 45, 45, 45, 45, 45, 45, 92, 45, 45, 45, 45, 78, 45, 68, 69, 149, 45, 54, 55, 61, 71, 71, 62, 70, 63, 81, 68, 69, 64, 73, 74, 73, 74, 76, 76, 79, 57, 82, 90, 91, 75, 56, 75, 93, 99, 100, 146, 66, 111, 75, 140, 75, 104, 105, 104, 105, 107, 107, 109, 109, 102, 58, 101, 106, 111, 106, 56, 66, 112, 112, 114, 115, 106, 125, 106, 73, 74, 76, 76, 126, 103, 131, 101, 130, 130, 129, 75, 132, 133, 104, 105, 107, 107, 109, 109, 75, 135, 137, 137, 128, 106, 136, 138, 138, 139, 139, 75, 141, 147, 106, 143, 143, 127, 142, 148, 75, 144, 144, 135, 106, 145, 145, 124, 136, 137, 137, 138, 138, 106, 139, 139, 143, 143, 75, 144, 144, 145, 145, 122, 121, 106, 120, 75, 119, 118, 117, 116, 113, 111, 106, 56, 56, 56, 56, 56, 60, 108, 98, 60, 65, 97, 65, 65, 65, 65, 65, 66, 66, 96, 66, 66, 85, 85, 86, 86, 95, 95, 101, 101, 101, 101, 101, 110, 110, 110, 110, 110, 110, 110, 123, 123, 134, 134, 134, 134, 134, 134, 134, 89, 88, 87, 80, 77, 72, 67, 151, 15, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151 } ; static yyconst flex_int16_t yy_chk[354] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 4, 19, 3, 4, 3, 4, 158, 5, 3, 4, 5, 40, 5, 40, 6, 7, 5, 6, 7, 6, 7, 66, 8, 6, 7, 8, 19, 8, 149, 9, 10, 8, 9, 10, 9, 10, 21, 11, 9, 10, 11, 66, 11, 18, 12, 13, 11, 12, 13, 12, 13, 51, 14, 12, 13, 14, 36, 14, 28, 28, 146, 14, 18, 18, 21, 31, 31, 21, 30, 21, 39, 30, 30, 21, 33, 33, 34, 34, 34, 34, 36, 56, 39, 49, 49, 33, 58, 34, 51, 64, 64, 140, 67, 134, 33, 120, 34, 68, 68, 69, 69, 69, 69, 71, 71, 101, 56, 103, 68, 110, 69, 58, 67, 73, 73, 75, 75, 68, 97, 69, 76, 76, 76, 76, 97, 101, 105, 103, 104, 104, 100, 76, 106, 106, 107, 107, 107, 107, 109, 109, 76, 111, 112, 112, 99, 107, 111, 113, 113, 115, 115, 112, 127, 142, 107, 130, 130, 98, 127, 142, 112, 131, 131, 135, 130, 133, 133, 96, 135, 137, 137, 138, 138, 130, 139, 139, 143, 143, 137, 144, 144, 145, 145, 91, 90, 143, 89, 137, 88, 87, 79, 78, 74, 72, 143, 152, 152, 152, 152, 152, 153, 70, 63, 153, 154, 62, 154, 154, 154, 154, 154, 155, 155, 61, 155, 155, 156, 156, 157, 157, 159, 159, 160, 160, 160, 160, 160, 161, 161, 161, 161, 161, 161, 161, 162, 162, 163, 163, 163, 163, 163, 163, 163, 48, 47, 46, 37, 35, 32, 24, 15, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151 } ; /* Table of booleans, true if rule could match eol. */ static yyconst flex_int32_t yy_rule_can_match_eol[76] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, }; extern int yy_flex_debug; int yy_flex_debug = 0; static yy_state_type *yy_state_buf=0, *yy_state_ptr=0; static char *yy_full_match; static int yy_lp; #define REJECT \ { \ *yy_cp = (yy_hold_char); /* undo effects of setting up yytext */ \ yy_cp = (yy_full_match); /* restore poss. backed-over text */ \ ++(yy_lp); \ goto find_rule; \ } static int yy_more_offset = 0; static int yy_prev_more_offset = 0; #define yymore() ((yy_more_offset) = yy_flex_strlen( yytext )) #define YY_NEED_STRLEN #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET \ { \ (yy_more_offset) = (yy_prev_more_offset); \ yyleng -= (yy_more_offset); \ } #ifndef YYLMAX #define YYLMAX 8192 #endif char yytext[YYLMAX]; char *yytext_ptr; #line 1 "tptp5.l" #line 3 "tptp5.l" //----------------------------------------------------------------------------- #include #include #include #if _WINDOWS #include #define isatty _isatty #else // Linux #include #define _strdup strdup #endif #include "tptp5.h" #include "tptp5.tab.h" #define YY_NO_UNISTD_H #define YY_SKIP_YYWRAP static int yywrap() { return 1; } //----------------------------------------------------------------------------- //----Compile with -DP_VERBOSE=2 to list tokens as they are seen. #ifndef P_VERBOSE # define P_VERBOSE 0 # endif int verbose2 = P_VERBOSE; //----If tptp_prev_tok == PERIOD, you are outside any sentence. //#ifndef PERIOD //# error "Period not defined" //# define PERIOD 46 //# endif #define TPTP_STORE_SIZE 32768 //----These have to be external as they are references from other code that //----is generated by lex/yacc. int tptp_prev_tok = PERIOD; int tptp_store_size = TPTP_STORE_SIZE; char* tptp_lval[TPTP_STORE_SIZE]; //----------------------------------------------------------------------------- void tptp_print_tok(char* lval) { printf("%3d:%s;\n", tptp_prev_tok, lval); return; } //----------------------------------------------------------------------------- int tptp_update_lval(char* lval) { static int tptp_next_store = 0; int next = tptp_next_store; free(tptp_lval[tptp_next_store]); tptp_lval[tptp_next_store] = _strdup(lval); tptp_next_store = (tptp_next_store+1) % TPTP_STORE_SIZE; if (verbose2 == 2) { tptp_print_tok(lval); } return next; } //----------------------------------------------------------------------------- //----%Start: INITIAL begin sentence, B before formula. No others. #line 710 "tptp5.lex.cpp" #define INITIAL 0 #define B 1 #define FF 2 #define SQ1 3 #define SQ2 4 #define Q1 5 #define Q2 6 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif static int yy_init_globals (void ); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int yylex_destroy (void ); int yyget_debug (void ); void yyset_debug (int debug_flag ); YY_EXTRA_TYPE yyget_extra (void ); void yyset_extra (YY_EXTRA_TYPE user_defined ); FILE *yyget_in (void ); void yyset_in (FILE * in_str ); FILE *yyget_out (void ); void yyset_out (FILE * out_str ); int yyget_leng (void ); char *yyget_text (void ); int yyget_lineno (void ); void yyset_lineno (int line_number ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yywrap (void ); #else extern int yywrap (void ); #endif #endif static void yyunput (int c,char *buf_ptr ); #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void ); #else static int input (void ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO fwrite( yytext, yyleng, 1, yyout ) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ int n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int yylex (void); #define YY_DECL int yylex (void) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; #line 110 "tptp5.l" #line 901 "tptp5.lex.cpp" if ( !(yy_init) ) { (yy_init) = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif /* Create the reject buffer large enough to save one state per allowed character. */ if ( ! (yy_state_buf) ) (yy_state_buf) = (yy_state_type *)yyalloc(YY_STATE_BUF_SIZE ); if ( ! (yy_state_buf) ) YY_FATAL_ERROR( "out of dynamic memory in yylex()" ); if ( ! (yy_start) ) (yy_start) = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { yyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer(yyin,YY_BUF_SIZE ); } yy_load_buffer_state( ); } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = (yy_c_buf_p); /* Support of yytext. */ *yy_cp = (yy_hold_char); /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = (yy_start); (yy_state_ptr) = (yy_state_buf); *(yy_state_ptr)++ = yy_current_state; yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 152 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; *(yy_state_ptr)++ = yy_current_state; ++yy_cp; } while ( yy_base[yy_current_state] != 303 ); yy_find_action: yy_current_state = *--(yy_state_ptr); (yy_lp) = yy_accept[yy_current_state]; goto find_rule; find_rule: /* we branch to this label when backing up */ for ( ; ; ) /* until we find what rule we matched */ { if ( (yy_lp) && (yy_lp) < yy_accept[yy_current_state + 1] ) { yy_act = yy_acclist[(yy_lp)]; { (yy_full_match) = yy_cp; break; } } --yy_cp; yy_current_state = *--(yy_state_ptr); (yy_lp) = yy_accept[yy_current_state]; } YY_DO_BEFORE_ACTION; if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) { int yyl; for ( yyl = (yy_prev_more_offset); yyl < yyleng; ++yyl ) if ( yytext[yyl] == '\n' ) yylineno++; ; } do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 1: YY_RULE_SETUP #line 112 "tptp5.l" { tptp_prev_tok=AMPERSAND; yylval.ival = tptp_update_lval(yytext); return(AMPERSAND); } YY_BREAK case 2: YY_RULE_SETUP #line 117 "tptp5.l" { tptp_prev_tok=AT_SIGN; yylval.ival = tptp_update_lval(yytext); return(AT_SIGN); } YY_BREAK case 3: YY_RULE_SETUP #line 122 "tptp5.l" { tptp_prev_tok=AT_SIGN_MINUS; yylval.ival = tptp_update_lval(yytext); return(AT_SIGN_MINUS); } YY_BREAK case 4: YY_RULE_SETUP #line 127 "tptp5.l" { tptp_prev_tok=AT_SIGN_PLUS; yylval.ival = tptp_update_lval(yytext); return(AT_SIGN_PLUS); } YY_BREAK case 5: YY_RULE_SETUP #line 132 "tptp5.l" { tptp_prev_tok=CARET; yylval.ival = tptp_update_lval(yytext); return(CARET); } YY_BREAK case 6: YY_RULE_SETUP #line 137 "tptp5.l" { tptp_prev_tok=COLON; yylval.ival = tptp_update_lval(yytext); return(COLON); } YY_BREAK case 7: YY_RULE_SETUP #line 142 "tptp5.l" { tptp_prev_tok=COLON_EQUALS; yylval.ival = tptp_update_lval(yytext); return(COLON_EQUALS); } YY_BREAK case 8: YY_RULE_SETUP #line 147 "tptp5.l" { tptp_prev_tok=COMMA; yylval.ival = tptp_update_lval(yytext); return(COMMA); } YY_BREAK case 9: YY_RULE_SETUP #line 152 "tptp5.l" { tptp_prev_tok=EQUALS; yylval.ival = tptp_update_lval(yytext); return(EQUALS); } YY_BREAK case 10: YY_RULE_SETUP #line 157 "tptp5.l" { tptp_prev_tok=EQUALS_GREATER; yylval.ival = tptp_update_lval(yytext); return(EQUALS_GREATER); } YY_BREAK case 11: YY_RULE_SETUP #line 162 "tptp5.l" { tptp_prev_tok=EXCLAMATION; yylval.ival = tptp_update_lval(yytext); return(EXCLAMATION); } YY_BREAK case 12: YY_RULE_SETUP #line 167 "tptp5.l" { tptp_prev_tok=EXCLAMATION_EQUALS; yylval.ival = tptp_update_lval(yytext); return(EXCLAMATION_EQUALS); } YY_BREAK case 13: YY_RULE_SETUP #line 172 "tptp5.l" { tptp_prev_tok=EXCLAMATION_EXCLAMATION; yylval.ival = tptp_update_lval(yytext); return(EXCLAMATION_EXCLAMATION); } YY_BREAK case 14: YY_RULE_SETUP #line 177 "tptp5.l" { tptp_prev_tok=EXCLAMATION_GREATER; yylval.ival = tptp_update_lval(yytext); return(EXCLAMATION_GREATER); } YY_BREAK case 15: YY_RULE_SETUP #line 182 "tptp5.l" { tptp_prev_tok=LBRKT; yylval.ival = tptp_update_lval(yytext); return(LBRKT); } YY_BREAK case 16: YY_RULE_SETUP #line 187 "tptp5.l" { tptp_prev_tok=LESS_EQUALS; yylval.ival = tptp_update_lval(yytext); return(LESS_EQUALS); } YY_BREAK case 17: YY_RULE_SETUP #line 192 "tptp5.l" { tptp_prev_tok=LESS_EQUALS_GREATER; yylval.ival = tptp_update_lval(yytext); return(LESS_EQUALS_GREATER); } YY_BREAK case 18: YY_RULE_SETUP #line 197 "tptp5.l" { tptp_prev_tok=LESS_TILDE_GREATER; yylval.ival = tptp_update_lval(yytext); return(LESS_TILDE_GREATER); } YY_BREAK case 19: YY_RULE_SETUP #line 202 "tptp5.l" { tptp_prev_tok=LPAREN; yylval.ival = tptp_update_lval(yytext); return(LPAREN); } YY_BREAK case 20: YY_RULE_SETUP #line 207 "tptp5.l" { tptp_prev_tok=MINUS; yylval.ival = tptp_update_lval(yytext); return(MINUS); } YY_BREAK case 21: YY_RULE_SETUP #line 212 "tptp5.l" { tptp_prev_tok=MINUS_MINUS_GREATER; yylval.ival = tptp_update_lval(yytext); return(MINUS_MINUS_GREATER); } YY_BREAK case 22: YY_RULE_SETUP #line 217 "tptp5.l" { BEGIN INITIAL; tptp_prev_tok=PERIOD; yylval.ival = tptp_update_lval(yytext); return(PERIOD); } YY_BREAK case 23: YY_RULE_SETUP #line 223 "tptp5.l" { tptp_prev_tok=QUESTION; yylval.ival = tptp_update_lval(yytext); return(QUESTION); } YY_BREAK case 24: YY_RULE_SETUP #line 228 "tptp5.l" { tptp_prev_tok=QUESTION_QUESTION; yylval.ival = tptp_update_lval(yytext); return(QUESTION_QUESTION); } YY_BREAK case 25: YY_RULE_SETUP #line 233 "tptp5.l" { tptp_prev_tok=QUESTION_STAR; yylval.ival = tptp_update_lval(yytext); return(QUESTION_STAR); } YY_BREAK case 26: YY_RULE_SETUP #line 238 "tptp5.l" { tptp_prev_tok=RBRKT; yylval.ival = tptp_update_lval(yytext); return(RBRKT); } YY_BREAK case 27: YY_RULE_SETUP #line 243 "tptp5.l" { tptp_prev_tok=RPAREN; yylval.ival = tptp_update_lval(yytext); return(RPAREN); } YY_BREAK case 28: YY_RULE_SETUP #line 248 "tptp5.l" { tptp_prev_tok=STAR; yylval.ival = tptp_update_lval(yytext); return(STAR); } YY_BREAK case 29: YY_RULE_SETUP #line 253 "tptp5.l" { tptp_prev_tok=TILDE; yylval.ival = tptp_update_lval(yytext); return(TILDE); } YY_BREAK case 30: YY_RULE_SETUP #line 258 "tptp5.l" { tptp_prev_tok=TILDE_AMPERSAND; yylval.ival = tptp_update_lval(yytext); return(TILDE_AMPERSAND); } YY_BREAK case 31: YY_RULE_SETUP #line 263 "tptp5.l" { tptp_prev_tok=TILDE_VLINE; yylval.ival = tptp_update_lval(yytext); return(TILDE_VLINE); } YY_BREAK case 32: YY_RULE_SETUP #line 268 "tptp5.l" { tptp_prev_tok=VLINE; yylval.ival = tptp_update_lval(yytext); return(VLINE); } YY_BREAK case 33: YY_RULE_SETUP #line 273 "tptp5.l" { tptp_prev_tok=_DLR_cnf; yylval.ival = tptp_update_lval(yytext); return(_DLR_cnf); } YY_BREAK case 34: YY_RULE_SETUP #line 278 "tptp5.l" { tptp_prev_tok=_DLR_fof; yylval.ival = tptp_update_lval(yytext); return(_DLR_fof); } YY_BREAK case 35: YY_RULE_SETUP #line 283 "tptp5.l" { tptp_prev_tok=_DLR_fot; yylval.ival = tptp_update_lval(yytext); return(_DLR_fot); } YY_BREAK case 36: YY_RULE_SETUP #line 288 "tptp5.l" { tptp_prev_tok=_DLR_itef; yylval.ival = tptp_update_lval(yytext); return(_DLR_itef); } YY_BREAK case 37: YY_RULE_SETUP #line 293 "tptp5.l" { tptp_prev_tok=_DLR_itetf; yylval.ival = tptp_update_lval(yytext); return(_DLR_itetf); } YY_BREAK case 38: YY_RULE_SETUP #line 298 "tptp5.l" { tptp_prev_tok=_DLR_itett; yylval.ival = tptp_update_lval(yytext); return(_DLR_itett); } YY_BREAK case 39: YY_RULE_SETUP #line 303 "tptp5.l" { tptp_prev_tok=_DLR_tff; yylval.ival = tptp_update_lval(yytext); return(_DLR_tff); } YY_BREAK case 40: YY_RULE_SETUP #line 308 "tptp5.l" { tptp_prev_tok=_DLR_thf; yylval.ival = tptp_update_lval(yytext); return(_DLR_thf); } YY_BREAK case 41: YY_RULE_SETUP #line 313 "tptp5.l" { BEGIN B; tptp_prev_tok=_LIT_cnf; yylval.ival = tptp_update_lval(yytext); return(_LIT_cnf); } YY_BREAK case 42: YY_RULE_SETUP #line 319 "tptp5.l" { BEGIN B; tptp_prev_tok=_LIT_fof; yylval.ival = tptp_update_lval(yytext); return(_LIT_fof); } YY_BREAK case 43: YY_RULE_SETUP #line 325 "tptp5.l" { BEGIN B; tptp_prev_tok=_LIT_include; yylval.ival = tptp_update_lval(yytext); return(_LIT_include); } YY_BREAK case 44: YY_RULE_SETUP #line 331 "tptp5.l" { BEGIN B; tptp_prev_tok=_LIT_tff; yylval.ival = tptp_update_lval(yytext); return(_LIT_tff); } YY_BREAK case 45: YY_RULE_SETUP #line 337 "tptp5.l" { BEGIN B; tptp_prev_tok=_LIT_thf; yylval.ival = tptp_update_lval(yytext); return(_LIT_thf); } YY_BREAK case 46: YY_RULE_SETUP #line 344 "tptp5.l" { tptp_prev_tok=single_quoted; yylval.ival = tptp_update_lval(yytext); return(single_quoted); } YY_BREAK case 47: YY_RULE_SETUP #line 349 "tptp5.l" { tptp_prev_tok=distinct_object; yylval.ival = tptp_update_lval(yytext); return(distinct_object); } YY_BREAK case 48: YY_RULE_SETUP #line 354 "tptp5.l" { tptp_prev_tok=dollar_word; yylval.ival = tptp_update_lval(yytext); return(dollar_word); } YY_BREAK case 49: YY_RULE_SETUP #line 359 "tptp5.l" { tptp_prev_tok=dollar_dollar_word; yylval.ival = tptp_update_lval(yytext); return(dollar_dollar_word); } YY_BREAK case 50: YY_RULE_SETUP #line 364 "tptp5.l" { tptp_prev_tok=upper_word; yylval.ival = tptp_update_lval(yytext); return(upper_word); } YY_BREAK case 51: YY_RULE_SETUP #line 369 "tptp5.l" { tptp_prev_tok=lower_word; yylval.ival = tptp_update_lval(yytext); return(lower_word); } YY_BREAK case 52: YY_RULE_SETUP #line 374 "tptp5.l" { tptp_prev_tok=vline; yylval.ival = tptp_update_lval(yytext); return(vline); } YY_BREAK case 53: YY_RULE_SETUP #line 379 "tptp5.l" { tptp_prev_tok=star; yylval.ival = tptp_update_lval(yytext); return(star); } YY_BREAK case 54: YY_RULE_SETUP #line 384 "tptp5.l" { tptp_prev_tok=plus; yylval.ival = tptp_update_lval(yytext); return(plus); } YY_BREAK case 55: YY_RULE_SETUP #line 389 "tptp5.l" { tptp_prev_tok=arrow; yylval.ival = tptp_update_lval(yytext); return(arrow); } YY_BREAK case 56: YY_RULE_SETUP #line 394 "tptp5.l" { tptp_prev_tok=less_sign; yylval.ival = tptp_update_lval(yytext); return(less_sign); } YY_BREAK case 57: YY_RULE_SETUP #line 399 "tptp5.l" { tptp_prev_tok=real; yylval.ival = tptp_update_lval(yytext); return(real); } YY_BREAK case 58: YY_RULE_SETUP #line 404 "tptp5.l" { tptp_prev_tok=signed_real; yylval.ival = tptp_update_lval(yytext); return(signed_real); } YY_BREAK case 59: YY_RULE_SETUP #line 409 "tptp5.l" { tptp_prev_tok=unsigned_real; yylval.ival = tptp_update_lval(yytext); return(unsigned_real); } YY_BREAK case 60: YY_RULE_SETUP #line 414 "tptp5.l" { tptp_prev_tok=rational; yylval.ival = tptp_update_lval(yytext); return(rational); } YY_BREAK case 61: YY_RULE_SETUP #line 419 "tptp5.l" { tptp_prev_tok=signed_rational; yylval.ival = tptp_update_lval(yytext); return(signed_rational); } YY_BREAK case 62: YY_RULE_SETUP #line 424 "tptp5.l" { tptp_prev_tok=unsigned_rational; yylval.ival = tptp_update_lval(yytext); return(unsigned_rational); } YY_BREAK case 63: YY_RULE_SETUP #line 429 "tptp5.l" { tptp_prev_tok=integer; yylval.ival = tptp_update_lval(yytext); return(integer); } YY_BREAK case 64: YY_RULE_SETUP #line 434 "tptp5.l" { tptp_prev_tok=signed_integer; yylval.ival = tptp_update_lval(yytext); return(signed_integer); } YY_BREAK case 65: YY_RULE_SETUP #line 439 "tptp5.l" { tptp_prev_tok=unsigned_integer; yylval.ival = tptp_update_lval(yytext); return(unsigned_integer); } YY_BREAK case 66: YY_RULE_SETUP #line 444 "tptp5.l" { tptp_prev_tok=decimal; yylval.ival = tptp_update_lval(yytext); return(decimal); } YY_BREAK case 67: YY_RULE_SETUP #line 449 "tptp5.l" { tptp_prev_tok=positive_decimal; yylval.ival = tptp_update_lval(yytext); return(positive_decimal); } YY_BREAK case 68: YY_RULE_SETUP #line 454 "tptp5.l" { tptp_prev_tok=decimal_exponent; yylval.ival = tptp_update_lval(yytext); return(decimal_exponent); } YY_BREAK case 69: YY_RULE_SETUP #line 459 "tptp5.l" { tptp_prev_tok=decimal_fraction; yylval.ival = tptp_update_lval(yytext); return(decimal_fraction); } YY_BREAK case 70: YY_RULE_SETUP #line 464 "tptp5.l" { tptp_prev_tok=dot_decimal; yylval.ival = tptp_update_lval(yytext); return(dot_decimal); } YY_BREAK case 71: /* rule 71 can match eol */ YY_RULE_SETUP #line 469 "tptp5.l" tptp_update_lval(yytext); YY_BREAK case 72: /* rule 72 can match eol */ YY_RULE_SETUP #line 470 "tptp5.l" ; YY_BREAK case 73: /* rule 73 can match eol */ YY_RULE_SETUP #line 471 "tptp5.l" ; YY_BREAK case 74: YY_RULE_SETUP #line 472 "tptp5.l" return(unrecognized); YY_BREAK case 75: YY_RULE_SETUP #line 473 "tptp5.l" ECHO; YY_BREAK #line 1667 "tptp5.lex.cpp" case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(B): case YY_STATE_EOF(FF): case YY_STATE_EOF(SQ1): case YY_STATE_EOF(SQ2): case YY_STATE_EOF(Q1): case YY_STATE_EOF(Q2): yyterminate(); case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = (yy_hold_char); YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) { /* This was really a NUL. */ yy_state_type yy_next_state; (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state ); yy_bp = (yytext_ptr) + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++(yy_c_buf_p); yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = (yy_c_buf_p); goto yy_find_action; } } else switch ( yy_get_next_buffer( ) ) { case EOB_ACT_END_OF_FILE: { (yy_did_buffer_switch_on_eof) = 0; if ( yywrap( ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: (yy_c_buf_p) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (void) { register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = (yytext_ptr); register int number_to_move, i; int ret_val; if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; else { size_t num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ YY_FATAL_ERROR( "input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), (yy_n_chars), (int)num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } if ( (yy_n_chars) == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart(yyin ); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); } (yy_n_chars) += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (void) { register yy_state_type yy_current_state; register char *yy_cp; yy_current_state = (yy_start); (yy_state_ptr) = (yy_state_buf); *(yy_state_ptr)++ = yy_current_state; for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 152 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; *(yy_state_ptr)++ = yy_current_state; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) { register int yy_is_jam; register YY_CHAR yy_c = 1; while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 152 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 151); if ( ! yy_is_jam ) *(yy_state_ptr)++ = yy_current_state; return yy_is_jam ? 0 : yy_current_state; } static void yyunput (int c, register char * yy_bp ) { register char *yy_cp; yy_cp = (yy_c_buf_p); /* undo effects of setting up yytext */ *yy_cp = (yy_hold_char); if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) { /* need to shift things up to make room */ /* +2 for EOB chars. */ register int number_to_move = (yy_n_chars) + 2; register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; register char *source = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) *--dest = *--source; yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = (int)YY_CURRENT_BUFFER_LVALUE->yy_buf_size; if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); } *--yy_cp = (char) c; if ( c == '\n' ){ --yylineno; } (yytext_ptr) = yy_bp; (yy_hold_char) = *yy_cp; (yy_c_buf_p) = yy_cp; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void) #else static int input (void) #endif { int c; *(yy_c_buf_p) = (yy_hold_char); if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) /* This was really a NUL. */ *(yy_c_buf_p) = '\0'; else { /* need more input */ size_t offset = (yy_c_buf_p) - (yytext_ptr); ++(yy_c_buf_p); switch ( yy_get_next_buffer( ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yyrestart(yyin ); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yywrap( ) ) return EOF; if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(); #else return input(); #endif } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + offset; break; } } } c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ *(yy_c_buf_p) = '\0'; /* preserve yytext */ (yy_hold_char) = *++(yy_c_buf_p); if ( c == '\n' ) yylineno++; ; return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * * @note This function does not reset the start condition to @c INITIAL . */ void yyrestart (FILE * input_file ) { if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer(yyin,YY_BUF_SIZE ); } yy_init_buffer(YY_CURRENT_BUFFER,input_file ); yy_load_buffer_state( ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * */ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) { /* TODO. We should be able to replace this entire function body * with * yypop_buffer_state(); * yypush_buffer_state(new_buffer); */ yyensure_buffer_stack (); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } YY_CURRENT_BUFFER_LVALUE = new_buffer; yy_load_buffer_state( ); /* We don't actually know whether we did this switch during * EOF (yywrap()) processing, but the only time this flag * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ (yy_did_buffer_switch_on_eof) = 1; } static void yy_load_buffer_state (void) { (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; (yy_hold_char) = *(yy_c_buf_p); } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * * @return the allocated buffer state. */ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer(b,file ); return b; } /** Destroy the buffer. * @param b a buffer created with yy_create_buffer() * */ void yy_delete_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yyfree((void *) b->yy_ch_buf ); yyfree((void *) b ); } #ifndef __cplusplus extern int isatty (int ); #endif /* __cplusplus */ /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a yyrestart() or at EOF. */ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) { int oerrno = errno; yy_flush_buffer(b ); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then yy_init_buffer was _probably_ * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } #ifdef _WINDOWS b->yy_is_interactive = file ? (isatty( _fileno(file) ) > 0) : 0; #else b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; #endif errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * */ void yy_flush_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) yy_load_buffer_state( ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * */ void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) { if (new_buffer == NULL) return; yyensure_buffer_stack(); /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) (yy_buffer_stack_top)++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from yy_switch_to_buffer. */ yy_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * */ void yypop_buffer_state (void) { if (!YY_CURRENT_BUFFER) return; yy_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; if ((yy_buffer_stack_top) > 0) --(yy_buffer_stack_top); if (YY_CURRENT_BUFFER) { yy_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void yyensure_buffer_stack (void) { size_t num_to_alloc; if (!(yy_buffer_stack)) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) ); if ( ! (yy_buffer_stack) ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; (yy_buffer_stack_top) = 0; return; } if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = (yy_buffer_stack_max) + grow_size; (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc ((yy_buffer_stack), num_to_alloc * sizeof(struct yy_buffer_state*) ); if ( ! (yy_buffer_stack) ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = (int)b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; yy_switch_to_buffer(b ); return b; } /** Setup the input buffer state to scan a string. The next call to yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * yy_scan_bytes() instead. */ YY_BUFFER_STATE yy_scan_string (yyconst char * yystr ) { return yy_scan_bytes(yystr,(int)strlen(yystr) ); } /** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. * @param bytes the byte buffer to scan * @param len the number of bytes in the buffer pointed to by @a bytes. * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) yyalloc(n ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = yy_scan_buffer(buf,n ); if ( ! b ) YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg ) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = (yy_hold_char); \ (yy_c_buf_p) = yytext + yyless_macro_arg; \ (yy_hold_char) = *(yy_c_buf_p); \ *(yy_c_buf_p) = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the current line number. * */ int yyget_lineno (void) { return yylineno; } /** Get the input stream. * */ FILE *yyget_in (void) { return yyin; } /** Get the output stream. * */ FILE *yyget_out (void) { return yyout; } /** Get the length of the current token. * */ int yyget_leng (void) { return yyleng; } /** Get the current token. * */ char *yyget_text (void) { return yytext; } /** Set the current line number. * @param line_number * */ void yyset_lineno (int line_number ) { yylineno = line_number; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * * @see yy_switch_to_buffer */ void yyset_in (FILE * in_str ) { yyin = in_str ; } void yyset_out (FILE * out_str ) { yyout = out_str ; } int yyget_debug (void) { return yy_flex_debug; } void yyset_debug (int bdebug ) { yy_flex_debug = bdebug ; } static int yy_init_globals (void) { /* Initialization is the same as for the non-reentrant scanner. * This function is called from yylex_destroy(), so don't allocate here. */ /* We do not touch yylineno unless the option is enabled. */ yylineno = 1; (yy_buffer_stack) = 0; (yy_buffer_stack_top) = 0; (yy_buffer_stack_max) = 0; (yy_c_buf_p) = (char *) 0; (yy_init) = 0; (yy_start) = 0; (yy_state_buf) = 0; (yy_state_ptr) = 0; (yy_full_match) = 0; (yy_lp) = 0; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = (FILE *) 0; yyout = (FILE *) 0; #endif /* For future reference: Set errno on error, since we are called by * yylex_init() */ return 0; } /* yylex_destroy is for both reentrant and non-reentrant scanners. */ int yylex_destroy (void) { /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ yy_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; yypop_buffer_state(); } /* Destroy the stack itself. */ yyfree((yy_buffer_stack) ); (yy_buffer_stack) = NULL; yyfree ( (yy_state_buf) ); (yy_state_buf) = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * yylex() is called, initialization will occur. */ yy_init_globals( ); return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s ) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yyalloc (yy_size_t size ) { return (void *) malloc( size ); } void *yyrealloc (void * ptr, yy_size_t size ) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } void yyfree (void * ptr ) { free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 473 "tptp5.l" z3-4.4.0/examples/java/0000755000175000017500000000000012540347414014404 5ustar michaelmichaelz3-4.4.0/examples/java/README0000644000175000017500000000104312540347414015262 0ustar michaelmichaelA small example using the Z3 Java bindings. To build the example, configure Z3 with the --java option to scripts/mk_make.py, build via make examples in the build directory. It will create JavaExample.class in the build directory, which can be run on Windows via java -cp com.microsoft.z3.jar;. JavaExample On Linux and FreeBSD, we must use LD_LIBRARY_PATH=. java -cp com.microsoft.z3.jar:. JavaExample On OSX, the corresponding option is DYLD_LIBRARY_PATH: DYLD_LIBRARY_PATH=. java -cp com.microsoft.z3.jar:. JavaExample z3-4.4.0/examples/java/JavaExample.java0000644000175000017500000023736312540347414017462 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Program.java Abstract: Z3 Java API: Example program Author: Christoph Wintersteiger (cwinter) 2012-11-27 Notes: --*/ import java.util.*; import com.microsoft.z3.*; class JavaExample { @SuppressWarnings("serial") class TestFailedException extends Exception { public TestFailedException() { super("Check FAILED"); } }; // / Create axiom: function f is injective in the i-th argument. // / // / The following axiom is produced: // / // / forall (x_0, ..., x_n) finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i // / // / Where, finvis a fresh function declaration. public BoolExpr injAxiom(Context ctx, FuncDecl f, int i) { Sort[] domain = f.getDomain(); int sz = f.getDomainSize(); if (i >= sz) { System.out.println("failed to create inj axiom"); return null; } /* declare the i-th inverse of f: finv */ Sort finv_domain = f.getRange(); Sort finv_range = domain[i]; FuncDecl finv = ctx.mkFuncDecl("f_fresh", finv_domain, finv_range); /* allocate temporary arrays */ Expr[] xs = new Expr[sz]; Symbol[] names = new Symbol[sz]; Sort[] types = new Sort[sz]; /* fill types, names and xs */ for (int j = 0; j < sz; j++) { types[j] = domain[j]; names[j] = ctx.mkSymbol("x_" + Integer.toString(j)); xs[j] = ctx.mkBound(j, types[j]); } Expr x_i = xs[i]; /* create f(x_0, ..., x_i, ..., x_{n-1}) */ Expr fxs = f.apply(xs); /* create f_inv(f(x_0, ..., x_i, ..., x_{n-1})) */ Expr finv_fxs = finv.apply(fxs); /* create finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i */ Expr eq = ctx.mkEq(finv_fxs, x_i); /* use f(x_0, ..., x_i, ..., x_{n-1}) as the pattern for the quantifier */ Pattern p = ctx.mkPattern(fxs); /* create & assert quantifier */ BoolExpr q = ctx.mkForall(types, /* types of quantified variables */ names, /* names of quantified variables */ eq, 1, new Pattern[] { p } /* patterns */, null, null, null); return q; } // / Create axiom: function f is injective in the i-th argument. // / // / The following axiom is produced: // / // / forall (x_0, ..., x_n) finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i // / // / Where, finvis a fresh function declaration. public BoolExpr injAxiomAbs(Context ctx, FuncDecl f, int i) { Sort[] domain = f.getDomain(); int sz = f.getDomainSize(); if (i >= sz) { System.out.println("failed to create inj axiom"); return null; } /* declare the i-th inverse of f: finv */ Sort finv_domain = f.getRange(); Sort finv_range = domain[i]; FuncDecl finv = ctx.mkFuncDecl("f_fresh", finv_domain, finv_range); /* allocate temporary arrays */ Expr[] xs = new Expr[sz]; /* fill types, names and xs */ for (int j = 0; j < sz; j++) { xs[j] = ctx.mkConst("x_" + Integer.toString(j), domain[j]); } Expr x_i = xs[i]; /* create f(x_0, ..., x_i, ..., x_{n-1}) */ Expr fxs = f.apply(xs); /* create f_inv(f(x_0, ..., x_i, ..., x_{n-1})) */ Expr finv_fxs = finv.apply(fxs); /* create finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i */ Expr eq = ctx.mkEq(finv_fxs, x_i); /* use f(x_0, ..., x_i, ..., x_{n-1}) as the pattern for the quantifier */ Pattern p = ctx.mkPattern(fxs); /* create & assert quantifier */ BoolExpr q = ctx.mkForall(xs, /* types of quantified variables */ eq, /* names of quantified variables */ 1, new Pattern[] { p } /* patterns */, null, null, null); return q; } // / Assert the axiom: function f is commutative. // / // / This example uses the SMT-LIB parser to simplify the axiom // construction. // / private BoolExpr commAxiom(Context ctx, FuncDecl f) throws Exception { Sort t = f.getRange(); Sort[] dom = f.getDomain(); if (dom.length != 2 || !t.equals(dom[0]) || !t.equals(dom[1])) { System.out.println(Integer.toString(dom.length) + " " + dom[0].toString() + " " + dom[1].toString() + " " + t.toString()); throw new Exception( "function must be binary, and argument types must be equal to return type"); } String bench = "(benchmark comm :formula (forall (x " + t.getName() + ") (y " + t.getName() + ") (= (" + f.getName() + " x y) (" + f.getName() + " y x))))"; ctx.parseSMTLIBString(bench, new Symbol[] { t.getName() }, new Sort[] { t }, new Symbol[] { f.getName() }, new FuncDecl[] { f }); return ctx.getSMTLIBFormulas()[0]; } // / "Hello world" example: create a Z3 logical context, and delete it. public void simpleExample() { System.out.println("SimpleExample"); Log.append("SimpleExample"); { Context ctx = new Context(); /* do something with the context */ /* be kind to dispose manually and not wait for the GC. */ ctx.dispose(); } } Model check(Context ctx, BoolExpr f, Status sat) throws TestFailedException { Solver s = ctx.mkSolver(); s.add(f); if (s.check() != sat) throw new TestFailedException(); if (sat == Status.SATISFIABLE) return s.getModel(); else return null; } void solveTactical(Context ctx, Tactic t, Goal g, Status sat) throws TestFailedException { Solver s = ctx.mkSolver(t); System.out.println("\nTactical solver: " + s); for (BoolExpr a : g.getFormulas()) s.add(a); System.out.println("Solver: " + s); if (s.check() != sat) throw new TestFailedException(); } ApplyResult applyTactic(Context ctx, Tactic t, Goal g) { System.out.println("\nGoal: " + g); ApplyResult res = t.apply(g); System.out.println("Application result: " + res); Status q = Status.UNKNOWN; for (Goal sg : res.getSubgoals()) if (sg.isDecidedSat()) q = Status.SATISFIABLE; else if (sg.isDecidedUnsat()) q = Status.UNSATISFIABLE; switch (q) { case UNKNOWN: System.out.println("Tactic result: Undecided"); break; case SATISFIABLE: System.out.println("Tactic result: SAT"); break; case UNSATISFIABLE: System.out.println("Tactic result: UNSAT"); break; } return res; } void prove(Context ctx, BoolExpr f, boolean useMBQI) throws TestFailedException { BoolExpr[] assumptions = new BoolExpr[0]; prove(ctx, f, useMBQI, assumptions); } void prove(Context ctx, BoolExpr f, boolean useMBQI, BoolExpr... assumptions) throws TestFailedException { System.out.println("Proving: " + f); Solver s = ctx.mkSolver(); Params p = ctx.mkParams(); p.add("mbqi", useMBQI); s.setParameters(p); for (BoolExpr a : assumptions) s.add(a); s.add(ctx.mkNot(f)); Status q = s.check(); switch (q) { case UNKNOWN: System.out.println("Unknown because: " + s.getReasonUnknown()); break; case SATISFIABLE: throw new TestFailedException(); case UNSATISFIABLE: System.out.println("OK, proof: " + s.getProof()); break; } } void disprove(Context ctx, BoolExpr f, boolean useMBQI) throws TestFailedException { BoolExpr[] a = {}; disprove(ctx, f, useMBQI, a); } void disprove(Context ctx, BoolExpr f, boolean useMBQI, BoolExpr... assumptions) throws TestFailedException { System.out.println("Disproving: " + f); Solver s = ctx.mkSolver(); Params p = ctx.mkParams(); p.add("mbqi", useMBQI); s.setParameters(p); for (BoolExpr a : assumptions) s.add(a); s.add(ctx.mkNot(f)); Status q = s.check(); switch (q) { case UNKNOWN: System.out.println("Unknown because: " + s.getReasonUnknown()); break; case SATISFIABLE: System.out.println("OK, model: " + s.getModel()); break; case UNSATISFIABLE: throw new TestFailedException(); } } void modelConverterTest(Context ctx) throws TestFailedException { System.out.println("ModelConverterTest"); ArithExpr xr = (ArithExpr) ctx.mkConst(ctx.mkSymbol("x"), ctx.mkRealSort()); ArithExpr yr = (ArithExpr) ctx.mkConst(ctx.mkSymbol("y"), ctx.mkRealSort()); Goal g4 = ctx.mkGoal(true, false, false); g4.add(ctx.mkGt(xr, ctx.mkReal(10, 1))); g4.add(ctx.mkEq(yr, ctx.mkAdd(xr, ctx.mkReal(1, 1)))); g4.add(ctx.mkGt(yr, ctx.mkReal(1, 1))); ApplyResult ar = applyTactic(ctx, ctx.mkTactic("simplify"), g4); if (ar.getNumSubgoals() == 1 && (ar.getSubgoals()[0].isDecidedSat() || ar.getSubgoals()[0] .isDecidedUnsat())) throw new TestFailedException(); ar = applyTactic(ctx, ctx.andThen(ctx.mkTactic("simplify"), ctx.mkTactic("solve-eqs")), g4); if (ar.getNumSubgoals() == 1 && (ar.getSubgoals()[0].isDecidedSat() || ar.getSubgoals()[0] .isDecidedUnsat())) throw new TestFailedException(); Solver s = ctx.mkSolver(); for (BoolExpr e : ar.getSubgoals()[0].getFormulas()) s.add(e); Status q = s.check(); System.out.println("Solver says: " + q); System.out.println("Model: \n" + s.getModel()); System.out.println("Converted Model: \n" + ar.convertModel(0, s.getModel())); if (q != Status.SATISFIABLE) throw new TestFailedException(); } // / A simple array example. void arrayExample1(Context ctx) throws TestFailedException { System.out.println("ArrayExample1"); Log.append("ArrayExample1"); Goal g = ctx.mkGoal(true, false, false); ArraySort asort = ctx.mkArraySort(ctx.getIntSort(), ctx.mkBitVecSort(32)); ArrayExpr aex = (ArrayExpr) ctx.mkConst(ctx.mkSymbol("MyArray"), asort); Expr sel = ctx.mkSelect(aex, ctx.mkInt(0)); g.add(ctx.mkEq(sel, ctx.mkBV(42, 32))); Symbol xs = ctx.mkSymbol("x"); IntExpr xc = (IntExpr) ctx.mkConst(xs, ctx.getIntSort()); Symbol fname = ctx.mkSymbol("f"); Sort[] domain = { ctx.getIntSort() }; FuncDecl fd = ctx.mkFuncDecl(fname, domain, ctx.getIntSort()); Expr[] fargs = { ctx.mkConst(xs, ctx.getIntSort()) }; IntExpr fapp = (IntExpr) ctx.mkApp(fd, fargs); g.add(ctx.mkEq(ctx.mkAdd(xc, fapp), ctx.mkInt(123))); Solver s = ctx.mkSolver(); for (BoolExpr a : g.getFormulas()) s.add(a); System.out.println("Solver: " + s); Status q = s.check(); System.out.println("Status: " + q); if (q != Status.SATISFIABLE) throw new TestFailedException(); System.out.println("Model = " + s.getModel()); System.out.println("Interpretation of MyArray:\n" + s.getModel().getFuncInterp(aex.getFuncDecl())); System.out.println("Interpretation of x:\n" + s.getModel().getConstInterp(xc)); System.out.println("Interpretation of f:\n" + s.getModel().getFuncInterp(fd)); System.out.println("Interpretation of MyArray as Term:\n" + s.getModel().getFuncInterp(aex.getFuncDecl())); } // / Prove store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 // = i3 or select(a1, i3) = select(a2, i3)). // / This example demonstrates how to use the array // theory. public void arrayExample2(Context ctx) throws TestFailedException { System.out.println("ArrayExample2"); Log.append("ArrayExample2"); Sort int_type = ctx.getIntSort(); Sort array_type = ctx.mkArraySort(int_type, int_type); ArrayExpr a1 = (ArrayExpr) ctx.mkConst("a1", array_type); ArrayExpr a2 = ctx.mkArrayConst("a2", int_type, int_type); Expr i1 = ctx.mkConst("i1", int_type); Expr i2 = ctx.mkConst("i2", int_type); Expr i3 = ctx.mkConst("i3", int_type); Expr v1 = ctx.mkConst("v1", int_type); Expr v2 = ctx.mkConst("v2", int_type); Expr st1 = ctx.mkStore(a1, i1, v1); Expr st2 = ctx.mkStore(a2, i2, v2); Expr sel1 = ctx.mkSelect(a1, i3); Expr sel2 = ctx.mkSelect(a2, i3); /* create antecedent */ BoolExpr antecedent = ctx.mkEq(st1, st2); /* * create consequent: i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, * i3) */ BoolExpr consequent = ctx.mkOr(ctx.mkEq(i1, i3), ctx.mkEq(i2, i3), ctx.mkEq(sel1, sel2)); /* * prove store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = * i3 or select(a1, i3) = select(a2, i3)) */ BoolExpr thm = ctx.mkImplies(antecedent, consequent); System.out .println("prove: store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3))"); System.out.println(thm); prove(ctx, thm, false); } // / Show that distinct(a_0, ... , a_n) is // / unsatisfiable when a_i's are arrays from boolean to // / boolean and n > 4. // / This example also shows how to use the distinct // construct. public void arrayExample3(Context ctx) throws TestFailedException { System.out.println("ArrayExample3"); Log.append("ArrayExample2"); for (int n = 2; n <= 5; n++) { System.out.println("n = " + Integer.toString(n)); Sort bool_type = ctx.mkBoolSort(); Sort array_type = ctx.mkArraySort(bool_type, bool_type); Expr[] a = new Expr[n]; /* create arrays */ for (int i = 0; i < n; i++) { a[i] = ctx.mkConst("array_" + Integer.toString(i), array_type); } /* assert distinct(a[0], ..., a[n]) */ BoolExpr d = ctx.mkDistinct(a); System.out.println(d); /* context is satisfiable if n < 5 */ Model model = check(ctx, d, n < 5 ? Status.SATISFIABLE : Status.UNSATISFIABLE); if (n < 5) { for (int i = 0; i < n; i++) { System.out.println(a[i].toString() + " = " + model.evaluate(a[i], false)); } } } } // / Sudoku solving example. void sudokuExample(Context ctx) throws TestFailedException { System.out.println("SudokuExample"); Log.append("SudokuExample"); // 9x9 matrix of integer variables IntExpr[][] X = new IntExpr[9][]; for (int i = 0; i < 9; i++) { X[i] = new IntExpr[9]; for (int j = 0; j < 9; j++) X[i][j] = (IntExpr) ctx.mkConst( ctx.mkSymbol("x_" + (i + 1) + "_" + (j + 1)), ctx.getIntSort()); } // each cell contains a value in {1, ..., 9} BoolExpr[][] cells_c = new BoolExpr[9][]; for (int i = 0; i < 9; i++) { cells_c[i] = new BoolExpr[9]; for (int j = 0; j < 9; j++) cells_c[i][j] = ctx.mkAnd(ctx.mkLe(ctx.mkInt(1), X[i][j]), ctx.mkLe(X[i][j], ctx.mkInt(9))); } // each row contains a digit at most once BoolExpr[] rows_c = new BoolExpr[9]; for (int i = 0; i < 9; i++) rows_c[i] = ctx.mkDistinct(X[i]); // each column contains a digit at most once BoolExpr[] cols_c = new BoolExpr[9]; for (int j = 0; j < 9; j++) cols_c[j] = ctx.mkDistinct(X[j]); // each 3x3 square contains a digit at most once BoolExpr[][] sq_c = new BoolExpr[3][]; for (int i0 = 0; i0 < 3; i0++) { sq_c[i0] = new BoolExpr[3]; for (int j0 = 0; j0 < 3; j0++) { IntExpr[] square = new IntExpr[9]; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) square[3 * i + j] = X[3 * i0 + i][3 * j0 + j]; sq_c[i0][j0] = ctx.mkDistinct(square); } } BoolExpr sudoku_c = ctx.mkTrue(); for (BoolExpr[] t : cells_c) sudoku_c = ctx.mkAnd(ctx.mkAnd(t), sudoku_c); sudoku_c = ctx.mkAnd(ctx.mkAnd(rows_c), sudoku_c); sudoku_c = ctx.mkAnd(ctx.mkAnd(cols_c), sudoku_c); for (BoolExpr[] t : sq_c) sudoku_c = ctx.mkAnd(ctx.mkAnd(t), sudoku_c); // sudoku instance, we use '0' for empty cells int[][] instance = { { 0, 0, 0, 0, 9, 4, 0, 3, 0 }, { 0, 0, 0, 5, 1, 0, 0, 0, 7 }, { 0, 8, 9, 0, 0, 0, 0, 4, 0 }, { 0, 0, 0, 0, 0, 0, 2, 0, 8 }, { 0, 6, 0, 2, 0, 1, 0, 5, 0 }, { 1, 0, 2, 0, 0, 0, 0, 0, 0 }, { 0, 7, 0, 0, 0, 0, 5, 2, 0 }, { 9, 0, 0, 0, 6, 5, 0, 0, 0 }, { 0, 4, 0, 9, 7, 0, 0, 0, 0 } }; BoolExpr instance_c = ctx.mkTrue(); for (int i = 0; i < 9; i++) for (int j = 0; j < 9; j++) instance_c = ctx.mkAnd( instance_c, (BoolExpr) ctx.mkITE( ctx.mkEq(ctx.mkInt(instance[i][j]), ctx.mkInt(0)), ctx.mkTrue(), ctx.mkEq(X[i][j], ctx.mkInt(instance[i][j])))); Solver s = ctx.mkSolver(); s.add(sudoku_c); s.add(instance_c); if (s.check() == Status.SATISFIABLE) { Model m = s.getModel(); Expr[][] R = new Expr[9][9]; for (int i = 0; i < 9; i++) for (int j = 0; j < 9; j++) R[i][j] = m.evaluate(X[i][j], false); System.out.println("Sudoku solution:"); for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) System.out.print(" " + R[i][j]); System.out.println(); } } else { System.out.println("Failed to solve sudoku"); throw new TestFailedException(); } } // / A basic example of how to use quantifiers. void quantifierExample1(Context ctx) { System.out.println("QuantifierExample"); Log.append("QuantifierExample"); Sort[] types = new Sort[3]; IntExpr[] xs = new IntExpr[3]; Symbol[] names = new Symbol[3]; IntExpr[] vars = new IntExpr[3]; for (int j = 0; j < 3; j++) { types[j] = ctx.getIntSort(); names[j] = ctx.mkSymbol("x_" + Integer.toString(j)); xs[j] = (IntExpr) ctx.mkConst(names[j], types[j]); vars[j] = (IntExpr) ctx.mkBound(2 - j, types[j]); // <-- vars // reversed! } Expr body_vars = ctx.mkAnd( ctx.mkEq(ctx.mkAdd(vars[0], ctx.mkInt(1)), ctx.mkInt(2)), ctx.mkEq(ctx.mkAdd(vars[1], ctx.mkInt(2)), ctx.mkAdd(vars[2], ctx.mkInt(3)))); Expr body_const = ctx.mkAnd( ctx.mkEq(ctx.mkAdd(xs[0], ctx.mkInt(1)), ctx.mkInt(2)), ctx.mkEq(ctx.mkAdd(xs[1], ctx.mkInt(2)), ctx.mkAdd(xs[2], ctx.mkInt(3)))); Expr x = ctx.mkForall(types, names, body_vars, 1, null, null, ctx.mkSymbol("Q1"), ctx.mkSymbol("skid1")); System.out.println("Quantifier X: " + x.toString()); Expr y = ctx.mkForall(xs, body_const, 1, null, null, ctx.mkSymbol("Q2"), ctx.mkSymbol("skid2")); System.out.println("Quantifier Y: " + y.toString()); } void quantifierExample2(Context ctx) { System.out.println("QuantifierExample2"); Log.append("QuantifierExample2"); Expr q1, q2; FuncDecl f = ctx.mkFuncDecl("f", ctx.getIntSort(), ctx.getIntSort()); FuncDecl g = ctx.mkFuncDecl("g", ctx.getIntSort(), ctx.getIntSort()); // Quantifier with Exprs as the bound variables. { Expr x = ctx.mkConst("x", ctx.getIntSort()); Expr y = ctx.mkConst("y", ctx.getIntSort()); Expr f_x = ctx.mkApp(f, x); Expr f_y = ctx.mkApp(f, y); Expr g_y = ctx.mkApp(g, y); @SuppressWarnings("unused") Pattern[] pats = new Pattern[] { ctx.mkPattern(f_x, g_y) }; Expr[] no_pats = new Expr[] { f_y }; Expr[] bound = new Expr[] { x, y }; Expr body = ctx.mkAnd(ctx.mkEq(f_x, f_y), ctx.mkEq(f_y, g_y)); q1 = ctx.mkForall(bound, body, 1, null, no_pats, ctx.mkSymbol("q"), ctx.mkSymbol("sk")); System.out.println(q1); } // Quantifier with de-Brujin indices. { Expr x = ctx.mkBound(1, ctx.getIntSort()); Expr y = ctx.mkBound(0, ctx.getIntSort()); Expr f_x = ctx.mkApp(f, x); Expr f_y = ctx.mkApp(f, y); Expr g_y = ctx.mkApp(g, y); @SuppressWarnings("unused") Pattern[] pats = new Pattern[] { ctx.mkPattern(f_x, g_y) }; Expr[] no_pats = new Expr[] { f_y }; Symbol[] names = new Symbol[] { ctx.mkSymbol("x"), ctx.mkSymbol("y") }; Sort[] sorts = new Sort[] { ctx.getIntSort(), ctx.getIntSort() }; Expr body = ctx.mkAnd(ctx.mkEq(f_x, f_y), ctx.mkEq(f_y, g_y)); q2 = ctx.mkForall(sorts, names, body, 1, null, // pats, no_pats, ctx.mkSymbol("q"), ctx.mkSymbol("sk")); System.out.println(q2); } System.out.println(q1.equals(q2)); } // / Prove that f(x, y) = f(w, v) implies y = v when // / f is injective in the second argument. public void quantifierExample3(Context ctx) throws TestFailedException { System.out.println("QuantifierExample3"); Log.append("QuantifierExample3"); /* * If quantified formulas are asserted in a logical context, then the * model produced by Z3 should be viewed as a potential model. */ /* declare function f */ Sort I = ctx.getIntSort(); FuncDecl f = ctx.mkFuncDecl("f", new Sort[] { I, I }, I); /* f is injective in the second argument. */ BoolExpr inj = injAxiom(ctx, f, 1); /* create x, y, v, w, fxy, fwv */ Expr x = ctx.mkIntConst("x"); Expr y = ctx.mkIntConst("y"); Expr v = ctx.mkIntConst("v"); Expr w = ctx.mkIntConst("w"); Expr fxy = ctx.mkApp(f, x, y); Expr fwv = ctx.mkApp(f, w, v); /* f(x, y) = f(w, v) */ BoolExpr p1 = ctx.mkEq(fxy, fwv); /* prove f(x, y) = f(w, v) implies y = v */ BoolExpr p2 = ctx.mkEq(y, v); prove(ctx, p2, false, inj, p1); /* disprove f(x, y) = f(w, v) implies x = w */ BoolExpr p3 = ctx.mkEq(x, w); disprove(ctx, p3, false, inj, p1); } // / Prove that f(x, y) = f(w, v) implies y = v when // / f is injective in the second argument. public void quantifierExample4(Context ctx) throws TestFailedException { System.out.println("QuantifierExample4"); Log.append("QuantifierExample4"); /* * If quantified formulas are asserted in a logical context, then the * model produced by Z3 should be viewed as a potential model. */ /* declare function f */ Sort I = ctx.getIntSort(); FuncDecl f = ctx.mkFuncDecl("f", new Sort[] { I, I }, I); /* f is injective in the second argument. */ BoolExpr inj = injAxiomAbs(ctx, f, 1); /* create x, y, v, w, fxy, fwv */ Expr x = ctx.mkIntConst("x"); Expr y = ctx.mkIntConst("y"); Expr v = ctx.mkIntConst("v"); Expr w = ctx.mkIntConst("w"); Expr fxy = ctx.mkApp(f, x, y); Expr fwv = ctx.mkApp(f, w, v); /* f(x, y) = f(w, v) */ BoolExpr p1 = ctx.mkEq(fxy, fwv); /* prove f(x, y) = f(w, v) implies y = v */ BoolExpr p2 = ctx.mkEq(y, v); prove(ctx, p2, false, inj, p1); /* disprove f(x, y) = f(w, v) implies x = w */ BoolExpr p3 = ctx.mkEq(x, w); disprove(ctx, p3, false, inj, p1); } // / Some basic tests. void basicTests(Context ctx) throws TestFailedException { System.out.println("BasicTests"); Symbol fname = ctx.mkSymbol("f"); Symbol x = ctx.mkSymbol("x"); Symbol y = ctx.mkSymbol("y"); Sort bs = ctx.mkBoolSort(); Sort[] domain = { bs, bs }; FuncDecl f = ctx.mkFuncDecl(fname, domain, bs); Expr fapp = ctx.mkApp(f, ctx.mkConst(x, bs), ctx.mkConst(y, bs)); Expr[] fargs2 = { ctx.mkFreshConst("cp", bs) }; Sort[] domain2 = { bs }; Expr fapp2 = ctx.mkApp(ctx.mkFreshFuncDecl("fp", domain2, bs), fargs2); BoolExpr trivial_eq = ctx.mkEq(fapp, fapp); BoolExpr nontrivial_eq = ctx.mkEq(fapp, fapp2); Goal g = ctx.mkGoal(true, false, false); g.add(trivial_eq); g.add(nontrivial_eq); System.out.println("Goal: " + g); Solver solver = ctx.mkSolver(); for (BoolExpr a : g.getFormulas()) solver.add(a); if (solver.check() != Status.SATISFIABLE) throw new TestFailedException(); ApplyResult ar = applyTactic(ctx, ctx.mkTactic("simplify"), g); if (ar.getNumSubgoals() == 1 && (ar.getSubgoals()[0].isDecidedSat() || ar.getSubgoals()[0] .isDecidedUnsat())) throw new TestFailedException(); ar = applyTactic(ctx, ctx.mkTactic("smt"), g); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedSat()) throw new TestFailedException(); g.add(ctx.mkEq(ctx.mkNumeral(1, ctx.mkBitVecSort(32)), ctx.mkNumeral(2, ctx.mkBitVecSort(32)))); ar = applyTactic(ctx, ctx.mkTactic("smt"), g); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedUnsat()) throw new TestFailedException(); Goal g2 = ctx.mkGoal(true, true, false); ar = applyTactic(ctx, ctx.mkTactic("smt"), g2); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedSat()) throw new TestFailedException(); g2 = ctx.mkGoal(true, true, false); g2.add(ctx.mkFalse()); ar = applyTactic(ctx, ctx.mkTactic("smt"), g2); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedUnsat()) throw new TestFailedException(); Goal g3 = ctx.mkGoal(true, true, false); Expr xc = ctx.mkConst(ctx.mkSymbol("x"), ctx.getIntSort()); Expr yc = ctx.mkConst(ctx.mkSymbol("y"), ctx.getIntSort()); g3.add(ctx.mkEq(xc, ctx.mkNumeral(1, ctx.getIntSort()))); g3.add(ctx.mkEq(yc, ctx.mkNumeral(2, ctx.getIntSort()))); BoolExpr constr = ctx.mkEq(xc, yc); g3.add(constr); ar = applyTactic(ctx, ctx.mkTactic("smt"), g3); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedUnsat()) throw new TestFailedException(); modelConverterTest(ctx); // Real num/den test. RatNum rn = ctx.mkReal(42, 43); Expr inum = rn.getNumerator(); Expr iden = rn.getDenominator(); System.out.println("Numerator: " + inum + " Denominator: " + iden); if (!inum.toString().equals("42") || !iden.toString().equals("43")) throw new TestFailedException(); if (!rn.toDecimalString(3).toString().equals("0.976?")) throw new TestFailedException(); bigIntCheck(ctx, ctx.mkReal("-1231231232/234234333")); bigIntCheck(ctx, ctx.mkReal("-123123234234234234231232/234234333")); bigIntCheck(ctx, ctx.mkReal("-234234333")); bigIntCheck(ctx, ctx.mkReal("234234333/2")); String bn = "1234567890987654321"; if (!ctx.mkInt(bn).getBigInteger().toString().equals(bn)) throw new TestFailedException(); if (!ctx.mkBV(bn, 128).getBigInteger().toString().equals(bn)) throw new TestFailedException(); if (ctx.mkBV(bn, 32).getBigInteger().toString().equals(bn)) throw new TestFailedException(); // Error handling test. try { @SuppressWarnings("unused") IntExpr i = ctx.mkInt("1/2"); throw new TestFailedException(); // unreachable } catch (Z3Exception e) { } } // / Some basic expression casting tests. void castingTest(Context ctx) throws TestFailedException { System.out.println("CastingTest"); Sort[] domain = { ctx.getBoolSort(), ctx.getBoolSort() }; FuncDecl f = ctx.mkFuncDecl("f", domain, ctx.getBoolSort()); AST upcast = ctx.mkFuncDecl(ctx.mkSymbol("q"), domain, ctx.getBoolSort()); try { @SuppressWarnings("unused") FuncDecl downcast = (FuncDecl) f; // OK } catch (ClassCastException e) { throw new TestFailedException(); } try { @SuppressWarnings("unused") Expr uc = (Expr) upcast; throw new TestFailedException(); // should not be reachable! } catch (ClassCastException e) { } Symbol s = ctx.mkSymbol(42); IntSymbol si = (s.getClass() == IntSymbol.class) ? (IntSymbol) s : null; if (si == null) throw new TestFailedException(); try { @SuppressWarnings("unused") IntSymbol si2 = (IntSymbol) s; } catch (ClassCastException e) { throw new TestFailedException(); } s = ctx.mkSymbol("abc"); StringSymbol ss = (s.getClass() == StringSymbol.class) ? (StringSymbol) s : null; if (ss == null) throw new TestFailedException(); try { @SuppressWarnings("unused") StringSymbol ss2 = (StringSymbol) s; } catch (ClassCastException e) { throw new TestFailedException(); } try { @SuppressWarnings("unused") IntSymbol si2 = (IntSymbol) s; throw new TestFailedException(); // unreachable } catch (Exception e) { } Sort srt = ctx.mkBitVecSort(32); BitVecSort bvs = null; try { bvs = (BitVecSort) srt; } catch (ClassCastException e) { throw new TestFailedException(); } if (bvs.getSize() != 32) throw new TestFailedException(); Expr q = ctx.mkAdd(ctx.mkInt(1), ctx.mkInt(2)); Expr q2 = q.getArgs()[1]; Sort qs = q2.getSort(); if (qs.getClass() != IntSort.class) throw new TestFailedException(); try { @SuppressWarnings("unused") IntSort isrt = (IntSort) qs; } catch (ClassCastException e) { throw new TestFailedException(); } AST a = ctx.mkInt(42); try { Expr.class.cast(a); } catch (ClassCastException e) { throw new TestFailedException(); } try { ArithExpr.class.cast(a); } catch (ClassCastException e) { throw new TestFailedException(); } try { IntExpr.class.cast(a); } catch (ClassCastException e) { throw new TestFailedException(); } try { IntNum.class.cast(a); } catch (ClassCastException e) { throw new TestFailedException(); } Expr[][] earr = new Expr[2][]; earr[0] = new Expr[2]; earr[1] = new Expr[2]; earr[0][0] = ctx.mkTrue(); earr[0][1] = ctx.mkTrue(); earr[1][0] = ctx.mkFalse(); earr[1][1] = ctx.mkFalse(); for (Expr[] ea : earr) for (Expr e : ea) { try { Expr ns = ctx.mkNot((BoolExpr) e); @SuppressWarnings("unused") BoolExpr ens = (BoolExpr) ns; } catch (ClassCastException ex) { throw new TestFailedException(); } } } // / Shows how to read an SMT1 file. void smt1FileTest(String filename) { System.out.print("SMT File test "); { HashMap cfg = new HashMap(); Context ctx = new Context(cfg); ctx.parseSMTLIBFile(filename, null, null, null, null); BoolExpr a = ctx.mkAnd(ctx.getSMTLIBFormulas()); System.out.println("read formula: " + a); } } // / Shows how to read an SMT2 file. void smt2FileTest(String filename) { Date before = new Date(); System.out.println("SMT2 File test "); System.gc(); { HashMap cfg = new HashMap(); cfg.put("model", "true"); Context ctx = new Context(cfg); Expr a = ctx.parseSMTLIB2File(filename, null, null, null, null); long t_diff = ((new Date()).getTime() - before.getTime()) / 1000; System.out.println("SMT2 file read time: " + t_diff + " sec"); // Iterate over the formula. LinkedList q = new LinkedList(); q.add(a); int cnt = 0; while (q.size() > 0) { AST cur = (AST) q.removeFirst(); cnt++; if (cur.getClass() == Expr.class) if (!(cur.isVar())) for (Expr c : ((Expr) cur).getArgs()) q.add(c); } System.out.println(cnt + " ASTs"); } long t_diff = ((new Date()).getTime() - before.getTime()) / 1000; System.out.println("SMT2 file test took " + t_diff + " sec"); } // / Shows how to use Solver(logic) // / @param ctx void logicExample(Context ctx) throws TestFailedException { System.out.println("LogicTest"); Log.append("LogicTest"); com.microsoft.z3.Global.ToggleWarningMessages(true); BitVecSort bvs = ctx.mkBitVecSort(32); Expr x = ctx.mkConst("x", bvs); Expr y = ctx.mkConst("y", bvs); BoolExpr eq = ctx.mkEq(x, y); // Use a solver for QF_BV Solver s = ctx.mkSolver("QF_BV"); s.add(eq); Status res = s.check(); System.out.println("solver result: " + res); // Or perhaps a tactic for QF_BV Goal g = ctx.mkGoal(true, false, false); g.add(eq); Tactic t = ctx.mkTactic("qfbv"); ApplyResult ar = t.apply(g); System.out.println("tactic result: " + ar); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedSat()) throw new TestFailedException(); } // / Demonstrates how to use the ParOr tactic. void parOrExample(Context ctx) throws TestFailedException { System.out.println("ParOrExample"); Log.append("ParOrExample"); BitVecSort bvs = ctx.mkBitVecSort(32); Expr x = ctx.mkConst("x", bvs); Expr y = ctx.mkConst("y", bvs); BoolExpr q = ctx.mkEq(x, y); Goal g = ctx.mkGoal(true, false, false); g.add(q); Tactic t1 = ctx.mkTactic("qfbv"); Tactic t2 = ctx.mkTactic("qfbv"); Tactic p = ctx.parOr(t1, t2); ApplyResult ar = p.apply(g); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedSat()) throw new TestFailedException(); } void bigIntCheck(Context ctx, RatNum r) { System.out.println("Num: " + r.getBigIntNumerator()); System.out.println("Den: " + r.getBigIntDenominator()); } // / Find a model for x xor y. public void findModelExample1(Context ctx) throws TestFailedException { System.out.println("FindModelExample1"); Log.append("FindModelExample1"); BoolExpr x = ctx.mkBoolConst("x"); BoolExpr y = ctx.mkBoolConst("y"); BoolExpr x_xor_y = ctx.mkXor(x, y); Model model = check(ctx, x_xor_y, Status.SATISFIABLE); System.out.println("x = " + model.evaluate(x, false) + ", y = " + model.evaluate(y, false)); } // / Find a model for x < y + 1, x > 2. // / Then, assert not(x = y), and find another model. public void findModelExample2(Context ctx) throws TestFailedException { System.out.println("FindModelExample2"); Log.append("FindModelExample2"); IntExpr x = ctx.mkIntConst("x"); IntExpr y = ctx.mkIntConst("y"); IntExpr one = ctx.mkInt(1); IntExpr two = ctx.mkInt(2); ArithExpr y_plus_one = ctx.mkAdd(y, one); BoolExpr c1 = ctx.mkLt(x, y_plus_one); BoolExpr c2 = ctx.mkGt(x, two); BoolExpr q = ctx.mkAnd(c1, c2); System.out.println("model for: x < y + 1, x > 2"); Model model = check(ctx, q, Status.SATISFIABLE); System.out.println("x = " + model.evaluate(x, false) + ", y =" + model.evaluate(y, false)); /* assert not(x = y) */ BoolExpr x_eq_y = ctx.mkEq(x, y); BoolExpr c3 = ctx.mkNot(x_eq_y); q = ctx.mkAnd(q, c3); System.out.println("model for: x < y + 1, x > 2, not(x = y)"); model = check(ctx, q, Status.SATISFIABLE); System.out.println("x = " + model.evaluate(x, false) + ", y = " + model.evaluate(y, false)); } // / Prove x = y implies g(x) = g(y), and // / disprove x = y implies g(g(x)) = g(y). // / This function demonstrates how to create uninterpreted // / types and functions. public void proveExample1(Context ctx) throws TestFailedException { System.out.println("ProveExample1"); Log.append("ProveExample1"); /* create uninterpreted type. */ Sort U = ctx.mkUninterpretedSort(ctx.mkSymbol("U")); /* declare function g */ FuncDecl g = ctx.mkFuncDecl("g", U, U); /* create x and y */ Expr x = ctx.mkConst("x", U); Expr y = ctx.mkConst("y", U); /* create g(x), g(y) */ Expr gx = g.apply(x); Expr gy = g.apply(y); /* assert x = y */ BoolExpr eq = ctx.mkEq(x, y); /* prove g(x) = g(y) */ BoolExpr f = ctx.mkEq(gx, gy); System.out.println("prove: x = y implies g(x) = g(y)"); prove(ctx, ctx.mkImplies(eq, f), false); /* create g(g(x)) */ Expr ggx = g.apply(gx); /* disprove g(g(x)) = g(y) */ f = ctx.mkEq(ggx, gy); System.out.println("disprove: x = y implies g(g(x)) = g(y)"); disprove(ctx, ctx.mkImplies(eq, f), false); /* Print the model using the custom model printer */ Model m = check(ctx, ctx.mkNot(f), Status.SATISFIABLE); System.out.println(m); } // / Prove not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0 // . // / Then, show that z < -1 is not implied. // / This example demonstrates how to combine uninterpreted // functions // / and arithmetic. public void proveExample2(Context ctx) throws TestFailedException { System.out.println("ProveExample2"); Log.append("ProveExample2"); /* declare function g */ Sort I = ctx.getIntSort(); FuncDecl g = ctx.mkFuncDecl("g", I, I); /* create x, y, and z */ IntExpr x = ctx.mkIntConst("x"); IntExpr y = ctx.mkIntConst("y"); IntExpr z = ctx.mkIntConst("z"); /* create gx, gy, gz */ Expr gx = ctx.mkApp(g, x); Expr gy = ctx.mkApp(g, y); Expr gz = ctx.mkApp(g, z); /* create zero */ IntExpr zero = ctx.mkInt(0); /* assert not(g(g(x) - g(y)) = g(z)) */ ArithExpr gx_gy = ctx.mkSub((IntExpr) gx, (IntExpr) gy); Expr ggx_gy = ctx.mkApp(g, gx_gy); BoolExpr eq = ctx.mkEq(ggx_gy, gz); BoolExpr c1 = ctx.mkNot(eq); /* assert x + z <= y */ ArithExpr x_plus_z = ctx.mkAdd(x, z); BoolExpr c2 = ctx.mkLe(x_plus_z, y); /* assert y <= x */ BoolExpr c3 = ctx.mkLe(y, x); /* prove z < 0 */ BoolExpr f = ctx.mkLt(z, zero); System.out .println("prove: not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0"); prove(ctx, f, false, c1, c2, c3); /* disprove z < -1 */ IntExpr minus_one = ctx.mkInt(-1); f = ctx.mkLt(z, minus_one); System.out .println("disprove: not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < -1"); disprove(ctx, f, false, c1, c2, c3); } // / Show how push & pop can be used to create "backtracking" points. // / This example also demonstrates how big numbers can be // / created in ctx. public void pushPopExample1(Context ctx) throws TestFailedException { System.out.println("PushPopExample1"); Log.append("PushPopExample1"); /* create a big number */ IntSort int_type = ctx.getIntSort(); IntExpr big_number = ctx .mkInt("1000000000000000000000000000000000000000000000000000000"); /* create number 3 */ IntExpr three = (IntExpr) ctx.mkNumeral("3", int_type); /* create x */ IntExpr x = ctx.mkIntConst("x"); Solver solver = ctx.mkSolver(); /* assert x >= "big number" */ BoolExpr c1 = ctx.mkGe(x, big_number); System.out.println("assert: x >= 'big number'"); solver.add(c1); /* create a backtracking point */ System.out.println("push"); solver.push(); /* assert x <= 3 */ BoolExpr c2 = ctx.mkLe(x, three); System.out.println("assert: x <= 3"); solver.add(c2); /* context is inconsistent at this point */ if (solver.check() != Status.UNSATISFIABLE) throw new TestFailedException(); /* * backtrack: the constraint x <= 3 will be removed, since it was * asserted after the last ctx.Push. */ System.out.println("pop"); solver.pop(1); /* the context is consistent again. */ if (solver.check() != Status.SATISFIABLE) throw new TestFailedException(); /* new constraints can be asserted... */ /* create y */ IntExpr y = ctx.mkIntConst("y"); /* assert y > x */ BoolExpr c3 = ctx.mkGt(y, x); System.out.println("assert: y > x"); solver.add(c3); /* the context is still consistent. */ if (solver.check() != Status.SATISFIABLE) throw new TestFailedException(); } // / Tuples. // / Check that the projection of a tuple // / returns the corresponding element. public void tupleExample(Context ctx) throws TestFailedException { System.out.println("TupleExample"); Log.append("TupleExample"); Sort int_type = ctx.getIntSort(); TupleSort tuple = ctx.mkTupleSort(ctx.mkSymbol("mk_tuple"), // name of // tuple // constructor new Symbol[] { ctx.mkSymbol("first"), ctx.mkSymbol("second") }, // names // of // projection // operators new Sort[] { int_type, int_type } // types of projection // operators ); FuncDecl first = tuple.getFieldDecls()[0]; // declarations are for // projections @SuppressWarnings("unused") FuncDecl second = tuple.getFieldDecls()[1]; Expr x = ctx.mkConst("x", int_type); Expr y = ctx.mkConst("y", int_type); Expr n1 = tuple.mkDecl().apply(x, y); Expr n2 = first.apply(n1); BoolExpr n3 = ctx.mkEq(x, n2); System.out.println("Tuple example: " + n3); prove(ctx, n3, false); } // / Simple bit-vector example. // / // / This example disproves that x - 10 <= 0 IFF x <= 10 for (32-bit) // machine integers // / public void bitvectorExample1(Context ctx) throws TestFailedException { System.out.println("BitvectorExample1"); Log.append("BitvectorExample1"); Sort bv_type = ctx.mkBitVecSort(32); BitVecExpr x = (BitVecExpr) ctx.mkConst("x", bv_type); BitVecNum zero = (BitVecNum) ctx.mkNumeral("0", bv_type); BitVecNum ten = ctx.mkBV(10, 32); BitVecExpr x_minus_ten = ctx.mkBVSub(x, ten); /* bvsle is signed less than or equal to */ BoolExpr c1 = ctx.mkBVSLE(x, ten); BoolExpr c2 = ctx.mkBVSLE(x_minus_ten, zero); BoolExpr thm = ctx.mkIff(c1, c2); System.out .println("disprove: x - 10 <= 0 IFF x <= 10 for (32-bit) machine integers"); disprove(ctx, thm, false); } // / Find x and y such that: x ^ y - 103 == x * y public void bitvectorExample2(Context ctx) throws TestFailedException { System.out.println("BitvectorExample2"); Log.append("BitvectorExample2"); /* construct x ^ y - 103 == x * y */ Sort bv_type = ctx.mkBitVecSort(32); BitVecExpr x = ctx.mkBVConst("x", 32); BitVecExpr y = ctx.mkBVConst("y", 32); BitVecExpr x_xor_y = ctx.mkBVXOR(x, y); BitVecExpr c103 = (BitVecNum) ctx.mkNumeral("103", bv_type); BitVecExpr lhs = ctx.mkBVSub(x_xor_y, c103); BitVecExpr rhs = ctx.mkBVMul(x, y); BoolExpr ctr = ctx.mkEq(lhs, rhs); System.out .println("find values of x and y, such that x ^ y - 103 == x * y"); /* find a model (i.e., values for x an y that satisfy the constraint */ Model m = check(ctx, ctr, Status.SATISFIABLE); System.out.println(m); } // / Demonstrates how to use the SMTLIB parser. public void parserExample1(Context ctx) throws TestFailedException { System.out.println("ParserExample1"); Log.append("ParserExample1"); ctx.parseSMTLIBString( "(benchmark tst :extrafuns ((x Int) (y Int)) :formula (> x y) :formula (> x 0))", null, null, null, null); for (BoolExpr f : ctx.getSMTLIBFormulas()) System.out.println("formula " + f); @SuppressWarnings("unused") Model m = check(ctx, ctx.mkAnd(ctx.getSMTLIBFormulas()), Status.SATISFIABLE); } // / Demonstrates how to initialize the parser symbol table. public void parserExample2(Context ctx) throws TestFailedException { System.out.println("ParserExample2"); Log.append("ParserExample2"); Symbol[] declNames = { ctx.mkSymbol("a"), ctx.mkSymbol("b") }; FuncDecl a = ctx.mkConstDecl(declNames[0], ctx.mkIntSort()); FuncDecl b = ctx.mkConstDecl(declNames[1], ctx.mkIntSort()); FuncDecl[] decls = new FuncDecl[] { a, b }; ctx.parseSMTLIBString("(benchmark tst :formula (> a b))", null, null, declNames, decls); BoolExpr f = ctx.getSMTLIBFormulas()[0]; System.out.println("formula: " + f); check(ctx, f, Status.SATISFIABLE); } // / Demonstrates how to initialize the parser symbol table. public void parserExample3(Context ctx) throws Exception { System.out.println("ParserExample3"); Log.append("ParserExample3"); /* declare function g */ Sort I = ctx.mkIntSort(); FuncDecl g = ctx.mkFuncDecl("g", new Sort[] { I, I }, I); BoolExpr ca = commAxiom(ctx, g); ctx.parseSMTLIBString( "(benchmark tst :formula (forall (x Int) (y Int) (implies (= x y) (= (gg x 0) (gg 0 y)))))", null, null, new Symbol[] { ctx.mkSymbol("gg") }, new FuncDecl[] { g }); BoolExpr thm = ctx.getSMTLIBFormulas()[0]; System.out.println("formula: " + thm); prove(ctx, thm, false, ca); } // / Display the declarations, assumptions and formulas in a SMT-LIB string. public void parserExample4(Context ctx) { System.out.println("ParserExample4"); Log.append("ParserExample4"); ctx.parseSMTLIBString( "(benchmark tst :extrafuns ((x Int) (y Int)) :assumption (= x 20) :formula (> x y) :formula (> x 0))", null, null, null, null); for (FuncDecl decl : ctx.getSMTLIBDecls()) { System.out.println("Declaration: " + decl); } for (BoolExpr f : ctx.getSMTLIBAssumptions()) { System.out.println("Assumption: " + f); } for (BoolExpr f : ctx.getSMTLIBFormulas()) { System.out.println("Formula: " + f); } } // / Demonstrates how to handle parser errors using Z3 error handling // support. // / public void parserExample5(Context ctx) { System.out.println("ParserExample5"); try { ctx.parseSMTLIBString( /* * the following string has a parsing error: missing * parenthesis */ "(benchmark tst :extrafuns ((x Int (y Int)) :formula (> x y) :formula (> x 0))", null, null, null, null); } catch (Z3Exception e) { System.out.println("Z3 error: " + e); } } // / Create an ite-Expr (if-then-else Exprs). public void iteExample(Context ctx) { System.out.println("ITEExample"); Log.append("ITEExample"); BoolExpr f = ctx.mkFalse(); Expr one = ctx.mkInt(1); Expr zero = ctx.mkInt(0); Expr ite = ctx.mkITE(f, one, zero); System.out.println("Expr: " + ite); } // / Create an enumeration data type. public void enumExample(Context ctx) throws TestFailedException { System.out.println("EnumExample"); Log.append("EnumExample"); Symbol name = ctx.mkSymbol("fruit"); EnumSort fruit = ctx.mkEnumSort(name, ctx.mkSymbol("apple"), ctx.mkSymbol("banana"), ctx.mkSymbol("orange")); System.out.println((fruit.getConsts()[0])); System.out.println((fruit.getConsts()[1])); System.out.println((fruit.getConsts()[2])); System.out.println((fruit.getTesterDecls()[0])); System.out.println((fruit.getTesterDecls()[1])); System.out.println((fruit.getTesterDecls()[2])); Expr apple = fruit.getConsts()[0]; Expr banana = fruit.getConsts()[1]; Expr orange = fruit.getConsts()[2]; /* Apples are different from oranges */ prove(ctx, ctx.mkNot(ctx.mkEq(apple, orange)), false); /* Apples pass the apple test */ prove(ctx, (BoolExpr) ctx.mkApp(fruit.getTesterDecls()[0], apple), false); /* Oranges fail the apple test */ disprove(ctx, (BoolExpr) ctx.mkApp(fruit.getTesterDecls()[0], orange), false); prove(ctx, (BoolExpr) ctx.mkNot((BoolExpr) ctx.mkApp( fruit.getTesterDecls()[0], orange)), false); Expr fruity = ctx.mkConst("fruity", fruit); /* If something is fruity, then it is an apple, banana, or orange */ prove(ctx, ctx.mkOr(ctx.mkEq(fruity, apple), ctx.mkEq(fruity, banana), ctx.mkEq(fruity, orange)), false); } // / Create a list datatype. public void listExample(Context ctx) throws TestFailedException { System.out.println("ListExample"); Log.append("ListExample"); Sort int_ty; ListSort int_list; Expr nil, l1, l2, x, y, u, v; BoolExpr fml, fml1; int_ty = ctx.mkIntSort(); int_list = ctx.mkListSort(ctx.mkSymbol("int_list"), int_ty); nil = ctx.mkConst(int_list.getNilDecl()); l1 = ctx.mkApp(int_list.getConsDecl(), ctx.mkInt(1), nil); l2 = ctx.mkApp(int_list.getConsDecl(), ctx.mkInt(2), nil); /* nil != cons(1, nil) */ prove(ctx, ctx.mkNot(ctx.mkEq(nil, l1)), false); /* cons(2,nil) != cons(1, nil) */ prove(ctx, ctx.mkNot(ctx.mkEq(l1, l2)), false); /* cons(x,nil) = cons(y, nil) => x = y */ x = ctx.mkConst("x", int_ty); y = ctx.mkConst("y", int_ty); l1 = ctx.mkApp(int_list.getConsDecl(), x, nil); l2 = ctx.mkApp(int_list.getConsDecl(), y, nil); prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(x, y)), false); /* cons(x,u) = cons(x, v) => u = v */ u = ctx.mkConst("u", int_list); v = ctx.mkConst("v", int_list); l1 = ctx.mkApp(int_list.getConsDecl(), x, u); l2 = ctx.mkApp(int_list.getConsDecl(), y, v); prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(u, v)), false); prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(x, y)), false); /* is_nil(u) or is_cons(u) */ prove(ctx, ctx.mkOr((BoolExpr) ctx.mkApp(int_list.getIsNilDecl(), u), (BoolExpr) ctx.mkApp(int_list.getIsConsDecl(), u)), false); /* occurs check u != cons(x,u) */ prove(ctx, ctx.mkNot(ctx.mkEq(u, l1)), false); /* destructors: is_cons(u) => u = cons(head(u),tail(u)) */ fml1 = ctx.mkEq( u, ctx.mkApp(int_list.getConsDecl(), ctx.mkApp(int_list.getHeadDecl(), u), ctx.mkApp(int_list.getTailDecl(), u))); fml = ctx.mkImplies((BoolExpr) ctx.mkApp(int_list.getIsConsDecl(), u), fml1); System.out.println("Formula " + fml); prove(ctx, fml, false); disprove(ctx, fml1, false); } // / Create a binary tree datatype. public void treeExample(Context ctx) throws TestFailedException { System.out.println("TreeExample"); Log.append("TreeExample"); Sort cell; FuncDecl nil_decl, is_nil_decl, cons_decl, is_cons_decl, car_decl, cdr_decl; Expr nil, l1, l2, x, y, u, v; BoolExpr fml, fml1; String[] head_tail = new String[] { "car", "cdr" }; Sort[] sorts = new Sort[] { null, null }; int[] sort_refs = new int[] { 0, 0 }; Constructor nil_con, cons_con; nil_con = ctx.mkConstructor("nil", "is_nil", null, null, null); cons_con = ctx.mkConstructor("cons", "is_cons", head_tail, sorts, sort_refs); Constructor[] constructors = new Constructor[] { nil_con, cons_con }; cell = ctx.mkDatatypeSort("cell", constructors); nil_decl = nil_con.ConstructorDecl(); is_nil_decl = nil_con.getTesterDecl(); cons_decl = cons_con.ConstructorDecl(); is_cons_decl = cons_con.getTesterDecl(); FuncDecl[] cons_accessors = cons_con.getAccessorDecls(); car_decl = cons_accessors[0]; cdr_decl = cons_accessors[1]; nil = ctx.mkConst(nil_decl); l1 = ctx.mkApp(cons_decl, nil, nil); l2 = ctx.mkApp(cons_decl, l1, nil); /* nil != cons(nil, nil) */ prove(ctx, ctx.mkNot(ctx.mkEq(nil, l1)), false); /* cons(x,u) = cons(x, v) => u = v */ u = ctx.mkConst("u", cell); v = ctx.mkConst("v", cell); x = ctx.mkConst("x", cell); y = ctx.mkConst("y", cell); l1 = ctx.mkApp(cons_decl, x, u); l2 = ctx.mkApp(cons_decl, y, v); prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(u, v)), false); prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(x, y)), false); /* is_nil(u) or is_cons(u) */ prove(ctx, ctx.mkOr((BoolExpr) ctx.mkApp(is_nil_decl, u), (BoolExpr) ctx.mkApp(is_cons_decl, u)), false); /* occurs check u != cons(x,u) */ prove(ctx, ctx.mkNot(ctx.mkEq(u, l1)), false); /* destructors: is_cons(u) => u = cons(car(u),cdr(u)) */ fml1 = ctx.mkEq( u, ctx.mkApp(cons_decl, ctx.mkApp(car_decl, u), ctx.mkApp(cdr_decl, u))); fml = ctx.mkImplies((BoolExpr) ctx.mkApp(is_cons_decl, u), fml1); System.out.println("Formula " + fml); prove(ctx, fml, false); disprove(ctx, fml1, false); } // / Create a forest of trees. // / // / forest ::= nil | cons(tree, forest) // / tree ::= nil | cons(forest, forest) // / public void forestExample(Context ctx) throws TestFailedException { System.out.println("ForestExample"); Log.append("ForestExample"); Sort tree, forest; @SuppressWarnings("unused") FuncDecl nil1_decl, is_nil1_decl, cons1_decl, is_cons1_decl, car1_decl, cdr1_decl; @SuppressWarnings("unused") FuncDecl nil2_decl, is_nil2_decl, cons2_decl, is_cons2_decl, car2_decl, cdr2_decl; @SuppressWarnings("unused") Expr nil1, nil2, t1, t2, t3, t4, f1, f2, f3, l1, l2, x, y, u, v; // // Declare the names of the accessors for cons. // Then declare the sorts of the accessors. // For this example, all sorts refer to the new types 'forest' and // 'tree' // being declared, so we pass in null for both sorts1 and sorts2. // On the other hand, the sort_refs arrays contain the indices of the // two new sorts being declared. The first element in sort1_refs // points to 'tree', which has index 1, the second element in sort1_refs // array // points to 'forest', which has index 0. // Symbol[] head_tail1 = new Symbol[] { ctx.mkSymbol("head"), ctx.mkSymbol("tail") }; Sort[] sorts1 = new Sort[] { null, null }; int[] sort1_refs = new int[] { 1, 0 }; // the first item points to a // tree, the second to a forest Symbol[] head_tail2 = new Symbol[] { ctx.mkSymbol("car"), ctx.mkSymbol("cdr") }; Sort[] sorts2 = new Sort[] { null, null }; int[] sort2_refs = new int[] { 0, 0 }; // both items point to the forest // datatype. Constructor nil1_con, cons1_con, nil2_con, cons2_con; Constructor[] constructors1 = new Constructor[2], constructors2 = new Constructor[2]; Symbol[] sort_names = { ctx.mkSymbol("forest"), ctx.mkSymbol("tree") }; /* build a forest */ nil1_con = ctx.mkConstructor(ctx.mkSymbol("nil"), ctx.mkSymbol("is_nil"), null, null, null); cons1_con = ctx.mkConstructor(ctx.mkSymbol("cons1"), ctx.mkSymbol("is_cons1"), head_tail1, sorts1, sort1_refs); constructors1[0] = nil1_con; constructors1[1] = cons1_con; /* build a tree */ nil2_con = ctx.mkConstructor(ctx.mkSymbol("nil2"), ctx.mkSymbol("is_nil2"), null, null, null); cons2_con = ctx.mkConstructor(ctx.mkSymbol("cons2"), ctx.mkSymbol("is_cons2"), head_tail2, sorts2, sort2_refs); constructors2[0] = nil2_con; constructors2[1] = cons2_con; Constructor[][] clists = new Constructor[][] { constructors1, constructors2 }; Sort[] sorts = ctx.mkDatatypeSorts(sort_names, clists); forest = sorts[0]; tree = sorts[1]; // // Now that the datatype has been created. // Query the constructors for the constructor // functions, testers, and field accessors. // nil1_decl = nil1_con.ConstructorDecl(); is_nil1_decl = nil1_con.getTesterDecl(); cons1_decl = cons1_con.ConstructorDecl(); is_cons1_decl = cons1_con.getTesterDecl(); FuncDecl[] cons1_accessors = cons1_con.getAccessorDecls(); car1_decl = cons1_accessors[0]; cdr1_decl = cons1_accessors[1]; nil2_decl = nil2_con.ConstructorDecl(); is_nil2_decl = nil2_con.getTesterDecl(); cons2_decl = cons2_con.ConstructorDecl(); is_cons2_decl = cons2_con.getTesterDecl(); FuncDecl[] cons2_accessors = cons2_con.getAccessorDecls(); car2_decl = cons2_accessors[0]; cdr2_decl = cons2_accessors[1]; nil1 = ctx.mkConst(nil1_decl); nil2 = ctx.mkConst(nil2_decl); f1 = ctx.mkApp(cons1_decl, nil2, nil1); t1 = ctx.mkApp(cons2_decl, nil1, nil1); t2 = ctx.mkApp(cons2_decl, f1, nil1); t3 = ctx.mkApp(cons2_decl, f1, f1); t4 = ctx.mkApp(cons2_decl, nil1, f1); f2 = ctx.mkApp(cons1_decl, t1, nil1); f3 = ctx.mkApp(cons1_decl, t1, f1); /* nil != cons(nil,nil) */ prove(ctx, ctx.mkNot(ctx.mkEq(nil1, f1)), false); prove(ctx, ctx.mkNot(ctx.mkEq(nil2, t1)), false); /* cons(x,u) = cons(x, v) => u = v */ u = ctx.mkConst("u", forest); v = ctx.mkConst("v", forest); x = ctx.mkConst("x", tree); y = ctx.mkConst("y", tree); l1 = ctx.mkApp(cons1_decl, x, u); l2 = ctx.mkApp(cons1_decl, y, v); prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(u, v)), false); prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(x, y)), false); /* is_nil(u) or is_cons(u) */ prove(ctx, ctx.mkOr((BoolExpr) ctx.mkApp(is_nil1_decl, u), (BoolExpr) ctx.mkApp(is_cons1_decl, u)), false); /* occurs check u != cons(x,u) */ prove(ctx, ctx.mkNot(ctx.mkEq(u, l1)), false); } // / Demonstrate how to use #Eval. public void evalExample1(Context ctx) { System.out.println("EvalExample1"); Log.append("EvalExample1"); IntExpr x = ctx.mkIntConst("x"); IntExpr y = ctx.mkIntConst("y"); IntExpr two = ctx.mkInt(2); Solver solver = ctx.mkSolver(); /* assert x < y */ solver.add(ctx.mkLt(x, y)); /* assert x > 2 */ solver.add(ctx.mkGt(x, two)); /* find model for the constraints above */ Model model = null; if (Status.SATISFIABLE == solver.check()) { model = solver.getModel(); System.out.println(model); System.out.println("\nevaluating x+y"); Expr v = model.evaluate(ctx.mkAdd(x, y), false); if (v != null) { System.out.println("result = " + (v)); } else { System.out.println("Failed to evaluate: x+y"); } } else { System.out.println("BUG, the constraints are satisfiable."); } } // / Demonstrate how to use #Eval on tuples. public void evalExample2(Context ctx) { System.out.println("EvalExample2"); Log.append("EvalExample2"); Sort int_type = ctx.getIntSort(); TupleSort tuple = ctx.mkTupleSort(ctx.mkSymbol("mk_tuple"), // name of // tuple // constructor new Symbol[] { ctx.mkSymbol("first"), ctx.mkSymbol("second") }, // names // of // projection // operators new Sort[] { int_type, int_type } // types of projection // operators ); FuncDecl first = tuple.getFieldDecls()[0]; // declarations are for // projections FuncDecl second = tuple.getFieldDecls()[1]; Expr tup1 = ctx.mkConst("t1", tuple); Expr tup2 = ctx.mkConst("t2", tuple); Solver solver = ctx.mkSolver(); /* assert tup1 != tup2 */ solver.add(ctx.mkNot(ctx.mkEq(tup1, tup2))); /* assert first tup1 = first tup2 */ solver.add(ctx.mkEq(ctx.mkApp(first, tup1), ctx.mkApp(first, tup2))); /* find model for the constraints above */ Model model = null; if (Status.SATISFIABLE == solver.check()) { model = solver.getModel(); System.out.println(model); System.out.println("evaluating tup1 " + (model.evaluate(tup1, false))); System.out.println("evaluating tup2 " + (model.evaluate(tup2, false))); System.out.println("evaluating second(tup2) " + (model.evaluate(ctx.mkApp(second, tup2), false))); } else { System.out.println("BUG, the constraints are satisfiable."); } } // / Demonstrate how to use Pushand Popto // / control the size of models. // / Note: this test is specialized to 32-bit bitvectors. public void checkSmall(Context ctx, Solver solver, BitVecExpr... to_minimize) { int num_Exprs = to_minimize.length; int[] upper = new int[num_Exprs]; int[] lower = new int[num_Exprs]; for (int i = 0; i < upper.length; ++i) { upper[i] = Integer.MAX_VALUE; lower[i] = 0; } boolean some_work = true; int last_index = -1; int last_upper = 0; while (some_work) { solver.push(); boolean check_is_sat = true; while (check_is_sat && some_work) { // Assert all feasible bounds. for (int i = 0; i < num_Exprs; ++i) { solver.add(ctx.mkBVULE(to_minimize[i], ctx.mkBV(upper[i], 32))); } check_is_sat = Status.SATISFIABLE == solver.check(); if (!check_is_sat) { if (last_index != -1) { lower[last_index] = last_upper + 1; } break; } System.out.println(solver.getModel()); // narrow the bounds based on the current model. for (int i = 0; i < num_Exprs; ++i) { Expr v = solver.getModel().evaluate(to_minimize[i], false); int ui = ((BitVecNum) v).getInt(); if (ui < upper[i]) { upper[i] = (int) ui; } System.out.println(i + " " + lower[i] + " " + upper[i]); } // find a new bound to add some_work = false; last_index = 0; for (int i = 0; i < num_Exprs; ++i) { if (lower[i] < upper[i]) { last_upper = (upper[i] + lower[i]) / 2; last_index = i; solver.add(ctx.mkBVULE(to_minimize[i], ctx.mkBV(last_upper, 32))); some_work = true; break; } } } solver.pop(); } } // / Reduced-size model generation example. public void findSmallModelExample(Context ctx) { System.out.println("FindSmallModelExample"); Log.append("FindSmallModelExample"); BitVecExpr x = ctx.mkBVConst("x", 32); BitVecExpr y = ctx.mkBVConst("y", 32); BitVecExpr z = ctx.mkBVConst("z", 32); Solver solver = ctx.mkSolver(); solver.add(ctx.mkBVULE(x, ctx.mkBVAdd(y, z))); checkSmall(ctx, solver, x, y, z); } // / Simplifier example. public void simplifierExample(Context ctx) { System.out.println("SimplifierExample"); Log.append("SimplifierExample"); IntExpr x = ctx.mkIntConst("x"); IntExpr y = ctx.mkIntConst("y"); IntExpr z = ctx.mkIntConst("z"); @SuppressWarnings("unused") IntExpr u = ctx.mkIntConst("u"); Expr t1 = ctx.mkAdd(x, ctx.mkSub(y, ctx.mkAdd(x, z))); Expr t2 = t1.simplify(); System.out.println((t1) + " -> " + (t2)); } // / Extract unsatisfiable core example public void unsatCoreAndProofExample(Context ctx) { System.out.println("UnsatCoreAndProofExample"); Log.append("UnsatCoreAndProofExample"); Solver solver = ctx.mkSolver(); BoolExpr pa = ctx.mkBoolConst("PredA"); BoolExpr pb = ctx.mkBoolConst("PredB"); BoolExpr pc = ctx.mkBoolConst("PredC"); BoolExpr pd = ctx.mkBoolConst("PredD"); BoolExpr p1 = ctx.mkBoolConst("P1"); BoolExpr p2 = ctx.mkBoolConst("P2"); BoolExpr p3 = ctx.mkBoolConst("P3"); BoolExpr p4 = ctx.mkBoolConst("P4"); BoolExpr[] assumptions = new BoolExpr[] { ctx.mkNot(p1), ctx.mkNot(p2), ctx.mkNot(p3), ctx.mkNot(p4) }; BoolExpr f1 = ctx.mkAnd(pa, pb, pc); BoolExpr f2 = ctx.mkAnd(pa, ctx.mkNot(pb), pc); BoolExpr f3 = ctx.mkOr(ctx.mkNot(pa), ctx.mkNot(pc)); BoolExpr f4 = pd; solver.add(ctx.mkOr(f1, p1)); solver.add(ctx.mkOr(f2, p2)); solver.add(ctx.mkOr(f3, p3)); solver.add(ctx.mkOr(f4, p4)); Status result = solver.check(assumptions); if (result == Status.UNSATISFIABLE) { System.out.println("unsat"); System.out.println("proof: " + solver.getProof()); System.out.println("core: "); for (Expr c : solver.getUnsatCore()) { System.out.println(c); } } } /// Extract unsatisfiable core example with AssertAndTrack public void unsatCoreAndProofExample2(Context ctx) { System.out.println("UnsatCoreAndProofExample2"); Log.append("UnsatCoreAndProofExample2"); Solver solver = ctx.mkSolver(); BoolExpr pa = ctx.mkBoolConst("PredA"); BoolExpr pb = ctx.mkBoolConst("PredB"); BoolExpr pc = ctx.mkBoolConst("PredC"); BoolExpr pd = ctx.mkBoolConst("PredD"); BoolExpr f1 = ctx.mkAnd(new BoolExpr[] { pa, pb, pc }); BoolExpr f2 = ctx.mkAnd(new BoolExpr[] { pa, ctx.mkNot(pb), pc }); BoolExpr f3 = ctx.mkOr(ctx.mkNot(pa), ctx.mkNot(pc)); BoolExpr f4 = pd; BoolExpr p1 = ctx.mkBoolConst("P1"); BoolExpr p2 = ctx.mkBoolConst("P2"); BoolExpr p3 = ctx.mkBoolConst("P3"); BoolExpr p4 = ctx.mkBoolConst("P4"); solver.assertAndTrack(f1, p1); solver.assertAndTrack(f2, p2); solver.assertAndTrack(f3, p3); solver.assertAndTrack(f4, p4); Status result = solver.check(); if (result == Status.UNSATISFIABLE) { System.out.println("unsat"); System.out.println("core: "); for (Expr c : solver.getUnsatCore()) { System.out.println(c); } } } public void finiteDomainExample(Context ctx) { System.out.println("FiniteDomainExample"); Log.append("FiniteDomainExample"); FiniteDomainSort s = ctx.mkFiniteDomainSort("S", 10); FiniteDomainSort t = ctx.mkFiniteDomainSort("T", 10); Expr s1 = ctx.mkNumeral(1, s); Expr t1 = ctx.mkNumeral(1, t); System.out.println(s); System.out.println(t); System.out.println(s1); System.out.println(ctx.mkNumeral(2, s)); System.out.println(t1); // But you cannot mix numerals of different sorts // even if the size of their domains are the same: // System.out.println(ctx.mkEq(s1, t1)); } public void floatingPointExample1(Context ctx) throws TestFailedException { System.out.println("FloatingPointExample1"); Log.append("FloatingPointExample1"); FPSort s = ctx.mkFPSort(11, 53); System.out.println("Sort: " + s); FPNum x = (FPNum)ctx.mkNumeral("-1e1", s); /* -1 * 10^1 = -10 */ FPNum y = (FPNum)ctx.mkNumeral("-10", s); /* -10 */ FPNum z = (FPNum)ctx.mkNumeral("-1.25p3", s); /* -1.25 * 2^3 = -1.25 * 8 = -10 */ System.out.println("x=" + x.toString() + "; y=" + y.toString() + "; z=" + z.toString()); BoolExpr a = ctx.mkAnd(ctx.mkFPEq(x, y), ctx.mkFPEq(y, z)); check(ctx, ctx.mkNot(a), Status.UNSATISFIABLE); /* nothing is equal to NaN according to floating-point * equality, so NaN == k should be unsatisfiable. */ FPExpr k = (FPExpr)ctx.mkConst("x", s); FPExpr nan = ctx.mkFPNaN(s); /* solver that runs the default tactic for QF_FP. */ Solver slvr = ctx.mkSolver("QF_FP"); slvr.add(ctx.mkFPEq(nan, k)); if (slvr.check() != Status.UNSATISFIABLE) throw new TestFailedException(); System.out.println("OK, unsat:" + System.getProperty("line.separator") + slvr); /* NaN is equal to NaN according to normal equality. */ slvr = ctx.mkSolver("QF_FP"); slvr.add(ctx.mkEq(nan, nan)); if (slvr.check() != Status.SATISFIABLE) throw new TestFailedException(); System.out.println("OK, sat:" + System.getProperty("line.separator") + slvr); /* Let's prove -1e1 * -1.25e3 == +100 */ x = (FPNum)ctx.mkNumeral("-1e1", s); y = (FPNum)ctx.mkNumeral("-1.25p3", s); FPExpr x_plus_y = (FPExpr)ctx.mkConst("x_plus_y", s); FPNum r = (FPNum)ctx.mkNumeral("100", s); slvr = ctx.mkSolver("QF_FP"); slvr.add(ctx.mkEq(x_plus_y, ctx.mkFPMul(ctx.mkFPRoundNearestTiesToAway(), x, y))); slvr.add(ctx.mkNot(ctx.mkFPEq(x_plus_y, r))); if (slvr.check() != Status.UNSATISFIABLE) throw new TestFailedException(); System.out.println("OK, unsat:" + System.getProperty("line.separator") + slvr); } public void floatingPointExample2(Context ctx) throws TestFailedException { System.out.println("FloatingPointExample2"); Log.append("FloatingPointExample2"); FPSort double_sort = ctx.mkFPSort(11, 53); FPRMSort rm_sort = ctx.mkFPRoundingModeSort(); FPRMExpr rm = (FPRMExpr)ctx.mkConst(ctx.mkSymbol("rm"), rm_sort); BitVecExpr x = (BitVecExpr)ctx.mkConst(ctx.mkSymbol("x"), ctx.mkBitVecSort(64)); FPExpr y = (FPExpr)ctx.mkConst(ctx.mkSymbol("y"), double_sort); FPExpr fp_val = ctx.mkFP(42, double_sort); BoolExpr c1 = ctx.mkEq(y, fp_val); BoolExpr c2 = ctx.mkEq(x, ctx.mkFPToBV(rm, y, 64, false)); BoolExpr c3 = ctx.mkEq(x, ctx.mkBV(42, 64)); BoolExpr c4 = ctx.mkEq(ctx.mkNumeral(42, ctx.getRealSort()), ctx.mkFPToReal(fp_val)); BoolExpr c5 = ctx.mkAnd(c1, c2, c3, c4); System.out.println("c5 = " + c5); /* Generic solver */ Solver s = ctx.mkSolver(); s.add(c5); if (s.check() != Status.SATISFIABLE) throw new TestFailedException(); System.out.println("OK, model: " + s.getModel().toString()); } public static void main(String[] args) { JavaExample p = new JavaExample(); try { com.microsoft.z3.Global.ToggleWarningMessages(true); Log.open("test.log"); System.out.print("Z3 Major Version: "); System.out.println(Version.getMajor()); System.out.print("Z3 Full Version: "); System.out.println(Version.getString()); p.simpleExample(); { // These examples need model generation turned on. HashMap cfg = new HashMap(); cfg.put("model", "true"); Context ctx = new Context(cfg); p.basicTests(ctx); p.castingTest(ctx); p.sudokuExample(ctx); p.quantifierExample1(ctx); p.quantifierExample2(ctx); p.logicExample(ctx); p.parOrExample(ctx); p.findModelExample1(ctx); p.findModelExample2(ctx); p.pushPopExample1(ctx); p.arrayExample1(ctx); p.arrayExample3(ctx); p.bitvectorExample1(ctx); p.bitvectorExample2(ctx); p.parserExample1(ctx); p.parserExample2(ctx); p.parserExample4(ctx); p.parserExample5(ctx); p.iteExample(ctx); p.evalExample1(ctx); p.evalExample2(ctx); p.findSmallModelExample(ctx); p.simplifierExample(ctx); p.finiteDomainExample(ctx); p.floatingPointExample1(ctx); p.floatingPointExample2(ctx); } { // These examples need proof generation turned on. HashMap cfg = new HashMap(); cfg.put("proof", "true"); Context ctx = new Context(cfg); p.proveExample1(ctx); p.proveExample2(ctx); p.arrayExample2(ctx); p.tupleExample(ctx); p.parserExample3(ctx); p.enumExample(ctx); p.listExample(ctx); p.treeExample(ctx); p.forestExample(ctx); p.unsatCoreAndProofExample(ctx); p.unsatCoreAndProofExample2(ctx); } { // These examples need proof generation turned on and auto-config // set to false. HashMap cfg = new HashMap(); cfg.put("proof", "true"); cfg.put("auto-config", "false"); Context ctx = new Context(cfg); p.quantifierExample3(ctx); p.quantifierExample4(ctx); } Log.close(); if (Log.isOpen()) System.out.println("Log is still open!"); } catch (Z3Exception ex) { System.out.println("Z3 Managed Exception: " + ex.getMessage()); System.out.println("Stack trace: "); ex.printStackTrace(System.out); } catch (TestFailedException ex) { System.out.println("TEST CASE FAILED: " + ex.getMessage()); System.out.println("Stack trace: "); ex.printStackTrace(System.out); } catch (Exception ex) { System.out.println("Unknown Exception: " + ex.getMessage()); System.out.println("Stack trace: "); ex.printStackTrace(System.out); } } } z3-4.4.0/examples/python/0000755000175000017500000000000012540347414015004 5ustar michaelmichaelz3-4.4.0/examples/python/hamiltonian/0000755000175000017500000000000012540347414017307 5ustar michaelmichaelz3-4.4.0/examples/python/hamiltonian/hamiltonian.py0000644000175000017500000000557512540347414022200 0ustar michaelmichael############################################ # Copyright (c) 2012 Ganesh Gopalakrishnan ganesh@cs.utah.edu # # Check if the given graph has a Hamiltonian cycle. # # Author: Ganesh Gopalakrishnan ganesh@cs.utah.edu ############################################ from z3 import * def gencon(gr): """ Input a graph as an adjacency list, e.g. {0:[1,2], 1:[2], 2:[1,0]}. Produces solver to check if the given graph has a Hamiltonian cycle. Query the solver using s.check() and if sat, then s.model() spells out the cycle. Two example graphs from http://en.wikipedia.org/wiki/Hamiltonian_path are tested. ======================================================= Explanation: Generate a list of Int vars. Constrain the first Int var ("Node 0") to be 0. Pick a node i, and attempt to number all nodes reachable from i to have a number one higher (mod L) than assigned to node i (use an Or constraint). ======================================================= """ L = len(gr) cv = [Int('cv%s'%i) for i in range(L)] s = Solver() s.add(cv[0]==0) for i in range(L): s.add(Or([cv[j]==(cv[i]+1)%L for j in gr[i]])) return s def examples(): # Example Graphs: The Dodecahedral graph from http://en.wikipedia.org/wiki/Hamiltonian_path grdodec = { 0: [1, 4, 5], 1: [0, 7, 2], 2: [1, 9, 3], 3: [2, 11, 4], 4: [3, 13, 0], 5: [0, 14, 6], 6: [5, 16, 7], 7: [6, 8, 1], 8: [7, 17, 9], 9: [8, 10, 2], 10: [9, 18, 11], 11: [10, 3, 12], 12: [11, 19, 13], 13: [12, 14, 4], 14: [13, 15, 5], 15: [14, 16, 19], 16: [6, 17, 15], 17: [16, 8, 18], 18: [10, 19, 17], 19: [18, 12, 15] } import pprint pp = pprint.PrettyPrinter(indent=4) pp.pprint(grdodec) sdodec=gencon(grdodec) print(sdodec.check()) print(sdodec.model()) # ======================================================= # See http://en.wikipedia.org/wiki/Hamiltonian_path for the Herschel graph # being the smallest possible polyhdral graph that does not have a Hamiltonian # cycle. # grherschel = { 0: [1, 9, 10, 7], 1: [0, 8, 2], 2: [1, 9, 3], 3: [2, 8, 4], 4: [3, 9, 10, 5], 5: [4, 8, 6], 6: [5, 10, 7], 7: [6, 8, 0], 8: [1, 3, 5, 7], 9: [2, 0, 4], 10: [6, 4, 0] } pp.pprint(grherschel) sherschel=gencon(grherschel) print(sherschel.check()) # ======================================================= if __name__ == "__main__": examples() z3-4.4.0/examples/python/README0000644000175000017500000000023412540347414015663 0ustar michaelmichaelThe example is copied to the build directory during configuration. You can execute it using python example.py in the build directory after you build Z3.z3-4.4.0/examples/python/complex/0000755000175000017500000000000012540347414016453 5ustar michaelmichaelz3-4.4.0/examples/python/complex/complex.py0000644000175000017500000000634312540347414020502 0ustar michaelmichael############################################ # Copyright (c) 2012 Microsoft Corporation # # Complex numbers in Z3 # See http://research.microsoft.com/en-us/um/people/leonardo/blog/2013/01/26/complex.html # # Author: Leonardo de Moura (leonardo) ############################################ from z3 import * def _to_complex(a): if isinstance(a, ComplexExpr): return a else: return ComplexExpr(a, RealVal(0)) def _is_zero(a): return (isinstance(a, int) and a == 0) or (is_rational_value(a) and a.numerator_as_long() == 0) class ComplexExpr: def __init__(self, r, i): self.r = r self.i = i def __add__(self, other): other = _to_complex(other) return ComplexExpr(self.r + other.r, self.i + other.i) def __radd__(self, other): other = _to_complex(other) return ComplexExpr(other.r + self.r, other.i + self.i) def __sub__(self, other): other = _to_complex(other) return ComplexExpr(self.r - other.r, self.i - other.i) def __rsub__(self, other): other = _to_complex(other) return ComplexExpr(other.r - self.r, other.i - self.i) def __mul__(self, other): other = _to_complex(other) return ComplexExpr(self.r*other.r - self.i*other.i, self.r*other.i + self.i*other.r) def __mul__(self, other): other = _to_complex(other) return ComplexExpr(other.r*self.r - other.i*self.i, other.i*self.r + other.r*self.i) def __pow__(self, k): if k == 0: return ComplexExpr(1, 0) if k == 1: return self if k < 0: return (self ** (-k)).inv() return reduce(lambda x, y: x * y, [self for _ in xrange(k)], ComplexExpr(1, 0)) def inv(self): den = self.r*self.r + self.i*self.i return ComplexExpr(self.r/den, -self.i/den) def __div__(self, other): inv_other = _to_complex(other).inv() return self.__mul__(inv_other) def __rdiv__(self, other): other = _to_complex(other) return self.inv().__mul__(other) def __eq__(self, other): other = _to_complex(other) return And(self.r == other.r, self.i == other.i) def __neq__(self, other): return Not(self.__eq__(other)) def simplify(self): return ComplexExpr(simplify(self.r), simplify(self.i)) def repr_i(self): if is_rational_value(self.i): return "%s*I" % self.i else: return "(%s)*I" % str(self.i) def __repr__(self): if _is_zero(self.i): return str(self.r) elif _is_zero(self.r): return self.repr_i() else: return "%s + %s" % (self.r, self.repr_i()) def Complex(a): return ComplexExpr(Real('%s.r' % a), Real('%s.i' % a)) I = ComplexExpr(RealVal(0), RealVal(1)) def evaluate_cexpr(m, e): return ComplexExpr(m[e.r], m[e.i]) x = Complex("x") s = Tactic('qfnra-nlsat').solver() s.add(x*x == -2) print(s) print(s.check()) m = s.model() print('x = %s' % evaluate_cexpr(m, x)) print((evaluate_cexpr(m,x)*evaluate_cexpr(m,x)).simplify()) s.add(x.i != -1) print(s) print(s.check()) print(s.model()) s.add(x.i != 1) print(s.check()) # print(s.model()) print ((3 + I) ** 2)/(5 - I) print ((3 + I) ** -3)/(5 - I) z3-4.4.0/examples/python/example.py0000644000175000017500000000025012540347414017006 0ustar michaelmichael# Copyright (c) Microsoft Corporation 2015 from z3 import * x = Real('x') y = Real('y') s = Solver() s.add(x + y > 5, x > 1, y > 1) print(s.check()) print(s.model()) z3-4.4.0/examples/c/0000755000175000017500000000000012540347414013705 5ustar michaelmichaelz3-4.4.0/examples/c/README0000644000175000017500000000063512540347414014571 0ustar michaelmichaelSmall example using the c++ bindings. To build the example execute make examples in the build directory. This command will create the executable c_example. On Windows, you can just execute it. On OSX and Linux, you must install z3 first using sudo make install OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (OSX) with the build directory. You need that to be able to find the Z3 shared library. z3-4.4.0/examples/c/test_capi.c0000644000175000017500000024363712540347414016043 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include #include #include #include #include #include #define LOG_Z3_CALLS #ifdef LOG_Z3_CALLS #define LOG_MSG(msg) Z3_append_log(msg) #else #define LOG_MSG(msg) ((void)0) #endif /** \defgroup capi_ex C API examples */ /*@{*/ /** @name Auxiliary Functions */ /*@{*/ /** \brief exit gracefully in case of error. */ void exitf(const char* message) { fprintf(stderr,"BUG: %s.\n", message); exit(1); } /** \brief exit if unreachable code was reached. */ void unreachable() { exitf("unreachable code was reached"); } /** \brief Simpler error handler. */ void error_handler(Z3_context c, Z3_error_code e) { printf("Error code: %d\n", e); exitf("incorrect use of Z3"); } static jmp_buf g_catch_buffer; /** \brief Low tech exceptions. In high-level programming languages, an error handler can throw an exception. */ void throw_z3_error(Z3_context c, Z3_error_code e) { longjmp(g_catch_buffer, e); } /** \brief Create a logical context. Enable model construction. Other configuration parameters can be passed in the cfg variable. Also enable tracing to stderr and register custom error handler. */ Z3_context mk_context_custom(Z3_config cfg, Z3_error_handler err) { Z3_context ctx; Z3_set_param_value(cfg, "model", "true"); ctx = Z3_mk_context(cfg); Z3_set_error_handler(ctx, err); return ctx; } /** \brief Create a logical context. Enable model construction only. Also enable tracing to stderr and register standard error handler. */ Z3_context mk_context() { Z3_config cfg; Z3_context ctx; cfg = Z3_mk_config(); ctx = mk_context_custom(cfg, error_handler); Z3_del_config(cfg); return ctx; } /** \brief Create a logical context. Enable fine-grained proof construction. Enable model construction. Also enable tracing to stderr and register standard error handler. */ Z3_context mk_proof_context() { Z3_config cfg = Z3_mk_config(); Z3_context ctx; Z3_set_param_value(cfg, "proof", "true"); ctx = mk_context_custom(cfg, throw_z3_error); Z3_del_config(cfg); return ctx; } /** \brief Create a variable using the given name and type. */ Z3_ast mk_var(Z3_context ctx, const char * name, Z3_sort ty) { Z3_symbol s = Z3_mk_string_symbol(ctx, name); return Z3_mk_const(ctx, s, ty); } /** \brief Create a boolean variable using the given name. */ Z3_ast mk_bool_var(Z3_context ctx, const char * name) { Z3_sort ty = Z3_mk_bool_sort(ctx); return mk_var(ctx, name, ty); } /** \brief Create an integer variable using the given name. */ Z3_ast mk_int_var(Z3_context ctx, const char * name) { Z3_sort ty = Z3_mk_int_sort(ctx); return mk_var(ctx, name, ty); } /** \brief Create a Z3 integer node using a C int. */ Z3_ast mk_int(Z3_context ctx, int v) { Z3_sort ty = Z3_mk_int_sort(ctx); return Z3_mk_int(ctx, v, ty); } /** \brief Create a real variable using the given name. */ Z3_ast mk_real_var(Z3_context ctx, const char * name) { Z3_sort ty = Z3_mk_real_sort(ctx); return mk_var(ctx, name, ty); } /** \brief Create the unary function application: (f x). */ Z3_ast mk_unary_app(Z3_context ctx, Z3_func_decl f, Z3_ast x) { Z3_ast args[1] = {x}; return Z3_mk_app(ctx, f, 1, args); } /** \brief Create the binary function application: (f x y). */ Z3_ast mk_binary_app(Z3_context ctx, Z3_func_decl f, Z3_ast x, Z3_ast y) { Z3_ast args[2] = {x, y}; return Z3_mk_app(ctx, f, 2, args); } /** \brief Check whether the logical context is satisfiable, and compare the result with the expected result. If the context is satisfiable, then display the model. */ void check(Z3_context ctx, Z3_lbool expected_result) { Z3_model m = 0; Z3_lbool result = Z3_check_and_get_model(ctx, &m); switch (result) { case Z3_L_FALSE: printf("unsat\n"); break; case Z3_L_UNDEF: printf("unknown\n"); printf("potential model:\n%s\n", Z3_model_to_string(ctx, m)); break; case Z3_L_TRUE: printf("sat\n%s\n", Z3_model_to_string(ctx, m)); break; } if (m) { Z3_del_model(ctx, m); } if (result != expected_result) { exitf("unexpected result"); } } /** \brief Prove that the constraints already asserted into the logical context implies the given formula. The result of the proof is displayed. Z3 is a satisfiability checker. So, one can prove \c f by showing that (not f) is unsatisfiable. The context \c ctx is not modified by this function. */ void prove(Z3_context ctx, Z3_ast f, Z3_bool is_valid) { Z3_model m; Z3_ast not_f; /* save the current state of the context */ Z3_push(ctx); not_f = Z3_mk_not(ctx, f); Z3_assert_cnstr(ctx, not_f); m = 0; switch (Z3_check_and_get_model(ctx, &m)) { case Z3_L_FALSE: /* proved */ printf("valid\n"); if (!is_valid) { exitf("unexpected result"); } break; case Z3_L_UNDEF: /* Z3 failed to prove/disprove f. */ printf("unknown\n"); if (m != 0) { /* m should be viewed as a potential counterexample. */ printf("potential counterexample:\n%s\n", Z3_model_to_string(ctx, m)); } if (is_valid) { exitf("unexpected result"); } break; case Z3_L_TRUE: /* disproved */ printf("invalid\n"); if (m) { /* the model returned by Z3 is a counterexample */ printf("counterexample:\n%s\n", Z3_model_to_string(ctx, m)); } if (is_valid) { exitf("unexpected result"); } break; } if (m) { Z3_del_model(ctx, m); } /* restore context */ Z3_pop(ctx, 1); } /** \brief Assert the axiom: function f is injective in the i-th argument. The following axiom is asserted into the logical context: \code forall (x_0, ..., x_n) finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i \endcode Where, \c finv is a fresh function declaration. */ void assert_inj_axiom(Z3_context ctx, Z3_func_decl f, unsigned i) { unsigned sz, j; Z3_sort finv_domain, finv_range; Z3_func_decl finv; Z3_sort * types; /* types of the quantified variables */ Z3_symbol * names; /* names of the quantified variables */ Z3_ast * xs; /* arguments for the application f(x_0, ..., x_i, ..., x_{n-1}) */ Z3_ast x_i, fxs, finv_fxs, eq; Z3_pattern p; Z3_ast q; sz = Z3_get_domain_size(ctx, f); if (i >= sz) { exitf("failed to create inj axiom"); } /* declare the i-th inverse of f: finv */ finv_domain = Z3_get_range(ctx, f); finv_range = Z3_get_domain(ctx, f, i); finv = Z3_mk_fresh_func_decl(ctx, "inv", 1, &finv_domain, finv_range); /* allocate temporary arrays */ types = (Z3_sort *) malloc(sizeof(Z3_sort) * sz); names = (Z3_symbol *) malloc(sizeof(Z3_symbol) * sz); xs = (Z3_ast *) malloc(sizeof(Z3_ast) * sz); /* fill types, names and xs */ for (j = 0; j < sz; j++) { types[j] = Z3_get_domain(ctx, f, j); }; for (j = 0; j < sz; j++) { names[j] = Z3_mk_int_symbol(ctx, j); }; for (j = 0; j < sz; j++) { xs[j] = Z3_mk_bound(ctx, j, types[j]); }; x_i = xs[i]; /* create f(x_0, ..., x_i, ..., x_{n-1}) */ fxs = Z3_mk_app(ctx, f, sz, xs); /* create f_inv(f(x_0, ..., x_i, ..., x_{n-1})) */ finv_fxs = mk_unary_app(ctx, finv, fxs); /* create finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i */ eq = Z3_mk_eq(ctx, finv_fxs, x_i); /* use f(x_0, ..., x_i, ..., x_{n-1}) as the pattern for the quantifier */ p = Z3_mk_pattern(ctx, 1, &fxs); printf("pattern: %s\n", Z3_pattern_to_string(ctx, p)); printf("\n"); /* create & assert quantifier */ q = Z3_mk_forall(ctx, 0, /* using default weight */ 1, /* number of patterns */ &p, /* address of the "array" of patterns */ sz, /* number of quantified variables */ types, names, eq); printf("assert axiom:\n%s\n", Z3_ast_to_string(ctx, q)); Z3_assert_cnstr(ctx, q); /* free temporary arrays */ free(types); free(names); free(xs); } /** \brief Assert the axiom: function f is commutative. This example uses the SMT-LIB parser to simplify the axiom construction. */ void assert_comm_axiom(Z3_context ctx, Z3_func_decl f) { Z3_sort t; Z3_symbol f_name, t_name; Z3_ast q; t = Z3_get_range(ctx, f); if (Z3_get_domain_size(ctx, f) != 2 || Z3_get_domain(ctx, f, 0) != t || Z3_get_domain(ctx, f, 1) != t) { exitf("function must be binary, and argument types must be equal to return type"); } /* Inside the parser, function f will be referenced using the symbol 'f'. */ f_name = Z3_mk_string_symbol(ctx, "f"); /* Inside the parser, type t will be referenced using the symbol 'T'. */ t_name = Z3_mk_string_symbol(ctx, "T"); Z3_parse_smtlib_string(ctx, "(benchmark comm :formula (forall (x T) (y T) (= (f x y) (f y x))))", 1, &t_name, &t, 1, &f_name, &f); q = Z3_get_smtlib_formula(ctx, 0); printf("assert axiom:\n%s\n", Z3_ast_to_string(ctx, q)); Z3_assert_cnstr(ctx, q); } /** \brief Z3 does not support explicitly tuple updates. They can be easily implemented as macros. The argument \c t must have tuple type. A tuple update is a new tuple where field \c i has value \c new_val, and all other fields have the value of the respective field of \c t. update(t, i, new_val) is equivalent to mk_tuple(proj_0(t), ..., new_val, ..., proj_n(t)) */ Z3_ast mk_tuple_update(Z3_context c, Z3_ast t, unsigned i, Z3_ast new_val) { Z3_sort ty; Z3_func_decl mk_tuple_decl; unsigned num_fields, j; Z3_ast * new_fields; Z3_ast result; ty = Z3_get_sort(c, t); if (Z3_get_sort_kind(c, ty) != Z3_DATATYPE_SORT) { exitf("argument must be a tuple"); } num_fields = Z3_get_tuple_sort_num_fields(c, ty); if (i >= num_fields) { exitf("invalid tuple update, index is too big"); } new_fields = (Z3_ast*) malloc(sizeof(Z3_ast) * num_fields); for (j = 0; j < num_fields; j++) { if (i == j) { /* use new_val at position i */ new_fields[j] = new_val; } else { /* use field j of t */ Z3_func_decl proj_decl = Z3_get_tuple_sort_field_decl(c, ty, j); new_fields[j] = mk_unary_app(c, proj_decl, t); } } mk_tuple_decl = Z3_get_tuple_sort_mk_decl(c, ty); result = Z3_mk_app(c, mk_tuple_decl, num_fields, new_fields); free(new_fields); return result; } /** \brief Display a symbol in the given output stream. */ void display_symbol(Z3_context c, FILE * out, Z3_symbol s) { switch (Z3_get_symbol_kind(c, s)) { case Z3_INT_SYMBOL: fprintf(out, "#%d", Z3_get_symbol_int(c, s)); break; case Z3_STRING_SYMBOL: fprintf(out, "%s", Z3_get_symbol_string(c, s)); break; default: unreachable(); } } /** \brief Display the given type. */ void display_sort(Z3_context c, FILE * out, Z3_sort ty) { switch (Z3_get_sort_kind(c, ty)) { case Z3_UNINTERPRETED_SORT: display_symbol(c, out, Z3_get_sort_name(c, ty)); break; case Z3_BOOL_SORT: fprintf(out, "bool"); break; case Z3_INT_SORT: fprintf(out, "int"); break; case Z3_REAL_SORT: fprintf(out, "real"); break; case Z3_BV_SORT: fprintf(out, "bv%d", Z3_get_bv_sort_size(c, ty)); break; case Z3_ARRAY_SORT: fprintf(out, "["); display_sort(c, out, Z3_get_array_sort_domain(c, ty)); fprintf(out, "->"); display_sort(c, out, Z3_get_array_sort_range(c, ty)); fprintf(out, "]"); break; case Z3_DATATYPE_SORT: if (Z3_get_datatype_sort_num_constructors(c, ty) != 1) { fprintf(out, "%s", Z3_sort_to_string(c,ty)); break; } { unsigned num_fields = Z3_get_tuple_sort_num_fields(c, ty); unsigned i; fprintf(out, "("); for (i = 0; i < num_fields; i++) { Z3_func_decl field = Z3_get_tuple_sort_field_decl(c, ty, i); if (i > 0) { fprintf(out, ", "); } display_sort(c, out, Z3_get_range(c, field)); } fprintf(out, ")"); break; } default: fprintf(out, "unknown["); display_symbol(c, out, Z3_get_sort_name(c, ty)); fprintf(out, "]"); break; } } /** \brief Custom ast pretty printer. This function demonstrates how to use the API to navigate terms. */ void display_ast(Z3_context c, FILE * out, Z3_ast v) { switch (Z3_get_ast_kind(c, v)) { case Z3_NUMERAL_AST: { Z3_sort t; fprintf(out, "%s", Z3_get_numeral_string(c, v)); t = Z3_get_sort(c, v); fprintf(out, ":"); display_sort(c, out, t); break; } case Z3_APP_AST: { unsigned i; Z3_app app = Z3_to_app(c, v); unsigned num_fields = Z3_get_app_num_args(c, app); Z3_func_decl d = Z3_get_app_decl(c, app); fprintf(out, "%s", Z3_func_decl_to_string(c, d)); if (num_fields > 0) { fprintf(out, "["); for (i = 0; i < num_fields; i++) { if (i > 0) { fprintf(out, ", "); } display_ast(c, out, Z3_get_app_arg(c, app, i)); } fprintf(out, "]"); } break; } case Z3_QUANTIFIER_AST: { fprintf(out, "quantifier"); ; } default: fprintf(out, "#unknown"); } } /** \brief Custom function interpretations pretty printer. */ void display_function_interpretations(Z3_context c, FILE * out, Z3_model m) { unsigned num_functions, i; fprintf(out, "function interpretations:\n"); num_functions = Z3_get_model_num_funcs(c, m); for (i = 0; i < num_functions; i++) { Z3_func_decl fdecl; Z3_symbol name; Z3_ast func_else; unsigned num_entries, j; fdecl = Z3_get_model_func_decl(c, m, i); name = Z3_get_decl_name(c, fdecl); display_symbol(c, out, name); fprintf(out, " = {"); num_entries = Z3_get_model_func_num_entries(c, m, i); for (j = 0; j < num_entries; j++) { unsigned num_args, k; if (j > 0) { fprintf(out, ", "); } num_args = Z3_get_model_func_entry_num_args(c, m, i, j); fprintf(out, "("); for (k = 0; k < num_args; k++) { if (k > 0) { fprintf(out, ", "); } display_ast(c, out, Z3_get_model_func_entry_arg(c, m, i, j, k)); } fprintf(out, "|->"); display_ast(c, out, Z3_get_model_func_entry_value(c, m, i, j)); fprintf(out, ")"); } if (num_entries > 0) { fprintf(out, ", "); } fprintf(out, "(else|->"); func_else = Z3_get_model_func_else(c, m, i); display_ast(c, out, func_else); fprintf(out, ")}\n"); } } /** \brief Custom model pretty printer. */ void display_model(Z3_context c, FILE * out, Z3_model m) { unsigned num_constants; unsigned i; num_constants = Z3_get_model_num_constants(c, m); for (i = 0; i < num_constants; i++) { Z3_symbol name; Z3_func_decl cnst = Z3_get_model_constant(c, m, i); Z3_ast a, v; Z3_bool ok; name = Z3_get_decl_name(c, cnst); display_symbol(c, out, name); fprintf(out, " = "); a = Z3_mk_app(c, cnst, 0, 0); v = a; ok = Z3_eval(c, m, a, &v); display_ast(c, out, v); fprintf(out, "\n"); } display_function_interpretations(c, out, m); } /** \brief Similar to #check, but uses #display_model instead of #Z3_model_to_string. */ void check2(Z3_context ctx, Z3_lbool expected_result) { Z3_model m = 0; Z3_lbool result = Z3_check_and_get_model(ctx, &m); switch (result) { case Z3_L_FALSE: printf("unsat\n"); break; case Z3_L_UNDEF: printf("unknown\n"); printf("potential model:\n"); display_model(ctx, stdout, m); break; case Z3_L_TRUE: printf("sat\n"); display_model(ctx, stdout, m); break; } if (m) { Z3_del_model(ctx, m); } if (result != expected_result) { exitf("unexpected result"); } } /** \brief Display Z3 version in the standard output. */ void display_version() { unsigned major, minor, build, revision; Z3_get_version(&major, &minor, &build, &revision); printf("Z3 %d.%d.%d.%d\n", major, minor, build, revision); } /*@}*/ /** @name Examples */ /*@{*/ /** \brief "Hello world" example: create a Z3 logical context, and delete it. */ void simple_example() { Z3_context ctx; LOG_MSG("simple_example"); printf("\nsimple_example\n"); ctx = mk_context(); /* do something with the context */ printf("CONTEXT:\n%sEND OF CONTEXT\n", Z3_context_to_string(ctx)); /* delete logical context */ Z3_del_context(ctx); } /** Demonstration of how Z3 can be used to prove validity of De Morgan's Duality Law: {e not(x and y) <-> (not x) or ( not y) } */ void demorgan() { Z3_config cfg; Z3_context ctx; Z3_sort bool_sort; Z3_symbol symbol_x, symbol_y; Z3_ast x, y, not_x, not_y, x_and_y, ls, rs, conjecture, negated_conjecture; Z3_ast args[2]; printf("\nDeMorgan\n"); LOG_MSG("DeMorgan"); cfg = Z3_mk_config(); ctx = Z3_mk_context(cfg); Z3_del_config(cfg); bool_sort = Z3_mk_bool_sort(ctx); symbol_x = Z3_mk_int_symbol(ctx, 0); symbol_y = Z3_mk_int_symbol(ctx, 1); x = Z3_mk_const(ctx, symbol_x, bool_sort); y = Z3_mk_const(ctx, symbol_y, bool_sort); /* De Morgan - with a negation around */ /* !(!(x && y) <-> (!x || !y)) */ not_x = Z3_mk_not(ctx, x); not_y = Z3_mk_not(ctx, y); args[0] = x; args[1] = y; x_and_y = Z3_mk_and(ctx, 2, args); ls = Z3_mk_not(ctx, x_and_y); args[0] = not_x; args[1] = not_y; rs = Z3_mk_or(ctx, 2, args); conjecture = Z3_mk_iff(ctx, ls, rs); negated_conjecture = Z3_mk_not(ctx, conjecture); Z3_assert_cnstr(ctx, negated_conjecture); switch (Z3_check(ctx)) { case Z3_L_FALSE: /* The negated conjecture was unsatisfiable, hence the conjecture is valid */ printf("DeMorgan is valid\n"); break; case Z3_L_UNDEF: /* Check returned undef */ printf("Undef\n"); break; case Z3_L_TRUE: /* The negated conjecture was satisfiable, hence the conjecture is not valid */ printf("DeMorgan is not valid\n"); break; } Z3_del_context(ctx); } /** \brief Find a model for x xor y. */ void find_model_example1() { Z3_context ctx; Z3_ast x, y, x_xor_y; printf("\nfind_model_example1\n"); LOG_MSG("find_model_example1"); ctx = mk_context(); x = mk_bool_var(ctx, "x"); y = mk_bool_var(ctx, "y"); x_xor_y = Z3_mk_xor(ctx, x, y); Z3_assert_cnstr(ctx, x_xor_y); printf("model for: x xor y\n"); check(ctx, Z3_L_TRUE); Z3_del_context(ctx); } /** \brief Find a model for x < y + 1, x > 2. Then, assert not(x = y), and find another model. */ void find_model_example2() { Z3_context ctx; Z3_ast x, y, one, two, y_plus_one; Z3_ast x_eq_y; Z3_ast args[2]; Z3_ast c1, c2, c3; printf("\nfind_model_example2\n"); LOG_MSG("find_model_example2"); ctx = mk_context(); x = mk_int_var(ctx, "x"); y = mk_int_var(ctx, "y"); one = mk_int(ctx, 1); two = mk_int(ctx, 2); args[0] = y; args[1] = one; y_plus_one = Z3_mk_add(ctx, 2, args); c1 = Z3_mk_lt(ctx, x, y_plus_one); c2 = Z3_mk_gt(ctx, x, two); Z3_assert_cnstr(ctx, c1); Z3_assert_cnstr(ctx, c2); printf("model for: x < y + 1, x > 2\n"); check(ctx, Z3_L_TRUE); /* assert not(x = y) */ x_eq_y = Z3_mk_eq(ctx, x, y); c3 = Z3_mk_not(ctx, x_eq_y); Z3_assert_cnstr(ctx, c3); printf("model for: x < y + 1, x > 2, not(x = y)\n"); check(ctx, Z3_L_TRUE); Z3_del_context(ctx); } /** \brief Prove x = y implies g(x) = g(y), and disprove x = y implies g(g(x)) = g(y). This function demonstrates how to create uninterpreted types and functions. */ void prove_example1() { Z3_context ctx; Z3_symbol U_name, g_name, x_name, y_name; Z3_sort U; Z3_sort g_domain[1]; Z3_func_decl g; Z3_ast x, y, gx, ggx, gy; Z3_ast eq, f; printf("\nprove_example1\n"); LOG_MSG("prove_example1"); ctx = mk_context(); /* create uninterpreted type. */ U_name = Z3_mk_string_symbol(ctx, "U"); U = Z3_mk_uninterpreted_sort(ctx, U_name); /* declare function g */ g_name = Z3_mk_string_symbol(ctx, "g"); g_domain[0] = U; g = Z3_mk_func_decl(ctx, g_name, 1, g_domain, U); /* create x and y */ x_name = Z3_mk_string_symbol(ctx, "x"); y_name = Z3_mk_string_symbol(ctx, "y"); x = Z3_mk_const(ctx, x_name, U); y = Z3_mk_const(ctx, y_name, U); /* create g(x), g(y) */ gx = mk_unary_app(ctx, g, x); gy = mk_unary_app(ctx, g, y); /* assert x = y */ eq = Z3_mk_eq(ctx, x, y); Z3_assert_cnstr(ctx, eq); /* prove g(x) = g(y) */ f = Z3_mk_eq(ctx, gx, gy); printf("prove: x = y implies g(x) = g(y)\n"); prove(ctx, f, Z3_TRUE); /* create g(g(x)) */ ggx = mk_unary_app(ctx, g, gx); /* disprove g(g(x)) = g(y) */ f = Z3_mk_eq(ctx, ggx, gy); printf("disprove: x = y implies g(g(x)) = g(y)\n"); prove(ctx, f, Z3_FALSE); Z3_del_context(ctx); } /** \brief Prove not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0 . Then, show that z < -1 is not implied. This example demonstrates how to combine uninterpreted functions and arithmetic. */ void prove_example2() { Z3_context ctx; Z3_sort int_sort; Z3_symbol g_name; Z3_sort g_domain[1]; Z3_func_decl g; Z3_ast x, y, z, zero, minus_one, x_plus_z, gx, gy, gz, gx_gy, ggx_gy; Z3_ast args[2]; Z3_ast eq, c1, c2, c3, f; printf("\nprove_example2\n"); LOG_MSG("prove_example2"); ctx = mk_context(); /* declare function g */ int_sort = Z3_mk_int_sort(ctx); g_name = Z3_mk_string_symbol(ctx, "g"); g_domain[0] = int_sort; g = Z3_mk_func_decl(ctx, g_name, 1, g_domain, int_sort); /* create x, y, and z */ x = mk_int_var(ctx, "x"); y = mk_int_var(ctx, "y"); z = mk_int_var(ctx, "z"); /* create gx, gy, gz */ gx = mk_unary_app(ctx, g, x); gy = mk_unary_app(ctx, g, y); gz = mk_unary_app(ctx, g, z); /* create zero */ zero = mk_int(ctx, 0); /* assert not(g(g(x) - g(y)) = g(z)) */ args[0] = gx; args[1] = gy; gx_gy = Z3_mk_sub(ctx, 2, args); ggx_gy = mk_unary_app(ctx, g, gx_gy); eq = Z3_mk_eq(ctx, ggx_gy, gz); c1 = Z3_mk_not(ctx, eq); Z3_assert_cnstr(ctx, c1); /* assert x + z <= y */ args[0] = x; args[1] = z; x_plus_z = Z3_mk_add(ctx, 2, args); c2 = Z3_mk_le(ctx, x_plus_z, y); Z3_assert_cnstr(ctx, c2); /* assert y <= x */ c3 = Z3_mk_le(ctx, y, x); Z3_assert_cnstr(ctx, c3); /* prove z < 0 */ f = Z3_mk_lt(ctx, z, zero); printf("prove: not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0\n"); prove(ctx, f, Z3_TRUE); /* disprove z < -1 */ minus_one = mk_int(ctx, -1); f = Z3_mk_lt(ctx, z, minus_one); printf("disprove: not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < -1\n"); prove(ctx, f, Z3_FALSE); Z3_del_context(ctx); } /** \brief Show how push & pop can be used to create "backtracking" points. This example also demonstrates how big numbers can be created in Z3. */ void push_pop_example1() { Z3_context ctx; Z3_sort int_sort; Z3_symbol x_sym, y_sym; Z3_ast x, y, big_number, three; Z3_ast c1, c2, c3; printf("\npush_pop_example1\n"); LOG_MSG("push_pop_example1"); ctx = mk_context(); /* create a big number */ int_sort = Z3_mk_int_sort(ctx); big_number = Z3_mk_numeral(ctx, "1000000000000000000000000000000000000000000000000000000", int_sort); /* create number 3 */ three = Z3_mk_numeral(ctx, "3", int_sort); /* create x */ x_sym = Z3_mk_string_symbol(ctx, "x"); x = Z3_mk_const(ctx, x_sym, int_sort); /* assert x >= "big number" */ c1 = Z3_mk_ge(ctx, x, big_number); printf("assert: x >= 'big number'\n"); Z3_assert_cnstr(ctx, c1); /* create a backtracking point */ printf("push\n"); Z3_push(ctx); printf("number of scopes: %d\n", Z3_get_num_scopes(ctx)); /* assert x <= 3 */ c2 = Z3_mk_le(ctx, x, three); printf("assert: x <= 3\n"); Z3_assert_cnstr(ctx, c2); /* context is inconsistent at this point */ check2(ctx, Z3_L_FALSE); /* backtrack: the constraint x <= 3 will be removed, since it was asserted after the last Z3_push. */ printf("pop\n"); Z3_pop(ctx, 1); printf("number of scopes: %d\n", Z3_get_num_scopes(ctx)); /* the context is consistent again. */ check2(ctx, Z3_L_TRUE); /* new constraints can be asserted... */ /* create y */ y_sym = Z3_mk_string_symbol(ctx, "y"); y = Z3_mk_const(ctx, y_sym, int_sort); /* assert y > x */ c3 = Z3_mk_gt(ctx, y, x); printf("assert: y > x\n"); Z3_assert_cnstr(ctx, c3); /* the context is still consistent. */ check2(ctx, Z3_L_TRUE); Z3_del_context(ctx); } /** \brief Prove that f(x, y) = f(w, v) implies y = v when \c f is injective in the second argument. \sa assert_inj_axiom. */ void quantifier_example1() { Z3_config cfg; Z3_context ctx; Z3_sort int_sort; Z3_symbol f_name; Z3_sort f_domain[2]; Z3_func_decl f; Z3_ast x, y, w, v, fxy, fwv; Z3_ast p1, p2, p3, not_p3; printf("\nquantifier_example1\n"); LOG_MSG("quantifier_example1"); cfg = Z3_mk_config(); /* If quantified formulas are asserted in a logical context, then Z3 may return L_UNDEF. In this case, the model produced by Z3 should be viewed as a potential/candidate model. */ /* The current model finder for quantified formulas cannot handle injectivity. So, we are limiting the number of iterations to avoid a long "wait". */ Z3_global_param_set("smt.mbqi.max_iterations", "10"); ctx = mk_context_custom(cfg, error_handler); Z3_del_config(cfg); /* declare function f */ int_sort = Z3_mk_int_sort(ctx); f_name = Z3_mk_string_symbol(ctx, "f"); f_domain[0] = int_sort; f_domain[1] = int_sort; f = Z3_mk_func_decl(ctx, f_name, 2, f_domain, int_sort); /* assert that f is injective in the second argument. */ assert_inj_axiom(ctx, f, 1); /* create x, y, v, w, fxy, fwv */ x = mk_int_var(ctx, "x"); y = mk_int_var(ctx, "y"); v = mk_int_var(ctx, "v"); w = mk_int_var(ctx, "w"); fxy = mk_binary_app(ctx, f, x, y); fwv = mk_binary_app(ctx, f, w, v); /* assert f(x, y) = f(w, v) */ p1 = Z3_mk_eq(ctx, fxy, fwv); Z3_assert_cnstr(ctx, p1); /* prove f(x, y) = f(w, v) implies y = v */ p2 = Z3_mk_eq(ctx, y, v); printf("prove: f(x, y) = f(w, v) implies y = v\n"); prove(ctx, p2, Z3_TRUE); /* disprove f(x, y) = f(w, v) implies x = w */ /* using check2 instead of prove */ p3 = Z3_mk_eq(ctx, x, w); not_p3 = Z3_mk_not(ctx, p3); Z3_assert_cnstr(ctx, not_p3); printf("disprove: f(x, y) = f(w, v) implies x = w\n"); printf("that is: not(f(x, y) = f(w, v) implies x = w) is satisfiable\n"); check2(ctx, Z3_L_UNDEF); printf("reason for last failure: %d (7 = quantifiers)\n", Z3_get_search_failure(ctx)); if (Z3_get_search_failure(ctx) != Z3_QUANTIFIERS) { exitf("unexpected result"); } Z3_del_context(ctx); /* reset global parameters set by this function */ Z3_global_param_reset_all(); } /** \brief Prove store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3)). This example demonstrates how to use the array theory. */ void array_example1() { Z3_context ctx; Z3_sort int_sort, array_sort; Z3_ast a1, a2, i1, v1, i2, v2, i3; Z3_ast st1, st2, sel1, sel2; Z3_ast antecedent, consequent; Z3_ast ds[3]; Z3_ast thm; printf("\narray_example1\n"); LOG_MSG("array_example1"); ctx = mk_context(); int_sort = Z3_mk_int_sort(ctx); array_sort = Z3_mk_array_sort(ctx, int_sort, int_sort); a1 = mk_var(ctx, "a1", array_sort); a2 = mk_var(ctx, "a2", array_sort); i1 = mk_var(ctx, "i1", int_sort); i2 = mk_var(ctx, "i2", int_sort); i3 = mk_var(ctx, "i3", int_sort); v1 = mk_var(ctx, "v1", int_sort); v2 = mk_var(ctx, "v2", int_sort); st1 = Z3_mk_store(ctx, a1, i1, v1); st2 = Z3_mk_store(ctx, a2, i2, v2); sel1 = Z3_mk_select(ctx, a1, i3); sel2 = Z3_mk_select(ctx, a2, i3); /* create antecedent */ antecedent = Z3_mk_eq(ctx, st1, st2); /* create consequent: i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3) */ ds[0] = Z3_mk_eq(ctx, i1, i3); ds[1] = Z3_mk_eq(ctx, i2, i3); ds[2] = Z3_mk_eq(ctx, sel1, sel2); consequent = Z3_mk_or(ctx, 3, ds); /* prove store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3)) */ thm = Z3_mk_implies(ctx, antecedent, consequent); printf("prove: store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3))\n"); printf("%s\n", Z3_ast_to_string(ctx, thm)); prove(ctx, thm, Z3_TRUE); Z3_del_context(ctx); } /** \brief Show that distinct(a_0, ... , a_n) is unsatisfiable when \c a_i's are arrays from boolean to boolean and n > 4. This example also shows how to use the \c distinct construct. */ void array_example2() { Z3_context ctx; Z3_sort bool_sort, array_sort; Z3_ast a[5]; Z3_ast d; unsigned i, n; printf("\narray_example2\n"); LOG_MSG("array_example2"); for (n = 2; n <= 5; n++) { printf("n = %d\n", n); ctx = mk_context(); bool_sort = Z3_mk_bool_sort(ctx); array_sort = Z3_mk_array_sort(ctx, bool_sort, bool_sort); /* create arrays */ for (i = 0; i < n; i++) { Z3_symbol s = Z3_mk_int_symbol(ctx, i); a[i] = Z3_mk_const(ctx, s, array_sort); } /* assert distinct(a[0], ..., a[n]) */ d = Z3_mk_distinct(ctx, n, a); printf("%s\n", Z3_ast_to_string(ctx, d)); Z3_assert_cnstr(ctx, d); /* context is satisfiable if n < 5 */ check2(ctx, n < 5 ? Z3_L_TRUE : Z3_L_FALSE); Z3_del_context(ctx); } } /** \brief Simple array type construction/deconstruction example. */ void array_example3() { Z3_context ctx; Z3_sort bool_sort, int_sort, array_sort; Z3_sort domain, range; printf("\narray_example3\n"); LOG_MSG("array_example3"); ctx = mk_context(); bool_sort = Z3_mk_bool_sort(ctx); int_sort = Z3_mk_int_sort(ctx); array_sort = Z3_mk_array_sort(ctx, int_sort, bool_sort); if (Z3_get_sort_kind(ctx, array_sort) != Z3_ARRAY_SORT) { exitf("type must be an array type"); } domain = Z3_get_array_sort_domain(ctx, array_sort); range = Z3_get_array_sort_range(ctx, array_sort); printf("domain: "); display_sort(ctx, stdout, domain); printf("\n"); printf("range: "); display_sort(ctx, stdout, range); printf("\n"); if (int_sort != domain || bool_sort != range) { exitf("invalid array type"); } Z3_del_context(ctx); } /** \brief Simple tuple type example. It creates a tuple that is a pair of real numbers. */ void tuple_example1() { Z3_context ctx; Z3_sort real_sort, pair_sort; Z3_symbol mk_tuple_name; Z3_func_decl mk_tuple_decl; Z3_symbol proj_names[2]; Z3_sort proj_sorts[2]; Z3_func_decl proj_decls[2]; Z3_func_decl get_x_decl, get_y_decl; printf("\ntuple_example1\n"); LOG_MSG("tuple_example1"); ctx = mk_context(); real_sort = Z3_mk_real_sort(ctx); /* Create pair (tuple) type */ mk_tuple_name = Z3_mk_string_symbol(ctx, "mk_pair"); proj_names[0] = Z3_mk_string_symbol(ctx, "get_x"); proj_names[1] = Z3_mk_string_symbol(ctx, "get_y"); proj_sorts[0] = real_sort; proj_sorts[1] = real_sort; /* Z3_mk_tuple_sort will set mk_tuple_decl and proj_decls */ pair_sort = Z3_mk_tuple_sort(ctx, mk_tuple_name, 2, proj_names, proj_sorts, &mk_tuple_decl, proj_decls); get_x_decl = proj_decls[0]; /* function that extracts the first element of a tuple. */ get_y_decl = proj_decls[1]; /* function that extracts the second element of a tuple. */ printf("tuple_sort: "); display_sort(ctx, stdout, pair_sort); printf("\n"); { /* prove that get_x(mk_pair(x,y)) == 1 implies x = 1*/ Z3_ast app1, app2, x, y, one; Z3_ast eq1, eq2, eq3, thm; x = mk_real_var(ctx, "x"); y = mk_real_var(ctx, "y"); app1 = mk_binary_app(ctx, mk_tuple_decl, x, y); app2 = mk_unary_app(ctx, get_x_decl, app1); one = Z3_mk_numeral(ctx, "1", real_sort); eq1 = Z3_mk_eq(ctx, app2, one); eq2 = Z3_mk_eq(ctx, x, one); thm = Z3_mk_implies(ctx, eq1, eq2); printf("prove: get_x(mk_pair(x, y)) = 1 implies x = 1\n"); prove(ctx, thm, Z3_TRUE); /* disprove that get_x(mk_pair(x,y)) == 1 implies y = 1*/ eq3 = Z3_mk_eq(ctx, y, one); thm = Z3_mk_implies(ctx, eq1, eq3); printf("disprove: get_x(mk_pair(x, y)) = 1 implies y = 1\n"); prove(ctx, thm, Z3_FALSE); } { /* prove that get_x(p1) = get_x(p2) and get_y(p1) = get_y(p2) implies p1 = p2 */ Z3_ast p1, p2, x1, x2, y1, y2; Z3_ast antecedents[2]; Z3_ast antecedent, consequent, thm; p1 = mk_var(ctx, "p1", pair_sort); p2 = mk_var(ctx, "p2", pair_sort); x1 = mk_unary_app(ctx, get_x_decl, p1); y1 = mk_unary_app(ctx, get_y_decl, p1); x2 = mk_unary_app(ctx, get_x_decl, p2); y2 = mk_unary_app(ctx, get_y_decl, p2); antecedents[0] = Z3_mk_eq(ctx, x1, x2); antecedents[1] = Z3_mk_eq(ctx, y1, y2); antecedent = Z3_mk_and(ctx, 2, antecedents); consequent = Z3_mk_eq(ctx, p1, p2); thm = Z3_mk_implies(ctx, antecedent, consequent); printf("prove: get_x(p1) = get_x(p2) and get_y(p1) = get_y(p2) implies p1 = p2\n"); prove(ctx, thm, Z3_TRUE); /* disprove that get_x(p1) = get_x(p2) implies p1 = p2 */ thm = Z3_mk_implies(ctx, antecedents[0], consequent); printf("disprove: get_x(p1) = get_x(p2) implies p1 = p2\n"); prove(ctx, thm, Z3_FALSE); } { /* demonstrate how to use the mk_tuple_update function */ /* prove that p2 = update(p1, 0, 10) implies get_x(p2) = 10 */ Z3_ast p1, p2, one, ten, updt, x, y; Z3_ast antecedent, consequent, thm; p1 = mk_var(ctx, "p1", pair_sort); p2 = mk_var(ctx, "p2", pair_sort); one = Z3_mk_numeral(ctx, "1", real_sort); ten = Z3_mk_numeral(ctx, "10", real_sort); updt = mk_tuple_update(ctx, p1, 0, ten); antecedent = Z3_mk_eq(ctx, p2, updt); x = mk_unary_app(ctx, get_x_decl, p2); consequent = Z3_mk_eq(ctx, x, ten); thm = Z3_mk_implies(ctx, antecedent, consequent); printf("prove: p2 = update(p1, 0, 10) implies get_x(p2) = 10\n"); prove(ctx, thm, Z3_TRUE); /* disprove that p2 = update(p1, 0, 10) implies get_y(p2) = 10 */ y = mk_unary_app(ctx, get_y_decl, p2); consequent = Z3_mk_eq(ctx, y, ten); thm = Z3_mk_implies(ctx, antecedent, consequent); printf("disprove: p2 = update(p1, 0, 10) implies get_y(p2) = 10\n"); prove(ctx, thm, Z3_FALSE); } Z3_del_context(ctx); } /** \brief Simple bit-vector example. This example disproves that x - 10 <= 0 IFF x <= 10 for (32-bit) machine integers */ void bitvector_example1() { Z3_context ctx; Z3_sort bv_sort; Z3_ast x, zero, ten, x_minus_ten, c1, c2, thm; printf("\nbitvector_example1\n"); LOG_MSG("bitvector_example1"); ctx = mk_context(); bv_sort = Z3_mk_bv_sort(ctx, 32); x = mk_var(ctx, "x", bv_sort); zero = Z3_mk_numeral(ctx, "0", bv_sort); ten = Z3_mk_numeral(ctx, "10", bv_sort); x_minus_ten = Z3_mk_bvsub(ctx, x, ten); /* bvsle is signed less than or equal to */ c1 = Z3_mk_bvsle(ctx, x, ten); c2 = Z3_mk_bvsle(ctx, x_minus_ten, zero); thm = Z3_mk_iff(ctx, c1, c2); printf("disprove: x - 10 <= 0 IFF x <= 10 for (32-bit) machine integers\n"); prove(ctx, thm, Z3_FALSE); Z3_del_context(ctx); } /** \brief Find x and y such that: x ^ y - 103 == x * y */ void bitvector_example2() { Z3_context ctx = mk_context(); /* construct x ^ y - 103 == x * y */ Z3_sort bv_sort = Z3_mk_bv_sort(ctx, 32); Z3_ast x = mk_var(ctx, "x", bv_sort); Z3_ast y = mk_var(ctx, "y", bv_sort); Z3_ast x_xor_y = Z3_mk_bvxor(ctx, x, y); Z3_ast c103 = Z3_mk_numeral(ctx, "103", bv_sort); Z3_ast lhs = Z3_mk_bvsub(ctx, x_xor_y, c103); Z3_ast rhs = Z3_mk_bvmul(ctx, x, y); Z3_ast ctr = Z3_mk_eq(ctx, lhs, rhs); printf("\nbitvector_example2\n"); LOG_MSG("bitvector_example2"); printf("find values of x and y, such that x ^ y - 103 == x * y\n"); /* add the constraint x ^ y - 103 == x * y<\tt> to the logical context */ Z3_assert_cnstr(ctx, ctr); /* find a model (i.e., values for x an y that satisfy the constraint */ check(ctx, Z3_L_TRUE); Z3_del_context(ctx); } /** \brief Demonstrate how to use #Z3_eval. */ void eval_example1() { Z3_context ctx; Z3_ast x, y, two; Z3_ast c1, c2; Z3_model m; printf("\neval_example1\n"); LOG_MSG("eval_example1"); ctx = mk_context(); x = mk_int_var(ctx, "x"); y = mk_int_var(ctx, "y"); two = mk_int(ctx, 2); /* assert x < y */ c1 = Z3_mk_lt(ctx, x, y); Z3_assert_cnstr(ctx, c1); /* assert x > 2 */ c2 = Z3_mk_gt(ctx, x, two); Z3_assert_cnstr(ctx, c2); /* find model for the constraints above */ if (Z3_check_and_get_model(ctx, &m) == Z3_L_TRUE) { Z3_ast x_plus_y; Z3_ast args[2] = {x, y}; Z3_ast v; printf("MODEL:\n%s", Z3_model_to_string(ctx, m)); x_plus_y = Z3_mk_add(ctx, 2, args); printf("\nevaluating x+y\n"); if (Z3_eval(ctx, m, x_plus_y, &v)) { printf("result = "); display_ast(ctx, stdout, v); printf("\n"); } else { exitf("failed to evaluate: x+y"); } Z3_del_model(ctx, m); } else { exitf("the constraints are satisfiable"); } Z3_del_context(ctx); } /** \brief Several logical context can be used simultaneously. */ void two_contexts_example1() { Z3_context ctx1, ctx2; Z3_ast x1, x2; printf("\ntwo_contexts_example1\n"); LOG_MSG("two_contexts_example1"); /* using the same (default) configuration to initialized both logical contexts. */ ctx1 = mk_context(); ctx2 = mk_context(); x1 = Z3_mk_const(ctx1, Z3_mk_int_symbol(ctx1,0), Z3_mk_bool_sort(ctx1)); x2 = Z3_mk_const(ctx2, Z3_mk_int_symbol(ctx2,0), Z3_mk_bool_sort(ctx2)); Z3_del_context(ctx1); /* ctx2 can still be used. */ printf("%s\n", Z3_ast_to_string(ctx2, x2)); Z3_del_context(ctx2); } /** \brief Demonstrates how error codes can be read insted of registering an error handler. */ void error_code_example1() { Z3_config cfg; Z3_context ctx; Z3_ast x; Z3_model m; Z3_ast v; Z3_func_decl x_decl; const char * str; printf("\nerror_code_example1\n"); LOG_MSG("error_code_example11"); /* Do not register an error handler, as we want to use Z3_get_error_code manually */ cfg = Z3_mk_config(); ctx = mk_context_custom(cfg, NULL); Z3_del_config(cfg); x = mk_bool_var(ctx, "x"); x_decl = Z3_get_app_decl(ctx, Z3_to_app(ctx, x)); Z3_assert_cnstr(ctx, x); if (Z3_check_and_get_model(ctx, &m) != Z3_L_TRUE) { exitf("unexpected result"); } if (!Z3_eval_func_decl(ctx, m, x_decl, &v)) { exitf("did not obtain value for declaration.\n"); } if (Z3_get_error_code(ctx) == Z3_OK) { printf("last call succeeded.\n"); } /* The following call will fail since the value of x is a boolean */ str = Z3_get_numeral_string(ctx, v); if (Z3_get_error_code(ctx) != Z3_OK) { printf("last call failed.\n"); } Z3_del_model(ctx, m); Z3_del_context(ctx); } /** \brief Demonstrates how error handlers can be used. */ void error_code_example2() { Z3_config cfg; Z3_context ctx = NULL; int r; printf("\nerror_code_example2\n"); LOG_MSG("error_code_example2"); /* low tech try&catch */ r = setjmp(g_catch_buffer); if (r == 0) { Z3_ast x, y, app; cfg = Z3_mk_config(); ctx = mk_context_custom(cfg, throw_z3_error); Z3_del_config(cfg); x = mk_int_var(ctx, "x"); y = mk_bool_var(ctx, "y"); printf("before Z3_mk_iff\n"); /* the next call will produce an error */ app = Z3_mk_iff(ctx, x, y); unreachable(); Z3_del_context(ctx); } else { printf("Z3 error: %s.\n", Z3_get_error_msg((Z3_error_code)r)); if (ctx != NULL) { Z3_del_context(ctx); } } } /** \brief Demonstrates how to use the SMTLIB parser. */ void parser_example1() { Z3_context ctx; unsigned i, num_formulas; printf("\nparser_example1\n"); LOG_MSG("parser_example1"); ctx = mk_context(); Z3_parse_smtlib_string(ctx, "(benchmark tst :extrafuns ((x Int) (y Int)) :formula (> x y) :formula (> x 0))", 0, 0, 0, 0, 0, 0); num_formulas = Z3_get_smtlib_num_formulas(ctx); for (i = 0; i < num_formulas; i++) { Z3_ast f = Z3_get_smtlib_formula(ctx, i); printf("formula %d: %s\n", i, Z3_ast_to_string(ctx, f)); Z3_assert_cnstr(ctx, f); } check(ctx, Z3_L_TRUE); Z3_del_context(ctx); } /** \brief Demonstrates how to initialize the parser symbol table. */ void parser_example2() { Z3_context ctx; Z3_ast x, y; Z3_symbol names[2]; Z3_func_decl decls[2]; Z3_ast f; printf("\nparser_example2\n"); LOG_MSG("parser_example2"); ctx = mk_context(); /* Z3_enable_arithmetic doesn't need to be invoked in this example because it will be implicitly invoked by mk_int_var. */ x = mk_int_var(ctx, "x"); decls[0] = Z3_get_app_decl(ctx, Z3_to_app(ctx, x)); y = mk_int_var(ctx, "y"); decls[1] = Z3_get_app_decl(ctx, Z3_to_app(ctx, y)); names[0] = Z3_mk_string_symbol(ctx, "a"); names[1] = Z3_mk_string_symbol(ctx, "b"); Z3_parse_smtlib_string(ctx, "(benchmark tst :formula (> a b))", 0, 0, 0, /* 'x' and 'y' declarations are inserted as 'a' and 'b' into the parser symbol table. */ 2, names, decls); f = Z3_get_smtlib_formula(ctx, 0); printf("formula: %s\n", Z3_ast_to_string(ctx, f)); Z3_assert_cnstr(ctx, f); check(ctx, Z3_L_TRUE); Z3_del_context(ctx); } /** \brief Demonstrates how to initialize the parser symbol table. */ void parser_example3() { Z3_config cfg; Z3_context ctx; Z3_sort int_sort; Z3_symbol g_name; Z3_sort g_domain[2]; Z3_func_decl g; Z3_ast thm; printf("\nparser_example3\n"); LOG_MSG("parser_example3"); cfg = Z3_mk_config(); /* See quantifer_example1 */ Z3_set_param_value(cfg, "model", "true"); ctx = mk_context_custom(cfg, error_handler); Z3_del_config(cfg); /* declare function g */ int_sort = Z3_mk_int_sort(ctx); g_name = Z3_mk_string_symbol(ctx, "g"); g_domain[0] = int_sort; g_domain[1] = int_sort; g = Z3_mk_func_decl(ctx, g_name, 2, g_domain, int_sort); assert_comm_axiom(ctx, g); Z3_parse_smtlib_string(ctx, "(benchmark tst :formula (forall (x Int) (y Int) (implies (= x y) (= (g x 0) (g 0 y)))))", 0, 0, 0, 1, &g_name, &g); thm = Z3_get_smtlib_formula(ctx, 0); printf("formula: %s\n", Z3_ast_to_string(ctx, thm)); prove(ctx, thm, Z3_TRUE); Z3_del_context(ctx); } /** \brief Display the declarations, assumptions and formulas in a SMT-LIB string. */ void parser_example4() { Z3_context ctx; unsigned i, num_decls, num_assumptions, num_formulas; printf("\nparser_example4\n"); LOG_MSG("parser_example4"); ctx = mk_context(); Z3_parse_smtlib_string(ctx, "(benchmark tst :extrafuns ((x Int) (y Int)) :assumption (= x 20) :formula (> x y) :formula (> x 0))", 0, 0, 0, 0, 0, 0); num_decls = Z3_get_smtlib_num_decls(ctx); for (i = 0; i < num_decls; i++) { Z3_func_decl d = Z3_get_smtlib_decl(ctx, i); printf("declaration %d: %s\n", i, Z3_func_decl_to_string(ctx, d)); } num_assumptions = Z3_get_smtlib_num_assumptions(ctx); for (i = 0; i < num_assumptions; i++) { Z3_ast a = Z3_get_smtlib_assumption(ctx, i); printf("assumption %d: %s\n", i, Z3_ast_to_string(ctx, a)); } num_formulas = Z3_get_smtlib_num_formulas(ctx); for (i = 0; i < num_formulas; i++) { Z3_ast f = Z3_get_smtlib_formula(ctx, i); printf("formula %d: %s\n", i, Z3_ast_to_string(ctx, f)); } Z3_del_context(ctx); } /** \brief Demonstrates how to handle parser errors using Z3 error handling support. */ void parser_example5() { Z3_config cfg; Z3_context ctx = NULL; int r; printf("\nparser_example5\n"); LOG_MSG("parser_example5"); r = setjmp(g_catch_buffer); if (r == 0) { cfg = Z3_mk_config(); ctx = mk_context_custom(cfg, throw_z3_error); Z3_del_config(cfg); Z3_parse_smtlib_string(ctx, /* the following string has a parsing error: missing parenthesis */ "(benchmark tst :extrafuns ((x Int (y Int)) :formula (> x y) :formula (> x 0))", 0, 0, 0, 0, 0, 0); unreachable(); Z3_del_context(ctx); } else { printf("Z3 error: %s.\n", Z3_get_error_msg((Z3_error_code)r)); if (ctx != NULL) { printf("Error message: '%s'.\n",Z3_get_smtlib_error(ctx)); Z3_del_context(ctx); } } } /** \brief Demonstrate different ways of creating rational numbers: decimal and fractional representations. */ void numeral_example() { Z3_context ctx; Z3_ast n1, n2; Z3_sort real_ty; printf("\nnumeral_example\n"); LOG_MSG("numeral_example"); ctx = mk_context(); real_ty = Z3_mk_real_sort(ctx); n1 = Z3_mk_numeral(ctx, "1/2", real_ty); n2 = Z3_mk_numeral(ctx, "0.5", real_ty); printf("Numerals n1:%s", Z3_ast_to_string(ctx, n1)); printf(" n2:%s\n", Z3_ast_to_string(ctx, n2)); prove(ctx, Z3_mk_eq(ctx, n1, n2), Z3_TRUE); n1 = Z3_mk_numeral(ctx, "-1/3", real_ty); n2 = Z3_mk_numeral(ctx, "-0.33333333333333333333333333333333333333333333333333", real_ty); printf("Numerals n1:%s", Z3_ast_to_string(ctx, n1)); printf(" n2:%s\n", Z3_ast_to_string(ctx, n2)); prove(ctx, Z3_mk_not(ctx, Z3_mk_eq(ctx, n1, n2)), Z3_TRUE); Z3_del_context(ctx); } /** \brief Test ite-term (if-then-else terms). */ void ite_example() { Z3_context ctx; Z3_ast f, one, zero, ite; printf("\nite_example\n"); LOG_MSG("ite_example"); ctx = mk_context(); f = Z3_mk_false(ctx); one = mk_int(ctx, 1); zero = mk_int(ctx, 0); ite = Z3_mk_ite(ctx, f, one, zero); printf("term: %s\n", Z3_ast_to_string(ctx, ite)); /* delete logical context */ Z3_del_context(ctx); } /** \brief Create an enumeration data type. */ void enum_example() { Z3_context ctx = mk_context(); Z3_sort fruit; Z3_symbol name = Z3_mk_string_symbol(ctx, "fruit"); Z3_symbol enum_names[3]; Z3_func_decl enum_consts[3]; Z3_func_decl enum_testers[3]; Z3_ast apple, orange, banana, fruity; Z3_ast ors[3]; printf("\nenum_example\n"); LOG_MSG("enum_example"); enum_names[0] = Z3_mk_string_symbol(ctx,"apple"); enum_names[1] = Z3_mk_string_symbol(ctx,"banana"); enum_names[2] = Z3_mk_string_symbol(ctx,"orange"); fruit = Z3_mk_enumeration_sort(ctx, name, 3, enum_names, enum_consts, enum_testers); printf("%s\n", Z3_func_decl_to_string(ctx, enum_consts[0])); printf("%s\n", Z3_func_decl_to_string(ctx, enum_consts[1])); printf("%s\n", Z3_func_decl_to_string(ctx, enum_consts[2])); printf("%s\n", Z3_func_decl_to_string(ctx, enum_testers[0])); printf("%s\n", Z3_func_decl_to_string(ctx, enum_testers[1])); printf("%s\n", Z3_func_decl_to_string(ctx, enum_testers[2])); apple = Z3_mk_app(ctx, enum_consts[0], 0, 0); banana = Z3_mk_app(ctx, enum_consts[1], 0, 0); orange = Z3_mk_app(ctx, enum_consts[2], 0, 0); /* Apples are different from oranges */ prove(ctx, Z3_mk_not(ctx, Z3_mk_eq(ctx, apple, orange)), Z3_TRUE); /* Apples pass the apple test */ prove(ctx, Z3_mk_app(ctx, enum_testers[0], 1, &apple), Z3_TRUE); /* Oranges fail the apple test */ prove(ctx, Z3_mk_app(ctx, enum_testers[0], 1, &orange), Z3_FALSE); prove(ctx, Z3_mk_not(ctx, Z3_mk_app(ctx, enum_testers[0], 1, &orange)), Z3_TRUE); fruity = mk_var(ctx, "fruity", fruit); /* If something is fruity, then it is an apple, banana, or orange */ ors[0] = Z3_mk_eq(ctx, fruity, apple); ors[1] = Z3_mk_eq(ctx, fruity, banana); ors[2] = Z3_mk_eq(ctx, fruity, orange); prove(ctx, Z3_mk_or(ctx, 3, ors), Z3_TRUE); /* delete logical context */ Z3_del_context(ctx); } /** \brief Create a list datatype. */ void list_example() { Z3_context ctx = mk_context(); Z3_sort int_ty, int_list; Z3_func_decl nil_decl, is_nil_decl, cons_decl, is_cons_decl, head_decl, tail_decl; Z3_ast nil, l1, l2, x, y, u, v, fml, fml1; Z3_ast ors[2]; printf("\nlist_example\n"); LOG_MSG("list_example"); int_ty = Z3_mk_int_sort(ctx); int_list = Z3_mk_list_sort(ctx, Z3_mk_string_symbol(ctx, "int_list"), int_ty, &nil_decl, &is_nil_decl, &cons_decl, &is_cons_decl, &head_decl, &tail_decl); nil = Z3_mk_app(ctx, nil_decl, 0, 0); l1 = mk_binary_app(ctx, cons_decl, mk_int(ctx, 1), nil); l2 = mk_binary_app(ctx, cons_decl, mk_int(ctx, 2), nil); /* nil != cons(1, nil) */ prove(ctx, Z3_mk_not(ctx, Z3_mk_eq(ctx, nil, l1)), Z3_TRUE); /* cons(2,nil) != cons(1, nil) */ prove(ctx, Z3_mk_not(ctx, Z3_mk_eq(ctx, l1, l2)), Z3_TRUE); /* cons(x,nil) = cons(y, nil) => x = y */ x = mk_var(ctx, "x", int_ty); y = mk_var(ctx, "y", int_ty); l1 = mk_binary_app(ctx, cons_decl, x, nil); l2 = mk_binary_app(ctx, cons_decl, y, nil); prove(ctx, Z3_mk_implies(ctx, Z3_mk_eq(ctx,l1,l2), Z3_mk_eq(ctx, x, y)), Z3_TRUE); /* cons(x,u) = cons(x, v) => u = v */ u = mk_var(ctx, "u", int_list); v = mk_var(ctx, "v", int_list); l1 = mk_binary_app(ctx, cons_decl, x, u); l2 = mk_binary_app(ctx, cons_decl, y, v); prove(ctx, Z3_mk_implies(ctx, Z3_mk_eq(ctx,l1,l2), Z3_mk_eq(ctx, u, v)), Z3_TRUE); prove(ctx, Z3_mk_implies(ctx, Z3_mk_eq(ctx,l1,l2), Z3_mk_eq(ctx, x, y)), Z3_TRUE); /* is_nil(u) or is_cons(u) */ ors[0] = Z3_mk_app(ctx, is_nil_decl, 1, &u); ors[1] = Z3_mk_app(ctx, is_cons_decl, 1, &u); prove(ctx, Z3_mk_or(ctx, 2, ors), Z3_TRUE); /* occurs check u != cons(x,u) */ prove(ctx, Z3_mk_not(ctx, Z3_mk_eq(ctx, u, l1)), Z3_TRUE); /* destructors: is_cons(u) => u = cons(head(u),tail(u)) */ fml1 = Z3_mk_eq(ctx, u, mk_binary_app(ctx, cons_decl, mk_unary_app(ctx, head_decl, u), mk_unary_app(ctx, tail_decl, u))); fml = Z3_mk_implies(ctx, Z3_mk_app(ctx, is_cons_decl, 1, &u), fml1); printf("Formula %s\n", Z3_ast_to_string(ctx, fml)); prove(ctx, fml, Z3_TRUE); prove(ctx, fml1, Z3_FALSE); /* delete logical context */ Z3_del_context(ctx); } /** \brief Create a binary tree datatype. */ void tree_example() { Z3_context ctx = mk_context(); Z3_sort cell; Z3_func_decl nil_decl, is_nil_decl, cons_decl, is_cons_decl, car_decl, cdr_decl; Z3_ast nil, l1, l2, x, y, u, v, fml, fml1; Z3_symbol head_tail[2] = { Z3_mk_string_symbol(ctx, "car"), Z3_mk_string_symbol(ctx, "cdr") }; Z3_sort sorts[2] = { 0, 0 }; unsigned sort_refs[2] = { 0, 0 }; Z3_constructor nil_con, cons_con; Z3_constructor constructors[2]; Z3_func_decl cons_accessors[2]; Z3_ast ors[2]; printf("\ntree_example\n"); LOG_MSG("tree_example"); nil_con = Z3_mk_constructor(ctx, Z3_mk_string_symbol(ctx, "nil"), Z3_mk_string_symbol(ctx, "is_nil"), 0, 0, 0, 0); cons_con = Z3_mk_constructor(ctx, Z3_mk_string_symbol(ctx, "cons"), Z3_mk_string_symbol(ctx, "is_cons"), 2, head_tail, sorts, sort_refs); constructors[0] = nil_con; constructors[1] = cons_con; cell = Z3_mk_datatype(ctx, Z3_mk_string_symbol(ctx, "cell"), 2, constructors); Z3_query_constructor(ctx, nil_con, 0, &nil_decl, &is_nil_decl, 0); Z3_query_constructor(ctx, cons_con, 2, &cons_decl, &is_cons_decl, cons_accessors); car_decl = cons_accessors[0]; cdr_decl = cons_accessors[1]; Z3_del_constructor(ctx,nil_con); Z3_del_constructor(ctx,cons_con); nil = Z3_mk_app(ctx, nil_decl, 0, 0); l1 = mk_binary_app(ctx, cons_decl, nil, nil); l2 = mk_binary_app(ctx, cons_decl, l1, nil); /* nil != cons(nil, nil) */ prove(ctx, Z3_mk_not(ctx, Z3_mk_eq(ctx, nil, l1)), Z3_TRUE); /* cons(x,u) = cons(x, v) => u = v */ u = mk_var(ctx, "u", cell); v = mk_var(ctx, "v", cell); x = mk_var(ctx, "x", cell); y = mk_var(ctx, "y", cell); l1 = mk_binary_app(ctx, cons_decl, x, u); l2 = mk_binary_app(ctx, cons_decl, y, v); prove(ctx, Z3_mk_implies(ctx, Z3_mk_eq(ctx,l1,l2), Z3_mk_eq(ctx, u, v)), Z3_TRUE); prove(ctx, Z3_mk_implies(ctx, Z3_mk_eq(ctx,l1,l2), Z3_mk_eq(ctx, x, y)), Z3_TRUE); /* is_nil(u) or is_cons(u) */ ors[0] = Z3_mk_app(ctx, is_nil_decl, 1, &u); ors[1] = Z3_mk_app(ctx, is_cons_decl, 1, &u); prove(ctx, Z3_mk_or(ctx, 2, ors), Z3_TRUE); /* occurs check u != cons(x,u) */ prove(ctx, Z3_mk_not(ctx, Z3_mk_eq(ctx, u, l1)), Z3_TRUE); /* destructors: is_cons(u) => u = cons(car(u),cdr(u)) */ fml1 = Z3_mk_eq(ctx, u, mk_binary_app(ctx, cons_decl, mk_unary_app(ctx, car_decl, u), mk_unary_app(ctx, cdr_decl, u))); fml = Z3_mk_implies(ctx, Z3_mk_app(ctx, is_cons_decl, 1, &u), fml1); printf("Formula %s\n", Z3_ast_to_string(ctx, fml)); prove(ctx, fml, Z3_TRUE); prove(ctx, fml1, Z3_FALSE); /* delete logical context */ Z3_del_context(ctx); } /** \brief Create a forest of trees. forest ::= nil | cons(tree, forest) tree ::= nil | cons(forest, forest) */ void forest_example() { Z3_context ctx = mk_context(); Z3_sort tree, forest; Z3_func_decl nil1_decl, is_nil1_decl, cons1_decl, is_cons1_decl, car1_decl, cdr1_decl; Z3_func_decl nil2_decl, is_nil2_decl, cons2_decl, is_cons2_decl, car2_decl, cdr2_decl; Z3_ast nil1, nil2, t1, t2, t3, t4, f1, f2, f3, l1, l2, x, y, u, v; Z3_symbol head_tail[2] = { Z3_mk_string_symbol(ctx, "car"), Z3_mk_string_symbol(ctx, "cdr") }; Z3_sort sorts[2] = { 0, 0 }; unsigned sort_refs[2] = { 0, 0 }; Z3_constructor nil1_con, cons1_con, nil2_con, cons2_con; Z3_constructor constructors1[2], constructors2[2]; Z3_func_decl cons_accessors[2]; Z3_ast ors[2]; Z3_constructor_list clist1, clist2; Z3_constructor_list clists[2]; Z3_symbol sort_names[2] = { Z3_mk_string_symbol(ctx, "forest"), Z3_mk_string_symbol(ctx, "tree") }; printf("\nforest_example\n"); LOG_MSG("forest_example"); /* build a forest */ nil1_con = Z3_mk_constructor(ctx, Z3_mk_string_symbol(ctx, "nil1"), Z3_mk_string_symbol(ctx, "is_nil1"), 0, 0, 0, 0); sort_refs[0] = 1; /* the car of a forest is a tree */ sort_refs[1] = 0; cons1_con = Z3_mk_constructor(ctx, Z3_mk_string_symbol(ctx, "cons1"), Z3_mk_string_symbol(ctx, "is_cons1"), 2, head_tail, sorts, sort_refs); constructors1[0] = nil1_con; constructors1[1] = cons1_con; /* build a tree */ nil2_con = Z3_mk_constructor(ctx, Z3_mk_string_symbol(ctx, "nil2"), Z3_mk_string_symbol(ctx, "is_nil2"),0, 0, 0, 0); sort_refs[0] = 0; /* both branches of a tree are forests */ sort_refs[1] = 0; cons2_con = Z3_mk_constructor(ctx, Z3_mk_string_symbol(ctx, "cons2"), Z3_mk_string_symbol(ctx, "is_cons2"),2, head_tail, sorts, sort_refs); constructors2[0] = nil2_con; constructors2[1] = cons2_con; clist1 = Z3_mk_constructor_list(ctx, 2, constructors1); clist2 = Z3_mk_constructor_list(ctx, 2, constructors2); clists[0] = clist1; clists[1] = clist2; Z3_mk_datatypes(ctx, 2, sort_names, sorts, clists); forest = sorts[0]; tree = sorts[1]; Z3_query_constructor(ctx, nil1_con, 0, &nil1_decl, &is_nil1_decl, 0); Z3_query_constructor(ctx, cons1_con, 2, &cons1_decl, &is_cons1_decl, cons_accessors); car1_decl = cons_accessors[0]; cdr1_decl = cons_accessors[1]; Z3_query_constructor(ctx, nil2_con, 0, &nil2_decl, &is_nil2_decl, 0); Z3_query_constructor(ctx, cons2_con, 2, &cons2_decl, &is_cons2_decl, cons_accessors); car2_decl = cons_accessors[0]; cdr2_decl = cons_accessors[1]; Z3_del_constructor_list(ctx, clist1); Z3_del_constructor_list(ctx, clist2); Z3_del_constructor(ctx,nil1_con); Z3_del_constructor(ctx,cons1_con); Z3_del_constructor(ctx,nil2_con); Z3_del_constructor(ctx,cons2_con); nil1 = Z3_mk_app(ctx, nil1_decl, 0, 0); nil2 = Z3_mk_app(ctx, nil2_decl, 0, 0); f1 = mk_binary_app(ctx, cons1_decl, nil2, nil1); t1 = mk_binary_app(ctx, cons2_decl, nil1, nil1); t2 = mk_binary_app(ctx, cons2_decl, f1, nil1); t3 = mk_binary_app(ctx, cons2_decl, f1, f1); t4 = mk_binary_app(ctx, cons2_decl, nil1, f1); f2 = mk_binary_app(ctx, cons1_decl, t1, nil1); f3 = mk_binary_app(ctx, cons1_decl, t1, f1); /* nil != cons(nil,nil) */ prove(ctx, Z3_mk_not(ctx, Z3_mk_eq(ctx, nil1, f1)), Z3_TRUE); prove(ctx, Z3_mk_not(ctx, Z3_mk_eq(ctx, nil2, t1)), Z3_TRUE); /* cons(x,u) = cons(x, v) => u = v */ u = mk_var(ctx, "u", forest); v = mk_var(ctx, "v", forest); x = mk_var(ctx, "x", tree); y = mk_var(ctx, "y", tree); l1 = mk_binary_app(ctx, cons1_decl, x, u); l2 = mk_binary_app(ctx, cons1_decl, y, v); prove(ctx, Z3_mk_implies(ctx, Z3_mk_eq(ctx,l1,l2), Z3_mk_eq(ctx, u, v)), Z3_TRUE); prove(ctx, Z3_mk_implies(ctx, Z3_mk_eq(ctx,l1,l2), Z3_mk_eq(ctx, x, y)), Z3_TRUE); /* is_nil(u) or is_cons(u) */ ors[0] = Z3_mk_app(ctx, is_nil1_decl, 1, &u); ors[1] = Z3_mk_app(ctx, is_cons1_decl, 1, &u); prove(ctx, Z3_mk_or(ctx, 2, ors), Z3_TRUE); /* occurs check u != cons(x,u) */ prove(ctx, Z3_mk_not(ctx, Z3_mk_eq(ctx, u, l1)), Z3_TRUE); /* delete logical context */ Z3_del_context(ctx); } /** \brief Create a binary tree datatype of the form BinTree ::= nil | node(value : Int, left : BinTree, right : BinTree) */ void binary_tree_example() { Z3_context ctx = mk_context(); Z3_sort cell; Z3_func_decl nil_decl, /* nil : BinTree (constructor) */ is_nil_decl, /* is_nil : BinTree -> Bool (tester, return true if the given BinTree is a nil) */ node_decl, /* node : Int, BinTree, BinTree -> BinTree (constructor) */ is_node_decl, /* is_node : BinTree -> Bool (tester, return true if the given BinTree is a node) */ value_decl, /* value : BinTree -> Int (accessor for nodes) */ left_decl, /* left : BinTree -> BinTree (accessor for nodes, retrieves the left child of a node) */ right_decl; /* right : BinTree -> BinTree (accessor for nodes, retrieves the right child of a node) */ Z3_symbol node_accessor_names[3] = { Z3_mk_string_symbol(ctx, "value"), Z3_mk_string_symbol(ctx, "left"), Z3_mk_string_symbol(ctx, "right") }; Z3_sort node_accessor_sorts[3] = { Z3_mk_int_sort(ctx), 0, 0 }; unsigned node_accessor_sort_refs[3] = { 0, 0, 0 }; Z3_constructor nil_con, node_con; Z3_constructor constructors[2]; Z3_func_decl node_accessors[3]; printf("\nbinary_tree_example\n"); LOG_MSG("binary_tree_example"); /* nil_con and node_con are auxiliary datastructures used to create the new recursive datatype BinTree */ nil_con = Z3_mk_constructor(ctx, Z3_mk_string_symbol(ctx, "nil"), Z3_mk_string_symbol(ctx, "is-nil"), 0, 0, 0, 0); node_con = Z3_mk_constructor(ctx, Z3_mk_string_symbol(ctx, "node"), Z3_mk_string_symbol(ctx, "is-cons"), 3, node_accessor_names, node_accessor_sorts, node_accessor_sort_refs); constructors[0] = nil_con; constructors[1] = node_con; /* create the new recursive datatype */ cell = Z3_mk_datatype(ctx, Z3_mk_string_symbol(ctx, "BinTree"), 2, constructors); /* retrieve the new declarations: constructors (nil_decl, node_decl), testers (is_nil_decl, is_cons_del), and accessors (value_decl, left_decl, right_decl */ Z3_query_constructor(ctx, nil_con, 0, &nil_decl, &is_nil_decl, 0); Z3_query_constructor(ctx, node_con, 3, &node_decl, &is_node_decl, node_accessors); value_decl = node_accessors[0]; left_decl = node_accessors[1]; right_decl = node_accessors[2]; /* delete auxiliary/helper structures */ Z3_del_constructor(ctx, nil_con); Z3_del_constructor(ctx, node_con); /* small example using the recursive datatype BinTree */ { /* create nil */ Z3_ast nil = Z3_mk_app(ctx, nil_decl, 0, 0); /* create node1 ::= node(10, nil, nil) */ Z3_ast args1[3] = { mk_int(ctx, 10), nil, nil }; Z3_ast node1 = Z3_mk_app(ctx, node_decl, 3, args1); /* create node2 ::= node(30, node1, nil) */ Z3_ast args2[3] = { mk_int(ctx, 30), node1, nil }; Z3_ast node2 = Z3_mk_app(ctx, node_decl, 3, args2); /* create node3 ::= node(20, node2, node1); */ Z3_ast args3[3] = { mk_int(ctx, 20), node2, node1 }; Z3_ast node3 = Z3_mk_app(ctx, node_decl, 3, args3); /* prove that nil != node1 */ prove(ctx, Z3_mk_not(ctx, Z3_mk_eq(ctx, nil, node1)), Z3_TRUE); /* prove that nil = left(node1) */ prove(ctx, Z3_mk_eq(ctx, nil, mk_unary_app(ctx, left_decl, node1)), Z3_TRUE); /* prove that node1 = right(node3) */ prove(ctx, Z3_mk_eq(ctx, node1, mk_unary_app(ctx, right_decl, node3)), Z3_TRUE); /* prove that !is-nil(node2) */ prove(ctx, Z3_mk_not(ctx, mk_unary_app(ctx, is_nil_decl, node2)), Z3_TRUE); /* prove that value(node2) >= 0 */ prove(ctx, Z3_mk_ge(ctx, mk_unary_app(ctx, value_decl, node2), mk_int(ctx, 0)), Z3_TRUE); } /* delete logical context */ Z3_del_context(ctx); } /** \brief Prove a theorem and extract, and print the proof. This example illustrates the use of #Z3_check_assumptions. */ void unsat_core_and_proof_example() { Z3_context ctx = mk_proof_context(); Z3_ast pa = mk_bool_var(ctx, "PredA"); Z3_ast pb = mk_bool_var(ctx, "PredB"); Z3_ast pc = mk_bool_var(ctx, "PredC"); Z3_ast pd = mk_bool_var(ctx, "PredD"); Z3_ast p1 = mk_bool_var(ctx, "P1"); Z3_ast p2 = mk_bool_var(ctx, "P2"); Z3_ast p3 = mk_bool_var(ctx, "P3"); Z3_ast p4 = mk_bool_var(ctx, "P4"); Z3_ast assumptions[4] = { Z3_mk_not(ctx, p1), Z3_mk_not(ctx, p2), Z3_mk_not(ctx, p3), Z3_mk_not(ctx, p4) }; Z3_ast args1[3] = { pa, pb, pc }; Z3_ast f1 = Z3_mk_and(ctx, 3, args1); Z3_ast args2[3] = { pa, Z3_mk_not(ctx, pb), pc }; Z3_ast f2 = Z3_mk_and(ctx, 3, args2); Z3_ast args3[2] = { Z3_mk_not(ctx, pa), Z3_mk_not(ctx, pc) }; Z3_ast f3 = Z3_mk_or(ctx, 2, args3); Z3_ast f4 = pd; Z3_ast g1[2] = { f1, p1 }; Z3_ast g2[2] = { f2, p2 }; Z3_ast g3[2] = { f3, p3 }; Z3_ast g4[2] = { f4, p4 }; Z3_ast core[4] = { 0, 0, 0, 0 }; unsigned core_size = 4; Z3_ast proof; Z3_model m = 0; Z3_lbool result; unsigned i; printf("\nunsat_core_and_proof_example\n"); LOG_MSG("unsat_core_and_proof_example"); Z3_assert_cnstr(ctx, Z3_mk_or(ctx, 2, g1)); Z3_assert_cnstr(ctx, Z3_mk_or(ctx, 2, g2)); Z3_assert_cnstr(ctx, Z3_mk_or(ctx, 2, g3)); Z3_assert_cnstr(ctx, Z3_mk_or(ctx, 2, g4)); result = Z3_check_assumptions(ctx, 4, assumptions, &m, &proof, &core_size, core); switch (result) { case Z3_L_FALSE: printf("unsat\n"); printf("proof: %s\n", Z3_ast_to_string(ctx, proof)); printf("\ncore:\n"); for (i = 0; i < core_size; ++i) { printf("%s\n", Z3_ast_to_string(ctx, core[i])); } printf("\n"); break; case Z3_L_UNDEF: printf("unknown\n"); printf("potential model:\n"); display_model(ctx, stdout, m); break; case Z3_L_TRUE: printf("sat\n"); display_model(ctx, stdout, m); break; } if (m) { Z3_del_model(ctx, m); } /* delete logical context */ Z3_del_context(ctx); } #define MAX_RETRACTABLE_ASSERTIONS 1024 /** \brief Very simple logical context wrapper with support for retractable constraints. A retractable constraint can be "removed" without using push/pop. */ typedef struct { Z3_context m_context; // IMPORTANT: the fields m_answer_literals, m_retracted and m_num_answer_literals must be saved/restored // if push/pop operations are performed on m_context. Z3_ast m_answer_literals[MAX_RETRACTABLE_ASSERTIONS]; Z3_bool m_retracted[MAX_RETRACTABLE_ASSERTIONS]; // true if the assertion was retracted. unsigned m_num_answer_literals; } Z3_ext_context_struct; typedef Z3_ext_context_struct * Z3_ext_context; /** \brief Create a logical context wrapper with support for retractable constraints. */ Z3_ext_context mk_ext_context() { Z3_ext_context ctx = (Z3_ext_context) malloc(sizeof(Z3_ext_context_struct)); ctx->m_context = mk_context(); ctx->m_num_answer_literals = 0; return ctx; } /** \brief Delete the given logical context wrapper. */ void del_ext_context(Z3_ext_context ctx) { Z3_del_context(ctx->m_context); free(ctx); } /** \brief Create a retractable constraint. \return An id that can be used to retract/reassert the constraint. */ unsigned assert_retractable_cnstr(Z3_ext_context ctx, Z3_ast c) { unsigned result; Z3_sort ty; Z3_ast ans_lit; Z3_ast args[2]; if (ctx->m_num_answer_literals == MAX_RETRACTABLE_ASSERTIONS) { exitf("maximum number of retractable constraints was exceeded."); } ty = Z3_mk_bool_sort(ctx->m_context); ans_lit = Z3_mk_fresh_const(ctx->m_context, "k", ty); result = ctx->m_num_answer_literals; ctx->m_answer_literals[result] = ans_lit; ctx->m_retracted[result] = Z3_FALSE; ctx->m_num_answer_literals++; // assert: c OR (not ans_lit) args[0] = c; args[1] = Z3_mk_not(ctx->m_context, ans_lit); Z3_assert_cnstr(ctx->m_context, Z3_mk_or(ctx->m_context, 2, args)); return result; } /** \brief Retract an constraint asserted using #assert_retractable_cnstr. */ void retract_cnstr(Z3_ext_context ctx, unsigned id) { if (id >= ctx->m_num_answer_literals) { exitf("invalid constraint id."); } ctx->m_retracted[id] = Z3_TRUE; } /** \brief Reassert a constraint retracted using #retract_cnstr. */ void reassert_cnstr(Z3_ext_context ctx, unsigned id) { if (id >= ctx->m_num_answer_literals) { exitf("invalid constraint id."); } ctx->m_retracted[id] = Z3_FALSE; } /** \brief Check whether the logical context wrapper with support for retractable assertions is feasible or not. */ Z3_lbool ext_check(Z3_ext_context ctx) { Z3_lbool result; unsigned num_assumptions = 0; Z3_ast assumptions[MAX_RETRACTABLE_ASSERTIONS]; Z3_ast core[MAX_RETRACTABLE_ASSERTIONS]; unsigned core_size; Z3_ast dummy_proof = 0; // we don't care about the proof in this example. Z3_model dummy_model = 0; // we don't care about the model in this example. unsigned i; for (i = 0; i < ctx->m_num_answer_literals; i++) { if (ctx->m_retracted[i] == Z3_FALSE) { // Since the answer literal was not retracted, we added it as an assumption. // Recall that we assert (C \/ (not ans_lit)). Therefore, adding ans_lit as an assumption has the effect of "asserting" C. // If the constraint was "retracted" (ctx->m_retracted[i] == Z3_true), then we don't really need to add (not ans_lit) as an assumption. assumptions[num_assumptions] = ctx->m_answer_literals[i]; num_assumptions ++; } } result = Z3_check_assumptions(ctx->m_context, num_assumptions, assumptions, &dummy_model, &dummy_proof, &core_size, core); if (result == Z3_L_FALSE) { // Display the UNSAT core printf("unsat core: "); for (i = 0; i < core_size; i++) { // In this example, we display the core based on the assertion ids. unsigned j; for (j = 0; j < ctx->m_num_answer_literals; j++) { if (core[i] == ctx->m_answer_literals[j]) { printf("%d ", j); break; } } if (j == ctx->m_num_answer_literals) { exitf("bug in Z3, the core contains something that is not an assumption."); } } printf("\n"); } if (dummy_model) { Z3_del_model(ctx->m_context, dummy_model); } return result; } /** \brief Simple example using the functions: #mk_ext_context, #assert_retractable_cnstr, #retract_cnstr, #reassert_cnstr and #del_ext_context. */ void incremental_example1() { Z3_ext_context ext_ctx = mk_ext_context(); Z3_context ctx = ext_ctx->m_context; Z3_ast x, y, z, two, one; unsigned c1, c2, c3, c4; Z3_bool result; printf("\nincremental_example1\n"); LOG_MSG("incremental_example1"); x = mk_int_var(ctx, "x"); y = mk_int_var(ctx, "y"); z = mk_int_var(ctx, "z"); two = mk_int(ctx, 2); one = mk_int(ctx, 1); /* assert x < y */ c1 = assert_retractable_cnstr(ext_ctx, Z3_mk_lt(ctx, x, y)); /* assert x = z */ c2 = assert_retractable_cnstr(ext_ctx, Z3_mk_eq(ctx, x, z)); /* assert x > 2 */ c3 = assert_retractable_cnstr(ext_ctx, Z3_mk_gt(ctx, x, two)); /* assert y < 1 */ c4 = assert_retractable_cnstr(ext_ctx, Z3_mk_lt(ctx, y, one)); result = ext_check(ext_ctx); if (result != Z3_L_FALSE) exitf("bug in Z3"); printf("unsat\n"); retract_cnstr(ext_ctx, c4); result = ext_check(ext_ctx); if (result != Z3_L_TRUE) exitf("bug in Z3"); printf("sat\n"); reassert_cnstr(ext_ctx, c4); result = ext_check(ext_ctx); if (result != Z3_L_FALSE) exitf("bug in Z3"); printf("unsat\n"); retract_cnstr(ext_ctx, c2); result = ext_check(ext_ctx); if (result != Z3_L_FALSE) exitf("bug in Z3"); printf("unsat\n"); retract_cnstr(ext_ctx, c3); result = ext_check(ext_ctx); if (result != Z3_L_TRUE) exitf("bug in Z3"); printf("sat\n"); del_ext_context(ext_ctx); } /** \brief Simple example showing how to use reference counters in Z3 to manage memory efficiently. */ void reference_counter_example() { Z3_config cfg; Z3_context ctx; Z3_sort ty; Z3_ast x, y, x_xor_y; Z3_symbol sx, sy; printf("\nreference_counter_example\n"); LOG_MSG("reference_counter_example"); cfg = Z3_mk_config(); Z3_set_param_value(cfg, "model", "true"); // Create a Z3 context where the user is reponsible for managing // Z3_ast reference counters. ctx = Z3_mk_context_rc(cfg); Z3_del_config(cfg); ty = Z3_mk_bool_sort(ctx); Z3_inc_ref(ctx, Z3_sort_to_ast(ctx, ty)); // Z3_sort_to_ast(ty) is just syntax sugar for ((Z3_ast) ty) sx = Z3_mk_string_symbol(ctx, "x"); // Z3_symbol is not a Z3_ast. No reference counting is not needed. x = Z3_mk_const(ctx, sx, ty); Z3_inc_ref(ctx, x); sy = Z3_mk_string_symbol(ctx, "y"); y = Z3_mk_const(ctx, sy, ty); Z3_inc_ref(ctx, y); // ty is not needed anymore. Z3_dec_ref(ctx, Z3_sort_to_ast(ctx, ty)); x_xor_y = Z3_mk_xor(ctx, x, y); Z3_inc_ref(ctx, x_xor_y); // x and y are not needed anymore. Z3_dec_ref(ctx, x); Z3_dec_ref(ctx, y); Z3_assert_cnstr(ctx, x_xor_y); // x_or_y is not needed anymore. Z3_dec_ref(ctx, x_xor_y); printf("model for: x xor y\n"); check(ctx, Z3_L_TRUE); // Test push & pop Z3_push(ctx); Z3_pop(ctx, 1); Z3_del_context(ctx); } /** \brief Demonstrates how to use SMT2 parser. */ void smt2parser_example() { Z3_context ctx; Z3_ast fs; printf("\nsmt2parser_example\n"); LOG_MSG("smt2parser_example"); ctx = mk_context(); fs = Z3_parse_smtlib2_string(ctx, "(declare-fun a () (_ BitVec 8)) (assert (bvuge a #x10)) (assert (bvule a #xf0))", 0, 0, 0, 0, 0, 0); printf("formulas: %s\n", Z3_ast_to_string(ctx, fs)); Z3_del_context(ctx); } /** \brief Demonstrates how to use the function \c Z3_substitute to replace subexpressions in a Z3 AST. */ void substitute_example() { Z3_context ctx; Z3_sort int_ty; Z3_ast a, b; Z3_func_decl f; Z3_func_decl g; Z3_ast fab, ga, ffabga, r; printf("\nsubstitute_example\n"); LOG_MSG("substitute_example"); ctx = mk_context(); int_ty = Z3_mk_int_sort(ctx); a = mk_int_var(ctx,"a"); b = mk_int_var(ctx,"b"); { Z3_sort f_domain[2] = { int_ty, int_ty }; f = Z3_mk_func_decl(ctx, Z3_mk_string_symbol(ctx, "f"), 2, f_domain, int_ty); } g = Z3_mk_func_decl(ctx, Z3_mk_string_symbol(ctx, "g"), 1, &int_ty, int_ty); { Z3_ast args[2] = { a, b }; fab = Z3_mk_app(ctx, f, 2, args); } ga = Z3_mk_app(ctx, g, 1, &a); { Z3_ast args[2] = { fab, ga }; ffabga = Z3_mk_app(ctx, f, 2, args); } // Replace b -> 0, g(a) -> 1 in f(f(a, b), g(a)) { Z3_ast zero = Z3_mk_numeral(ctx, "0", int_ty); Z3_ast one = Z3_mk_numeral(ctx, "1", int_ty); Z3_ast from[2] = { b, ga }; Z3_ast to[2] = { zero, one }; r = Z3_substitute(ctx, ffabga, 2, from, to); } // Display r printf("substitution result: %s\n", Z3_ast_to_string(ctx, r)); Z3_del_context(ctx); } /** \brief Demonstrates how to use the function \c Z3_substitute_vars to replace (free) variables with expressions in a Z3 AST. */ void substitute_vars_example() { Z3_context ctx; Z3_sort int_ty; Z3_ast x0, x1; Z3_ast a, b, gb; Z3_func_decl f; Z3_func_decl g; Z3_ast f01, ff010, r; printf("\nsubstitute_vars_example\n"); LOG_MSG("substitute_vars_example"); ctx = mk_context(); int_ty = Z3_mk_int_sort(ctx); x0 = Z3_mk_bound(ctx, 0, int_ty); x1 = Z3_mk_bound(ctx, 1, int_ty); { Z3_sort f_domain[2] = { int_ty, int_ty }; f = Z3_mk_func_decl(ctx, Z3_mk_string_symbol(ctx, "f"), 2, f_domain, int_ty); } g = Z3_mk_func_decl(ctx, Z3_mk_string_symbol(ctx, "g"), 1, &int_ty, int_ty); { Z3_ast args[2] = { x0, x1 }; f01 = Z3_mk_app(ctx, f, 2, args); } { Z3_ast args[2] = { f01, x0 }; ff010 = Z3_mk_app(ctx, f, 2, args); } a = mk_int_var(ctx, "a"); b = mk_int_var(ctx, "b"); gb = Z3_mk_app(ctx, g, 1, &b); // Replace x0 -> a, x1 -> g(b) in f(f(x0,x1),x0) { Z3_ast to[2] = { a, gb }; r = Z3_substitute_vars(ctx, ff010, 2, to); } // Display r printf("substitution result: %s\n", Z3_ast_to_string(ctx, r)); Z3_del_context(ctx); } /** \brief Demonstrates some basic features of the FloatingPoint theory. */ void fpa_example() { Z3_config cfg; Z3_context ctx; Z3_sort double_sort, rm_sort; Z3_symbol s_rm, s_x, s_y, s_x_plus_y; Z3_ast rm, x, y, n, x_plus_y, c1, c2, c3, c4, c5, c6; Z3_ast args[2], args2[2], and_args[3], args3[3]; printf("\nFPA-example\n"); LOG_MSG("FPA-example"); cfg = Z3_mk_config(); ctx = Z3_mk_context(cfg); Z3_del_config(cfg); double_sort = Z3_mk_fpa_sort(ctx, 11, 53); rm_sort = Z3_mk_fpa_rounding_mode_sort(ctx); // Show that there are x, y s.t. (x + y) = 42.0 (with rounding mode). s_rm = Z3_mk_string_symbol(ctx, "rm"); rm = Z3_mk_const(ctx, s_rm, rm_sort); s_x = Z3_mk_string_symbol(ctx, "x"); s_y = Z3_mk_string_symbol(ctx, "y"); x = Z3_mk_const(ctx, s_x, double_sort); y = Z3_mk_const(ctx, s_y, double_sort); n = Z3_mk_fpa_numeral_double(ctx, 42.0, double_sort); s_x_plus_y = Z3_mk_string_symbol(ctx, "x_plus_y"); x_plus_y = Z3_mk_const(ctx, s_x_plus_y, double_sort); c1 = Z3_mk_eq(ctx, x_plus_y, Z3_mk_fpa_add(ctx, rm, x, y)); args[0] = c1; args[1] = Z3_mk_eq(ctx, x_plus_y, n); c2 = Z3_mk_and(ctx, 2, (Z3_ast*)&args); args2[0] = c2; args2[1] = Z3_mk_not(ctx, Z3_mk_eq(ctx, rm, Z3_mk_fpa_rtz(ctx))); c3 = Z3_mk_and(ctx, 2, (Z3_ast*)&args2); and_args[0] = Z3_mk_not(ctx, Z3_mk_fpa_is_zero(ctx, y)); and_args[1] = Z3_mk_not(ctx, Z3_mk_fpa_is_nan(ctx, y)); and_args[2] = Z3_mk_not(ctx, Z3_mk_fpa_is_infinite(ctx, y)); args3[0] = c3; args3[1] = Z3_mk_and(ctx, 3, and_args); c4 = Z3_mk_and(ctx, 2, (Z3_ast*)&args3); printf("c4: %s\n", Z3_ast_to_string(ctx, c4)); Z3_push(ctx); Z3_assert_cnstr(ctx, c4); check(ctx, Z3_L_TRUE); Z3_pop(ctx, 1); // Show that the following are equal: // (fp #b0 #b10000000001 #xc000000000000) // ((_ to_fp 11 53) #x401c000000000000)) // ((_ to_fp 11 53) RTZ 1.75 2))) // ((_ to_fp 11 53) RTZ 7.0))) Z3_push(ctx); c1 = Z3_mk_fpa_fp(ctx, Z3_mk_numeral(ctx, "0", Z3_mk_bv_sort(ctx, 1)), Z3_mk_numeral(ctx, "3377699720527872", Z3_mk_bv_sort(ctx, 52)), Z3_mk_numeral(ctx, "1025", Z3_mk_bv_sort(ctx, 11))); c2 = Z3_mk_fpa_to_fp_bv(ctx, Z3_mk_numeral(ctx, "4619567317775286272", Z3_mk_bv_sort(ctx, 64)), Z3_mk_fpa_sort(ctx, 11, 53)); c3 = Z3_mk_fpa_to_fp_int_real(ctx, Z3_mk_fpa_rtz(ctx), Z3_mk_numeral(ctx, "2", Z3_mk_int_sort(ctx)), Z3_mk_numeral(ctx, "1.75", Z3_mk_real_sort(ctx)), Z3_mk_fpa_sort(ctx, 11, 53)); c4 = Z3_mk_fpa_to_fp_real(ctx, Z3_mk_fpa_rtz(ctx), Z3_mk_numeral(ctx, "7.0", Z3_mk_real_sort(ctx)), Z3_mk_fpa_sort(ctx, 11, 53)); args3[0] = Z3_mk_eq(ctx, c1, c2); args3[1] = Z3_mk_eq(ctx, c1, c3); args3[2] = Z3_mk_eq(ctx, c1, c4); c5 = Z3_mk_and(ctx, 3, args3); printf("c5: %s\n", Z3_ast_to_string(ctx, c5)); Z3_assert_cnstr(ctx, c5); check(ctx, Z3_L_TRUE); Z3_pop(ctx, 1); Z3_del_context(ctx); } /*@}*/ /*@}*/ int main() { #ifdef LOG_Z3_CALLS Z3_open_log("z3.log"); #endif display_version(); simple_example(); demorgan(); find_model_example1(); find_model_example2(); prove_example1(); prove_example2(); push_pop_example1(); quantifier_example1(); array_example1(); array_example2(); array_example3(); tuple_example1(); bitvector_example1(); bitvector_example2(); eval_example1(); two_contexts_example1(); error_code_example1(); error_code_example2(); parser_example1(); parser_example2(); parser_example3(); parser_example4(); parser_example5(); numeral_example(); ite_example(); list_example(); tree_example(); forest_example(); binary_tree_example(); enum_example(); unsat_core_and_proof_example(); incremental_example1(); reference_counter_example(); smt2parser_example(); substitute_example(); substitute_vars_example(); fpa_example(); return 0; } z3-4.4.0/examples/msf/0000755000175000017500000000000012540347414014250 5ustar michaelmichaelz3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3.Tests/0000755000175000017500000000000012540347414022402 5ustar michaelmichaelz3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3.Tests/App.config0000644000175000017500000000701312540347414024312 0ustar michaelmichael
z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverFoundation.Plugin.Z3.Tests.csproj0000644000175000017500000000620412540347414031760 0ustar michaelmichael Debug AnyCPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B} Library Properties Microsoft.SolverFoundation.Plugin.Z3.Tests SolverFoundation.Plugin.Z3.Tests v4.0 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 x86 pdbonly true bin\Release\ TRACE prompt 4 x86 ..\Microsoft.Solver.Foundation.dll {7340e664-f648-4ff7-89b2-f4da424996d3} SolverFoundation.Plugin.Z3 z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverTests.cs0000644000175000017500000001026612540347414025233 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.SolverFoundation.Common; using Microsoft.SolverFoundation.Solvers; using Microsoft.SolverFoundation.Services; using Microsoft.SolverFoundation.Plugin.Z3; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.SolverFoundation.Plugin.Z3.Tests { [TestClass] public class SolverTests { [TestMethod] public void TestMILPSolver1() { var solver = new Z3MILPSolver(); int goal; solver.AddRow("goal", out goal); int x1, x2, z; // 0 <= x1 <= 2 solver.AddVariable("x1", out x1); solver.SetBounds(x1, 0, 2); // 0 <= x2 <= 2 solver.AddVariable("x2", out x2); solver.SetBounds(x2, 0, 2); // z is an integer in [0,1] solver.AddVariable("z", out z); solver.SetIntegrality(z, true); solver.SetBounds(z, 0, 1); //max x1 + x2 solver.SetCoefficient(goal, x1, 1); solver.SetCoefficient(goal, x2, 1); solver.AddGoal(goal, 1, false); // 0 <= x1 -z <= 1 int row1; solver.AddRow("rowI1", out row1); solver.SetBounds(row1, 0, 1); solver.SetCoefficient(row1, x1, 1); solver.SetCoefficient(row1, z, -1); // 0 <= x2 + z <= 2 int row2; solver.AddRow("rowI2", out row2); solver.SetBounds(row2, 0, 2); solver.SetCoefficient(row2, x2, 1); solver.SetCoefficient(row2, z, 1); var p = new Z3MILPParams(); solver.Solve(p); Assert.IsTrue(solver.Result == LinearResult.Optimal); Assert.AreEqual(solver.GetValue(x1), 2 * Rational.One); Assert.AreEqual(solver.GetValue(x2), Rational.One); Assert.AreEqual(solver.GetValue(z), Rational.One); Assert.AreEqual(solver.GetValue(goal), 3 * Rational.One); } [TestMethod] public void TestMILPSolver2() { var solver = new Z3MILPSolver(); int goal, extraGoal; Rational M = 100; solver.AddRow("goal", out goal); int x1, x2, z; // 0 <= x1 <= 100 solver.AddVariable("x1", out x1); solver.SetBounds(x1, 0, M); // 0 <= x2 <= 100 solver.AddVariable("x2", out x2); solver.SetBounds(x2, 0, M); // z is an integer in [0,1] solver.AddVariable("z", out z); solver.SetIntegrality(z, true); solver.SetBounds(z, 0, 1); solver.SetCoefficient(goal, x1, 1); solver.SetCoefficient(goal, x2, 2); solver.AddGoal(goal, 1, false); solver.AddRow("extraGoal", out extraGoal); solver.SetCoefficient(extraGoal, x1, 2); solver.SetCoefficient(extraGoal, x2, 1); solver.AddGoal(extraGoal, 2, false); // x1 + x2 >= 1 int row; solver.AddRow("row", out row); solver.SetBounds(row, 1, Rational.PositiveInfinity); solver.SetCoefficient(row, x1, 1); solver.SetCoefficient(row, x2, 1); // x1 - M*z <= 0 int row1; solver.AddRow("rowI1", out row1); solver.SetBounds(row1, Rational.NegativeInfinity, 0); solver.SetCoefficient(row1, x1, 1); solver.SetCoefficient(row1, z, -M); // x2 - M* (1-z) <= 0 int row2; solver.AddRow("rowI2", out row2); solver.SetBounds(row2, Rational.NegativeInfinity, M); solver.SetCoefficient(row2, x2, 1); solver.SetCoefficient(row2, z, M); var p = new Z3MILPParams(); p.OptKind = OptimizationKind.BoundingBox; solver.Solve(p); Assert.IsTrue(solver.Result == LinearResult.Optimal); Assert.AreEqual(solver.GetValue(goal), 200 * Rational.One); Assert.AreEqual(solver.GetValue(extraGoal), 200 * Rational.One); } } } z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3.Tests/Properties/0000755000175000017500000000000012540347414024536 5ustar michaelmichaelz3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3.Tests/Properties/AssemblyInfo.cs0000644000175000017500000000264012540347414027462 0ustar michaelmichaelusing System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("SolverFoundation.Plugin.Z3.Tests")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("SolverFoundation.Plugin.Z3.Tests")] [assembly: AssemblyCopyright("Copyright © 2013")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("27657eee-ca7b-4996-a905-86a3f4584988")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3.Tests/ServiceTests.cs0000644000175000017500000000515012540347414025355 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.SolverFoundation.Common; using Microsoft.SolverFoundation.Solvers; using Microsoft.SolverFoundation.Services; using Microsoft.SolverFoundation.Plugin.Z3; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.SolverFoundation.Plugin.Z3.Tests { [TestClass] public class ServiceTests { [TestInitialize] public void SetUp() { SolverContext context = SolverContext.GetContext(); context.ClearModel(); } private void TestService1(Directive directive) { SolverContext context = SolverContext.GetContext(); Model model = context.CreateModel(); Decision x1 = new Decision(Domain.RealRange(0, 2), "x1"); Decision x2 = new Decision(Domain.RealRange(0, 2), "x2"); Decision z = new Decision(Domain.IntegerRange(0, 1), "z"); model.AddDecisions(x1, x2, z); model.AddConstraint("Row0", x1 - z <= 1); model.AddConstraint("Row1", x2 + z <= 2); Goal goal = model.AddGoal("Goal0", GoalKind.Maximize, x1 + x2); Solution solution = context.Solve(directive); Assert.IsTrue(goal.ToInt32() == 3); context.ClearModel(); } private void TestService2(Directive directive) { SolverContext context = SolverContext.GetContext(); Model model = context.CreateModel(); Decision x1 = new Decision(Domain.RealNonnegative, "x1"); Decision x2 = new Decision(Domain.RealNonnegative, "x2"); Decision z = new Decision(Domain.IntegerRange(0, 1), "z"); Rational M = 100; model.AddDecisions(x1, x2, z); model.AddConstraint("Row0", x1 + x2 >= 1); model.AddConstraint("Row1", x1 - z * 100 <= 0); model.AddConstraint("Row2", x2 + z * 100 <= 100); Goal goal = model.AddGoal("Goal0", GoalKind.Maximize, x1 + x2); Solution solution = context.Solve(directive); Assert.IsTrue(goal.ToInt32() == 100); context.ClearModel(); } [TestMethod] public void TestService1() { TestService1(new Z3MILPDirective()); TestService1(new Z3TermDirective()); } [TestMethod] public void TestService2() { TestService2(new Z3MILPDirective()); TestService2(new Z3TermDirective()); } } } z3-4.4.0/examples/msf/README0000644000175000017500000000202512540347414015127 0ustar michaelmichaelIn order to use Z3 MSF plugin, follow the following steps: 1. Compile latest Z3 .NET API (from any branch consisting of opt features) and copy 'libz3.dll' and 'Microsoft.Z3.dll' to the folder of 'Z3MSFPlugin.sln'. 2. Retrieve 'Microsoft.Solver.Foundation.dll' from http://archive.msdn.microsoft.com/solverfoundation/Release/ProjectReleases.aspx?ReleaseId=1799, preferably using DLL only version. Copy 'Microsoft.Solver.Foundation.dll' to the folder of 'Z3MSFPlugin.sln' 3. Build 'Z3MSFPlugin.sln'. Note that you have to compile using x86 target for Microsoft.Z3.dll 32-bit and x64 target for Microsoft.Z3.dll 64-bit. The solution consists of a plugin project, a test project with a few simple test cases and a command line projects for external OML, MPS and SMPS models. To retrieve SMT2 models which are native to Z3, set the logging file in corresponding directives or solver params e.g. var p = new Z3MILPParams(); p.SMT2LogFile = "model.smt2"; For more details, check out the commands in 'Validator\Program.cs'. Enjoy! z3-4.4.0/examples/msf/Z3MSFPlugin.sln0000644000175000017500000002152612540347414017015 0ustar michaelmichael Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2012 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SolverFoundation.Plugin.Z3", "SolverFoundation.Plugin.Z3\SolverFoundation.Plugin.Z3.csproj", "{7340E664-F648-4FF7-89B2-F4DA424996D3}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SolverFoundation.Plugin.Z3.Tests", "SolverFoundation.Plugin.Z3.Tests\SolverFoundation.Plugin.Z3.Tests.csproj", "{280AEE2F-1FDB-4A27-BE37-14DC154C873B}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Validator", "Validator\Validator.csproj", "{54835857-129F-44C9-B529-A42158647B36}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F1E99540-BA5E-46DF-9E29-6146A309CD18}" ProjectSection(SolutionItems) = preProject README = README EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution commercial_64|Any CPU = commercial_64|Any CPU commercial_64|Mixed Platforms = commercial_64|Mixed Platforms commercial_64|x64 = commercial_64|x64 commercial_64|x86 = commercial_64|x86 commercial|Any CPU = commercial|Any CPU commercial|Mixed Platforms = commercial|Mixed Platforms commercial|x64 = commercial|x64 commercial|x86 = commercial|x86 Debug|Any CPU = Debug|Any CPU Debug|Mixed Platforms = Debug|Mixed Platforms Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|Mixed Platforms = Release|Mixed Platforms Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|Any CPU.ActiveCfg = commercial_64|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|Any CPU.Build.0 = commercial_64|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|Mixed Platforms.ActiveCfg = commercial_64|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|Mixed Platforms.Build.0 = commercial_64|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|x64.ActiveCfg = commercial_64|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|x86.ActiveCfg = commercial_64|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|x86.Build.0 = commercial_64|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|Any CPU.ActiveCfg = commercial|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|Any CPU.Build.0 = commercial|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|Mixed Platforms.ActiveCfg = commercial|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|Mixed Platforms.Build.0 = commercial|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|x64.ActiveCfg = commercial|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|x86.ActiveCfg = commercial|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|x86.Build.0 = commercial|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|Any CPU.Build.0 = Debug|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|Mixed Platforms.Build.0 = Debug|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|x64.ActiveCfg = Debug|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|x86.ActiveCfg = Debug|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|x86.Build.0 = Debug|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|Any CPU.ActiveCfg = Release|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|Any CPU.Build.0 = Release|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|Mixed Platforms.ActiveCfg = Release|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|Mixed Platforms.Build.0 = Release|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|x64.ActiveCfg = Release|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|x86.ActiveCfg = Release|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|x86.Build.0 = Release|x86 {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|Any CPU.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|Any CPU.Build.0 = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|Mixed Platforms.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|Mixed Platforms.Build.0 = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|x64.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|x86.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|Any CPU.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|Any CPU.Build.0 = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|Mixed Platforms.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|Mixed Platforms.Build.0 = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|x64.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|x86.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|Any CPU.Build.0 = Debug|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|x64.ActiveCfg = Debug|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|x86.ActiveCfg = Debug|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|x86.Build.0 = Debug|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|Any CPU.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|Any CPU.Build.0 = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|Mixed Platforms.Build.0 = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|x64.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|x86.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|x86.Build.0 = Release|Any CPU {54835857-129F-44C9-B529-A42158647B36}.commercial_64|Any CPU.ActiveCfg = Release|Any CPU {54835857-129F-44C9-B529-A42158647B36}.commercial_64|Any CPU.Build.0 = Release|Any CPU {54835857-129F-44C9-B529-A42158647B36}.commercial_64|Mixed Platforms.ActiveCfg = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.commercial_64|Mixed Platforms.Build.0 = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.commercial_64|x64.ActiveCfg = Release|x64 {54835857-129F-44C9-B529-A42158647B36}.commercial_64|x64.Build.0 = Release|x64 {54835857-129F-44C9-B529-A42158647B36}.commercial_64|x86.ActiveCfg = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.commercial_64|x86.Build.0 = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.commercial|Any CPU.ActiveCfg = Release|Any CPU {54835857-129F-44C9-B529-A42158647B36}.commercial|Any CPU.Build.0 = Release|Any CPU {54835857-129F-44C9-B529-A42158647B36}.commercial|Mixed Platforms.ActiveCfg = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.commercial|Mixed Platforms.Build.0 = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.commercial|x64.ActiveCfg = Release|x64 {54835857-129F-44C9-B529-A42158647B36}.commercial|x64.Build.0 = Release|x64 {54835857-129F-44C9-B529-A42158647B36}.commercial|x86.ActiveCfg = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.commercial|x86.Build.0 = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {54835857-129F-44C9-B529-A42158647B36}.Debug|Any CPU.Build.0 = Debug|Any CPU {54835857-129F-44C9-B529-A42158647B36}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {54835857-129F-44C9-B529-A42158647B36}.Debug|Mixed Platforms.Build.0 = Debug|x86 {54835857-129F-44C9-B529-A42158647B36}.Debug|x64.ActiveCfg = Debug|x64 {54835857-129F-44C9-B529-A42158647B36}.Debug|x64.Build.0 = Debug|x64 {54835857-129F-44C9-B529-A42158647B36}.Debug|x86.ActiveCfg = Debug|x86 {54835857-129F-44C9-B529-A42158647B36}.Debug|x86.Build.0 = Debug|x86 {54835857-129F-44C9-B529-A42158647B36}.Release|Any CPU.ActiveCfg = Release|Any CPU {54835857-129F-44C9-B529-A42158647B36}.Release|Any CPU.Build.0 = Release|Any CPU {54835857-129F-44C9-B529-A42158647B36}.Release|Mixed Platforms.ActiveCfg = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.Release|Mixed Platforms.Build.0 = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.Release|x64.ActiveCfg = Release|x64 {54835857-129F-44C9-B529-A42158647B36}.Release|x64.Build.0 = Release|x64 {54835857-129F-44C9-B529-A42158647B36}.Release|x86.ActiveCfg = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal z3-4.4.0/examples/msf/Validator/0000755000175000017500000000000012540347414016175 5ustar michaelmichaelz3-4.4.0/examples/msf/Validator/App.config0000644000175000017500000000701312540347414020105 0ustar michaelmichael
z3-4.4.0/examples/msf/Validator/Validator.csproj0000644000175000017500000001240112540347414021342 0ustar michaelmichael Debug AnyCPU 9.0.21022 2.0 {54835857-129F-44C9-B529-A42158647B36} Exe Properties Validator Validator v4.0 512 publish\ true Disk false Foreground 7 Days false false true 0 1.0.0.%2a false false true true full false bin\Debug\ DEBUG;TRACE prompt 4 pdbonly true bin\Release\ TRACE prompt 4 true bin\x64\Debug\ DEBUG;TRACE full x86 true GlobalSuppressions.cs prompt bin\x64\Release\ TRACE true pdbonly x64 true GlobalSuppressions.cs prompt true bin\x86\Debug\ DEBUG;TRACE full x86 prompt MinimumRecommendedRules.ruleset bin\x86\Release\ TRACE true pdbonly x86 prompt MinimumRecommendedRules.ruleset ..\Microsoft.Solver.Foundation.dll {7340e664-f648-4ff7-89b2-f4da424996d3} SolverFoundation.Plugin.Z3 z3-4.4.0/examples/msf/Validator/Program.cs0000644000175000017500000001603712540347414020142 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.IO; using System.Linq; using System.Collections.Generic; using Microsoft.SolverFoundation.Common; using Microsoft.SolverFoundation.Solvers; using Microsoft.SolverFoundation.Plugin.Z3; using Microsoft.SolverFoundation.Services; using System.Text; namespace Validator { class Program { static void LoadModel(SolverContext context, string fileName) { string ext = Path.GetExtension(fileName).ToLower(); if (ext == ".mps") { context.LoadModel(FileFormat.MPS, Path.GetFullPath(fileName)); } else if (ext == ".smps") { context.LoadModel(FileFormat.SMPS, Path.GetFullPath(fileName)); } else if (ext == ".oml") { context.LoadModel(FileFormat.OML, Path.GetFullPath(fileName)); } else { throw new NotSupportedException("This file format hasn't been supported."); } } static void ExecuteZ3(string fileName, Z3BaseDirective directive) { SolverContext context = SolverContext.GetContext(); try { LoadModel(context, fileName); Solution solution = context.Solve(directive); Report report = solution.GetReport(); Console.Write("{0}", report); } catch (Exception e) { Console.WriteLine("Skipping unsolvable instance in {0} with error message '{1}'.", fileName, e.Message); } finally { context.ClearModel(); } } static void ConvertToSMT2(string fileName, Z3BaseDirective directive) { SolverContext context = SolverContext.GetContext(); try { LoadModel(context, fileName); if (context.CurrentModel.Goals.Any()) { directive.SMT2LogFile = Path.ChangeExtension(fileName, ".smt2"); context.Solve(() => true, directive); } } catch (Exception e) { Console.WriteLine("Skipping unconvertable instance in {0} with error message '{1}'.", fileName, e.Message); } finally { context.ClearModel(); } } static void ValidateZ3(string fileName, Z3BaseDirective directive) { SolverContext context = SolverContext.GetContext(); try { LoadModel(context, fileName); if (context.CurrentModel.Goals.Any()) { var msfDirective = (directive is Z3MILPDirective) ? (Directive)new MixedIntegerProgrammingDirective() { TimeLimit = 10000 } : (Directive)new Directive() { TimeLimit = 10000 }; var sol1 = context.Solve(msfDirective); Console.WriteLine("Solved the model using MSF."); Console.Write("{0}", sol1.GetReport()); var expectedGoals = sol1.Goals.Select(x => x.ToDouble()); context.ClearModel(); context.LoadModel(FileFormat.OML, Path.GetFullPath(fileName)); directive.SMT2LogFile = Path.ChangeExtension(fileName, ".smt2"); var sol2 = context.Solve(directive); //Console.Write("{0}", sol2.GetReport()); var actualGoals = sol2.Goals.Select(x => x.ToDouble()); Console.WriteLine("Solved the model using Z3."); var goalPairs = expectedGoals.Zip(actualGoals, (expected, actual) => new { expected, actual }).ToArray(); bool validated = goalPairs.All(p => Math.Abs(p.expected - p.actual) <= 0.0001); if (validated) { Console.WriteLine("INFO: Two solvers give approximately the same results."); } else { Console.Error.WriteLine("ERROR: Discrepancy found between results."); if (!validated && File.Exists(directive.SMT2LogFile)) { var sb = new StringBuilder(); for(int i = 0; i < goalPairs.Length; i++) { sb.AppendFormat("\n(echo \"Goal {0}: actual |-> {1:0.0000}, expected |-> {2:0.0000}\")", i + 1, goalPairs[i].actual, goalPairs[i].expected); } Console.Error.WriteLine(sb.ToString()); File.AppendAllText(directive.SMT2LogFile, sb.ToString()); } } } else { Console.WriteLine("Ignoring this instance without having any goal."); } } catch (Exception e) { Console.WriteLine("Skipping unsolvable instance in {0} with error message '{1}'.", fileName, e.Message); } finally { context.ClearModel(); } } static void Main(string[] args) { Z3BaseDirective directive = new Z3MILPDirective(); for (int i = 0; i < args.Length; ++i) { if (args[i] == "-s" || args[i] == "-solve") { ExecuteZ3(args[i + 1], directive); return; } if (args[i] == "-c" || args[i] == "-convert") { ConvertToSMT2(args[i + 1], directive); return; } if (args[i] == "-v" || args[i] == "-validate") { ValidateZ3(args[i + 1], directive); return; } if (args[i] == "-t" || args[i] == "-term") { directive = new Z3TermDirective(); } } if (args.Length > 0) { ExecuteZ3(args[0], directive); return; } Console.WriteLine(@" Validator is a simple command line to migrate benchmarks from OML, MPS and SMPS to SMT2 formats. Commands: -solve : solving the model using Z3 -convert : converting the model into SMT2 format -validate : validating by comparing results between Z3 and MSF solvers -term : change the default Z3 MILP solver to Z3 Term solver where is any file with OML, MPS or SMPS extension. Examples: Validator.exe -convert model.mps Validator.exe -term -solve model.oml "); } } } z3-4.4.0/examples/msf/Validator/MicrosoftSolverFoundationForExcel.dll.config0000644000175000017500000000654712540347414026771 0ustar michaelmichael
z3-4.4.0/examples/msf/Validator/Properties/0000755000175000017500000000000012540347414020331 5ustar michaelmichaelz3-4.4.0/examples/msf/Validator/Properties/AssemblyInfo.cs0000644000175000017500000000260612540347414023257 0ustar michaelmichaelusing System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("testSolver")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Microsoft")] [assembly: AssemblyProduct("testSolver")] [assembly: AssemblyCopyright("Copyright © Microsoft 2009")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("c03c1084-d119-483f-80fe-c639eae75959")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3/0000755000175000017500000000000012540347414021301 5ustar michaelmichaelz3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3/Z3TermSolver.cs0000644000175000017500000003746012540347414024161 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Threading; using System.Globalization; using System.Collections.Generic; using Microsoft.SolverFoundation.Common; using Microsoft.SolverFoundation.Properties; using Microsoft.SolverFoundation.Solvers; using Microsoft.SolverFoundation.Services; using Microsoft.Z3; using System.Linq; using System.Diagnostics; using System.IO; namespace Microsoft.SolverFoundation.Plugin.Z3 { /// /// The class is implementation of the MSF constraint solver /// using the Microsoft Z3 solver as the backend. /// This solver supports Int, Real constraints and their arbitrary boolean combinations. /// public class Z3TermSolver : TermModel, ITermSolver, INonlinearSolution, IReportProvider { private NonlinearResult _result; private Z3BaseSolver _solver; /// Constructor that initializes the base clases public Z3TermSolver() : base(null) { _solver = new Z3BaseSolver(this); } /// Constructor that initializes the base clases public Z3TermSolver(ISolverEnvironment context) : this() { } /// /// Shutdown can be called when when the solver is not active, i.e. /// when it is done with Solve() or it has gracefully returns from Solve() /// after an abort. /// public void Shutdown() { _solver.DestructSolver(true); } private BoolExpr MkBool(int rid) { var context = _solver.Context; if (IsConstant(rid)) { Rational lower, upper; GetBounds(rid, out lower, out upper); Debug.Assert(lower == upper); if (lower.IsZero) return context.MkFalse(); return context.MkTrue(); } if (IsOperation(rid)) { BoolExpr[] children; ArithExpr[] operands; TermModelOperation op = GetOperation(rid); switch(op) { case TermModelOperation.And: Debug.Assert(GetOperandCount(rid) >= 2, "Conjunction requires at least two operands."); children = (GetOperands(rid)).Select(x => MkBool(x)).ToArray(); return context.MkAnd(children); case TermModelOperation.Or: Debug.Assert(GetOperandCount(rid) >= 2, "Disjunction requires at least two operands."); children = (GetOperands(rid)).Select(x => MkBool(x)).ToArray(); return context.MkOr(children); case TermModelOperation.Not: Debug.Assert(GetOperandCount(rid) == 1, "Negation is unary."); return context.MkNot(MkBool(GetOperand(rid, 0))); case TermModelOperation.If: Debug.Assert(GetOperandCount(rid) == 3, "If is ternary."); BoolExpr b = MkBool(GetOperand(rid, 0)); Expr x1 = MkBool(GetOperand(rid, 1)); Expr x2 = MkBool(GetOperand(rid, 2)); return (BoolExpr)context.MkITE(b, x1, x2); case TermModelOperation.Unequal: Debug.Assert(GetOperandCount(rid) >= 2, "Distinct should have at least two operands."); return context.MkDistinct((GetOperands(rid)).Select(x => MkTerm(x)).ToArray()); case TermModelOperation.Greater: case TermModelOperation.Less: case TermModelOperation.GreaterEqual: case TermModelOperation.LessEqual: case TermModelOperation.Equal: Debug.Assert(GetOperandCount(rid) >= 2, "Comparison should have at least two operands."); operands = (GetOperands(rid)).Select(x => MkTerm(x)).ToArray(); return ReduceComparison(GetOperation(rid), operands); case TermModelOperation.Identity: Debug.Assert(GetOperandCount(rid) == 1, "Identity takes exactly one operand."); return MkBool(GetOperand(rid, 0)); default: return context.MkEq(MkTerm(rid), _solver.GetNumeral(Rational.One)); } } return context.MkEq(MkTerm(rid), _solver.GetNumeral(Rational.One)); } private ArithExpr MkBoolToArith(BoolExpr e) { var context = _solver.Context; return (ArithExpr)context.MkITE(e, _solver.GetNumeral(Rational.One), _solver.GetNumeral(Rational.Zero)); } private ArithExpr MkTerm(int rid) { var context = _solver.Context; if (IsConstant(rid)) { Rational lower, upper; GetBounds(rid, out lower, out upper); Debug.Assert(lower == upper); return _solver.GetNumeral(lower); } else if (IsOperation(rid)) { ArithExpr[] operands; TermModelOperation op = GetOperation(rid); switch(op) { case TermModelOperation.And: case TermModelOperation.Or: case TermModelOperation.Not: case TermModelOperation.Unequal: case TermModelOperation.Greater: case TermModelOperation.Less: case TermModelOperation.GreaterEqual: case TermModelOperation.LessEqual: case TermModelOperation.Equal: return MkBoolToArith(MkBool(rid)); case TermModelOperation.If: Debug.Assert(GetOperandCount(rid) == 3, "If is ternary."); BoolExpr b = MkBool(GetOperand(rid, 0)); Expr x1 = MkTerm(GetOperand(rid, 1)); Expr x2 = MkTerm(GetOperand(rid, 2)); return (ArithExpr)context.MkITE(b, x1, x2); case TermModelOperation.Plus: Debug.Assert(GetOperandCount(rid) >= 2, "Plus takes at least two operands."); operands = (GetOperands(rid)).Select(x => MkTerm(x)).ToArray(); return context.MkAdd(operands); case TermModelOperation.Minus: Debug.Assert(GetOperandCount(rid) == 1, "Minus takes exactly one operand."); return context.MkUnaryMinus(MkTerm(GetOperand(rid, 0))); case TermModelOperation.Times: Debug.Assert(GetOperandCount(rid) >= 2, "Times requires at least two operands."); operands = (GetOperands(rid)).Select(x => MkTerm(x)).ToArray(); return context.MkMul(operands); case TermModelOperation.Identity: Debug.Assert(GetOperandCount(rid) == 1, "Identity takes exactly one operand."); return MkTerm(GetOperand(rid, 0)); case TermModelOperation.Abs: Debug.Assert(GetOperandCount(rid) == 1, "Abs takes exactly one operand."); ArithExpr e = MkTerm(GetOperand(rid, 0)); ArithExpr minusE = context.MkUnaryMinus(e); ArithExpr zero = _solver.GetNumeral(Rational.Zero); return (ArithExpr)context.MkITE(context.MkGe(e, zero), e, minusE); default: Console.Error.WriteLine("{0} operation isn't supported.", op); throw new NotSupportedException(); } } else { return _solver.GetVariable(rid); } } private BoolExpr ReduceComparison(TermModelOperation type, ArithExpr[] operands) { var context = _solver.Context; Debug.Assert(operands.Length >= 2); Func mkComparison; switch (type) { case TermModelOperation.Greater: mkComparison = (x, y) => context.MkGt(x, y); break; case TermModelOperation.Less: mkComparison = (x, y) => context.MkLt(x, y); break; case TermModelOperation.GreaterEqual: mkComparison = (x, y) => context.MkGe(x, y); break; case TermModelOperation.LessEqual: mkComparison = (x, y) => context.MkLe(x, y); break; case TermModelOperation.Equal: mkComparison = (x, y) => context.MkEq(x, y); break; default: throw new NotSupportedException(); } BoolExpr current = mkComparison(operands[0], operands[1]); for (int i = 1; i < operands.Length - 1; ++i) current = context.MkAnd(current, mkComparison(operands[i], operands[i + 1])); return current; } private bool IsBoolRow(int rid) { Rational lower, upper; GetBounds(rid, out lower, out upper); return lower == upper && lower.IsOne && IsBoolTerm(rid); } private bool IsBoolTerm(int rid) { if (IsConstant(rid)) { Rational lower, upper; GetBounds(rid, out lower, out upper); Debug.Assert(lower == upper); return lower.IsOne || lower.IsZero; } if (IsOperation(rid)) { TermModelOperation op = GetOperation(rid); switch (op) { case TermModelOperation.And: case TermModelOperation.Or: case TermModelOperation.Not: case TermModelOperation.LessEqual: case TermModelOperation.Less: case TermModelOperation.Greater: case TermModelOperation.GreaterEqual: case TermModelOperation.Unequal: case TermModelOperation.Equal: return true; case TermModelOperation.If: return IsBoolTerm(GetOperand(rid, 1)) && IsBoolTerm(GetOperand(rid, 2)); case TermModelOperation.Identity: return IsBoolTerm(GetOperand(rid, 0)); default: return false; } } return false; } /// /// Adds a MSF row to the Z3 assertions. /// /// The MSF row id private void AddRow(int rid) { if (IsConstant(rid)) return; if (IsBoolRow(rid)) { _solver.AssertBool(MkBool(rid)); return; } // Start with the 0 term ArithExpr row = MkTerm(rid); _solver.AssertArith(rid, row); } private TermModelOperation[] _supportedOperations = { TermModelOperation.And, TermModelOperation.Or, TermModelOperation.Not, TermModelOperation.Unequal, TermModelOperation.Greater, TermModelOperation.Less, TermModelOperation.GreaterEqual, TermModelOperation.LessEqual, TermModelOperation.Equal, TermModelOperation.If, TermModelOperation.Plus, TermModelOperation.Minus, TermModelOperation.Times, TermModelOperation.Identity, TermModelOperation.Abs }; /// /// Gets the operations supported by the solver. /// /// All the TermModelOperations supported by the solver. public IEnumerable SupportedOperations { get { return _supportedOperations; } } /// /// Set results based on internal solver status /// private void SetResult(Z3Result status) { switch (status) { case Z3Result.Optimal: _result = NonlinearResult.Optimal; break; case Z3Result.LocalOptimal: _result = NonlinearResult.LocalOptimal; break; case Z3Result.Feasible: _result = NonlinearResult.Feasible; break; case Z3Result.Infeasible: _result = NonlinearResult.Infeasible; break; case Z3Result.Interrupted: _result = NonlinearResult.Interrupted; break; default: Debug.Assert(false, "Unrecognized Z3 Result"); break; } } /// /// Starts solving the problem using the Z3 solver. /// /// Parameters to the solver /// The solution to the problem public INonlinearSolution Solve(ISolverParameters parameters) { // Get the Z3 parameters var z3Params = parameters as Z3BaseParams; Debug.Assert(z3Params != null, "Parameters should be an instance of Z3BaseParams."); _solver.Solve(z3Params, Goals, AddRow, MkTerm, SetResult); return this; } double INonlinearSolution.GetValue(int vid) { Debug.Assert(_solver.Variables.ContainsKey(vid), "This index should correspond to a variable."); return GetValue(vid).ToDouble(); } public int SolvedGoalCount { get { return GoalCount; } } public double GetSolutionValue(int goalIndex) { var goal = Goals.ElementAt(goalIndex); Debug.Assert(goal != null, "Goal should be an element of the goal list."); return GetValue(goal.Index).ToDouble(); } public void GetSolvedGoal(int goalIndex, out object key, out int vid, out bool minimize, out bool optimal) { var goal = Goals.ElementAt(goalIndex); Debug.Assert(goal != null, "Goal should be an element of the goal list."); key = goal.Key; vid = goal.Index; minimize = goal.Minimize; optimal = _result == NonlinearResult.Optimal; } public NonlinearResult Result { get { return _result; } } public Report GetReport(SolverContext context, Solution solution, SolutionMapping solutionMapping) { PluginSolutionMapping pluginSolutionMapping = solutionMapping as PluginSolutionMapping; if (pluginSolutionMapping == null && solutionMapping != null) throw new ArgumentException("solutionMapping is not a LinearSolutionMapping", "solutionMapping"); return new Z3TermSolverReport(context, this, solution, pluginSolutionMapping); } } public class Z3TermSolverReport : Report { public Z3TermSolverReport(SolverContext context, ISolver solver, Solution solution, PluginSolutionMapping pluginSolutionMapping) : base(context, solver, solution, pluginSolutionMapping) { } } } z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseDirective.cs0000644000175000017500000000473712540347414024571 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Text; using Microsoft.SolverFoundation.Services; namespace Microsoft.SolverFoundation.Plugin.Z3 { /// /// Combining objective functions /// public enum OptimizationKind { Lexicographic, BoundingBox, ParetoOptimal }; /// /// Algorithm for solving cardinality constraints /// public enum CardinalityAlgorithm { FuMalik, CoreMaxSAT } /// /// Algorithm for solving pseudo-boolean constraints /// public enum PseudoBooleanAlgorithm { WeightedMaxSAT, IterativeWeightedMaxSAT, BisectionWeightedMaxSAT, WeightedPartialMaxSAT2 } /// /// Strategy for solving arithmetic optimization /// public enum ArithmeticStrategy { Basic, Farkas } public abstract class Z3BaseDirective : Directive { protected OptimizationKind _optKind; protected CardinalityAlgorithm _cardAlgorithm; protected PseudoBooleanAlgorithm _pboAlgorithm; protected ArithmeticStrategy _arithStrategy; protected string _smt2LogFile; public Z3BaseDirective() { Arithmetic = Arithmetic.Exact; } public OptimizationKind OptKind { get { return _optKind; } set { _optKind = value; } } public CardinalityAlgorithm CardinalityAlgorithm { get { return _cardAlgorithm; } set { _cardAlgorithm = value; } } public PseudoBooleanAlgorithm PseudoBooleanAlgorithm { get { return _pboAlgorithm; } set { _pboAlgorithm = value; } } public ArithmeticStrategy ArithmeticStrategy { get { return _arithStrategy; } set { _arithStrategy = value; } } public string SMT2LogFile { get { return _smt2LogFile; } set { _smt2LogFile = value; } } public override string ToString() { var sb = new StringBuilder(); sb.Append(this.GetType().Name); sb.Append("("); sb.AppendFormat("OptKind: {0}, ", _optKind); sb.AppendFormat("SMT2LogFile: {0}", _smt2LogFile); sb.Append(")"); return sb.ToString(); } } } z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPDirective.cs0000644000175000017500000000034512540347414024447 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ using Microsoft.SolverFoundation.Services; using System; namespace Microsoft.SolverFoundation.Plugin.Z3 { public class Z3MILPDirective : Z3BaseDirective { } } z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3/App.config0000644000175000017500000000701312540347414023211 0ustar michaelmichael
z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPParams.cs0000644000175000017500000000110712540347414023751 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ using Microsoft.SolverFoundation.Services; using System; namespace Microsoft.SolverFoundation.Plugin.Z3 { public class Z3MILPParams : Z3BaseParams { // Need these constructors for reflection done by plugin model public Z3MILPParams() : base() { } public Z3MILPParams(Directive directive) : base(directive) { } public Z3MILPParams(Func queryAbortFunction) : base(queryAbortFunction) { } public Z3MILPParams(Z3BaseParams z3Parameters) : base (z3Parameters) { } } }z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3/SolverFoundation.Plugin.Z3.csproj0000644000175000017500000001430712540347414027561 0ustar michaelmichael Debug AnyCPU 9.0.30729 2.0 {7340E664-F648-4FF7-89B2-F4DA424996D3} Library Properties Microsoft.SolverFoundation.Plugin.Z3 SolverFoundation.Plugin.Z3 v4.0 512 false publish\ true Disk false Foreground 7 Days false false true 0 1.0.0.%2a false false true true full false bin\Debug\ DEBUG;TRACE prompt 4 AllRules.ruleset pdbonly true bin\Release\ TRACE prompt 4 AllRules.ruleset bin\commercial\ TRACE true pdbonly AnyCPU prompt bin\commercial_64\ TRACE true pdbonly AnyCPU prompt true bin\x86\Debug\ DEBUG;TRACE full x86 prompt AllRules.ruleset bin\x86\Release\ TRACE true pdbonly x86 prompt AllRules.ruleset bin\x86\commercial\ TRACE true pdbonly x86 prompt AllRules.ruleset bin\x86\commercial_64\ TRACE true pdbonly x86 prompt ..\Microsoft.Solver.Foundation.dll False ..\Microsoft.Z3.dll z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3/AbortWorker.cs0000644000175000017500000000535012540347414024074 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using Microsoft.Z3; namespace Microsoft.SolverFoundation.Plugin.Z3 { /// /// Thread that will wait until the query abort function returns true or /// the stop method is called. If the abort function returns true at some /// point it will issue a softCancel() call to Z3. /// internal class AbortWorker { #region Private Members /// The Z3 solver private Microsoft.Z3.Context _context; /// The abort function to use to check if we are aborted private Func _QueryAbortFunction; /// Flag indicating that worker should stop private bool _stop = false; /// Flag indicating that we have been sent an abort signal private bool _aborted = false; #endregion Private Members #region Construction /// /// Worker constructor taking a Z3 instance and a function to periodically /// check for aborts. /// /// Z3 instance /// method to call to check for aborts public AbortWorker(Context context, Func queryAbortFunction) { _context = context; _QueryAbortFunction = queryAbortFunction; } #endregion Construction #region Public Methods /// /// Stop the abort worker. /// public void Stop() { _stop = true; } /// /// Is true if we have been aborted. /// public bool Aborted { get { return _aborted; } } /// /// Starts the abort worker. The worker checks the abort method /// periodically until either it is stopped by a call to the Stop() /// method or it gets an abort signal. In the latter case it will /// issue a soft abort signal to Z3. /// public void Start() { // We go until someone stops us _stop = false; while (!_stop && !_QueryAbortFunction()) { // Wait for a while Thread.Sleep(10); } // If we were stopped on abort, cancel z3 if (!_stop) { _context.Interrupt(); _aborted = true; } } #endregion Public Methods } } z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3/Z3TermParams.cs0000644000175000017500000000077612540347414024132 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ using Microsoft.SolverFoundation.Services; using System; namespace Microsoft.SolverFoundation.Plugin.Z3 { public class Z3TermParams : Z3BaseParams { public Z3TermParams() : base() { } public Z3TermParams(Directive directive) : base(directive) { } public Z3TermParams(Func queryAbortFunction) : base(queryAbortFunction) { } public Z3TermParams(Z3BaseParams z3Parameters) : base(z3Parameters) { } } }z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseParams.cs0000644000175000017500000000550012540347414024063 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ using Microsoft.SolverFoundation.Services; using System; namespace Microsoft.SolverFoundation.Plugin.Z3 { /// /// Implementation of the solver parameters for Z3 /// public class Z3BaseParams : ISolverParameters { #region Private Members /// The abort method we can call (defaults to always false) protected Func _queryAbortFunction = delegate() { return false; }; /// The directive to use protected Directive _directive = null; protected OptimizationKind _optKind; protected CardinalityAlgorithm _cardAlgorithm; protected PseudoBooleanAlgorithm _pboAlgorithm; protected ArithmeticStrategy _arithStrategy; protected string _smt2LogFile; #endregion Private Members #region Construction public Z3BaseParams() { } public Z3BaseParams(Directive directive) { _directive = directive; var z3Directive = directive as Z3BaseDirective; if (z3Directive != null) { _optKind = z3Directive.OptKind; _cardAlgorithm = z3Directive.CardinalityAlgorithm; _pboAlgorithm = z3Directive.PseudoBooleanAlgorithm; _arithStrategy = z3Directive.ArithmeticStrategy; _smt2LogFile = z3Directive.SMT2LogFile; } } public Z3BaseParams(Func queryAbortFunction) { _queryAbortFunction = queryAbortFunction; } public Z3BaseParams(Z3BaseParams z3Parameters) { _queryAbortFunction = z3Parameters._queryAbortFunction; } #endregion Construction #region ISolverParameters Members /// /// Getter for the abort method /// public Func QueryAbort { get { return _queryAbortFunction; } set { _queryAbortFunction = value; } } public OptimizationKind OptKind { get { return _optKind; } set { _optKind = value; } } public CardinalityAlgorithm CardinalityAlgorithm { get { return _cardAlgorithm; } set { _cardAlgorithm = value; } } public PseudoBooleanAlgorithm PseudoBooleanAlgorithm { get { return _pboAlgorithm; } set { _pboAlgorithm = value; } } public ArithmeticStrategy ArithmeticStrategy { get { return _arithStrategy; } set { _arithStrategy = value; } } public string SMT2LogFile { get { return _smt2LogFile; } set { _smt2LogFile = value; } } #endregion } }z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3/Utils.cs0000644000175000017500000000733012540347414022733 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using Microsoft.Z3; using Microsoft.SolverFoundation.Common; namespace Microsoft.SolverFoundation.Plugin.Z3 { public class Utils { /// /// Returns the Z3 term corresponding to the MSF rational number. /// /// The MSF rational /// The Z3 term public static ArithExpr GetNumeral(Context context, Rational rational, Sort sort = null) { try { sort = rational.IsInteger() ? ((Sort)context.IntSort) : (sort == null ? (Sort)context.RealSort : sort); return (ArithExpr)context.MkNumeral(rational.ToString(), sort); } catch (Z3Exception e) { Console.Error.WriteLine("Conversion of {0} failed:\n {1}", rational, e); throw new NotSupportedException(); } } private static long BASE = 10 ^ 18; private static Rational ToRational(System.Numerics.BigInteger bi) { if (System.Numerics.BigInteger.Abs(bi) <= BASE) { return (Rational)((long)bi); } return BASE * ToRational(bi / BASE) + ToRational(bi % BASE); } public static Rational ToRational(IntNum i) { return ToRational(i.BigInteger); } public static Rational ToRational(RatNum r) { return ToRational(r.BigIntNumerator) / ToRational(r.BigIntDenominator); } public static Rational ToRational(Expr expr) { Debug.Assert(expr is ArithExpr, "Only accept ArithExpr for now."); var e = expr as ArithExpr; if (e is IntNum) { Debug.Assert(expr.IsIntNum, "Number should be an integer."); return ToRational(expr as IntNum); } if (e is RatNum) { Debug.Assert(expr.IsRatNum, "Number should be a rational."); return ToRational(expr as RatNum); } if (e.IsAdd) { Rational r = Rational.Zero; foreach (var arg in e.Args) { r += ToRational(arg); } return r; } if (e.IsMul) { Rational r = Rational.One; foreach (var arg in e.Args) { r *= ToRational(arg); } return r; } if (e.IsUMinus) { return -ToRational(e.Args[0]); } if (e.IsDiv) { return ToRational(e.Args[0]) / ToRational(e.Args[1]); } if (e.IsSub) { Rational r = ToRational(e.Args[0]); for (int i = 1; i < e.Args.Length; ++i) { r -= ToRational(e.Args[i]); } return r; } if (e.IsConst && e.FuncDecl.Name.ToString() == "oo") { return Rational.PositiveInfinity; } if (e.IsConst && e.FuncDecl.Name.ToString() == "epsilon") { return Rational.One/Rational.PositiveInfinity; } Debug.Assert(false, "Should not happen"); return Rational.One; } } } z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPSolver.cs0000644000175000017500000001717712540347414024016 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.IO; using Microsoft.Z3; using Microsoft.SolverFoundation.Common; using Microsoft.SolverFoundation.Services; using Microsoft.SolverFoundation.Plugin; namespace Microsoft.SolverFoundation.Plugin.Z3 { /// /// The class is implementation of the MSF mixed linear programming solver /// using the Microsoft Z3 solver as the backend. /// public class Z3MILPSolver : LinearModel, ILinearSolver, ILinearSolution, IReportProvider { #region Private members private LinearResult _result; private LinearSolutionQuality _solutionQuality; private Z3BaseSolver _solver; #endregion Private members #region Solver construction and destruction /// Constructor that initializes the base clases public Z3MILPSolver() : base(null) { _result = LinearResult.Feasible; _solver = new Z3BaseSolver(this); } /// Constructor that initializes the base clases public Z3MILPSolver(ISolverEnvironment context) : this() { } /// /// Shutdown can be called when when the solver is not active, i.e. /// when it is done with Solve() or it has gracefully returns from Solve() /// after an abort. /// public void Shutdown() { _solver.DestructSolver(true); } #endregion Solver construction and destruction #region Obtaining information about the solution public ILinearSolverReport GetReport(LinearSolverReportType reportType) { // We don't support sensitivity report return null; } #endregion Obtaining information about the solution #region Construction of the problem /// /// Get corresponding Z3 formula of a MSF row. /// /// The MSF row id private ArithExpr MkGoalRow(int rid) { // Start with the 0 term List row = new List(); // Now, add all the entries of this row foreach (LinearEntry entry in GetRowEntries(rid)) { // Get the variable and constant in the row ArithExpr e = _solver.GetVariable(entry.Index); if (!entry.Value.IsOne) { e = _solver.Context.MkMul(_solver.GetNumeral(entry.Value, e.Sort), e); } row.Add(e); } switch (row.Count) { case 0: return _solver.GetNumeral(new Rational()); case 1: return row[0]; default: return _solver.Context.MkAdd(row.ToArray()); } } /// /// Adds a MSF row to the Z3 assertions. /// /// The MSF row id private void AddRow(int rid) { // Start with the 0 term ArithExpr row = MkGoalRow(rid); _solver.AssertArith(rid, row); } /// /// Set results based on internal solver status /// private void SetResult(Z3Result status) { switch (status) { case Z3Result.Optimal: _result = LinearResult.Optimal; _solutionQuality = LinearSolutionQuality.Exact; break; case Z3Result.LocalOptimal: _result = LinearResult.Feasible; _solutionQuality = LinearSolutionQuality.Approximate; break; case Z3Result.Feasible: _result = LinearResult.Feasible; _solutionQuality = LinearSolutionQuality.Exact; break; case Z3Result.Infeasible: _result = LinearResult.InfeasiblePrimal; _solutionQuality = LinearSolutionQuality.None; break; case Z3Result.Interrupted: _result = LinearResult.Interrupted; _solutionQuality = LinearSolutionQuality.None; break; default: Debug.Assert(false, "Unrecognized Z3 Result"); break; } } #endregion Construction of the problem #region Solving the problem /// /// Starts solving the problem using the Z3 solver. /// /// Parameters to the solver /// The solution to the problem public ILinearSolution Solve(ISolverParameters parameters) { // Get the Z3 parameters var z3Params = parameters as Z3BaseParams; Debug.Assert(z3Params != null, "Parameters should be an instance of Z3BaseParams."); _solver.Solve(z3Params, Goals, AddRow, MkGoalRow, SetResult); return this; } #endregion Solving the problem #region ILinearSolution Members public Rational GetSolutionValue(int goalIndex) { var goal = Goals.ElementAt(goalIndex); Debug.Assert(goal != null, "Goal should be an element of the goal list."); return GetValue(goal.Index); } public void GetSolvedGoal(int goalIndex, out object key, out int vid, out bool minimize, out bool optimal) { var goal = Goals.ElementAt(goalIndex); Debug.Assert(goal != null, "Goal should be an element of the goal list."); key = goal.Key; vid = goal.Index; minimize = goal.Minimize; optimal = _result == LinearResult.Optimal; } // LpResult is LP relaxation assignment. public LinearResult LpResult { get { return _result; } } public Rational MipBestBound { get { Debug.Assert(GoalCount > 0, "MipBestBound is only applicable for optimization instances."); return GetSolutionValue(0); } } public LinearResult MipResult { get { return _result; } } public LinearResult Result { get { return _result; } } public LinearSolutionQuality SolutionQuality { get { return _solutionQuality; } } public int SolvedGoalCount { get { return GoalCount; } } #endregion public Report GetReport(SolverContext context, Solution solution, SolutionMapping solutionMapping) { LinearSolutionMapping lpSolutionMapping = solutionMapping as LinearSolutionMapping; if (lpSolutionMapping == null && solutionMapping != null) throw new ArgumentException("solutionMapping is not a LinearSolutionMapping", "solutionMapping"); return new Z3LinearSolverReport(context, this, solution, lpSolutionMapping); } } /// /// Class implementing the LinearReport. /// public class Z3LinearSolverReport : LinearReport { public Z3LinearSolverReport(SolverContext context, ISolver solver, Solution solution, LinearSolutionMapping solutionMapping) : base(context, solver, solution, solutionMapping) { } } } z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseSolver.cs0000644000175000017500000003405412540347414024120 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Collections.Generic; using System.Threading; using System.IO; using System.Linq; using System.Text; using System.Diagnostics; using Microsoft.Z3; using Microsoft.SolverFoundation.Common; using Microsoft.SolverFoundation.Services; namespace Microsoft.SolverFoundation.Plugin.Z3 { internal enum Z3Result { Optimal, LocalOptimal, Feasible, Interrupted, Infeasible } /// /// The basic solver class to take care of transformation from an MSF instance to an Z3 instance /// internal class Z3BaseSolver { /// Representing MSF model private IRowVariableModel _model; /// The Z3 solver we are currently using private Context _context = null; /// Default optimization solver private Optimize _optSolver = null; /// Marks when we are inside the Solve() method private bool _isSolving = false; /// A map from MSF variable ids to Z3 variables private Dictionary _variables = new Dictionary(); /// A map from MSF variable ids to Z3 goal ids private Dictionary _goals = new Dictionary(); internal Z3BaseSolver(IRowVariableModel model) { _model = model; } internal Context Context { get { return _context; } } internal Dictionary Variables { get { return _variables; } } internal Dictionary Goals { get { return _goals; } } /// /// Destructs a currently active Z3 solver and the associated data. /// internal void DestructSolver(bool checkInSolve) { if (_context != null) { if (checkInSolve && !_isSolving) { _variables.Clear(); if (!_isSolving) { _optSolver.Dispose(); _context.Dispose(); } } else { Console.Error.WriteLine("Z3 destruction is invoked while in Solving phase."); } } } /// /// Constructs a Z3 solver to be used. /// internal void ConstructSolver(Z3BaseParams parameters) { // If Z3 is there already, kill it if (_context != null) { DestructSolver(false); } _context = new Context(); _optSolver = _context.MkOptimize(); var p = _context.MkParams(); switch (parameters.OptKind) { case OptimizationKind.BoundingBox: p.Add("priority", _context.MkSymbol("box")); break; case OptimizationKind.Lexicographic: p.Add("priority", _context.MkSymbol("lex")); break; case OptimizationKind.ParetoOptimal: p.Add("priority", _context.MkSymbol("pareto")); break; default: Debug.Assert(false, String.Format("Unknown optimization option {0}", parameters.OptKind)); break; } switch (parameters.CardinalityAlgorithm) { case CardinalityAlgorithm.FuMalik: p.Add("maxsat_engine", _context.MkSymbol("fu_malik")); break; case CardinalityAlgorithm.CoreMaxSAT: p.Add("maxsat_engine", _context.MkSymbol("core_maxsat")); break; default: Debug.Assert(false, String.Format("Unknown cardinality algorithm option {0}", parameters.CardinalityAlgorithm)); break; } switch (parameters.PseudoBooleanAlgorithm) { case PseudoBooleanAlgorithm.WeightedMaxSAT: p.Add("wmaxsat_engine", _context.MkSymbol("wmax")); break; case PseudoBooleanAlgorithm.IterativeWeightedMaxSAT: p.Add("wmaxsat_engine", _context.MkSymbol("iwmax")); break; case PseudoBooleanAlgorithm.BisectionWeightedMaxSAT: p.Add("wmaxsat_engine", _context.MkSymbol("bwmax")); break; case PseudoBooleanAlgorithm.WeightedPartialMaxSAT2: p.Add("wmaxsat_engine", _context.MkSymbol("wpm2")); break; default: Debug.Assert(false, String.Format("Unknown pseudo-boolean algorithm option {0}", parameters.PseudoBooleanAlgorithm)); break; } switch (parameters.ArithmeticStrategy) { case ArithmeticStrategy.Basic: p.Add("engine", _context.MkSymbol("basic")); break; case ArithmeticStrategy.Farkas: p.Add("engine", _context.MkSymbol("farkas")); break; default: Debug.Assert(false, String.Format("Unknown arithmetic strategy option {0}", parameters.ArithmeticStrategy)); break; } _optSolver.Parameters = p; } internal ArithExpr GetVariable(int vid) { Expr variable; if (!_variables.TryGetValue(vid, out variable)) { AddVariable(vid); variable = _variables[vid]; } return (ArithExpr)variable; } internal void AssertBool(BoolExpr row) { _optSolver.Assert(row); } internal void AssertArith(int vid, ArithExpr variable) { // Get the bounds on the row Rational lower, upper; _model.GetBounds(vid, out lower, out upper); // Case of equality if (lower == upper) { // Create the equality term Expr eqConst = GetNumeral(lower, variable.Sort); BoolExpr constraint = _context.MkEq(eqConst, variable); // Assert the constraint _optSolver.Assert(constraint); } else { // If upper bound is finite assert the upper bound constraint if (lower.IsFinite) { // Create the lower Bound constraint ArithExpr lowerTerm = GetNumeral(lower, variable.Sort); BoolExpr constraint = _context.MkLe(lowerTerm, variable); // Assert the constraint _optSolver.Assert(constraint); } // If lower bound is finite assert the lower bound constraint if (upper.IsFinite) { // Create the upper bound constraint ArithExpr upperTerm = GetNumeral(upper, variable.Sort); BoolExpr constraint = _context.MkGe(upperTerm, variable); // Assert the constraint _optSolver.Assert(constraint); } } } /// /// Adds a MSF variable with the coresponding assertion to the Z3 variables. /// /// The MSF id of the variable internal void AddVariable(int vid) { // Is the variable an integer bool isInteger = _model.GetIntegrality(vid); // Construct the sort we will be using Sort sort = isInteger ? (Sort)_context.IntSort : (Sort)_context.RealSort; // Get the variable key object key = _model.GetKeyFromIndex(vid); // Try to construct the name string name; if (key != null) name = String.Format("x_{0}_{1}", key, vid); else name = String.Format("x_{0}", vid); ArithExpr variable = (ArithExpr)_context.MkConst(name, sort); // Create the variable and add it to the map Debug.Assert(!_variables.ContainsKey(vid), "Variable names should be unique."); _variables.Add(vid, variable); AssertArith(vid, variable); } internal ArithExpr GetNumeral(Rational rational, Sort sort = null) { return Utils.GetNumeral(_context, rational, sort); } internal void Solve(Z3BaseParams parameters, IEnumerable modelGoals, Action addRow, Func mkGoalRow, Action setResult) { _variables.Clear(); _goals.Clear(); try { // Mark that we are in solving phase _isSolving = true; // Construct Z3 ConstructSolver(parameters); // Add all the variables foreach (int vid in _model.VariableIndices) { AddVariable(vid); } // Add all the rows foreach (int rid in _model.RowIndices) { addRow(rid); } // Add enabled goals to optimization problem foreach (IGoal g in modelGoals) { if (!g.Enabled) continue; ArithExpr gr = mkGoalRow(g.Index); if (g.Minimize) _goals.Add(g, _optSolver.MkMinimize(gr)); else _goals.Add(g, _optSolver.MkMaximize(gr)); } if (_goals.Any() && parameters.SMT2LogFile != null) { Debug.WriteLine("Dumping SMT2 benchmark to log file..."); File.WriteAllText(parameters.SMT2LogFile, _optSolver.ToString()); } bool aborted = parameters.QueryAbort(); if (!aborted) { // Start the abort thread AbortWorker abortWorker = new AbortWorker(_context, parameters.QueryAbort); Thread abortThread = new Thread(abortWorker.Start); abortThread.Start(); // Now solve the problem Status status = _optSolver.Check(); // Stop the abort thread abortWorker.Stop(); abortThread.Join(); switch (status) { case Status.SATISFIABLE: Microsoft.Z3.Model model = _optSolver.Model; Debug.Assert(model != null, "Should be able to get Z3 model."); // Remember the solution values foreach (KeyValuePair pair in _variables) { var value = Utils.ToRational(model.Eval(pair.Value, true)); _model.SetValue(pair.Key, value); } // Remember all objective values foreach (var pair in _goals) { var optimalValue = Utils.ToRational(_optSolver.GetUpper(pair.Value)); _model.SetValue(pair.Key.Index, optimalValue); } model.Dispose(); setResult(_goals.Any() ? Z3Result.Optimal : Z3Result.Feasible); break; case Status.UNSATISFIABLE: setResult(Z3Result.Infeasible); break; case Status.UNKNOWN: if (abortWorker.Aborted) { Microsoft.Z3.Model subOptimalModel = _optSolver.Model; if (subOptimalModel != null && subOptimalModel.NumConsts != 0) { // Remember the solution values foreach (KeyValuePair pair in _variables) { var value = Utils.ToRational(subOptimalModel.Eval(pair.Value, true)); _model.SetValue(pair.Key, value); } // Remember all objective values foreach (var pair in _goals) { var optimalValue = Utils.ToRational(_optSolver.GetUpper(pair.Value)); _model.SetValue(pair.Key.Index, optimalValue); } subOptimalModel.Dispose(); setResult(Z3Result.LocalOptimal); } else setResult(Z3Result.Infeasible); } else setResult(Z3Result.Interrupted); break; default: Debug.Assert(false, "Unrecognized Z3 Status"); break; } } } finally { _isSolving = false; } // Now kill Z3 DestructSolver(true); } } } z3-4.4.0/examples/msf/SolverFoundation.Plugin.Z3/Z3TermDirective.cs0000644000175000017500000000034512540347414024615 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ using Microsoft.SolverFoundation.Services; using System; namespace Microsoft.SolverFoundation.Plugin.Z3 { public class Z3TermDirective : Z3BaseDirective { } } z3-4.4.0/doc/0000755000175000017500000000000012540347414012412 5ustar michaelmichaelz3-4.4.0/doc/update_code_website.cmd0000644000175000017500000000041312540347414017073 0ustar michaelmichaelREM Script for updating the website containing the Z3 Code documentation. REM You must be inside the Microsoft network to execute this script, and REM robocopy must be in your PATH. robocopy /S code\html \\research\root\web\external\en-us\UM\redmond\projects\z3\codez3-4.4.0/doc/mk_api_doc.py0000644000175000017500000001017212540347414015052 0ustar michaelmichael# Copyright (c) Microsoft Corporation 2015 import os import shutil import re import getopt import pydoc import sys import subprocess import shutil ML_ENABLED=False BUILD_DIR='../build' def norm_path(p): # We use '/' on mk_project for convenience return os.path.join(*(p.split('/'))) def display_help(exit_code): print("mk_api_doc.py: Z3 documentation generator\n") print("\nOptions:") print(" -h, --help display this message.") print(" -b , --build= subdirectory where Z3 is built (default: ../build).") print(" --ml include ML/OCaml API documentation.") def parse_options(): global ML_ENABLED, BUILD_DIR try: options, remainder = getopt.gnu_getopt(sys.argv[1:], 'b:h', ['build=', 'help', 'ml']) except: print("ERROR: Invalid command line option") display_help(1) for opt, arg in options: if opt in ('-b', '--build'): BUILD_DIR = norm_path(arg) elif opt in ('h', '--help'): display_help() exit(1) elif opt in ('--ml'): ML_ENABLED=True else: print("ERROR: Invalid command line option: %s" % opt) display_help(1) def mk_dir(d): if not os.path.exists(d): os.makedirs(d) # Eliminate def_API and extra_API directives from file 'inf'. # The result is stored in 'outf'. def cleanup_API(inf, outf): pat1 = re.compile(".*def_API.*") pat2 = re.compile(".*extra_API.*") _inf = open(inf, 'r') _outf = open(outf, 'w') for line in _inf: if not pat1.match(line) and not pat2.match(line): _outf.write(line) try: parse_options() fi = open('website.dox', 'r') fo = open('website-adj.dox', 'w') for line in fi: if (line != '[ML]\n'): fo.write(line) elif (ML_ENABLED): fo.write(' - ML/OCaml API\n') fi.close() fo.close() mk_dir('api/html') mk_dir('tmp') shutil.copyfile('website-adj.dox', 'tmp/website.dox') os.remove('website-adj.dox') shutil.copyfile('../src/api/python/z3.py', 'tmp/z3py.py') cleanup_API('../src/api/z3_api.h', 'tmp/z3_api.h') cleanup_API('../src/api/z3_algebraic.h', 'tmp/z3_algebraic.h') cleanup_API('../src/api/z3_polynomial.h', 'tmp/z3_polynomial.h') cleanup_API('../src/api/z3_rcf.h', 'tmp/z3_rcf.h') cleanup_API('../src/api/z3_interp.h', 'tmp/z3_interp.h') cleanup_API('../src/api/z3_fpa.h', 'tmp/z3_fpa.h') print "Removed annotations from z3_api.h." try: if subprocess.call(['doxygen', 'z3api.dox']) != 0: print "ERROR: doxygen returned nonzero return code" exit(1) except: print "ERROR: failed to execute 'doxygen', make sure doxygen (http://www.doxygen.org) is available in your system." exit(1) print "Generated C and .NET API documentation." os.remove('tmp/z3_api.h') os.remove('tmp/z3_algebraic.h') os.remove('tmp/z3_polynomial.h') os.remove('tmp/z3_rcf.h') os.remove('tmp/z3_interp.h') os.remove('tmp/z3_fpa.h') print "Removed temporary file z3_api.h." os.remove('tmp/website.dox') print "Removed temporary file website.dox" os.remove('tmp/z3py.py') print "Removed temporary file z3py.py" os.removedirs('tmp') print "Removed temporary directory tmp." sys.path.append('../src/api/python') pydoc.writedoc('z3') shutil.move('z3.html', 'api/html/z3.html') print "Generated Python documentation." if ML_ENABLED: mk_dir('api/html/ml') if subprocess.call(['ocamldoc', '-html', '-d', 'api\html\ml', '-sort', '-hide', 'Z3', '-I', '%s/api/ml' % BUILD_DIR, '../src/api/ml/z3enums.mli', '../src/api/ml/z3.mli']) != 0: print "ERROR: ocamldoc failed." exit(1) print "Generated ML/OCaml documentation." print "Documentation was successfully generated at subdirectory './api/html'." except: print "ERROR: failed to generate documentation" exit(1) z3-4.4.0/doc/z3code.dox0000644000175000017500000012646712540347414014335 0ustar michaelmichael# Doxyfile 1.6.3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = Z3 PROJECT_NUMBER = OUTPUT_DIRECTORY = code CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class " \ "The $name widget " \ "The $name file " \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = YES INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = ".." STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 ALIASES = "beginfaq=
    " \ "faq{2}=
  • \1

    \2
  • " \ "endfaq=
" \ "cmdopt{1}=\arg /\1" \ "ext{1}=.\1" \ "ty{1}=\1" \ "emph{1}=\1" \ "extdoc{2}=\2" \ "nicebox{1}=
\1
" \ "mlonly=\if Ocaml" \ "endmlonly=\endif" \ "mlh=\if Ocaml" \ "endmlh=\endif" \ "conly=" \ "ccode{1}=\1" \ "zframe=" OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO # Doxygen has a built-in mapping, but you can override or extend it using this tag. # The format is ext=language, where ext is a file extension, and language is one of # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES TYPEDEF_HIDES_STRUCT = NO SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = YES EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = YES EXTRACT_ANON_NSPACES = YES HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = YES CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = YES SHOW_INCLUDE_FILES = YES FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = NO # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by # doxygen. The layout file controls the global structure of the generated output files # in an output format independent way. The create the layout file that represents # doxygen's defaults, run doxygen with the -l option. You can optionally specify a # file name after the option, if omitted DoxygenLayout.xml will be used as the name # of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text " # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = ../src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.cpp *.h # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = YES # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = YES # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER # are set, an additional index file will be generated that can be used as input for # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated # HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. # For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's # filter section matches. # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = YES # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = YES # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = YES # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be implemented using a PHP enabled web server instead of at the web client using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server based approach is that it scales better to large projects and allows full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = NO USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO LATEX_SOURCE_CODE = NO GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = YES # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED =Z3_ast_opt:=Z3_ast Z3_bool_opt:=Z3_bool Z3_func_interp_opt:=Z3_func_interp Z3_model_opt:=Z3_model __out_opt:=__out # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # Z3_ast_opt Z3_bool_opt Z3_func_interp_opt Z3_model_opt __out_opt # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = NO # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = YES # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = YES # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = YES # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 1000 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = NO # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES z3-4.4.0/doc/update_api_website.cmd0000644000175000017500000000040412540347414016732 0ustar michaelmichaelREM Script for updating the website containing the Z3 API documentation. REM You must be inside the Microsoft network to execute this script, and REM robocopy must be in your PATH. robocopy /S api\html \\research\root\web\external\en-us\UM\redmond\projects\z3z3-4.4.0/doc/README0000644000175000017500000000110612540347414013270 0ustar michaelmichaelAPI documentation ----------------- To generate the API documentation for the C, .NET and Python APIs, we must execute python mk_api_doc.py We must have doxygen installed in our system. The documentation will be stored in the subdirectory './api/html'. The main file is './api/html/index.html' Code documentation ------------------ To generate documentation for the Z3 code, we must execute doxygen z3code.dox We must also have dot installed in our system. The documentation will be store in the subdirectory './code/html'. The main file is './code/html/index.html' z3-4.4.0/doc/website.dox0000644000175000017500000000171412540347414014573 0ustar michaelmichael/** \mainpage An Efficient Theorem Prover Z3 is a high-performance theorem prover being developed at Microsoft Research. The Z3 website moved to http://github.com/z3prover.. The old Z3 websites can be found here and here. This website hosts the automatically generated documentation for the Z3 APIs. - \ref capi - \ref cppapi - .NET API - Java API - Python API (also available in pydoc format) [ML] - Try Z3 online at RiSE4Fun. */ z3-4.4.0/doc/z3api.dox0000644000175000017500000023243012540347414014160 0ustar michaelmichael# Doxyfile 1.8.2 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = Z3 # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = api # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class " \ "The $name widget " \ "The $name file " \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = YES # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. Note that you specify absolute paths here, but also # relative paths, which will be relative from the directory where doxygen is # started. STRIP_FROM_PATH = ".." # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = "beginfaq=
    " \ "faq{2}=
  • \1

    \2
  • " \ "endfaq=
" \ "cmdopt{1}=\arg /\1" \ "ext{1}=.\1" \ "ty{1}=\1" \ "emph{1}=\1" \ "extdoc{2}=\2" \ "nicebox{1}=
\1
" \ "mlonly=\if Ocaml" \ "endmlonly=\endif" \ "mlh=\if Ocaml" \ "endmlh=\endif" \ "conly=" \ "ccode{1}=\1" \ "zframe=" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, # and language is one of the parsers supported by doxygen: IDL, Java, # Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, # C++. For instance to make doxygen treat .inc files as Fortran files (default # is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note # that for custom extensions you also need to set FILE_PATTERNS otherwise the # files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented classes, # or namespaces to their corresponding documentation. Such a link can be # prevented in individual cases by by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter and setter methods for a property. Setting this option to YES (the default) will make doxygen replace the get and set methods by a property in the documentation. This will only work if the methods are indeed getting or setting a simple type. If this is not the case, or you want to show the methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields will be shown inline in the documentation # of the scope in which they are defined (i.e. file, namespace, or group # documentation), provided this scope is documented. If set to NO (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. # SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given # their name and scope. Since this can be an expensive process and often the # same symbol appear multiple times in the code, doxygen keeps a cache of # pre-resolved symbols. If the cache is too small doxygen will become slower. # If the cache is too large, memory is wasted. The cache size is given by this # formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = YES # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = YES # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = NO # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = NO # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = NO # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = NO # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text " # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = ../src/api/dotnet \ ../src/api/java \ ../src/api/c++ \ ./tmp # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = website.dox \ z3_api.h \ z3_algebraic.h \ z3_polynomial.h \ z3_rcf.h \ z3_interp.h \ z3_fpa.h \ z3++.h \ z3py.py \ *.cs \ *.java # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = YES # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If left blank doxygen will # generate a default style sheet. Note that it is recommended to use # HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this # tag will in the future become obsolete. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional # user-defined cascading style sheet that is included after the standard # style sheets created by doxygen. Using this option one can overrule # certain style aspects. This is preferred over using HTML_STYLESHEET # since it does not replace the standard style sheet and is therefor more # robust against future updates. Doxygen will copy the style sheet file to # the output directory. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = YES # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely # identify the documentation publisher. This should be a reverse domain-name # style string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = YES # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. # XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. # XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = YES # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = Z3_ast_opt:=Z3_ast \ Z3_bool_opt:=Z3_bool \ Z3_func_interp_opt:=Z3_func_interp \ Z3_model_opt:=Z3_model \ __out_opt:=__out # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = YES # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = NO # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. # DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = NO # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # managable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = NO # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 1000 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = NO # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = NO z3-4.4.0/.gitignore0000644000175000017500000000250612540347414013640 0ustar michaelmichael*~ *.pyc *.pyo # Ignore callgrind files callgrind.out.* # .hpp files are automatically generated *.hpp .z3-trace # OCaml generated files *.a *.cma *.cmo *.cmi *.cmxa ocamlz3 # Java generated files *.class *.jar # Emacs temp files \#*\# # Directories with generated code and documentation release/* build/* build-dist/* dist/* doc/html/* # GTAGS generated files src/GPATH src/GRTAGS src/GSYMS src/GTAGS src/HTML/* # CSCOPE files src/cscope.in.out src/cscope.out src/cscope.po.out ncscope.out # CEDET files .cproject .project # Commonly used directories for code bld_dbg/* bld_rel/* bld_dbg_x64/* bld_rel_x64/* # Auto generated files. config.log config.status install_tactic.cpp mem_initializer.cpp gparams_register_modules.cpp scripts/config-debug.mk scripts/config-release.mk src/api/api_commands.cpp src/api/api_log_macros.h src/api/api_log_macros.cpp src/api/dll/api_dll.def src/api/dotnet/Enumerations.cs src/api/dotnet/Native.cs src/api/dotnet/Properties/AssemblyInfo.cs src/api/dotnet/Microsoft.Z3.xml src/api/python/z3consts.py src/api/python/z3core.py src/ast/pattern/database.h src/util/version.h src/api/java/Native.cpp src/api/java/Native.java src/api/java/enumerations/*.java src/api/ml/z3native_stubs.c src/api/ml/z3native.ml src/api/ml/z3enums.ml src/api/ml/z3native.mli src/api/ml/z3enums.mli src/api/ml/z3.mllib *.bak doc/api doc/code z3-4.4.0/src/0000755000175000017500000000000012540347414012434 5ustar michaelmichaelz3-4.4.0/src/ast/0000755000175000017500000000000012540347414013223 5ustar michaelmichaelz3-4.4.0/src/ast/bv_decl_plugin.h0000644000175000017500000003643612540347414016364 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bv_decl_plugin.h Abstract: Author: Leonardo de Moura (leonardo) 2008-01-09. Revision History: --*/ #ifndef _BV_DECL_PLUGIN_H_ #define _BV_DECL_PLUGIN_H_ #include"ast.h" enum bv_sort_kind { BV_SORT }; enum bv_op_kind { OP_BV_NUM, OP_BIT1, OP_BIT0, OP_BNEG, OP_BADD, OP_BSUB, OP_BMUL, OP_BSDIV, OP_BUDIV, OP_BSREM, OP_BUREM, OP_BSMOD, // special functions to record the division by 0 cases // these are internal functions OP_BSDIV0, OP_BUDIV0, OP_BSREM0, OP_BUREM0, OP_BSMOD0, // special functions where division by 0 has a fixed interpretation. OP_BSDIV_I, OP_BUDIV_I, OP_BSREM_I, OP_BUREM_I, OP_BSMOD_I, OP_ULEQ, OP_SLEQ, OP_UGEQ, OP_SGEQ, OP_ULT, OP_SLT, OP_UGT, OP_SGT, OP_BAND, OP_BOR, OP_BNOT, OP_BXOR, OP_BNAND, OP_BNOR, OP_BXNOR, OP_CONCAT, OP_SIGN_EXT, OP_ZERO_EXT, OP_EXTRACT, OP_REPEAT, OP_BREDOR, OP_BREDAND, OP_BCOMP, OP_BSHL, OP_BLSHR, OP_BASHR, OP_ROTATE_LEFT, OP_ROTATE_RIGHT, OP_EXT_ROTATE_LEFT, OP_EXT_ROTATE_RIGHT, OP_BUMUL_NO_OVFL, // no unsigned multiplication overflow predicate OP_BSMUL_NO_OVFL, // no signed multiplication overflow predicate OP_BSMUL_NO_UDFL, // no signed multiplication underflow predicate OP_BIT2BOOL, // predicate OP_MKBV, // bools to bv OP_INT2BV, OP_BV2INT, OP_CARRY, OP_XOR3, LAST_BV_OP }; // Assume k is a "div" operator. It returns the div0 uninterpreted function that // models the value of "div" it is underspecified (i.e., when the denominator is zero). inline bv_op_kind get_div0_op(bv_op_kind k) { switch (k) { case OP_BSDIV: return OP_BSDIV0; case OP_BUDIV: return OP_BUDIV0; case OP_BSREM: return OP_BSREM0; case OP_BUREM: return OP_BUREM0; case OP_BSMOD: return OP_BSMOD0; default: UNREACHABLE(); return LAST_BV_OP; } } // Assume decl is the declaration of a "div" operator. It returns the div0 declaration that // models the value of "div" it is underspecified (i.e., when the denominator is zero). inline func_decl * get_div0_decl(ast_manager & m, func_decl * decl) { return m.mk_func_decl(decl->get_family_id(), get_div0_op(static_cast(decl->get_decl_kind())), 0, 0, 1, decl->get_domain()); } class bv_decl_plugin : public decl_plugin { protected: symbol m_bv_sym; symbol m_concat_sym; symbol m_sign_extend_sym; symbol m_zero_extend_sym; symbol m_extract_sym; symbol m_rotate_left_sym; symbol m_rotate_right_sym; symbol m_repeat_sym; symbol m_bit2bool_sym; symbol m_mkbv_sym; func_decl * m_bit0; func_decl * m_bit1; func_decl * m_carry; func_decl * m_xor3; ptr_vector m_bv_sorts; sort * m_int_sort; ptr_vector m_bv_neg; ptr_vector m_bv_add; ptr_vector m_bv_sub; ptr_vector m_bv_mul; ptr_vector m_bv_sdiv; ptr_vector m_bv_udiv; ptr_vector m_bv_srem; ptr_vector m_bv_urem; ptr_vector m_bv_smod; ptr_vector m_bv_sdiv0; ptr_vector m_bv_udiv0; ptr_vector m_bv_srem0; ptr_vector m_bv_urem0; ptr_vector m_bv_smod0; ptr_vector m_bv_sdiv_i; ptr_vector m_bv_udiv_i; ptr_vector m_bv_srem_i; ptr_vector m_bv_urem_i; ptr_vector m_bv_smod_i; ptr_vector m_bv_uleq; ptr_vector m_bv_sleq; ptr_vector m_bv_ugeq; ptr_vector m_bv_sgeq; ptr_vector m_bv_ult; ptr_vector m_bv_slt; ptr_vector m_bv_ugt; ptr_vector m_bv_sgt; ptr_vector m_bv_and; ptr_vector m_bv_or; ptr_vector m_bv_not; ptr_vector m_bv_xor; ptr_vector m_bv_nand; ptr_vector m_bv_nor; ptr_vector m_bv_xnor; ptr_vector m_bv_redor; ptr_vector m_bv_redand; ptr_vector m_bv_comp; ptr_vector m_bv_mul_ovfl; ptr_vector m_bv_smul_ovfl; ptr_vector m_bv_smul_udfl; ptr_vector m_bv_shl; ptr_vector m_bv_lshr; ptr_vector m_bv_ashr; ptr_vector m_ext_rotate_left; ptr_vector m_ext_rotate_right; ptr_vector m_bv2int; ptr_vector m_int2bv; vector > m_bit2bool; ptr_vector m_mkbv; virtual void set_manager(ast_manager * m, family_id id); void mk_bv_sort(unsigned bv_size); sort * get_bv_sort(unsigned bv_size); func_decl * mk_func_decl(decl_kind k, unsigned bv_size); func_decl * mk_binary(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size, bool ac, bool idempotent = false); func_decl * mk_unary(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); func_decl * mk_pred(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); func_decl * mk_reduction(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); func_decl * mk_comp(unsigned bv_size); bool get_bv_size(sort * t, int & result); bool get_bv_size(expr * t, int & result); bool get_concat_size(unsigned arity, sort * const * domain, int & result); bool get_extend_size(unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, int & result); bool get_extract_size(unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, int & result); func_decl * mk_bv2int(unsigned bv_size, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain); func_decl * mk_int2bv(unsigned bv_size, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain); func_decl * mk_bit2bool(unsigned bv_size, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain); func_decl * mk_mkbv(unsigned arity, sort * const * domain); bool get_int2bv_size(unsigned num_parameters, parameter const * parameters, int & result); func_decl * mk_num_decl(unsigned num_parameters, parameter const * parameters, unsigned arity); void get_offset_term(app * a, expr * & t, rational & offset) const; public: bv_decl_plugin(); virtual ~bv_decl_plugin() {} virtual void finalize(); virtual decl_plugin * mk_fresh() { return alloc(bv_decl_plugin); } virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range); virtual bool is_value(app * e) const; virtual bool is_unique_value(app * e) const { return is_value(e); } virtual void get_op_names(svector & op_names, symbol const & logic); virtual void get_sort_names(svector & sort_names, symbol const & logic); virtual bool are_distinct(app* a, app* b) const; virtual expr * get_some_value(sort * s); }; class bv_recognizers { family_id m_afid; public: bv_recognizers(family_id fid):m_afid(fid) {} family_id get_fid() const { return m_afid; } family_id get_family_id() const { return get_fid(); } bool is_numeral(expr const * n, rational & val, unsigned & bv_size) const; bool is_numeral(expr const * n) const { return is_app_of(n, get_fid(), OP_BV_NUM); } bool is_allone(expr const * e) const; bool is_zero(expr const * e) const; bool is_bv_sort(sort const * s) const; bool is_bv(expr const* e) const { return is_bv_sort(get_sort(e)); } bool is_concat(expr const * e) const { return is_app_of(e, get_fid(), OP_CONCAT); } bool is_extract(func_decl const * f) const { return is_decl_of(f, get_fid(), OP_EXTRACT); } bool is_extract(expr const * e) const { return is_app_of(e, get_fid(), OP_EXTRACT); } unsigned get_extract_high(func_decl const * f) const { return f->get_parameter(0).get_int(); } unsigned get_extract_low(func_decl const * f) const { return f->get_parameter(1).get_int(); } unsigned get_extract_high(expr const * n) const { SASSERT(is_extract(n)); return get_extract_high(to_app(n)->get_decl()); } unsigned get_extract_low(expr const * n) const { SASSERT(is_extract(n)); return get_extract_low(to_app(n)->get_decl()); } bool is_extract(expr const * e, unsigned & low, unsigned & high, expr * & b) const; bool is_bv2int(expr const * e, expr * & r) const; bool is_bv_add(expr const * e) const { return is_app_of(e, get_fid(), OP_BADD); } bool is_bv_sub(expr const * e) const { return is_app_of(e, get_fid(), OP_BSUB); } bool is_bv_mul(expr const * e) const { return is_app_of(e, get_fid(), OP_BMUL); } bool is_bv_neg(expr const * e) const { return is_app_of(e, get_fid(), OP_BNEG); } bool is_bv_sdiv(expr const * e) const { return is_app_of(e, get_fid(), OP_BSDIV); } bool is_bv_udiv(expr const * e) const { return is_app_of(e, get_fid(), OP_BUDIV); } bool is_bv_srem(expr const * e) const { return is_app_of(e, get_fid(), OP_BSREM); } bool is_bv_urem(expr const * e) const { return is_app_of(e, get_fid(), OP_BUREM); } bool is_bv_smod(expr const * e) const { return is_app_of(e, get_fid(), OP_BSMOD); } bool is_bv_and(expr const * e) const { return is_app_of(e, get_fid(), OP_BAND); } bool is_bv_or(expr const * e) const { return is_app_of(e, get_fid(), OP_BOR); } bool is_bv_xor(expr const * e) const { return is_app_of(e, get_fid(), OP_BXOR); } bool is_bv_nand(expr const * e) const { return is_app_of(e, get_fid(), OP_BNAND); } bool is_bv_nor(expr const * e) const { return is_app_of(e, get_fid(), OP_BNOR); } bool is_bv_not(expr const * e) const { return is_app_of(e, get_fid(), OP_BNOT); } bool is_bv_ule(expr const * e) const { return is_app_of(e, get_fid(), OP_ULEQ); } bool is_bv_sle(expr const * e) const { return is_app_of(e, get_fid(), OP_SLEQ); } bool is_bit2bool(expr const * e) const { return is_app_of(e, get_fid(), OP_BIT2BOOL); } bool is_bv2int(expr const* e) const { return is_app_of(e, get_fid(), OP_BV2INT); } bool is_int2bv(expr const* e) const { return is_app_of(e, get_fid(), OP_INT2BV); } bool is_mkbv(expr const * e) const { return is_app_of(e, get_fid(), OP_MKBV); } bool is_bv_ashr(expr const * e) const { return is_app_of(e, get_fid(), OP_BASHR); } bool is_bv_lshr(expr const * e) const { return is_app_of(e, get_fid(), OP_BLSHR); } bool is_bv_shl(expr const * e) const { return is_app_of(e, get_fid(), OP_BSHL); } bool is_sign_ext(expr const * e) const { return is_app_of(e, get_fid(), OP_SIGN_EXT); } MATCH_BINARY(is_bv_add); MATCH_BINARY(is_bv_mul); MATCH_BINARY(is_bv_sle); MATCH_BINARY(is_bv_ule); MATCH_BINARY(is_bv_shl); rational norm(rational const & val, unsigned bv_size, bool is_signed) const ; rational norm(rational const & val, unsigned bv_size) const { return norm(val, bv_size, false); } bool has_sign_bit(rational const & n, unsigned bv_size) const; bool mult_inverse(rational const & n, unsigned bv_size, rational & result); }; class bv_util : public bv_recognizers { ast_manager & m_manager; bv_decl_plugin * m_plugin; public: bv_util(ast_manager & m); ast_manager & get_manager() const { return m_manager; } app * mk_numeral(rational const & val, sort* s); app * mk_numeral(rational const & val, unsigned bv_size); app * mk_numeral(uint64 u, unsigned bv_size) { return mk_numeral(rational(u, rational::ui64()), bv_size); } sort * mk_sort(unsigned bv_size); unsigned get_bv_size(sort const * s) const { SASSERT(is_bv_sort(s)); return static_cast(s->get_parameter(0).get_int()); } unsigned get_bv_size(expr const * n) const { return get_bv_size(m_manager.get_sort(n)); } app * mk_ule(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_ULEQ, arg1, arg2); } app * mk_sle(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_SLEQ, arg1, arg2); } app * mk_extract(unsigned high, unsigned low, expr * n) { parameter params[2] = { parameter(high), parameter(low) }; return m_manager.mk_app(get_fid(), OP_EXTRACT, 2, params, 1, &n); } app * mk_concat(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_CONCAT, num, args); } app * mk_concat(expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_concat(2, args); } app * mk_bv_or(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BOR, num, args); } app * mk_bv_not(expr * arg) { return m_manager.mk_app(get_fid(), OP_BNOT, arg); } app * mk_bv_xor(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BXOR, num, args); } app * mk_bv_neg(expr * arg) { return m_manager.mk_app(get_fid(), OP_BNEG, arg); } app * mk_bv_urem(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_BUREM, arg1, arg2); } app * mk_bv_srem(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_BSREM, arg1, arg2); } app * mk_bv_add(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_BADD, arg1, arg2); } app * mk_bv_sub(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_BSUB, arg1, arg2); } app * mk_bv_mul(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_BMUL, arg1, arg2); } app * mk_zero_extend(unsigned n, expr* e) { parameter p(n); return m_manager.mk_app(get_fid(), OP_ZERO_EXT, 1, &p, 1, &e); } app * mk_sign_extend(unsigned n, expr* e) { parameter p(n); return m_manager.mk_app(get_fid(), OP_SIGN_EXT, 1, &p, 1, &e); } app * mk_bv_shl(expr* arg1, expr* arg2) { return m_manager.mk_app(get_fid(), OP_BSHL, arg1, arg2); } app * mk_bv_ashr(expr* arg1, expr* arg2) { return m_manager.mk_app(get_fid(), OP_BASHR, arg1, arg2); } app * mk_bv_lshr(expr* arg1, expr* arg2) { return m_manager.mk_app(get_fid(), OP_BLSHR, arg1, arg2); } app * mk_bv2int(expr* e); app * mk_bvsmul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_OVFL, n, m); } app * mk_bvsmul_no_udfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_UDFL, n, m); } app * mk_bvumul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUMUL_NO_OVFL, n, m); } app * mk_bv(unsigned n, expr* const* es) { return m_manager.mk_app(get_fid(), OP_MKBV, n, es); } }; #endif /* _BV_DECL_PLUGIN_H_ */ z3-4.4.0/src/ast/ast_lt.cpp0000644000175000017500000001430612540347414015221 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ast_lt.cpp Abstract: Total order on ASTs that does not depend on the internal ids. Author: Leonardo de Moura (leonardo) 2011-04-08 Revision History: --*/ #include"ast.h" #define check_symbol(S1,S2) if (S1 != S2) return lt(S1,S2) #define check_value(V1,V2) if (V1 != V2) return V1 < V2 #define check_bool(B1,B2) if (B1 != B2) return !B1 && B2 #define check_ptr(P1,P2) if (!P1 && P2) return true; if (P1 && !P2) return false #define check_ast(T1,T2) if (T1 != T2) { n1 = T1; n2 = T2; goto start; } #define check_parameter(p1, p2) { \ check_value(p1.get_kind(), p2.get_kind()); \ switch (p1.get_kind()) { \ case parameter::PARAM_INT: \ check_value(p1.get_int(), p2.get_int()); \ break; \ case parameter::PARAM_AST: \ check_ast(p1.get_ast(), p2.get_ast()); \ break; \ case parameter::PARAM_SYMBOL: \ check_symbol(p1.get_symbol(), p2.get_symbol()); \ break; \ case parameter::PARAM_RATIONAL: \ check_value(p1.get_rational(), p2.get_rational()); \ break; \ case parameter::PARAM_DOUBLE: \ check_value(p1.get_double(), p2.get_double()); \ break; \ case parameter::PARAM_EXTERNAL: \ check_value(p1.get_ext_id(), p2.get_ext_id()); \ break; \ default: \ UNREACHABLE(); \ break; \ } \ } bool lt(ast * n1, ast * n2) { unsigned num; start: if (n1 == n2) return false; check_value(n1->get_kind(), n2->get_kind()); switch(n1->get_kind()) { case AST_SORT: check_symbol(to_sort(n1)->get_name(), to_sort(n2)->get_name()); check_value(to_sort(n1)->get_num_parameters(), to_sort(n2)->get_num_parameters()); num = to_sort(n1)->get_num_parameters(); SASSERT(num > 0); for (unsigned i = 0; i < num; i++) { parameter p1 = to_sort(n1)->get_parameter(i); parameter p2 = to_sort(n2)->get_parameter(i); check_parameter(p1, p2); } UNREACHABLE(); return false; case AST_FUNC_DECL: check_symbol(to_func_decl(n1)->get_name(), to_func_decl(n2)->get_name()); check_value(to_func_decl(n1)->get_arity(), to_func_decl(n2)->get_arity()); check_value(to_func_decl(n1)->get_num_parameters(), to_func_decl(n2)->get_num_parameters()); num = to_func_decl(n1)->get_num_parameters(); for (unsigned i = 0; i < num; i++) { parameter p1 = to_func_decl(n1)->get_parameter(i); parameter p2 = to_func_decl(n2)->get_parameter(i); check_parameter(p1, p2); } num = to_func_decl(n1)->get_arity(); for (unsigned i = 0; i < num; i++) { ast * d1 = to_func_decl(n1)->get_domain(i); ast * d2 = to_func_decl(n2)->get_domain(i); check_ast(d1, d2); } n1 = to_func_decl(n1)->get_range(); n2 = to_func_decl(n2)->get_range(); goto start; case AST_APP: check_value(to_app(n1)->get_num_args(), to_app(n2)->get_num_args()); check_value(to_app(n1)->get_depth(), to_app(n2)->get_depth()); check_ast(to_app(n1)->get_decl(), to_app(n2)->get_decl()); num = to_app(n1)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg1 = to_app(n1)->get_arg(i); expr * arg2 = to_app(n2)->get_arg(i); check_ast(arg1, arg2); } UNREACHABLE(); return false; case AST_QUANTIFIER: check_bool(to_quantifier(n1)->is_forall(), to_quantifier(n2)->is_forall()); check_value(to_quantifier(n1)->get_num_decls(), to_quantifier(n2)->get_num_decls()); check_value(to_quantifier(n1)->get_num_patterns(), to_quantifier(n2)->get_num_patterns()); check_value(to_quantifier(n1)->get_num_no_patterns(), to_quantifier(n2)->get_num_no_patterns()); check_value(to_quantifier(n1)->get_weight(), to_quantifier(n2)->get_weight()); num = to_quantifier(n1)->get_num_decls(); for (unsigned i = 0; i < num; i++) { check_symbol(to_quantifier(n1)->get_decl_name(i), to_quantifier(n2)->get_decl_name(i)); check_ast(to_quantifier(n1)->get_decl_sort(i), to_quantifier(n2)->get_decl_sort(i)); } num = to_quantifier(n1)->get_num_patterns(); for (unsigned i = 0; i < num; i++) { check_ast(to_quantifier(n1)->get_pattern(i), to_quantifier(n2)->get_pattern(i)); } num = to_quantifier(n1)->get_num_no_patterns(); for (unsigned i = 0; i < num; i++) { check_ast(to_quantifier(n1)->get_no_pattern(i), to_quantifier(n2)->get_no_pattern(i)); } n1 = to_quantifier(n1)->get_expr(); n2 = to_quantifier(n2)->get_expr(); goto start; case AST_VAR: check_value(to_var(n1)->get_idx(), to_var(n2)->get_idx()); n1 = to_var(n1)->get_sort(); n2 = to_var(n2)->get_sort(); goto start; default: UNREACHABLE(); return false; } } bool is_sorted(unsigned num, expr * const * ns) { for (unsigned i = 1; i < num; i++) { ast * prev = ns[i-1]; ast * curr = ns[i]; if (lt(curr, prev)) return false; } return true; } bool lex_lt(unsigned num, ast * const * n1, ast * const * n2) { for (unsigned i = 0; i < num; i ++) { if (n1[i] == n2[i]) continue; return lt(n1[i], n2[i]); } return false; } z3-4.4.0/src/ast/ast_printer.cpp0000644000175000017500000000375612540347414016274 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ast_printer.cpp Abstract: Abstract AST printer Author: Leonardo de Moura (leonardo) 2012-10-21 Revision History: --*/ #include"ast_printer.h" #include"pp.h" class simple_ast_printer_context : public ast_printer_context { ast_manager & m_manager; scoped_ptr m_env; smt2_pp_environment_dbg & env() const { return *(m_env.get()); } public: simple_ast_printer_context(ast_manager & m):m_manager(m) { m_env = alloc(smt2_pp_environment_dbg, m); } virtual ~simple_ast_printer_context() {} ast_manager & m() const { return m_manager; } virtual ast_manager & get_ast_manager() { return m_manager; } virtual void display(std::ostream & out, sort * s, unsigned indent = 0) const { out << mk_ismt2_pp(s, m(), indent); } virtual void display(std::ostream & out, expr * n, unsigned indent = 0) const { out << mk_ismt2_pp(n, m(), indent); } virtual void display(std::ostream & out, func_decl * f, unsigned indent = 0) const { out << f->get_name(); } virtual void pp(sort * s, format_ns::format_ref & r) const { mk_smt2_format(s, env(), params_ref(), r); } virtual void pp(func_decl * f, format_ns::format_ref & r) const { mk_smt2_format(f, env(), params_ref(), r); } virtual void pp(expr * n, format_ns::format_ref & r) const { sbuffer buf; mk_smt2_format(n, env(), params_ref(), 0, 0, r, buf); } virtual void pp(expr * n, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names) const { mk_smt2_format(n, env(), params_ref(), num_vars, var_prefix, r, var_names); } virtual void display(std::ostream & out, expr * n, unsigned indent, unsigned num_vars, char const * var_prefix, sbuffer & var_names) const { NOT_IMPLEMENTED_YET(); } }; ast_printer_context * mk_simple_ast_printer_context(ast_manager & m) { return alloc(simple_ast_printer_context, m); } z3-4.4.0/src/ast/format.cpp0000644000175000017500000001576612540347414015236 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: format.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-20. Revision History: --*/ #include"format.h" #include"recurse_expr_def.h" namespace format_ns { class format_decl_plugin : public decl_plugin { protected: sort * m_format_sort; symbol m_nil; symbol m_string; symbol m_indent; symbol m_compose; symbol m_choice; symbol m_line_break; symbol m_line_break_ext; virtual void set_manager(ast_manager * m, family_id id) { SASSERT(m->is_format_manager()); decl_plugin::set_manager(m, id); m_format_sort = m->mk_sort(symbol("format"), sort_info(id, FORMAT_SORT)); m->inc_ref(m_format_sort); } public: format_decl_plugin(): m_format_sort(0), m_nil("nil"), m_string("string"), m_indent("indent"), m_compose("compose"), m_choice("choice"), m_line_break("cr"), m_line_break_ext("cr++") { } virtual ~format_decl_plugin() {} virtual void finalize() { if (m_format_sort) m_manager->dec_ref(m_format_sort); } virtual decl_plugin * mk_fresh() { return alloc(format_decl_plugin); } virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters) { SASSERT(k == FORMAT_SORT); return m_format_sort; } virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { switch (k) { case OP_NIL: return m_manager->mk_func_decl(m_nil, arity, domain, m_format_sort, func_decl_info(m_family_id, OP_NIL)); case OP_STRING: return m_manager->mk_func_decl(m_string, arity, domain, m_format_sort, func_decl_info(m_family_id, OP_STRING, num_parameters, parameters)); case OP_INDENT: return m_manager->mk_func_decl(m_indent, arity, domain, m_format_sort, func_decl_info(m_family_id, OP_INDENT, num_parameters, parameters)); case OP_COMPOSE: return m_manager->mk_func_decl(m_compose, arity, domain, m_format_sort, func_decl_info(m_family_id, OP_COMPOSE)); case OP_CHOICE: return m_manager->mk_func_decl(m_choice, arity, domain, m_format_sort, func_decl_info(m_family_id, OP_CHOICE)); case OP_LINE_BREAK: return m_manager->mk_func_decl(m_line_break, arity, domain, m_format_sort, func_decl_info(m_family_id, OP_LINE_BREAK)); case OP_LINE_BREAK_EXT: return m_manager->mk_func_decl(m_line_break_ext, arity, domain, m_format_sort, func_decl_info(m_family_id, OP_LINE_BREAK_EXT, num_parameters, parameters)); default: return 0; } } }; family_id get_format_family_id(ast_manager & m) { symbol f("format"); if (!fm(m).has_plugin(f)) fm(m).register_plugin(f, alloc(format_decl_plugin)); return fm(m).mk_family_id(f); } static family_id fid(ast_manager & m) { return get_format_family_id(m); } sort * fsort(ast_manager & m) { return fm(m).mk_sort(fid(m), FORMAT_SORT); } struct flat_visitor { ast_manager & m_manager; family_id m_fid; flat_visitor(ast_manager & m): m_manager(fm(m)), m_fid(fid(m)) { SASSERT(m_manager.is_format_manager()); } format * visit(var *) { UNREACHABLE(); return 0; } format * visit(quantifier * q, format *, format * const *, format * const *) { UNREACHABLE(); return 0; } format * visit(format * n, format * const * children) { if (is_app_of(n, m_fid, OP_LINE_BREAK)) return mk_string(m_manager, " "); else if (is_app_of(n, m_fid, OP_LINE_BREAK_EXT)) return mk_string(m_manager, n->get_decl()->get_parameter(0).get_symbol().bare_str()); else if (is_app_of(n, m_fid, OP_CHOICE)) return to_app(n->get_arg(0)); else return m_manager.mk_app(n->get_decl(), n->get_num_args(), (expr *const*) children); } }; format * flat(ast_manager & m, format * f) { flat_visitor v(m); recurse_expr r(v); return r(f); } format * mk_string(ast_manager & m, char const * str) { symbol s(str); parameter p(s); return fm(m).mk_app(fid(m), OP_STRING, 1, &p, 0, 0); } format * mk_int(ast_manager & m, int i) { static char buffer[128]; #ifdef _WINDOWS sprintf_s(buffer, ARRAYSIZE(buffer), "%d", i); #else sprintf(buffer, "%d", i); #endif return mk_string(m, buffer); } format * mk_unsigned(ast_manager & m, unsigned u) { static char buffer[128]; #ifdef _WINDOWS sprintf_s(buffer, ARRAYSIZE(buffer), "%u", u); #else sprintf(buffer, "%u", u); #endif return mk_string(m, buffer); } format * mk_indent(ast_manager & m, unsigned i, format * f) { parameter p(i); expr * e = static_cast(f); return fm(m).mk_app(fid(m), OP_INDENT, 1, &p, 1, &e); } format * mk_line_break(ast_manager & m) { return fm(m).mk_app(fid(m), OP_LINE_BREAK); } format * mk_choice(ast_manager & m, format * f1, format * f2) { return fm(m).mk_app(fid(m), OP_CHOICE, f1, f2); } format * mk_group(ast_manager & m, format * f) { return mk_choice(m, flat(m, f), f); } format * mk_compose(ast_manager & m, unsigned num_children, format * const * children) { return fm(m).mk_app(fid(m), OP_COMPOSE, num_children, (expr * const *) children); } format * mk_compose(ast_manager & m, format * f1, format * f2) { return fm(m).mk_app(fid(m), OP_COMPOSE, f1, f2); } format * mk_compose(ast_manager & m, format * f1, format * f2, format * f3) { return fm(m).mk_app(fid(m), OP_COMPOSE, f1, f2, f3); } format * mk_compose(ast_manager & m, format * f1, format * f2, format * f3, format * f4) { expr * f[4] = { f1, f2, f3, f4 }; return fm(m).mk_app(fid(m), OP_COMPOSE, 4, f); } format * mk_nil(ast_manager & m) { return fm(m).mk_app(fid(m), OP_NIL); } }; z3-4.4.0/src/ast/ast_printer.h0000644000175000017500000000327512540347414015735 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ast_printer.h Abstract: Abstract AST printer Author: Leonardo de Moura (leonardo) 2012-10-21 Revision History: --*/ #ifndef _AST_PRINTER_H_ #define _AST_PRINTER_H_ #include"ast.h" #include"ast_smt2_pp.h" class ast_printer { public: virtual ~ast_printer() {} virtual void pp(sort * s, format_ns::format_ref & r) const { UNREACHABLE(); } virtual void pp(func_decl * f, format_ns::format_ref & r) const { UNREACHABLE(); } virtual void pp(expr * n, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names) const { UNREACHABLE(); } virtual void pp(expr * n, format_ns::format_ref & r) const { UNREACHABLE(); } virtual void display(std::ostream & out, sort * s, unsigned indent = 0) const { out << "#" << s->get_id() << "\n"; } virtual void display(std::ostream & out, expr * n, unsigned indent, unsigned num_vars, char const * var_prefix, sbuffer & var_names) const { out << "#" << n->get_id() << "\n"; } virtual void display(std::ostream & out, expr * n, unsigned indent = 0) const { out << "#" << n->get_id() << "\n"; } virtual void display(std::ostream & out, func_decl * f, unsigned indent = 0) const { out << "#" << f->get_id() << "\n"; } }; class ast_printer_context : public ast_printer { public: virtual ~ast_printer_context() {} virtual ast_manager & get_ast_manager() = 0; virtual std::ostream & regular_stream() { return std::cout; } virtual std::ostream & diagnostic_stream() { return std::cerr; } }; ast_printer_context * mk_simple_ast_printer_context(ast_manager & m); #endif z3-4.4.0/src/ast/dl_decl_plugin.h0000644000175000017500000002130312540347414016337 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_decl_plugin.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2010-04-10 Revision History: --*/ #ifndef _DL_DECL_PLUGIN_H_ #define _DL_DECL_PLUGIN_H_ #include"ast.h" #include "arith_decl_plugin.h" #include "bv_decl_plugin.h" namespace datalog { enum dl_sort_kind { DL_RELATION_SORT, DL_FINITE_SORT, DL_RULE_SORT }; enum dl_op_kind { OP_RA_STORE, OP_RA_EMPTY, OP_RA_IS_EMPTY, OP_RA_JOIN, OP_RA_UNION, OP_RA_WIDEN, OP_RA_PROJECT, OP_RA_FILTER, OP_RA_NEGATION_FILTER, OP_RA_RENAME, OP_RA_COMPLEMENT, OP_RA_SELECT, OP_RA_CLONE, OP_DL_CONSTANT, OP_DL_LT, OP_DL_REP, OP_DL_ABS, OP_DL_MIN, LAST_RA_OP }; class dl_decl_plugin : public decl_plugin { symbol m_store_sym; symbol m_empty_sym; symbol m_is_empty_sym; symbol m_join_sym; symbol m_union_sym; symbol m_widen_sym; symbol m_project_sym; symbol m_filter_sym; symbol m_negation_filter_sym; symbol m_rename_sym; symbol m_complement_sym; symbol m_select_sym; symbol m_clone_sym; symbol m_num_sym; symbol m_lt_sym; symbol m_le_sym; symbol m_rule_sym; symbol m_min_sym; bool check_bounds(char const* msg, unsigned low, unsigned up, unsigned val) const; bool check_domain(unsigned low, unsigned up, unsigned val) const; bool check_params(unsigned low, unsigned up, unsigned val) const; bool is_rel_sort(sort* s); bool is_rel_sort(sort* s, ptr_vector& sorts); bool is_fin_sort(sort* r); func_decl * mk_store_select(decl_kind k, unsigned arity, sort * const * domain); func_decl * mk_empty(parameter const& p); func_decl * mk_is_empty(sort* r); func_decl * mk_join(unsigned num_params, parameter const* params, sort* r1, sort* r2); func_decl * mk_unionw(decl_kind k, sort* s1, sort* s2); func_decl * mk_project(unsigned num_params, parameter const * params, sort* r); func_decl * mk_filter(parameter const& p, sort* r); func_decl * mk_rename(unsigned num_params, parameter const * params, sort* r); func_decl * mk_complement(sort* r); func_decl * mk_negation_filter(unsigned num_params, parameter const* params, sort* r1, sort* r2); func_decl * mk_constant(parameter const* params); func_decl * mk_compare(decl_kind k, symbol const& sym, sort*const* domain); func_decl * mk_clone(sort* r); func_decl * mk_rule(unsigned arity); func_decl * mk_min(decl_kind k, unsigned num_parameters, parameter const * parameters); sort * mk_finite_sort(unsigned num_params, parameter const* params); sort * mk_relation_sort(unsigned num_params, parameter const* params); sort * mk_rule_sort(); public: /** Is \c decl a min aggregation function? */ static bool is_aggregate(const func_decl* const decl) { return decl->get_decl_kind() == OP_DL_MIN; } /** \pre: is_aggregate(aggregate) \returns function declaration of predicate which is subject to min aggregation function */ static func_decl * min_func_decl(const func_decl* const aggregate) { SASSERT(is_aggregate(aggregate)); parameter const & relation_parameter = aggregate->get_parameter(0); return to_func_decl(relation_parameter.get_ast()); } /** \pre: is_aggregate(aggregate) \returns column identifier (starting at zero) which is minimized by aggregation function */ static unsigned min_col(const func_decl* const aggregate) { SASSERT(is_aggregate(aggregate)); return (unsigned)aggregate->get_parameter(1).get_int(); } /** \pre: is_aggregate(aggregate) \returns column identifiers for the "group by" in the given min aggregation function */ static unsigned_vector group_by_cols(const func_decl* const aggregate) { SASSERT(is_aggregate(aggregate)); unsigned _min_col = min_col(aggregate); if (aggregate->get_arity() == 0U) return unsigned_vector(); unsigned col_num = 0; unsigned_vector cols(aggregate->get_arity() - 1U); for (unsigned i = 0; i < cols.size(); ++i, ++col_num) { if (col_num == _min_col) ++col_num; cols[i] = col_num; } return cols; } dl_decl_plugin(); virtual ~dl_decl_plugin() {} virtual decl_plugin * mk_fresh() { return alloc(dl_decl_plugin); } // // Contract for sort DL_RELATION_SORT // parameters[0] - 1st dimension // ... // parameters[n-1] - nth dimension // // Contract for sort DL_FINITE_SORT // parameters[0] - name // parameters[1] - uint64 // virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); // // Contract for func_decl: // parameters[0] - array sort // Contract for OP_DL_CONSTANT: // parameters[0] - rational containing uint64 with constant value // parameters[1] - a DL_FINITE_SORT sort of the constant // Contract for others: // no parameters virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); virtual void get_op_names(svector & op_names, symbol const & logic); virtual void get_sort_names(svector & sort_names, symbol const & logic); virtual bool is_value(app * e) const { return is_app_of(e, m_family_id, OP_DL_CONSTANT); } virtual bool is_unique_value(app * e) const { return is_value(e); } }; class dl_decl_util { /** Some plugins need to be registered in the ast_manager before we create some of the member objects (the bv_util requires bv plugin installed). Doing this in the constructor of the dl_decl_plugin object is too late (as member objects are created before we get into the constructor), so we have this auxiliary object that is the first object of the context, so that it gets created first. It's only purpose is that in its constructor the required plugins are added to the ast manager. */ class ast_plugin_registrator { public: ast_plugin_registrator(ast_manager& m); }; ast_plugin_registrator m_plugin_registrator; ast_manager& m; arith_util m_arith; bv_util m_bv; family_id m_fid; public: dl_decl_util(ast_manager& m); // create a constant belonging to a given finite domain. // the options include the DL_FINITE_SORT, BV_SORT, and BOOL_SORT app* mk_numeral(uint64 value, sort* s); app* mk_lt(expr* a, expr* b); app* mk_le(expr* a, expr* b); bool is_lt(const expr* a) const { return is_app_of(a, m_fid, OP_DL_LT); } bool is_numeral(const expr* c) const { return is_app_of(c, m_fid, OP_DL_CONSTANT); } bool is_numeral(const expr* e, uint64& v) const; // // Utilities for extracting constants // from bit-vectors and finite domains. // bool is_numeral_ext(expr* c, uint64& v) const; bool is_numeral_ext(expr* c) const; sort* mk_sort(const symbol& name, uint64 domain_size); bool try_get_size(const sort *, uint64& size) const; bool is_finite_sort(sort* s) const { return is_sort_of(s, m_fid, DL_FINITE_SORT); } bool is_finite_sort(expr* e) const { return is_finite_sort(m.get_sort(e)); } bool is_rule_sort(sort* s) const { return is_sort_of(s, m_fid, DL_RULE_SORT); } sort* mk_rule_sort(); app* mk_rule(symbol const& name, unsigned num_args = 0, expr* const* args = 0); app* mk_fact(symbol const& name) { return mk_rule(name, 0, 0); } ast_manager& get_manager() const { return m; } family_id get_family_id() const { return m_fid; } }; }; #endif /* _DL_DECL_PLUGIN_H_ */ z3-4.4.0/src/ast/pp.cpp0000644000175000017500000001050412540347414014346 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pp.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-20. Revision History: --*/ #include"pp.h" #include"pp_params.hpp" using namespace format_ns; static std::pair space_upto_line_break(ast_manager & m, format * f) { unsigned r; SASSERT(f->get_family_id() == fm(m).get_family_id("format")); decl_kind k = f->get_decl_kind(); switch(k) { case OP_STRING: { size_t len = strlen(f->get_decl()->get_parameter(0).get_symbol().bare_str()); return std::make_pair(static_cast(len), false); } case OP_CHOICE: return space_upto_line_break(m, to_app(f->get_arg(0))); case OP_COMPOSE: r = 0; for (unsigned i = 0; i < f->get_num_args(); i++) { std::pair pair = space_upto_line_break(m, to_app(f->get_arg(i))); r += pair.first; if (pair.second) return std::make_pair(r, true); } return std::make_pair(r, false); case OP_INDENT: return space_upto_line_break(m, to_app(f->get_arg(0))); case OP_LINE_BREAK: case OP_LINE_BREAK_EXT: return std::make_pair(0, true); default: return std::make_pair(0, false); } } inline bool fits(ast_manager & m, format * f, unsigned space_left) { unsigned s = space_upto_line_break(m, f).first; TRACE("fits", tout << "s: " << s << " space_left " << space_left << "\n";); return s <= space_left; } void pp(std::ostream & out, format * f, ast_manager & m, params_ref const & _p) { pp_params p(_p); unsigned max_width = p.max_width(); unsigned max_ribbon = p.max_ribbon(); unsigned max_num_lines = p.max_num_lines(); unsigned max_indent = p.max_indent(); bool bounded = p.bounded(); bool single_line = p.single_line(); unsigned pos = 0; unsigned ribbon_pos = 0; unsigned line = 0; unsigned len; unsigned i; int space_left; svector > todo; todo.push_back(std::make_pair(f, 0)); app_ref space(mk_string(m, " "), fm(m)); while (!todo.empty()) { if (line >= max_num_lines) return; std::pair pair = todo.back(); format * f = pair.first; unsigned indent = pair.second; todo.pop_back(); SASSERT(f->get_family_id() == fm(m).get_family_id("format")); switch (f->get_decl_kind()) { case OP_STRING: if (bounded && pos > max_width) break; len = static_cast(strlen(f->get_decl()->get_parameter(0).get_symbol().bare_str())); if (bounded && pos + len > max_width) { out << "..."; break; } pos += len; ribbon_pos += len; out << f->get_decl()->get_parameter(0).get_symbol(); break; case OP_INDENT: todo.push_back(std::make_pair(to_app(f->get_arg(0)), std::min(indent + f->get_decl()->get_parameter(0).get_int(), max_indent))); break; case OP_COMPOSE: i = f->get_num_args(); while (i > 0) { --i; todo.push_back(std::make_pair(to_app(f->get_arg(i)), indent)); } break; case OP_CHOICE: space_left = std::min(max_width - pos, max_ribbon - pos); if (space_left > 0 && fits(m, to_app(f->get_arg(0)), space_left)) todo.push_back(std::make_pair(to_app(f->get_arg(0)), indent)); else todo.push_back(std::make_pair(to_app(f->get_arg(1)), indent)); break; case OP_LINE_BREAK: case OP_LINE_BREAK_EXT: if (single_line) { todo.push_back(std::make_pair(space, indent)); break; } pos = indent; ribbon_pos = 0; line++; if (line < max_num_lines) { out << "\n"; for (unsigned i = 0; i < indent; i++) out << " "; } else out << "...\n"; break; default: break; } } } z3-4.4.0/src/ast/fpa/0000755000175000017500000000000012540347414013771 5ustar michaelmichaelz3-4.4.0/src/ast/fpa/fpa2bv_converter.h0000644000175000017500000002152212540347414017413 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_converter.h Abstract: Conversion routines for Floating Point -> Bit-Vector Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #ifndef _FPA2BV_CONVERTER_ #define _FPA2BV_CONVERTER_ #include"ast.h" #include"obj_hashtable.h" #include"ref_util.h" #include"fpa_decl_plugin.h" #include"bv_decl_plugin.h" #include"basic_simplifier_plugin.h" typedef enum { BV_RM_TIES_TO_EVEN, BV_RM_TIES_TO_AWAY, BV_RM_TO_POSITIVE, BV_RM_TO_NEGATIVE, BV_RM_TO_ZERO = 4 } BV_RM_VAL; struct func_decl_triple { func_decl_triple () { f_sgn = 0; f_sig = 0; f_exp = 0; } func_decl_triple (func_decl * sgn, func_decl * sig, func_decl * exp) { f_sgn = sgn; f_sig = sig; f_exp = exp; } func_decl * f_sgn; func_decl * f_sig; func_decl * f_exp; }; class fpa2bv_converter { protected: ast_manager & m; basic_simplifier_plugin m_simp; fpa_util m_util; bv_util m_bv_util; arith_util m_arith_util; mpf_manager & m_mpf_manager; unsynch_mpz_manager & m_mpz_manager; fpa_decl_plugin * m_plugin; bool m_hi_fp_unspecified; obj_map m_const2bv; obj_map m_rm_const2bv; obj_map m_uf2bvuf; obj_map m_uf23bvuf; public: fpa2bv_converter(ast_manager & m); ~fpa2bv_converter(); fpa_util & fu() { return m_util; } bv_util & bu() { return m_bv_util; } arith_util & au() { return m_arith_util; } bool is_float(sort * s) { return m_util.is_float(s); } bool is_float(expr * e) { return is_app(e) && m_util.is_float(to_app(e)->get_decl()->get_range()); } bool is_rm(expr * e) { return m_util.is_rm(e); } bool is_rm(sort * s) { return m_util.is_rm(s); } bool is_float_family(func_decl * f) { return f->get_family_id() == m_util.get_family_id(); } void mk_fp(expr * sign, expr * exponent, expr * significand, expr_ref & result); void mk_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void split_fp(expr * e, expr * & sgn, expr * & exp, expr * & sig) const; void split_fp(expr * e, expr_ref & sgn, expr_ref & exp, expr_ref & sig) const; void mk_eq(expr * a, expr * b, expr_ref & result); void mk_ite(expr * c, expr * t, expr * f, expr_ref & result); void mk_distinct(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_rounding_mode(func_decl * f, expr_ref & result); void mk_numeral(func_decl * f, unsigned num, expr * const * args, expr_ref & result); virtual void mk_const(func_decl * f, expr_ref & result); virtual void mk_rm_const(func_decl * f, expr_ref & result); void mk_uninterpreted_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_var(unsigned base_inx, sort * srt, expr_ref & result); void mk_pinf(func_decl * f, expr_ref & result); void mk_ninf(func_decl * f, expr_ref & result); void mk_nan(func_decl * f, expr_ref & result); void mk_nzero(func_decl *f, expr_ref & result); void mk_pzero(func_decl *f, expr_ref & result); void mk_add(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_sub(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_neg(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_mul(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_div(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_rem(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_abs(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_min(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_max(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_fma(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_sqrt(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_round_to_integral(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_float_eq(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_float_lt(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_float_gt(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_float_le(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_float_ge(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_zero(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_nzero(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_pzero(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_negative(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_positive(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_nan(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_inf(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_normal(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_subnormal(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_fp_float(func_decl * f, sort * s, expr * rm, expr * x, expr_ref & result); void mk_to_fp_signed(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_fp_unsigned(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr * x, expr_ref & result); void mk_to_fp_real_int(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_ubv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_sbv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_real(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void set_unspecified_fp_hi(bool v) { m_hi_fp_unspecified = v; } expr_ref mk_to_ubv_unspecified(unsigned width); expr_ref mk_to_sbv_unspecified(unsigned width); expr_ref mk_to_real_unspecified(); obj_map const & const2bv() const { return m_const2bv; } obj_map const & rm_const2bv() const { return m_rm_const2bv; } obj_map const & uf2bvuf() const { return m_uf2bvuf; } obj_map const & uf23bvuf() const { return m_uf23bvuf; } void reset(void); void dbg_decouple(const char * prefix, expr_ref & e); expr_ref_vector m_extra_assertions; protected: void mk_one(func_decl *f, expr_ref sign, expr_ref & result); void mk_is_nan(expr * e, expr_ref & result); void mk_is_inf(expr * e, expr_ref & result); void mk_is_pinf(expr * e, expr_ref & result); void mk_is_ninf(expr * e, expr_ref & result); void mk_is_pos(expr * e, expr_ref & result); void mk_is_neg(expr * e, expr_ref & result); void mk_is_zero(expr * e, expr_ref & result); void mk_is_nzero(expr * e, expr_ref & result); void mk_is_pzero(expr * e, expr_ref & result); void mk_is_denormal(expr * e, expr_ref & result); void mk_is_normal(expr * e, expr_ref & result); void mk_is_rm(expr * e, BV_RM_VAL rm, expr_ref & result); void mk_top_exp(unsigned sz, expr_ref & result); void mk_bot_exp(unsigned sz, expr_ref & result); void mk_min_exp(unsigned ebits, expr_ref & result); void mk_max_exp(unsigned ebits, expr_ref & result); void mk_leading_zeros(expr * e, unsigned max_bits, expr_ref & result); void mk_bias(expr * e, expr_ref & result); void mk_unbias(expr * e, expr_ref & result); void unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & lz, bool normalize); void round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & result); expr_ref mk_rounding_decision(expr * rm, expr * sgn, expr * last, expr * round, expr * sticky); void 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); app * mk_fresh_const(char const * prefix, unsigned sz); }; #endif z3-4.4.0/src/ast/fpa/fpa2bv_converter.cpp0000644000175000017500000043765412540347414017767 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_converter.cpp Abstract: Conversion routines for Floating Point -> Bit-Vector Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #include #include"ast_smt2_pp.h" #include"well_sorted.h" #include"th_rewriter.h" #include"fpa2bv_converter.h" #define BVULT(X,Y,R) { expr_ref bvult_eq(m), bvult_not(m); m_simp.mk_eq(X, Y, bvult_eq); m_simp.mk_not(bvult_eq, bvult_not); expr_ref t(m); t = m_bv_util.mk_ule(X,Y); m_simp.mk_and(t, bvult_not, R); } #define BVSLT(X,Y,R) { expr_ref bvslt_eq(m), bvslt_not(m); m_simp.mk_eq(X, Y, bvslt_eq); m_simp.mk_not(bvslt_eq, bvslt_not); expr_ref t(m); t = m_bv_util.mk_sle(X,Y); m_simp.mk_and(t, bvslt_not, R); } fpa2bv_converter::fpa2bv_converter(ast_manager & m) : m(m), m_simp(m), m_util(m), m_bv_util(m), m_arith_util(m), m_mpf_manager(m_util.fm()), m_mpz_manager(m_mpf_manager.mpz_manager()), m_hi_fp_unspecified(true), m_extra_assertions(m) { m_plugin = static_cast(m.get_plugin(m.mk_family_id("fpa"))); } fpa2bv_converter::~fpa2bv_converter() { reset(); } void fpa2bv_converter::mk_eq(expr * a, expr * b, expr_ref & result) { SASSERT(is_app_of(a, m_plugin->get_family_id(), OP_FPA_FP)); SASSERT(is_app_of(b, m_plugin->get_family_id(), OP_FPA_FP)); TRACE("fpa2bv", tout << "mk_eq a=" << mk_ismt2_pp(a, m) << std::endl; tout << "mk_eq b=" << mk_ismt2_pp(b, m) << std::endl;); expr_ref eq_sgn(m), eq_exp(m), eq_sig(m); m_simp.mk_eq(to_app(a)->get_arg(0), to_app(b)->get_arg(0), eq_sgn); m_simp.mk_eq(to_app(a)->get_arg(1), to_app(b)->get_arg(1), eq_exp); m_simp.mk_eq(to_app(a)->get_arg(2), to_app(b)->get_arg(2), eq_sig); dbg_decouple("fpa2bv_eq_sgn", eq_sgn); dbg_decouple("fpa2bv_eq_exp", eq_exp); dbg_decouple("fpa2bv_eq_sig", eq_sig); expr_ref both_the_same(m); m_simp.mk_and(eq_sgn, eq_exp, eq_sig, both_the_same); dbg_decouple("fpa2bv_eq_both_the_same", both_the_same); // The SMT FPA theory asks for _one_ NaN value, but the bit-blasting // has many, like IEEE754. This encoding of equality makes it look like // a single NaN again. expr_ref a_is_nan(m), b_is_nan(m), both_are_nan(m); mk_is_nan(a, a_is_nan); mk_is_nan(b, b_is_nan); m_simp.mk_and(a_is_nan, b_is_nan, both_are_nan); dbg_decouple("fpa2bv_eq_both_are_nan", both_are_nan); m_simp.mk_or(both_are_nan, both_the_same, result); } void fpa2bv_converter::mk_ite(expr * c, expr * t, expr * f, expr_ref & result) { SASSERT(is_app_of(t, m_plugin->get_family_id(), OP_FPA_FP)); SASSERT(is_app_of(f, m_plugin->get_family_id(), OP_FPA_FP)); expr *t_sgn, *t_sig, *t_exp; expr *f_sgn, *f_sig, *f_exp; split_fp(t, t_sgn, t_exp, t_sig); split_fp(f, f_sgn, f_exp, f_sig); expr_ref sgn(m), s(m), e(m); m_simp.mk_ite(c, t_sgn, f_sgn, sgn); m_simp.mk_ite(c, t_sig, f_sig, s); m_simp.mk_ite(c, t_exp, f_exp, e); mk_fp(sgn, e, s, result); } void fpa2bv_converter::mk_distinct(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { // Note: in SMT there is only one NaN, so multiple of them are considered // equal, thus (distinct NaN NaN) is false, even if the two NaNs have // different bitwise representations (see also mk_eq). result = m.mk_true(); for (unsigned i = 0; i < num; i++) { for (unsigned j = i+1; j < num; j++) { expr_ref eq(m); mk_eq(args[i], args[j], eq); m_simp.mk_and(result, m.mk_not(eq), result); } } } void fpa2bv_converter::mk_numeral(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 0); SASSERT(f->get_num_parameters() == 1); SASSERT(f->get_parameter(0).is_external()); unsigned p_id = f->get_parameter(0).get_ext_id(); mpf const & v = m_plugin->get_value(p_id); unsigned sbits = v.get_sbits(); unsigned ebits = v.get_ebits(); bool sign = m_util.fm().sgn(v); mpz const & sig = m_util.fm().sig(v); mpf_exp_t const & exp = m_util.fm().exp(v); if (m_util.fm().is_nan(v)) mk_nan(f, result); else if (m_util.fm().is_inf(v)) { if (m_util.fm().sgn(v)) mk_ninf(f, result); else mk_pinf(f, result); } else { expr_ref bv_sgn(m), bv_sig(m), e(m), biased_exp(m); bv_sgn = m_bv_util.mk_numeral( (sign) ? 1 : 0, 1); bv_sig = m_bv_util.mk_numeral(rational(sig), sbits-1); e = m_bv_util.mk_numeral(exp, ebits); mk_bias(e, biased_exp); mk_fp(bv_sgn, biased_exp, bv_sig, result); TRACE("fpa2bv_dbg", tout << "value of [" << sign << " " << m_mpz_manager.to_string(sig) << " " << exp << "] is " << mk_ismt2_pp(result, m) << std::endl;); } } app * fpa2bv_converter::mk_fresh_const(char const * prefix, unsigned sz) { return m.mk_fresh_const(prefix, m_bv_util.mk_sort(sz)); } void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) { SASSERT(f->get_family_id() == null_family_id); SASSERT(f->get_arity() == 0); expr * r; if (m_const2bv.find(f, r)) { result = r; } else { sort * srt = f->get_range(); SASSERT(is_float(srt)); unsigned ebits = m_util.get_ebits(srt); unsigned sbits = m_util.get_sbits(srt); app_ref sgn(m), s(m), e(m); #ifdef Z3DEBUG std::string p("fpa2bv"); std::string name = f->get_name().str(); sgn = mk_fresh_const((p + "_sgn_" + name).c_str(), 1); s = mk_fresh_const((p + "_sig_" + name).c_str(), sbits - 1); e = mk_fresh_const((p + "_exp_" + name).c_str(), ebits); #else app_ref bv(m); unsigned bv_sz = 1 + ebits + (sbits - 1); bv = mk_fresh_const(0, bv_sz); sgn = m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, bv); e = m_bv_util.mk_extract(bv_sz - 2, sbits - 1, bv); s = m_bv_util.mk_extract(sbits - 2, 0, bv); SASSERT(m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.get_bv_size(s) == sbits-1); SASSERT(m_bv_util.get_bv_size(e) == ebits); #endif mk_fp(sgn, e, s, result); m_const2bv.insert(f, result); m.inc_ref(f); m.inc_ref(result); } } void fpa2bv_converter::mk_var(unsigned base_inx, sort * srt, expr_ref & result) { SASSERT(is_float(srt)); unsigned ebits = m_util.get_ebits(srt); unsigned sbits = m_util.get_sbits(srt); expr_ref sgn(m), s(m), e(m); sgn = m.mk_var(base_inx, m_bv_util.mk_sort(1)); s = m.mk_var(base_inx + 1, m_bv_util.mk_sort(sbits-1)); e = m.mk_var(base_inx + 2, m_bv_util.mk_sort(ebits)); mk_fp(sgn, e, s, result); } void fpa2bv_converter::mk_uninterpreted_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_dbg", tout << "UF: " << mk_ismt2_pp(f, m) << std::endl; ); SASSERT(f->get_arity() == num); expr_ref_buffer new_args(m); for (unsigned i = 0; i < num ; i ++) if (is_float(args[i])) { expr * sgn, * sig, * exp; split_fp(args[i], sgn, exp, sig); new_args.push_back(sgn); new_args.push_back(sig); new_args.push_back(exp); } else new_args.push_back(args[i]); func_decl * fd; func_decl_triple fd3; if (m_uf2bvuf.find(f, fd)) { result = m.mk_app(fd, new_args.size(), new_args.c_ptr()); } else if (m_uf23bvuf.find(f, fd3)) { expr_ref a_sgn(m), a_sig(m), a_exp(m); a_sgn = m.mk_app(fd3.f_sgn, new_args.size(), new_args.c_ptr()); a_sig = m.mk_app(fd3.f_sig, new_args.size(), new_args.c_ptr()); a_exp = m.mk_app(fd3.f_exp, new_args.size(), new_args.c_ptr()); mk_fp(a_sgn, a_exp, a_sig, result); } else { sort_ref_buffer new_domain(m); for (unsigned i = 0; i < f->get_arity() ; i ++) if (is_float(f->get_domain()[i])) { new_domain.push_back(m_bv_util.mk_sort(1)); new_domain.push_back(m_bv_util.mk_sort(m_util.get_sbits(f->get_domain()[i])-1)); new_domain.push_back(m_bv_util.mk_sort(m_util.get_ebits(f->get_domain()[i]))); } else new_domain.push_back(f->get_domain()[i]); if (!is_float(f->get_range())) { func_decl * fbv = m.mk_func_decl(f->get_name(), new_domain.size(), new_domain.c_ptr(), f->get_range(), *f->get_info()); TRACE("fpa2bv_dbg", tout << "New UF func_decl : " << mk_ismt2_pp(fbv, m) << std::endl; ); m_uf2bvuf.insert(f, fbv); m.inc_ref(f); m.inc_ref(fbv); result = m.mk_app(fbv, new_args.size(), new_args.c_ptr()); } else { string_buffer<> name_buffer; name_buffer.reset(); name_buffer << f->get_name() << ".sgn"; func_decl * f_sgn = m.mk_func_decl(symbol(name_buffer.c_str()), new_domain.size(), new_domain.c_ptr(), m_bv_util.mk_sort(1)); name_buffer.reset(); name_buffer << f->get_name() << ".sig"; func_decl * f_sig = m.mk_func_decl(symbol(name_buffer.c_str()), new_domain.size(), new_domain.c_ptr(), m_bv_util.mk_sort(m_util.get_sbits(f->get_range())-1)); name_buffer.reset(); name_buffer << f->get_name() << ".exp"; func_decl * f_exp = m.mk_func_decl(symbol(name_buffer.c_str()), new_domain.size(), new_domain.c_ptr(), m_bv_util.mk_sort(m_util.get_ebits(f->get_range()))); expr_ref a_sgn(m), a_sig(m), a_exp(m); a_sgn = m.mk_app(f_sgn, new_args.size(), new_args.c_ptr()); a_sig = m.mk_app(f_sig, new_args.size(), new_args.c_ptr()); a_exp = m.mk_app(f_exp, new_args.size(), new_args.c_ptr()); TRACE("fpa2bv_dbg", tout << "New UF func_decls : " << std::endl; tout << mk_ismt2_pp(f_sgn, m) << std::endl; tout << mk_ismt2_pp(f_sig, m) << std::endl; tout << mk_ismt2_pp(f_exp, m) << std::endl; ); m_uf23bvuf.insert(f, func_decl_triple(f_sgn, f_sig, f_exp)); m.inc_ref(f); m.inc_ref(f_sgn); m.inc_ref(f_sig); m.inc_ref(f_exp); mk_fp(a_sgn, a_exp, a_sig, result); } } TRACE("fpa2bv_dbg", tout << "UF result: " << mk_ismt2_pp(result, m) << std::endl; ); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { SASSERT(f->get_family_id() == null_family_id); SASSERT(f->get_arity() == 0); expr * r; if (m_rm_const2bv.find(f, r)) { result = r; } else { SASSERT(is_rm(f->get_range())); result = m.mk_fresh_const( #ifdef Z3DEBUG "fpa2bv_rm" #else 0 #endif , m_bv_util.mk_sort(3)); m_rm_const2bv.insert(f, result); m.inc_ref(f); m.inc_ref(result); expr_ref rcc(m); rcc = bu().mk_ule(result, bu().mk_numeral(4, 3)); m_extra_assertions.push_back(rcc); } } void fpa2bv_converter::mk_pinf(func_decl * f, expr_ref & result) { sort * srt = f->get_range(); SASSERT(is_float(srt)); unsigned sbits = m_util.get_sbits(srt); unsigned ebits = m_util.get_ebits(srt); expr_ref top_exp(m); mk_top_exp(ebits, top_exp); mk_fp(m_bv_util.mk_numeral(0, 1), top_exp, m_bv_util.mk_numeral(0, sbits-1), result); } void fpa2bv_converter::mk_ninf(func_decl * f, expr_ref & result) { sort * srt = f->get_range(); SASSERT(is_float(srt)); unsigned sbits = m_util.get_sbits(srt); unsigned ebits = m_util.get_ebits(srt); expr_ref top_exp(m); mk_top_exp(ebits, top_exp); mk_fp(m_bv_util.mk_numeral(1, 1), top_exp, m_bv_util.mk_numeral(0, sbits-1), result); } void fpa2bv_converter::mk_nan(func_decl * f, expr_ref & result) { sort * srt = f->get_range(); SASSERT(is_float(srt)); unsigned sbits = m_util.get_sbits(srt); unsigned ebits = m_util.get_ebits(srt); expr_ref top_exp(m); mk_top_exp(ebits, top_exp); mk_fp(m_bv_util.mk_numeral(0, 1), top_exp, m_bv_util.mk_numeral(1, sbits-1), result); } void fpa2bv_converter::mk_nzero(func_decl *f, expr_ref & result) { sort * srt = f->get_range(); SASSERT(is_float(srt)); unsigned sbits = m_util.get_sbits(srt); unsigned ebits = m_util.get_ebits(srt); expr_ref bot_exp(m); mk_bot_exp(ebits, bot_exp); mk_fp(m_bv_util.mk_numeral(1, 1), bot_exp, m_bv_util.mk_numeral(0, sbits - 1), result); } void fpa2bv_converter::mk_pzero(func_decl *f, expr_ref & result) { sort * srt = f->get_range(); SASSERT(is_float(srt)); unsigned sbits = m_util.get_sbits(srt); unsigned ebits = m_util.get_ebits(srt); expr_ref bot_exp(m); mk_bot_exp(ebits, bot_exp); mk_fp(m_bv_util.mk_numeral(0, 1), bot_exp, m_bv_util.mk_numeral(0, sbits-1), result); } void fpa2bv_converter::mk_one(func_decl *f, expr_ref sign, expr_ref & result) { sort * srt = f->get_range(); SASSERT(is_float(srt)); unsigned sbits = m_util.get_sbits(srt); unsigned ebits = m_util.get_ebits(srt); mk_fp(sign, m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1), ebits), m_bv_util.mk_numeral(0, sbits-1), 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 expr_ref cap(m), cap_le_delta(m); cap = m_bv_util.mk_numeral(sbits+2, ebits); cap_le_delta = m_bv_util.mk_ule(cap, exp_delta); m_simp.mk_ite(cap_le_delta, cap, exp_delta, exp_delta); dbg_decouple("fpa2bv_add_exp_delta_capped", exp_delta); // Three extra bits for c/d c_sig = m_bv_util.mk_concat(c_sig, m_bv_util.mk_numeral(0, 3)); d_sig = m_bv_util.mk_concat(d_sig, m_bv_util.mk_numeral(0, 3)); SASSERT(is_well_sorted(m, c_sig)); SASSERT(is_well_sorted(m, d_sig)); // Alignment shift with sticky bit computation. expr_ref big_d_sig(m); big_d_sig = m_bv_util.mk_concat(d_sig, m_bv_util.mk_numeral(0, sbits+3)); SASSERT(is_well_sorted(m, big_d_sig)); expr_ref shifted_big(m), shifted_d_sig(m), sticky_raw(m), sticky(m); shifted_big = m_bv_util.mk_bv_lshr(big_d_sig, m_bv_util.mk_concat(m_bv_util.mk_numeral(0, (2*(sbits+3))-ebits), exp_delta)); shifted_d_sig = m_bv_util.mk_extract((2*(sbits+3)-1), (sbits+3), shifted_big); SASSERT(is_well_sorted(m, shifted_d_sig)); sticky_raw = m_bv_util.mk_extract(sbits+2, 0, shifted_big); expr_ref sticky_eq(m), nil_sbit3(m), one_sbit3(m); nil_sbit3 = m_bv_util.mk_numeral(0, sbits+3); one_sbit3 = m_bv_util.mk_numeral(1, sbits+3); m_simp.mk_eq(sticky_raw, nil_sbit3, sticky_eq); m_simp.mk_ite(sticky_eq, nil_sbit3, one_sbit3, sticky); SASSERT(is_well_sorted(m, sticky)); expr * or_args[2] = { shifted_d_sig, sticky }; shifted_d_sig = m_bv_util.mk_bv_or(2, or_args); SASSERT(is_well_sorted(m, shifted_d_sig)); expr_ref eq_sgn(m); m_simp.mk_eq(c_sgn, d_sgn, eq_sgn); // dbg_decouple("fpa2bv_add_eq_sgn", eq_sgn); TRACE("fpa2bv_add_core", tout << "EQ_SGN = " << mk_ismt2_pp(eq_sgn, m) << std::endl; ); // two extra bits for catching the overflow. c_sig = m_bv_util.mk_zero_extend(2, c_sig); shifted_d_sig = m_bv_util.mk_zero_extend(2, shifted_d_sig); SASSERT(m_bv_util.get_bv_size(c_sig) == sbits+5); SASSERT(m_bv_util.get_bv_size(shifted_d_sig) == sbits+5); dbg_decouple("fpa2bv_add_c_sig", c_sig); dbg_decouple("fpa2bv_add_shifted_d_sig", shifted_d_sig); expr_ref sum(m); m_simp.mk_ite(eq_sgn, m_bv_util.mk_bv_add(c_sig, shifted_d_sig), m_bv_util.mk_bv_sub(c_sig, shifted_d_sig), sum); SASSERT(is_well_sorted(m, sum)); dbg_decouple("fpa2bv_add_sum", sum); expr_ref sign_bv(m), n_sum(m); sign_bv = m_bv_util.mk_extract(sbits+4, sbits+4, sum); n_sum = m_bv_util.mk_bv_neg(sum); dbg_decouple("fpa2bv_add_sign_bv", sign_bv); dbg_decouple("fpa2bv_add_n_sum", n_sum); family_id bvfid = m_bv_util.get_fid(); expr_ref res_sgn_c1(m), res_sgn_c2(m), res_sgn_c3(m); expr_ref not_c_sgn(m), not_d_sgn(m), not_sign_bv(m); not_c_sgn = m_bv_util.mk_bv_not(c_sgn); not_d_sgn = m_bv_util.mk_bv_not(d_sgn); not_sign_bv = m_bv_util.mk_bv_not(sign_bv); res_sgn_c1 = m.mk_app(bvfid, OP_BAND, not_c_sgn, d_sgn, sign_bv); res_sgn_c2 = m.mk_app(bvfid, OP_BAND, c_sgn, not_d_sgn, not_sign_bv); res_sgn_c3 = m.mk_app(bvfid, OP_BAND, c_sgn, d_sgn); expr * res_sgn_or_args[3] = { res_sgn_c1, res_sgn_c2, res_sgn_c3 }; res_sgn = m_bv_util.mk_bv_or(3, res_sgn_or_args); expr_ref res_sig_eq(m), sig_abs(m), one_1(m); one_1 = m_bv_util.mk_numeral(1, 1); m_simp.mk_eq(sign_bv, one_1, res_sig_eq); m_simp.mk_ite(res_sig_eq, n_sum, sum, sig_abs); dbg_decouple("fpa2bv_add_sig_abs", sig_abs); res_sig = m_bv_util.mk_extract(sbits+3, 0, sig_abs); res_exp = m_bv_util.mk_sign_extend(2, c_exp); // rounder requires 2 extra bits! } void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); expr_ref rm(m), x(m), y(m); rm = args[0]; x = args[1]; y = args[2]; expr_ref nan(m), nzero(m), pzero(m); mk_nan(f, nan); mk_nzero(f, nzero); mk_pzero(f, pzero); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_neg(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_neg(m), y_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_neg(x, x_is_neg); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); mk_is_neg(y, y_is_neg); mk_is_inf(y, y_is_inf); dbg_decouple("fpa2bv_add_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_add_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_add_x_is_pos", x_is_pos); dbg_decouple("fpa2bv_add_x_is_neg", x_is_neg); dbg_decouple("fpa2bv_add_x_is_inf", x_is_inf); dbg_decouple("fpa2bv_add_y_is_nan", y_is_nan); dbg_decouple("fpa2bv_add_y_is_zero", y_is_zero); dbg_decouple("fpa2bv_add_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_add_y_is_neg", y_is_neg); dbg_decouple("fpa2bv_add_y_is_inf", y_is_inf); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); m_simp.mk_or(x_is_nan, y_is_nan, c1); v1 = nan; mk_is_inf(x, c2); expr_ref nx(m), ny(m), nx_xor_ny(m), inf_xor(m); mk_is_neg(x, nx); mk_is_neg(y, ny); m_simp.mk_xor(nx, ny, nx_xor_ny); m_simp.mk_and(y_is_inf, nx_xor_ny, inf_xor); mk_ite(inf_xor, nan, x, v2); mk_is_inf(y, c3); expr_ref xy_is_neg(m), v3_and(m); m_simp.mk_xor(x_is_neg, y_is_neg, xy_is_neg); m_simp.mk_and(x_is_inf, xy_is_neg, v3_and); mk_ite(v3_and, nan, y, v3); expr_ref rm_is_to_neg(m), signs_and(m), signs_xor(m), v4_and(m), rm_and_xor(m), neg_cond(m); m_simp.mk_and(x_is_zero, y_is_zero, c4); m_simp.mk_and(x_is_neg, y_is_neg, signs_and); m_simp.mk_xor(x_is_neg, y_is_neg, signs_xor); mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); m_simp.mk_and(rm_is_to_neg, signs_xor, rm_and_xor); m_simp.mk_or(signs_and, rm_and_xor, neg_cond); mk_ite(neg_cond, nzero, pzero, v4); m_simp.mk_and(x_is_neg, y_is_neg, v4_and); mk_ite(v4_and, x, v4, v4); c5 = x_is_zero; v5 = y; c6 = y_is_zero; v6 = x; // Actual addition. unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, false); unpack(y, b_sgn, b_sig, b_exp, b_lz, false); dbg_decouple("fpa2bv_add_unpack_a_sgn", a_sgn); dbg_decouple("fpa2bv_add_unpack_a_sig", a_sig); dbg_decouple("fpa2bv_add_unpack_a_exp", a_exp); dbg_decouple("fpa2bv_add_unpack_b_sgn", b_sgn); dbg_decouple("fpa2bv_add_unpack_b_sig", b_sig); dbg_decouple("fpa2bv_add_unpack_b_exp", b_exp); expr_ref swap_cond(m); swap_cond = m_bv_util.mk_sle(a_exp, b_exp); expr_ref c_sgn(m), c_sig(m), c_exp(m), d_sgn(m), d_sig(m), d_exp(m); m_simp.mk_ite(swap_cond, b_sgn, a_sgn, c_sgn); m_simp.mk_ite(swap_cond, b_sig, a_sig, c_sig); // has sbits m_simp.mk_ite(swap_cond, b_exp, a_exp, c_exp); // has ebits m_simp.mk_ite(swap_cond, a_sgn, b_sgn, d_sgn); m_simp.mk_ite(swap_cond, a_sig, b_sig, d_sig); // has sbits m_simp.mk_ite(swap_cond, a_exp, b_exp, d_exp); // has ebits expr_ref res_sgn(m), res_sig(m), res_exp(m); add_core(sbits, ebits, rm, c_sgn, c_sig, c_exp, d_sgn, d_sig, d_exp, res_sgn, res_sig, res_exp); expr_ref is_zero_sig(m), nil_sbit4(m); nil_sbit4 = m_bv_util.mk_numeral(0, sbits+4); m_simp.mk_eq(res_sig, nil_sbit4, is_zero_sig); SASSERT(is_well_sorted(m, is_zero_sig)); dbg_decouple("fpa2bv_add_is_zero_sig", is_zero_sig); expr_ref zero_case(m); mk_ite(rm_is_to_neg, nzero, pzero, zero_case); expr_ref rounded(m); round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); mk_ite(is_zero_sig, zero_case, rounded, v7); mk_ite(c6, v6, v7, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_add", tout << "ADD = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_sub(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); expr_ref t(m); mk_neg(f, 1, &args[2], t); expr * nargs[3] = { args[0], args[1], t }; mk_add(f, 3, nargs, result); } void fpa2bv_converter::mk_neg(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); expr * sgn, * s, * e; split_fp(args[0], sgn, e, s); expr_ref c(m), nsgn(m); mk_is_nan(args[0], c); nsgn = m_bv_util.mk_bv_not(sgn); expr_ref r_sgn(m); m_simp.mk_ite(c, sgn, nsgn, r_sgn); mk_fp(r_sgn, e, s, result); } void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); expr_ref rm(m), x(m), y(m); rm = args[0]; x = args[1]; y = args[2]; expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(f, nan); mk_nzero(f, nzero); mk_pzero(f, pzero); mk_ninf(f, ninf); mk_pinf(f, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); mk_is_inf(y, y_is_inf); dbg_decouple("fpa2bv_mul_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_mul_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_mul_x_is_pos", x_is_pos); dbg_decouple("fpa2bv_mul_x_is_inf", x_is_inf); dbg_decouple("fpa2bv_mul_y_is_nan", y_is_nan); dbg_decouple("fpa2bv_mul_y_is_zero", y_is_zero); dbg_decouple("fpa2bv_mul_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_mul_y_is_inf", y_is_inf); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); // (x is NaN) || (y is NaN) -> NaN m_simp.mk_or(x_is_nan, y_is_nan, c1); v1 = nan; // (x is +oo) -> if (y is 0) then NaN else inf with y's sign. mk_is_pinf(x, c2); expr_ref y_sgn_inf(m); mk_ite(y_is_pos, pinf, ninf, y_sgn_inf); mk_ite(y_is_zero, nan, y_sgn_inf, v2); // (y is +oo) -> if (x is 0) then NaN else inf with x's sign. mk_is_pinf(y, c3); expr_ref x_sgn_inf(m); mk_ite(x_is_pos, pinf, ninf, x_sgn_inf); mk_ite(x_is_zero, nan, x_sgn_inf, v3); // (x is -oo) -> if (y is 0) then NaN else inf with -y's sign. mk_is_ninf(x, c4); expr_ref neg_y_sgn_inf(m); mk_ite(y_is_pos, ninf, pinf, neg_y_sgn_inf); mk_ite(y_is_zero, nan, neg_y_sgn_inf, v4); // (y is -oo) -> if (x is 0) then NaN else inf with -x's sign. mk_is_ninf(y, c5); expr_ref neg_x_sgn_inf(m); mk_ite(x_is_pos, ninf, pinf, neg_x_sgn_inf); mk_ite(x_is_zero, nan, neg_x_sgn_inf, v5); // (x is 0) || (y is 0) -> x but with sign = x.sign ^ y.sign m_simp.mk_or(x_is_zero, y_is_zero, c6); expr_ref sign_xor(m); m_simp.mk_xor(x_is_pos, y_is_pos, sign_xor); mk_ite(sign_xor, nzero, pzero, v6); // else comes the actual multiplication. unsigned sbits = m_util.get_sbits(f->get_range()); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); unpack(y, b_sgn, b_sig, b_exp, b_lz, true); dbg_decouple("fpa2bv_mul_a_sig", a_sig); dbg_decouple("fpa2bv_mul_a_exp", a_exp); dbg_decouple("fpa2bv_mul_b_sig", b_sig); dbg_decouple("fpa2bv_mul_b_exp", b_exp); expr_ref a_lz_ext(m), b_lz_ext(m); a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); dbg_decouple("fpa2bv_mul_lz_a", a_lz); dbg_decouple("fpa2bv_mul_lz_b", b_lz); expr_ref a_sig_ext(m), b_sig_ext(m); a_sig_ext = m_bv_util.mk_zero_extend(sbits, a_sig); b_sig_ext = m_bv_util.mk_zero_extend(sbits, b_sig); expr_ref a_exp_ext(m), b_exp_ext(m); a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); expr_ref res_sgn(m), res_sig(m), res_exp(m); expr * signs[2] = { a_sgn, b_sgn }; res_sgn = m_bv_util.mk_bv_xor(2, signs); dbg_decouple("fpa2bv_mul_res_sgn", res_sgn); res_exp = m_bv_util.mk_bv_add( m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); expr_ref product(m); product = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); dbg_decouple("fpa2bv_mul_product", product); SASSERT(m_bv_util.get_bv_size(product) == 2*sbits); expr_ref h_p(m), l_p(m), rbits(m); h_p = m_bv_util.mk_extract(2*sbits-1, sbits, product); l_p = m_bv_util.mk_extract(sbits-1, 0, product); if (sbits >= 4) { expr_ref sticky(m); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(sbits-4, 0, product)); rbits = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits-1, sbits-3, product), sticky); } else rbits = m_bv_util.mk_concat(l_p, m_bv_util.mk_numeral(0, 4 - sbits)); SASSERT(m_bv_util.get_bv_size(rbits) == 4); res_sig = m_bv_util.mk_concat(h_p, rbits); round(f->get_range(), rm, res_sgn, res_sig, res_exp, v7); // And finally, we tie them together. mk_ite(c6, v6, v7, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_mul", tout << "MUL = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); expr_ref rm(m), x(m), y(m); rm = args[0]; x = args[1]; y = args[2]; expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(f, nan); mk_nzero(f, nzero); mk_pzero(f, pzero); mk_ninf(f, ninf); mk_pinf(f, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); mk_is_inf(y, y_is_inf); dbg_decouple("fpa2bv_div_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_div_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_div_x_is_pos", x_is_pos); dbg_decouple("fpa2bv_div_x_is_inf", x_is_inf); dbg_decouple("fpa2bv_div_y_is_nan", y_is_nan); dbg_decouple("fpa2bv_div_y_is_zero", y_is_zero); dbg_decouple("fpa2bv_div_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_div_y_is_inf", y_is_inf); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m), c7(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m), v8(m); // (x is NaN) || (y is NaN) -> NaN m_simp.mk_or(x_is_nan, y_is_nan, c1); v1 = nan; // (x is +oo) -> if (y is oo) then NaN else inf with y's sign. mk_is_pinf(x, c2); expr_ref y_sgn_inf(m); mk_ite(y_is_pos, pinf, ninf, y_sgn_inf); mk_ite(y_is_inf, nan, y_sgn_inf, v2); // (y is +oo) -> if (x is oo) then NaN else 0 with sign x.sgn ^ y.sgn mk_is_pinf(y, c3); expr_ref xy_zero(m), signs_xor(m); m_simp.mk_xor(x_is_pos, y_is_pos, signs_xor); mk_ite(signs_xor, nzero, pzero, xy_zero); mk_ite(x_is_inf, nan, xy_zero, v3); // (x is -oo) -> if (y is oo) then NaN else inf with -y's sign. mk_is_ninf(x, c4); expr_ref neg_y_sgn_inf(m); mk_ite(y_is_pos, ninf, pinf, neg_y_sgn_inf); mk_ite(y_is_inf, nan, neg_y_sgn_inf, v4); // (y is -oo) -> if (x is oo) then NaN else 0 with sign x.sgn ^ y.sgn mk_is_ninf(y, c5); mk_ite(x_is_inf, nan, xy_zero, v5); // (y is 0) -> if (x is 0) then NaN else inf with xor sign. c6 = y_is_zero; expr_ref sgn_inf(m); mk_ite(signs_xor, ninf, pinf, sgn_inf); mk_ite(x_is_zero, nan, sgn_inf, v6); // (x is 0) -> result is zero with sgn = x.sgn^y.sgn // This is a special case to avoid problems with the unpacking of zero. c7 = x_is_zero; mk_ite(signs_xor, nzero, pzero, v7); // else comes the actual division. unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); SASSERT(ebits <= sbits); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); unpack(y, b_sgn, b_sig, b_exp, b_lz, true); unsigned extra_bits = sbits+2; expr_ref a_sig_ext(m), b_sig_ext(m); a_sig_ext = m_bv_util.mk_concat(a_sig, m_bv_util.mk_numeral(0, sbits + extra_bits)); b_sig_ext = m_bv_util.mk_zero_extend(sbits + extra_bits, b_sig); expr_ref a_exp_ext(m), b_exp_ext(m); a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); expr_ref res_sgn(m), res_sig(m), res_exp(m); expr * signs[2] = { a_sgn, b_sgn }; res_sgn = m_bv_util.mk_bv_xor(2, signs); expr_ref a_lz_ext(m), b_lz_ext(m); a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); res_exp = m_bv_util.mk_bv_sub( m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); expr_ref quotient(m); quotient = m.mk_app(m_bv_util.get_fid(), OP_BUDIV, a_sig_ext, b_sig_ext); dbg_decouple("fpa2bv_div_quotient", quotient); SASSERT(m_bv_util.get_bv_size(quotient) == (sbits + sbits + extra_bits)); expr_ref sticky(m); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(extra_bits-2, 0, quotient)); res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(extra_bits+sbits+1, extra_bits-1, quotient), sticky); SASSERT(m_bv_util.get_bv_size(res_sig) == (sbits + 4)); expr_ref res_sig_lz(m); mk_leading_zeros(res_sig, sbits + 4, res_sig_lz); dbg_decouple("fpa2bv_div_res_sig_lz", res_sig_lz); expr_ref res_sig_shift_amount(m); res_sig_shift_amount = m_bv_util.mk_bv_sub(res_sig_lz, m_bv_util.mk_numeral(1, sbits + 4)); dbg_decouple("fpa2bv_div_res_sig_shift_amount", res_sig_shift_amount); expr_ref shift_cond(m); shift_cond = m_bv_util.mk_ule(res_sig_lz, m_bv_util.mk_numeral(1, sbits + 4)); m_simp.mk_ite(shift_cond, res_sig, m_bv_util.mk_bv_shl(res_sig, res_sig_shift_amount), res_sig); m_simp.mk_ite(shift_cond, res_exp, m_bv_util.mk_bv_sub(res_exp, m_bv_util.mk_extract(ebits+1, 0, res_sig_shift_amount)), res_exp); round(f->get_range(), rm, res_sgn, res_sig, res_exp, v8); // And finally, we tie them together. mk_ite(c7, v7, v8, result); mk_ite(c6, v6, result, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_div", tout << "DIV = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_rem(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); // Remainder is always exact, so there is no rounding mode. expr_ref x(m), y(m); x = args[0]; y = args[1]; expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(f, nan); mk_nzero(f, nzero); mk_pzero(f, pzero); mk_ninf(f, ninf); mk_pinf(f, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); mk_is_inf(y, y_is_inf); dbg_decouple("fpa2bv_rem_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_rem_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_rem_x_is_pos", x_is_pos); dbg_decouple("fpa2bv_rem_x_is_inf", x_is_inf); dbg_decouple("fpa2bv_rem_y_is_nan", y_is_nan); dbg_decouple("fpa2bv_rem_y_is_zero", y_is_zero); dbg_decouple("fpa2bv_rem_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_rem_y_is_inf", y_is_inf); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); // (x is NaN) || (y is NaN) -> NaN m_simp.mk_or(x_is_nan, y_is_nan, c1); v1 = nan; // (x is +-oo) -> NaN c2 = x_is_inf; v2 = nan; // (y is +-oo) -> x c3 = y_is_inf; v3 = x; // (y is 0) -> NaN. c4 = y_is_zero; v4 = nan; // (x is 0) -> x c5 = x_is_zero; v5 = pzero; // else the actual remainder. unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); expr_ref b_sgn(m), b_sig(m), b_exp(m), b_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); unpack(y, b_sgn, b_sig, b_exp, b_lz, true); BVSLT(a_exp, b_exp, c6); v6 = x; // max. exponent difference is (2^ebits) - 3 const mpz & two_to_ebits = fu().fm().m_powers2(ebits); mpz max_exp_diff; m_mpz_manager.sub(two_to_ebits, 3, max_exp_diff); SASSERT(m_mpz_manager.is_int64(max_exp_diff)); SASSERT(m_mpz_manager.get_uint64(max_exp_diff) <= UINT_MAX); unsigned int max_exp_diff_ui = (unsigned int)m_mpz_manager.get_uint64(max_exp_diff); m_mpz_manager.del(max_exp_diff); expr_ref exp_diff(m); exp_diff = m_bv_util.mk_bv_sub(a_exp, b_exp); dbg_decouple("fpa2bv_rem_exp_diff", exp_diff); // CMW: This creates _huge_ bit-vectors, which is potentially sub-optimal, // but calculating this via rem = x - y * nearest(x/y) creates huge circuits. expr_ref huge_sig(m), shifted_sig(m), huge_rem(m); huge_sig = m_bv_util.mk_zero_extend(max_exp_diff_ui, a_sig); shifted_sig = m_bv_util.mk_bv_shl(huge_sig, m_bv_util.mk_zero_extend(max_exp_diff_ui + sbits - ebits, exp_diff)); huge_rem = m_bv_util.mk_bv_urem(shifted_sig, m_bv_util.mk_zero_extend(max_exp_diff_ui, b_sig)); dbg_decouple("fpa2bv_rem_huge_rem", huge_rem); expr_ref res_sgn(m), res_sig(m), res_exp(m); res_sgn = a_sgn; res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits, 0, huge_rem), m_bv_util.mk_numeral(0, 3)); res_exp = m_bv_util.mk_sign_extend(2, b_exp); // CMW: Actual rounding is not necessary here, this is // just convenience to get rid of the extra bits. expr_ref rm(m); rm = m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3); round(f->get_range(), rm, res_sgn, res_sig, res_exp, v7); // And finally, we tie them together. mk_ite(c6, v6, v7, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); expr_ref result_is_zero(m), zeros(m); mk_is_zero(result, result_is_zero); mk_ite(x_is_pos, pzero, nzero, zeros); mk_ite(result_is_zero, zeros, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_rem", tout << "REM = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_abs(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); expr * sgn, * s, * e; split_fp(args[0], sgn, e, s); mk_fp(m_bv_util.mk_numeral(0, 1), e, s, result); } void fpa2bv_converter::mk_min(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr * x = args[0], * y = args[1]; expr * x_sgn, * x_sig, * x_exp; expr * y_sgn, * y_sig, * y_exp; split_fp(x, x_sgn, x_exp, x_sig); split_fp(y, y_sgn, y_exp, y_sig); expr_ref x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m), both_zero(m), pzero(m); mk_is_zero(x, x_is_zero); mk_is_zero(y, y_is_zero); m_simp.mk_and(x_is_zero, y_is_zero, both_zero); mk_is_nan(x, x_is_nan); mk_is_nan(y, y_is_nan); mk_pzero(f, pzero); expr_ref sgn_diff(m); sgn_diff = m.mk_not(m.mk_eq(x_sgn, y_sgn)); expr_ref lt(m); mk_float_lt(f, num, args, lt); result = y; mk_ite(lt, x, result, result); mk_ite(both_zero, y, result, result); mk_ite(m.mk_and(both_zero, sgn_diff), pzero, result, result); // min(-0.0, +0.0) = min(+0.0, -0.0) = +0.0 mk_ite(y_is_nan, x, result, result); mk_ite(x_is_nan, y, result, result); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_max(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr * x = args[0], *y = args[1]; expr * x_sgn, *x_sig, *x_exp; expr * y_sgn, *y_sig, *y_exp; split_fp(x, x_sgn, x_exp, x_sig); split_fp(y, y_sgn, y_exp, y_sig); expr_ref x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m), both_zero(m), pzero(m); mk_is_zero(x, x_is_zero); mk_is_zero(y, y_is_zero); m_simp.mk_and(x_is_zero, y_is_zero, both_zero); mk_is_nan(x, x_is_nan); mk_is_nan(y, y_is_nan); mk_pzero(f, pzero); expr_ref sgn_diff(m); sgn_diff = m.mk_not(m.mk_eq(x_sgn, y_sgn)); expr_ref gt(m); mk_float_gt(f, num, args, gt); result = y; mk_ite(gt, x, result, result); mk_ite(both_zero, y, result, result); mk_ite(m.mk_and(both_zero, sgn_diff), pzero, result, result); // max(-0.0, +0.0) = max(+0.0, -0.0) = +0.0 mk_ite(y_is_nan, x, result, result); mk_ite(x_is_nan, y, result, result); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 4); // fusedma means (x * y) + z expr_ref rm(m), x(m), y(m), z(m); rm = args[0]; x = args[1]; y = args[2]; z = args[3]; expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(f, nan); mk_nzero(f, nzero); mk_pzero(f, pzero); mk_ninf(f, ninf); mk_pinf(f, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_neg(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_neg(m), y_is_inf(m); expr_ref z_is_nan(m), z_is_zero(m), z_is_pos(m), z_is_neg(m), z_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_neg(x, x_is_neg); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); mk_is_neg(y, y_is_neg); mk_is_inf(y, y_is_inf); mk_is_nan(z, z_is_nan); mk_is_zero(z, z_is_zero); mk_is_pos(z, z_is_pos); mk_is_neg(z, z_is_neg); mk_is_inf(z, z_is_inf); expr_ref rm_is_to_neg(m); mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); dbg_decouple("fpa2bv_fma_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_fma_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_fma_x_is_pos", x_is_pos); dbg_decouple("fpa2bv_fma_x_is_inf", x_is_inf); dbg_decouple("fpa2bv_fma_y_is_nan", y_is_nan); dbg_decouple("fpa2bv_fma_y_is_zero", y_is_zero); dbg_decouple("fpa2bv_fma_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_fma_y_is_inf", y_is_inf); dbg_decouple("fpa2bv_fma_z_is_nan", z_is_nan); dbg_decouple("fpa2bv_fma_z_is_zero", z_is_zero); dbg_decouple("fpa2bv_fma_z_is_pos", z_is_pos); dbg_decouple("fpa2bv_fma_z_is_inf", z_is_inf); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m), c7(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m), v8(m); expr_ref inf_xor(m), inf_cond(m); m_simp.mk_xor(x_is_neg, y_is_neg, inf_xor); m_simp.mk_xor(inf_xor, z_is_neg, inf_xor); m_simp.mk_and(z_is_inf, inf_xor, inf_cond); // (x is NaN) || (y is NaN) || (z is Nan) -> NaN m_simp.mk_or(x_is_nan, y_is_nan, z_is_nan, c1); v1 = nan; // (x is +oo) -> if (y is 0) then NaN else inf with y's sign. mk_is_pinf(x, c2); expr_ref y_sgn_inf(m), inf_or(m); mk_ite(y_is_pos, pinf, ninf, y_sgn_inf); m_simp.mk_or(y_is_zero, inf_cond, inf_or); mk_ite(inf_or, nan, y_sgn_inf, v2); // (y is +oo) -> if (x is 0) then NaN else inf with x's sign. mk_is_pinf(y, c3); expr_ref x_sgn_inf(m); mk_ite(x_is_pos, pinf, ninf, x_sgn_inf); m_simp.mk_or(x_is_zero, inf_cond, inf_or); mk_ite(inf_or, nan, x_sgn_inf, v3); // (x is -oo) -> if (y is 0) then NaN else inf with -y's sign. mk_is_ninf(x, c4); expr_ref neg_y_sgn_inf(m); mk_ite(y_is_pos, ninf, pinf, neg_y_sgn_inf); m_simp.mk_or(y_is_zero, inf_cond, inf_or); mk_ite(inf_or, nan, neg_y_sgn_inf, v4); // (y is -oo) -> if (x is 0) then NaN else inf with -x's sign. mk_is_ninf(y, c5); expr_ref neg_x_sgn_inf(m); mk_ite(x_is_pos, ninf, pinf, neg_x_sgn_inf); m_simp.mk_or(x_is_zero, inf_cond, inf_or); mk_ite(inf_or, nan, neg_x_sgn_inf, v5); // z is +-INF -> z. mk_is_inf(z, c6); v6 = z; // (x is 0) || (y is 0) -> z m_simp.mk_or(x_is_zero, y_is_zero, c7); expr_ref ite_c(m); m_simp.mk_and(z_is_zero, m.mk_not(rm_is_to_neg), ite_c); mk_ite(ite_c, pzero, z, v7); // else comes the fused multiplication. unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); expr_ref b_sgn(m), b_sig(m), b_exp(m), b_lz(m); expr_ref c_sgn(m), c_sig(m), c_exp(m), c_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); unpack(y, b_sgn, b_sig, b_exp, b_lz, true); unpack(z, c_sgn, c_sig, c_exp, c_lz, true); expr_ref a_lz_ext(m), b_lz_ext(m), c_lz_ext(m); a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); c_lz_ext = m_bv_util.mk_zero_extend(2, c_lz); expr_ref a_sig_ext(m), b_sig_ext(m); a_sig_ext = m_bv_util.mk_zero_extend(sbits, a_sig); b_sig_ext = m_bv_util.mk_zero_extend(sbits, b_sig); expr_ref a_exp_ext(m), b_exp_ext(m), c_exp_ext(m); a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); c_exp_ext = m_bv_util.mk_sign_extend(2, c_exp); dbg_decouple("fpa2bv_fma_a_sig", a_sig_ext); dbg_decouple("fpa2bv_fma_b_sig", b_sig_ext); dbg_decouple("fpa2bv_fma_c_sig", c_sig); dbg_decouple("fpa2bv_fma_a_exp", a_exp_ext); dbg_decouple("fpa2bv_fma_b_exp", b_exp_ext); dbg_decouple("fpa2bv_fma_c_exp", c_exp_ext); dbg_decouple("fpa2bv_fma_a_lz", a_lz_ext); dbg_decouple("fpa2bv_fma_b_lz", b_lz_ext); dbg_decouple("fpa2bv_fma_c_lz", c_lz_ext); expr_ref mul_sgn(m), mul_sig(m), mul_exp(m); expr * signs[2] = { a_sgn, b_sgn }; mul_sgn = m_bv_util.mk_bv_xor(2, signs); dbg_decouple("fpa2bv_fma_mul_sgn", mul_sgn); mul_exp = m_bv_util.mk_bv_add(m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); dbg_decouple("fpa2bv_fma_mul_exp", mul_exp); mul_sig = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); dbg_decouple("fpa2bv_fma_mul_sig", mul_sig); SASSERT(m_bv_util.get_bv_size(mul_sig) == 2*sbits); SASSERT(m_bv_util.get_bv_size(mul_exp) == ebits + 2); // The product has the form [-1][0].[2*sbits - 2]. // Extend c c_sig = m_bv_util.mk_zero_extend(1, m_bv_util.mk_concat(c_sig, m_bv_util.mk_numeral(0, sbits-1))); c_exp_ext = m_bv_util.mk_bv_sub(c_exp_ext, c_lz_ext); SASSERT(m_bv_util.get_bv_size(mul_sig) == 2 * sbits); SASSERT(m_bv_util.get_bv_size(c_sig) == 2 * sbits); expr_ref swap_cond(m); swap_cond = m_bv_util.mk_sle(mul_exp, c_exp_ext); SASSERT(is_well_sorted(m, swap_cond)); dbg_decouple("fpa2bv_fma_swap_cond", swap_cond); expr_ref e_sgn(m), e_sig(m), e_exp(m), f_sgn(m), f_sig(m), f_exp(m); m_simp.mk_ite(swap_cond, c_sgn, mul_sgn, e_sgn); m_simp.mk_ite(swap_cond, c_sig, mul_sig, e_sig); // has 2 * sbits m_simp.mk_ite(swap_cond, c_exp_ext, mul_exp, e_exp); // has ebits + 2 m_simp.mk_ite(swap_cond, mul_sgn, c_sgn, f_sgn); m_simp.mk_ite(swap_cond, mul_sig, c_sig, f_sig); // has 2 * sbits m_simp.mk_ite(swap_cond, mul_exp, c_exp_ext, f_exp); // has ebits + 2 SASSERT(is_well_sorted(m, e_sgn)); SASSERT(is_well_sorted(m, e_sig)); SASSERT(is_well_sorted(m, e_exp)); SASSERT(is_well_sorted(m, f_sgn)); SASSERT(is_well_sorted(m, f_sig)); SASSERT(is_well_sorted(m, f_exp)); SASSERT(m_bv_util.get_bv_size(e_sig) == 2 * sbits); SASSERT(m_bv_util.get_bv_size(f_sig) == 2 * sbits); SASSERT(m_bv_util.get_bv_size(e_exp) == ebits + 2); SASSERT(m_bv_util.get_bv_size(f_exp) == ebits + 2); expr_ref res_sgn(m), res_sig(m), res_exp(m); expr_ref exp_delta(m); exp_delta = m_bv_util.mk_bv_sub(e_exp, f_exp); dbg_decouple("fpa2bv_fma_add_exp_delta", exp_delta); // cap the delta expr_ref cap(m), cap_le_delta(m); cap = m_bv_util.mk_numeral(2*sbits, ebits+2); cap_le_delta = m_bv_util.mk_ule(cap, exp_delta); m_simp.mk_ite(cap_le_delta, cap, exp_delta, exp_delta); SASSERT(m_bv_util.get_bv_size(exp_delta) == ebits+2); SASSERT(is_well_sorted(m, exp_delta)); dbg_decouple("fpa2bv_fma_add_exp_delta_capped", exp_delta); // Alignment shift with sticky bit computation. expr_ref shifted_big(m), shifted_f_sig(m), sticky_raw(m); shifted_big = m_bv_util.mk_bv_lshr( m_bv_util.mk_concat(f_sig, m_bv_util.mk_numeral(0, sbits)), m_bv_util.mk_zero_extend((3*sbits)-(ebits+2), exp_delta)); shifted_f_sig = m_bv_util.mk_extract(3*sbits-1, sbits, shifted_big); sticky_raw = m_bv_util.mk_extract(sbits-1, 0, shifted_big); SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == 2 * sbits); SASSERT(is_well_sorted(m, shifted_f_sig)); expr_ref sticky(m); sticky = m_bv_util.mk_zero_extend(2*sbits-1, m.mk_app(m_bv_util.get_fid(), OP_BREDOR, sticky_raw.get())); SASSERT(is_well_sorted(m, sticky)); dbg_decouple("fpa2bv_fma_f_sig_sticky_raw", sticky_raw); dbg_decouple("fpa2bv_fma_f_sig_sticky", sticky); expr * or_args[2] = { shifted_f_sig, sticky }; shifted_f_sig = m_bv_util.mk_bv_or(2, or_args); SASSERT(is_well_sorted(m, shifted_f_sig)); // Significant addition. // Two extra bits for catching the overflow. e_sig = m_bv_util.mk_zero_extend(2, e_sig); shifted_f_sig = m_bv_util.mk_zero_extend(2, shifted_f_sig); expr_ref eq_sgn(m); m_simp.mk_eq(e_sgn, f_sgn, eq_sgn); SASSERT(m_bv_util.get_bv_size(e_sig) == 2*sbits + 2); SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == 2*sbits + 2); dbg_decouple("fpa2bv_fma_add_e_sig", e_sig); dbg_decouple("fpa2bv_fma_add_shifted_f_sig", shifted_f_sig); expr_ref sum(m); m_simp.mk_ite(eq_sgn, m_bv_util.mk_bv_add(e_sig, shifted_f_sig), m_bv_util.mk_bv_sub(e_sig, shifted_f_sig), sum); SASSERT(is_well_sorted(m, sum)); dbg_decouple("fpa2bv_fma_add_sum", sum); expr_ref sign_bv(m), n_sum(m); sign_bv = m_bv_util.mk_extract(2*sbits+1, 2*sbits+1, sum); n_sum = m_bv_util.mk_bv_neg(sum); expr_ref res_sig_eq(m), sig_abs(m), one_1(m); one_1 = m_bv_util.mk_numeral(1, 1); m_simp.mk_eq(sign_bv, one_1, res_sig_eq); m_simp.mk_ite(res_sig_eq, n_sum, sum, sig_abs); dbg_decouple("fpa2bv_fma_add_sign_bv", sign_bv); dbg_decouple("fpa2bv_fma_add_n_sum", n_sum); dbg_decouple("fpa2bv_fma_add_sig_abs", sig_abs); res_exp = e_exp; // Result could overflow into 4.xxx ... family_id bvfid = m_bv_util.get_fid(); expr_ref res_sgn_c1(m), res_sgn_c2(m), res_sgn_c3(m); expr_ref not_e_sgn(m), not_f_sgn(m), not_sign_bv(m); not_e_sgn = m_bv_util.mk_bv_not(e_sgn); not_f_sgn = m_bv_util.mk_bv_not(f_sgn); not_sign_bv = m_bv_util.mk_bv_not(sign_bv); res_sgn_c1 = m.mk_app(bvfid, OP_BAND, not_e_sgn, f_sgn, sign_bv); res_sgn_c2 = m.mk_app(bvfid, OP_BAND, e_sgn, not_f_sgn, not_sign_bv); res_sgn_c3 = m.mk_app(bvfid, OP_BAND, e_sgn, f_sgn); expr * res_sgn_or_args[3] = { res_sgn_c1, res_sgn_c2, res_sgn_c3 }; res_sgn = m_bv_util.mk_bv_or(3, res_sgn_or_args); if (sbits > 5) { sticky_raw = m_bv_util.mk_extract(sbits - 5, 0, sig_abs); sticky = m_bv_util.mk_zero_extend(sbits + 3, m.mk_app(bvfid, OP_BREDOR, sticky_raw.get())); expr * res_or_args[2] = { m_bv_util.mk_extract(2 * sbits - 1, sbits - 4, sig_abs), sticky }; res_sig = m_bv_util.mk_bv_or(2, res_or_args); } else { unsigned too_short = 6 - sbits; sig_abs = m_bv_util.mk_concat(sig_abs, m_bv_util.mk_numeral(0, too_short)); res_sig = m_bv_util.mk_extract(sbits + 3, 0, sig_abs); } dbg_decouple("fpa2bv_fma_add_sum_sticky", sticky); SASSERT(m_bv_util.get_bv_size(res_sig) == sbits + 4); expr_ref is_zero_sig(m), nil_sbits4(m); nil_sbits4 = m_bv_util.mk_numeral(0, sbits+4); m_simp.mk_eq(res_sig, nil_sbits4, is_zero_sig); SASSERT(is_well_sorted(m, is_zero_sig)); dbg_decouple("fpa2bv_fma_is_zero_sig", is_zero_sig); expr_ref zero_case(m); mk_ite(rm_is_to_neg, nzero, pzero, zero_case); expr_ref rounded(m); round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); mk_ite(is_zero_sig, zero_case, rounded, v8); // And finally, we tie them together. mk_ite(c7, v7, v8, result); mk_ite(c6, v6, result, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_fma_", tout << "FMA = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_sqrt(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr_ref rm(m), x(m); rm = args[0]; x = args[1]; expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(f, nan); mk_nzero(f, nzero); mk_pzero(f, pzero); mk_ninf(f, ninf); mk_pinf(f, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_inf(x, x_is_inf); expr_ref zero1(m), one1(m); zero1 = m_bv_util.mk_numeral(0, 1); one1 = m_bv_util.mk_numeral(1, 1); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); // (x is NaN) -> NaN c1 = x_is_nan; v1 = x; // (x is +oo) -> +oo mk_is_pinf(x, c2); v2 = x; // (x is +-0) -> +-0 mk_is_zero(x, c3); v3 = x; // (x < 0) -> NaN mk_is_neg(x, c4); v4 = nan; // else comes the actual square root. unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); dbg_decouple("fpa2bv_sqrt_sig", a_sig); dbg_decouple("fpa2bv_sqrt_exp", a_exp); SASSERT(m_bv_util.get_bv_size(a_sig) == sbits); SASSERT(m_bv_util.get_bv_size(a_exp) == ebits); expr_ref res_sgn(m), res_sig(m), res_exp(m); res_sgn = zero1; expr_ref real_exp(m); real_exp = m_bv_util.mk_bv_sub(m_bv_util.mk_sign_extend(1, a_exp), m_bv_util.mk_zero_extend(1, a_lz)); res_exp = m_bv_util.mk_sign_extend(2, m_bv_util.mk_extract(ebits, 1, real_exp)); expr_ref e_is_odd(m); e_is_odd = m.mk_eq(m_bv_util.mk_extract(0, 0, real_exp), one1); dbg_decouple("fpa2bv_sqrt_e_is_odd", e_is_odd); dbg_decouple("fpa2bv_sqrt_real_exp", real_exp); expr_ref sig_prime(m); m_simp.mk_ite(e_is_odd, m_bv_util.mk_concat(a_sig, zero1), m_bv_util.mk_concat(zero1, a_sig), sig_prime); SASSERT(m_bv_util.get_bv_size(sig_prime) == sbits+1); dbg_decouple("fpa2bv_sqrt_sig_prime", sig_prime); // This is algorithm 10.2 in the Handbook of Floating-Point Arithmetic expr_ref Q(m), R(m), S(m), T(m); const mpz & p2 = fu().fm().m_powers2(sbits+3); Q = m_bv_util.mk_numeral(p2, sbits+5); R = m_bv_util.mk_bv_sub(m_bv_util.mk_concat(sig_prime, m_bv_util.mk_numeral(0, 4)), Q); S = Q; for (unsigned i = 0; i < sbits + 3; i++) { dbg_decouple("fpa2bv_sqrt_Q", Q); dbg_decouple("fpa2bv_sqrt_R", R); S = m_bv_util.mk_concat(zero1, m_bv_util.mk_extract(sbits+4, 1, S)); expr_ref twoQ_plus_S(m); twoQ_plus_S = m_bv_util.mk_bv_add(m_bv_util.mk_concat(Q, zero1), m_bv_util.mk_concat(zero1, S)); T = m_bv_util.mk_bv_sub(m_bv_util.mk_concat(R, zero1), twoQ_plus_S); dbg_decouple("fpa2bv_sqrt_T", T); SASSERT(m_bv_util.get_bv_size(Q) == sbits + 5); SASSERT(m_bv_util.get_bv_size(R) == sbits + 5); SASSERT(m_bv_util.get_bv_size(S) == sbits + 5); SASSERT(m_bv_util.get_bv_size(T) == sbits + 6); expr_ref t_lt_0(m); m_simp.mk_eq(m_bv_util.mk_extract(sbits+5, sbits+5, T), one1, t_lt_0); expr * or_args[2] = { Q, S }; m_simp.mk_ite(t_lt_0, Q, m_bv_util.mk_bv_or(2, or_args), Q); m_simp.mk_ite(t_lt_0, m_bv_util.mk_concat(m_bv_util.mk_extract(sbits+3, 0, R), zero1), m_bv_util.mk_extract(sbits+4, 0, T), R); } expr_ref is_exact(m); m_simp.mk_eq(R, m_bv_util.mk_numeral(0, sbits+5), is_exact); dbg_decouple("fpa2bv_sqrt_is_exact", is_exact); expr_ref rest(m), last(m), q_is_odd(m), rest_ext(m); last = m_bv_util.mk_extract(0, 0, Q); rest = m_bv_util.mk_extract(sbits+3, 1, Q); dbg_decouple("fpa2bv_sqrt_last", last); dbg_decouple("fpa2bv_sqrt_rest", rest); rest_ext = m_bv_util.mk_zero_extend(1, rest); expr_ref sticky(m); m_simp.mk_ite(is_exact, m_bv_util.mk_zero_extend(sbits+3, last), m_bv_util.mk_numeral(1, sbits+4), sticky); expr * or_args[2] = { rest_ext, sticky }; res_sig = m_bv_util.mk_bv_or(2, or_args); SASSERT(m_bv_util.get_bv_size(res_sig) == sbits + 4); expr_ref rounded(m); round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); v5 = rounded; // And finally, we tie them together. mk_ite(c4, v4, v5, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr_ref rm(m), x(m); rm = args[0]; x = args[1]; expr_ref rm_is_rta(m), rm_is_rte(m), rm_is_rtp(m), rm_is_rtn(m), rm_is_rtz(m); mk_is_rm(rm, BV_RM_TIES_TO_AWAY, rm_is_rta); mk_is_rm(rm, BV_RM_TIES_TO_EVEN, rm_is_rte); mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_is_rtp); mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_rtn); mk_is_rm(rm, BV_RM_TO_ZERO, rm_is_rtz); expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(f, nan); mk_nzero(f, nzero); mk_pzero(f, pzero); expr_ref x_is_zero(m), x_is_pos(m), x_is_neg(m); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_neg(x, x_is_neg); dbg_decouple("fpa2bv_r2i_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_r2i_x_is_pos", x_is_pos); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m); // (x is NaN) -> NaN mk_is_nan(x, c1); v1 = nan; // (x is +-oo) -> x mk_is_inf(x, c2); v2 = x; // (x is +-0) -> x ; -0.0 -> -0.0, says IEEE754, Sec 5.9. mk_is_zero(x, c3); v3 = x; expr_ref one_1(m), zero_1(m); one_1 = m_bv_util.mk_numeral(1, 1); zero_1 = m_bv_util.mk_numeral(0, 1); unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); dbg_decouple("fpa2bv_r2i_unpacked_sig", a_sig); dbg_decouple("fpa2bv_r2i_unpacked_exp", a_exp); expr_ref xzero(m); mk_ite(m.mk_eq(a_sgn, one_1), nzero, pzero, xzero); // exponent < 0 -> 0/1 expr_ref exp_lt_zero(m), exp_h(m); exp_h = m_bv_util.mk_extract(ebits-1, ebits-1, a_exp); m_simp.mk_eq(exp_h, one_1, exp_lt_zero); dbg_decouple("fpa2bv_r2i_exp_lt_zero", exp_lt_zero); c4 = exp_lt_zero; expr_ref pone(m), none(m), xone(m), c421(m), c422(m), c423(m), t1(m), t2(m), tie(m), v42(m), exp_lt_m1(m); mk_one(f, zero_1, pone); mk_one(f, one_1, none); mk_ite(m.mk_eq(a_sgn, one_1), none, pone, xone); m_simp.mk_eq(a_sig, m_bv_util.mk_numeral(fu().fm().m_powers2(sbits-1), sbits), t1); m_simp.mk_eq(a_exp, m_bv_util.mk_numeral(-1, ebits), t2); m_simp.mk_and(t1, t2, tie); dbg_decouple("fpa2bv_r2i_c42_tie", tie); m_simp.mk_and(tie, rm_is_rte, c421); m_simp.mk_and(tie, rm_is_rta, c422); c423 = m_bv_util.mk_sle(a_exp, m_bv_util.mk_numeral(-2, ebits)); dbg_decouple("fpa2bv_r2i_c421", c421); dbg_decouple("fpa2bv_r2i_c422", c422); dbg_decouple("fpa2bv_r2i_c423", c423); v42 = xone; mk_ite(c423, xzero, v42, v42); mk_ite(c422, xone, v42, v42); mk_ite(c421, xzero, v42, v42); expr_ref v4_rtn(m), v4_rtp(m); mk_ite(x_is_neg, nzero, pone, v4_rtp); mk_ite(x_is_neg, none, pzero, v4_rtn); mk_ite(rm_is_rtp, v4_rtp, v42, v4); mk_ite(rm_is_rtn, v4_rtn, v4, v4); mk_ite(rm_is_rtz, xzero, v4, v4); SASSERT(is_well_sorted(m, v4)); // exponent >= sbits-1 expr_ref exp_is_large(m); exp_is_large = m_bv_util.mk_sle(m_bv_util.mk_numeral(sbits-1, ebits), a_exp); dbg_decouple("fpa2bv_r2i_exp_is_large", exp_is_large); c5 = exp_is_large; v5 = x; // Actual conversion with rounding. // x.exponent >= 0 && x.exponent < x.sbits - 1 expr_ref res_sgn(m), res_sig(m), res_exp(m); res_sgn = a_sgn; res_exp = a_exp; expr_ref shift(m), rshift(m), div(m), rem(m); shift = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(sbits - 1, sbits + 1), m_bv_util.mk_sign_extend(sbits - ebits + 1, a_exp)); rshift = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(sbits, sbits + 1), shift); div = m_bv_util.mk_bv_lshr(m_bv_util.mk_zero_extend(1, a_sig), shift); rem = m_bv_util.mk_bv_lshr(m_bv_util.mk_bv_shl(m_bv_util.mk_zero_extend(1, a_sig), rshift), rshift); SASSERT(is_well_sorted(m, div)); SASSERT(is_well_sorted(m, rem)); SASSERT(m_bv_util.get_bv_size(div) == sbits + 1); SASSERT(m_bv_util.get_bv_size(rem) == sbits + 1); dbg_decouple("fpa2bv_r2i_shift", shift); dbg_decouple("fpa2bv_r2i_rshift", rshift); dbg_decouple("fpa2bv_r2i_div", div); dbg_decouple("fpa2bv_r2i_rem", rem); expr_ref div_p1(m); div_p1 = m_bv_util.mk_bv_add(div, m_bv_util.mk_numeral(1, sbits+1)); expr_ref tie2(m), tie2_c(m), div_last(m), v51(m), rem_shl(m); rem_shl = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits - 1, 0, rem), zero_1); m_simp.mk_eq(rem_shl, m_bv_util.mk_bv_shl(m_bv_util.mk_numeral(1, sbits+1), shift), tie2); div_last = m_bv_util.mk_extract(0, 0, div); tie2_c = m.mk_or(m.mk_and(tie2, m.mk_or(m.mk_and(rm_is_rte, m.mk_eq(div_last, one_1)), m.mk_and(rm_is_rta, m.mk_eq(div_last, zero_1)))), m.mk_xor(m.mk_eq(a_sgn, one_1), m_bv_util.mk_sle(m_bv_util.mk_bv_shl(m_bv_util.mk_numeral(1, sbits + 1), shift), rem_shl))); m_simp.mk_ite(tie2_c, div_p1, div, v51); dbg_decouple("fpa2bv_r2i_v51", v51); dbg_decouple("fpa2bv_r2i_tie2", tie2); SASSERT(is_well_sorted(m, tie2)); SASSERT(is_well_sorted(m, tie2_c)); SASSERT(is_well_sorted(m, v51)); expr_ref c521(m), v52(m); m_simp.mk_not(m.mk_eq(rem, m_bv_util.mk_numeral(0, sbits+1)), c521); m_simp.mk_and(c521, m.mk_eq(res_sgn, zero_1), c521); m_simp.mk_ite(c521, div_p1, div, v52); expr_ref c531(m), v53(m); m_simp.mk_not(m.mk_eq(rem, m_bv_util.mk_numeral(0, sbits+1)), c531); m_simp.mk_and(c531, m.mk_eq(res_sgn, one_1), c531); m_simp.mk_ite(c531, div_p1, div, v53); expr_ref c51(m), c52(m), c53(m); c51 = m.mk_or(rm_is_rte, rm_is_rta); c52 = rm_is_rtp; c53 = rm_is_rtn; res_sig = div; m_simp.mk_ite(c53, v53, res_sig, res_sig); m_simp.mk_ite(c52, v52, res_sig, res_sig); m_simp.mk_ite(c51, v51, res_sig, res_sig); res_sig = m_bv_util.mk_concat(res_sig, m_bv_util.mk_numeral(0, 3)); // rounding bits are all 0. SASSERT(m_bv_util.get_bv_size(res_exp) == ebits); SASSERT(m_bv_util.get_bv_size(shift) == sbits + 1); expr_ref e_shift(m); e_shift = (ebits + 2 <= sbits + 1) ? m_bv_util.mk_extract(ebits + 1, 0, shift) : m_bv_util.mk_sign_extend((ebits + 2) - (sbits + 1), shift); SASSERT(m_bv_util.get_bv_size(e_shift) == ebits + 2); res_exp = m_bv_util.mk_bv_add(m_bv_util.mk_zero_extend(2, res_exp), e_shift); SASSERT(m_bv_util.get_bv_size(res_sgn) == 1); SASSERT(m_bv_util.get_bv_size(res_sig) == sbits + 4); SASSERT(m_bv_util.get_bv_size(res_exp) == ebits + 2); // CMW: We use the rounder for normalization. round(f->get_range(), rm, res_sgn, res_sig, res_exp, v6); // And finally, we tie them together. mk_ite(c5, v5, v6, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_round_to_integral", tout << "ROUND2INTEGRAL = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_float_eq(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr * x = args[0], * y = args[1]; TRACE("fpa2bv_float_eq", tout << "X = " << mk_ismt2_pp(x, m) << std::endl; tout << "Y = " << mk_ismt2_pp(y, m) << std::endl;); expr_ref c1(m), c2(m), x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m); mk_is_nan(x, x_is_nan); mk_is_nan(y, y_is_nan); m_simp.mk_or(x_is_nan, y_is_nan, c1); mk_is_zero(x, x_is_zero); mk_is_zero(y, y_is_zero); m_simp.mk_and(x_is_zero, y_is_zero, c2); expr * x_sgn, * x_sig, * x_exp; expr * y_sgn, * y_sig, * y_exp; split_fp(x, x_sgn, x_exp, x_sig); split_fp(y, y_sgn, y_exp, y_sig); expr_ref x_eq_y_sgn(m), x_eq_y_exp(m), x_eq_y_sig(m); m_simp.mk_eq(x_sgn, y_sgn, x_eq_y_sgn); m_simp.mk_eq(x_exp, y_exp, x_eq_y_exp); m_simp.mk_eq(x_sig, y_sig, x_eq_y_sig); expr_ref c3(m), t4(m); m_simp.mk_not(x_eq_y_sgn, c3); m_simp.mk_and(x_eq_y_exp, x_eq_y_sig, t4); expr_ref c3t4(m), c2else(m); m_simp.mk_ite(c3, m.mk_false(), t4, c3t4); m_simp.mk_ite(c2, m.mk_true(), c3t4, c2else); m_simp.mk_ite(c1, m.mk_false(), c2else, result); TRACE("fpa2bv_float_eq", tout << "FLOAT_EQ = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_float_lt(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr * x = args[0], * y = args[1]; expr_ref c1(m), c2(m), x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m); mk_is_nan(x, x_is_nan); mk_is_nan(y, y_is_nan); m_simp.mk_or(x_is_nan, y_is_nan, c1); mk_is_zero(x, x_is_zero); mk_is_zero(y, y_is_zero); m_simp.mk_and(x_is_zero, y_is_zero, c2); expr * x_sgn, * x_sig, * x_exp; expr * y_sgn, * y_sig, * y_exp; split_fp(x, x_sgn, x_exp, x_sig); split_fp(y, y_sgn, y_exp, y_sig); expr_ref c3(m), t3(m), t4(m), one_1(m), nil_1(m); one_1 = m_bv_util.mk_numeral(1, 1); nil_1 = m_bv_util.mk_numeral(0, 1); m_simp.mk_eq(x_sgn, one_1, c3); expr_ref y_sgn_eq_0(m), y_lt_x_exp(m), y_lt_x_sig(m), y_eq_x_exp(m), y_le_x_sig_exp(m), t3_or(m); m_simp.mk_eq(y_sgn, nil_1, y_sgn_eq_0); BVULT(y_exp, x_exp, y_lt_x_exp); BVULT(y_sig, x_sig, y_lt_x_sig); m_simp.mk_eq(y_exp, x_exp, y_eq_x_exp); m_simp.mk_and(y_eq_x_exp, y_lt_x_sig, y_le_x_sig_exp); m_simp.mk_or(y_lt_x_exp, y_le_x_sig_exp, t3_or); m_simp.mk_ite(y_sgn_eq_0, m.mk_true(), t3_or, t3); expr_ref y_sgn_eq_1(m), x_lt_y_exp(m), x_eq_y_exp(m), x_lt_y_sig(m), x_le_y_sig_exp(m), t4_or(m); m_simp.mk_eq(y_sgn, one_1, y_sgn_eq_1); BVULT(x_exp, y_exp, x_lt_y_exp); m_simp.mk_eq(x_exp, y_exp, x_eq_y_exp); BVULT(x_sig, y_sig, x_lt_y_sig); m_simp.mk_and(x_eq_y_exp, x_lt_y_sig, x_le_y_sig_exp); m_simp.mk_or(x_lt_y_exp, x_le_y_sig_exp, t4_or); m_simp.mk_ite(y_sgn_eq_1, m.mk_false(), t4_or, t4); expr_ref c3t3t4(m), c2else(m); m_simp.mk_ite(c3, t3, t4, c3t3t4); m_simp.mk_ite(c2, m.mk_false(), c3t3t4, c2else); m_simp.mk_ite(c1, m.mk_false(), c2else, result); } void fpa2bv_converter::mk_float_gt(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr * x = args[0], * y = args[1]; expr_ref t3(m); mk_float_le(f, num, args, t3); expr_ref nan_or(m), xy_zero(m), not_t3(m), r_else(m); expr_ref x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m); mk_is_nan(x, x_is_nan); mk_is_nan(y, y_is_nan); m_simp.mk_or(x_is_nan, y_is_nan, nan_or); mk_is_zero(x, x_is_zero); mk_is_zero(y, y_is_zero); m_simp.mk_and(x_is_zero, y_is_zero, xy_zero); m_simp.mk_not(t3, not_t3); m_simp.mk_ite(xy_zero, m.mk_false(), not_t3, r_else); m_simp.mk_ite(nan_or, m.mk_false(), r_else, result); } void fpa2bv_converter::mk_float_le(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr_ref a(m), b(m); mk_float_lt(f, num, args, a); mk_float_eq(f, num, args, b); m_simp.mk_or(a, b, result); } void fpa2bv_converter::mk_float_ge(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr_ref a(m), b(m); mk_float_gt(f, num, args, a); mk_float_eq(f, num, args, b); m_simp.mk_or(a, b, result); } void fpa2bv_converter::mk_is_zero(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); mk_is_zero(args[0], result); } void fpa2bv_converter::mk_is_nzero(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); expr_ref a0_is_neg(m), a0_is_zero(m); mk_is_neg(args[0], a0_is_neg); mk_is_zero(args[0], a0_is_zero); m_simp.mk_and(a0_is_neg, a0_is_zero, result); } void fpa2bv_converter::mk_is_pzero(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); expr_ref a0_is_pos(m), a0_is_zero(m); mk_is_pos(args[0], a0_is_pos); mk_is_zero(args[0], a0_is_zero); m_simp.mk_and(a0_is_pos, a0_is_zero, result); } void fpa2bv_converter::mk_is_nan(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); mk_is_nan(args[0], result); } void fpa2bv_converter::mk_is_inf(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); mk_is_inf(args[0], result); } void fpa2bv_converter::mk_is_normal(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); mk_is_normal(args[0], result); } void fpa2bv_converter::mk_is_subnormal(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); mk_is_denormal(args[0], result); } void fpa2bv_converter::mk_is_negative(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); expr_ref t1(m), t2(m); mk_is_nan(args[0], t1); mk_is_neg(args[0], t2); result = m.mk_and(m.mk_not(t1), t2); TRACE("fpa2bv_is_negative", tout << "result = " << mk_ismt2_pp(result, m) << std::endl;); } void fpa2bv_converter::mk_is_positive(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); expr_ref t1(m), t2(m); mk_is_nan(args[0], t1); mk_is_pos(args[0], t2); result = m.mk_and(m.mk_not(t1), t2); } void fpa2bv_converter::mk_to_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_fp", for (unsigned i=0; i < num; i++) tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl; ); if (num == 1 && m_bv_util.is_bv(args[0])) { sort * s = f->get_range(); unsigned to_sbits = m_util.get_sbits(s); unsigned to_ebits = m_util.get_ebits(s); expr * bv = args[0]; int sz = m_bv_util.get_bv_size(bv); SASSERT((unsigned)sz == to_sbits + to_ebits); mk_fp(m_bv_util.mk_extract(sz - 1, sz - 1, bv), m_bv_util.mk_extract(sz - 2, sz - to_ebits - 1, bv), m_bv_util.mk_extract(sz - to_ebits - 2, 0, bv), result); } else if (num == 2 && m_bv_util.is_bv(args[0]) && m_bv_util.get_bv_size(args[0]) == 3 && m_util.is_float(m.get_sort(args[1]))) { // rm + float -> float mk_to_fp_float(f, f->get_range(), args[0], args[1], result); } else if (num == 2 && m_bv_util.is_bv(args[0]) && m_bv_util.get_bv_size(args[0]) == 3 && (m_arith_util.is_int(args[1]) || m_arith_util.is_real(args[1]))) { // rm + real -> float mk_to_fp_real(f, f->get_range(), args[0], args[1], result); } else if (num == 2 && m_bv_util.is_bv(args[0]) && m_bv_util.get_bv_size(args[0]) == 3 && m_bv_util.is_bv(args[1])) { // rm + signed bv -> float mk_to_fp_signed(f, num, args, result); } else if (num == 3 && m_bv_util.is_bv(args[0]) && m_bv_util.is_bv(args[1]) && m_bv_util.is_bv(args[2])) { // 3 BV -> float SASSERT(m_bv_util.get_bv_size(args[0]) == 1); SASSERT(m_util.get_ebits(f->get_range()) == m_bv_util.get_bv_size(args[1])); SASSERT(m_util.get_sbits(f->get_range()) == m_bv_util.get_bv_size(args[2])+1); mk_fp(args[0], args[1], args[2], result); } else if (num == 3 && m_bv_util.is_bv(args[0]) && m_bv_util.get_bv_size(args[0]) == 3 && m_arith_util.is_numeral(args[1]) && m_arith_util.is_numeral(args[2])) { // rm + real + int -> float mk_to_fp_real_int(f, num, args, result); } else UNREACHABLE(); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_fp_float(func_decl * f, sort * s, expr * rm, expr * x, expr_ref & result) { unsigned from_sbits = m_util.get_sbits(m.get_sort(x)); unsigned from_ebits = m_util.get_ebits(m.get_sort(x)); unsigned to_sbits = m_util.get_sbits(s); unsigned to_ebits = m_util.get_ebits(s); if (from_sbits == to_sbits && from_ebits == to_ebits) result = x; else { expr_ref c1(m), c2(m), c3(m), c4(m), c5(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m); expr_ref one1(m); one1 = m_bv_util.mk_numeral(1, 1); expr_ref ninf(m), pinf(m); mk_pinf(f, pinf); mk_ninf(f, ninf); // NaN -> NaN mk_is_nan(x, c1); mk_nan(f, v1); // +0 -> +0 mk_is_pzero(x, c2); mk_pzero(f, v2); // -0 -> -0 mk_is_nzero(x, c3); mk_nzero(f, v3); // +oo -> +oo mk_is_pinf(x, c4); v4 = pinf; // -oo -> -oo mk_is_ninf(x, c5); v5 = ninf; // otherwise: the actual conversion with rounding. expr_ref sgn(m), sig(m), exp(m), lz(m); unpack(x, sgn, sig, exp, lz, true); dbg_decouple("fpa2bv_to_float_x_sgn", sgn); dbg_decouple("fpa2bv_to_float_x_sig", sig); dbg_decouple("fpa2bv_to_float_x_exp", exp); dbg_decouple("fpa2bv_to_float_lz", lz); expr_ref res_sgn(m), res_sig(m), res_exp(m); res_sgn = sgn; SASSERT(m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.get_bv_size(sig) == from_sbits); SASSERT(m_bv_util.get_bv_size(exp) == from_ebits); SASSERT(m_bv_util.get_bv_size(lz) == from_ebits); if (from_sbits < (to_sbits + 3)) { // make sure that sig has at least to_sbits + 3 res_sig = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, to_sbits + 3 - from_sbits)); } else if (from_sbits > (to_sbits + 3)) { // collapse the extra bits into a sticky bit. expr_ref sticky(m), low(m), high(m); high = m_bv_util.mk_extract(from_sbits - 1, from_sbits - to_sbits - 2, sig); SASSERT(m_bv_util.get_bv_size(high) == to_sbits + 2); low = m_bv_util.mk_extract(from_sbits - to_sbits - 3, 0, sig); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, low.get()); SASSERT(m_bv_util.get_bv_size(sticky) == 1); dbg_decouple("fpa2bv_to_float_sticky", sticky); res_sig = m_bv_util.mk_concat(high, sticky); SASSERT(m_bv_util.get_bv_size(res_sig) == to_sbits + 3); } else res_sig = sig; res_sig = m_bv_util.mk_zero_extend(1, res_sig); // extra zero in the front for the rounder. unsigned sig_sz = m_bv_util.get_bv_size(res_sig); SASSERT(sig_sz == to_sbits + 4); expr_ref exponent_overflow(m), exponent_underflow(m); exponent_overflow = m.mk_false(); exponent_underflow = m.mk_false(); if (from_ebits < (to_ebits + 2)) { res_exp = m_bv_util.mk_sign_extend(to_ebits - from_ebits + 2, exp); // subtract lz for subnormal numbers. expr_ref lz_ext(m); lz_ext = m_bv_util.mk_zero_extend(to_ebits - from_ebits + 2, lz); res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext); } else if (from_ebits > (to_ebits + 2)) { expr_ref lz_rest(m), lz_redor(m), lz_redor_bool(m); lz_rest = m_bv_util.mk_extract(from_ebits - 1, to_ebits + 2, lz); lz_redor = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, lz_rest.get()); m_simp.mk_eq(lz_redor, one1, lz_redor_bool); dbg_decouple("fpa2bv_to_float_exp_lz_redor", lz_redor); // subtract lz for subnormal numbers. expr_ref exp_sub_lz(m); exp_sub_lz = m_bv_util.mk_bv_sub(exp, lz); dbg_decouple("fpa2bv_to_float_exp_sub_lz", exp_sub_lz); expr_ref high(m), low(m), low_msb(m); expr_ref no_ovf(m), zero1(m); high = m_bv_util.mk_extract(from_ebits - 1, to_ebits + 2, exp_sub_lz); low = m_bv_util.mk_extract(to_ebits + 1, 0, exp_sub_lz); low_msb = m_bv_util.mk_extract(to_ebits + 1, to_ebits + 1, low); dbg_decouple("fpa2bv_to_float_exp_high", high); dbg_decouple("fpa2bv_to_float_exp_low", low); dbg_decouple("fpa2bv_to_float_exp_low_msb", low_msb); res_exp = low; expr_ref high_red_or(m), high_red_and(m); high_red_or = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, high.get()); high_red_and = m.mk_app(m_bv_util.get_fid(), OP_BREDAND, high.get()); expr_ref h_or_eq_0(m), h_and_eq_1(m), low_msb_is_one(m), low_msb_is_zero(m); zero1 = m_bv_util.mk_numeral(0, 1); m_simp.mk_eq(high_red_and, one1, h_and_eq_1); m_simp.mk_eq(high_red_or, zero1, h_or_eq_0); m_simp.mk_eq(low_msb, zero1, low_msb_is_zero); m_simp.mk_eq(low_msb, one1, low_msb_is_one); dbg_decouple("fpa2bv_to_float_exp_h_and_eq_1", h_and_eq_1); dbg_decouple("fpa2bv_to_float_exp_h_or_eq_0", h_or_eq_0); dbg_decouple("fpa2bv_to_float_exp_s_is_zero", low_msb_is_zero); dbg_decouple("fpa2bv_to_float_exp_s_is_one", low_msb_is_one); m_simp.mk_and(h_or_eq_0, low_msb_is_one, exponent_underflow); m_simp.mk_and(h_and_eq_1, low_msb_is_zero, exponent_overflow); m_simp.mk_or(exponent_overflow, lz_redor_bool, exponent_overflow); dbg_decouple("fpa2bv_to_float_exp_ovf", exponent_overflow); dbg_decouple("fpa2bv_to_float_exp_udf", exponent_underflow); // exponent underflow means that the result is the smallest // representable float, rounded according to rm. m_simp.mk_ite(exponent_underflow, m_bv_util.mk_concat(m_bv_util.mk_numeral(1, 1), m_bv_util.mk_numeral(1, to_ebits+1)), res_exp, res_exp); m_simp.mk_ite(exponent_underflow, m_bv_util.mk_numeral(1, to_sbits+4), res_sig, res_sig); } else // from_ebits == (to_ebits + 2) res_exp = m_bv_util.mk_bv_sub(exp, lz); SASSERT(m_bv_util.get_bv_size(res_exp) == to_ebits + 2); SASSERT(is_well_sorted(m, res_exp)); dbg_decouple("fpa2bv_to_float_res_sig", res_sig); dbg_decouple("fpa2bv_to_float_res_exp", res_exp); expr_ref rounded(m); expr_ref rm_e(rm, m); round(s, rm_e, res_sgn, res_sig, res_exp, rounded); expr_ref is_neg(m), sig_inf(m); m_simp.mk_eq(sgn, one1, is_neg); mk_ite(is_neg, ninf, pinf, sig_inf); mk_ite(exponent_overflow, sig_inf, rounded, v6); // And finally, we tie them together. mk_ite(c5, v5, v6, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); } SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr * x, expr_ref & result) { TRACE("fpa2bv_to_fp_real", tout << "rm: " << mk_ismt2_pp(rm, m) << std::endl << "x: " << mk_ismt2_pp(x, m) << std::endl;); SASSERT(m_util.is_float(s)); SASSERT(au().is_real(x) || au().is_int(x)); unsigned ebits = m_util.get_ebits(s); unsigned sbits = m_util.get_sbits(s); if (m_bv_util.is_numeral(rm) && m_util.au().is_numeral(x)) { rational tmp_rat; unsigned sz; m_bv_util.is_numeral(to_expr(rm), tmp_rat, sz); SASSERT(tmp_rat.is_int32()); SASSERT(sz == 3); BV_RM_VAL bv_rm = (BV_RM_VAL)tmp_rat.get_unsigned(); mpf_rounding_mode mrm; switch (bv_rm) { case BV_RM_TIES_TO_AWAY: mrm = MPF_ROUND_NEAREST_TAWAY; break; case BV_RM_TIES_TO_EVEN: mrm = MPF_ROUND_NEAREST_TEVEN; break; case BV_RM_TO_NEGATIVE: mrm = MPF_ROUND_TOWARD_NEGATIVE; break; case BV_RM_TO_POSITIVE: mrm = MPF_ROUND_TOWARD_POSITIVE; break; case BV_RM_TO_ZERO: mrm = MPF_ROUND_TOWARD_ZERO; break; default: UNREACHABLE(); } rational q; bool is_int; m_util.au().is_numeral(x, q, is_int); if (q.is_zero()) return mk_pzero(f, result); else { scoped_mpf v(m_mpf_manager); m_util.fm().set(v, ebits, sbits, mrm, q.to_mpq()); expr_ref sgn(m), sig(m), exp(m), unbiased_exp(m); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v)) ? 1 : 0, 1); sig = m_bv_util.mk_numeral(m_util.fm().sig(v), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v), ebits); mk_bias(unbiased_exp, exp); mk_fp(sgn, exp, sig, result); } } else if (m_util.au().is_numeral(x)) { rational q; bool is_int; m_util.au().is_numeral(x, q, is_int); if (m_util.au().is_zero(x)) mk_pzero(f, result); else { expr_ref rm_nta(m), rm_nte(m), rm_tp(m), rm_tn(m), rm_tz(m); mk_is_rm(rm, BV_RM_TIES_TO_AWAY, rm_nta); mk_is_rm(rm, BV_RM_TIES_TO_EVEN, rm_nte); mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_tp); mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_tn); mk_is_rm(rm, BV_RM_TO_ZERO, rm_tz); scoped_mpf v_nta(m_mpf_manager), v_nte(m_mpf_manager), v_tp(m_mpf_manager); scoped_mpf v_tn(m_mpf_manager), v_tz(m_mpf_manager); m_util.fm().set(v_nta, ebits, sbits, MPF_ROUND_NEAREST_TAWAY, q.to_mpq()); m_util.fm().set(v_nte, ebits, sbits, MPF_ROUND_NEAREST_TEVEN, q.to_mpq()); m_util.fm().set(v_tp, ebits, sbits, MPF_ROUND_TOWARD_POSITIVE, q.to_mpq()); m_util.fm().set(v_tn, ebits, sbits, MPF_ROUND_TOWARD_NEGATIVE, q.to_mpq()); m_util.fm().set(v_tz, ebits, sbits, MPF_ROUND_TOWARD_ZERO, q.to_mpq()); expr_ref v1(m), v2(m), v3(m), v4(m); expr_ref sgn(m), sig(m), exp(m), unbiased_exp(m); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_nta)) ? 1 : 0, 1); sig = m_bv_util.mk_numeral(m_util.fm().sig(v_nta), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_nta), ebits); mk_bias(unbiased_exp, exp); mk_fp(sgn, exp, sig, v1); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_nte)) ? 1 : 0, 1); sig = m_bv_util.mk_numeral(m_util.fm().sig(v_nte), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_nte), ebits); mk_bias(unbiased_exp, exp); mk_fp(sgn, exp, sig, v2); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_tp)) ? 1 : 0, 1); sig = m_bv_util.mk_numeral(m_util.fm().sig(v_tp), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_tp), ebits); mk_bias(unbiased_exp, exp); mk_fp(sgn, exp, sig, v3); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_tn)) ? 1 : 0, 1); sig = m_bv_util.mk_numeral(m_util.fm().sig(v_tn), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_tn), ebits); mk_bias(unbiased_exp, exp); mk_fp(sgn, exp, sig, v4); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_tp)) ? 1 : 0, 1); sig = m_bv_util.mk_numeral(m_util.fm().sig(v_tp), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_tp), ebits); mk_bias(unbiased_exp, exp); mk_fp(sgn, exp, sig, result); mk_ite(rm_tn, v4, result, result); mk_ite(rm_tp, v3, result, result); mk_ite(rm_nte, v2, result, result); mk_ite(rm_nta, v1, result, result); } } else { SASSERT(!m_arith_util.is_numeral(x)); bv_util & bu = m_bv_util; arith_util & au = m_arith_util; expr_ref bv0(m), bv1(m), zero(m), two(m); bv0 = bu.mk_numeral(0, 1); bv1 = bu.mk_numeral(1, 1); zero = au.mk_numeral(rational(0), false); two = au.mk_numeral(rational(2), false); expr_ref sgn(m), sig(m), exp(m); sgn = mk_fresh_const("fpa2bv_to_fp_real_sgn", 1); sig = mk_fresh_const("fpa2bv_to_fp_real_sig", sbits + 4); exp = mk_fresh_const("fpa2bv_to_fp_real_exp", ebits + 2); expr_ref rme(rm, m); round(s, rme, sgn, sig, exp, result); expr * e = m.mk_eq(m_util.mk_to_real(result), x); m_extra_assertions.push_back(e); } SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_fp_real_int(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { // rm + real + int -> float SASSERT(m_util.is_float(f->get_range())); unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); expr * rm = args[0]; rational q; if (!m_arith_util.is_numeral(args[1], q)) UNREACHABLE(); rational e; if (!m_arith_util.is_numeral(args[2], e)) UNREACHABLE(); SASSERT(e.is_int64()); SASSERT(m_mpz_manager.eq(e.to_mpq().denominator(), 1)); if (q.is_zero()) return mk_pzero(f, result); else { scoped_mpf nte(m_mpf_manager), nta(m_mpf_manager), tp(m_mpf_manager), tn(m_mpf_manager), tz(m_mpf_manager); m_mpf_manager.set(nte, ebits, sbits, MPF_ROUND_NEAREST_TEVEN, q.to_mpq(), e.to_mpq().numerator()); m_mpf_manager.set(nta, ebits, sbits, MPF_ROUND_NEAREST_TAWAY, q.to_mpq(), e.to_mpq().numerator()); m_mpf_manager.set(tp, ebits, sbits, MPF_ROUND_TOWARD_POSITIVE, q.to_mpq(), e.to_mpq().numerator()); m_mpf_manager.set(tn, ebits, sbits, MPF_ROUND_TOWARD_NEGATIVE, q.to_mpq(), e.to_mpq().numerator()); m_mpf_manager.set(tz, ebits, sbits, MPF_ROUND_TOWARD_ZERO, q.to_mpq(), e.to_mpq().numerator()); app_ref a_nte(m), a_nta(m), a_tp(m), a_tn(m), a_tz(m); a_nte = m_plugin->mk_numeral(nte); a_nta = m_plugin->mk_numeral(nta); a_tp = m_plugin->mk_numeral(tp); a_tn = m_plugin->mk_numeral(tn); a_tz = m_plugin->mk_numeral(tz); expr_ref bv_nte(m), bv_nta(m), bv_tp(m), bv_tn(m), bv_tz(m); mk_numeral(a_nte->get_decl(), 0, 0, bv_nte); mk_numeral(a_nta->get_decl(), 0, 0, bv_nta); mk_numeral(a_tp->get_decl(), 0, 0, bv_tp); mk_numeral(a_tn->get_decl(), 0, 0, bv_tn); mk_numeral(a_tz->get_decl(), 0, 0, bv_tz); expr_ref c1(m), c2(m), c3(m), c4(m); c1 = m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3)); c2 = m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3)); c3 = m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TIES_TO_AWAY, 3)); c4 = m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3)); mk_ite(c1, bv_tn, bv_tz, result); mk_ite(c2, bv_tp, result, result); mk_ite(c3, bv_nta, result, result); mk_ite(c4, bv_nte, result, result); } } void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_real", for (unsigned i = 0; i < num; i++) tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); SASSERT(num == 1); SASSERT(f->get_num_parameters() == 0); SASSERT(is_app_of(args[0], m_plugin->get_family_id(), OP_FPA_FP)); expr * x = args[0]; sort * s = m.get_sort(x); unsigned ebits = m_util.get_ebits(s); unsigned sbits = m_util.get_sbits(s); sort * rs = m_arith_util.mk_real(); expr_ref x_is_nan(m), x_is_inf(m), x_is_zero(m); mk_is_nan(x, x_is_nan); mk_is_inf(x, x_is_inf); mk_is_zero(x, x_is_zero); expr_ref sgn(m), sig(m), exp(m), lz(m); unpack(x, sgn, sig, exp, lz, true); // sig is of the form [1].[sigbits] SASSERT(m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.get_bv_size(sig) == sbits); SASSERT(m_bv_util.get_bv_size(exp) == ebits); expr_ref rsig(m), bit(m), zero(m), one(m), two(m), bv0(m), bv1(m); zero = m_arith_util.mk_numeral(rational(0), rs); one = m_arith_util.mk_numeral(rational(1), rs); two = m_arith_util.mk_numeral(rational(2), rs); bv0 = m_bv_util.mk_numeral(0, 1); bv1 = m_bv_util.mk_numeral(1, 1); rsig = one; for (unsigned i = sbits - 2; i != (unsigned)-1; i--) { bit = m_bv_util.mk_extract(i, i, sig); rsig = m_arith_util.mk_add(m_arith_util.mk_mul(rsig, two), m.mk_ite(m.mk_eq(bit, bv1), one, zero)); } const mpz & p2 = fu().fm().m_powers2(sbits - 1); expr_ref ep2(m); ep2 = m_arith_util.mk_numeral(rational(p2), false); rsig = m_arith_util.mk_div(rsig, ep2); dbg_decouple("fpa2bv_to_real_ep2", ep2); dbg_decouple("fpa2bv_to_real_rsig", rsig); expr_ref exp_n(m), exp_p(m), exp_is_neg(m), exp_abs(m); exp_is_neg = m.mk_eq(m_bv_util.mk_extract(ebits - 1, ebits - 1, exp), bv1); dbg_decouple("fpa2bv_to_real_exp_is_neg", exp_is_neg); exp_p = m_bv_util.mk_sign_extend(1, exp); exp_n = m_bv_util.mk_bv_neg(exp_p); exp_abs = m.mk_ite(exp_is_neg, exp_n, exp_p); dbg_decouple("fpa2bv_to_real_exp_abs", exp); SASSERT(m_bv_util.get_bv_size(exp_abs) == ebits + 1); expr_ref exp2(m), prev_bit(m); exp2 = zero; for (unsigned i = ebits; i != (unsigned)-1; i--) { bit = m_bv_util.mk_extract(i, i, exp_abs); exp2 = m_arith_util.mk_add(m_arith_util.mk_mul(exp2, two), m.mk_ite(m.mk_eq(bit, bv1), one, zero)); prev_bit = bit; } exp2 = m.mk_ite(exp_is_neg, m_arith_util.mk_div(one, exp2), exp2); dbg_decouple("fpa2bv_to_real_exp2", exp2); expr_ref res(m), two_exp2(m); two_exp2 = m_arith_util.mk_power(two, exp2); res = m_arith_util.mk_mul(rsig, two_exp2); res = m.mk_ite(m.mk_eq(sgn, bv1), m_arith_util.mk_uminus(res), res); dbg_decouple("fpa2bv_to_real_sig_times_exp2", res); TRACE("fpa2bv_to_real", tout << "rsig = " << mk_ismt2_pp(rsig, m) << std::endl; tout << "exp2 = " << mk_ismt2_pp(exp2, m) << std::endl;); result = m.mk_ite(x_is_zero, zero, res); result = m.mk_ite(x_is_inf, mk_to_real_unspecified(), result); result = m.mk_ite(x_is_nan, mk_to_real_unspecified(), result); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_fp_signed", for (unsigned i = 0; i < num; i++) tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); // This is a conversion from signed bitvector to float: // ; from signed machine integer, represented as a 2's complement bit vector // ((_ to_fp eb sb) RoundingMode (_ BitVec m) (_ FloatingPoint eb sb)) // Semantics: // Let b in[[(_ BitVec m)]] and let n be the signed integer represented by b (in 2's complement format). // [[(_ to_fp eb sb)]](r, b) = +/ -infinity if n is too large / too small to be represented as a finite // number of [[(_ FloatingPoint eb sb)]]; [[(_ to_fp eb sb)]](r, x) = y otherwise, where y is the finite // number such that [[fp.to_real]](y) is closest to n according to rounding mode r. SASSERT(num == 2); SASSERT(m_util.is_float(f->get_range())); SASSERT(m_bv_util.is_bv(args[0])); SASSERT(m_bv_util.is_bv(args[1])); expr_ref rm(m), x(m); rm = args[0]; x = args[1]; dbg_decouple("fpa2bv_to_fp_signed_x", x); unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); unsigned bv_sz = m_bv_util.get_bv_size(x); SASSERT(m_bv_util.get_bv_size(rm) == 3); expr_ref bv0_1(m), bv1_1(m), bv0_sz(m), bv1_sz(m); bv0_1 = m_bv_util.mk_numeral(0, 1); bv1_1 = m_bv_util.mk_numeral(1, 1); bv0_sz = m_bv_util.mk_numeral(0, bv_sz); bv1_sz = m_bv_util.mk_numeral(1, bv_sz); expr_ref is_zero(m), nzero(m), pzero(m), ninf(m), pinf(m); is_zero = m.mk_eq(x, bv0_sz); mk_nzero(f, nzero); mk_pzero(f, pzero); mk_ninf(f, ninf); mk_pinf(f, pinf); // Special case: x == 0 -> p/n zero expr_ref c1(m), v1(m); c1 = is_zero; v1 = pzero; // Special case: x != 0 expr_ref is_neg_bit(m), exp_too_large(m), sig_4(m), exp_2(m); expr_ref is_neg(m), x_abs(m); is_neg_bit = m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, x); is_neg = m.mk_eq(is_neg_bit, bv1_1); x_abs = m.mk_ite(is_neg, m_bv_util.mk_bv_neg(x), x); dbg_decouple("fpa2bv_to_fp_signed_is_neg", is_neg); // x_abs has an extra bit in the front. // x_abs is [bv_sz-1, bv_sz-2] . [bv_sz-3 ... 0] * 2^(bv_sz-2) // bv_sz-2 is the "1.0" bit for the rounder. expr_ref lz(m), e_bv_sz(m), e_rest_sz(m); mk_leading_zeros(x_abs, bv_sz, lz); e_bv_sz = m_bv_util.mk_numeral(bv_sz, bv_sz); e_rest_sz = m_bv_util.mk_bv_sub(e_bv_sz, lz); SASSERT(m_bv_util.get_bv_size(lz) == m_bv_util.get_bv_size(e_bv_sz)); dbg_decouple("fpa2bv_to_fp_signed_lz", lz); expr_ref shifted_sig(m); shifted_sig = m_bv_util.mk_bv_shl(x_abs, lz); expr_ref sticky(m); // shifted_sig is [bv_sz-1, bv_sz-2] . [bv_sz-3 ... 0] * 2^(bv_sz-2) * 2^(-lz) unsigned sig_sz = sbits + 4; // we want extra rounding bits. if (sig_sz <= bv_sz) { expr_ref sig_rest(m); sig_4 = m_bv_util.mk_extract(bv_sz - 1, bv_sz - sig_sz + 1, shifted_sig); // one short sig_rest = m_bv_util.mk_extract(bv_sz - sig_sz, 0, shifted_sig); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, sig_rest.get()); sig_4 = m_bv_util.mk_concat(sig_4, sticky); } else { unsigned extra_bits = sig_sz - bv_sz; expr_ref extra_zeros(m); extra_zeros = m_bv_util.mk_numeral(0, extra_bits); sig_4 = m_bv_util.mk_concat(shifted_sig, extra_zeros); lz = m_bv_util.mk_bv_add(m_bv_util.mk_concat(extra_zeros, lz), m_bv_util.mk_numeral(extra_bits, sig_sz)); bv_sz = bv_sz + extra_bits; SASSERT(is_well_sorted(m, lz)); } SASSERT(m_bv_util.get_bv_size(sig_4) == sig_sz); expr_ref s_exp(m), exp_rest(m); s_exp = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(bv_sz - 2, bv_sz), lz); // s_exp = (bv_sz-2) + (-lz) signed SASSERT(m_bv_util.get_bv_size(s_exp) == bv_sz); unsigned exp_sz = ebits + 2; // (+2 for rounder) exp_2 = m_bv_util.mk_extract(exp_sz - 1, 0, s_exp); // the remaining bits are 0 if ebits is large enough. exp_too_large = m.mk_false(); // The exponent is at most bv_sz, i.e., we need ld(bv_sz)+1 ebits. // exp < bv_sz (+sign bit which is [0]) unsigned exp_worst_case_sz = (unsigned)((log((double)bv_sz) / log((double)2)) + 1.0); TRACE("fpa2bv_to_fp_signed", tout << "exp worst case sz: " << exp_worst_case_sz << std::endl;); if (exp_sz < exp_worst_case_sz) { // exp_sz < exp_worst_case_sz and exp >= 0. // Take the maximum legal exponent; this // allows us to keep the most precision. expr_ref max_exp(m), max_exp_bvsz(m); mk_max_exp(exp_sz, max_exp); max_exp_bvsz = m_bv_util.mk_zero_extend(bv_sz - exp_sz, max_exp); exp_too_large = m_bv_util.mk_ule(m_bv_util.mk_bv_add( max_exp_bvsz, m_bv_util.mk_numeral(1, bv_sz)), s_exp); sig_4 = m.mk_ite(exp_too_large, m_bv_util.mk_numeral(0, sig_sz), sig_4); exp_2 = m.mk_ite(exp_too_large, max_exp, exp_2); } dbg_decouple("fpa2bv_to_fp_signed_exp_too_large", exp_too_large); expr_ref sgn(m), sig(m), exp(m); sgn = is_neg_bit; sig = sig_4; exp = exp_2; dbg_decouple("fpa2bv_to_fp_signed_sgn", sgn); dbg_decouple("fpa2bv_to_fp_signed_sig", sig); dbg_decouple("fpa2bv_to_fp_signed_exp", exp); SASSERT(m_bv_util.get_bv_size(sig) == sbits + 4); SASSERT(m_bv_util.get_bv_size(exp) == ebits + 2); expr_ref v2(m); round(f->get_range(), rm, sgn, sig, exp, v2); mk_ite(c1, v1, v2, result); } void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_fp_unsigned", for (unsigned i = 0; i < num; i++) tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); // This is a conversion from unsigned bitvector to float: // ((_ to_fp_unsigned eb sb) RoundingMode (_ BitVec m) (_ FloatingPoint eb sb)) // Semantics: // Let b in[[(_ BitVec m)]] and let n be the unsigned integer represented by b. // [[(_ to_fp_unsigned eb sb)]](r, x) = +infinity if n is too large to be // represented as a finite number of[[(_ FloatingPoint eb sb)]]; // [[(_ to_fp_unsigned eb sb)]](r, x) = y otherwise, where y is the finite number // such that[[fp.to_real]](y) is closest to n according to rounding mode r. SASSERT(num == 2); SASSERT(m_util.is_float(f->get_range())); SASSERT(m_bv_util.is_bv(args[0])); SASSERT(m_bv_util.is_bv(args[1])); expr_ref rm(m), x(m); rm = args[0]; x = args[1]; dbg_decouple("fpa2bv_to_fp_unsigned_x", x); unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); unsigned bv_sz = m_bv_util.get_bv_size(x); SASSERT(m_bv_util.get_bv_size(rm) == 3); expr_ref bv0_1(m), bv1_1(m), bv0_sz(m), bv1_sz(m); bv0_1 = m_bv_util.mk_numeral(0, 1); bv1_1 = m_bv_util.mk_numeral(1, 1); bv0_sz = m_bv_util.mk_numeral(0, bv_sz); bv1_sz = m_bv_util.mk_numeral(1, bv_sz); expr_ref is_zero(m), nzero(m), pzero(m), ninf(m), pinf(m); is_zero = m.mk_eq(x, bv0_sz); mk_nzero(f, nzero); mk_pzero(f, pzero); mk_ninf(f, ninf); mk_pinf(f, pinf); // Special case: x == 0 -> p/n zero expr_ref c1(m), v1(m); c1 = is_zero; v1 = pzero; // Special case: x != 0 expr_ref exp_too_large(m), sig_4(m), exp_2(m); // x is [bv_sz-1] . [bv_sz-2 ... 0] * 2^(bv_sz-1) // bv_sz-1 is the "1.0" bit for the rounder. expr_ref lz(m), e_bv_sz(m), e_rest_sz(m); mk_leading_zeros(x, bv_sz, lz); e_bv_sz = m_bv_util.mk_numeral(bv_sz, bv_sz); e_rest_sz = m_bv_util.mk_bv_sub(e_bv_sz, lz); SASSERT(m_bv_util.get_bv_size(lz) == m_bv_util.get_bv_size(e_bv_sz)); dbg_decouple("fpa2bv_to_fp_unsigned_lz", lz); expr_ref shifted_sig(m); shifted_sig = m_bv_util.mk_bv_shl(x, lz); expr_ref sticky(m); // shifted_sig is [bv_sz-1] . [bv_sz-2 ... 0] * 2^(bv_sz-1) * 2^(-lz) unsigned sig_sz = sbits + 4; // we want extra rounding bits. if (sig_sz <= bv_sz) { expr_ref sig_rest(m); sig_4 = m_bv_util.mk_extract(bv_sz - 1, bv_sz - sig_sz + 1, shifted_sig); // one short sig_rest = m_bv_util.mk_extract(bv_sz - sig_sz, 0, shifted_sig); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, sig_rest.get()); sig_4 = m_bv_util.mk_concat(sig_4, sticky); } else { unsigned extra_bits = sig_sz - bv_sz; expr_ref extra_zeros(m); extra_zeros = m_bv_util.mk_numeral(0, extra_bits); sig_4 = m_bv_util.mk_concat(shifted_sig, extra_zeros); lz = m_bv_util.mk_bv_add(m_bv_util.mk_concat(extra_zeros, lz), m_bv_util.mk_numeral(extra_bits, sig_sz)); bv_sz = bv_sz + extra_bits; SASSERT(is_well_sorted(m, lz)); } SASSERT(m_bv_util.get_bv_size(sig_4) == sig_sz); expr_ref s_exp(m), exp_rest(m); s_exp = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(bv_sz - 2, bv_sz), lz); // s_exp = (bv_sz-2) + (-lz) signed SASSERT(m_bv_util.get_bv_size(s_exp) == bv_sz); unsigned exp_sz = ebits + 2; // (+2 for rounder) exp_2 = m_bv_util.mk_extract(exp_sz - 1, 0, s_exp); // the remaining bits are 0 if ebits is large enough. exp_too_large = m.mk_false(); // This is always in range. // The exponent is at most bv_sz, i.e., we need ld(bv_sz)+1 ebits. // exp < bv_sz (+sign bit which is [0]) unsigned exp_worst_case_sz = (unsigned)((log((double)bv_sz) / log((double)2)) + 1.0); if (exp_sz < exp_worst_case_sz) { // exp_sz < exp_worst_case_sz and exp >= 0. // Take the maximum legal exponent; this // allows us to keep the most precision. expr_ref max_exp(m), max_exp_bvsz(m); mk_max_exp(exp_sz, max_exp); max_exp_bvsz = m_bv_util.mk_zero_extend(bv_sz - exp_sz, max_exp); exp_too_large = m_bv_util.mk_ule(m_bv_util.mk_bv_add( max_exp_bvsz, m_bv_util.mk_numeral(1, bv_sz)), s_exp); sig_4 = m.mk_ite(exp_too_large, m_bv_util.mk_numeral(0, sig_sz), sig_4); exp_2 = m.mk_ite(exp_too_large, max_exp, exp_2); } dbg_decouple("fpa2bv_to_fp_unsigned_exp_too_large", exp_too_large); expr_ref sgn(m), sig(m), exp(m); sgn = bv0_1; sig = sig_4; exp = exp_2; dbg_decouple("fpa2bv_to_fp_unsigned_sgn", sgn); dbg_decouple("fpa2bv_to_fp_unsigned_sig", sig); dbg_decouple("fpa2bv_to_fp_unsigned_exp", exp); SASSERT(m_bv_util.get_bv_size(sig) == sbits + 4); SASSERT(m_bv_util.get_bv_size(exp) == ebits + 2); expr_ref v2(m); round(f->get_range(), rm, sgn, sig, exp, v2); mk_ite(c1, v1, v2, result); } void fpa2bv_converter::mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); expr * sgn, * s, * e; split_fp(args[0], sgn, e, s); result = m_bv_util.mk_concat(m_bv_util.mk_concat(sgn, e), s); } void fpa2bv_converter::mk_to_ubv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_ubv", for (unsigned i = 0; i < num; i++) tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); SASSERT(f->get_num_parameters() == 1); SASSERT(f->get_parameter(0).is_int()); SASSERT(num == 2); SASSERT(m_bv_util.get_bv_size(args[0]) == 3); SASSERT(m_util.is_float(args[1])); expr * rm = args[0]; expr * x = args[1]; sort * xs = m.get_sort(x); sort * bv_srt = f->get_range(); unsigned ebits = m_util.get_ebits(xs); unsigned sbits = m_util.get_sbits(xs); unsigned bv_sz = (unsigned)f->get_parameter(0).get_int(); expr_ref bv0(m), bv1(m); bv0 = m_bv_util.mk_numeral(0, 1); bv1 = m_bv_util.mk_numeral(1, 1); expr_ref x_is_nan(m), x_is_inf(m), x_is_zero(m), x_is_neg(m), x_is_nzero(m); mk_is_nan(x, x_is_nan); mk_is_inf(x, x_is_inf); mk_is_zero(x, x_is_zero); mk_is_neg(x, x_is_neg); mk_is_nzero(x, x_is_nzero); // NaN, Inf, or negative (except -0) -> unspecified expr_ref c1(m), v1(m); c1 = m.mk_or(x_is_nan, x_is_inf, m.mk_and(x_is_neg, m.mk_not(x_is_nzero))); v1 = mk_to_ubv_unspecified(bv_sz); // +-Zero -> 0 expr_ref c2(m), v2(m); c2 = x_is_zero; v2 = m_bv_util.mk_numeral(rational(0), bv_srt); dbg_decouple("fpa2bv_to_ubv_c2", c2); // Otherwise... expr_ref sgn(m), sig(m), exp(m), lz(m); unpack(x, sgn, sig, exp, lz, true); // sig is of the form +- [1].[sig] * 2^(exp-lz) SASSERT(m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.get_bv_size(sig) == sbits); SASSERT(m_bv_util.get_bv_size(exp) == ebits); SASSERT(m_bv_util.get_bv_size(lz) == ebits); dbg_decouple("fpa2bv_to_ubv_sig", sig); unsigned sig_sz = m_bv_util.get_bv_size(sig); SASSERT(sig_sz == sbits); if (sig_sz < (bv_sz + 3)) sig = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, bv_sz - sig_sz + 3)); sig_sz = m_bv_util.get_bv_size(sig); SASSERT(sig_sz >= (bv_sz + 3)); expr_ref exp_m_lz(m), shift(m), shift_neg(m), bv0_e2(m), shift_abs(m); exp_m_lz = m_bv_util.mk_bv_sub(m_bv_util.mk_sign_extend(2, exp), m_bv_util.mk_zero_extend(2, lz)); shift = m_bv_util.mk_bv_sub(exp_m_lz, m_bv_util.mk_numeral(bv_sz - 1, ebits + 2)); shift_neg = m_bv_util.mk_bv_neg(shift); bv0_e2 = m_bv_util.mk_numeral(0, ebits + 2); shift_abs = m.mk_ite(m_bv_util.mk_sle(shift, bv0_e2), shift_neg, shift); SASSERT(m_bv_util.get_bv_size(shift) == ebits + 2); SASSERT(m_bv_util.get_bv_size(shift_neg) == ebits + 2); SASSERT(m_bv_util.get_bv_size(shift_abs) == ebits + 2); dbg_decouple("fpa2bv_to_ubv_shift", shift); dbg_decouple("fpa2bv_to_ubv_shift_abs", shift_abs); // x is of the form +- [1].[sig][r][g][s] ... and at least bv_sz + 3 long // [1][ ... sig ... ][r][g][ ... s ...] // [ ... ubv ... ][r][g][ ... s ... ] expr_ref max_shift(m); max_shift = m_bv_util.mk_numeral(sig_sz, sig_sz); shift_abs = m_bv_util.mk_zero_extend(sig_sz - ebits - 2, shift_abs); SASSERT(m_bv_util.get_bv_size(shift_abs) == sig_sz); expr_ref c_in_limits(m); c_in_limits = m_bv_util.mk_sle(shift, m_bv_util.mk_numeral(0, ebits + 2)); dbg_decouple("fpa2bv_to_ubv_in_limits", c_in_limits); expr_ref shifted_sig(m); shifted_sig = m_bv_util.mk_bv_lshr(sig, shift_abs); dbg_decouple("fpa2bv_to_ubv_shifted_sig", shifted_sig); expr_ref last(m), round(m), sticky(m); last = m_bv_util.mk_extract(sig_sz - bv_sz - 0, sig_sz - bv_sz - 0, shifted_sig); round = m_bv_util.mk_extract(sig_sz - bv_sz - 1, sig_sz - bv_sz - 1, shifted_sig); sticky = m.mk_ite(m.mk_eq(m_bv_util.mk_extract(sig_sz - bv_sz - 2, 0, shifted_sig), m_bv_util.mk_numeral(0, sig_sz - (bv_sz + 3) + 2)), bv0, bv1); dbg_decouple("fpa2bv_to_ubv_last", last); dbg_decouple("fpa2bv_to_ubv_round", round); dbg_decouple("fpa2bv_to_ubv_sticky", sticky); expr_ref rounding_decision(m); rounding_decision = mk_rounding_decision(rm, sgn, last, round, sticky); SASSERT(m_bv_util.get_bv_size(rounding_decision) == 1); dbg_decouple("fpa2bv_to_ubv_rounding_decision", rounding_decision); expr_ref unrounded_sig(m), pre_rounded(m), inc(m); unrounded_sig = m_bv_util.mk_zero_extend(1, m_bv_util.mk_extract(sig_sz - 1, sig_sz - bv_sz, shifted_sig)); inc = m_bv_util.mk_zero_extend(1, m_bv_util.mk_zero_extend(bv_sz - 1, rounding_decision)); pre_rounded = m_bv_util.mk_bv_add(unrounded_sig, inc); expr_ref rnd_overflow(m), rnd(m), rnd_has_overflown(m); rnd_overflow = m_bv_util.mk_extract(bv_sz, bv_sz, pre_rounded); rnd = m_bv_util.mk_extract(bv_sz - 1, 0, pre_rounded); rnd_has_overflown = m.mk_eq(rnd_overflow, bv1); dbg_decouple("fpa2bv_to_ubv_rnd_has_overflown", rnd_has_overflown); result = m.mk_ite(rnd_has_overflown, mk_to_ubv_unspecified(bv_sz), rnd); result = m.mk_ite(c_in_limits, result, mk_to_ubv_unspecified(bv_sz)); result = m.mk_ite(c2, v2, result); result = m.mk_ite(c1, v1, result); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_sbv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_sbv", for (unsigned i = 0; i < num; i++) tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); SASSERT(f->get_num_parameters() == 1); SASSERT(f->get_parameter(0).is_int()); SASSERT(num == 2); SASSERT(m_bv_util.get_bv_size(args[0]) == 3); SASSERT(m_util.is_float(args[1])); expr * rm = args[0]; expr * x = args[1]; sort * xs = m.get_sort(x); sort * bv_srt = f->get_range(); unsigned ebits = m_util.get_ebits(xs); unsigned sbits = m_util.get_sbits(xs); unsigned bv_sz = (unsigned)f->get_parameter(0).get_int(); expr_ref bv0(m), bv1(m), bv0_2(m), bv1_2(m), bv3_2(m); bv0 = m_bv_util.mk_numeral(0, 1); bv1 = m_bv_util.mk_numeral(1, 1); bv0_2 = m_bv_util.mk_numeral(0, 2); bv1_2 = m_bv_util.mk_numeral(1, 2); bv3_2 = m_bv_util.mk_numeral(3, 2); expr_ref x_is_nan(m), x_is_inf(m), x_is_zero(m), x_is_neg(m), x_is_nzero(m); mk_is_nan(x, x_is_nan); mk_is_inf(x, x_is_inf); mk_is_zero(x, x_is_zero); mk_is_neg(x, x_is_neg); mk_is_nzero(x, x_is_nzero); // NaN, Inf -> unspecified expr_ref c1(m), v1(m); c1 = m.mk_or(x_is_nan, x_is_inf); v1 = mk_to_sbv_unspecified(bv_sz); dbg_decouple("fpa2bv_to_sbv_c1", c1); // +-Zero -> 0 expr_ref c2(m), v2(m); c2 = x_is_zero; v2 = m_bv_util.mk_numeral(rational(0), bv_srt); dbg_decouple("fpa2bv_to_sbv_c2", c2); // Otherwise... expr_ref sgn(m), sig(m), exp(m), lz(m); unpack(x, sgn, sig, exp, lz, true); dbg_decouple("fpa2bv_to_sbv_sgn", sgn); dbg_decouple("fpa2bv_to_sbv_sig", sig); dbg_decouple("fpa2bv_to_sbv_exp", exp); // x is of the form +- [1].[sig] * 2^(exp-lz) SASSERT(m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.get_bv_size(sig) == sbits); SASSERT(m_bv_util.get_bv_size(exp) == ebits); SASSERT(m_bv_util.get_bv_size(lz) == ebits); dbg_decouple("fpa2bv_to_sbv_sig", sig); unsigned sig_sz = m_bv_util.get_bv_size(sig); SASSERT(sig_sz == sbits); if (sig_sz < (bv_sz + 3)) sig = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, bv_sz - sig_sz + 3)); sig_sz = m_bv_util.get_bv_size(sig); SASSERT(sig_sz >= (bv_sz + 3)); expr_ref exp_m_lz(m), shift(m), shift_neg(m), bv0_e2(m), shift_abs(m); exp_m_lz = m_bv_util.mk_bv_sub(m_bv_util.mk_sign_extend(2, exp), m_bv_util.mk_zero_extend(2, lz)); shift = m_bv_util.mk_bv_sub(exp_m_lz, m_bv_util.mk_numeral(bv_sz - 1, ebits + 2)); shift_neg = m_bv_util.mk_bv_neg(shift); bv0_e2 = m_bv_util.mk_numeral(0, ebits + 2); shift_abs = m.mk_ite(m_bv_util.mk_sle(shift, bv0_e2), shift_neg, shift); SASSERT(m_bv_util.get_bv_size(shift) == ebits + 2); SASSERT(m_bv_util.get_bv_size(shift_neg) == ebits + 2); SASSERT(m_bv_util.get_bv_size(shift_abs) == ebits + 2); dbg_decouple("fpa2bv_to_sbv_shift", shift); // sig is of the form +- [1].[sig][r][g][s] ... and at least bv_sz + 3 long // [1][ ... sig ... ][r][g][ ... s ...] // [ ... ubv ... ][r][g][ ... s ... ] expr_ref max_shift(m); max_shift = m_bv_util.mk_numeral(sig_sz, sig_sz); shift_abs = m_bv_util.mk_zero_extend(sig_sz - ebits - 2, shift_abs); SASSERT(m_bv_util.get_bv_size(shift_abs) == sig_sz); dbg_decouple("fpa2bv_to_sbv_shift_abs", shift_abs); expr_ref c_in_limits(m); c_in_limits = m_bv_util.mk_sle(shift, m_bv_util.mk_numeral(0, ebits + 2)); dbg_decouple("fpa2bv_to_sbv_in_limits", c_in_limits); expr_ref huge_sig(m), huge_shift(m), huge_shifted_sig(m); huge_sig = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, sig_sz)); huge_shift = m_bv_util.mk_concat(m_bv_util.mk_numeral(0, sig_sz), shift_abs); huge_shifted_sig = m_bv_util.mk_bv_lshr(huge_sig, huge_shift); dbg_decouple("fpa2bv_to_sbv_huge_shifted_sig", huge_shifted_sig); SASSERT(m_bv_util.get_bv_size(huge_shifted_sig) == 2 * sig_sz); expr_ref upper_hss(m), lower_hss(m); upper_hss = m_bv_util.mk_extract(2 * sig_sz - 1, sig_sz + 1, huge_shifted_sig); lower_hss = m_bv_util.mk_extract(sig_sz, 0, huge_shifted_sig); SASSERT(m_bv_util.get_bv_size(upper_hss) == sig_sz - 1); SASSERT(m_bv_util.get_bv_size(lower_hss) == sig_sz + 1); dbg_decouple("fpa2bv_to_sbv_upper_hss", upper_hss); dbg_decouple("fpa2bv_to_sbv_lower_hss", lower_hss); expr_ref last(m), round(m), sticky(m); last = m_bv_util.mk_extract(1, 1, upper_hss); round = m_bv_util.mk_extract(0, 0, upper_hss); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, lower_hss.get()); dbg_decouple("fpa2bv_to_sbv_last", last); dbg_decouple("fpa2bv_to_sbv_round", round); dbg_decouple("fpa2bv_to_sbv_sticky", sticky); expr_ref upper_hss_w_sticky(m); upper_hss_w_sticky = m_bv_util.mk_concat(upper_hss, sticky); dbg_decouple("fpa2bv_to_sbv_upper_hss_w_sticky", upper_hss_w_sticky); SASSERT(m_bv_util.get_bv_size(upper_hss_w_sticky) == sig_sz); expr_ref rounding_decision(m); rounding_decision = mk_rounding_decision(rm, sgn, last, round, sticky); SASSERT(m_bv_util.get_bv_size(rounding_decision) == 1); dbg_decouple("fpa2bv_to_sbv_rounding_decision", rounding_decision); expr_ref unrounded_sig(m), pre_rounded(m), inc(m); unrounded_sig = m_bv_util.mk_extract(sig_sz - 1, sig_sz - bv_sz - 1, upper_hss_w_sticky); inc = m_bv_util.mk_zero_extend(bv_sz, rounding_decision); pre_rounded = m_bv_util.mk_bv_add(unrounded_sig, inc); dbg_decouple("fpa2bv_to_sbv_inc", inc); dbg_decouple("fpa2bv_to_sbv_unrounded_sig", unrounded_sig); dbg_decouple("fpa2bv_to_sbv_pre_rounded", pre_rounded); expr_ref rnd_overflow(m), rnd_abs(m), rnd_signed(m), rnd_has_overflown(m), extra_neg(m); rnd_overflow = m_bv_util.mk_extract(bv_sz, bv_sz - 1, pre_rounded); rnd_abs = m_bv_util.mk_extract(bv_sz - 1, 0, pre_rounded); rnd_signed = m.mk_ite(m.mk_eq(sgn, bv1), m_bv_util.mk_bv_neg(rnd_abs), rnd_abs); extra_neg = m_bv_util.mk_numeral(fu().fm().m_powers2(bv_sz-1), bv_sz+1); rnd_has_overflown = m.mk_and(m.mk_not(m.mk_eq(rnd_overflow, bv0_2)), m.mk_not(m.mk_and(m.mk_eq(sgn, bv1), m.mk_eq(pre_rounded, extra_neg)))); dbg_decouple("fpa2bv_to_sbv_extra_neg", extra_neg); dbg_decouple("fpa2bv_to_sbv_rnd_overflow", rnd_overflow); dbg_decouple("fpa2bv_to_sbv_rnd_abs", rnd_abs); dbg_decouple("fpa2bv_to_sbv_rnd_has_overflown", rnd_has_overflown); result = m.mk_ite(rnd_has_overflown, mk_to_sbv_unspecified(bv_sz), rnd_signed); result = m.mk_ite(c_in_limits, result, mk_to_sbv_unspecified(bv_sz)); result = m.mk_ite(c2, v2, result); result = m.mk_ite(c1, v1, result); SASSERT(is_well_sorted(m, result)); } expr_ref fpa2bv_converter::mk_to_ubv_unspecified(unsigned width) { if (m_hi_fp_unspecified) return expr_ref(m_bv_util.mk_numeral(0, width), m); else return expr_ref(m_util.mk_internal_to_ubv_unspecified(width), m); } expr_ref fpa2bv_converter::mk_to_sbv_unspecified(unsigned width) { if (m_hi_fp_unspecified) return expr_ref(m_bv_util.mk_numeral(0, width), m); else return expr_ref(m_util.mk_internal_to_sbv_unspecified(width), m); } expr_ref fpa2bv_converter::mk_to_real_unspecified() { if (m_hi_fp_unspecified) return expr_ref(m_arith_util.mk_numeral(rational(0), false), m); else return expr_ref(m_util.mk_internal_to_real_unspecified(), m); } void fpa2bv_converter::mk_fp(expr * sign, expr * exponent, expr * significand, expr_ref & result) { SASSERT(m_bv_util.is_bv(sign) && m_bv_util.get_bv_size(sign) == 1); SASSERT(m_bv_util.is_bv(significand)); SASSERT(m_bv_util.is_bv(exponent)); result = m.mk_app(m_util.get_family_id(), OP_FPA_FP, sign, exponent, significand); } void fpa2bv_converter::mk_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); SASSERT(m_bv_util.get_bv_size(args[0]) == 1); SASSERT(m_util.get_sbits(f->get_range()) == m_bv_util.get_bv_size(args[2]) + 1); SASSERT(m_util.get_ebits(f->get_range()) == m_bv_util.get_bv_size(args[1])); mk_fp(args[0], args[1], args[2], result); TRACE("fpa2bv_mk_fp", tout << "mk_fp result = " << mk_ismt2_pp(result, m) << std::endl;); } void fpa2bv_converter::split_fp(expr * e, expr * & sgn, expr * & exp, expr * & sig) const { SASSERT(is_app_of(e, m_plugin->get_family_id(), OP_FPA_FP)); SASSERT(to_app(e)->get_num_args() == 3); sgn = to_app(e)->get_arg(0); exp = to_app(e)->get_arg(1); sig = to_app(e)->get_arg(2); } void fpa2bv_converter::split_fp(expr * e, expr_ref & sgn, expr_ref & exp, expr_ref & sig) const { SASSERT(is_app_of(e, m_plugin->get_family_id(), OP_FPA_FP)); SASSERT(to_app(e)->get_num_args() == 3); expr *e_sgn, *e_sig, *e_exp; split_fp(e, e_sgn, e_exp, e_sig); sgn = e_sgn; exp = e_exp; sig = e_sig; } void fpa2bv_converter::mk_is_nan(expr * e, expr_ref & result) { expr * sgn, * sig, * exp; split_fp(e, sgn, exp, sig); // exp == 1^n , sig != 0 expr_ref sig_is_zero(m), sig_is_not_zero(m), exp_is_top(m), top_exp(m), zero(m); mk_top_exp(m_bv_util.get_bv_size(exp), top_exp); zero = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(sig)); m_simp.mk_eq(sig, zero, sig_is_zero); m_simp.mk_not(sig_is_zero, sig_is_not_zero); m_simp.mk_eq(exp, top_exp, exp_is_top); m_simp.mk_and(exp_is_top, sig_is_not_zero, result); } void fpa2bv_converter::mk_is_inf(expr * e, expr_ref & result) { expr * sgn, * sig, * exp; split_fp(e, sgn, exp, sig); expr_ref eq1(m), eq2(m), top_exp(m), zero(m); mk_top_exp(m_bv_util.get_bv_size(exp), top_exp); zero = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(sig)); m_simp.mk_eq(sig, zero, eq1); m_simp.mk_eq(exp, top_exp, eq2); m_simp.mk_and(eq1, eq2, result); } void fpa2bv_converter::mk_is_pinf(expr * e, expr_ref & result) { expr_ref e_is_pos(m), e_is_inf(m); mk_is_pos(e, e_is_pos); mk_is_inf(e, e_is_inf); m_simp.mk_and(e_is_pos, e_is_inf, result); } void fpa2bv_converter::mk_is_ninf(expr * e, expr_ref & result) { expr_ref e_is_neg(m), e_is_inf(m); mk_is_neg(e, e_is_neg); mk_is_inf(e, e_is_inf); m_simp.mk_and(e_is_neg, e_is_inf, result); } void fpa2bv_converter::mk_is_pos(expr * e, expr_ref & result) { SASSERT(is_app_of(e, m_plugin->get_family_id(), OP_FPA_FP)); SASSERT(to_app(e)->get_num_args() == 3); expr * a0 = to_app(e)->get_arg(0); expr_ref zero(m); zero = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(a0)); m_simp.mk_eq(a0, zero, result); } void fpa2bv_converter::mk_is_neg(expr * e, expr_ref & result) { SASSERT(is_app_of(e, m_plugin->get_family_id(), OP_FPA_FP)); SASSERT(to_app(e)->get_num_args() == 3); expr * a0 = to_app(e)->get_arg(0); expr_ref one(m); one = m_bv_util.mk_numeral(1, m_bv_util.get_bv_size(a0)); m_simp.mk_eq(a0, one, result); } void fpa2bv_converter::mk_is_zero(expr * e, expr_ref & result) { expr * sgn, * sig, * exp; split_fp(e, sgn, exp, sig); expr_ref eq1(m), eq2(m), bot_exp(m), zero(m); mk_bot_exp(m_bv_util.get_bv_size(exp), bot_exp); zero = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(sig)); m_simp.mk_eq(sig, zero, eq1); m_simp.mk_eq(exp, bot_exp, eq2); m_simp.mk_and(eq1, eq2, result); } void fpa2bv_converter::mk_is_nzero(expr * e, expr_ref & result) { expr * sgn, * sig, * exp; split_fp(e, sgn, exp, sig); expr_ref e_is_zero(m), eq(m), one_1(m); mk_is_zero(e, e_is_zero); one_1 = m_bv_util.mk_numeral(1, 1); m_simp.mk_eq(sgn, one_1, eq); m_simp.mk_and(eq, e_is_zero, result); } void fpa2bv_converter::mk_is_pzero(expr * e, expr_ref & result) { expr * sgn, * sig, * exp; split_fp(e, sgn, exp, sig); expr_ref e_is_zero(m), eq(m), nil_1(m); mk_is_zero(e, e_is_zero); nil_1 = m_bv_util.mk_numeral(0, 1); m_simp.mk_eq(sgn, nil_1, eq); m_simp.mk_and(eq, e_is_zero, result); } void fpa2bv_converter::mk_is_denormal(expr * e, expr_ref & result) { expr * sgn, *sig, *exp; split_fp(e, sgn, exp, sig); expr_ref zero(m), zexp(m), is_zero(m), n_is_zero(m); zero = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(exp)); m_simp.mk_eq(exp, zero, result); m_simp.mk_eq(exp, zero, zexp); mk_is_zero(e, is_zero); m_simp.mk_not(is_zero, n_is_zero); m_simp.mk_and(n_is_zero, zexp, result); } void fpa2bv_converter::mk_is_normal(expr * e, expr_ref & result) { expr * sgn, * sig, * exp; split_fp(e, sgn, exp, sig); expr_ref is_special(m), is_denormal(m), p(m), is_zero(m); mk_is_denormal(e, is_denormal); mk_is_zero(e, is_zero); unsigned ebits = m_bv_util.get_bv_size(exp); p = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits), ebits); m_simp.mk_eq(exp, p, is_special); expr_ref or_ex(m); m_simp.mk_or(is_special, is_denormal, or_ex); m_simp.mk_or(is_zero, or_ex, or_ex); m_simp.mk_not(or_ex, result); } void fpa2bv_converter::mk_is_rm(expr * e, BV_RM_VAL rm, expr_ref & result) { SASSERT(m_bv_util.is_bv(e) && m_bv_util.get_bv_size(e) == 3); expr_ref rm_num(m); rm_num = m_bv_util.mk_numeral(rm, 3); switch(rm) { case BV_RM_TIES_TO_AWAY: case BV_RM_TIES_TO_EVEN: case BV_RM_TO_NEGATIVE: case BV_RM_TO_POSITIVE: case BV_RM_TO_ZERO: return m_simp.mk_eq(e, rm_num, result); default: UNREACHABLE(); } } void fpa2bv_converter::mk_top_exp(unsigned sz, expr_ref & result) { result = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(sz), sz); } void fpa2bv_converter::mk_bot_exp(unsigned sz, expr_ref & result) { result = m_bv_util.mk_numeral(0, sz); } void fpa2bv_converter::mk_min_exp(unsigned ebits, expr_ref & result) { SASSERT(ebits >= 2); const mpz & z = m_mpf_manager.m_powers2.m1(ebits-1, true); result = m_bv_util.mk_numeral(z + mpz(1), ebits); } void fpa2bv_converter::mk_max_exp(unsigned ebits, expr_ref & result) { SASSERT(ebits >= 2); result = m_bv_util.mk_numeral(m_mpf_manager.m_powers2.m1(ebits-1, false), ebits); } void fpa2bv_converter::mk_leading_zeros(expr * e, unsigned max_bits, expr_ref & result) { SASSERT(m_bv_util.is_bv(e)); unsigned bv_sz = m_bv_util.get_bv_size(e); if (bv_sz == 0) result = m_bv_util.mk_numeral(0, max_bits); else if (bv_sz == 1) { expr_ref eq(m), nil_1(m), one_m(m), nil_m(m); nil_1 = m_bv_util.mk_numeral(0, 1); one_m = m_bv_util.mk_numeral(1, max_bits); nil_m = m_bv_util.mk_numeral(0, max_bits); m_simp.mk_eq(e, nil_1, eq); m_simp.mk_ite(eq, one_m, nil_m, result); } else { expr_ref H(m), L(m); H = m_bv_util.mk_extract(bv_sz-1, bv_sz/2, e); L = m_bv_util.mk_extract(bv_sz/2-1, 0, e); unsigned H_size = m_bv_util.get_bv_size(H); // unsigned L_size = m_bv_util.get_bv_size(L); expr_ref lzH(m), lzL(m); mk_leading_zeros(H, max_bits, lzH); /* recursive! */ mk_leading_zeros(L, max_bits, lzL); expr_ref H_is_zero(m), nil_h(m); nil_h = m_bv_util.mk_numeral(0, H_size); m_simp.mk_eq(H, nil_h, H_is_zero); expr_ref sum(m), h_m(m); h_m = m_bv_util.mk_numeral(H_size, max_bits); sum = m_bv_util.mk_bv_add(h_m, lzL); m_simp.mk_ite(H_is_zero, sum, lzH, result); } SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_bias(expr * e, expr_ref & result) { unsigned ebits = m_bv_util.get_bv_size(e); SASSERT(ebits >= 2); expr_ref bias(m); bias = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1), ebits); result = m_bv_util.mk_bv_add(e, bias); } void fpa2bv_converter::mk_unbias(expr * e, expr_ref & result) { unsigned ebits = m_bv_util.get_bv_size(e); SASSERT(ebits >= 2); expr_ref e_plus_one(m); e_plus_one = m_bv_util.mk_bv_add(e, m_bv_util.mk_numeral(1, ebits)); expr_ref leading(m), n_leading(m), rest(m); leading = m_bv_util.mk_extract(ebits-1, ebits-1, e_plus_one); n_leading = m_bv_util.mk_bv_not(leading); rest = m_bv_util.mk_extract(ebits-2, 0, e_plus_one); result = m_bv_util.mk_concat(n_leading, rest); } void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & lz, bool normalize) { SASSERT(is_app_of(e, m_plugin->get_family_id(), OP_FPA_FP)); SASSERT(to_app(e)->get_num_args() == 3); sort * srt = to_app(e)->get_decl()->get_range(); SASSERT(is_float(srt)); unsigned sbits = m_util.get_sbits(srt); unsigned ebits = m_util.get_ebits(srt); split_fp(e, sgn, exp, sig); SASSERT(m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.get_bv_size(exp) == ebits); SASSERT(m_bv_util.get_bv_size(sig) == sbits-1); expr_ref is_normal(m); mk_is_normal(e, is_normal); expr_ref normal_sig(m), normal_exp(m); normal_sig = m_bv_util.mk_concat(m_bv_util.mk_numeral(1, 1), sig); mk_unbias(exp, normal_exp); dbg_decouple("fpa2bv_unpack_normal_exp", normal_exp); expr_ref denormal_sig(m), denormal_exp(m); denormal_sig = m_bv_util.mk_zero_extend(1, sig); // SASSERT(ebits >= 3); // Note: when ebits=2 there is no 1-exponent, so mk_unbias will produce a 0. denormal_exp = m_bv_util.mk_numeral(1, ebits); mk_unbias(denormal_exp, denormal_exp); dbg_decouple("fpa2bv_unpack_denormal_exp", denormal_exp); expr_ref zero_e(m); zero_e = m_bv_util.mk_numeral(0, ebits); if (normalize) { expr_ref is_sig_zero(m), zero_s(m); zero_s = m_bv_util.mk_numeral(0, sbits); m_simp.mk_eq(zero_s, denormal_sig, is_sig_zero); expr_ref lz_d(m); mk_leading_zeros(denormal_sig, ebits, lz_d); m_simp.mk_ite(m.mk_or(is_normal, is_sig_zero), zero_e, lz_d, lz); dbg_decouple("fpa2bv_unpack_lz", lz); expr_ref shift(m); m_simp.mk_ite(is_sig_zero, zero_e, lz, shift); dbg_decouple("fpa2bv_unpack_shift", shift); SASSERT(is_well_sorted(m, is_sig_zero)); SASSERT(is_well_sorted(m, shift)); SASSERT(m_bv_util.get_bv_size(shift) == ebits); 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 { // the maximum shift is `sbits', because after that the mantissa // would be zero anyways. So we can safely cut the shift variable down, // as long as we check the higher bits. expr_ref sh(m), is_sh_zero(m), sl(m), sbits_s(m), short_shift(m); zero_s = m_bv_util.mk_numeral(0, sbits-1); sbits_s = m_bv_util.mk_numeral(sbits, sbits); sh = m_bv_util.mk_extract(ebits-1, sbits, shift); m_simp.mk_eq(zero_s, sh, is_sh_zero); short_shift = m_bv_util.mk_extract(sbits-1, 0, shift); m_simp.mk_ite(is_sh_zero, short_shift, sbits_s, sl); denormal_sig = m_bv_util.mk_bv_shl(denormal_sig, sl); } } else lz = zero_e; SASSERT(is_well_sorted(m, normal_sig)); SASSERT(is_well_sorted(m, denormal_sig)); SASSERT(is_well_sorted(m, normal_exp)); SASSERT(is_well_sorted(m, denormal_exp)); dbg_decouple("fpa2bv_unpack_is_normal", is_normal); m_simp.mk_ite(is_normal, normal_sig, denormal_sig, sig); m_simp.mk_ite(is_normal, normal_exp, denormal_exp, exp); SASSERT(is_well_sorted(m, sgn)); SASSERT(is_well_sorted(m, sig)); SASSERT(is_well_sorted(m, exp)); SASSERT(m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.get_bv_size(sig) == sbits); SASSERT(m_bv_util.get_bv_size(exp) == ebits); TRACE("fpa2bv_unpack", tout << "UNPACK SGN = " << mk_ismt2_pp(sgn, m) << std::endl; ); TRACE("fpa2bv_unpack", tout << "UNPACK SIG = " << mk_ismt2_pp(sig, m) << std::endl; ); TRACE("fpa2bv_unpack", tout << "UNPACK EXP = " << mk_ismt2_pp(exp, m) << std::endl; ); } void fpa2bv_converter::mk_rounding_mode(func_decl * f, expr_ref & result) { switch(f->get_decl_kind()) { case OP_FPA_RM_NEAREST_TIES_TO_EVEN: result = m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3); break; case OP_FPA_RM_NEAREST_TIES_TO_AWAY: result = m_bv_util.mk_numeral(BV_RM_TIES_TO_AWAY, 3); break; case OP_FPA_RM_TOWARD_POSITIVE: result = m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3); break; case OP_FPA_RM_TOWARD_NEGATIVE: result = m_bv_util.mk_numeral(BV_RM_TO_NEGATIVE, 3); break; case OP_FPA_RM_TOWARD_ZERO: result = m_bv_util.mk_numeral(BV_RM_TO_ZERO, 3); break; default: UNREACHABLE(); } } void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { #ifdef Z3DEBUG return; // CMW: This works only for quantifier-free formulas. expr_ref new_e(m); new_e = m.mk_fresh_const(prefix, m.get_sort(e)); m_extra_assertions.push_back(m.mk_eq(new_e, e)); e = new_e; #endif } expr_ref fpa2bv_converter::mk_rounding_decision(expr * rm, expr * sgn, expr * last, expr * round, expr * sticky) { expr_ref rmr(rm, m); expr_ref sgnr(sgn, m); expr_ref lastr(last, m); expr_ref roundr(round, m); expr_ref stickyr(sticky, m); dbg_decouple("fpa2bv_rnd_dec_rm", rmr); dbg_decouple("fpa2bv_rnd_dec_sgn", sgnr); dbg_decouple("fpa2bv_rnd_dec_last", lastr); dbg_decouple("fpa2bv_rnd_dec_round", roundr); dbg_decouple("fpa2bv_rnd_dec_sticky", stickyr); expr_ref last_or_sticky(m), round_or_sticky(m), not_last(m), not_round(m), not_sticky(m), not_lors(m), not_rors(m), not_sgn(m); expr * last_sticky[2] = { last, sticky }; expr * round_sticky[2] = { round, sticky }; last_or_sticky = m_bv_util.mk_bv_or(2, last_sticky); round_or_sticky = m_bv_util.mk_bv_or(2, round_sticky); not_last= m_bv_util.mk_bv_not(last); not_round = m_bv_util.mk_bv_not(round); not_sticky = m_bv_util.mk_bv_not(sticky); not_lors = m_bv_util.mk_bv_not(last_or_sticky); not_rors = m_bv_util.mk_bv_not(round_or_sticky); not_sgn = m_bv_util.mk_bv_not(sgn); expr * nround_lors[2] = { not_round, not_lors }; expr * pos_args[2] = { sgn, not_rors }; expr * neg_args[2] = { not_sgn, not_rors }; expr * nl_r[2] = { last, not_round }; expr * nl_nr_sn[3] = { not_last, not_round, not_sticky }; expr_ref inc_teven(m), inc_taway(m), inc_pos(m), inc_neg(m); inc_teven = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, nround_lors)); expr *taway_args[2] = { m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, nl_r)), m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(3, nl_nr_sn)) }; inc_taway = m_bv_util.mk_bv_or(2, taway_args); inc_pos = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, pos_args)); inc_neg = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, neg_args)); expr_ref res(m), inc_c2(m), inc_c3(m), inc_c4(m); expr_ref rm_is_to_neg(m), rm_is_to_pos(m), rm_is_away(m), rm_is_even(m), nil_1(m); nil_1 = m_bv_util.mk_numeral(0, 1); mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_is_to_pos); mk_is_rm(rm, BV_RM_TIES_TO_AWAY, rm_is_away); mk_is_rm(rm, BV_RM_TIES_TO_EVEN, rm_is_even); m_simp.mk_ite(rm_is_to_neg, inc_neg, nil_1, inc_c4); m_simp.mk_ite(rm_is_to_pos, inc_pos, inc_c4, inc_c3); m_simp.mk_ite(rm_is_away, inc_taway, inc_c3, inc_c2); m_simp.mk_ite(rm_is_even, inc_teven, inc_c2, res); dbg_decouple("fpa2bv_rnd_dec_res", res); return res; } void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & result) { unsigned ebits = m_util.get_ebits(s); unsigned sbits = m_util.get_sbits(s); dbg_decouple("fpa2bv_rnd_rm", rm); dbg_decouple("fpa2bv_rnd_sgn", sgn); dbg_decouple("fpa2bv_rnd_sig", sig); dbg_decouple("fpa2bv_rnd_exp", exp); SASSERT(is_well_sorted(m, rm)); SASSERT(is_well_sorted(m, sgn)); SASSERT(is_well_sorted(m, sig)); SASSERT(is_well_sorted(m, exp)); TRACE("fpa2bv_dbg", tout << "RND: " << std::endl << "ebits = " << ebits << std::endl << "sbits = " << sbits << std::endl << "sgn = " << mk_ismt2_pp(sgn, m) << std::endl << "sig = " << mk_ismt2_pp(sig, m) << std::endl << "exp = " << mk_ismt2_pp(exp, m) << std::endl; ); // Assumptions: sig is of the form f[-1:0] . f[1:sbits-1] [guard,round,sticky], // i.e., it has 2 + (sbits-1) + 3 = sbits + 4 bits, where the first one is in sgn. // Furthermore, note that sig is an unsigned bit-vector, while exp is signed. SASSERT(m_bv_util.is_bv(rm) && m_bv_util.get_bv_size(rm) == 3); SASSERT(m_bv_util.is_bv(sgn) && m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.is_bv(sig) && m_bv_util.get_bv_size(sig) >= 5); SASSERT(m_bv_util.is_bv(exp) && m_bv_util.get_bv_size(exp) >= 4); SASSERT(m_bv_util.get_bv_size(sig) == sbits+4); SASSERT(m_bv_util.get_bv_size(exp) == ebits+2); // bool UNFen = false; // bool OVFen = false; expr_ref e_min(m), e_max(m); mk_min_exp(ebits, e_min); mk_max_exp(ebits, e_max); TRACE("fpa2bv_dbg", tout << "e_min = " << mk_ismt2_pp(e_min, m) << std::endl << "e_max = " << mk_ismt2_pp(e_max, m) << std::endl;); expr_ref OVF1(m), e_top_three(m), sigm1(m), e_eq_emax_and_sigm1(m), e_eq_emax(m); expr_ref e3(m), ne3(m), e2(m), e1(m), e21(m), one_1(m), h_exp(m), sh_exp(m), th_exp(m); one_1 = m_bv_util.mk_numeral(1, 1); h_exp = m_bv_util.mk_extract(ebits+1, ebits+1, exp); sh_exp = m_bv_util.mk_extract(ebits, ebits, exp); th_exp = m_bv_util.mk_extract(ebits-1, ebits-1, exp); m_simp.mk_eq(h_exp, one_1, e3); m_simp.mk_eq(sh_exp, one_1, e2); m_simp.mk_eq(th_exp, one_1, e1); m_simp.mk_or(e2, e1, e21); m_simp.mk_not(e3, ne3); m_simp.mk_and(ne3, e21, e_top_three); expr_ref ext_emax(m), t_sig(m); ext_emax = m_bv_util.mk_zero_extend(2, e_max); t_sig = m_bv_util.mk_extract(sbits+3, sbits+3, sig); m_simp.mk_eq(ext_emax, exp, e_eq_emax); m_simp.mk_eq(t_sig, one_1, sigm1); m_simp.mk_and(e_eq_emax, sigm1, e_eq_emax_and_sigm1); m_simp.mk_or(e_top_three, e_eq_emax_and_sigm1, OVF1); dbg_decouple("fpa2bv_rnd_OVF1", OVF1); TRACE("fpa2bv_dbg", tout << "OVF1 = " << mk_ismt2_pp(OVF1, m) << std::endl;); SASSERT(is_well_sorted(m, OVF1)); expr_ref lz(m); mk_leading_zeros(sig, ebits+2, lz); // CMW: is this always large enough? dbg_decouple("fpa2bv_rnd_lz", lz); TRACE("fpa2bv_dbg", tout << "LZ = " << mk_ismt2_pp(lz, m) << std::endl;); expr_ref t(m); t = m_bv_util.mk_bv_add(exp, m_bv_util.mk_numeral(1, ebits+2)); t = m_bv_util.mk_bv_sub(t, lz); t = m_bv_util.mk_bv_sub(t, m_bv_util.mk_sign_extend(2, e_min)); dbg_decouple("fpa2bv_rnd_t", t); expr_ref TINY(m); TINY = m_bv_util.mk_sle(t, m_bv_util.mk_numeral((unsigned)-1, ebits+2)); dbg_decouple("fpa2bv_rnd_TINY", TINY); TRACE("fpa2bv_dbg", tout << "TINY = " << mk_ismt2_pp(TINY, m) << std::endl;); SASSERT(is_well_sorted(m, TINY)); expr_ref beta(m); beta = m_bv_util.mk_bv_add(m_bv_util.mk_bv_sub(exp, lz), m_bv_util.mk_numeral(1, ebits+2)); TRACE("fpa2bv_dbg", tout << "beta = " << mk_ismt2_pp(beta, m)<< std::endl; ); SASSERT(is_well_sorted(m, beta)); dbg_decouple("fpa2bv_rnd_beta", beta); dbg_decouple("fpa2bv_rnd_e_min", e_min); dbg_decouple("fpa2bv_rnd_e_max", e_max); expr_ref sigma(m), sigma_add(m); sigma_add = m_bv_util.mk_bv_sub(exp, m_bv_util.mk_sign_extend(2, e_min)); sigma_add = m_bv_util.mk_bv_add(sigma_add, m_bv_util.mk_numeral(1, ebits+2)); m_simp.mk_ite(TINY, sigma_add, lz, sigma); dbg_decouple("fpa2bv_rnd_sigma", sigma); TRACE("fpa2bv_dbg", tout << "Shift distance: " << mk_ismt2_pp(sigma, m) << std::endl;); SASSERT(is_well_sorted(m, sigma)); // Normalization shift dbg_decouple("fpa2bv_rnd_sig_before_shift", sig); unsigned sig_size = m_bv_util.get_bv_size(sig); SASSERT(sig_size == sbits+4); SASSERT(m_bv_util.get_bv_size(sigma) == ebits+2); unsigned sigma_size = ebits+2; expr_ref sigma_neg(m), sigma_cap(m), sigma_neg_capped(m), sigma_lt_zero(m), sig_ext(m), rs_sig(m), ls_sig(m), big_sh_sig(m), sigma_le_cap(m); sigma_neg = m_bv_util.mk_bv_neg(sigma); sigma_cap = m_bv_util.mk_numeral(sbits+2, sigma_size); sigma_le_cap = m_bv_util.mk_ule(sigma_neg, sigma_cap); m_simp.mk_ite(sigma_le_cap, sigma_neg, sigma_cap, sigma_neg_capped); dbg_decouple("fpa2bv_rnd_sigma_neg", sigma_neg); dbg_decouple("fpa2bv_rnd_sigma_cap", sigma_cap); dbg_decouple("fpa2bv_rnd_sigma_neg_capped", sigma_neg_capped); sigma_lt_zero = m_bv_util.mk_sle(sigma, m_bv_util.mk_numeral((unsigned)-1, sigma_size)); dbg_decouple("fpa2bv_rnd_sigma_lt_zero", sigma_lt_zero); sig_ext = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, sig_size)); rs_sig = m_bv_util.mk_bv_lshr(sig_ext, m_bv_util.mk_zero_extend(2*sig_size - sigma_size, sigma_neg_capped)); ls_sig = m_bv_util.mk_bv_shl(sig_ext, m_bv_util.mk_zero_extend(2*sig_size - sigma_size, sigma)); m_simp.mk_ite(sigma_lt_zero, rs_sig, ls_sig, big_sh_sig); SASSERT(m_bv_util.get_bv_size(big_sh_sig) == 2*sig_size); dbg_decouple("fpa2bv_rnd_big_sh_sig", big_sh_sig); unsigned sig_extract_low_bit = (2*sig_size-1)-(sbits+2)+1; sig = m_bv_util.mk_extract(2*sig_size-1, sig_extract_low_bit, big_sh_sig); SASSERT(m_bv_util.get_bv_size(sig) == sbits+2); dbg_decouple("fpa2bv_rnd_shifted_sig", sig); expr_ref sticky(m); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(sig_extract_low_bit-1, 0, big_sh_sig)); SASSERT(is_well_sorted(m, sticky)); SASSERT(is_well_sorted(m, sig)); // put the sticky bit into the significand. expr_ref ext_sticky(m); ext_sticky = m_bv_util.mk_zero_extend(sbits+1, sticky); expr * tmp[] = { sig, ext_sticky }; sig = m_bv_util.mk_bv_or(2, tmp); SASSERT(is_well_sorted(m, sig)); SASSERT(m_bv_util.get_bv_size(sig) == sbits+2); // CMW: The (OVF1 && OVFen) and (TINY && UNFen) cases are never taken. expr_ref ext_emin(m); ext_emin = m_bv_util.mk_zero_extend(2, e_min); m_simp.mk_ite(TINY, ext_emin, beta, exp); SASSERT(is_well_sorted(m, exp)); // Significand rounding expr_ref round(m), last(m); sticky = m_bv_util.mk_extract(0, 0, sig); // new sticky bit! round = m_bv_util.mk_extract(1, 1, sig); last = m_bv_util.mk_extract(2, 2, sig); TRACE("fpa2bv_dbg", tout << "sticky = " << mk_ismt2_pp(sticky, m) << std::endl;); dbg_decouple("fpa2bv_rnd_sticky", sticky); dbg_decouple("fpa2bv_rnd_round", round); dbg_decouple("fpa2bv_rnd_last", last); sig = m_bv_util.mk_extract(sbits+1, 2, sig); expr_ref inc(m); inc = mk_rounding_decision(rm, sgn, last, round, sticky); SASSERT(m_bv_util.get_bv_size(inc) == 1 && is_well_sorted(m, inc)); dbg_decouple("fpa2bv_rnd_inc", inc); sig = m_bv_util.mk_bv_add(m_bv_util.mk_zero_extend(1, sig), m_bv_util.mk_zero_extend(sbits, inc)); SASSERT(is_well_sorted(m, sig)); dbg_decouple("fpa2bv_rnd_sig_plus_inc", sig); // Post normalization SASSERT(m_bv_util.get_bv_size(sig) == sbits + 1); expr_ref SIGovf(m); t_sig = m_bv_util.mk_extract(sbits, sbits, sig); m_simp.mk_eq(t_sig, one_1, SIGovf); SASSERT(is_well_sorted(m, SIGovf)); dbg_decouple("fpa2bv_rnd_SIGovf", SIGovf); expr_ref hallbut1_sig(m), lallbut1_sig(m); hallbut1_sig = m_bv_util.mk_extract(sbits, 1, sig); lallbut1_sig = m_bv_util.mk_extract(sbits-1, 0, sig); m_simp.mk_ite(SIGovf, hallbut1_sig, lallbut1_sig, sig); SASSERT(m_bv_util.get_bv_size(exp) == ebits + 2); expr_ref exp_p1(m); exp_p1 = m_bv_util.mk_bv_add(exp, m_bv_util.mk_numeral(1, ebits+2)); m_simp.mk_ite(SIGovf, exp_p1, exp, exp); SASSERT(is_well_sorted(m, sig)); SASSERT(is_well_sorted(m, exp)); dbg_decouple("fpa2bv_rnd_sig_postnormalized", sig); dbg_decouple("fpa2bv_rnd_exp_postnormalized", exp); SASSERT(m_bv_util.get_bv_size(sig) == sbits); SASSERT(m_bv_util.get_bv_size(exp) == ebits + 2); SASSERT(m_bv_util.get_bv_size(e_max) == ebits); // Exponent adjustment and rounding expr_ref biased_exp(m); mk_bias(m_bv_util.mk_extract(ebits-1, 0, exp), biased_exp); dbg_decouple("fpa2bv_rnd_unbiased_exp", exp); dbg_decouple("fpa2bv_rnd_biased_exp", biased_exp); // AdjustExp SASSERT(is_well_sorted(m, OVF1)); SASSERT(m.is_bool(OVF1)); expr_ref preOVF2(m), OVF2(m), OVF(m), exp_redand(m), pem2m1(m); exp_redand = m.mk_app(m_bv_util.get_fid(), OP_BREDAND, biased_exp.get()); m_simp.mk_eq(exp_redand, one_1, preOVF2); m_simp.mk_and(SIGovf, preOVF2, OVF2); pem2m1 = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-2), ebits); m_simp.mk_ite(OVF2, pem2m1, biased_exp, biased_exp); m_simp.mk_or(OVF1, OVF2, OVF); SASSERT(is_well_sorted(m, OVF2)); SASSERT(is_well_sorted(m, OVF)); SASSERT(m.is_bool(OVF2)); SASSERT(m.is_bool(OVF)); dbg_decouple("fpa2bv_rnd_OVF2", OVF2); dbg_decouple("fpa2bv_rnd_OVF", OVF); // ExpRnd expr_ref top_exp(m), bot_exp(m); mk_top_exp(ebits, top_exp); mk_bot_exp(ebits, bot_exp); expr_ref nil_1(m); nil_1 = m_bv_util.mk_numeral(0, 1); expr_ref rm_is_to_zero(m), rm_is_to_neg(m), rm_is_to_pos(m), rm_zero_or_neg(m), rm_zero_or_pos(m); mk_is_rm(rm, BV_RM_TO_ZERO, rm_is_to_zero); mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_is_to_pos); m_simp.mk_or(rm_is_to_zero, rm_is_to_neg, rm_zero_or_neg); m_simp.mk_or(rm_is_to_zero, rm_is_to_pos, rm_zero_or_pos); dbg_decouple("fpa2bv_rnd_rm_is_to_zero", rm_is_to_zero); dbg_decouple("fpa2bv_rnd_rm_is_to_neg", rm_is_to_neg); dbg_decouple("fpa2bv_rnd_rm_is_to_pos", rm_is_to_pos); expr_ref sgn_is_zero(m); m_simp.mk_eq(sgn, m_bv_util.mk_numeral(0, 1), sgn_is_zero); dbg_decouple("fpa2bv_rnd_sgn_is_zero", sgn_is_zero); expr_ref max_sig(m), max_exp(m), inf_sig(m), inf_exp(m); max_sig = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(sbits-1, false), sbits-1); max_exp = m_bv_util.mk_concat(m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1, false), ebits-1), m_bv_util.mk_numeral(0, 1)); inf_sig = m_bv_util.mk_numeral(0, sbits-1); inf_exp = top_exp; dbg_decouple("fpa2bv_rnd_max_exp", max_exp); dbg_decouple("fpa2bv_rnd_max_sig", max_sig); dbg_decouple("fpa2bv_rnd_inf_sig", inf_sig); dbg_decouple("fpa2bv_rnd_inf_exp", inf_exp); expr_ref ovfl_exp(m), max_inf_exp_neg(m), max_inf_exp_pos(m), n_d_check(m), n_d_exp(m); m_simp.mk_ite(rm_zero_or_pos, max_exp, inf_exp, max_inf_exp_neg); m_simp.mk_ite(rm_zero_or_neg, max_exp, inf_exp, max_inf_exp_pos); m_simp.mk_ite(sgn_is_zero, max_inf_exp_pos, max_inf_exp_neg, ovfl_exp); t_sig = m_bv_util.mk_extract(sbits-1, sbits-1, sig); m_simp.mk_eq(t_sig, nil_1, n_d_check); m_simp.mk_ite(n_d_check, bot_exp /* denormal */, biased_exp, n_d_exp); m_simp.mk_ite(OVF, ovfl_exp, n_d_exp, exp); expr_ref max_inf_sig_neg(m), max_inf_sig_pos(m), ovfl_sig(m), rest_sig(m); m_simp.mk_ite(rm_zero_or_pos, max_sig, inf_sig, max_inf_sig_neg); m_simp.mk_ite(rm_zero_or_neg, max_sig, inf_sig, max_inf_sig_pos); m_simp.mk_ite(sgn_is_zero, max_inf_sig_pos, max_inf_sig_neg, ovfl_sig); rest_sig = m_bv_util.mk_extract(sbits-2, 0, sig); m_simp.mk_ite(OVF, ovfl_sig, rest_sig, sig); dbg_decouple("fpa2bv_rnd_max_inf_sig_neg", max_inf_sig_neg); dbg_decouple("fpa2bv_rnd_max_inf_sig_pos", max_inf_sig_pos); dbg_decouple("fpa2bv_rnd_rm_zero_or_neg", rm_zero_or_neg); dbg_decouple("fpa2bv_rnd_rm_zero_or_pos", rm_zero_or_pos); dbg_decouple("fpa2bv_rnd_sgn_final", sgn); dbg_decouple("fpa2bv_rnd_sig_final", sig); dbg_decouple("fpa2bv_rnd_exp_final", exp); expr_ref res_sgn(m), res_sig(m), res_exp(m); res_sgn = sgn; res_sig = sig; res_exp = exp; SASSERT(m_bv_util.get_bv_size(res_sgn) == 1); SASSERT(is_well_sorted(m, res_sgn)); SASSERT(m_bv_util.get_bv_size(res_sig) == sbits-1); SASSERT(is_well_sorted(m, res_sig)); SASSERT(m_bv_util.get_bv_size(res_exp) == ebits); SASSERT(is_well_sorted(m, res_exp)); mk_fp(res_sgn, res_exp, res_sig, result); TRACE("fpa2bv_round", tout << "ROUND = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::reset(void) { dec_ref_map_key_values(m, m_const2bv); dec_ref_map_key_values(m, m_rm_const2bv); dec_ref_map_key_values(m, m_uf2bvuf); obj_map::iterator it = m_uf23bvuf.begin(); obj_map::iterator end = m_uf23bvuf.end(); for (; it != end; ++it) { m.dec_ref(it->m_key); m.dec_ref(it->m_value.f_sgn); m.dec_ref(it->m_value.f_sig); m.dec_ref(it->m_value.f_exp); } m_uf23bvuf.reset(); m_extra_assertions.reset(); } z3-4.4.0/src/ast/fpa/fpa2bv_rewriter.h0000644000175000017500000003020412540347414017244 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_rewriter.h Abstract: Rewriter for converting FPA to BV Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #ifndef _FPA2BV_REWRITER_H_ #define _FPA2BV_REWRITER_H_ #include"cooperate.h" #include"rewriter_def.h" #include"bv_decl_plugin.h" #include"fpa2bv_converter.h" #include"fpa2bv_rewriter_params.hpp" struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { ast_manager & m_manager; expr_ref_vector m_out; fpa2bv_converter & m_conv; sort_ref_vector m_bindings; unsigned long long m_max_memory; unsigned m_max_steps; ast_manager & m() const { return m_manager; } fpa2bv_rewriter_cfg(ast_manager & m, fpa2bv_converter & c, params_ref const & p) : m_manager(m), m_out(m), m_conv(c), m_bindings(m) { updt_params(p); // We need to make sure that the mananger has the BV plugin loaded. symbol s_bv("bv"); if (!m_manager.has_plugin(s_bv)) m_manager.register_plugin(s_bv, alloc(bv_decl_plugin)); } ~fpa2bv_rewriter_cfg() { } void cleanup_buffers() { m_out.finalize(); } void reset() { } void updt_local_params(params_ref const & _p) { fpa2bv_rewriter_params p(_p); bool v = p.hi_fp_unspecified(); m_conv.set_unspecified_fp_hi(v); } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_max_steps = p.get_uint("max_steps", UINT_MAX); updt_local_params(p); } bool max_steps_exceeded(unsigned num_steps) const { cooperate("fpa2bv"); return num_steps > m_max_steps; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { TRACE("fpa2bv_rw", tout << "APP: " << f->get_name() << std::endl; ); if (num == 0 && f->get_family_id() == null_family_id && m_conv.is_float(f->get_range())) { m_conv.mk_const(f, result); return BR_DONE; } if (num == 0 && f->get_family_id() == null_family_id && m_conv.is_rm(f->get_range())) { m_conv.mk_rm_const(f, result); return BR_DONE; } if (m().is_eq(f)) { SASSERT(num == 2); SASSERT(m().get_sort(args[0]) == m().get_sort(args[1])); sort * ds = f->get_domain()[0]; if (m_conv.is_float(ds)) { m_conv.mk_eq(args[0], args[1], result); return BR_DONE; } else if (m_conv.is_rm(ds)) { result = m().mk_eq(args[0], args[1]); return BR_DONE; } return BR_FAILED; } else if (m().is_ite(f)) { SASSERT(num == 3); if (m_conv.is_float(args[1])) { m_conv.mk_ite(args[0], args[1], args[2], result); return BR_DONE; } return BR_FAILED; } else if (m().is_distinct(f)) { sort * ds = f->get_domain()[0]; if (m_conv.is_float(ds) || m_conv.is_rm(ds)) { m_conv.mk_distinct(f, num, args, result); return BR_DONE; } return BR_FAILED; } if (m_conv.is_float_family(f)) { switch (f->get_decl_kind()) { case OP_FPA_RM_NEAREST_TIES_TO_AWAY: case OP_FPA_RM_NEAREST_TIES_TO_EVEN: case OP_FPA_RM_TOWARD_NEGATIVE: case OP_FPA_RM_TOWARD_POSITIVE: case OP_FPA_RM_TOWARD_ZERO: m_conv.mk_rounding_mode(f, result); return BR_DONE; case OP_FPA_NUM: m_conv.mk_numeral(f, num, args, result); return BR_DONE; case OP_FPA_PLUS_INF: m_conv.mk_pinf(f, result); return BR_DONE; case OP_FPA_MINUS_INF: m_conv.mk_ninf(f, result); return BR_DONE; case OP_FPA_PLUS_ZERO: m_conv.mk_pzero(f, result); return BR_DONE; case OP_FPA_MINUS_ZERO: m_conv.mk_nzero(f, result); return BR_DONE; case OP_FPA_NAN: m_conv.mk_nan(f, result); return BR_DONE; case OP_FPA_ADD: m_conv.mk_add(f, num, args, result); return BR_DONE; case OP_FPA_SUB: m_conv.mk_sub(f, num, args, result); return BR_DONE; case OP_FPA_NEG: m_conv.mk_neg(f, num, args, result); return BR_DONE; case OP_FPA_MUL: m_conv.mk_mul(f, num, args, result); return BR_DONE; case OP_FPA_DIV: m_conv.mk_div(f, num, args, result); return BR_DONE; case OP_FPA_REM: m_conv.mk_rem(f, num, args, result); return BR_DONE; case OP_FPA_ABS: m_conv.mk_abs(f, num, args, result); return BR_DONE; case OP_FPA_MIN: m_conv.mk_min(f, num, args, result); return BR_DONE; case OP_FPA_MAX: m_conv.mk_max(f, num, args, result); return BR_DONE; case OP_FPA_FMA: m_conv.mk_fma(f, num, args, result); return BR_DONE; case OP_FPA_SQRT: m_conv.mk_sqrt(f, num, args, result); return BR_DONE; case OP_FPA_ROUND_TO_INTEGRAL: m_conv.mk_round_to_integral(f, num, args, result); return BR_DONE; case OP_FPA_EQ: m_conv.mk_float_eq(f, num, args, result); return BR_DONE; case OP_FPA_LT: m_conv.mk_float_lt(f, num, args, result); return BR_DONE; case OP_FPA_GT: m_conv.mk_float_gt(f, num, args, result); return BR_DONE; case OP_FPA_LE: m_conv.mk_float_le(f, num, args, result); return BR_DONE; case OP_FPA_GE: m_conv.mk_float_ge(f, num, args, result); return BR_DONE; case OP_FPA_IS_ZERO: m_conv.mk_is_zero(f, num, args, result); return BR_DONE; case OP_FPA_IS_NAN: m_conv.mk_is_nan(f, num, args, result); return BR_DONE; case OP_FPA_IS_INF: m_conv.mk_is_inf(f, num, args, result); return BR_DONE; case OP_FPA_IS_NORMAL: m_conv.mk_is_normal(f, num, args, result); return BR_DONE; case OP_FPA_IS_SUBNORMAL: m_conv.mk_is_subnormal(f, num, args, result); return BR_DONE; case OP_FPA_IS_POSITIVE: m_conv.mk_is_positive(f, num, args, result); return BR_DONE; case OP_FPA_IS_NEGATIVE: m_conv.mk_is_negative(f, num, args, result); return BR_DONE; case OP_FPA_TO_FP: m_conv.mk_to_fp(f, num, args, result); return BR_DONE; case OP_FPA_TO_FP_UNSIGNED: m_conv.mk_to_fp_unsigned(f, num, args, result); return BR_DONE; case OP_FPA_FP: m_conv.mk_fp(f, num, args, result); return BR_DONE; case OP_FPA_TO_UBV: m_conv.mk_to_ubv(f, num, args, result); return BR_DONE; case OP_FPA_TO_SBV: m_conv.mk_to_sbv(f, num, args, result); return BR_DONE; case OP_FPA_TO_REAL: m_conv.mk_to_real(f, num, args, result); return BR_DONE; case OP_FPA_TO_IEEE_BV: m_conv.mk_to_ieee_bv(f, num, args, result); return BR_DONE; case OP_FPA_INTERNAL_BVWRAP: case OP_FPA_INTERNAL_BVUNWRAP: case OP_FPA_INTERNAL_TO_REAL_UNSPECIFIED: case OP_FPA_INTERNAL_TO_UBV_UNSPECIFIED: case OP_FPA_INTERNAL_TO_SBV_UNSPECIFIED: return BR_FAILED; default: TRACE("fpa2bv", tout << "unsupported operator: " << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << std::endl;); NOT_IMPLEMENTED_YET(); } } if (f->get_family_id() == null_family_id) { bool is_float_uf = m_conv.is_float(f->get_range()); unsigned i = 0; while (!is_float_uf && i < num) { is_float_uf = m_conv.is_float(f->get_domain()[i]); i++; } if (is_float_uf) { m_conv.mk_uninterpreted_function(f, num, args, result); return BR_DONE; } } return BR_FAILED; } bool pre_visit(expr * t) { TRACE("fpa2bv", tout << "pre_visit: " << mk_ismt2_pp(t, m()) << std::endl;); if (is_quantifier(t)) { quantifier * q = to_quantifier(t); TRACE("fpa2bv", tout << "pre_visit quantifier [" << q->get_id() << "]: " << mk_ismt2_pp(q->get_expr(), m()) << std::endl;); sort_ref_vector new_bindings(m_manager); for (unsigned i = 0 ; i < q->get_num_decls(); i++) new_bindings.push_back(q->get_decl_sort(i)); SASSERT(new_bindings.size() == q->get_num_decls()); m_bindings.append(new_bindings); } return true; } bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { unsigned curr_sz = m_bindings.size(); SASSERT(old_q->get_num_decls() <= curr_sz); unsigned num_decls = old_q->get_num_decls(); unsigned old_sz = curr_sz - num_decls; string_buffer<> name_buffer; ptr_buffer new_decl_sorts; sbuffer new_decl_names; for (unsigned i = 0; i < num_decls; i++) { symbol const & n = old_q->get_decl_name(i); sort * s = old_q->get_decl_sort(i); if (m_conv.is_float(s)) { unsigned ebits = m_conv.fu().get_ebits(s); unsigned sbits = m_conv.fu().get_sbits(s); name_buffer.reset(); name_buffer << n << ".bv"; new_decl_names.push_back(symbol(name_buffer.c_str())); new_decl_sorts.push_back(m_conv.bu().mk_sort(sbits+ebits)); } else { new_decl_sorts.push_back(s); new_decl_names.push_back(n); } } result = m().mk_quantifier(old_q->is_forall(), new_decl_sorts.size(), new_decl_sorts.c_ptr(), new_decl_names.c_ptr(), new_body, old_q->get_weight(), old_q->get_qid(), old_q->get_skid(), old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns); result_pr = 0; m_bindings.shrink(old_sz); TRACE("fpa2bv", tout << "reduce_quantifier[" << old_q->get_depth() << "]: " << mk_ismt2_pp(old_q->get_expr(), m()) << std::endl << " new body: " << mk_ismt2_pp(new_body, m()) << std::endl; tout << "result = " << mk_ismt2_pp(result, m()) << std::endl;); return true; } bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { if (t->get_idx() >= m_bindings.size()) return false; // unsigned inx = m_bindings.size() - t->get_idx() - 1; expr_ref new_exp(m()); sort * s = t->get_sort(); if (m_conv.is_float(s)) { expr_ref new_var(m()); unsigned ebits = m_conv.fu().get_ebits(s); unsigned sbits = m_conv.fu().get_sbits(s); new_var = m().mk_var(t->get_idx(), m_conv.bu().mk_sort(sbits+ebits)); m_conv.mk_fp(m_conv.bu().mk_extract(sbits+ebits-1, sbits+ebits-1, new_var), m_conv.bu().mk_extract(ebits - 1, 0, new_var), m_conv.bu().mk_extract(sbits+ebits-2, ebits, new_var), new_exp); } else new_exp = m().mk_var(t->get_idx(), s); result = new_exp; result_pr = 0; TRACE("fpa2bv", tout << "reduce_var: " << mk_ismt2_pp(t, m()) << " -> " << mk_ismt2_pp(result, m()) << std::endl;); return true; } }; template class rewriter_tpl; struct fpa2bv_rewriter : public rewriter_tpl { fpa2bv_rewriter_cfg m_cfg; fpa2bv_rewriter(ast_manager & m, fpa2bv_converter & c, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, c, p) { } }; #endif z3-4.4.0/src/ast/fpa/fpa2bv_rewriter_params.pyg0000644000175000017500000000044412540347414021162 0ustar michaelmichaeldef_module_params(module_name='rewriter', class_name='fpa2bv_rewriter_params', export=True, params=(("hi_fp_unspecified", BOOL, True, "use the 'hardware interpretation' for unspecified values in fp.to_ubv, fp.to_sbv, and fp.to_real"), )) z3-4.4.0/src/ast/expr2var.h0000644000175000017500000000342512540347414015151 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: expr2var.h Abstract: The mapping between Z3 expressions and (low level) variables. Example of low level variables: - SAT solver - Polynomial - etc. Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #ifndef _EXPR2VAR_H_ #define _EXPR2VAR_H_ #include"ast.h" #include"obj_hashtable.h" /** \brief The mapping between Z3 expressions and (low level) variables. */ class expr2var { public: typedef unsigned var; typedef obj_map expr2var_mapping; typedef expr2var_mapping::iterator iterator; typedef ptr_vector::const_iterator recent_iterator; protected: ast_manager & m_manager; expr2var_mapping m_mapping; ptr_vector m_recent_exprs; bool m_interpreted_vars; public: expr2var(ast_manager & m); ~expr2var(); ast_manager & m() const { return m_manager; } void insert(expr * n, var v); var to_var(expr * n) const; bool is_var(expr * n) const { return m_mapping.contains(n); } void display(std::ostream & out) const; void mk_inv(expr_ref_vector & var2expr) const; // return true if the mapping contains interpreted vars. bool interpreted_vars() const { return m_interpreted_vars; } iterator begin() const { return m_mapping.begin(); } iterator end() const { return m_mapping.end(); } void reset_recent() { m_recent_exprs.reset(); } // Iterators for traversing the recently registered expressions. // The set of recent registered expressions is reset by using reset_recent(). recent_iterator begin_recent() const { return m_recent_exprs.begin(); } recent_iterator end_recent() const { return m_recent_exprs.end(); } void reset(); }; #endif z3-4.4.0/src/ast/recurse_expr.h0000644000175000017500000000231512540347414016103 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: recurse_expr.h Abstract: Traverse an expression applying a visitor. Author: Leonardo de Moura (leonardo) 2008-01-11. Revision History: --*/ #ifndef _RECURSE_EXPR_H_ #define _RECURSE_EXPR_H_ #include"ast.h" #include"obj_hashtable.h" template class recurse_expr : public Visitor { obj_map m_cache; ptr_vector m_todo; vector m_results1; vector m_results2; bool is_cached(expr * n) const { T c; return m_cache.find(n, c); } T get_cached(expr * n) const { return m_cache.find(n); } void cache_result(expr * n, T c) { m_cache.insert(n, c); } void visit(expr * n, bool & visited); bool visit_children(expr * n); void process(expr * n); public: recurse_expr(Visitor const & v = Visitor()):Visitor(v) {} T operator()(expr * n); void reset() { m_cache.reset(); m_todo.reset(); } void finalize() { m_cache.finalize(); m_todo.finalize(); } }; #endif /* _RECURSE_EXPR_H_ */ z3-4.4.0/src/ast/ast_smt2_pp.h0000644000175000017500000001147012540347414015632 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ast_smt2_pp.cpp Abstract: Pretty printer of AST formulas using SMT2 format. This printer is more expensive than the one in ast_smt_pp.h, but is supposed to generated a "prettier" and SMT2 compliant output. Author: Leonardo de Moura (leonardo) Revision History: --*/ #ifndef _AST_SMT2_PP_H_ #define _AST_SMT2_PP_H_ #include"format.h" #include"params.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"array_decl_plugin.h" #include"fpa_decl_plugin.h" #include"dl_decl_plugin.h" #include"smt2_util.h" class smt2_pp_environment { protected: format_ns::format * mk_neg(format_ns::format * f) const; format_ns::format * mk_float(rational const & val) const; bool is_indexed_fdecl(func_decl * f) const; format_ns::format * pp_fdecl_params(format_ns::format * fname, func_decl * f); bool is_sort_param(func_decl * f) const; format_ns::format * pp_as(format_ns::format * fname, sort * s); format_ns::format * pp_signature(format_ns::format * f_name, func_decl * f); public: virtual ~smt2_pp_environment() {} virtual ast_manager & get_manager() const = 0; virtual arith_util & get_autil() = 0; virtual bv_util & get_bvutil() = 0; virtual array_util & get_arutil() = 0; virtual fpa_util & get_futil() = 0; virtual datalog::dl_decl_util& get_dlutil() = 0; virtual bool uses(symbol const & s) const = 0; virtual format_ns::format * pp_fdecl(func_decl * f, unsigned & len); virtual format_ns::format * pp_bv_literal(app * t, bool use_bv_lits, bool bv_neg); virtual format_ns::format * pp_arith_literal(app * t, bool decimal, unsigned prec); virtual format_ns::format * pp_float_literal(app * t, bool use_bv_lits, bool use_float_real_lits); virtual format_ns::format * pp_datalog_literal(app * t); virtual format_ns::format * pp_sort(sort * s); virtual format_ns::format * pp_fdecl_ref(func_decl * f); format_ns::format * pp_fdecl_name(symbol const & fname, unsigned & len) const; format_ns::format * pp_fdecl_name(func_decl * f, unsigned & len) const; }; /** \brief Simple environment that ignores name clashes. Useful for debugging code. */ class smt2_pp_environment_dbg : public smt2_pp_environment { ast_manager & m_manager; arith_util m_autil; bv_util m_bvutil; array_util m_arutil; fpa_util m_futil; datalog::dl_decl_util m_dlutil; public: smt2_pp_environment_dbg(ast_manager & m):m_manager(m), m_autil(m), m_bvutil(m), m_arutil(m), m_futil(m), m_dlutil(m) {} virtual ast_manager & get_manager() const { return m_manager; } virtual arith_util & get_autil() { return m_autil; } virtual bv_util & get_bvutil() { return m_bvutil; } virtual array_util & get_arutil() { return m_arutil; } virtual fpa_util & get_futil() { return m_futil; } virtual datalog::dl_decl_util& get_dlutil() { return m_dlutil; } virtual bool uses(symbol const & s) const { return false; } }; void mk_smt2_format(expr * n, smt2_pp_environment & env, params_ref const & p, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names); void mk_smt2_format(sort * s, smt2_pp_environment & env, params_ref const & p, format_ns::format_ref & r); void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & p, format_ns::format_ref & r); std::ostream & ast_smt2_pp(std::ostream & out, expr * n, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = 0); std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0); std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0); /** \brief Internal wrapper (for debugging purposes only) */ struct mk_ismt2_pp { ast * m_ast; ast_manager & m_manager; params_ref m_empty; params_ref const & m_params; unsigned m_indent; unsigned m_num_vars; char const * m_var_prefix; mk_ismt2_pp(ast * t, ast_manager & m, params_ref const & p, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = 0); mk_ismt2_pp(ast * t, ast_manager & m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = 0); }; std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p); std::ostream& operator<<(std::ostream& out, expr_ref const& e); std::ostream& operator<<(std::ostream& out, app_ref const& e); std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e); std::ostream& operator<<(std::ostream& out, app_ref_vector const& e); #endif z3-4.4.0/src/ast/expr_map.h0000644000175000017500000000224012540347414015205 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: expr_map.h Abstract: Mapping from expressions to expressions + proofs. This mapping is used to cache simplification results. For every entry [e1->(e2, p)] we have that p is a proof that (= e1 e2). Author: Leonardo (leonardo) 2008-01-03 Notes: --*/ #ifndef _EXPR_MAP_H_ #define _EXPR_MAP_H_ #include"ast.h" #include"obj_hashtable.h" /** \brief Map from expressions to expressions+proofs. When proof production is disabled, no extra space is used. */ class expr_map { ast_manager & m_manager; bool m_store_proofs; obj_map m_expr2expr; obj_map m_expr2pr; public: expr_map(ast_manager & m); expr_map(ast_manager & m, bool store_proofs); ~expr_map(); void insert(expr * k, expr * d, proof * p); bool contains(expr * k) const { return m_expr2expr.contains(k); } void get(expr * k, expr * & d, proof * & p) const; void erase(expr * k); void reset(); void flush(); void set_store_proofs(bool f) { if (m_store_proofs != f) flush(); m_store_proofs = f; } }; #endif z3-4.4.0/src/ast/expr2polynomial.h0000644000175000017500000000627712540347414016554 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: expr2polynomial.h Abstract: Translator from Z3 expressions into multivariate polynomials (and back). Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #ifndef _EXPR2POLYNOMIAL_H_ #define _EXPR2POLYNOMIAL_H_ #include"ast.h" #include"polynomial.h" class expr2var; class expr2polynomial { struct imp; imp * m_imp; public: expr2polynomial(ast_manager & am, polynomial::manager & pm, expr2var * e2v, /* If true, the expressions converted into polynomials should only contain Z3 free variables. A Z3 variable x, with idx i, is converted into the variable i of the polynomial manager pm. An exception is thrown if there is a mismatch between the sorts x and the variable in the polynomial manager. The argument e2v is ignored when use_var_idxs is true. Moreover, only real variables are allowed. */ bool use_var_idxs = false ); virtual ~expr2polynomial(); ast_manager & m() const; polynomial::manager & pm() const; /** \brief Convert a Z3 expression into a polynomial in Z[x0, ..., x_n]. Since Z3 expressions may be representing polynomials in Q[x0, ..., x_n], the method also returns a "denominator" d. Thus, we have that n is equal to p/d \remark Return false if t is not an integer or real expression. \pre The only supported operators are MUL, ADD, SUB, UMINUS, TO_REAL, TO_INT, POWER (with constants) */ bool to_polynomial(expr * t, polynomial::polynomial_ref & p, polynomial::scoped_numeral & d); /** \brief Convert a polynomial into a Z3 expression. \remark If the polynomial has one real variable, then the resultant expression is an real expression. Otherwise, it is an integer */ void to_expr(polynomial::polynomial_ref const & p, bool use_power, expr_ref & r); /** \brief Return true if t was encoded as a variable by the translator. */ bool is_var(expr * t) const; /** \brief Return the mapping from expressions to variables \pre the object was created using use_var_idxs = false. */ expr2var const & get_mapping() const; /** \brief Cancel/Interrupt execution. */ void set_cancel(bool f); /** \brief Return true if the variable is associated with an expression of integer sort. */ virtual bool is_int(polynomial::var x) const { UNREACHABLE(); return false; } protected: virtual polynomial::var mk_var(bool is_int) { UNREACHABLE(); return polynomial::null_var; } }; class default_expr2polynomial : public expr2polynomial { svector m_is_int; public: default_expr2polynomial(ast_manager & am, polynomial::manager & pm); virtual ~default_expr2polynomial(); virtual bool is_int(polynomial::var x) const; protected: virtual polynomial::var mk_var(bool is_int); }; #endif z3-4.4.0/src/ast/static_features.h0000644000175000017500000002113112540347414016557 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: static_features.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-16. Revision History: --*/ #ifndef _STATIC_FEATURES_H_ #define _STATIC_FEATURES_H_ #include"ast.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"array_decl_plugin.h" #include"fpa_decl_plugin.h" #include"map.h" struct static_features { ast_manager & m_manager; arith_util m_autil; bv_util m_bvutil; array_util m_arrayutil; fpa_util m_fpautil; family_id m_bfid; family_id m_afid; family_id m_lfid; family_id m_arrfid; ast_mark m_already_visited; bool m_cnf; unsigned m_num_exprs; // unsigned m_num_roots; // unsigned m_max_depth; unsigned m_num_quantifiers; // unsigned m_num_quantifiers_with_patterns; // unsigned m_num_quantifiers_with_multi_patterns; // unsigned m_num_clauses; unsigned m_num_bin_clauses; // unsigned m_num_units; // unsigned m_sum_clause_size; unsigned m_num_nested_formulas; // unsigned m_num_bool_exprs; // unsigned m_num_bool_constants; // unsigned m_num_formula_trees; unsigned m_max_formula_depth; unsigned m_sum_formula_depth; unsigned m_num_or_and_trees; unsigned m_max_or_and_tree_depth; unsigned m_sum_or_and_tree_depth; unsigned m_num_ite_trees; unsigned m_max_ite_tree_depth; unsigned m_sum_ite_tree_depth; unsigned m_num_ands; // unsigned m_num_ors; // num nested ors unsigned m_num_iffs; // unsigned m_num_ite_formulas; // unsigned m_num_ite_terms; // unsigned m_num_sharing; unsigned m_num_interpreted_exprs; // doesn't include bool_exprs unsigned m_num_uninterpreted_exprs; // unsigned m_num_interpreted_constants; // doesn't include bool_consts unsigned m_num_uninterpreted_constants; // unsigned m_num_uninterpreted_functions; // unsigned m_num_eqs; // bool m_has_rational; // bool m_has_int; // bool m_has_real; // bool m_has_bv; // bool m_has_fpa; // bool m_has_arrays; // rational m_arith_k_sum; // sum of the numerals in arith atoms. unsigned m_num_arith_terms; unsigned m_num_arith_eqs; // equalities of the form t = k where k is a numeral unsigned m_num_arith_ineqs; unsigned m_num_diff_terms; // <= m_num_arith_terms unsigned m_num_diff_eqs; // <= m_num_arith_eqs unsigned m_num_diff_ineqs; // <= m_num_arith_ineqs unsigned m_num_simple_eqs; // eqs of the form x = k unsigned m_num_simple_ineqs; // ineqs of the form x <= k or x >= k unsigned m_num_non_linear; unsigned_vector m_num_apps; // mapping decl_id -> num_apps; unsigned_vector m_num_theory_terms; // mapping family_id -> num_terms unsigned_vector m_num_theory_atoms; // mapping family_id -> num_atoms unsigned_vector m_num_theory_constants; // mapping family_id -> num_exprs unsigned_vector m_num_theory_eqs; // mapping family_id -> num_eqs unsigned m_num_aliens; // unsigned_vector m_num_aliens_per_family; // mapping family_id -> num_alies exprs unsigned_vector m_expr2depth; // expr-id -> depth unsigned m_max_stack_depth; // maximal depth of stack we are willing to walk. u_map m_expr2or_and_depth; u_map m_expr2ite_depth; u_map m_expr2formula_depth; unsigned m_num_theories; svector m_theories; // mapping family_id -> bool symbol m_label_sym; symbol m_pattern_sym; symbol m_expr_list_sym; bool is_marked(ast * e) const { return m_already_visited.is_marked(e); } void mark(ast * e) { m_already_visited.mark(e, true); } bool is_bool(expr const * e) const { return m_manager.is_bool(e); } bool is_basic_expr(expr const * e) const { return is_app(e) && to_app(e)->get_family_id() == m_bfid; } bool is_arith_expr(expr const * e) const { return is_app(e) && to_app(e)->get_family_id() == m_afid; } bool is_numeral(expr const * e) const { return m_autil.is_numeral(e); } bool is_numeral(expr const * e, rational & r) const { return m_autil.is_numeral(e, r); } bool is_minus_one(expr const * e) const { rational r; return m_autil.is_numeral(e, r) && r.is_minus_one(); } bool is_diff_term(expr const * e, rational & r) const; bool is_diff_atom(expr const * e) const; bool is_gate(expr const * e) const; void mark_theory(family_id fid) { if (fid != null_family_id && !m_manager.is_builtin_family_id(fid) && !m_theories.get(fid, false)) { m_theories.setx(fid, true, false); m_num_theories++; } } void acc_num(rational const & r) { if (r.is_neg()) m_arith_k_sum -= r; else m_arith_k_sum += r; } void acc_num(expr const * e) { rational r; if (is_numeral(e, r)) { acc_num(r); } } bool arith_k_sum_is_small() const { return m_arith_k_sum < rational(INT_MAX / 8); } void inc_num_apps(func_decl const * d) { unsigned id = d->get_decl_id(); m_num_apps.reserve(id+1, 0); m_num_apps[id]++; } void inc_theory_terms(family_id fid) { m_num_theory_terms.reserve(fid+1, 0); m_num_theory_terms[fid]++; } void inc_theory_atoms(family_id fid) { m_num_theory_atoms.reserve(fid+1, 0); m_num_theory_atoms[fid]++; } void inc_theory_constants(family_id fid) { m_num_theory_constants.reserve(fid+1, 0); m_num_theory_constants[fid]++; } void inc_theory_eqs(family_id fid) { m_num_theory_eqs.reserve(fid+1, 0); m_num_theory_eqs[fid]++; } void inc_num_aliens(family_id fid) { m_num_aliens_per_family.reserve(fid+1, 0); m_num_aliens_per_family[fid]++; } void update_core(expr * e); void update_core(sort * s); void process(expr * e, bool form_ctx, bool or_and_ctx, bool ite_ctx, unsigned stack_depth); void process_root(expr * e); unsigned get_depth(expr const * e) const { return m_expr2depth.get(e->get_id(), 1); } void set_depth(expr const * e, unsigned d) { m_expr2depth.setx(e->get_id(), d, 1); } unsigned get_or_and_depth(expr const * e) const { unsigned d = 0; m_expr2or_and_depth.find(e->get_id(), d); return d; } void set_or_and_depth(expr const * e, unsigned d) { m_expr2or_and_depth.insert(e->get_id(), d); } unsigned get_ite_depth(expr const * e) const { unsigned d = 0; m_expr2ite_depth.find(e->get_id(), d); return d; } void set_ite_depth(expr const * e, unsigned d) { m_expr2ite_depth.insert(e->get_id(), d); } unsigned get_form_depth(expr const * e) const { unsigned d = 0; m_expr2formula_depth.find(e->get_id(), d); return d; } void set_form_depth(expr const * e, unsigned d) { m_expr2formula_depth.insert(e->get_id(), d); } static_features(ast_manager & m); void reset(); void flush_cache(); void collect(unsigned num_formulas, expr * const * formulas); void collect(expr * f) { process_root(f); } bool internal_family(symbol const & f_name) const; void display_family_data(std::ostream & out, char const * prefix, unsigned_vector const & data) const; void display_primitive(std::ostream & out) const; void display(std::ostream & out) const; void get_feature_vector(vector & result); bool has_uf() const; unsigned num_theories() const; unsigned num_non_uf_theories() const; }; #endif /* _STATIC_FEATURES_H_ */ z3-4.4.0/src/ast/decl_collector.cpp0000644000175000017500000000522712540347414016712 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: smt_decl_collector.cpp Abstract: Collect uninterpreted func_delcs and sorts. This class was originally in ast_smt_pp.h Author: Leonardo (leonardo) 2011-10-04 Revision History: --*/ #include"decl_collector.h" void decl_collector::visit_sort(sort * n) { family_id fid = n->get_family_id(); if (m().is_uninterp(n)) m_sorts.push_back(n); if (fid == m_dt_fid) m_sorts.push_back(n); } bool decl_collector::is_bool(sort * s) { return m().is_bool(s); } void decl_collector::visit_func(func_decl * n) { family_id fid = n->get_family_id(); if (fid == null_family_id) { if (m_sep_preds && is_bool(n->get_range())) m_preds.push_back(n); else m_decls.push_back(n); } } decl_collector::decl_collector(ast_manager & m, bool preds): m_manager(m), m_sep_preds(preds) { m_basic_fid = m_manager.get_basic_family_id(); m_dt_fid = m_manager.mk_family_id("datatype"); } void decl_collector::visit(ast* n) { ptr_vector todo; todo.push_back(n); while (!todo.empty()) { n = todo.back(); todo.pop_back(); if (!m_visited.is_marked(n)) { m_visited.mark(n, true); switch(n->get_kind()) { case AST_APP: { app * a = to_app(n); for (unsigned i = 0; i < a->get_num_args(); ++i) { todo.push_back(a->get_arg(i)); } todo.push_back(a->get_decl()); break; } case AST_QUANTIFIER: { quantifier * q = to_quantifier(n); unsigned num_decls = q->get_num_decls(); for (unsigned i = 0; i < num_decls; ++i) { todo.push_back(q->get_decl_sort(i)); } todo.push_back(q->get_expr()); for (unsigned i = 0; i < q->get_num_patterns(); ++i) { todo.push_back(q->get_pattern(i)); } break; } case AST_SORT: visit_sort(to_sort(n)); break; case AST_FUNC_DECL: { func_decl * d = to_func_decl(n); for (unsigned i = 0; i < d->get_arity(); ++i) { todo.push_back(d->get_domain(i)); } todo.push_back(d->get_range()); visit_func(d); break; } case AST_VAR: break; default: UNREACHABLE(); } } } } z3-4.4.0/src/ast/pattern/0000755000175000017500000000000012540347414014700 5ustar michaelmichaelz3-4.4.0/src/ast/pattern/pattern_inference_params.cpp0000644000175000017500000000156012540347414022444 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: pattern_inference_params.h Abstract: Author: Leonardo de Moura (leonardo) 2012-12-02. Revision History: --*/ #include"pattern_inference_params.h" #include"pattern_inference_params_helper.hpp" void pattern_inference_params::updt_params(params_ref const & _p) { pattern_inference_params_helper p(_p); m_pi_max_multi_patterns = p.max_multi_patterns(); m_pi_block_loop_patterns = p.block_loop_patterns(); m_pi_arith = static_cast(p.arith()); m_pi_use_database = p.use_database(); m_pi_arith_weight = p.arith_weight(); m_pi_non_nested_arith_weight = p.non_nested_arith_weight(); m_pi_pull_quantifiers = p.pull_quantifiers(); m_pi_warnings = p.warnings(); } z3-4.4.0/src/ast/pattern/database.smt20000644000175000017500000003622012540347414017256 0ustar michaelmichael(declare-fun ?store (Int Int Int) Int) (declare-fun ?select (Int Int) Int) (declare-fun ?PO (Int Int) Int) (declare-fun ?asChild (Int Int) Int) (declare-fun ?classDown (Int Int) Int) (declare-fun ?array (Int) Int) (declare-fun ?elemtype (Int) Int) (declare-fun ?is (Int Int) Int) (declare-fun ?cast (Int Int) Int) (declare-fun ?Object () Int) (declare-fun ?null () Int) (declare-fun ?typeof (Int) Int) (declare-fun ?asElems (Int) Int) (declare-fun ?isAllocated (Int Int) Int) (declare-fun ?fClosedTime (Int) Int) (declare-fun ?eClosedTime (Int) Int) (declare-fun ?max (Int) Int) (declare-fun ?asLockSet (Int) Int) (declare-fun ?isNewArray (Int) Int) (declare-fun ?classLiteral (Int) Int) (declare-fun ?Class () Int) (declare-fun ?alloc () Int) (declare-fun ?arrayType () Int) (declare-fun ?f (Int) Int) (declare-fun ?finv (Int) Int) (declare-fun ?select2 (Int Int Int) Int) (declare-fun ?store2 (Int Int Int Int) Int) (declare-fun ?subtypes (Int Int) Bool) (declare-fun ?Unbox (Int) Int) (declare-fun ?UnboxedType (Int) Int) (declare-fun ?Box (Int Int) Int) (declare-fun ?System.Object () Int) (declare-fun ?Smt.true () Int) (declare-fun ?AsRepField (Int Int) Int) (declare-fun ?AsPeerField (Int) Int) (declare-fun ?nullObject () Int) (declare-fun ?ownerRef_ () Int) (declare-fun ?ownerFrame_ () Int) (declare-fun IntsHeap (Int) Int) (declare-fun ?localinv_ () Int) (declare-fun ?inv_ () Int) (declare-fun ?BaseClass_ (Int) Int) (declare-fun ?typeof_ (Int) Int) (declare-fun ?PeerGroupPlaceholder_ () Int) (declare-fun ?ClassRepr (Int) Int) (declare-fun ?RefArray (Int Int) Int) (declare-fun Ints_ (Int Int) Int) (declare-fun ?RefArrayGet (Int Int) Int) (declare-fun ?elements_ () Int) (declare-fun ?NonNullRefArray (Int Int) Int) (declare-fun IntsNotNull_ (Int Int) Int) (declare-fun ?Rank_ (Int) Int) (declare-fun ?ValueArray (Int Int) Int) (declare-fun ?ArrayCategory_ (Int) Int) (declare-fun ?ArrayCategoryValue_ () Int) (declare-fun ?ElementType_ (Int) Int) (declare-fun ?System.Array () Int) (declare-fun ?allocated_ () Int) (declare-fun ?StructGet_ (Int Int) Int) (declare-fun ?AsRangeField (Int Int) Int) (declare-fun IntsAllocated (Int Int) Int) (declare-fun IntnRange (Int Int) Bool) (declare-fun ?isAllocated_ (Int Int) Bool) (declare-fun ?AsDirectSubClass (Int Int) Int) (declare-fun ?OneClassDown (Int Int) Int) (assert (forall ((a Int) (i Int) (e Int)) (! (= (?select (?store a i e) i) e) :pattern (?store a i e) :weight 0))) (assert (forall ((a Int) (i Int) (j Int) (e Int)) (! (or (= i j) (= (?select (?store a i e) j) (?select a j))) :pattern (?select (?store a i e) j) :weight 0))) (assert (forall ((t0 Int) (t1 Int) (t2 Int)) (! (or (not (= (?PO t0 t1) 1)) (not (= (?PO t1 t2) 1)) (= (?PO t0 t2) 1)) :pattern ((?PO t0 t1) (?PO t1 t2))))) (assert (forall ((t0 Int) (t1 Int)) (! (or (not (= (?PO t0 t1) 1)) (not (= (?PO t1 t0) 1)) (= t0 t1)) :pattern ((?PO t0 t1) (?PO t1 t0))))) (assert (forall ((t0 Int) (t1 Int) (t2 Int)) (! (or (not (= (?PO t0 (?asChild t1 t2)) 1)) (= (?classDown t2 t0) (?asChild t1 t2))) :pattern (?PO t0 (?asChild t1 t2))))) (assert (forall ((t Int)) (! (= (?finv (?f t)) t) :pattern (?f t)))) (assert (forall ((t0 Int) (t1 Int) ) (! (iff (= (?PO t0 (?array t1)) 1) (not (or (not (= t0 (?array (?elemtype t0)))) (not (= (?PO (?elemtype t0) t1) 1))))) :pattern (?PO t0 (?array t1))))) (assert (forall ((x Int) (t Int)) (! (or (not (= (?is x t) 1)) (= (?cast x t) x)) :pattern (?cast x t)))) (assert (forall ((x Int) (t Int)) (! (or (not (= (?PO t ?Object) 1)) (iff (= (?is x t) 1) (or (= x ?null) (= (?PO (?typeof x) t) 1)))) :pattern ((?PO t ?Object) (?is x t))))) (assert (forall ((e Int) (a Int) (i Int)) (! (= (?is (?select (?select (?asElems e) a) i) (?elemtype (?typeof a))) 1) :pattern (?select (?select (?asElems e) a) i)))) (assert (forall ((x Int) (f Int) (a0 Int)) (! (or (<= (+ a0 (* (- 1) (?fClosedTime f))) 0) (not (= (?isAllocated x a0) 1)) (= (?isAllocated (?select f x) a0) 1)) :pattern (?isAllocated (?select f x) a0)))) (assert (forall ((a Int) (e Int) (i Int) (a0 Int)) (! (or (<= (+ a0 (* (- 1) (?eClosedTime e))) 0) (not (= (?isAllocated a a0) 1)) (= (?isAllocated (?select (?select e a) i) a0) 1)) :pattern (?isAllocated (?select (?select e a) i) a0)))) (assert (forall ((S Int)) (! (= (?select (?asLockSet S) (?max (?asLockSet S))) 1) :pattern (?select (?asLockSet S) (?max (?asLockSet S)))))) (assert (forall ((s Int)) (! (or (not (= 1 (?isNewArray s))) (= (?PO (?typeof s) ?arrayType) 1)) :pattern (?isNewArray s)))) (assert (forall ((t Int)) (! (not (or (= (?classLiteral t) ?null) (not (= (?is (?classLiteral t) ?Class) 1)) (not (= (?isAllocated (?classLiteral t) ?alloc) 1)))) :pattern (?classLiteral t)))) (assert (forall ((A Int) (o Int) (f Int) (v Int)) (! (= (?select2 (?store2 A o f v) o f) v) :pattern (?store2 A o f v) :weight 0))) (assert (forall ((A Int) (o Int) (f Int) (p Int) (g Int) (v Int)) (! (or (= o p) (= (?select2 (?store2 A o f v) p g) (?select2 A p g))) :pattern (?select2 (?store2 A o f v) p g) :weight 0))) (assert (forall ((A Int) (o Int) (f Int) (p Int) (g Int) (v Int)) (! (or (= f g) (= (?select2 (?store2 A o f v) p g) (?select2 A p g))) :pattern (?select2 (?store2 A o f v) p g) :weight 0))) (assert (forall ((t Int) (u Int) (v Int)) (! (or (not (?subtypes t u)) (not (?subtypes u v)) (?subtypes t v)) :pattern ((?subtypes t u) (?subtypes u v))))) (assert (forall ((t Int) (u Int)) (! (or (not (?subtypes t u)) (not (?subtypes u t)) (= t u)) :pattern ((?subtypes t u) (?subtypes u t))))) (assert (forall ((x Int) (p Int)) (! (or (not (?subtypes (?UnboxedType (?Box x p)) ?System.Object)) (not (= (?Box x p) p)) (= x p)) :pattern (?subtypes (?UnboxedType (?Box x p)) ?System.Object)))) (assert (forall ((h Int) (o Int) (f Int) (T Int)) (! (or (not (= (IntsHeap h) ?Smt.true)) (= (?select2 h o (?AsRepField f T)) ?nullObject) (not (or (not (= (?select2 h (?select2 h o (?AsRepField f T)) ?ownerRef_) o)) (not (= (?select2 h (?select2 h o (?AsRepField f T)) ?ownerFrame_) T))))) :pattern (?select2 h o (?AsRepField f T))))) (assert (forall ((h Int) (o Int) (f Int)) (! (or (not (= (IntsHeap h) ?Smt.true)) (= (?select2 h o (?AsPeerField f)) ?nullObject) (not (or (not (= (?select2 h (?select2 h o (?AsPeerField f)) ?ownerRef_) (?select2 h o ?ownerRef_))) (not (= (?select2 h (?select2 h o (?AsPeerField f)) ?ownerFrame_) (?select2 h o ?ownerFrame_)))))) :pattern (?select2 h o (?AsPeerField f))))) (assert (forall ((h Int) (o Int)) (! (or (not (= (IntsHeap h) ?Smt.true)) (= (?select2 h o ?ownerFrame_) ?PeerGroupPlaceholder_) (not (?subtypes (?select2 h (?select2 h o ?ownerRef_) ?inv_) (?select2 h o ?ownerFrame_))) (= (?select2 h (?select2 h o ?ownerRef_) ?localinv_) (?BaseClass_ (?select2 h o ?ownerFrame_))) (not (or (not (= (?select2 h o ?inv_) (?typeof_ o))) (not (= (?select2 h o ?localinv_) (?typeof_ o)))))) :pattern (?subtypes (?select2 h (?select2 h o ?ownerRef_) ?inv_) (?select2 h o ?ownerFrame_))))) (assert (forall ((T Int) (h Int)) (! (or (not (= (IntsHeap h) ?Smt.true)) (= (?select2 h (?ClassRepr T) ?ownerFrame_) ?PeerGroupPlaceholder_)) :pattern (?select2 h (?ClassRepr T) ?ownerFrame_)))) (assert (forall ((a Int) (T Int) (i Int) (r Int) (heap Int)) (! (or (not (= (IntsHeap heap) ?Smt.true)) (not (?subtypes (?typeof_ a) (?RefArray T r))) (= (Ints_ (?RefArrayGet (?select2 heap a ?elements_) i) T) ?Smt.true)) :pattern ((?subtypes (?typeof_ a) (?RefArray T r)) (?RefArrayGet (?select2 heap a ?elements_) i))))) (assert (forall ((a Int) (T Int) (r Int)) (! (or (= a ?nullObject) (not (?subtypes (?typeof_ a) (?RefArray T r))) (= (?Rank_ a) r)) :pattern (?subtypes (?typeof_ a) (?RefArray T r))))) (assert (forall ((T Int) (ET Int) (r Int)) (! (or (not (?subtypes T (?ValueArray ET r))) (= (?ArrayCategory_ T) ?ArrayCategoryValue_)) :pattern (?subtypes T (?ValueArray ET r))))) (assert (forall ((A Int) (r Int) (T Int)) (! (or (not (?subtypes T (?RefArray A r))) (not (or (not (= T (?RefArray (?ElementType_ T) r))) (not (?subtypes (?ElementType_ T) A))))) :pattern (?subtypes T (?RefArray A r))))) (assert (forall ((A Int) (r Int) (T Int)) (! (or (not (?subtypes T (?ValueArray A r))) (= T (?ValueArray A r))) :pattern (?subtypes T (?ValueArray A r))))) (assert (forall ((A Int) (B Int) (C Int)) (! (or (not (?subtypes C (?AsDirectSubClass B A))) (= (?OneClassDown C A) B)) :pattern (?subtypes C (?AsDirectSubClass B A))))) (assert (forall ((o Int) (T Int)) (! (iff (= (Ints_ o T) ?Smt.true) (or (= o ?nullObject) (?subtypes (?typeof_ o) T))) :pattern (Ints_ o T)))) (assert (forall ((o Int) (T Int)) (! (iff (= (IntsNotNull_ o T) ?Smt.true) (or (= o ?nullObject) (not (= (Ints_ o T) ?Smt.true)))) :pattern (IntsNotNull_ o T)))) (assert (forall ((h Int) (o Int)) (! (or (not (= (IntsHeap h) ?Smt.true)) (= o ?nullObject) (not (?subtypes (?typeof_ o) ?System.Array)) (not (or (not (= (?select2 h o ?inv_) (?typeof_ o))) (not (= (?select2 h o ?localinv_) (?typeof_ o)))))) :pattern ((?subtypes (?typeof_ o) ?System.Array) (?select2 h o ?inv_))))) (assert (forall ((h Int) (o Int) (f Int) (T Int)) (! (or (not (= (IntsHeap h) ?Smt.true)) (IntnRange (?select2 h o (?AsRangeField f T)) T)) :pattern (?select2 h o (?AsRangeField f T))))) (assert (forall ((h Int) (o Int) (f Int)) (! (or (not (= (IntsHeap h) ?Smt.true)) (not (= (?select2 h o ?allocated_) ?Smt.true)) (= (IntsAllocated h (?select2 h o f)) ?Smt.true)) :pattern (IntsAllocated h (?select2 h o f))))) (assert (forall ((h Int) (s Int) (f Int)) (! (or (not (= (IntsAllocated h s) ?Smt.true)) (= (IntsAllocated h (?StructGet_ s f)) ?Smt.true)) :pattern (IntsAllocated h (?StructGet_ s f))))) (assert (forall ((x Int) (f Int) (a0 Int)) (! (or (<= (+ a0 (* (- 1) (?fClosedTime f))) 0) (not (?isAllocated_ x a0)) (?isAllocated_ (?select f x) a0)) :pattern (?isAllocated_ (?select f x) a0)))) (assert (forall ((a Int) (e Int) (i Int) (a0 Int)) (! (or (<= (+ a0 (* (- 1) (?eClosedTime e))) 0) (not (?isAllocated_ a a0)) (?isAllocated_ (?select (?select e a) i) a0)) :pattern (?isAllocated_ (?select (?select e a) i) a0)))) (assert (forall ((e Int) (a Int) (i Int)) (! (= (?is (?select (?select (?asElems e) a) i) (?elemtype (?typeof a))) ?Smt.true) :pattern (?select (?select (?asElems e) a) i)))) (assert (forall ((t0 Int) (t1 Int)) (! (iff (?subtypes t0 (?array t1)) (not (or (not (= t0 (?array (?elemtype t0)))) (not (?subtypes (?elemtype t0) t1))))) :pattern (?subtypes t0 (?array t1))))) (assert (forall ((t0 Int) (t1 Int) (t2 Int)) (! (or (not (?subtypes t0 (?asChild t1 t2))) (= (?classDown t2 t0) (?asChild t1 t2))) :pattern (?subtypes t0 (?asChild t1 t2))))) (assert (forall ((t0 Int) (t1 Int)) (! (iff (?subtypes t0 (?array t1)) (not (or (not (= t0 (?array (?elemtype t0)))) (not (?subtypes (?elemtype t0) t1))))) :pattern (?subtypes t0 (?array t1))))) (assert (forall ((x Int) (t Int)) (! (or (not (= (?is x t) ?Smt.true)) (= (?cast x t) x)) :pattern (?cast x t)))) (assert (forall ((x Int) (t Int)) (! (or (not (?subtypes t ?Object)) (iff (= (?is x t) ?Smt.true) (or (= x ?null) (?subtypes (?typeof x) t)))) :pattern ((?subtypes t ?Object) (?is x t))))) (assert (forall ((e Int) (a Int) (i Int)) (! (= (?is (?select (?select (?asElems e) a) i) (?elemtype (?typeof a))) 1) :pattern (?select (?select (?asElems e) a) i)))) z3-4.4.0/src/ast/pattern/expr_pattern_match.h0000644000175000017500000000765112540347414020751 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: expr_pattern_match.h Abstract: Search for opportune pattern matching utilities. Author: Nikolaj Bjorner (nbjorner) 2007-04-10 Leonardo (leonardo) Notes: --*/ #ifndef _EXPR_PATTERN_MATCH_H_ #define _EXPR_PATTERN_MATCH_H_ #include"ast.h" #include"map.h" class expr_pattern_match { enum instr_kind { BACKTRACK, BIND, BIND_AC, BIND_C, CHOOSE_AC, CHOOSE_C, SET_VAR, CHECK_VAR, CHECK_TERM, SET_BOUND, CHECK_BOUND, YIELD, }; struct instr { instr(instr_kind k) : m_kind(k) {} instr(instr_kind k, unsigned o, unsigned next, app* app, unsigned count): m_kind(k), m_offset(o), m_next(next), m_app(app), m_count(count) {} instr_kind m_kind; unsigned m_offset; unsigned m_next; app* m_app; expr* m_pat; unsigned m_reg; unsigned m_other_reg; unsigned m_count; unsigned m_num_bound; }; typedef obj_map subst; typedef obj_map bound; struct inst_proc { ast_manager& m_manager; expr_ref_vector m_pinned; subst& m_subst; bound& m_bound; obj_map m_memoize; ptr_vector& m_regs; inst_proc(ast_manager& m, subst& s, bound& b, ptr_vector& regs) : m_manager(m), m_pinned(m), m_subst(s), m_bound(b), m_regs(regs) {} void operator()(ast* a) { } void operator()(expr* a) { m_memoize.insert(a, a); } void operator()(var* v) { var* b = 0; if (m_bound.find(v, b)) { m_memoize.insert(v, b); } else { UNREACHABLE(); } } void operator()(app * n) { unsigned r; ptr_vector args; unsigned num_args = n->get_num_args(); func_decl * decl = n->get_decl(); expr* result; if (m_subst.find(decl, r)) { decl = to_app(m_regs[r])->get_decl(); } for (unsigned i = 0; i < num_args; ++i) { expr* arg = 0; if (m_memoize.find(n->get_arg(i), arg)) { SASSERT(arg); args.push_back(arg); } else { UNREACHABLE(); } } if (m_manager.is_pattern(n)) { result = m_manager.mk_pattern(num_args, reinterpret_cast(args.c_ptr())); } else { result = m_manager.mk_app(decl, num_args, args.c_ptr()); } m_pinned.push_back(result); m_memoize.insert(n, result); return; } }; ast_manager & m_manager; quantifier_ref_vector m_precompiled; unsigned_vector m_first_instrs; svector m_instrs; ptr_vector m_regs; ptr_vector m_bound_dom; ptr_vector m_bound_rng; public: expr_pattern_match(ast_manager & manager); ~expr_pattern_match(); virtual bool match_quantifier(quantifier * qf, app_ref_vector & patterns, unsigned & weight); virtual void initialize(char const * database); void display(std::ostream& out) const; private: void instantiate(expr* a, unsigned num_bound, subst& s, expr_ref& result); void compile(expr* q); bool match(expr* a, unsigned init, subst& s); bool match_decl(func_decl const * pat, func_decl const * d) const; bool is_var(func_decl* d); void display(std::ostream& out, instr const& pc) const; }; #endif z3-4.4.0/src/ast/pattern/pattern_inference_params.h0000644000175000017500000000257712540347414022122 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pattern_inference_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-03-24. Revision History: --*/ #ifndef _PATTERN_INFERENCE_PARAMS_H_ #define _PATTERN_INFERENCE_PARAMS_H_ #include"params.h" enum arith_pattern_inference_kind { AP_NO, // do not infer patterns with arithmetic terms AP_CONSERVATIVE, // only infer patterns with arithmetic terms if there is no other option AP_FULL // always use patterns with arithmetic terms }; struct pattern_inference_params { unsigned m_pi_max_multi_patterns; bool m_pi_block_loop_patterns; arith_pattern_inference_kind m_pi_arith; bool m_pi_use_database; unsigned m_pi_arith_weight; unsigned m_pi_non_nested_arith_weight; bool m_pi_pull_quantifiers; int m_pi_nopat_weight; bool m_pi_avoid_skolems; bool m_pi_warnings; pattern_inference_params(params_ref const & p = params_ref()): m_pi_nopat_weight(-1), m_pi_avoid_skolems(true) { updt_params(p); } void updt_params(params_ref const & _p); }; #endif /* _PATTERN_INFERENCE_PARAMS_H_ */ z3-4.4.0/src/ast/pattern/pattern_inference.cpp0000644000175000017500000006525512540347414021114 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pattern_inference.cpp Abstract: Author: Leonardo de Moura (leonardo) 2006-12-08. Revision History: --*/ #include"pattern_inference.h" #include"ast_ll_pp.h" #include"ast_pp.h" #include"ast_util.h" #include"warning.h" #include"arith_decl_plugin.h" #include"pull_quant.h" #include"well_sorted.h" #include"for_each_expr.h" void smaller_pattern::save(expr * p1, expr * p2) { expr_pair e(p1, p2); if (!m_cache.contains(e)) { TRACE("smaller_pattern_proc", tout << "saving: " << p1->get_id() << " " << p2->get_id() << "\n";); m_cache.insert(e); m_todo.push_back(e); } } bool smaller_pattern::process(expr * p1, expr * p2) { m_todo.reset(); m_cache.reset(); save(p1, p2); while (!m_todo.empty()) { expr_pair & curr = m_todo.back(); p1 = curr.first; p2 = curr.second; m_todo.pop_back(); ast_kind k1 = p1->get_kind(); if (k1 != AST_VAR && k1 != p2->get_kind()) return false; switch (k1) { case AST_APP: { app * app1 = to_app(p1); app * app2 = to_app(p2); unsigned num1 = app1->get_num_args(); if (num1 != app2->get_num_args() || app1->get_decl() != app2->get_decl()) return false; for (unsigned i = 0; i < num1; i++) save(app1->get_arg(i), app2->get_arg(i)); break; } case AST_VAR: { unsigned idx = to_var(p1)->get_idx(); if (idx < m_bindings.size()) { if (m_bindings[idx] == 0) m_bindings[idx] = p2; else if (m_bindings[idx] != p2) return false; } // it is a variable bound by an external quantifier else if (p1 != p2) return false; break; } default: if (p1 != p2) return false; break; } } return true; } bool smaller_pattern::operator()(unsigned num_bindings, expr * p1, expr * p2) { m_bindings.resize(num_bindings); for (unsigned i = 0; i < num_bindings; i++) m_bindings[i] = 0; return process(p1, p2); } pattern_inference::pattern_inference(ast_manager & m, pattern_inference_params & params): simplifier(m), m_params(params), m_bfid(m.get_basic_family_id()), m_afid(m.mk_family_id("arith")), m_le(m), m_nested_arith_only(true), m_block_loop_patterns(params.m_pi_block_loop_patterns), m_candidates(m), m_pattern_weight_lt(m_candidates_info), m_collect(m, *this), m_contains_subpattern(*this), m_database(m) { if (params.m_pi_arith == AP_NO) register_forbidden_family(m_afid); enable_ac_support(false); } void pattern_inference::collect::operator()(expr * n, unsigned num_bindings) { SASSERT(m_info.empty()); SASSERT(m_todo.empty()); SASSERT(m_cache.empty()); m_num_bindings = num_bindings; m_todo.push_back(entry(n, 0)); while (!m_todo.empty()) { entry & e = m_todo.back(); n = e.m_node; unsigned delta = e.m_delta; TRACE("collect", tout << "processing: " << n->get_id() << " " << delta << " kind: " << n->get_kind() << "\n";); TRACE("collect_info", tout << mk_pp(n, m) << "\n";); if (visit_children(n, delta)) { m_todo.pop_back(); save_candidate(n, delta); } } reset(); } inline void pattern_inference::collect::visit(expr * n, unsigned delta, bool & visited) { entry e(n, delta); if (!m_cache.contains(e)) { m_todo.push_back(e); visited = false; } } bool pattern_inference::collect::visit_children(expr * n, unsigned delta) { bool visited = true; unsigned i; switch (n->get_kind()) { case AST_APP: i = to_app(n)->get_num_args(); while (i > 0) { --i; visit(to_app(n)->get_arg(i), delta, visited); } break; case AST_QUANTIFIER: visit(to_quantifier(n)->get_expr(), delta + to_quantifier(n)->get_num_decls(), visited); break; default: break; } return visited; } inline void pattern_inference::collect::save(expr * n, unsigned delta, info * i) { m_cache.insert(entry(n, delta), i); if (i != 0) m_info.push_back(i); } void pattern_inference::collect::save_candidate(expr * n, unsigned delta) { switch (n->get_kind()) { case AST_VAR: { unsigned idx = to_var(n)->get_idx(); if (idx >= delta) { idx = idx - delta; uint_set free_vars; if (idx < m_num_bindings) free_vars.insert(idx); info * i = 0; if (delta == 0) i = alloc(info, m, n, free_vars, 1); else i = alloc(info, m, m.mk_var(idx, to_var(n)->get_sort()), free_vars, 1); save(n, delta, i); } else { save(n, delta, 0); } return; } case AST_APP: { app * c = to_app(n); func_decl * decl = c->get_decl(); if (m_owner.is_forbidden(c)) { save(n, delta, 0); return; } if (c->get_num_args() == 0) { save(n, delta, alloc(info, m, n, uint_set(), 1)); return; } ptr_buffer buffer; bool changed = false; // false if none of the children is mapped to a node different from itself. uint_set free_vars; unsigned size = 1; unsigned num = c->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * child = c->get_arg(i); info * child_info = 0; #ifdef Z3DEBUG bool found = #endif m_cache.find(entry(child, delta), child_info); SASSERT(found); if (child_info == 0) { save(n, delta, 0); return; } buffer.push_back(child_info->m_node.get()); free_vars |= child_info->m_free_vars; size += child_info->m_size; if (child != child_info->m_node.get()) changed = true; } app * new_node = 0; if (changed) new_node = m.mk_app(decl, buffer.size(), buffer.c_ptr()); else new_node = to_app(n); save(n, delta, alloc(info, m, new_node, free_vars, size)); // Remark: arithmetic patterns are only used if they are nested inside other terms. // That is, we never consider x + 1 as pattern. On the other hand, f(x+1) can be a pattern // if arithmetic is not in the forbidden list. // // Remark: The rule above has an exception. The operators (div, idiv, mod) are allowed to be // used as patterns even when they are not nested in other terms. The motivation is that // Z3 currently doesn't implement them (i.e., they are uninterpreted). So, some users add axioms // stating properties about these operators. family_id fid = c->get_family_id(); decl_kind k = c->get_decl_kind(); if (!free_vars.empty() && (fid != m_afid || (fid == m_afid && !m_owner.m_nested_arith_only && (k == OP_DIV || k == OP_IDIV || k == OP_MOD || k == OP_REM || k == OP_MUL)))) { TRACE("pattern_inference", tout << "potential candidate: \n" << mk_pp(new_node, m) << "\n";); m_owner.add_candidate(new_node, free_vars, size); } return; } default: save(n, delta, 0); return; } } void pattern_inference::collect::reset() { m_cache.reset(); std::for_each(m_info.begin(), m_info.end(), delete_proc()); m_info.reset(); SASSERT(m_todo.empty()); } void pattern_inference::add_candidate(app * n, uint_set const & free_vars, unsigned size) { for (unsigned i = 0; i < m_num_no_patterns; i++) { if (n == m_no_patterns[i]) return; } if (!m_candidates_info.contains(n)) { m_candidates_info.insert(n, info(free_vars, size)); m_candidates.push_back(n); } } /** \brief Copy the non-looping patterns in m_candidates to result when m_params.m_pi_block_loop_patterns = true. Otherwise, copy m_candidates to result. */ void pattern_inference::filter_looping_patterns(ptr_vector & result) { unsigned num = m_candidates.size(); for (unsigned i1 = 0; i1 < num; i1++) { app * n1 = m_candidates.get(i1); expr2info::obj_map_entry * e1 = m_candidates_info.find_core(n1); SASSERT(e1); uint_set const & s1 = e1->get_data().m_value.m_free_vars; if (m_block_loop_patterns) { bool smaller = false; for (unsigned i2 = 0; i2 < num; i2++) { if (i1 != i2) { app * n2 = m_candidates.get(i2); expr2info::obj_map_entry * e2 = m_candidates_info.find_core(n2); if (e2) { uint_set const & s2 = e2->get_data().m_value.m_free_vars; // Remark: the comparison operator only makes sense if both AST nodes // contain the same number of variables. // Example: // (f X Y) <: (f (g X Z W) Y) if (s1 == s2 && m_le(m_num_bindings, n1, n2) && !m_le(m_num_bindings, n2, n1)) { smaller = true; break; } } } } if (!smaller) result.push_back(n1); else m_candidates_info.erase(n1); } else { result.push_back(n1); } } } inline void pattern_inference::contains_subpattern::save(expr * n) { unsigned id = n->get_id(); m_already_processed.assure_domain(id); if (!m_already_processed.contains(id)) { m_todo.push_back(n); m_already_processed.insert(id); } } bool pattern_inference::contains_subpattern::operator()(expr * n) { m_already_processed.reset(); m_todo.reset(); expr2info::obj_map_entry * _e = m_owner.m_candidates_info.find_core(n); SASSERT(_e); uint_set const & s1 = _e->get_data().m_value.m_free_vars; save(n); unsigned num; while (!m_todo.empty()) { expr * curr = m_todo.back(); m_todo.pop_back(); switch (curr->get_kind()) { case AST_APP: if (curr != n) { expr2info::obj_map_entry * e = m_owner.m_candidates_info.find_core(curr); if (e) { uint_set const & s2 = e->get_data().m_value.m_free_vars; SASSERT(s2.subset_of(s1)); if (s1 == s2) { TRACE("pattern_inference", tout << mk_pp(n, m_owner.m) << "\nis bigger than\n" << mk_pp(to_app(curr), m_owner.m) << "\n";); return true; } } } num = to_app(curr)->get_num_args(); for (unsigned i = 0; i < num; i++) save(to_app(curr)->get_arg(i)); break; case AST_VAR: break; default: UNREACHABLE(); } } return false; } /** Return true if n contains a direct/indirect child that is also a pattern, and contains the same number of free variables. */ inline bool pattern_inference::contains_subpattern(expr * n) { return m_contains_subpattern(n); } /** \brief Copy a pattern p in patterns to result, if there is no direct/indirect child of p in patterns which contains the same set of variables. Remark: Every pattern p in patterns is also a member of m_pattern_map. */ void pattern_inference::filter_bigger_patterns(ptr_vector const & patterns, ptr_vector & result) { ptr_vector::const_iterator it = patterns.begin(); ptr_vector::const_iterator end = patterns.end(); for (; it != end; ++it) { app * curr = *it; if (!contains_subpattern(curr)) result.push_back(curr); } } bool pattern_inference::pattern_weight_lt::operator()(expr * n1, expr * n2) const { expr2info::obj_map_entry * e1 = m_candidates_info.find_core(n1); expr2info::obj_map_entry * e2 = m_candidates_info.find_core(n2); SASSERT(e1 != 0); SASSERT(e2 != 0); info const & i1 = e1->get_data().m_value; info const & i2 = e2->get_data().m_value; unsigned num_free_vars1 = i1.m_free_vars.num_elems(); unsigned num_free_vars2 = i2.m_free_vars.num_elems(); return num_free_vars1 > num_free_vars2 || (num_free_vars1 == num_free_vars2 && i1.m_size < i2.m_size); } /** \brief Create unary patterns (single expressions that contain all bound variables). If a candidate does not contain all bound variables, then it is copied to remaining_candidate_patterns. The new patterns are stored in result. */ void pattern_inference::candidates2unary_patterns(ptr_vector const & candidate_patterns, ptr_vector & remaining_candidate_patterns, app_ref_buffer & result) { ptr_vector::const_iterator it = candidate_patterns.begin(); ptr_vector::const_iterator end = candidate_patterns.end(); for (; it != end; ++it) { app * candidate = *it; expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate); info const & i = e->get_data().m_value; if (i.m_free_vars.num_elems() == m_num_bindings) { app * new_pattern = m.mk_pattern(candidate); result.push_back(new_pattern); } else { remaining_candidate_patterns.push_back(candidate); } } } // TODO: this code is too inefficient when the number of candidate // patterns is too big. // HACK: limit the number of case-splits: #define MAX_SPLITS 32 void pattern_inference::candidates2multi_patterns(unsigned max_num_patterns, ptr_vector const & candidate_patterns, app_ref_buffer & result) { SASSERT(!candidate_patterns.empty()); m_pre_patterns.push_back(alloc(pre_pattern)); unsigned sz = candidate_patterns.size(); unsigned num_splits = 0; for (unsigned j = 0; j < m_pre_patterns.size(); j++) { pre_pattern * curr = m_pre_patterns[j]; if (curr->m_free_vars.num_elems() == m_num_bindings) { app * new_pattern = m.mk_pattern(curr->m_exprs.size(), curr->m_exprs.c_ptr()); result.push_back(new_pattern); if (result.size() >= max_num_patterns) return; } else if (curr->m_idx < sz) { app * n = candidate_patterns[curr->m_idx]; expr2info::obj_map_entry * e = m_candidates_info.find_core(n); uint_set const & s = e->get_data().m_value.m_free_vars; if (!s.subset_of(curr->m_free_vars)) { pre_pattern * new_p = alloc(pre_pattern,*curr); new_p->m_exprs.push_back(n); new_p->m_free_vars |= s; new_p->m_idx++; m_pre_patterns.push_back(new_p); if (num_splits < MAX_SPLITS) { m_pre_patterns[j] = 0; curr->m_idx++; m_pre_patterns.push_back(curr); num_splits++; } } else { m_pre_patterns[j] = 0; curr->m_idx++; m_pre_patterns.push_back(curr); } } TRACE("pattern_inference", tout << "m_pre_patterns.size(): " << m_pre_patterns.size() << "\nnum_splits: " << num_splits << "\n";); } } void pattern_inference::reset_pre_patterns() { std::for_each(m_pre_patterns.begin(), m_pre_patterns.end(), delete_proc()); m_pre_patterns.reset(); } static void dump_app_vector(std::ostream & out, ptr_vector const & v, ast_manager & m) { ptr_vector::const_iterator it = v.begin(); ptr_vector::const_iterator end = v.end(); for (; it != end; ++it) out << mk_pp(*it, m) << "\n"; } bool pattern_inference::is_forbidden(app * n) const { func_decl const * decl = n->get_decl(); if (is_ground(n)) return false; // Remark: skolem constants should not be used in patterns, since they do not // occur outside of the quantifier. That is, Z3 will never match this kind of // pattern. if (m_params.m_pi_avoid_skolems && decl->is_skolem()) { CTRACE("pattern_inference_skolem", decl->is_skolem(), tout << "ignoring: " << mk_pp(n, m) << "\n";); return true; } if (is_forbidden(decl)) return true; return false; } bool pattern_inference::has_preferred_patterns(ptr_vector & candidate_patterns, app_ref_buffer & result) { if (m_preferred.empty()) return false; bool found = false; ptr_vector::const_iterator it = candidate_patterns.begin(); ptr_vector::const_iterator end = candidate_patterns.end(); for (; it != end; ++it) { app * candidate = *it; if (m_preferred.contains(to_app(candidate)->get_decl())) { expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate); info const & i = e->get_data().m_value; if (i.m_free_vars.num_elems() == m_num_bindings) { TRACE("pattern_inference", tout << "found preferred pattern:\n" << mk_pp(candidate, m) << "\n";); app * p = m.mk_pattern(candidate); result.push_back(p); found = true; } } } return found; } void pattern_inference::mk_patterns(unsigned num_bindings, expr * n, unsigned num_no_patterns, expr * const * no_patterns, app_ref_buffer & result) { m_num_bindings = num_bindings; m_num_no_patterns = num_no_patterns; m_no_patterns = no_patterns; m_collect(n, num_bindings); TRACE("pattern_inference", tout << mk_pp(n, m); tout << "\ncandidates:\n"; unsigned num = m_candidates.size(); for (unsigned i = 0; i < num; i++) { tout << mk_pp(m_candidates.get(i), m) << "\n"; }); if (!m_candidates.empty()) { m_tmp1.reset(); filter_looping_patterns(m_tmp1); TRACE("pattern_inference", tout << "candidates after removing looping-patterns:\n"; dump_app_vector(tout, m_tmp1, m);); SASSERT(!m_tmp1.empty()); if (!has_preferred_patterns(m_tmp1, result)) { // continue if there are no preferred patterns m_tmp2.reset(); filter_bigger_patterns(m_tmp1, m_tmp2); SASSERT(!m_tmp2.empty()); TRACE("pattern_inference", tout << "candidates after removing bigger patterns:\n"; dump_app_vector(tout, m_tmp2, m);); m_tmp1.reset(); candidates2unary_patterns(m_tmp2, m_tmp1, result); unsigned num_extra_multi_patterns = m_params.m_pi_max_multi_patterns; if (result.empty()) num_extra_multi_patterns++; if (num_extra_multi_patterns > 0 && !m_tmp1.empty()) { // m_pattern_weight_lt is not a total order std::stable_sort(m_tmp1.begin(), m_tmp1.end(), m_pattern_weight_lt); TRACE("pattern_inference", tout << "candidates after sorting:\n"; dump_app_vector(tout, m_tmp1, m);); candidates2multi_patterns(num_extra_multi_patterns, m_tmp1, result); } } } reset_pre_patterns(); m_candidates_info.reset(); m_candidates.reset(); } #include"database.h" // defines g_pattern_database void pattern_inference::reduce1_quantifier(quantifier * q) { TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m) << "\n";); if (!q->is_forall()) { simplifier::reduce1_quantifier(q); return; } int weight = q->get_weight(); if (m_params.m_pi_use_database) { m_database.initialize(g_pattern_database); app_ref_vector new_patterns(m); unsigned new_weight; if (m_database.match_quantifier(q, new_patterns, new_weight)) { #ifdef Z3DEBUG for (unsigned i = 0; i < new_patterns.size(); i++) { SASSERT(is_well_sorted(m, new_patterns.get(i))); } #endif quantifier_ref new_q(m); if (q->get_num_patterns() > 0) { // just update the weight... TRACE("pattern_inference", tout << "updating weight to: " << new_weight << "\n" << mk_pp(q, m) << "\n";); new_q = m.update_quantifier_weight(q, new_weight); } else { quantifier_ref tmp(m); tmp = m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), q->get_expr()); new_q = m.update_quantifier_weight(tmp, new_weight); TRACE("pattern_inference", tout << "found patterns in database, weight: " << new_weight << "\n" << mk_pp(new_q, m) << "\n";); } proof * pr = 0; if (m.fine_grain_proofs()) pr = m.mk_rewrite(q, new_q); cache_result(q, new_q, pr); return; } } if (q->get_num_patterns() > 0) { simplifier::reduce1_quantifier(q); return; } if (m_params.m_pi_nopat_weight >= 0) weight = m_params.m_pi_nopat_weight; SASSERT(q->get_num_patterns() == 0); expr * new_body; proof * new_body_pr; get_cached(q->get_expr(), new_body, new_body_pr); ptr_buffer new_no_patterns; unsigned num_no_patterns = q->get_num_no_patterns(); for (unsigned i = 0; i < num_no_patterns; i++) { expr * new_pattern; proof * new_pattern_pr; get_cached(q->get_no_pattern(i), new_pattern, new_pattern_pr); new_no_patterns.push_back(new_pattern); } app_ref_buffer new_patterns(m); if (m_params.m_pi_arith == AP_CONSERVATIVE) m_forbidden.push_back(m_afid); mk_patterns(q->get_num_decls(), new_body, new_no_patterns.size(), new_no_patterns.c_ptr(), new_patterns); if (new_patterns.empty() && !new_no_patterns.empty()) { if (new_patterns.empty()) { mk_patterns(q->get_num_decls(), new_body, 0, 0, new_patterns); if (m_params.m_pi_warnings && !new_patterns.empty()) { warning_msg("ignoring nopats annotation because Z3 couldn't find any other pattern (quantifier id: %s)", q->get_qid().str().c_str()); } } } if (m_params.m_pi_arith == AP_CONSERVATIVE) { m_forbidden.pop_back(); if (new_patterns.empty()) { flet l1(m_block_loop_patterns, false); // allow looping patterns mk_patterns(q->get_num_decls(), new_body, new_no_patterns.size(), new_no_patterns.c_ptr(), new_patterns); if (!new_patterns.empty()) { weight = std::max(weight, static_cast(m_params.m_pi_arith_weight)); if (m_params.m_pi_warnings) { warning_msg("using arith. in pattern (quantifier id: %s), the weight was increased to %d (this value can be modified using PI_ARITH_WEIGHT=).", q->get_qid().str().c_str(), weight); } } } } if (m_params.m_pi_arith != AP_NO && new_patterns.empty()) { if (new_patterns.empty()) { flet l1(m_nested_arith_only, false); // try to find a non-nested arith pattern flet l2(m_block_loop_patterns, false); // allow looping patterns mk_patterns(q->get_num_decls(), new_body, new_no_patterns.size(), new_no_patterns.c_ptr(), new_patterns); if (!new_patterns.empty()) { weight = std::max(weight, static_cast(m_params.m_pi_non_nested_arith_weight)); if (m_params.m_pi_warnings) { warning_msg("using non nested arith. pattern (quantifier id: %s), the weight was increased to %d (this value can be modified using PI_NON_NESTED_ARITH_WEIGHT=).", q->get_qid().str().c_str(), weight); } // verbose_stream() << mk_pp(q, m) << "\n"; } } } quantifier_ref new_q(m); new_q = m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_body); if (weight != q->get_weight()) new_q = m.update_quantifier_weight(new_q, weight); proof_ref pr(m); if (m.fine_grain_proofs()) { if (new_body_pr == 0) new_body_pr = m.mk_reflexivity(new_body); pr = m.mk_quant_intro(q, new_q, new_body_pr); } if (new_patterns.empty() && m_params.m_pi_pull_quantifiers) { pull_quant pull(m); expr_ref new_expr(m); proof_ref new_pr(m); pull(new_q, new_expr, new_pr); quantifier * new_new_q = to_quantifier(new_expr); if (new_new_q != new_q) { mk_patterns(new_new_q->get_num_decls(), new_new_q->get_expr(), 0, 0, new_patterns); if (!new_patterns.empty()) { if (m_params.m_pi_warnings) { warning_msg("pulled nested quantifier to be able to find an useable pattern (quantifier id: %s)", q->get_qid().str().c_str()); } new_q = m.update_quantifier(new_new_q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_new_q->get_expr()); if (m.fine_grain_proofs()) { pr = m.mk_transitivity(pr, new_pr); pr = m.mk_transitivity(pr, m.mk_quant_intro(new_new_q, new_q, m.mk_reflexivity(new_q->get_expr()))); } TRACE("pattern_inference", tout << "pulled quantifier:\n" << mk_pp(new_q, m) << "\n";); } } } if (new_patterns.empty()) { if (m_params.m_pi_warnings) { warning_msg("failed to find a pattern for quantifier (quantifier id: %s)", q->get_qid().str().c_str()); } TRACE("pi_failed", tout << mk_pp(q, m) << "\n";); } if (new_patterns.empty() && new_body == q->get_expr()) { cache_result(q, q, 0); return; } cache_result(q, new_q, pr); } #if 0 // unused static void dump_expr_vector(std::ostream & out, ptr_vector const & v, ast_manager & m) { ptr_vector::const_iterator it = v.begin(); ptr_vector::const_iterator end = v.end(); for (; it != end; ++it) out << mk_pp(*it, m) << "\n"; } #endif z3-4.4.0/src/ast/pattern/pattern_inference_params_helper.pyg0000644000175000017500000000274312540347414024024 0ustar michaelmichaeldef_module_params(class_name='pattern_inference_params_helper', module_name='pi', description='pattern inference (heuristics) for universal formulas (without annotation)', export=True, params=(('max_multi_patterns', UINT, 0, 'when patterns are not provided, the prover uses a heuristic to infer them, this option sets the threshold on the number of extra multi-patterns that can be created; by default, the prover creates at most one multi-pattern when there is no unary pattern'), ('block_loop_patterns', BOOL, True, 'block looping patterns during pattern inference'), ('arith', UINT, 1, '0 - do not infer patterns with arithmetic terms, 1 - use patterns with arithmetic terms if there is no other pattern, 2 - always use patterns with arithmetic terms'), ('use_database', BOOL, False, 'use pattern database'), ('arith_weight', UINT, 5, 'default weight for quantifiers where the only available pattern has nested arithmetic terms'), ('non_nested_arith_weight', UINT, 10, 'default weight for quantifiers where the only available pattern has non nested arithmetic terms'), ('pull_quantifiers', BOOL, True, 'pull nested quantifiers, if no pattern was found'), ('warnings', BOOL, False, 'enable/disable warning messages in the pattern inference module'))) z3-4.4.0/src/ast/pattern/pattern_inference.h0000644000175000017500000001757012540347414020556 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pattern_inference.h Abstract: Author: Leonardo de Moura (leonardo) 2006-12-08. Revision History: --*/ #ifndef _PATTERN_INFERENCE_H_ #define _PATTERN_INFERENCE_H_ #include"ast.h" #include"simplifier.h" #include"pattern_inference_params.h" #include"vector.h" #include"uint_set.h" #include"nat_set.h" #include"obj_hashtable.h" #include"obj_pair_hashtable.h" #include"map.h" #include"expr_pattern_match.h" /** \brief A pattern p_1 is smaller than a pattern p_2 iff every instance of p_2 is also an instance of p_1. Example: f(X) is smaller than f(g(X)) because every instance of f(g(X)) is also an instance of f(X). */ class smaller_pattern { ast_manager & m; ptr_vector m_bindings; typedef std::pair expr_pair; typedef obj_pair_hashtable cache; svector m_todo; cache m_cache; void save(expr * p1, expr * p2); bool process(expr * p1, expr * p2); smaller_pattern & operator=(smaller_pattern const &); public: smaller_pattern(ast_manager & m): m(m) { } bool operator()(unsigned num_bindings, expr * p1, expr * p2); }; class pattern_inference : public simplifier { pattern_inference_params & m_params; family_id m_bfid; family_id m_afid; svector m_forbidden; obj_hashtable m_preferred; smaller_pattern m_le; unsigned m_num_bindings; unsigned m_num_no_patterns; expr * const * m_no_patterns; bool m_nested_arith_only; bool m_block_loop_patterns; struct info { uint_set m_free_vars; unsigned m_size; info(uint_set const & vars, unsigned size): m_free_vars(vars), m_size(size) { } info(): m_free_vars(), m_size(0) { } }; typedef obj_map expr2info; expr2info m_candidates_info; // candidate -> set of free vars + size app_ref_vector m_candidates; ptr_vector m_tmp1; ptr_vector m_tmp2; ptr_vector m_todo; // Compare candidates patterns based on their usefulness // p1 < p2 if // - p1 has more free variables than p2 // - p1 and p2 has the same number of free variables, // and p1 is smaller than p2. struct pattern_weight_lt { expr2info & m_candidates_info; pattern_weight_lt(expr2info & i): m_candidates_info(i) { } bool operator()(expr * n1, expr * n2) const; }; pattern_weight_lt m_pattern_weight_lt; // // Functor for collecting candidates. // class collect { struct entry { expr * m_node; unsigned m_delta; entry():m_node(0), m_delta(0) {} entry(expr * n, unsigned d):m_node(n), m_delta(d) {} unsigned hash() const { return hash_u_u(m_node->get_id(), m_delta); } bool operator==(entry const & e) const { return m_node == e.m_node && m_delta == e.m_delta; } }; struct info { expr_ref m_node; uint_set m_free_vars; unsigned m_size; info(ast_manager & m, expr * n, uint_set const & vars, unsigned sz): m_node(n, m), m_free_vars(vars), m_size(sz) {} }; ast_manager & m; pattern_inference & m_owner; family_id m_afid; unsigned m_num_bindings; typedef map, default_eq > cache; cache m_cache; ptr_vector m_info; svector m_todo; void visit(expr * n, unsigned delta, bool & visited); bool visit_children(expr * n, unsigned delta); void save(expr * n, unsigned delta, info * i); void save_candidate(expr * n, unsigned delta); void reset(); public: collect(ast_manager & m, pattern_inference & o):m(m), m_owner(o), m_afid(m.mk_family_id("arith")) {} void operator()(expr * n, unsigned num_bindings); }; collect m_collect; void add_candidate(app * n, uint_set const & s, unsigned size); void filter_looping_patterns(ptr_vector & result); bool has_preferred_patterns(ptr_vector & candidate_patterns, app_ref_buffer & result); void filter_bigger_patterns(ptr_vector const & patterns, ptr_vector & result); class contains_subpattern { pattern_inference & m_owner; nat_set m_already_processed; ptr_vector m_todo; void save(expr * n); public: contains_subpattern(pattern_inference & owner): m_owner(owner) {} bool operator()(expr * n); }; contains_subpattern m_contains_subpattern; bool contains_subpattern(expr * n); struct pre_pattern { ptr_vector m_exprs; // elements of the pattern. uint_set m_free_vars; // set of free variables in m_exprs unsigned m_idx; // idx of the next candidate to process. pre_pattern(): m_idx(0) { } }; ptr_vector m_pre_patterns; expr_pattern_match m_database; void candidates2unary_patterns(ptr_vector const & candidate_patterns, ptr_vector & remaining_candidate_patterns, app_ref_buffer & result); void candidates2multi_patterns(unsigned max_num_patterns, ptr_vector const & candidate_patterns, app_ref_buffer & result); void reset_pre_patterns(); /** \brief All minimal unary patterns (i.e., expressions that contain all bound variables) are copied to result. If there are unary patterns, then at most num_extra_multi_patterns multi patterns are created. If there are no unary pattern, then at most 1 + num_extra_multi_patterns multi_patterns are created. */ void mk_patterns(unsigned num_bindings, // IN number of bindings. expr * n, // IN node where the patterns are going to be extracted. unsigned num_no_patterns, // IN num. patterns that should not be used. expr * const * no_patterns, // IN patterns that should not be used. app_ref_buffer & result); // OUT result virtual void reduce1_quantifier(quantifier * q); public: pattern_inference(ast_manager & m, pattern_inference_params & params); void register_forbidden_family(family_id fid) { SASSERT(fid != m_bfid); m_forbidden.push_back(fid); } /** \brief Register f as a preferred function symbol. The inference algorithm gives preference to patterns rooted by this kind of function symbol. */ void register_preferred(func_decl * f) { m_preferred.insert(f); } void register_preferred(unsigned num, func_decl * const * fs) { for (unsigned i = 0; i < num; i++) register_preferred(fs[i]); } bool is_forbidden(func_decl const * decl) const { family_id fid = decl->get_family_id(); if (fid == m_bfid && decl->get_decl_kind() != OP_TRUE && decl->get_decl_kind() != OP_FALSE) return true; return std::find(m_forbidden.begin(), m_forbidden.end(), fid) != m_forbidden.end(); } bool is_forbidden(app * n) const; }; #endif /* _PATTERN_INFERENCE_H_ */ z3-4.4.0/src/ast/pattern/expr_pattern_match.cpp0000644000175000017500000003377512540347414021312 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: ast_pattern_match.cpp Abstract: Search for opportune pattern matching utilities. Author: Nikolaj Bjorner (nbjorner) 2007-04-10 Leonardo (leonardo) Notes: instead of the brute force enumeration of permutations we can add an instruction 'gate' which copies the ast into a register and creates another register with the same term. Matching against a 'gate' is a noop, apart from clearing the ast in the register. Then on backtracking we know how many terms were matched from the permutation. It does not make sense to enumerate all combinations of terms that were not considered, so skip these. Also, compilation should re-order terms to fail fast. --*/ #include"ast.h" #include"expr_pattern_match.h" #include"for_each_ast.h" #include"ast_ll_pp.h" #include"ast_pp.h" #include"cmd_context.h" #include"smt2parser.h" expr_pattern_match::expr_pattern_match(ast_manager & manager): m_manager(manager), m_precompiled(manager) { } expr_pattern_match::~expr_pattern_match() { } bool expr_pattern_match::match_quantifier(quantifier* qf, app_ref_vector& patterns, unsigned& weight) { if (m_regs.empty()) { // HACK: the code crashes if database is empty. return false; } m_regs[0] = qf->get_expr(); for (unsigned i = 0; i < m_precompiled.size(); ++i) { quantifier* qf2 = m_precompiled[i].get(); if (qf2->is_forall() != qf->is_forall()) { continue; } if (qf2->get_num_decls() != qf->get_num_decls()) { continue; } subst s; if (match(qf->get_expr(), m_first_instrs[i], s)) { for (unsigned j = 0; j < qf2->get_num_patterns(); ++j) { app* p = static_cast(qf2->get_pattern(j)); expr_ref p_result(m_manager); instantiate(p, qf->get_num_decls(), s, p_result); patterns.push_back(to_app(p_result.get())); } weight = qf2->get_weight(); return true; } } return false; } void expr_pattern_match::instantiate(expr* a, unsigned num_bound, subst& s, expr_ref& result) { bound b; for (unsigned i = 0; i < num_bound; ++i) { b.insert(m_bound_dom[i], m_bound_rng[i]); } inst_proc proc(m_manager, s, b, m_regs); for_each_ast(proc, a); expr* v = 0; proc.m_memoize.find(a, v); SASSERT(v); result = v; } void expr_pattern_match::compile(expr* q) { SASSERT(q->get_kind() == AST_QUANTIFIER); quantifier* qf = to_quantifier(q); unsigned ip = m_instrs.size(); m_first_instrs.push_back(ip); m_precompiled.push_back(qf); instr instr(BACKTRACK); unsigned_vector regs; ptr_vector pats; unsigned max_reg = 1; subst s; pats.push_back(qf->get_expr()); regs.push_back(0); unsigned num_bound = 0; obj_map bound; while (!pats.empty()) { unsigned reg = regs.back(); expr* pat = pats.back(); regs.pop_back(); pats.pop_back(); instr.m_pat = pat; instr.m_next = m_instrs.size()+1; instr.m_reg = reg; instr.m_offset = max_reg; switch(pat->get_kind()) { case AST_VAR: { var* b = to_var(pat); if (bound.find(b, instr.m_num_bound)) { instr.m_kind = CHECK_BOUND; } else { instr.m_kind = SET_BOUND; instr.m_num_bound = num_bound; bound.insert(b, num_bound); ++num_bound; } break; } case AST_APP: { unsigned r = 0; app* app = to_app(pat); func_decl* d = app->get_decl(); for (unsigned i = 0; i < app->get_num_args(); ++i) { regs.push_back(max_reg); pats.push_back(app->get_arg(i)); ++max_reg; } if (is_var(d)) { if (s.find(d, r)) { instr.m_kind = CHECK_VAR; instr.m_other_reg = r; } else { instr.m_kind = SET_VAR; s.insert(d, reg); } } else { if (d->is_associative() && d->is_commutative()) { instr.m_kind = BIND_AC; } else if (d->is_commutative()) { SASSERT(app->get_num_args() == 2); instr.m_kind = BIND_C; } else { instr.m_kind = BIND; } } break; } default: instr.m_kind = CHECK_TERM; break; } m_instrs.push_back(instr); } if (m_regs.size() <= max_reg) { m_regs.resize(max_reg+1, 0); } if (m_bound_dom.size() <= num_bound) { m_bound_dom.resize(num_bound+1, 0); m_bound_rng.resize(num_bound+1, 0); } instr.m_kind = YIELD; m_instrs.push_back(instr); } bool expr_pattern_match::match(expr* a, unsigned init, subst& s) { svector bstack; instr pc = m_instrs[init]; while (true) { bool ok = false; switch(pc.m_kind) { case YIELD: // substitution s contains registers with matching declarations. return true; case CHECK_TERM: ok = (pc.m_pat == m_regs[pc.m_reg]); break; case SET_VAR: case CHECK_VAR: { app* app1 = to_app(pc.m_pat); a = m_regs[pc.m_reg]; if (a->get_kind() != AST_APP) { break; } app* app2 = to_app(a); if (app1->get_num_args() != app2->get_num_args()) { break; } if (pc.m_kind == CHECK_VAR && to_app(m_regs[pc.m_reg])->get_decl() != to_app(m_regs[pc.m_other_reg])->get_decl()) { break; } for (unsigned i = 0; i < app2->get_num_args(); ++i) { m_regs[pc.m_offset + i] = app2->get_arg(i); } if (pc.m_kind == SET_VAR) { s.insert(app1->get_decl(), pc.m_reg); } ok = true; break; } case SET_BOUND: { a = m_regs[pc.m_reg]; if (a->get_kind() != AST_VAR) { break; } ok = true; var* var_a = to_var(a); var* var_p = to_var(pc.m_pat); // check that the mapping of bound variables remains a bijection. for (unsigned i = 0; ok && i < pc.m_num_bound; ++i) { ok = (a != m_bound_rng[i]); } if (!ok) { break; } m_bound_dom[pc.m_num_bound] = var_p; m_bound_rng[pc.m_num_bound] = var_a; break; } case CHECK_BOUND: TRACE("expr_pattern_match", tout << "check bound " << pc.m_num_bound << " " << pc.m_reg; ); ok = m_bound_rng[pc.m_num_bound] == m_regs[pc.m_reg]; break; case BIND: case BIND_AC: case BIND_C: { TRACE("expr_pattern_match", display(tout, pc); tout << mk_pp(m_regs[pc.m_reg],m_manager) << "\n";); app* app1 = to_app(pc.m_pat); a = m_regs[pc.m_reg]; if (a->get_kind() != AST_APP) { break; } app* app2 = to_app(a); if (app1->get_num_args() != app2->get_num_args()) { break; } if (!match_decl(app1->get_decl(), app2->get_decl())) { break; } switch(pc.m_kind) { case BIND: for (unsigned i = 0; i < app2->get_num_args(); ++i) { m_regs[pc.m_offset + i] = app2->get_arg(i); } ok = true; break; // process the next instruction. case BIND_AC: // push CHOOSE_AC on the backtracking stack. bstack.push_back(instr(CHOOSE_AC, pc.m_offset, pc.m_next, app2, 1)); break; case BIND_C: // push CHOOSE_C on the backtracking stack. ok = true; m_regs[pc.m_offset] = app2->get_arg(0); m_regs[pc.m_offset+1] = app2->get_arg(1); bstack.push_back(instr(CHOOSE_C, pc.m_offset, pc.m_next, app2, 2)); break; default: break; } break; } case CHOOSE_C: ok = true; SASSERT (pc.m_count == 2); m_regs[pc.m_offset+1] = pc.m_app->get_arg(0); m_regs[pc.m_offset] = pc.m_app->get_arg(1); break; case CHOOSE_AC: { ok = true; app* app2 = pc.m_app; for (unsigned i = 0; i < app2->get_num_args(); ++i) { m_regs[pc.m_offset + i] = app2->get_arg(i); } // generate the k'th permutation. unsigned k = pc.m_count; unsigned fac = 1; unsigned num_args = pc.m_app->get_num_args(); for (unsigned j = 2; j <= num_args; ++j) { fac *= (j-1); SASSERT(((k /fac) % j) + 1 <= j); std::swap(m_regs[pc.m_offset + j - 1], m_regs[pc.m_offset + j - ((k / fac) % j) - 1]); } if (k < fac*num_args) { bstack.push_back(instr(CHOOSE_AC, pc.m_offset, pc.m_next, app2, k+1)); } break; } case BACKTRACK: if (bstack.empty()) { return false; } pc = bstack.back(); bstack.pop_back(); continue; // with the loop. } if (ok) { pc = m_instrs[pc.m_next]; } else { TRACE("expr_pattern_match", tout << "backtrack\n";); pc = m_instrs[0]; } } } bool expr_pattern_match::match_decl(func_decl const * pat, func_decl const * d) const { if (pat == d) { return true; } if (pat->get_arity() != d->get_arity()) { return false; } // match families if (pat->get_family_id() == null_family_id) { return false; } if (d->get_family_id() != pat->get_family_id()) { return false; } if (d->get_decl_kind() != pat->get_decl_kind()) { return false; } if (d->get_num_parameters() != pat->get_num_parameters()) { return false; } for (unsigned i = 0; i < d->get_num_parameters(); ++i) { if (!(d->get_parameter(i) == pat->get_parameter(i))) { return false; } } return true; } bool expr_pattern_match::is_var(func_decl* d) { const char* s = d->get_name().bare_str(); return s && *s == '?'; } void expr_pattern_match::initialize(char const * spec_string) { if (!m_instrs.empty()) { return; } m_instrs.push_back(instr(BACKTRACK)); std::istringstream is(spec_string); cmd_context ctx(true, &m_manager); VERIFY(parse_smt2_commands(ctx, is)); ptr_vector::const_iterator it = ctx.begin_assertions(); ptr_vector::const_iterator end = ctx.end_assertions(); for (; it != end; ++it) { compile(*it); } TRACE("expr_pattern_match", display(tout); ); } void expr_pattern_match::display(std::ostream& out) const { for (unsigned i = 0; i < m_instrs.size(); ++i) { display(out, m_instrs[i]); } } void expr_pattern_match::display(std::ostream& out, instr const& pc) const { switch(pc.m_kind) { case BACKTRACK: out << "backtrack\n"; break; case BIND: out << "bind "; out << mk_pp(to_app(pc.m_pat)->get_decl(), m_manager) << " "; out << mk_pp(pc.m_pat, m_manager) << "\n"; out << "next: " << pc.m_next << "\n"; out << "offset: " << pc.m_offset << "\n"; out << "reg: " << pc.m_reg << "\n"; break; case BIND_AC: out << "bind_ac "; out << mk_pp(to_app(pc.m_pat)->get_decl(), m_manager) << " "; out << mk_pp(pc.m_pat, m_manager) << "\n"; out << "next: " << pc.m_next << "\n"; out << "offset: " << pc.m_offset << "\n"; out << "reg: " << pc.m_reg << "\n"; break; case BIND_C: out << "bind_c "; out << mk_pp(to_app(pc.m_pat)->get_decl(), m_manager) << " "; out << mk_pp(pc.m_pat, m_manager) << "\n"; out << "next: " << pc.m_next << "\n"; out << "offset: " << pc.m_offset << "\n"; out << "reg: " << pc.m_reg << "\n"; break; case CHOOSE_AC: out << "choose_ac\n"; out << "next: " << pc.m_next << "\n"; out << "count: " << pc.m_count << "\n"; break; case CHOOSE_C: out << "choose_c\n"; out << "next: " << pc.m_next << "\n"; //out << "reg: " << pc.m_reg << "\n"; break; case CHECK_VAR: out << "check_var "; out << mk_pp(pc.m_pat, m_manager) << "\n"; out << "next: " << pc.m_next << "\n"; out << "reg: " << pc.m_reg << "\n"; out << "other_reg: " << pc.m_other_reg << "\n"; break; case CHECK_TERM: out << "check "; out << mk_pp(pc.m_pat, m_manager) << "\n"; out << "next: " << pc.m_next << "\n"; out << "reg: " << pc.m_reg << "\n"; break; case YIELD: out << "yield\n"; break; case SET_VAR: out << "set_var "; out << mk_pp(pc.m_pat, m_manager) << "\n"; out << "next: " << pc.m_next << "\n"; break; default: break; } } // TBD: fix type overloading. // TBD: bound number of permutations. // TBD: forward pruning checks. z3-4.4.0/src/ast/static_features.cpp0000644000175000017500000006066712540347414017133 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: static_features.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-05-16. Revision History: --*/ #include"static_features.h" #include"ast_pp.h" static_features::static_features(ast_manager & m): m_manager(m), m_autil(m), m_bvutil(m), m_arrayutil(m), m_fpautil(m), m_bfid(m.get_basic_family_id()), m_afid(m.mk_family_id("arith")), m_lfid(m.mk_family_id("label")), m_arrfid(m.mk_family_id("array")), m_label_sym("label"), m_pattern_sym("pattern"), m_expr_list_sym("expr-list") { reset(); } void static_features::reset() { m_already_visited .reset(); m_cnf = true; m_num_exprs = 0; m_num_roots = 0; m_max_depth = 0; m_num_quantifiers = 0; m_num_quantifiers_with_patterns = 0; m_num_quantifiers_with_multi_patterns = 0; m_num_clauses = 0; m_num_bin_clauses = 0; m_num_units = 0; m_sum_clause_size = 0; m_num_nested_formulas = 0; m_num_bool_exprs = 0; m_num_bool_constants = 0; m_num_formula_trees = 0; m_max_formula_depth = 0; m_sum_formula_depth = 0; m_num_or_and_trees = 0; m_max_or_and_tree_depth = 0; m_sum_or_and_tree_depth = 0; m_num_ite_trees = 0; m_max_ite_tree_depth = 0; m_sum_ite_tree_depth = 0; m_num_ors = 0; m_num_ands = 0; m_num_iffs = 0; m_num_ite_formulas = 0; m_num_ite_terms = 0; m_num_sharing = 0; m_num_interpreted_exprs = 0; m_num_uninterpreted_exprs = 0; m_num_interpreted_constants = 0; m_num_uninterpreted_constants = 0; m_num_uninterpreted_functions = 0; m_num_eqs = 0; m_has_rational = false; m_has_int = false; m_has_real = false; m_has_bv = false; m_has_fpa = false; m_has_arrays = false; m_arith_k_sum .reset(); m_num_arith_terms = 0; m_num_arith_eqs = 0; m_num_arith_ineqs = 0; m_num_diff_terms = 0; m_num_diff_eqs = 0; m_num_diff_ineqs = 0; m_num_simple_eqs = 0; m_num_simple_ineqs = 0; m_num_non_linear = 0; m_num_apps .reset(); m_num_theory_terms .reset(); m_num_theory_atoms .reset(); m_num_theory_constants .reset(); m_num_theory_eqs .reset(); m_num_aliens = 0; m_num_aliens_per_family .reset(); m_num_theories = 0; m_theories .reset(); m_max_stack_depth = 500; flush_cache(); } void static_features::flush_cache() { m_expr2depth.reset(); m_expr2or_and_depth.reset(); m_expr2ite_depth.reset(); m_expr2formula_depth.reset(); } #if 0 bool static_features::is_non_linear(expr * e) const { if (!is_arith_expr(e)) return false; if (is_numeral(e)) return true; if (m_autil.is_add(e)) return true; // the non } #endif bool static_features::is_diff_term(expr const * e, rational & r) const { // lhs can be 'x' or '(+ k x)' if (!is_arith_expr(e)) { r.reset(); return true; } if (is_numeral(e, r)) return true; return m_autil.is_add(e) && to_app(e)->get_num_args() == 2 && is_numeral(to_app(e)->get_arg(0), r) && !is_arith_expr(to_app(e)->get_arg(1)); } bool static_features::is_diff_atom(expr const * e) const { if (!is_bool(e)) return false; if (!m_manager.is_eq(e) && !is_arith_expr(e)) return false; SASSERT(to_app(e)->get_num_args() == 2); expr * lhs = to_app(e)->get_arg(0); expr * rhs = to_app(e)->get_arg(1); if (!is_arith_expr(lhs) && !is_arith_expr(rhs)) return true; if (!is_numeral(rhs)) return false; // lhs can be 'x' or '(+ x (* -1 y))' if (!is_arith_expr(lhs)) return true; expr* arg1, *arg2; if (!m_autil.is_add(lhs, arg1, arg2)) return false; // x if (is_arith_expr(arg1)) return false; // arg2: (* -1 y) expr* m1, *m2; return m_autil.is_mul(arg2, m1, m2) && is_minus_one(m1) && !is_arith_expr(m2); } bool static_features::is_gate(expr const * e) const { if (is_basic_expr(e)) { switch (to_app(e)->get_decl_kind()) { case OP_ITE: case OP_AND: case OP_OR: case OP_IFF: case OP_XOR: case OP_IMPLIES: return true; } } return false; } void static_features::update_core(expr * e) { m_num_exprs++; // even if a benchmark does not contain any theory interpreted function decls, we still have to install // the theory if the benchmark contains constants or function applications of an interpreted sort. sort * s = m_manager.get_sort(e); if (!m_manager.is_uninterp(s)) mark_theory(s->get_family_id()); bool _is_gate = is_gate(e); bool _is_eq = m_manager.is_eq(e); if (_is_gate) { m_cnf = false; m_num_nested_formulas++; switch (to_app(e)->get_decl_kind()) { case OP_ITE: if (is_bool(e)) m_num_ite_formulas++; else { m_num_ite_terms++; // process then&else nodes for (unsigned i = 1; i < 3; i++) { expr * arg = to_app(e)->get_arg(i); acc_num(arg); // Must check whether arg is diff logic or not. // Otherwise, problem can be incorrectly tagged as diff logic. sort * arg_s = m_manager.get_sort(arg); family_id fid_arg = arg_s->get_family_id(); if (fid_arg == m_afid) { m_num_arith_terms++; rational k; TRACE("diff_term", tout << "diff_term: " << is_diff_term(arg, k) << "\n" << mk_pp(arg, m_manager) << "\n";); if (is_diff_term(arg, k)) { m_num_diff_terms++; acc_num(k); } } } } break; case OP_AND: m_num_ands++; break; case OP_OR: m_num_ors++; break; case OP_IFF: m_num_iffs++; break; } } if (is_bool(e)) { m_num_bool_exprs++; if (is_app(e) && to_app(e)->get_num_args() == 0) m_num_bool_constants++; } if (is_quantifier(e)) { m_num_quantifiers++; unsigned num_patterns = to_quantifier(e)->get_num_patterns(); if (num_patterns > 0) { m_num_quantifiers_with_patterns++; for (unsigned i = 0; i < num_patterns; i++) { expr * p = to_quantifier(e)->get_pattern(i); if (is_app(p) && to_app(p)->get_num_args() > 1) { m_num_quantifiers_with_multi_patterns++; break; } } } } bool _is_le_ge = m_autil.is_le(e) || m_autil.is_ge(e); if (_is_le_ge) { m_num_arith_ineqs++; TRACE("diff_atom", tout << "diff_atom: " << is_diff_atom(e) << "\n" << mk_pp(e, m_manager) << "\n";); if (is_diff_atom(e)) m_num_diff_ineqs++; if (!is_arith_expr(to_app(e)->get_arg(0))) m_num_simple_ineqs++; acc_num(to_app(e)->get_arg(1)); } rational r; if (is_numeral(e, r)) { if (!r.is_int()) m_has_rational = true; } if (_is_eq) { m_num_eqs++; if (is_numeral(to_app(e)->get_arg(1))) { acc_num(to_app(e)->get_arg(1)); m_num_arith_eqs++; TRACE("diff_atom", tout << "diff_atom: " << is_diff_atom(e) << "\n" << mk_pp(e, m_manager) << "\n";); if (is_diff_atom(e)) m_num_diff_eqs++; if (!is_arith_expr(to_app(e)->get_arg(0))) m_num_simple_eqs++; } sort * s = m_manager.get_sort(to_app(e)->get_arg(0)); if (!m_manager.is_uninterp(s)) { family_id fid = s->get_family_id(); if (fid != null_family_id && fid != m_bfid) inc_theory_eqs(fid); } } if (!m_has_int && m_autil.is_int(e)) m_has_int = true; if (!m_has_real && m_autil.is_real(e)) m_has_real = true; if (!m_has_bv && m_bvutil.is_bv(e)) m_has_bv = true; if (!m_has_fpa && (m_fpautil.is_float(e) || m_fpautil.is_rm(e))) m_has_fpa = true; if (!m_has_arrays && m_arrayutil.is_array(e)) m_has_arrays = true; if (is_app(e)) { family_id fid = to_app(e)->get_family_id(); mark_theory(fid); if (fid != null_family_id && fid != m_bfid) { m_num_interpreted_exprs++; if (is_bool(e)) inc_theory_atoms(fid); else inc_theory_terms(fid); if (to_app(e)->get_num_args() == 0) m_num_interpreted_constants++; } if (fid == m_afid) { switch (to_app(e)->get_decl_kind()) { case OP_MUL: if (!is_numeral(to_app(e)->get_arg(0))) m_num_non_linear++; break; case OP_DIV: case OP_IDIV: case OP_REM: case OP_MOD: if (!is_numeral(to_app(e)->get_arg(1))) m_num_non_linear++; break; } } if (fid == null_family_id) { m_num_uninterpreted_exprs++; if (to_app(e)->get_num_args() == 0) { m_num_uninterpreted_constants++; sort * s = m_manager.get_sort(e); if (!m_manager.is_uninterp(s)) { family_id fid = s->get_family_id(); if (fid != null_family_id && fid != m_bfid) inc_theory_constants(fid); } } } if (m_arrayutil.is_array(e)) { TRACE("sf_array", tout << mk_ismt2_pp(e, m_manager) << "\n";); sort * ty = to_app(e)->get_decl()->get_range(); mark_theory(ty->get_family_id()); unsigned n = ty->get_num_parameters(); for (unsigned i = 0; i < n; i++) { sort * ds = to_sort(ty->get_parameter(i).get_ast()); update_core(ds); } } func_decl * d = to_app(e)->get_decl(); inc_num_apps(d); if (d->get_arity() > 0 && !is_marked(d)) { mark(d); if (fid == null_family_id) m_num_uninterpreted_functions++; } if (!_is_eq && !_is_gate) { unsigned num_args = to_app(e)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(e)->get_arg(i); sort * arg_s = m_manager.get_sort(arg); if (!m_manager.is_uninterp(arg_s)) { family_id fid_arg = arg_s->get_family_id(); if (fid_arg != fid && fid_arg != null_family_id) { m_num_aliens++; inc_num_aliens(fid_arg); if (fid_arg == m_afid) { SASSERT(!_is_le_ge); m_num_arith_terms++; rational k; TRACE("diff_term", tout << "diff_term: " << is_diff_term(arg, k) << "\n" << mk_pp(arg, m_manager) << "\n";); if (is_diff_term(arg, k)) { m_num_diff_terms++; acc_num(k); } } } } } } } } void static_features::update_core(sort * s) { mark_theory(s->get_family_id()); if (!m_has_int && m_autil.is_int(s)) m_has_int = true; if (!m_has_real && m_autil.is_real(s)) m_has_real = true; if (!m_has_bv && m_bvutil.is_bv_sort(s)) m_has_bv = true; if (!m_has_fpa && (m_fpautil.is_float(s) || m_fpautil.is_rm(s))) m_has_fpa = true; if (!m_has_arrays && m_arrayutil.is_array(s)) m_has_arrays = true; } void static_features::process(expr * e, bool form_ctx, bool or_and_ctx, bool ite_ctx, unsigned stack_depth) { TRACE("static_features", tout << "processing\n" << mk_pp(e, m_manager) << "\n";); if (is_var(e)) return; if (is_marked(e)) { m_num_sharing++; return; } if (stack_depth > m_max_stack_depth) { return; } mark(e); update_core(e); if (is_quantifier(e)) { expr * body = to_quantifier(e)->get_expr(); process(body, false, false, false, stack_depth+1); set_depth(e, get_depth(body)+1); return; } bool form_ctx_new = false; bool or_and_ctx_new = false; bool ite_ctx_new = false; if (is_basic_expr(e)) { switch (to_app(e)->get_decl_kind()) { case OP_ITE: form_ctx_new = m_manager.is_bool(e); ite_ctx_new = true; break; case OP_AND: case OP_OR: form_ctx_new = true; or_and_ctx_new = true; break; case OP_IFF: form_ctx_new = true; break; } } unsigned depth = 0; unsigned form_depth = 0; unsigned or_and_depth = 0; unsigned ite_depth = 0; unsigned num_args = to_app(e)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(e)->get_arg(i); if (m_manager.is_not(arg)) arg = to_app(arg)->get_arg(0); process(arg, form_ctx_new, or_and_ctx_new, ite_ctx_new, stack_depth+1); depth = std::max(depth, get_depth(arg)); if (form_ctx_new) form_depth = std::max(form_depth, get_form_depth(arg)); if (or_and_ctx_new) or_and_depth = std::max(or_and_depth, get_or_and_depth(arg)); if (ite_ctx_new) ite_depth = std::max(ite_depth, get_ite_depth(arg)); } depth++; set_depth(e, depth); if (depth > m_max_depth) m_max_depth = depth; if (form_ctx_new) { form_depth++; if (!form_ctx) { m_num_formula_trees++; m_sum_formula_depth += form_depth; if (form_depth > m_max_formula_depth) m_max_formula_depth = form_depth; } set_form_depth(e, form_depth); } if (or_and_ctx_new) { or_and_depth++; if (!or_and_ctx) { m_num_or_and_trees++; m_sum_or_and_tree_depth += or_and_depth; if (or_and_depth > m_max_or_and_tree_depth) m_max_or_and_tree_depth = or_and_depth; } set_or_and_depth(e, or_and_depth); } if (ite_ctx_new) { ite_depth++; if (!ite_ctx) { m_num_ite_trees++; m_sum_ite_tree_depth += ite_depth; if (ite_depth >= m_max_ite_tree_depth) m_max_ite_tree_depth = ite_depth; } set_ite_depth(e, ite_depth); } } void static_features::process_root(expr * e) { if (is_marked(e)) { m_num_sharing++; return; } m_num_roots++; if (m_manager.is_or(e)) { mark(e); m_num_clauses++; m_num_bool_exprs++; unsigned num_args = to_app(e)->get_num_args(); m_sum_clause_size += num_args; if (num_args == 2) m_num_bin_clauses++; unsigned depth = 0; unsigned form_depth = 0; unsigned or_and_depth = 0; for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(e)->get_arg(i); if (m_manager.is_not(arg)) arg = to_app(arg)->get_arg(0); process(arg, true, true, false, 0); depth = std::max(depth, get_depth(arg)); form_depth = std::max(form_depth, get_form_depth(arg)); or_and_depth = std::max(or_and_depth, get_or_and_depth(arg)); } depth++; set_depth(e, depth); if (depth > m_max_depth) m_max_depth = depth; form_depth++; m_num_formula_trees++; m_sum_formula_depth += form_depth; if (form_depth > m_max_formula_depth) m_max_formula_depth = form_depth; set_form_depth(e, form_depth); or_and_depth++; m_num_or_and_trees++; m_sum_or_and_tree_depth += or_and_depth; if (or_and_depth > m_max_or_and_tree_depth) m_max_or_and_tree_depth = or_and_depth; set_or_and_depth(e, or_and_depth); return; } if (!is_gate(e)) { m_sum_clause_size++; m_num_units++; m_num_clauses++; } process(e, false, false, false, 0); } void static_features::collect(unsigned num_formulas, expr * const * formulas) { for (unsigned i = 0; i < num_formulas; i++) process_root(formulas[i]); } bool static_features::internal_family(symbol const & f_name) const { return f_name == m_label_sym || f_name == m_pattern_sym || f_name == m_expr_list_sym; } void static_features::display_family_data(std::ostream & out, char const * prefix, unsigned_vector const & data) const { for (unsigned fid = 0; fid < data.size(); fid++) { symbol const & n = m_manager.get_family_name(fid); if (!internal_family(n)) out << prefix << "_" << n << " " << data[fid] << "\n"; } } bool static_features::has_uf() const { return m_num_uninterpreted_functions > 0; } unsigned static_features::num_non_uf_theories() const { return m_num_theories; } unsigned static_features::num_theories() const { return (num_non_uf_theories() + (has_uf() ? 1 : 0)); } void static_features::display_primitive(std::ostream & out) const { out << "BEGIN_PRIMITIVE_STATIC_FEATURES" << "\n"; out << "CNF " << m_cnf << "\n"; out << "NUM_EXPRS " << m_num_exprs << "\n"; out << "NUM_ROOTS " << m_num_roots << "\n"; out << "MAX_DEPTH " << m_max_depth << "\n"; out << "NUM_QUANTIFIERS " << m_num_quantifiers << "\n"; out << "NUM_QUANTIFIERS_WITH_PATTERNS " << m_num_quantifiers_with_patterns << "\n"; out << "NUM_QUANTIFIERS_WITH_MULTI_PATTERNS " << m_num_quantifiers_with_multi_patterns << "\n"; out << "NUM_CLAUSES " << m_num_clauses << "\n"; out << "NUM_BIN_CLAUSES " << m_num_bin_clauses << "\n"; out << "NUM_UNITS " << m_num_units << "\n"; out << "SUM_CLAUSE_SIZE " << m_sum_clause_size << "\n"; out << "NUM_NESTED_FORMULAS " << m_num_nested_formulas << "\n"; out << "NUM_BOOL_EXPRS " << m_num_bool_exprs << "\n"; out << "NUM_BOOL_CONSTANTS " << m_num_bool_constants << "\n"; out << "NUM_FORMULA_TREES " << m_num_formula_trees << "\n"; out << "MAX_FORMULA_DEPTH " << m_max_formula_depth << "\n"; out << "SUM_FORMULA_DEPTH " << m_sum_formula_depth << "\n"; out << "NUM_OR_AND_TREES " << m_num_or_and_trees << "\n"; out << "MAX_OR_AND_TREE_DEPTH " << m_max_or_and_tree_depth << "\n"; out << "SUM_OR_AND_TREE_DEPTH " << m_sum_or_and_tree_depth << "\n"; out << "NUM_ITE_TREES " << m_num_ite_trees << "\n"; out << "MAX_ITE_TREE_DEPTH " << m_max_ite_tree_depth << "\n"; out << "SUM_ITE_TREE_DEPTH " << m_sum_ite_tree_depth << "\n"; out << "NUM_ORS " << m_num_ors << "\n"; out << "NUM_ANDS " << m_num_ands << "\n"; out << "NUM_IFFS " << m_num_iffs << "\n"; out << "NUM_ITE_FORMULAS " << m_num_ite_formulas << "\n"; out << "NUM_ITE_TERMS " << m_num_ite_terms << "\n"; out << "NUM_SHARING " << m_num_sharing << "\n"; out << "NUM_INTERPRETED_EXPRS " << m_num_interpreted_exprs << "\n"; out << "NUM_UNINTERPRETED_EXPRS " << m_num_uninterpreted_exprs << "\n"; out << "NUM_INTERPRETED_CONSTANTS " << m_num_interpreted_constants << "\n"; out << "NUM_UNINTERPRETED_CONSTANTS " << m_num_uninterpreted_constants << "\n"; out << "NUM_UNINTERPRETED_FUNCTIONS " << m_num_uninterpreted_functions << "\n"; out << "NUM_EQS " << m_num_eqs << "\n"; out << "HAS_RATIONAL " << m_has_rational << "\n"; out << "HAS_INT " << m_has_int << "\n"; out << "HAS_REAL " << m_has_real << "\n"; out << "ARITH_K_SUM " << m_arith_k_sum << "\n"; out << "NUM_ARITH_TERMS " << m_num_arith_terms << "\n"; out << "NUM_ARITH_EQS " << m_num_arith_eqs << "\n"; out << "NUM_ARITH_INEQS " << m_num_arith_ineqs << "\n"; out << "NUM_DIFF_TERMS " << m_num_diff_terms << "\n"; out << "NUM_DIFF_EQS " << m_num_diff_eqs << "\n"; out << "NUM_DIFF_INEQS " << m_num_diff_ineqs << "\n"; out << "NUM_SIMPLE_EQS " << m_num_simple_eqs << "\n"; out << "NUM_SIMPLE_INEQS " << m_num_simple_ineqs << "\n"; out << "NUM_NON_LINEAR " << m_num_non_linear << "\n"; out << "NUM_ALIENS " << m_num_aliens << "\n"; display_family_data(out, "NUM_TERMS", m_num_theory_terms); display_family_data(out, "NUM_ATOMS", m_num_theory_atoms); display_family_data(out, "NUM_CONSTANTS", m_num_theory_constants); display_family_data(out, "NUM_EQS", m_num_theory_eqs); display_family_data(out, "NUM_ALIENS", m_num_aliens_per_family); out << "NUM_THEORIES " << num_theories() << "\n"; out << "END_PRIMITIVE_STATIC_FEATURES" << "\n"; } void static_features::display(std::ostream & out) const { out << "BEGIN_STATIC_FEATURES" << "\n"; out << "CNF " << m_cnf << "\n"; out << "MAX_DEPTH " << m_max_depth << "\n"; out << "MAX_OR_AND_TREE_DEPTH " << m_max_or_and_tree_depth << "\n"; out << "MAX_ITE_TREE_DEPTH " << m_max_ite_tree_depth << "\n"; out << "HAS_INT " << m_has_int << "\n"; out << "HAS_REAL " << m_has_real << "\n"; out << "HAS_QUANTIFIERS " << (m_num_quantifiers > 0) << "\n"; out << "PERC_QUANTIFIERS_WITH_PATTERNS " << (m_num_quantifiers > 0 ? (double) m_num_quantifiers_with_patterns / (double) m_num_quantifiers : 0) << "\n"; out << "PERC_QUANTIFIERS_WITH_MULTI_PATTERNS " << (m_num_quantifiers > 0 ? (double) m_num_quantifiers_with_multi_patterns / (double) m_num_quantifiers : 0) << "\n"; out << "IS_NON_LINEAR " << (m_num_non_linear > 0) << "\n"; out << "THEORY_COMBINATION " << (num_theories() > 1) << "\n"; out << "AVG_CLAUSE_SIZE " << (m_num_clauses > 0 ? (double) m_sum_clause_size / (double) m_num_clauses : 0) << "\n"; out << "PERC_BOOL_CONSTANTS " << (m_num_uninterpreted_constants > 0 ? (double) m_num_bool_constants / (double) m_num_uninterpreted_constants : 0) << "\n"; out << "PERC_NESTED_FORMULAS " << (m_num_bool_exprs > 0 ? (double) m_num_nested_formulas / (double) m_num_bool_exprs : 0) << "\n"; out << "IS_DIFF " << (m_num_arith_eqs == m_num_diff_eqs && m_num_arith_ineqs == m_num_diff_ineqs && m_num_arith_terms == m_num_diff_terms) << "\n"; out << "INEQ_EQ_RATIO " << (m_num_arith_eqs > 0 ? (double) m_num_arith_ineqs / (double) m_num_arith_eqs : 0) << "\n"; out << "PERC_ARITH_EQS " << (m_num_eqs > 0 ? (double) m_num_arith_eqs / (double) m_num_eqs : 0) << "\n"; out << "PERC_DIFF_EQS " << (m_num_arith_eqs > 0 ? (double) m_num_diff_eqs / (double) m_num_arith_eqs : 0) << "\n"; out << "PERC_DIFF_INEQS " << (m_num_arith_ineqs > 0 ? (double) m_num_diff_ineqs / (double) m_num_arith_ineqs : 0) << "\n"; out << "PERC_SIMPLE_EQS " << (m_num_arith_eqs > 0 ? (double) m_num_simple_eqs / (double) m_num_arith_eqs : 0) << "\n"; out << "PERC_SIMPLE_INEQS " << (m_num_arith_ineqs > 0 ? (double) m_num_simple_ineqs / (double) m_num_arith_ineqs : 0) << "\n"; out << "PERC_ALIENS " << (m_num_exprs > 0 ? (double) m_num_aliens / (double) m_num_exprs : 0) << "\n"; out << "END_STATIC_FEATURES" << "\n"; } void static_features::get_feature_vector(vector & result) { } z3-4.4.0/src/ast/used_vars.h0000644000175000017500000000234212540347414015370 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: used_vars.h Abstract: Functor used to collect the set of used variables. Author: Leonardo de Moura (leonardo) 2008-01-14. Revision History: --*/ #ifndef _USED_VARS_H_ #define _USED_VARS_H_ #include"ast.h" #include"expr_delta_pair.h" class used_vars { ptr_vector m_found_vars; typedef hashtable, default_eq > cache; cache m_cache; svector m_todo; void process(expr * n, unsigned delta); public: void operator()(expr * n) { m_found_vars.reset(); process(n, 0); } void reset() { m_found_vars.reset(); } void process(expr * n) { process(n, 0); } unsigned get_max_found_var_idx_plus_1() const { return m_found_vars.size(); } sort * get(unsigned var_idx) const { return m_found_vars[var_idx]; } sort * contains(unsigned var_idx) const { return var_idx < m_found_vars.size() ? m_found_vars[var_idx] : 0; } bool uses_all_vars(unsigned num_decls) const; bool uses_a_var(unsigned num_decls) const; unsigned get_num_vars() const; }; #endif /* _USED_VARS_H_ */ z3-4.4.0/src/ast/macro_substitution.cpp0000644000175000017500000001236412540347414017672 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: macro_substitution.cpp Abstract: Mapping from func_decl to quantifiers of the form Forall X. f(X) = T[X] Forall X. f(X) iff C[X] Author: Leonardo (leonardo) 2012-02-17 Notes: --*/ #include"macro_substitution.h" #include"ref_util.h" typedef obj_map func_decl2proof; typedef obj_map func_decl2expr_dependency; void macro_substitution::init() { if (proofs_enabled()) m_decl2macro_pr = alloc(func_decl2proof); if (unsat_core_enabled()) m_decl2macro_dep = alloc(func_decl2expr_dependency); } macro_substitution::macro_substitution(ast_manager & m): m_manager(m), m_cores_enabled(false), m_proofs_enabled(m.proofs_enabled()) { init(); } macro_substitution::macro_substitution(ast_manager & m, bool cores_enabled): m_manager(m), m_cores_enabled(cores_enabled), m_proofs_enabled(m.proofs_enabled()) { init(); } macro_substitution::macro_substitution(ast_manager & m, bool cores_enabled, bool proofs_enabled): m_manager(m), m_cores_enabled(cores_enabled), m_proofs_enabled(proofs_enabled) { SASSERT(!proofs_enabled || m.proofs_enabled()); init(); } macro_substitution::~macro_substitution() { reset(); } void macro_substitution::reset() { dec_ref_map_key_values(m_manager, m_decl2macro); if (proofs_enabled()) dec_ref_map_values(m_manager, *m_decl2macro_pr); if (unsat_core_enabled()) dec_ref_map_values(m_manager, *m_decl2macro_dep); } void macro_substitution::cleanup() { reset(); m_decl2macro.finalize(); if (proofs_enabled()) m_decl2macro_pr->finalize(); if (unsat_core_enabled()) m_decl2macro_dep->finalize(); } void macro_substitution::insert(func_decl * f, quantifier * q, proof * pr, expr_dependency * dep) { DEBUG_CODE({ app * body = to_app(q->get_expr()); SASSERT(m_manager.is_eq(body) || m_manager.is_iff(body)); expr * lhs = body->get_arg(0); expr * rhs = body->get_arg(1); SASSERT(is_app_of(lhs, f) || is_app_of(rhs, f)); }); obj_map::obj_map_entry * entry = m_decl2macro.insert_if_not_there2(f, 0); if (entry->get_data().m_value == 0) { // new entry m_manager.inc_ref(f); m_manager.inc_ref(q); entry->get_data().m_value = q; if (proofs_enabled()) { SASSERT(!m_decl2macro_pr->contains(f)); m_decl2macro_pr->insert(f, pr); m_manager.inc_ref(pr); } if (unsat_core_enabled()) { SASSERT(!m_decl2macro_dep->contains(f)); m_decl2macro_dep->insert(f, dep); m_manager.inc_ref(dep); } } else { // replacing entry m_manager.inc_ref(q); m_manager.dec_ref(entry->get_data().m_value); entry->get_data().m_value = q; if (proofs_enabled()) { obj_map::obj_map_entry * entry_pr = m_decl2macro_pr->find_core(f); SASSERT(entry_pr != 0); m_manager.inc_ref(pr); m_manager.dec_ref(entry_pr->get_data().m_value); entry_pr->get_data().m_value = pr; } if (unsat_core_enabled()) { obj_map::obj_map_entry * entry_dep = m_decl2macro_dep->find_core(f); SASSERT(entry_dep != 0); m_manager.inc_ref(dep); m_manager.dec_ref(entry_dep->get_data().m_value); entry_dep->get_data().m_value = dep; } } } void macro_substitution::erase(func_decl * f) { if (proofs_enabled()) { proof * pr = 0; if (m_decl2macro_pr->find(f, pr)) { m_manager.dec_ref(pr); m_decl2macro_pr->erase(f); } } if (unsat_core_enabled()) { expr_dependency * dep = 0; if (m_decl2macro_dep->find(f, dep)) { m_manager.dec_ref(dep); m_decl2macro_dep->erase(f); } } quantifier * q = 0; if (m_decl2macro.find(f, q)) { m_manager.dec_ref(f); m_manager.dec_ref(q); m_decl2macro.erase(f); } } void macro_substitution::get_head_def(quantifier * q, func_decl * f, app * & head, expr * & def) { app * body = to_app(q->get_expr()); SASSERT(m_manager.is_eq(body) || m_manager.is_iff(body)); expr * lhs = to_app(body)->get_arg(0); expr * rhs = to_app(body)->get_arg(1); SASSERT(is_app_of(lhs, f) || is_app_of(rhs, f)); SASSERT(!is_app_of(lhs, f) || !is_app_of(rhs, f)); if (is_app_of(lhs, f)) { head = to_app(lhs); def = rhs; } else { head = to_app(rhs); def = lhs; } } bool macro_substitution::find(func_decl * f, quantifier * & q, proof * & pr) { if (m_decl2macro.find(f, q)) { if (proofs_enabled()) m_decl2macro_pr->find(f, pr); return true; } return false; } bool macro_substitution::find(func_decl * f, quantifier * & q, proof * & pr, expr_dependency * & dep) { if (m_decl2macro.find(f, q)) { if (proofs_enabled()) m_decl2macro_pr->find(f, pr); if (unsat_core_enabled()) m_decl2macro_dep->find(f, dep); return true; } return false; } z3-4.4.0/src/ast/expr_substitution.h0000644000175000017500000000267412540347414017217 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: expr_substitution.h Abstract: expr -> expr substitution Author: Leonardo (leonardo) 2011-04-29 Notes: --*/ #ifndef _EXPR_SUBSTITUTION_H_ #define _EXPR_SUBSTITUTION_H_ #include"ast.h" class expr_substitution { ast_manager & m_manager; obj_map m_subst; scoped_ptr > m_subst_pr; scoped_ptr > m_subst_dep; unsigned m_cores_enabled:1; unsigned m_proofs_enabled:1; void init(); public: expr_substitution(ast_manager & m); expr_substitution(ast_manager & m, bool cores_enabled); expr_substitution(ast_manager & m, bool cores_enabled, bool proofs_enabled); ~expr_substitution(); ast_manager & m() const { return m_manager; } bool proofs_enabled() const { return m_proofs_enabled; } bool unsat_core_enabled() const { return m_cores_enabled; } bool empty() const { return m_subst.empty(); } void insert(expr * s, expr * def, proof * def_pr = 0, expr_dependency * def_dep = 0); void erase(expr * s); bool find(expr * s, expr * & def, proof * & def_pr); bool find(expr * s, expr * & def, proof * & def_pr, expr_dependency * & def_dep); bool contains(expr * s); void reset(); void cleanup(); }; #endif z3-4.4.0/src/ast/reg_decl_plugins.cpp0000644000175000017500000000313612540347414017237 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: reg_decl_plugins Abstract: Goodie for installing all available declarations plugins in an ast_manager Author: Leonardo de Moura (leonardo) 2012-10-24. Revision History: --*/ #include"ast.h" #include"arith_decl_plugin.h" #include"array_decl_plugin.h" #include"bv_decl_plugin.h" #include"datatype_decl_plugin.h" #include"dl_decl_plugin.h" #include"seq_decl_plugin.h" #include"pb_decl_plugin.h" #include"fpa_decl_plugin.h" void reg_decl_plugins(ast_manager & m) { if (!m.get_plugin(m.mk_family_id(symbol("arith")))) { m.register_plugin(symbol("arith"), alloc(arith_decl_plugin)); } if (!m.get_plugin(m.mk_family_id(symbol("bv")))) { m.register_plugin(symbol("bv"), alloc(bv_decl_plugin)); } if (!m.get_plugin(m.mk_family_id(symbol("array")))) { m.register_plugin(symbol("array"), alloc(array_decl_plugin)); } if (!m.get_plugin(m.mk_family_id(symbol("datatype")))) { m.register_plugin(symbol("datatype"), alloc(datatype_decl_plugin)); } if (!m.get_plugin(m.mk_family_id(symbol("datalog_relation")))) { m.register_plugin(symbol("datalog_relation"), alloc(datalog::dl_decl_plugin)); } if (!m.get_plugin(m.mk_family_id(symbol("seq")))) { m.register_plugin(symbol("seq"), alloc(seq_decl_plugin)); } if (!m.get_plugin(m.mk_family_id(symbol("fpa")))) { m.register_plugin(symbol("fpa"), alloc(fpa_decl_plugin)); } if (!m.get_plugin(m.mk_family_id(symbol("pb")))) { m.register_plugin(symbol("pb"), alloc(pb_decl_plugin)); } } z3-4.4.0/src/ast/macro_substitution.h0000644000175000017500000000334412540347414017335 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: macro_substitution.h Abstract: Mapping from func_decl to quantifiers of the form Forall X. f(X) = T[X] Forall X. f(X) iff C[X] Author: Leonardo (leonardo) 2012-02-17 Notes: --*/ #ifndef _MACRO_SUBSTITUTION_H_ #define _MACRO_SUBSTITUTION_H_ #include"ast.h" class macro_substitution { ast_manager & m_manager; obj_map m_decl2macro; scoped_ptr > m_decl2macro_pr; scoped_ptr > m_decl2macro_dep; unsigned m_cores_enabled:1; unsigned m_proofs_enabled:1; void init(); public: macro_substitution(ast_manager & m); macro_substitution(ast_manager & m, bool cores_enabled); macro_substitution(ast_manager & m, bool cores_enabled, bool proofs_enabled); ~macro_substitution(); ast_manager & m() const { return m_manager; } bool proofs_enabled() const { return m_proofs_enabled; } bool unsat_core_enabled() const { return m_cores_enabled; } bool empty() const { return m_decl2macro.empty(); } void insert(func_decl * f, quantifier * m, proof * pr, expr_dependency * dep = 0); void erase(func_decl * f); bool contains(func_decl * f) { return m_decl2macro.contains(f); } bool find(func_decl * f, quantifier * & q, proof * & pr); bool find(func_decl * f, quantifier * & q, proof * & pr, expr_dependency * & dep); void get_head_def(quantifier * q, func_decl * f, app * & head, expr * & def); void reset(); void cleanup(); }; #endif z3-4.4.0/src/ast/fpa_decl_plugin.h0000644000175000017500000003727012540347414016520 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa_decl_plugin.h Abstract: Floating point decl plugin Author: Leonardo de Moura (leonardo) 2012-01-15. Revision History: --*/ #ifndef _fpa_decl_plugin_H_ #define _fpa_decl_plugin_H_ #include"ast.h" #include"id_gen.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"mpf.h" enum fpa_sort_kind { FLOATING_POINT_SORT, ROUNDING_MODE_SORT, FLOAT16_SORT, FLOAT32_SORT, FLOAT64_SORT, FLOAT128_SORT }; enum fpa_op_kind { OP_FPA_RM_NEAREST_TIES_TO_EVEN, OP_FPA_RM_NEAREST_TIES_TO_AWAY, OP_FPA_RM_TOWARD_POSITIVE, OP_FPA_RM_TOWARD_NEGATIVE, OP_FPA_RM_TOWARD_ZERO, OP_FPA_NUM, OP_FPA_PLUS_INF, OP_FPA_MINUS_INF, OP_FPA_NAN, OP_FPA_PLUS_ZERO, OP_FPA_MINUS_ZERO, OP_FPA_ADD, OP_FPA_SUB, OP_FPA_NEG, OP_FPA_MUL, OP_FPA_DIV, OP_FPA_REM, OP_FPA_ABS, OP_FPA_MIN, OP_FPA_MAX, OP_FPA_FMA, // x*y + z OP_FPA_SQRT, OP_FPA_ROUND_TO_INTEGRAL, OP_FPA_EQ, OP_FPA_LT, OP_FPA_GT, OP_FPA_LE, OP_FPA_GE, OP_FPA_IS_NAN, OP_FPA_IS_INF, OP_FPA_IS_ZERO, OP_FPA_IS_NORMAL, OP_FPA_IS_SUBNORMAL, OP_FPA_IS_NEGATIVE, OP_FPA_IS_POSITIVE, OP_FPA_FP, OP_FPA_TO_FP, OP_FPA_TO_FP_UNSIGNED, OP_FPA_TO_UBV, OP_FPA_TO_SBV, OP_FPA_TO_REAL, /* Extensions */ OP_FPA_TO_IEEE_BV, /* Internal use only */ OP_FPA_INTERNAL_BVWRAP, OP_FPA_INTERNAL_BVUNWRAP, OP_FPA_INTERNAL_TO_UBV_UNSPECIFIED, OP_FPA_INTERNAL_TO_SBV_UNSPECIFIED, OP_FPA_INTERNAL_TO_REAL_UNSPECIFIED, LAST_FLOAT_OP }; class fpa_decl_plugin : public decl_plugin { struct mpf_hash_proc { scoped_mpf_vector const & m_values; mpf_hash_proc(scoped_mpf_vector const & values):m_values(values) {} unsigned operator()(unsigned id) const { return m_values.m().hash(m_values[id]); } }; struct mpf_eq_proc { scoped_mpf_vector const & m_values; mpf_eq_proc(scoped_mpf_vector const & values):m_values(values) {} bool operator()(unsigned id1, unsigned id2) const { return m_values.m().eq_core(m_values[id1], m_values[id2]); } }; typedef chashtable value_table; mpf_manager m_fm; id_gen m_id_gen; scoped_mpf_vector m_values; value_table m_value_table; sort * m_real_sort; sort * m_int_sort; family_id m_arith_fid; family_id m_bv_fid; bv_decl_plugin * m_bv_plugin; sort * mk_float_sort(unsigned ebits, unsigned sbits); sort * mk_rm_sort(); func_decl * mk_rm_const_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_float_const_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_bin_rel_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_unary_rel_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_unary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_binary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_rm_binary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_rm_unary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_fma(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_fp(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_to_fp(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_to_fp_unsigned(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_to_ubv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_to_sbv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_to_real(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_float_to_ieee_bv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_internal_bv_wrap(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_internal_bv_unwrap(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_internal_to_ubv_unspecified(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_internal_to_sbv_unspecified(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_internal_to_real_unspecified(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); virtual void set_manager(ast_manager * m, family_id id); unsigned mk_id(mpf const & v); void recycled_id(unsigned id); public: fpa_decl_plugin(); bool is_float_sort(sort * s) const { return is_sort_of(s, m_family_id, FLOATING_POINT_SORT); } bool is_rm_sort(sort * s) const { return is_sort_of(s, m_family_id, ROUNDING_MODE_SORT); } virtual ~fpa_decl_plugin(); virtual void finalize(); virtual decl_plugin * mk_fresh(); virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); virtual void get_op_names(svector & op_names, symbol const & logic); virtual void get_sort_names(svector & sort_names, symbol const & logic); virtual expr * get_some_value(sort * s); virtual bool is_value(app* e) const; virtual bool is_unique_value(app* e) const; mpf_manager & fm() { return m_fm; } func_decl * mk_numeral_decl(mpf const & v); app * mk_numeral(mpf const & v); bool is_numeral(expr * n); bool is_numeral(expr * n, mpf & val); bool is_rm_numeral(expr * n, mpf_rounding_mode & val); bool is_rm_numeral(expr * n); mpf const & get_value(unsigned id) const { SASSERT(m_value_table.contains(id)); return m_values[id]; } virtual void del(parameter const & p); virtual parameter translate(parameter const & p, decl_plugin & target); }; class fpa_util { ast_manager & m_manager; fpa_decl_plugin * m_plugin; family_id m_fid; arith_util m_a_util; bv_util m_bv_util; public: fpa_util(ast_manager & m); ~fpa_util(); ast_manager & m() const { return m_manager; } mpf_manager & fm() const { return m_plugin->fm(); } family_id get_fid() const { return m_fid; } family_id get_family_id() const { return m_fid; } arith_util & au() { return m_a_util; } fpa_decl_plugin & plugin() { return *m_plugin; } sort * mk_float_sort(unsigned ebits, unsigned sbits); sort * mk_rm_sort() { return m().mk_sort(m_fid, ROUNDING_MODE_SORT); } bool is_float(sort * s) { return is_sort_of(s, m_fid, FLOATING_POINT_SORT); } bool is_rm(sort * s) { return is_sort_of(s, m_fid, ROUNDING_MODE_SORT); } bool is_float(expr * e) { return is_float(m_manager.get_sort(e)); } bool is_rm(expr * e) { return is_rm(m_manager.get_sort(e)); } unsigned get_ebits(sort * s); unsigned get_sbits(sort * s); app * mk_round_nearest_ties_to_even() { return m().mk_const(m_fid, OP_FPA_RM_NEAREST_TIES_TO_EVEN); } app * mk_round_nearest_ties_to_away() { return m().mk_const(m_fid, OP_FPA_RM_NEAREST_TIES_TO_AWAY); } app * mk_round_toward_positive() { return m().mk_const(m_fid, OP_FPA_RM_TOWARD_POSITIVE); } app * mk_round_toward_negative() { return m().mk_const(m_fid, OP_FPA_RM_TOWARD_NEGATIVE); } app * mk_round_toward_zero() { return m().mk_const(m_fid, OP_FPA_RM_TOWARD_ZERO); } app * mk_nan(unsigned ebits, unsigned sbits); app * mk_pinf(unsigned ebits, unsigned sbits); app * mk_ninf(unsigned ebits, unsigned sbits); app * mk_nan(sort * s) { return mk_nan(get_ebits(s), get_sbits(s)); } app * mk_pinf(sort * s) { return mk_pinf(get_ebits(s), get_sbits(s)); } app * mk_ninf(sort * s) { return mk_ninf(get_ebits(s), get_sbits(s)); } app * mk_value(mpf const & v) { return m_plugin->mk_numeral(v); } bool is_numeral(expr * n) { return m_plugin->is_numeral(n); } bool is_numeral(expr * n, mpf & v) { return m_plugin->is_numeral(n, v); } bool is_rm_numeral(expr * n) { return m_plugin->is_rm_numeral(n); } bool is_rm_numeral(expr * n, mpf_rounding_mode & v) { return m_plugin->is_rm_numeral(n, v); } app * mk_pzero(unsigned ebits, unsigned sbits); app * mk_nzero(unsigned ebits, unsigned sbits); app * mk_pzero(sort * s) { return mk_pzero(get_ebits(s), get_sbits(s)); } app * mk_nzero(sort * s) { return mk_nzero(get_ebits(s), get_sbits(s)); } bool is_nan(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_nan(v); } bool is_pinf(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_pinf(v); } bool is_ninf(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_ninf(v); } bool is_zero(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_zero(v); } bool is_pzero(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_pzero(v); } bool is_nzero(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_nzero(v); } app * mk_fp(expr * arg1, expr * arg2, expr * arg3) { return m().mk_app(m_fid, OP_FPA_FP, arg1, arg2, arg3); } app * mk_to_fp(sort * s, expr * bv_t) { SASSERT(is_float(s) && s->get_num_parameters() == 2); return m().mk_app(m_fid, OP_FPA_TO_FP, 2, s->get_parameters(), 1, &bv_t); } app * mk_to_fp(sort * s, expr * rm, expr * t) { SASSERT(is_float(s) && s->get_num_parameters() == 2); expr * args[] = { rm, t }; return m().mk_app(m_fid, OP_FPA_TO_FP, 2, s->get_parameters(), 2, args); } app * mk_to_fp(sort * s, expr * rm, expr * sig, expr * exp) { SASSERT(is_float(s) && s->get_num_parameters() == 2); expr * args[] = { rm, sig, exp }; return m().mk_app(m_fid, OP_FPA_TO_FP, 2, s->get_parameters(), 3, args); } app * mk_to_fp_unsigned(sort * s, expr * rm, expr * t) { SASSERT(is_float(s) && s->get_num_parameters() == 2); expr * args[] = { rm, t }; return m().mk_app(m_fid, OP_FPA_TO_FP_UNSIGNED, 2, s->get_parameters(), 2, args); } bool is_to_fp(expr * n) { return is_app_of(n, m_fid, OP_FPA_TO_FP); } app * mk_to_ubv(expr * rm, expr * t, unsigned sz) { parameter ps[] = { parameter(sz) }; expr * args[] = { rm, t }; return m().mk_app(m_fid, OP_FPA_TO_UBV, 1, ps, 2, args); } app * mk_to_sbv(expr * rm, expr * t, unsigned sz) { parameter ps[] = { parameter(sz) }; expr * args[] = { rm, t }; return m().mk_app(m_fid, OP_FPA_TO_SBV, 1, ps, 2, args); } app * mk_to_real(expr * t) { return m().mk_app(m_fid, OP_FPA_TO_REAL, t); } app * mk_add(expr * arg1, expr * arg2, expr * arg3) { return m().mk_app(m_fid, OP_FPA_ADD, arg1, arg2, arg3); } app * mk_mul(expr * arg1, expr * arg2, expr * arg3) { return m().mk_app(m_fid, OP_FPA_MUL, arg1, arg2, arg3); } app * mk_sub(expr * arg1, expr * arg2, expr * arg3) { return m().mk_app(m_fid, OP_FPA_SUB, arg1, arg2, arg3); } app * mk_div(expr * arg1, expr * arg2, expr * arg3) { return m().mk_app(m_fid, OP_FPA_DIV, arg1, arg2, arg3); } app * mk_neg(expr * arg1) { return m().mk_app(m_fid, OP_FPA_NEG, arg1); } app * mk_rem(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_REM, arg1, arg2); } app * mk_max(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_MAX, arg1, arg2); } app * mk_min(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_MIN, arg1, arg2); } app * mk_abs(expr * arg1) { return m().mk_app(m_fid, OP_FPA_ABS, arg1); } app * mk_sqrt(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_SQRT, arg1, arg2); } app * mk_round_to_integral(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_ROUND_TO_INTEGRAL, arg1, arg2); } app * mk_fma(expr * arg1, expr * arg2, expr * arg3, expr * arg4) { expr * args[4] = { arg1, arg2, arg3, arg4 }; return m().mk_app(m_fid, OP_FPA_FMA, 4, args); } app * mk_float_eq(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_EQ, arg1, arg2); } app * mk_lt(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_LT, arg1, arg2); } app * mk_gt(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_GT, arg1, arg2); } app * mk_le(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_LE, arg1, arg2); } app * mk_ge(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_GE, arg1, arg2); } app * mk_is_nan(expr * arg1) { return m().mk_app(m_fid, OP_FPA_IS_NAN, arg1); } app * mk_is_inf(expr * arg1) { return m().mk_app(m_fid, OP_FPA_IS_INF, arg1); } app * mk_is_zero(expr * arg1) { return m().mk_app(m_fid, OP_FPA_IS_ZERO, arg1); } app * mk_is_normal(expr * arg1) { return m().mk_app(m_fid, OP_FPA_IS_NORMAL, arg1); } app * mk_is_subnormal(expr * arg1) { return m().mk_app(m_fid, OP_FPA_IS_SUBNORMAL, arg1); } app * mk_is_positive(expr * arg1) { return m().mk_app(m_fid, OP_FPA_IS_POSITIVE, arg1); } app * mk_is_negative(expr * arg1) { return m().mk_app(m_fid, OP_FPA_IS_NEGATIVE, arg1); } bool is_neg(expr * a) { return is_app_of(a, m_fid, OP_FPA_NEG); } app * mk_float_to_ieee_bv(expr * arg1) { return m().mk_app(m_fid, OP_FPA_TO_IEEE_BV, arg1); } app * mk_internal_to_ubv_unspecified(unsigned width); app * mk_internal_to_sbv_unspecified(unsigned width); app * mk_internal_to_real_unspecified(); bool is_wrap(expr * e) const { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_BVWRAP); } bool is_unwrap(expr * e) const { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_BVUNWRAP); } }; #endif z3-4.4.0/src/ast/well_sorted.h0000644000175000017500000000054012540347414015716 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: well_sorted.h Abstract: Author: Leonardo de Moura (leonardo) 2007-12-29. Revision History: --*/ #ifndef _WELL_SORTED_H_ #define _WELL_SORTED_H_ class ast_manager; class expr; bool is_well_sorted(ast_manager const & m, expr * n); #endif /* _WELL_SORTED_H_ */ z3-4.4.0/src/ast/arith_decl_plugin.h0000644000175000017500000003552012540347414017055 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: arith_decl_plugin.h Abstract: Author: Leonardo de Moura (leonardo) 2008-01-09 Revision History: --*/ #ifndef _ARITH_DECL_PLUGIN_H_ #define _ARITH_DECL_PLUGIN_H_ #include"ast.h" class sexpr; namespace algebraic_numbers { class anum; class manager; }; enum arith_sort_kind { REAL_SORT, INT_SORT }; enum arith_op_kind { OP_NUM, // rational & integers OP_IRRATIONAL_ALGEBRAIC_NUM, // irrationals that are roots of polynomials with integer coefficients // OP_LE, OP_GE, OP_LT, OP_GT, OP_ADD, OP_SUB, OP_UMINUS, OP_MUL, OP_DIV, OP_IDIV, OP_REM, OP_MOD, OP_TO_REAL, OP_TO_INT, OP_IS_INT, OP_ABS, OP_POWER, // hyperbolic and trigonometric functions OP_SIN, OP_COS, OP_TAN, OP_ASIN, OP_ACOS, OP_ATAN, OP_SINH, OP_COSH, OP_TANH, OP_ASINH, OP_ACOSH, OP_ATANH, // constants OP_PI, OP_E, // under-specified symbols OP_0_PW_0_INT, // 0^0 for integers OP_0_PW_0_REAL, // 0^0 for reals OP_NEG_ROOT, // x^n when n is even and x is negative OP_DIV_0, // x/0 OP_IDIV_0, // x div 0 OP_MOD_0, // x mod 0 OP_U_ASIN, // asin(x) for x < -1 or x > 1 OP_U_ACOS, // acos(x) for x < -1 or x > 1 LAST_ARITH_OP }; class arith_util; class arith_decl_plugin : public decl_plugin { protected: struct algebraic_numbers_wrapper; algebraic_numbers_wrapper * m_aw; symbol m_intv_sym; symbol m_realv_sym; symbol m_rootv_sym; sort * m_real_decl; sort * m_int_decl; func_decl * m_r_le_decl; func_decl * m_r_ge_decl; func_decl * m_r_lt_decl; func_decl * m_r_gt_decl; func_decl * m_r_add_decl; func_decl * m_r_sub_decl; func_decl * m_r_uminus_decl; func_decl * m_r_mul_decl; func_decl * m_r_div_decl; func_decl * m_i_le_decl; func_decl * m_i_ge_decl; func_decl * m_i_lt_decl; func_decl * m_i_gt_decl; func_decl * m_i_add_decl; func_decl * m_i_sub_decl; func_decl * m_i_uminus_decl; func_decl * m_i_mul_decl; func_decl * m_i_div_decl; func_decl * m_i_mod_decl; func_decl * m_i_rem_decl; func_decl * m_to_real_decl; func_decl * m_to_int_decl; func_decl * m_is_int_decl; func_decl * m_r_power_decl; func_decl * m_i_power_decl; func_decl * m_r_abs_decl; func_decl * m_i_abs_decl; func_decl * m_sin_decl; func_decl * m_cos_decl; func_decl * m_tan_decl; func_decl * m_asin_decl; func_decl * m_acos_decl; func_decl * m_atan_decl; func_decl * m_sinh_decl; func_decl * m_cosh_decl; func_decl * m_tanh_decl; func_decl * m_asinh_decl; func_decl * m_acosh_decl; func_decl * m_atanh_decl; app * m_pi; app * m_e; app * m_0_pw_0_int; app * m_0_pw_0_real; func_decl * m_neg_root_decl; func_decl * m_div_0_decl; func_decl * m_idiv_0_decl; func_decl * m_mod_0_decl; func_decl * m_u_asin_decl; func_decl * m_u_acos_decl; ptr_vector m_small_ints; ptr_vector m_small_reals; func_decl * mk_func_decl(decl_kind k, bool is_real); virtual void set_manager(ast_manager * m, family_id id); decl_kind fix_kind(decl_kind k, unsigned arity); func_decl * mk_num_decl(unsigned num_parameters, parameter const * parameters, unsigned arity); public: arith_decl_plugin(); virtual ~arith_decl_plugin(); virtual void finalize(); algebraic_numbers::manager & am() const; algebraic_numbers_wrapper & aw() const; virtual void del(parameter const & p); virtual parameter translate(parameter const & p, decl_plugin & target); virtual decl_plugin * mk_fresh() { return alloc(arith_decl_plugin); } virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range); virtual bool is_value(app * e) const; virtual bool is_unique_value(app * e) const; virtual bool are_equal(app * a, app * b) const; virtual bool are_distinct(app * a, app * b) const; virtual void get_op_names(svector & op_names, symbol const & logic); virtual void get_sort_names(svector & sort_names, symbol const & logic); app * mk_numeral(rational const & n, bool is_int); app * mk_numeral(algebraic_numbers::anum const & val, bool is_int); // Create a (real) numeral that is the i-th root of the polynomial encoded using the given sexpr. app * mk_numeral(sexpr const * p, unsigned i); app * mk_pi() const { return m_pi; } app * mk_e() const { return m_e; } app * mk_0_pw_0_int() const { return m_0_pw_0_int; } app * mk_0_pw_0_real() const { return m_0_pw_0_real; } virtual expr * get_some_value(sort * s); virtual void set_cancel(bool f); }; /** \brief Procedures for recognizing arithmetic expressions. We don't need access to ast_manager, and operations can be simultaneously executed in different threads. */ class arith_recognizers { protected: family_id m_afid; public: arith_recognizers(family_id id):m_afid(id) {} family_id get_family_id() const { return m_afid; } bool is_arith_expr(expr const * n) const { return is_app(n) && to_app(n)->get_family_id() == m_afid; } bool is_irrational_algebraic_numeral(expr const * n) const { return is_app_of(n, m_afid, OP_IRRATIONAL_ALGEBRAIC_NUM); } bool is_numeral(expr const * n, rational & val, bool & is_int) const; bool is_numeral(expr const * n, rational & val) const { bool is_int; return is_numeral(n, val, is_int); } bool is_numeral(expr const * n) const { return is_app_of(n, m_afid, OP_NUM); } bool is_zero(expr const * n) const { rational val; return is_numeral(n, val) && val.is_zero(); } bool is_minus_one(expr * n) const { rational tmp; return is_numeral(n, tmp) && tmp.is_minus_one(); } // return true if \c n is a term of the form (* -1 r) bool is_times_minus_one(expr * n, expr * & r) const { if (is_mul(n) && to_app(n)->get_num_args() == 2 && is_minus_one(to_app(n)->get_arg(0))) { r = to_app(n)->get_arg(1); return true; } return false; } bool is_le(expr const * n) const { return is_app_of(n, m_afid, OP_LE); } bool is_ge(expr const * n) const { return is_app_of(n, m_afid, OP_GE); } bool is_lt(expr const * n) const { return is_app_of(n, m_afid, OP_LT); } bool is_gt(expr const * n) const { return is_app_of(n, m_afid, OP_GT); } bool is_add(expr const * n) const { return is_app_of(n, m_afid, OP_ADD); } bool is_sub(expr const * n) const { return is_app_of(n, m_afid, OP_SUB); } bool is_uminus(expr const * n) const { return is_app_of(n, m_afid, OP_UMINUS); } bool is_mul(expr const * n) const { return is_app_of(n, m_afid, OP_MUL); } bool is_div(expr const * n) const { return is_app_of(n, m_afid, OP_DIV); } bool is_idiv(expr const * n) const { return is_app_of(n, m_afid, OP_IDIV); } bool is_mod(expr const * n) const { return is_app_of(n, m_afid, OP_MOD); } bool is_rem(expr const * n) const { return is_app_of(n, m_afid, OP_REM); } bool is_to_real(expr const * n) const { return is_app_of(n, m_afid, OP_TO_REAL); } bool is_to_int(expr const * n) const { return is_app_of(n, m_afid, OP_TO_INT); } bool is_is_int(expr const * n) const { return is_app_of(n, m_afid, OP_IS_INT); } bool is_power(expr const * n) const { return is_app_of(n, m_afid, OP_POWER); } bool is_int(sort const * s) const { return is_sort_of(s, m_afid, INT_SORT); } bool is_int(expr const * n) const { return is_int(get_sort(n)); } bool is_real(sort const * s) const { return is_sort_of(s, m_afid, REAL_SORT); } bool is_real(expr const * n) const { return is_real(get_sort(n)); } bool is_int_real(sort const * s) const { return s->get_family_id() == m_afid; } bool is_int_real(expr const * n) const { return is_int_real(get_sort(n)); } MATCH_UNARY(is_uminus); MATCH_UNARY(is_to_real); MATCH_UNARY(is_to_int); MATCH_BINARY(is_sub); MATCH_BINARY(is_add); MATCH_BINARY(is_mul); MATCH_BINARY(is_le); MATCH_BINARY(is_ge); MATCH_BINARY(is_lt); MATCH_BINARY(is_gt); MATCH_BINARY(is_mod); MATCH_BINARY(is_rem); MATCH_BINARY(is_div); MATCH_BINARY(is_idiv); bool is_pi(expr * arg) { return is_app_of(arg, m_afid, OP_PI); } bool is_e(expr * arg) { return is_app_of(arg, m_afid, OP_E); } }; class arith_util : public arith_recognizers { ast_manager & m_manager; arith_decl_plugin * m_plugin; void init_plugin(); arith_decl_plugin & plugin() const { if (!m_plugin) const_cast(this)->init_plugin(); SASSERT(m_plugin != 0); return *m_plugin; } public: arith_util(ast_manager & m); ast_manager & get_manager() const { return m_manager; } algebraic_numbers::manager & am() { return plugin().am(); } bool is_irrational_algebraic_numeral(expr const * n) const { return is_app_of(n, m_afid, OP_IRRATIONAL_ALGEBRAIC_NUM); } bool is_irrational_algebraic_numeral(expr const * n, algebraic_numbers::anum & val); algebraic_numbers::anum const & to_irrational_algebraic_numeral(expr const * n); sort * mk_int() { return m_manager.mk_sort(m_afid, INT_SORT); } sort * mk_real() { return m_manager.mk_sort(m_afid, REAL_SORT); } app * mk_numeral(rational const & val, bool is_int) const { return plugin().mk_numeral(val, is_int); } app * mk_numeral(rational const & val, sort const * s) const { SASSERT(is_int(s) || is_real(s)); return mk_numeral(val, is_int(s)); } app * mk_numeral(algebraic_numbers::anum const & val, bool is_int) { return plugin().mk_numeral(val, is_int); } app * mk_numeral(sexpr const * p, unsigned i) { return plugin().mk_numeral(p, i); } app * mk_le(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_LE, arg1, arg2); } app * mk_ge(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_GE, arg1, arg2); } app * mk_lt(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_LT, arg1, arg2); } app * mk_gt(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_GT, arg1, arg2); } app * mk_add(unsigned num_args, expr * const * args) { return m_manager.mk_app(m_afid, OP_ADD, num_args, args); } app * mk_add(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_ADD, arg1, arg2); } app * mk_add(expr * arg1, expr * arg2, expr* arg3) { return m_manager.mk_app(m_afid, OP_ADD, arg1, arg2, arg3); } app * mk_sub(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_SUB, arg1, arg2); } app * mk_sub(unsigned num_args, expr * const * args) { return m_manager.mk_app(m_afid, OP_SUB, num_args, args); } app * mk_mul(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_MUL, arg1, arg2); } app * mk_mul(expr * arg1, expr * arg2, expr* arg3) { return m_manager.mk_app(m_afid, OP_MUL, arg1, arg2, arg3); } app * mk_mul(unsigned num_args, expr * const * args) { return m_manager.mk_app(m_afid, OP_MUL, num_args, args); } app * mk_uminus(expr * arg) { return m_manager.mk_app(m_afid, OP_UMINUS, arg); } app * mk_div(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_DIV, arg1, arg2); } app * mk_idiv(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_IDIV, arg1, arg2); } app * mk_rem(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_REM, arg1, arg2); } app * mk_mod(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_MOD, arg1, arg2); } app * mk_to_real(expr * arg1) { return m_manager.mk_app(m_afid, OP_TO_REAL, arg1); } app * mk_to_int(expr * arg1) { return m_manager.mk_app(m_afid, OP_TO_INT, arg1); } app * mk_is_int(expr * arg1) { return m_manager.mk_app(m_afid, OP_IS_INT, arg1); } app * mk_power(expr* arg1, expr* arg2) { return m_manager.mk_app(m_afid, OP_POWER, arg1, arg2); } app * mk_sin(expr * arg) { return m_manager.mk_app(m_afid, OP_SIN, arg); } app * mk_cos(expr * arg) { return m_manager.mk_app(m_afid, OP_COS, arg); } app * mk_tan(expr * arg) { return m_manager.mk_app(m_afid, OP_TAN, arg); } app * mk_asin(expr * arg) { return m_manager.mk_app(m_afid, OP_ASIN, arg); } app * mk_acos(expr * arg) { return m_manager.mk_app(m_afid, OP_ACOS, arg); } app * mk_atan(expr * arg) { return m_manager.mk_app(m_afid, OP_ATAN, arg); } app * mk_sinh(expr * arg) { return m_manager.mk_app(m_afid, OP_SINH, arg); } app * mk_cosh(expr * arg) { return m_manager.mk_app(m_afid, OP_COSH, arg); } app * mk_tanh(expr * arg) { return m_manager.mk_app(m_afid, OP_TANH, arg); } app * mk_asinh(expr * arg) { return m_manager.mk_app(m_afid, OP_ASINH, arg); } app * mk_acosh(expr * arg) { return m_manager.mk_app(m_afid, OP_ACOSH, arg); } app * mk_atanh(expr * arg) { return m_manager.mk_app(m_afid, OP_ATANH, arg); } app * mk_pi() { return plugin().mk_pi(); } app * mk_e() { return plugin().mk_e(); } app * mk_0_pw_0_int() { return plugin().mk_0_pw_0_int(); } app * mk_0_pw_0_real() { return plugin().mk_0_pw_0_real(); } app * mk_div0(expr * arg) { return m_manager.mk_app(m_afid, OP_DIV_0, arg); } app * mk_idiv0(expr * arg) { return m_manager.mk_app(m_afid, OP_IDIV_0, arg); } app * mk_mod0(expr * arg) { return m_manager.mk_app(m_afid, OP_MOD_0, arg); } app * mk_neg_root(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_NEG_ROOT, arg1, arg2); } app * mk_u_asin(expr * arg) { return m_manager.mk_app(m_afid, OP_U_ASIN, arg); } app * mk_u_acos(expr * arg) { return m_manager.mk_app(m_afid, OP_U_ACOS, arg); } /** \brief Return the equality (= lhs rhs), but it makes sure that if one of the arguments is a numeral, then it will be in the right-hand-side; if none of them are numerals, then the left-hand-side has a smaller id than the right hand side. */ app * mk_eq(expr * lhs, expr * rhs) { if (is_numeral(lhs) || (!is_numeral(rhs) && lhs->get_id() > rhs->get_id())) std::swap(lhs, rhs); if (lhs == rhs) return m_manager.mk_true(); if (is_numeral(lhs) && is_numeral(rhs)) { SASSERT(lhs != rhs); return m_manager.mk_false(); } return m_manager.mk_eq(lhs, rhs); } void set_cancel(bool f) { plugin().set_cancel(f); } }; #endif /* _ARITH_DECL_PLUGIN_H_ */ z3-4.4.0/src/ast/expr2var.cpp0000644000175000017500000000330312540347414015477 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: expr2var.h Abstract: The mapping between Z3 expressions and (low level) variables. Example of low level variables: - SAT solver - Polynomial - etc. Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #include"expr2var.h" #include"ast_smt2_pp.h" #include"ref_util.h" void expr2var::insert(expr * n, var v) { if (!is_uninterp_const(n)) { TRACE("expr2var", tout << "interpreted:\n" << mk_ismt2_pp(n, m()) << "\n";); m_interpreted_vars = true; } m().inc_ref(n); m_mapping.insert(n, v); m_recent_exprs.push_back(n); } expr2var::expr2var(ast_manager & m): m_manager(m), m_interpreted_vars(false) { } expr2var::~expr2var() { dec_ref_map_keys(m(), m_mapping); } expr2var::var expr2var::to_var(expr * n) const { var v = UINT_MAX; m_mapping.find(n, v); return v; } void expr2var::display(std::ostream & out) const { obj_map::iterator it = m_mapping.begin(); obj_map::iterator end = m_mapping.end(); for (; it != end; ++it) { out << mk_ismt2_pp(it->m_key, m()) << " -> " << it->m_value << "\n"; } } void expr2var::mk_inv(expr_ref_vector & var2expr) const { obj_map::iterator it = m_mapping.begin(); obj_map::iterator end = m_mapping.end(); for (; it != end; ++it) { expr * t = it->m_key; var x = it->m_value; if (x >= var2expr.size()) var2expr.resize(x+1, 0); var2expr.set(x, t); } } void expr2var::reset() { dec_ref_map_keys(m(), m_mapping); SASSERT(m_mapping.empty()); m_recent_exprs.reset(); m_interpreted_vars = false; } z3-4.4.0/src/ast/num_occurs.cpp0000644000175000017500000000420712540347414016107 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: num_occurs.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-27. Revision History: --*/ #include"num_occurs.h" void num_occurs::process(expr * t, expr_fast_mark1 & visited) { ptr_buffer stack; #define VISIT(ARG) { \ if (!m_ignore_ref_count1 || ARG->get_ref_count() > 1) { \ obj_map::obj_map_entry * entry = m_num_occurs.insert_if_not_there2(ARG, 0); \ entry->get_data().m_value++; \ } \ if (!visited.is_marked(ARG)) { \ visited.mark(ARG, true); \ stack.push_back(ARG); \ } \ } VISIT(t); while (!stack.empty()) { expr * t = stack.back(); stack.pop_back(); unsigned j; switch (t->get_kind()) { case AST_APP: j = to_app(t)->get_num_args(); while (j > 0) { --j; expr * arg = to_app(t)->get_arg(j); VISIT(arg); } break; case AST_QUANTIFIER: if (!m_ignore_quantifiers) { expr * child = to_quantifier(t)->get_expr(); VISIT(child); } break; default: break; } } } void num_occurs::operator()(expr * t) { expr_fast_mark1 visited; process(t, visited); } void num_occurs::operator()(unsigned num, expr * const * ts) { expr_fast_mark1 visited; for (unsigned i = 0; i < num; i++) { process(ts[i], visited); } } z3-4.4.0/src/ast/for_each_ast.h0000644000175000017500000002147712540347414016024 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: for_each_ast.h Abstract: Visitor for AST nodes Author: Leonardo de Moura (leonardo) 2006-10-18. Revision History: --*/ #ifndef _FOR_EACH_AST_H_ #define _FOR_EACH_AST_H_ #include"ast.h" #include"trace.h" #include"map.h" template bool for_each_ast_args(ptr_vector & stack, ast_mark & visited, unsigned num_args, T * const * args) { bool result = true; for (unsigned i = 0; i < num_args; i++) { T * arg = args[i]; if (!visited.is_marked(arg)) { stack.push_back(arg); result = false; } } return result; } bool for_each_parameter(ptr_vector & stack, ast_mark & visited, unsigned num_args, parameter const * params); template void for_each_ast(ForEachProc & proc, ast_mark & visited, ast * n, bool visit_parameters = false) { ptr_vector stack; ast * curr; stack.push_back(n); while (!stack.empty()) { curr = stack.back(); TRACE("for_each_ast", tout << "visiting node: " << curr->get_id() << ", kind: " << get_ast_kind_name(curr->get_kind()) << ", stack size: " << stack.size() << "\n";); if (visited.is_marked(curr)) { stack.pop_back(); continue; } switch(curr->get_kind()) { case AST_SORT: if (visit_parameters && !for_each_parameter(stack, visited, to_sort(curr)->get_num_parameters(), to_sort(curr)->get_parameters())) { break; } proc(to_sort(curr)); visited.mark(curr, true); stack.pop_back(); break; case AST_VAR: { var* v = to_var(curr); proc(v); visited.mark(curr, true); stack.pop_back(); break; } case AST_FUNC_DECL: if (visit_parameters && !for_each_parameter(stack, visited, to_func_decl(curr)->get_num_parameters(), to_func_decl(curr)->get_parameters())) { break; } if (!for_each_ast_args(stack, visited, to_func_decl(curr)->get_arity(), to_func_decl(curr)->get_domain())) { break; } if (!visited.is_marked(to_func_decl(curr)->get_range())) { stack.push_back(to_func_decl(curr)->get_range()); break; } proc(to_func_decl(curr)); visited.mark(curr, true); stack.pop_back(); break; case AST_APP: if (!visited.is_marked(to_app(curr)->get_decl())) { stack.push_back(to_app(curr)->get_decl()); break; } if (for_each_ast_args(stack, visited, to_app(curr)->get_num_args(), to_app(curr)->get_args())) { proc(to_app(curr)); visited.mark(curr, true); stack.pop_back(); } break; case AST_QUANTIFIER: if (!for_each_ast_args(stack, visited, to_quantifier(curr)->get_num_patterns(), to_quantifier(curr)->get_patterns())) { break; } if (!for_each_ast_args(stack, visited, to_quantifier(curr)->get_num_no_patterns(), to_quantifier(curr)->get_no_patterns())) { break; } if (!visited.is_marked(to_quantifier(curr)->get_expr())) { stack.push_back(to_quantifier(curr)->get_expr()); break; } proc(to_quantifier(curr)); visited.mark(curr, true); stack.pop_back(); break; } } } template void for_each_ast(ForEachProc & proc, ast * n, bool visit_parameters = false) { ast_mark visited; for_each_ast(proc, visited, n, visit_parameters); } template struct for_each_ast_proc : public EscapeProc { void operator()(ast * n) { EscapeProc::operator()(n); } void operator()(sort * n) { operator()(static_cast(n)); } void operator()(func_decl * n) { operator()(static_cast(n)); } void operator()(var * n) { operator()(static_cast(n)); } void operator()(app * n) { operator()(static_cast(n)); } void operator()(quantifier * n) { operator()(static_cast(n)); } }; unsigned get_num_nodes(ast * n); template class recurse_ast { template class mem_map : public map, ptr_eq > {}; public: static T* recurse(Visitor & visit, ast * aArg) { unsigned arity; ast* a; ast * const * args; T* result; ptr_vector stack; mem_map memoize; ptr_vector results; stack.push_back(aArg); while (!stack.empty()) { a = stack.back(); results.reset(); if (memoize.find(a, result)) { stack.pop_back(); continue; } switch(a->get_kind()) { case AST_SORT: memoize.insert(a, visit.mk_sort(to_sort(a))); stack.pop_back(); break; case AST_FUNC_DECL: { arity = to_func_decl(a)->get_arity(); func_decl * func_decl_ast = to_func_decl(a); args = (ast * const *)(func_decl_ast->get_domain()); recurse_list(stack, arity, args, &memoize, results); if (!memoize.find(func_decl_ast->get_range(), result)) { stack.push_back(func_decl_ast->get_range()); } else if (results.size() == arity) { result = visit.mk_func_decl(func_decl_ast, result, results); memoize.insert(a, result); stack.pop_back(); } break; } case AST_APP: { app * app = to_app(a); arity = app->get_num_args(); args = (ast * const *)(app->get_args()); recurse_list(stack, arity, args, &memoize, results); if (arity == results.size()) { result = visit.mk_app(app, results); memoize.insert(a, result); stack.pop_back(); } break; } case AST_VAR: memoize.insert(a, visit.mk_var(to_var(a))); stack.pop_back(); break; case AST_QUANTIFIER: { quantifier * quantifier_ast = to_quantifier(a); ptr_vector decl_types; if (recurse_quantifier) { args = (ast * const *) quantifier_ast->get_decl_sorts(); arity = quantifier_ast->get_num_decls(); ast* body = quantifier_ast->get_expr(); recurse_list(stack, arity, args, &memoize, decl_types); if (!memoize.find(body, result)) { stack.push_back(body); } else if (decl_types.size() == arity) { result = visit.mk_quantifier(quantifier_ast, decl_types, result); memoize.insert(a, result); stack.pop_back(); } } else { result = visit.mk_quantifier(quantifier_ast, decl_types, result); memoize.insert(a, result); stack.pop_back(); } break; } default: UNREACHABLE(); break; } } if (!memoize.find(aArg, result)) { UNREACHABLE(); } return result; } private: template static void recurse_list(ptr_vector & stack, unsigned arity, AST * const * ast_list, mem_map * memoize, ptr_vector & results) { T * result; for (unsigned i = 0; i < arity; ++i) { if (memoize->find(ast_list[i], result)) { results.push_back(result); } else { stack.push_back(ast_list[i]); } } } }; #endif /* _FOR_EACH_AST_H_ */ z3-4.4.0/src/ast/ast_trail.h0000644000175000017500000000252112540347414015356 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ast_trail.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-02. Revision History: Extracted AST specific features from trail.h nbjorner 2014-9-28 --*/ #ifndef _AST_TRAIL_H_ #define _AST_TRAIL_H_ #include"ast.h" #include"trail.h" template class ast2ast_trailmap { ref_vector m_domain; ref_vector m_range; obj_map m_map; public: ast2ast_trailmap(ast_manager& m): m_domain(m), m_range(m), m_map() {} bool find(S* s, T*& t) { return m_map.find(s,t); } void insert(S* s, T* t) { SASSERT(!m_map.contains(s)); m_domain.push_back(s); m_range.push_back(t); m_map.insert(s,t); } void pop() { SASSERT(!m_domain.empty()); m_map.remove(m_domain.back()); m_domain.pop_back(); m_range.pop_back(); } }; template class ast2ast_trail : public trail { ast2ast_trailmap& m_map; public: ast2ast_trail(ast2ast_trailmap& m, S* s, T* t) : m_map(m) { m.insert(s,t); } virtual void undo(Ctx& ctx) { m_map.pop(); } }; #endif /* _AST_TRAIL_H_ */ z3-4.4.0/src/ast/normal_forms/0000755000175000017500000000000012540347414015721 5ustar michaelmichaelz3-4.4.0/src/ast/normal_forms/defined_names.h0000644000175000017500000000501212540347414020651 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: defined_names.h Abstract: In some transformations, we need to name expressions. These expressions are stored in a table. Author: Leonardo de Moura (leonardo) 2008-01-14. Revision History: --*/ #ifndef _DEFINED_NAMES_H_ #define _DEFINED_NAMES_H_ #include"ast.h" /** \brief Mapping from expressions to skolem functions that are used to name them. The mapping supports backtracking using the methods #push_scope and #pop_scope. */ class defined_names { struct impl; struct pos_impl; impl * m_impl; pos_impl * m_pos_impl; public: defined_names(ast_manager & m, char const * fresh_prefix = "z3name"); ~defined_names(); // ----------------------------------- // // High-level API // // ----------------------------------- /** \brief Create a name for expression \c e if it doesn't already exists. Return true if a new name was created, and false if a name already exists for \c e. The resultant new name is stored in n, and a [apply-def] proof that (= e n) is stored into pr. If true is returned, then the definition of the new name is stored into new_def, and a [def-intro] proof into new_def_pr. The proofs are not produced when proof generation is disabled. The definition of an expression e with name n is: - (and (or (not e) n) (or e (not n))) if e is an formula. - (and (or (not c) (= n t1)) (or c (= n t2))) if e is an if-then-else term of the form (ite c t1 t2) - (= n e) if e is a term. Remark: the definitions are closed with an universal quantifier if e contains free variables. */ bool mk_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr); /** \brief Create a name for a positive occurrence of the expression \c e. Return true if a new pos-name was created, and false if a pos-name already exists for \c e. If true is returned, then the definition of the new name is stored into new_def. It has the form: (or (not n) e) Remark: the definitions are closed with an universal quantifier if e contains free variables. */ bool mk_pos_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr); void push(); void pop(unsigned num_scopes); void reset(); unsigned get_num_names() const; func_decl * get_name_decl(unsigned i) const; }; #endif /* _DEFINED_NAMES_H_ */ z3-4.4.0/src/ast/normal_forms/pull_quant.h0000644000175000017500000000217412540347414020262 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: pull_quant.h Abstract: Pull nested quantifiers. Author: Leonardo (leonardo) 2008-01-20 Notes: --*/ #ifndef _PULL_QUANT_H_ #define _PULL_QUANT_H_ #include"ast.h" /** \brief Pull nested quantifiers in a formula. \warning It assumes the input formula is in NNF. \remark pull_quant(F) is a quantifier if F contains a quantifier. \remark If pull_quant(F) is a quantifier then its weight is Min{weight(Q') | Q' is a quantifier nested in F} */ class pull_quant { struct imp; imp * m_imp; public: pull_quant(ast_manager & m); ~pull_quant(); void operator()(expr * n, expr_ref & r, proof_ref & p); void reset(); void pull_quant2(expr * n, expr_ref & r, proof_ref & pr); }; /** \brief After applying this transformation the formula will not contain nested quantifiers. */ class pull_nested_quant { struct imp; imp * m_imp; public: pull_nested_quant(ast_manager & m); ~pull_nested_quant(); void operator()(expr * n, expr_ref & r, proof_ref & p); void reset(); }; #endif /* _PULL_QUANT_H_ */ z3-4.4.0/src/ast/normal_forms/nnf_params.pyg0000644000175000017500000000123512540347414020567 0ustar michaelmichaeldef_module_params('nnf', description='negation normal form', export=True, params=(max_memory_param(), ('sk_hack', BOOL, False, 'hack for VCC'), ('mode', SYMBOL, 'skolem', 'NNF translation mode: skolem (skolem normal form), quantifiers (skolem normal form + quantifiers in NNF), full'), ('ignore_labels', BOOL, False, 'remove/ignore labels in the input formula, this option is ignored if proofs are enabled'), ('skolemize', BOOL, True, 'skolemize (existential force) quantifiers'))) z3-4.4.0/src/ast/normal_forms/name_exprs.cpp0000644000175000017500000001105412540347414020567 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: name_exprs.h Abstract: Goodies for naming nested expressions. Author: Leonardo (leonardo) 2011-10-06 Notes: --*/ #include"name_exprs.h" #include"rewriter_def.h" #include"ast_smt2_pp.h" class name_exprs_core : public name_exprs { struct cfg : public default_rewriter_cfg { ast_manager & m_manager; defined_names & m_defined_names; expr_predicate & m_pred; app_ref m_r; proof_ref m_pr; expr_ref_vector * m_def_exprs; proof_ref_vector * m_def_proofs; cfg(ast_manager & m, defined_names & n, expr_predicate & pred): m_manager(m), m_defined_names(n), m_pred(pred), m_r(m), m_pr(m), m_def_exprs(0), m_def_proofs(0) { } void gen_name_for_expr(expr * n, expr * & t, proof * & t_pr) { expr_ref new_def(m_manager); proof_ref new_def_pr(m_manager); if (m_defined_names.mk_name(n, new_def, new_def_pr, m_r, m_pr)) { m_def_exprs->push_back(new_def); if (m_manager.proofs_enabled()) m_def_proofs->push_back(new_def_pr); } t = m_r.get(); t_pr = m_pr.get(); } bool get_subst(expr * s, expr * & t, proof * & t_pr) { TRACE("name_exprs", tout << "get_subst:\n" << mk_ismt2_pp(s, m_manager) << "\n";); if (m_pred(s)) { gen_name_for_expr(s, t, t_pr); return true; } return false; } }; typedef rewriter_tpl rw; cfg m_cfg; rw m_rw; public: name_exprs_core(ast_manager & m, defined_names & n, expr_predicate & pred): m_cfg(m, n, pred), m_rw(m, m.proofs_enabled(), m_cfg) { } virtual ~name_exprs_core() { } virtual void operator()(expr * n, expr_ref_vector & new_defs, proof_ref_vector & new_def_proofs, expr_ref & r, proof_ref & p) { m_cfg.m_def_exprs = &new_defs; m_cfg.m_def_proofs = &new_def_proofs; m_rw(n, r, p); TRACE("name_exprs", tout << mk_ismt2_pp(n, m_rw.m()) << "\n---->\n" << mk_ismt2_pp(r, m_rw.m()) << "\n";); } virtual void set_cancel(bool f) { m_rw.set_cancel(f); } virtual void reset() { m_rw.reset(); } }; name_exprs * mk_expr_namer(ast_manager & m, defined_names & n, expr_predicate & pred) { return alloc(name_exprs_core, m, n, pred); } class name_quantifier_labels : public name_exprs_core { class pred : public expr_predicate { ast_manager & m_manager; public: pred(ast_manager & m):m_manager(m) {} virtual bool operator()(expr * t) { return is_quantifier(t) || m_manager.is_label(t); } }; pred m_pred; public: name_quantifier_labels(ast_manager & m, defined_names & n): name_exprs_core(m, n, m_pred), m_pred(m) { } virtual ~name_quantifier_labels() { } }; name_exprs * mk_quantifier_label_namer(ast_manager & m, defined_names & n) { return alloc(name_quantifier_labels, m, n); } class name_nested_formulas : public name_exprs_core { struct pred : public expr_predicate { ast_manager & m_manager; expr * m_root; pred(ast_manager & m):m_manager(m), m_root(0) {} virtual bool operator()(expr * t) { TRACE("name_exprs", tout << "name_nested_formulas::pred:\n" << mk_ismt2_pp(t, m_manager) << "\n";); if (is_app(t)) return to_app(t)->get_family_id() == m_manager.get_basic_family_id() && to_app(t)->get_num_args() > 0 && t != m_root; return m_manager.is_label(t) || is_quantifier(t); } }; pred m_pred; public: name_nested_formulas(ast_manager & m, defined_names & n): name_exprs_core(m, n, m_pred), m_pred(m) { } virtual ~name_nested_formulas() { } virtual void operator()(expr * n, expr_ref_vector & new_defs, proof_ref_vector & new_def_proofs, expr_ref & r, proof_ref & p) { m_pred.m_root = n; TRACE("name_exprs", tout << "operator()\n";); name_exprs_core::operator()(n, new_defs, new_def_proofs, r, p); } }; name_exprs * mk_nested_formula_namer(ast_manager & m, defined_names & n) { return alloc(name_nested_formulas, m, n); } void del_name_exprs(name_exprs * functor) { dealloc(functor); } z3-4.4.0/src/ast/normal_forms/nnf.h0000644000175000017500000000240312540347414016652 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: nnf.h Abstract: Negation Normal Form & Skolemization Author: Leonardo (leonardo) 2008-01-11 Notes: Major revision on 2011-10-06 --*/ #ifndef _NNF_H_ #define _NNF_H_ #include"ast.h" #include"params.h" #include"defined_names.h" class nnf { struct imp; imp * m_imp; public: nnf(ast_manager & m, defined_names & n, params_ref const & p = params_ref()); ~nnf(); void operator()(expr * n, // [IN] expression that should be put into NNF expr_ref_vector & new_defs, // [OUT] new definitions proof_ref_vector & new_def_proofs, // [OUT] proofs of the new definitions expr_ref & r, // [OUT] resultant expression proof_ref & p // [OUT] proof for (~ n r) ); void updt_params(params_ref const & p); /* REG_MODULE_PARAMS('nnf', 'nnf::get_param_descrs') */ static void get_param_descrs(param_descrs & r); void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } void set_cancel(bool f); void reset(); void reset_cache(); }; #endif /* _NNF_H_ */ z3-4.4.0/src/ast/normal_forms/name_exprs.h0000644000175000017500000000341412540347414020235 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: name_exprs.h Abstract: Goodies for naming nested expressions. Author: Leonardo (leonardo) 2011-10-06 Notes: --*/ #ifndef _NAME_EXPRS_H_ #define _NAME_EXPRS_H_ #include"ast.h" #include"defined_names.h" class expr_predicate { public: virtual bool operator()(expr * t) = 0; }; class name_exprs { public: virtual ~name_exprs() {} virtual void operator()(expr * n, // [IN] expression that contain the sub-expressions to be named expr_ref_vector & new_defs, // [OUT] new definitions proof_ref_vector & new_def_proofs, // [OUT] proofs of the new definitions expr_ref & r, // [OUT] resultant expression proof_ref & p // [OUT] proof for (iff n p) ) = 0; virtual void set_cancel(bool f) = 0; void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } virtual void reset() = 0; }; /** \brief Create an expression "namer" that will create replace nested expressions that satisfy pred with new fresh declarations. */ name_exprs * mk_expr_namer(ast_manager & m, defined_names & n, expr_predicate & pred); /** \brief Create an expression "namer" that will replace quantifiers and labels with new fresh declarations. */ name_exprs * mk_quantifier_label_namer(ast_manager & m, defined_names & n); /** \brief Create an expression "namer" that will replace all nested formulas and term if-then-elses with fresh declarations. */ name_exprs * mk_nested_formula_namer(ast_manager & m, defined_names & n); void del_name_exprs(name_exprs * functor); #endif z3-4.4.0/src/ast/normal_forms/nnf.cpp0000644000175000017500000007374712540347414017230 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: nnf.cpp Abstract: Negation Normal Form & Skolemization Author: Leonardo (leonardo) 2008-01-11 Notes: Major revision on 2011-10-06 --*/ #include"nnf.h" #include"nnf_params.hpp" #include"warning.h" #include"used_vars.h" #include"well_sorted.h" #include"var_subst.h" #include"name_exprs.h" #include"act_cache.h" #include"cooperate.h" #include"ast_smt2_pp.h" /** \brief NNF translation mode. The cheapest mode is NNF_SKOLEM, and the most expensive is NNF_FULL. */ enum nnf_mode { NNF_SKOLEM, /* A subformula is put into NNF only if it contains quantifiers or labels. The result of the transformation will be in skolem normal form. If a formula is too expensive to be put into NNF, then nested quantifiers and labels are renamed. This mode is sufficient when using E-matching. */ NNF_QUANT, /* A subformula is put into NNF if it contains quantifiers, labels, or is in the scope of a quantifier. The result of the transformation will be in skolem normal form, and the body of quantifiers will be in NNF. If a ground formula is too expensive to be put into NNF, then nested quantifiers and labels are renamed. This mode is sufficient when using Superposition Calculus. Remark: If the problem does not contain quantifiers, then NNF_QUANT is identical to NNF_SKOLEM. */ NNF_OPPORTUNISTIC, /* Similar to NNF_QUANT, but a subformula is also put into NNF, if it is cheap. Otherwise, the nested quantifiers and labels are renamed. */ NNF_FULL /* Everything is put into NNF. */ }; class skolemizer { typedef act_cache cache; ast_manager & m_manager; symbol m_sk_hack; bool m_sk_hack_enabled; cache m_cache; cache m_cache_pr; void process(quantifier * q, expr_ref & r, proof_ref & p) { used_vars uv; uv(q); SASSERT(is_well_sorted(m(), q)); unsigned sz = uv.get_max_found_var_idx_plus_1(); ptr_buffer sorts; expr_ref_vector args(m()); for (unsigned i = 0; i < sz; i++) { sort * s = uv.get(i); if (s != 0) { sorts.push_back(s); args.push_back(m().mk_var(i, s)); } } TRACE("skolemizer", tout << "skid: " << q->get_skid() << "\n";); expr_ref_vector substitution(m()); unsigned num_decls = q->get_num_decls(); for (unsigned i = num_decls; i > 0; ) { --i; sort * r = q->get_decl_sort(i); func_decl * sk_decl = m().mk_fresh_func_decl(q->get_decl_name(i), q->get_skid(), sorts.size(), sorts.c_ptr(), r); app * sk = m().mk_app(sk_decl, args.size(), args.c_ptr()); substitution.push_back(sk); } // // (VAR 0) is in the first position of substitution. // (VAR num_decls-1) is in the last position. // for (unsigned i = 0; i < sz; i++) { sort * s = uv.get(i); if (s != 0) substitution.push_back(m().mk_var(i, s)); else substitution.push_back(0); } // // (VAR num_decls) ... (VAR num_decls+sz-1) // are in positions num_decls .. num_decls+sz-1 // std::reverse(substitution.c_ptr(), substitution.c_ptr() + substitution.size()); // // (VAR 0) should be in the last position of substitution. // var_subst s(m()); SASSERT(is_well_sorted(m(), q->get_expr())); expr_ref tmp(m()); expr * body = q->get_expr(); if (m_sk_hack_enabled) { unsigned num_patterns = q->get_num_patterns(); for (unsigned i = 0; i < num_patterns; ++i) { expr * p = q->get_pattern(i); if (is_sk_hack(p)) { expr * sk_hack = to_app(p)->get_arg(0); if (q->is_forall()) // check whether is in negative/positive context. tmp = m().mk_or(body, m().mk_not(sk_hack)); // negative context else tmp = m().mk_and(body, sk_hack); // positive context body = tmp; } } } s(body, substitution.size(), substitution.c_ptr(), r); p = 0; if (m().proofs_enabled()) { if (q->is_forall()) p = m().mk_skolemization(m().mk_not(q), m().mk_not(r)); else p = m().mk_skolemization(q, r); } } public: skolemizer(ast_manager & m): m_manager(m), m_sk_hack("sk_hack"), m_sk_hack_enabled(false), m_cache(m), m_cache_pr(m) { } void set_sk_hack(bool f) { m_sk_hack_enabled = f; } ast_manager & m() const { return m_manager; } void operator()(quantifier * q, expr_ref & r, proof_ref & p) { r = m_cache.find(q); if (r.get() != 0) { p = 0; if (m().proofs_enabled()) p = static_cast(m_cache_pr.find(q)); } else { process(q, r, p); m_cache.insert(q, r); if (m().proofs_enabled()) m_cache_pr.insert(q, p); } } bool is_sk_hack(expr * p) const { SASSERT(m().is_pattern(p)); if (to_app(p)->get_num_args() != 1) return false; expr * body = to_app(p)->get_arg(0); if (!is_app(body)) return false; func_decl * f = to_app(body)->get_decl(); if (!(f->get_name() == m_sk_hack && f->get_arity() == 1)) return false; if (!m().is_bool(body)) { warning_msg("sk_hack constant must return a Boolean"); return false; } return true; } }; typedef default_exception nnf_params_exception; typedef default_exception nnf_exception; struct nnf::imp { struct frame { expr * m_curr; unsigned m_i:28; unsigned m_pol:1; // pos/neg polarity unsigned m_in_q:1; // true if m_curr is nested in a quantifier unsigned m_new_child:1; unsigned m_cache_result:1; unsigned m_spos; // top of the result stack, when the frame was created. frame(expr * n, bool pol, bool in_q, bool cache_res, unsigned spos): m_curr(n), m_i(0), m_pol(pol), m_in_q(in_q), m_new_child(false), m_cache_result(cache_res), m_spos(spos) { } }; // There are four caches: #define NEG_NQ_CIDX 0 // negative polarity and not nested in a quantifier #define POS_NQ_CIDX 1 // positive polarity and not nested in a quantifier #define NEG_Q_CIDX 2 // negative polarity and nested in a quantifier #define POS_Q_CIDX 3 // positive polarity and nested in a quantifier ast_manager & m_manager; svector m_frame_stack; expr_ref_vector m_result_stack; typedef act_cache cache; cache * m_cache[4]; expr_ref_vector m_todo_defs; proof_ref_vector m_todo_proofs; // proof generation goodness ---- proof_ref_vector m_result_pr_stack; cache * m_cache_pr[4]; // ------------------------------ skolemizer m_skolemizer; // configuration ---------------- nnf_mode m_mode; bool m_ignore_labels; bool m_skolemize; // ------------------------------ name_exprs * m_name_nested_formulas; name_exprs * m_name_quant; volatile bool m_cancel; unsigned long long m_max_memory; // in bytes imp(ast_manager & m, defined_names & n, params_ref const & p): m_manager(m), m_result_stack(m), m_todo_defs(m), m_todo_proofs(m), m_result_pr_stack(m), m_skolemizer(m), m_cancel(false) { updt_params(p); for (unsigned i = 0; i < 4; i++) { m_cache[i] = alloc(act_cache, m); if (m.proofs_enabled()) m_cache_pr[i] = alloc(act_cache, m); } m_name_nested_formulas = mk_nested_formula_namer(m, n); m_name_quant = mk_quantifier_label_namer(m, n); } ast_manager & m() const { return m_manager; } bool proofs_enabled() const { return m().proofs_enabled(); } ~imp() { for (unsigned i = 0; i < 4; i++) { dealloc(m_cache[i]); if (proofs_enabled()) dealloc(m_cache_pr[i]); } del_name_exprs(m_name_nested_formulas); del_name_exprs(m_name_quant); } void updt_params(params_ref const & _p) { nnf_params p(_p); symbol mode_sym = p.mode(); if (mode_sym == "skolem") m_mode = NNF_SKOLEM; else if (mode_sym == "full") m_mode = NNF_FULL; else if (mode_sym == "quantifiers") m_mode = NNF_QUANT; else throw nnf_params_exception("invalid NNF mode"); TRACE("nnf", tout << "nnf-mode: " << m_mode << " " << mode_sym << "\n" << _p << "\n";); m_ignore_labels = p.ignore_labels(); m_skolemize = p.skolemize(); m_max_memory = megabytes_to_bytes(p.max_memory()); m_skolemizer.set_sk_hack(p.sk_hack()); } static void get_param_descrs(param_descrs & r) { nnf_params::collect_param_descrs(r); } void reset() { m_frame_stack.reset(); m_result_stack.reset(); m_result_pr_stack.reset(); m_todo_defs.reset(); m_todo_proofs.reset(); } void reset_cache() { for (unsigned i = 0; i < 4; i++) { m_cache[i]->reset(); if (proofs_enabled()) m_cache_pr[i]->reset(); } } void push_frame(expr * t, bool pol, bool in_q, bool cache_res) { m_frame_stack.push_back(frame(t, pol, in_q, cache_res, m_result_stack.size())); } static unsigned get_cache_idx(bool pol, bool in_q) { return static_cast(in_q) * 2 + static_cast(pol); } void cache_result(expr * t, bool pol, bool in_q, expr * v, proof * pr) { unsigned idx = get_cache_idx(pol, in_q); m_cache[idx]->insert(t, v); if (proofs_enabled()) m_cache_pr[idx]->insert(t, pr); } expr * get_cached(expr * t, bool pol, bool in_q) const { return m_cache[get_cache_idx(pol, in_q)]->find(t); } proof * get_cached_pr(expr * t, bool pol, bool in_q) const { SASSERT(proofs_enabled()); return static_cast(m_cache_pr[get_cache_idx(pol, in_q)]->find(t)); } /** \brief Return true if the result for (t, pol, in_q) is already cached, and store the result on the stack. */ bool process_cached(expr * t, bool pol, bool in_q) { expr * r = get_cached(t, pol, in_q); if (r) { m_result_stack.push_back(r); if (proofs_enabled()) { proof * pr = get_cached_pr(t, pol, in_q); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } m_frame_stack.pop_back(); set_new_child_flag(t, r); return true; } return false; } void set_cancel(bool f) { m_cancel = f; } void checkpoint() { cooperate("nnf"); if (memory::get_allocation_size() > m_max_memory) throw nnf_exception(Z3_MAX_MEMORY_MSG); if (m_cancel) throw nnf_exception(Z3_CANCELED_MSG); } void set_new_child_flag() { if (!m_frame_stack.empty()) m_frame_stack.back().m_new_child = true; } void set_new_child_flag(expr * old_t, expr * new_t) { if (old_t != new_t) set_new_child_flag(); } void skip(expr * t, bool pol) { expr * r = pol ? t : m().mk_not(t); m_result_stack.push_back(r); if (proofs_enabled()) { m_result_pr_stack.push_back(m().mk_oeq_reflexivity(r)); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } } bool visit(expr * t, bool pol, bool in_q) { SASSERT(m().is_bool(t)); if (m_mode == NNF_SKOLEM || (m_mode == NNF_QUANT && !in_q)) { if (!has_quantifiers(t) && !has_labels(t)) { skip(t, pol); return true; // t does not need to be processed } } bool cache_res = t->get_ref_count() > 1; if (cache_res) { expr * r = get_cached(t, pol, in_q); if (r) { m_result_stack.push_back(r); set_new_child_flag(t, r); if (proofs_enabled()) { proof * pr = get_cached_pr(t, pol, in_q); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } return true; // t was already processed } } switch (t->get_kind()) { case AST_APP: if (to_app(t)->get_num_args() == 0) { skip(t, pol); return true; } else { push_frame(t, pol, in_q, cache_res); return false; } case AST_QUANTIFIER: push_frame(t, pol, in_q, cache_res); return false; case AST_VAR: skip(t, pol); return true; default: UNREACHABLE(); return true; } } proof * mk_proof(bool pol, unsigned num_parents, proof * const * parents, app * old_e, app * new_e) { if (pol) { if (old_e->get_decl() == new_e->get_decl()) return m().mk_oeq_congruence(old_e, new_e, num_parents, parents); else return m().mk_nnf_pos(old_e, new_e, num_parents, parents); } else return m().mk_nnf_neg(old_e, new_e, num_parents, parents); } bool process_and_or(app * t, frame & fr) { unsigned num_args = t->get_num_args(); while (fr.m_i < num_args) { expr * arg = t->get_arg(fr.m_i); fr.m_i++; if (!visit(arg, fr.m_pol, fr.m_in_q)) return false; } app * r; if (m().is_and(t) == fr.m_pol) r = m().mk_and(t->get_num_args(), m_result_stack.c_ptr() + fr.m_spos); else r = m().mk_or(t->get_num_args(), m_result_stack.c_ptr() + fr.m_spos); m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(r); if (proofs_enabled()) { proof * pr = mk_proof(fr.m_pol, t->get_num_args(), m_result_pr_stack.c_ptr() + fr.m_spos, t, r); m_result_pr_stack.shrink(fr.m_spos); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } return true; } bool process_not(app * t, frame & fr) { if (fr.m_i == 0) { fr.m_i = 1; if (!visit(t->get_arg(0), !fr.m_pol, fr.m_in_q)) return false; } expr * r = m_result_stack.back(); proof * pr = 0; if (proofs_enabled()) { pr = m_result_pr_stack.back(); if (!fr.m_pol) { pr = m().mk_nnf_neg(t, r, 1, &pr); m_result_pr_stack.pop_back(); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } } return true; } bool process_implies(app * t, frame & fr) { SASSERT(t->get_num_args() == 2); switch (fr.m_i) { case 0: fr.m_i = 1; if (!visit(t->get_arg(0), !fr.m_pol, fr.m_in_q)) return false; case 1: fr.m_i = 2; if (!visit(t->get_arg(1), fr.m_pol, fr.m_in_q)) return false; default: break; } app * r; if (fr.m_pol) r = m().mk_or(2, m_result_stack.c_ptr() + fr.m_spos); else r = m().mk_and(2, m_result_stack.c_ptr() + fr.m_spos); m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(r); if (proofs_enabled()) { proof * pr = mk_proof(fr.m_pol, 2, m_result_pr_stack.c_ptr() + fr.m_spos, t, r); m_result_pr_stack.shrink(fr.m_spos); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } return true; } bool process_ite(app * t, frame & fr) { SASSERT(t->get_num_args() == 3); switch (fr.m_i) { case 0: fr.m_i = 1; if (!visit(t->get_arg(0), true, fr.m_in_q)) return false; case 1: fr.m_i = 2; if (!visit(t->get_arg(0), false, fr.m_in_q)) return false; case 2: fr.m_i = 3; if (!visit(t->get_arg(1), fr.m_pol, fr.m_in_q)) return false; case 3: fr.m_i = 4; if (!visit(t->get_arg(2), fr.m_pol, fr.m_in_q)) return false; default: break; } expr * const * rs = m_result_stack.c_ptr() + fr.m_spos; expr * _cond = rs[0]; expr * _not_cond = rs[1]; expr * _then = rs[2]; expr * _else = rs[3]; app * r = m().mk_and(m().mk_or(_not_cond, _then), m().mk_or(_cond, _else)); m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(r); if (proofs_enabled()) { proof * pr = mk_proof(fr.m_pol, 4, m_result_pr_stack.c_ptr() + fr.m_spos, t, r); m_result_pr_stack.shrink(fr.m_spos); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } return true; } bool is_eq(app * t) const { return m().is_eq(t) || m().is_iff(t); } bool process_iff_xor(app * t, frame & fr) { SASSERT(t->get_num_args() == 2); switch (fr.m_i) { case 0: fr.m_i = 1; if (!visit(t->get_arg(0), true, fr.m_in_q)) return false; case 1: fr.m_i = 2; if (!visit(t->get_arg(0), false, fr.m_in_q)) return false; case 2: fr.m_i = 3; if (!visit(t->get_arg(1), true, fr.m_in_q)) return false; case 3: fr.m_i = 4; if (!visit(t->get_arg(1), false, fr.m_in_q)) return false; default: break; } expr * const * rs = m_result_stack.c_ptr() + fr.m_spos; expr * lhs = rs[0]; expr * not_lhs = rs[1]; expr * rhs = rs[2]; expr * not_rhs = rs[3]; app * r; if (is_eq(t) == fr.m_pol) r = m().mk_and(m().mk_or(not_lhs, rhs), m().mk_or(lhs, not_rhs)); else r = m().mk_and(m().mk_or(lhs, rhs), m().mk_or(not_lhs, not_rhs)); m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(r); if (proofs_enabled()) { proof * pr = mk_proof(fr.m_pol, 4, m_result_pr_stack.c_ptr() + fr.m_spos, t, r); m_result_pr_stack.shrink(fr.m_spos); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } return true; } bool process_eq(app * t, frame & fr) { if (m().is_bool(t->get_arg(0))) return process_iff_xor(t, fr); else return process_default(t, fr); } bool process_default(app * t, frame & fr) { SASSERT(fr.m_i == 0); if (m_mode == NNF_FULL || t->has_quantifiers() || t->has_labels()) { expr_ref n2(m()); proof_ref pr2(m()); if (m_mode == NNF_FULL || (m_mode != NNF_SKOLEM && fr.m_in_q)) m_name_nested_formulas->operator()(t, m_todo_defs, m_todo_proofs, n2, pr2); else m_name_quant->operator()(t, m_todo_defs, m_todo_proofs, n2, pr2); if (!fr.m_pol) n2 = m().mk_not(n2); m_result_stack.push_back(n2); if (proofs_enabled()) { if (!fr.m_pol) { proof * prs[1] = { pr2 }; pr2 = m().mk_oeq_congruence(m().mk_not(t), static_cast(n2.get()), 1, prs); } m_result_pr_stack.push_back(pr2); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } } else { skip(t, fr.m_pol); } return true; } bool process_label(app * t, frame & fr) { if (fr.m_i == 0) { fr.m_i = 1; if (!visit(t->get_arg(0), fr.m_pol, fr.m_in_q)) return false; } expr * arg = m_result_stack.back(); proof * arg_pr = proofs_enabled() ? m_result_pr_stack.back() : 0; if (m_ignore_labels && !proofs_enabled()) return true; // the result is already on the stack buffer names; bool pos; m().is_label(t, pos, names); expr_ref r(m()); proof_ref pr(m()); if (fr.m_pol == pos) { expr * lbl_lit = m().mk_label_lit(names.size(), names.c_ptr()); r = m().mk_and(arg, lbl_lit); if (proofs_enabled()) { expr_ref aux(m_manager); aux = m().mk_label(true, names.size(), names.c_ptr(), arg); pr = m().mk_transitivity(mk_proof(fr.m_pol, 1, &arg_pr, t, to_app(aux)), m().mk_iff_oeq(m().mk_rewrite(aux, r))); } } else { r = arg; if (proofs_enabled()) { proof * p1 = m().mk_iff_oeq(m().mk_rewrite(t, t->get_arg(0))); pr = m().mk_transitivity(p1, arg_pr); } } m_result_stack.pop_back(); m_result_stack.push_back(r); if (proofs_enabled()) { m_result_pr_stack.pop_back(); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } return true; } bool process_app(app * t, frame & fr) { SASSERT(m().is_bool(t)); if (t->get_family_id() == m().get_basic_family_id()) { switch (static_cast(t->get_decl_kind())) { case OP_AND: case OP_OR: return process_and_or(t, fr); case OP_NOT: return process_not(t, fr); case OP_IMPLIES: return process_implies(t, fr); case OP_ITE: return process_ite(t, fr); case OP_IFF: case OP_XOR: return process_iff_xor(t, fr); case OP_EQ: return process_eq(t, fr); default: break; } } if (m().is_label(t)) { return process_label(t, fr); } return process_default(t, fr); } bool process_var(var * v, frame & fr) { skip(v, fr.m_pol); return true; } bool process_quantifier(quantifier * q, frame & fr) { expr_ref r(m()); proof_ref pr(m()); if (fr.m_i == 0) { fr.m_i = 1; if (q->is_forall() == fr.m_pol || !m_skolemize) { if (!visit(q->get_expr(), fr.m_pol, true)) return false; } else { m_skolemizer(q, r, pr); if (!visit(r, !q->is_forall(), fr.m_in_q)) return false; } } if (q->is_forall() == fr.m_pol || !m_skolemize) { expr * new_expr = m_result_stack.back(); proof * new_expr_pr = proofs_enabled() ? m_result_pr_stack.back() : 0; ptr_buffer new_patterns; if (q->is_forall() == fr.m_pol) { // collect non sk_hack patterns unsigned num_patterns = q->get_num_patterns(); for (unsigned i = 0; i < num_patterns; i++) { expr * pat = q->get_pattern(i); if (!m_skolemizer.is_sk_hack(pat)) new_patterns.push_back(pat); } } else { // New quantifier has existential force. // So, ignore patterns } quantifier * new_q = 0; proof * new_q_pr = 0; if (fr.m_pol) { new_q = m().update_quantifier(q, new_patterns.size(), new_patterns.c_ptr(), new_expr); if (proofs_enabled()) new_q_pr = m().mk_nnf_pos(q, new_q, 1, &new_expr_pr); } else { new_q = m().update_quantifier(q, !q->is_forall(), new_patterns.size(), new_patterns.c_ptr(), new_expr); if (proofs_enabled()) new_q_pr = m().mk_nnf_neg(q, new_q, 1, &new_expr_pr); } m_result_stack.pop_back(); m_result_stack.push_back(new_q); if (proofs_enabled()) { m_result_pr_stack.pop_back(); m_result_pr_stack.push_back(new_q_pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } } else { // Quantifier was skolemized. // The result is already on the stack. // However, the proof must be updated if (proofs_enabled()) { m_skolemizer(q, r, pr); // retrieve the proof pr = m().mk_transitivity(pr, m_result_pr_stack.back()); m_result_pr_stack.pop_back(); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } } return true; } void recover_result(expr * t, expr_ref & result, proof_ref & result_pr) { // recover result from the top of the stack. result = m_result_stack.back(); m_result_stack.pop_back(); SASSERT(m_result_stack.empty()); if (proofs_enabled()) { result_pr = m_result_pr_stack.back(); m_result_pr_stack.pop_back(); if (result_pr.get() == 0) result_pr = m().mk_reflexivity(t); SASSERT(m_result_pr_stack.empty()); } } void process(expr * t, expr_ref & result, proof_ref & result_pr) { TRACE("nnf", tout << "processing:\n" << mk_ismt2_pp(t, m()) << "\n";); SASSERT(m().is_bool(t)); if (visit(t, true /* positive polarity */, false /* not nested in quantifier */)) { recover_result(t, result, result_pr); return; } SASSERT(!m_frame_stack.empty()); while (!m_frame_stack.empty()) { checkpoint(); frame & fr = m_frame_stack.back(); expr * t = fr.m_curr; if (fr.m_i == 0 && t->get_ref_count() > 1 && process_cached(t, fr.m_pol, fr.m_in_q)) continue; bool status; switch (t->get_kind()) { case AST_APP: status = process_app(to_app(t), fr); break; case AST_QUANTIFIER: status = process_quantifier(to_quantifier(t), fr); break; case AST_VAR: status = process_var(to_var(t), fr); break; default: UNREACHABLE(); status = true; break; } if (status) { if (fr.m_cache_result) cache_result(fr.m_curr, fr.m_pol, fr.m_in_q, m_result_stack.back(), proofs_enabled() ? m_result_pr_stack.back() : 0); m_frame_stack.pop_back(); } } recover_result(t, result, result_pr); } void operator()(expr * n, expr_ref_vector & new_defs, proof_ref_vector & new_def_proofs, expr_ref & r, proof_ref & pr) { reset(); process(n, r, pr); unsigned old_sz1 = new_defs.size(); unsigned old_sz2 = new_def_proofs.size(); for (unsigned i = 0; i < m_todo_defs.size(); i++) { expr_ref dr(m()); proof_ref dpr(m()); process(m_todo_defs.get(i), dr, dpr); new_defs.push_back(dr); if (proofs_enabled()) { proof * new_pr = m().mk_modus_ponens(m_todo_proofs.get(i), dpr); new_def_proofs.push_back(new_pr); } } std::reverse(new_defs.c_ptr() + old_sz1, new_defs.c_ptr() + new_defs.size()); std::reverse(new_def_proofs.c_ptr() + old_sz2, new_def_proofs.c_ptr() + new_def_proofs.size()); } }; nnf::nnf(ast_manager & m, defined_names & n, params_ref const & p) { TRACE("nnf", tout << "nnf constructor: " << p << "\n";); m_imp = alloc(imp, m, n, p); } nnf::~nnf() { dealloc(m_imp); } void nnf::operator()(expr * n, expr_ref_vector & new_defs, proof_ref_vector & new_def_proofs, expr_ref & r, proof_ref & p) { m_imp->operator()(n, new_defs, new_def_proofs, r, p); TRACE("nnf_result", tout << mk_ismt2_pp(n, m_imp->m()) << "\nNNF result:\n" << mk_ismt2_pp(r, m_imp->m()) << "\n";); } void nnf::updt_params(params_ref const & p) { m_imp->updt_params(p); } void nnf::get_param_descrs(param_descrs & r) { imp::get_param_descrs(r); } void nnf::set_cancel(bool f) { m_imp->set_cancel(f); } void nnf::reset() { m_imp->reset(); } void nnf::reset_cache() { m_imp->reset_cache(); } z3-4.4.0/src/ast/normal_forms/defined_names.cpp0000644000175000017500000002663612540347414021223 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: defined_names.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-14. Revision History: --*/ #include"defined_names.h" #include"obj_hashtable.h" #include"used_vars.h" #include"var_subst.h" #include"ast_smt2_pp.h" #include"ast_pp.h" struct defined_names::impl { typedef obj_map expr2name; typedef obj_map expr2proof; ast_manager & m_manager; symbol m_z3name; /** \brief Mapping from expressions to their names. A name is an application. If the expression does not have free variables, then the name is just a constant. */ expr2name m_expr2name; /** \brief Mapping from expressions to the apply-def proof. That is, for each expression e, m_expr2proof[e] is the proof e and m_expr2name[2] are observ. equivalent. This mapping is not used if proof production is disabled. */ expr2proof m_expr2proof; /** \brief Domain of m_expr2name. It is used to keep the expressions alive and for backtracking */ expr_ref_vector m_exprs; expr_ref_vector m_names; //!< Range of m_expr2name. It is used to keep the names alive. proof_ref_vector m_apply_proofs; //!< Range of m_expr2proof. It is used to keep the def-intro proofs alive. unsigned_vector m_lims; //!< Backtracking support. impl(ast_manager & m, char const * prefix); virtual ~impl(); app * gen_name(expr * e, sort_ref_buffer & var_sorts, buffer & var_names); void cache_new_name(expr * e, app * name); void cache_new_name_intro_proof(expr * e, proof * pr); void bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref & result); void bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref_buffer & result); virtual void mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer const & var_names, expr_ref & new_def); bool mk_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr); void push_scope(); void pop_scope(unsigned num_scopes); void reset(); unsigned get_num_names() const { return m_names.size(); } func_decl * get_name_decl(unsigned i) const { return to_app(m_names.get(i))->get_decl(); } }; struct defined_names::pos_impl : public defined_names::impl { pos_impl(ast_manager & m, char const * fresh_prefix):impl(m, fresh_prefix) {} virtual void mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer const & var_names, expr_ref & new_def); }; defined_names::impl::impl(ast_manager & m, char const * prefix): m_manager(m), m_exprs(m), m_names(m), m_apply_proofs(m) { if (prefix) m_z3name = prefix; } defined_names::impl::~impl() { } /** \brief Given an expression \c e that may contain free variables, return an application (sk x_1 ... x_n), where sk is a fresh variable name, and x_i's are the free variables of \c e. Store in var_sorts and var_names information about the free variables of \c e. This data is used to create an universal quantifier over the definition of the new name. */ app * defined_names::impl::gen_name(expr * e, sort_ref_buffer & var_sorts, buffer & var_names) { used_vars uv; uv(e); unsigned num_vars = uv.get_max_found_var_idx_plus_1(); ptr_buffer new_args; ptr_buffer domain; for (unsigned i = 0; i < num_vars; i++) { sort * s = uv.get(i); if (s) { domain.push_back(s); new_args.push_back(m_manager.mk_var(i, s)); var_sorts.push_back(s); } else { var_sorts.push_back(m_manager.mk_bool_sort()); // could be any sort. } var_names.push_back(symbol(i)); } sort * range = m_manager.get_sort(e); func_decl * new_skolem_decl = m_manager.mk_fresh_func_decl(m_z3name, symbol::null, domain.size(), domain.c_ptr(), range); app * n = m_manager.mk_app(new_skolem_decl, new_args.size(), new_args.c_ptr()); TRACE("mk_definition_bug", tout << "gen_name: " << mk_ismt2_pp(n, m_manager) << "\n"; for (unsigned i = 0; i < var_sorts.size(); i++) tout << mk_pp(var_sorts[i], m_manager) << " "; tout << "\n";); return n; } /** \brief Cache \c n as a name for expression \c e. */ void defined_names::impl::cache_new_name(expr * e, app * n) { m_expr2name.insert(e, n); m_exprs.push_back(e); m_names.push_back(n); } /** \brief Cache \c pr as a proof that m_expr2name[e] is a name for expression \c e. */ void defined_names::impl::cache_new_name_intro_proof(expr * e, proof * pr) { SASSERT(m_expr2name.contains(e)); m_expr2proof.insert(e, pr); m_apply_proofs.push_back(pr); } /** \brief Given a definition conjunct \c def of the name \c name, store in \c result this definition. A quantifier is added around \c def_conjunct, if sorts and names are not empty. In this case, The application \c name is used as a pattern for the new quantifier. */ void defined_names::impl::bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref & result) { SASSERT(sorts.size() == names.size()); if (sorts.empty()) result = def_conjunct; else { expr * patterns[1] = { m_manager.mk_pattern(name) }; quantifier_ref q(m_manager); q = m_manager.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), def_conjunct, 1, symbol::null, symbol::null, 1, patterns); TRACE("mk_definition_bug", tout << "before elim_unused_vars:\n" << mk_ismt2_pp(q, m_manager) << "\n";); elim_unused_vars(m_manager, q, result); TRACE("mk_definition_bug", tout << "after elim_unused_vars:\n" << mk_ismt2_pp(result, m_manager) << "\n";); } } /** \brief Given a definition conjunct \c def of the name \c name, store in \c result this definition. A quantifier is added around \c def_conjunct, if sorts and names are not empty. In this case, The application \c name is used as a pattern for the new quantifier. */ void defined_names::impl::bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref_buffer & result) { expr_ref tmp(m_manager); bound_vars(sorts, names, def_conjunct, name, tmp); result.push_back(tmp); } #define MK_OR m_manager.mk_or #define MK_NOT m_manager.mk_not #define MK_EQ m_manager.mk_eq void defined_names::impl::mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer const & var_names, expr_ref & new_def) { expr_ref_buffer defs(m_manager); if (m_manager.is_bool(e)) { bound_vars(var_sorts, var_names, MK_OR(MK_NOT(n), e), n, defs); bound_vars(var_sorts, var_names, MK_OR(n, MK_NOT(e)), n, defs); } else if (m_manager.is_term_ite(e)) { bound_vars(var_sorts, var_names, MK_OR(MK_NOT(to_app(e)->get_arg(0)), MK_EQ(n, to_app(e)->get_arg(1))), n, defs); bound_vars(var_sorts, var_names, MK_OR(to_app(e)->get_arg(0), MK_EQ(n, to_app(e)->get_arg(2))), n, defs); } else { bound_vars(var_sorts, var_names, MK_EQ(e, n), n, defs); } new_def = defs.size() == 1 ? defs[0] : m_manager.mk_and(defs.size(), defs.c_ptr()); } void defined_names::pos_impl::mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer const & var_names, expr_ref & new_def) { bound_vars(var_sorts, var_names, MK_OR(MK_NOT(n), e), n, new_def); } bool defined_names::impl::mk_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr) { TRACE("mk_definition_bug", tout << "making name for:\n" << mk_ismt2_pp(e, m_manager) << "\n";); app * n_ptr; if (m_expr2name.find(e, n_ptr)) { TRACE("mk_definition_bug", tout << "name for expression is already cached..., returning false...\n";); n = n_ptr; if (m_manager.proofs_enabled()) { proof * pr_ptr; m_expr2proof.find(e, pr_ptr); SASSERT(pr_ptr); pr = pr_ptr; } return false; } else { sort_ref_buffer var_sorts(m_manager); buffer var_names; n = gen_name(e, var_sorts, var_names); cache_new_name(e, n); TRACE("mk_definition_bug", tout << "name: " << mk_ismt2_pp(n, m_manager) << "\n";); // variables are in reverse order in quantifiers std::reverse(var_sorts.c_ptr(), var_sorts.c_ptr() + var_sorts.size()); std::reverse(var_names.c_ptr(), var_names.c_ptr() + var_names.size()); mk_definition(e, n, var_sorts, var_names, new_def); TRACE("mk_definition_bug", tout << "new_def:\n" << mk_ismt2_pp(new_def, m_manager) << "\n";); if (m_manager.proofs_enabled()) { new_def_pr = m_manager.mk_def_intro(new_def); pr = m_manager.mk_apply_def(e, n, new_def_pr); cache_new_name_intro_proof(e, pr); } return true; } } void defined_names::impl::push_scope() { SASSERT(m_exprs.size() == m_names.size()); m_lims.push_back(m_exprs.size()); } void defined_names::impl::pop_scope(unsigned num_scopes) { unsigned lvl = m_lims.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; unsigned old_sz = m_lims[new_lvl]; unsigned sz = m_exprs.size(); SASSERT(old_sz <= sz); SASSERT(sz == m_names.size()); while (old_sz != sz) { --sz; if (m_manager.proofs_enabled()) { m_expr2proof.erase(m_exprs.back()); m_apply_proofs.pop_back(); } m_expr2name.erase(m_exprs.back()); m_exprs.pop_back(); m_names.pop_back(); } SASSERT(m_exprs.size() == old_sz); m_lims.shrink(new_lvl); } void defined_names::impl::reset() { m_expr2name.reset(); m_expr2proof.reset(); m_exprs.reset(); m_names.reset(); m_apply_proofs.reset(); m_lims.reset(); } defined_names::defined_names(ast_manager & m, char const * fresh_prefix) { m_impl = alloc(impl, m, fresh_prefix); m_pos_impl = alloc(pos_impl, m, fresh_prefix); } defined_names::~defined_names() { dealloc(m_impl); dealloc(m_pos_impl); } bool defined_names::mk_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr) { return m_impl->mk_name(e, new_def, new_def_pr, n, pr); } bool defined_names::mk_pos_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr) { return m_pos_impl->mk_name(e, new_def, new_def_pr, n, pr); } void defined_names::push() { m_impl->push_scope(); m_pos_impl->push_scope(); } void defined_names::pop(unsigned num_scopes) { m_impl->pop_scope(num_scopes); m_pos_impl->pop_scope(num_scopes); } void defined_names::reset() { m_impl->reset(); m_pos_impl->reset(); } unsigned defined_names::get_num_names() const { return m_impl->get_num_names() + m_pos_impl->get_num_names(); } func_decl * defined_names::get_name_decl(unsigned i) const { SASSERT(i < get_num_names()); unsigned n1 = m_impl->get_num_names(); return i < n1 ? m_impl->get_name_decl(i) : m_pos_impl->get_name_decl(i - n1); } z3-4.4.0/src/ast/normal_forms/pull_quant.cpp0000644000175000017500000003632012540347414020615 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: pull_quant.cpp Abstract: Pull nested quantifiers. Author: Leonardo (leonardo) 2008-01-20 Notes: --*/ #include"pull_quant.h" #include"var_subst.h" #include"rewriter_def.h" #include"ast_pp.h" struct pull_quant::imp { struct rw_cfg : public default_rewriter_cfg { ast_manager & m_manager; shift_vars m_shift; rw_cfg(ast_manager & m): m_manager(m), m_shift(m) { } bool pull_quant1_core(func_decl * d, unsigned num_children, expr * const * children, expr_ref & result) { ptr_buffer var_sorts; buffer var_names; symbol qid; int w = INT_MAX; // The input formula is in Skolem normal form... // So all children are forall (positive context) or exists (negative context). // Remark: (AND a1 ...) may be represented (NOT (OR (NOT a1) ...))) // So, when pulling a quantifier over a NOT, it becomes an exists. if (m_manager.is_not(d)) { SASSERT(num_children == 1); expr * child = children[0]; if (is_quantifier(child)) { quantifier * q = to_quantifier(child); expr * body = q->get_expr(); result = m_manager.update_quantifier(q, !q->is_forall(), m_manager.mk_not(body)); return true; } else { return false; } } bool found_quantifier = false; bool forall_children; for (unsigned i = 0; i < num_children; i++) { expr * child = children[i]; if (is_quantifier(child)) { if (!found_quantifier) { found_quantifier = true; forall_children = is_forall(child); } else { // Since the initial formula was in SNF, all children must be EXISTS or FORALL. SASSERT(forall_children == is_forall(child)); } quantifier * nested_q = to_quantifier(child); if (var_sorts.empty()) { // use the qid of one of the nested quantifiers. qid = nested_q->get_qid(); } w = std::min(w, nested_q->get_weight()); unsigned j = nested_q->get_num_decls(); while (j > 0) { --j; var_sorts.push_back(nested_q->get_decl_sort(j)); symbol s = nested_q->get_decl_name(j); if (std::find(var_names.begin(), var_names.end(), s) != var_names.end()) var_names.push_back(m_manager.mk_fresh_var_name(s.is_numerical() ? 0 : s.bare_str())); else var_names.push_back(s); } } } if (!var_sorts.empty()) { SASSERT(found_quantifier); // adjust the variable ids in formulas in new_children expr_ref_buffer new_adjusted_children(m_manager); expr_ref adjusted_child(m_manager); unsigned num_decls = var_sorts.size(); unsigned shift_amount = 0; TRACE("pull_quant", tout << "Result num decls:" << num_decls << "\n";); for (unsigned i = 0; i < num_children; i++) { expr * child = children[i]; if (!is_quantifier(child)) { // increment the free variables in child by num_decls because // child will be in the scope of num_decls bound variables. m_shift(child, num_decls, adjusted_child); TRACE("pull_quant", tout << "shifted by: " << num_decls << "\n" << mk_pp(child, m_manager) << "\n---->\n" << mk_pp(adjusted_child, m_manager) << "\n";); } else { quantifier * nested_q = to_quantifier(child); SASSERT(num_decls >= nested_q->get_num_decls()); // Assume nested_q is of the form // forall xs. P(xs, ys) // where xs (ys) represents the set of bound (free) variables. // // - the index of the variables xs must be increased by shift_amount. // That is, the number of new bound variables that will precede the bound // variables xs. // // - the index of the variables ys must be increased by num_decls - nested_q->get_num_decls. // That is, the total number of new bound variables that will be in the scope // of nested_q->get_expr(). m_shift(nested_q->get_expr(), nested_q->get_num_decls(), // bound for shift1/shift2 num_decls - nested_q->get_num_decls(), // shift1 (shift by this ammount if var idx >= bound) shift_amount, // shift2 (shift by this ammount if var idx < bound) adjusted_child); TRACE("pull_quant", tout << "shifted bound: " << nested_q->get_num_decls() << " shift1: " << shift_amount << " shift2: " << (num_decls - nested_q->get_num_decls()) << "\n" << mk_pp(nested_q->get_expr(), m_manager) << "\n---->\n" << mk_pp(adjusted_child, m_manager) << "\n";); shift_amount += nested_q->get_num_decls(); } new_adjusted_children.push_back(adjusted_child); } // Remark: patterns are ignored. // This is ok, since this functor is used in one of the following cases: // // 1) Superposition calculus is being used, so the // patterns are useless. // // 2) No patterns were provided, and the functor is used // to increase the effectiveness of the pattern inference // procedure. // // 3) MBQI std::reverse(var_sorts.begin(), var_sorts.end()); std::reverse(var_names.begin(), var_names.end()); result = m_manager.mk_quantifier(forall_children, var_sorts.size(), var_sorts.c_ptr(), var_names.c_ptr(), m_manager.mk_app(d, new_adjusted_children.size(), new_adjusted_children.c_ptr()), w, qid); return true; } else { SASSERT(!found_quantifier); return false; } } void pull_quant1(func_decl * d, unsigned num_children, expr * const * children, expr_ref & result) { if (!pull_quant1_core(d, num_children, children, result)) { result = m_manager.mk_app(d, num_children, children); } } void pull_quant1_core(quantifier * q, expr * new_expr, expr_ref & result) { // The original formula was in SNF, so the original quantifiers must be universal. SASSERT(is_forall(q)); SASSERT(is_forall(new_expr)); quantifier * nested_q = to_quantifier(new_expr); ptr_buffer var_sorts; buffer var_names; var_sorts.append(q->get_num_decls(), const_cast(q->get_decl_sorts())); var_sorts.append(nested_q->get_num_decls(), const_cast(nested_q->get_decl_sorts())); var_names.append(q->get_num_decls(), const_cast(q->get_decl_names())); var_names.append(nested_q->get_num_decls(), const_cast(nested_q->get_decl_names())); // Remark: patterns are ignored. // See comment in reduce1_app result = m_manager.mk_forall(var_sorts.size(), var_sorts.c_ptr(), var_names.c_ptr(), nested_q->get_expr(), std::min(q->get_weight(), nested_q->get_weight()), q->get_qid()); } void pull_quant1(quantifier * q, expr * new_expr, expr_ref & result) { // The original formula was in SNF, so the original quantifiers must be universal. SASSERT(is_forall(q)); if (is_forall(new_expr)) { pull_quant1_core(q, new_expr, result); } else { SASSERT(!is_quantifier(new_expr)); result = m_manager.update_quantifier(q, new_expr); } } void pull_quant1(expr * n, expr_ref & result) { if (is_app(n)) pull_quant1(to_app(n)->get_decl(), to_app(n)->get_num_args(), to_app(n)->get_args(), result); else if (is_quantifier(n)) pull_quant1(to_quantifier(n), to_quantifier(n)->get_expr(), result); else result = n; } // Code for proof generation... void pull_quant2(expr * n, expr_ref & r, proof_ref & pr) { pr = 0; if (is_app(n)) { expr_ref_buffer new_args(m_manager); expr_ref new_arg(m_manager); ptr_buffer proofs; unsigned num = to_app(n)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = to_app(n)->get_arg(i); pull_quant1(arg , new_arg); new_args.push_back(new_arg); if (new_arg != arg) proofs.push_back(m_manager.mk_pull_quant(arg, to_quantifier(new_arg))); } pull_quant1(to_app(n)->get_decl(), new_args.size(), new_args.c_ptr(), r); if (m_manager.fine_grain_proofs()) { app * r1 = m_manager.mk_app(to_app(n)->get_decl(), new_args.size(), new_args.c_ptr()); proof * p1 = proofs.empty() ? 0 : m_manager.mk_congruence(to_app(n), r1, proofs.size(), proofs.c_ptr()); proof * p2 = r1 == r ? 0 : m_manager.mk_pull_quant(r1, to_quantifier(r)); pr = m_manager.mk_transitivity(p1, p2); } } else if (is_quantifier(n)) { expr_ref new_expr(m_manager); pull_quant1(to_quantifier(n)->get_expr(), new_expr); pull_quant1(to_quantifier(n), new_expr, r); if (m_manager.fine_grain_proofs()) { quantifier * q1 = m_manager.update_quantifier(to_quantifier(n), new_expr); proof * p1 = 0; if (n != q1) { proof * p0 = m_manager.mk_pull_quant(to_quantifier(n)->get_expr(), to_quantifier(new_expr)); p1 = m_manager.mk_quant_intro(to_quantifier(n), q1, p0); } proof * p2 = q1 == r ? 0 : m_manager.mk_pull_quant(q1, to_quantifier(r)); pr = m_manager.mk_transitivity(p1, p2); } } else { r = n; } } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (!m_manager.is_or(f) && !m_manager.is_and(f) && !m_manager.is_not(f)) return BR_FAILED; if (!pull_quant1_core(f, num, args, result)) return BR_FAILED; if (m_manager.proofs_enabled()) { result_pr = m_manager.mk_pull_quant(m_manager.mk_app(f, num, args), to_quantifier(result.get())); } return BR_DONE; } bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { if (old_q->is_exists()) { UNREACHABLE(); return false; } if (!is_forall(new_body)) return false; pull_quant1_core(old_q, new_body, result); if (m_manager.proofs_enabled()) result_pr = m_manager.mk_pull_quant(old_q, to_quantifier(result.get())); return true; } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m) { } }; rw m_rw; imp(ast_manager & m): m_rw(m) { } void operator()(expr * n, expr_ref & r, proof_ref & p) { m_rw(n, r, p); } }; pull_quant::pull_quant(ast_manager & m) { m_imp = alloc(imp, m); } pull_quant::~pull_quant() { dealloc(m_imp); } void pull_quant::operator()(expr * n, expr_ref & r, proof_ref & p) { (*m_imp)(n, r, p); } void pull_quant::reset() { m_imp->m_rw.reset(); } void pull_quant::pull_quant2(expr * n, expr_ref & r, proof_ref & pr) { m_imp->m_rw.cfg().pull_quant2(n, r, pr); } struct pull_nested_quant::imp { struct rw_cfg : public default_rewriter_cfg { pull_quant m_pull; expr_ref m_r; proof_ref m_pr; rw_cfg(ast_manager & m):m_pull(m), m_r(m), m_pr(m) {} bool get_subst(expr * s, expr * & t, proof * & t_pr) { if (!is_quantifier(s)) return false; m_pull(to_quantifier(s), m_r, m_pr); t = m_r.get(); t_pr = m_pr.get(); return true; } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m) { } }; rw m_rw; imp(ast_manager & m): m_rw(m) { } void operator()(expr * n, expr_ref & r, proof_ref & p) { m_rw(n, r, p); } }; pull_nested_quant::pull_nested_quant(ast_manager & m) { m_imp = alloc(imp, m); } pull_nested_quant::~pull_nested_quant() { dealloc(m_imp); } void pull_nested_quant::operator()(expr * n, expr_ref & r, proof_ref & p) { (*m_imp)(n, r, p); } void pull_nested_quant::reset() { m_imp->m_rw.reset(); } z3-4.4.0/src/ast/used_vars.cpp0000644000175000017500000000551612540347414015731 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: used_vars.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-14. Revision History: --*/ #include"used_vars.h" void used_vars::process(expr * n, unsigned delta) { unsigned j, idx; m_cache.reset(); m_todo.reset(); m_todo.push_back(expr_delta_pair(n, delta)); while (!m_todo.empty()) { expr_delta_pair const & p = m_todo.back(); n = p.m_node; if (n->get_ref_count() > 1 && m_cache.contains(p)) { m_todo.pop_back(); continue; } if (n->get_ref_count() > 1) { // cache only shared and non-constant nodes m_cache.insert(p); } delta = p.m_delta; m_todo.pop_back(); switch (n->get_kind()) { case AST_APP: j = to_app(n)->get_num_args(); while (j > 0) { --j; expr * arg = to_app(n)->get_arg(j); m_todo.push_back(expr_delta_pair(arg, delta)); } break; case AST_VAR: idx = to_var(n)->get_idx(); if (idx >= delta) { idx = idx - delta; if (idx >= m_found_vars.size()) m_found_vars.resize(idx + 1, 0); m_found_vars[idx] = to_var(n)->get_sort(); } break; case AST_QUANTIFIER: // recurse so that memoization is correct with respect to 'delta'. delta += to_quantifier(n)->get_num_decls(); j = to_quantifier(n)->get_num_patterns(); while (j > 0) { --j; m_todo.push_back(expr_delta_pair(to_quantifier(n)->get_pattern(j), delta)); } j = to_quantifier(n)->get_num_no_patterns(); while (j > 0) { --j; m_todo.push_back(expr_delta_pair(to_quantifier(n)->get_no_pattern(j), delta)); } m_todo.push_back(expr_delta_pair(to_quantifier(n)->get_expr(), delta)); break; default: break; } } } bool used_vars::uses_all_vars(unsigned num_decls) const { if (num_decls > m_found_vars.size()) return false; for (unsigned i = 0; i < num_decls; i++) { if (!m_found_vars[i]) return false; } return true; } bool used_vars::uses_a_var(unsigned num_decls) const { num_decls = std::min(num_decls, m_found_vars.size()); for (unsigned i = 0; i < num_decls; i++) { if (m_found_vars[i]) return true; } return false; } unsigned used_vars::get_num_vars() const { unsigned r = 0; unsigned num = m_found_vars.size(); for (unsigned i = 0; i < num; i++) { if (m_found_vars[i]) r++; } return r; } z3-4.4.0/src/ast/has_free_vars.h0000644000175000017500000000047512540347414016211 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: has_free_vars.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-23. Revision History: --*/ #ifndef _HAS_FREE_VARS_H_ #define _HAS_FREE_VARS_H_ class expr; bool has_free_vars(expr * n); #endif /* _HAS_FREE_VARS_H_ */ z3-4.4.0/src/ast/simplifier/0000755000175000017500000000000012540347414015366 5ustar michaelmichaelz3-4.4.0/src/ast/simplifier/arith_simplifier_params_helper.pyg0000644000175000017500000000103212540347414024337 0ustar michaelmichaeldef_module_params(class_name='arith_simplifier_params_helper', module_name="old_simplify", # Parameters will be in the old_simplify module description="old simplification (stack) still used in the smt module", export=True, params=( ('arith.expand_eqs', BOOL, False, 'expand equalities into two inequalities'), ('arith.process_all_eqs', BOOL, False, 'put all equations in the form (= t c), where c is a numeral'))) z3-4.4.0/src/ast/simplifier/bit2int.cpp0000644000175000017500000003176112540347414017455 0ustar michaelmichael/*++ Copyright (c) 2009 Microsoft Corporation Module Name: bit2cpp.cpp Abstract: Routines for simplifying bit2int expressions. This propagates bv2int over arithmetical symbols as much as possible, converting arithmetic operations into bit-vector operations. Author: Nikolaj Bjorner (nbjorner) 2009-08-28 Revision History: --*/ #include "bit2int.h" #include "ast_pp.h" #include "ast_ll_pp.h" #include "for_each_ast.h" #define CHECK(_x_) if (!(_x_)) { UNREACHABLE(); } bit2int::bit2int(ast_manager & m) : m_manager(m), m_bv_util(m), m_arith_util(m), m_cache(m), m_bit0(m) { m_bit0 = m_bv_util.mk_numeral(0,1); } void bit2int::operator()(expr * m, expr_ref & result, proof_ref& p) { flush_cache(); expr_reduce emap(*this); for_each_ast(emap, m); result = get_cached(m); if (m_manager.proofs_enabled() && m != result.get()) { // TBD: rough p = m_manager.mk_rewrite(m, result); } TRACE("bit2int", tout << mk_pp(m, m_manager) << "======>\n" << mk_pp(result, m_manager) << "\n";); } unsigned bit2int::get_b2i_size(expr* n) { SASSERT(m_bv_util.is_bv2int(n)); return m_bv_util.get_bv_size(to_app(n)->get_arg(0)); } unsigned bit2int::get_numeral_bits(numeral const& k) { numeral two(2); numeral n(abs(k)); unsigned num_bits = 1; n = div(n, two); while (n.is_pos()) { ++num_bits; n = div(n, two); } return num_bits; } void bit2int::align_size(expr* e, unsigned sz, expr_ref& result) { unsigned sz1 = m_bv_util.get_bv_size(e); SASSERT(sz1 <= sz); m_bv_simplifier->mk_zeroext(sz-sz1, e, result); } void bit2int::align_sizes(expr_ref& a, expr_ref& b) { unsigned sz1 = m_bv_util.get_bv_size(a); unsigned sz2 = m_bv_util.get_bv_size(b); expr_ref tmp(m_manager); if (sz1 > sz2) { m_bv_simplifier->mk_zeroext(sz1-sz2, b, tmp); b = tmp; } else if (sz2 > sz1) { m_bv_simplifier->mk_zeroext(sz2-sz1, a, tmp); a = tmp; } } bool bit2int::extract_bv(expr* n, unsigned& sz, bool& sign, expr_ref& bv) { numeral k; bool is_int; if (m_bv_util.is_bv2int(n)) { bv = to_app(n)->get_arg(0); sz = m_bv_util.get_bv_size(bv); sign = false; return true; } else if (m_arith_util.is_numeral(n, k, is_int) && is_int) { sz = get_numeral_bits(k); bv = m_bv_util.mk_numeral(k, m_bv_util.mk_sort(sz)); sign = k.is_neg(); return true; } else { return false; } } bool bit2int::mk_add(expr* e1, expr* e2, expr_ref& result) { unsigned sz1, sz2; bool sign1, sign2; expr_ref tmp1(m_manager), tmp2(m_manager), tmp3(m_manager); if (extract_bv(e1, sz1, sign1, tmp1) && !sign1 && extract_bv(e2, sz2, sign2, tmp2) && !sign2) { unsigned sz; numeral k; if (m_bv_util.is_numeral(tmp1, k, sz) && k.is_zero()) { result = e2; return true; } if (m_bv_util.is_numeral(tmp2, k, sz) && k.is_zero()) { result = e1; return true; } align_sizes(tmp1, tmp2); m_bv_simplifier->mk_zeroext(1, tmp1, tmp1); m_bv_simplifier->mk_zeroext(1, tmp2, tmp2); SASSERT(m_bv_util.get_bv_size(tmp1) == m_bv_util.get_bv_size(tmp2)); m_bv_simplifier->mk_add(tmp1, tmp2, tmp3); m_bv_simplifier->mk_bv2int(tmp3, m_arith_util.mk_int(), result); return true; } return false; } bool bit2int::mk_comp(eq_type ty, expr* e1, expr* e2, expr_ref& result) { unsigned sz1, sz2; bool sign1, sign2; expr_ref tmp1(m_manager), tmp2(m_manager), tmp3(m_manager); if (extract_bv(e1, sz1, sign1, tmp1) && !sign1 && extract_bv(e2, sz2, sign2, tmp2) && !sign2) { align_sizes(tmp1, tmp2); SASSERT(m_bv_util.get_bv_size(tmp1) == m_bv_util.get_bv_size(tmp2)); switch(ty) { case lt: m_bv_simplifier->mk_leq_core(false, tmp2, tmp1, tmp3); result = m_manager.mk_not(tmp3); break; case le: m_bv_simplifier->mk_leq_core(false,tmp1, tmp2, result); break; case eq: result = m_manager.mk_eq(tmp1,tmp2); break; } return true; } return false; } bool bit2int::mk_mul(expr* e1, expr* e2, expr_ref& result) { unsigned sz1, sz2; bool sign1, sign2; expr_ref tmp1(m_manager), tmp2(m_manager); expr_ref tmp3(m_manager); if (extract_bv(e1, sz1, sign1, tmp1) && extract_bv(e2, sz2, sign2, tmp2)) { align_sizes(tmp1, tmp2); m_bv_simplifier->mk_zeroext(m_bv_util.get_bv_size(tmp1), tmp1, tmp1); m_bv_simplifier->mk_zeroext(m_bv_util.get_bv_size(tmp2), tmp2, tmp2); SASSERT(m_bv_util.get_bv_size(tmp1) == m_bv_util.get_bv_size(tmp2)); m_bv_simplifier->mk_mul(tmp1, tmp2, tmp3); m_bv_simplifier->mk_bv2int(tmp3, m_arith_util.mk_int(), result); if (sign1 != sign2) { result = m_arith_util.mk_uminus(result); } return true; } return false; } bool bit2int::is_bv_poly(expr* n, expr_ref& pos, expr_ref& neg) { ptr_vector todo; expr_ref tmp(m_manager); numeral k; bool is_int; todo.push_back(n); m_bv_simplifier->mk_bv2int(m_bit0, m_arith_util.mk_int(), pos); m_bv_simplifier->mk_bv2int(m_bit0, m_arith_util.mk_int(), neg); while (!todo.empty()) { n = todo.back(); todo.pop_back(); if (m_bv_util.is_bv2int(n)) { CHECK(mk_add(n, pos, pos)); } else if (m_arith_util.is_numeral(n, k, is_int) && is_int) { if (k.is_nonneg()) { CHECK(mk_add(n, pos, pos)); } else { tmp = m_arith_util.mk_numeral(-k, true); CHECK(mk_add(tmp, neg, neg)); } } else if (m_arith_util.is_add(n)) { for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) { todo.push_back(to_app(n)->get_arg(i)); } } else if (m_arith_util.is_mul(n) && to_app(n)->get_num_args() == 2 && m_arith_util.is_numeral(to_app(n)->get_arg(0), k, is_int) && is_int && k.is_minus_one() && m_bv_util.is_bv2int(to_app(n)->get_arg(1))) { CHECK(mk_add(to_app(n)->get_arg(1), neg, neg)); } else if (m_arith_util.is_mul(n) && to_app(n)->get_num_args() == 2 && m_arith_util.is_numeral(to_app(n)->get_arg(1), k, is_int) && is_int && k.is_minus_one() && m_bv_util.is_bv2int(to_app(n)->get_arg(0))) { CHECK(mk_add(to_app(n)->get_arg(0), neg, neg)); } else if (m_arith_util.is_uminus(n) && m_bv_util.is_bv2int(to_app(n)->get_arg(0))) { CHECK(mk_add(to_app(n)->get_arg(0), neg, neg)); } else { TRACE("bit2int", tout << "Not a poly: " << mk_pp(n, m_manager) << "\n";); return false; } } return true; } void bit2int::visit(quantifier* q) { expr_ref result(m_manager); result = get_cached(q->get_expr()); result = m_manager.update_quantifier(q, result); cache_result(q, result); } void bit2int::visit(app* n) { func_decl* f = n->get_decl(); unsigned num_args = n->get_num_args(); m_args.reset(); for (unsigned i = 0; i < num_args; ++i) { m_args.push_back(get_cached(n->get_arg(i))); } expr* const* args = m_args.c_ptr(); bool has_b2i = m_arith_util.is_le(n) || m_arith_util.is_ge(n) || m_arith_util.is_gt(n) || m_arith_util.is_lt(n) || m_manager.is_eq(n); expr_ref result(m_manager); for (unsigned i = 0; !has_b2i && i < num_args; ++i) { has_b2i = m_bv_util.is_bv2int(args[i]); } if (!has_b2i) { result = m_manager.mk_app(f, num_args, args); cache_result(n, result); return; } // // bv2int(x) + bv2int(y) -> bv2int(pad(x) + pad(y)) // bv2int(x) + k -> bv2int(pad(x) + pad(k)) // bv2int(x) * bv2int(y) -> bv2int(pad(x) * pad(y)) // bv2int(x) * k -> sign(k)*bv2int(pad(x) * pad(k)) // bv2int(x) - bv2int(y) <= z -> bv2int(x) <= bv2int(y) + z // bv2int(x) <= z - bv2int(y) -> bv2int(x) + bv2int(y) <= z // expr* e1, *e2; expr_ref tmp1(m_manager), tmp2(m_manager); expr_ref tmp3(m_manager); expr_ref pos1(m_manager), neg1(m_manager); expr_ref pos2(m_manager), neg2(m_manager); expr_ref e2bv(m_manager); bool sign2; numeral k; unsigned sz2; if (num_args >= 2) { e1 = args[0]; e2 = args[1]; } if (m_arith_util.is_add(n) && num_args >= 1) { result = e1; for (unsigned i = 1; i < num_args; ++i) { e1 = result; e2 = args[i]; if (!mk_add(e1, e2, result)) { result = m_manager.mk_app(f, num_args, args); cache_result(n, result); return; } } cache_result(n, result); } else if (m_arith_util.is_mul(n) && num_args >= 1) { result = e1; for (unsigned i = 1; i < num_args; ++i) { e1 = result; e2 = args[i]; if (!mk_mul(e1, e2, result)) { result = m_manager.mk_app(f, num_args, args); cache_result(n, result); return; } } cache_result(n, result); } else if (m_manager.is_eq(n) && is_bv_poly(e1, pos1, neg1) && is_bv_poly(e2, pos2, neg2) && mk_add(pos1, neg2, tmp1) && mk_add(neg1, pos2, tmp2) && mk_comp(eq, tmp1, tmp2, result)) { cache_result(n, result); } else if (m_arith_util.is_le(n) && is_bv_poly(e1, pos1, neg1) && is_bv_poly(e2, pos2, neg2) && mk_add(pos1, neg2, tmp1) && mk_add(neg1, pos2, tmp2) && mk_comp(le, tmp1, tmp2, result)) { cache_result(n, result); } else if (m_arith_util.is_lt(n) && is_bv_poly(e1, pos1, neg1) && is_bv_poly(e2, pos2, neg2) && mk_add(pos1, neg2, tmp1) && mk_add(neg1, pos2, tmp2) && mk_comp(lt, tmp1, tmp2, result)) { cache_result(n, result); } else if (m_arith_util.is_ge(n) && is_bv_poly(e1, pos1, neg1) && is_bv_poly(e2, pos2, neg2) && mk_add(pos1, neg2, tmp1) && mk_add(neg1, pos2, tmp2) && mk_comp(le, tmp2, tmp1, result)) { cache_result(n, result); } else if (m_arith_util.is_gt(n) && is_bv_poly(e1, pos1, neg1) && is_bv_poly(e2, pos2, neg2) && mk_add(pos1, neg2, tmp1) && mk_add(neg1, pos2, tmp2) && mk_comp(lt, tmp2, tmp1, result)) { cache_result(n, result); } else if (m_arith_util.is_mod(n) && is_bv_poly(e1, pos1, neg1) && extract_bv(e2, sz2, sign2, e2bv) && !sign2) { // // (pos1 - neg1) mod e2 = (pos1 + (e2 - (neg1 mod e2))) mod e2 // unsigned sz_p, sz_n, sz; bool sign_p, sign_n; expr_ref tmp_p(m_manager), tmp_n(m_manager); CHECK(extract_bv(pos1, sz_p, sign_p, tmp_p)); CHECK(extract_bv(neg1, sz_n, sign_n, tmp_n)); SASSERT(!sign_p && !sign_n); // pos1 mod e2 if (m_bv_util.is_numeral(tmp_n, k, sz) && k.is_zero()) { tmp1 = tmp_p; tmp2 = e2bv; align_sizes(tmp1, tmp2); m_bv_simplifier->mk_bv_urem(tmp1, tmp2, tmp3); m_bv_simplifier->mk_bv2int(tmp3, m_arith_util.mk_int(), result); cache_result(n, result); return; } // neg1 mod e2; tmp1 = tmp_n; tmp2 = e2bv; align_sizes(tmp1, tmp2); m_bv_simplifier->mk_bv_urem(tmp1, tmp2, tmp3); // e2 - (neg1 mod e2) tmp1 = e2bv; tmp2 = tmp3; align_sizes(tmp1, tmp2); m_bv_simplifier->mk_sub(tmp1, tmp2, tmp3); // pos1 + (e2 - (neg1 mod e2)) tmp1 = tmp_p; tmp2 = tmp3; align_sizes(tmp1, tmp2); m_bv_simplifier->mk_zeroext(1, tmp1, tmp_p); m_bv_simplifier->mk_zeroext(1, tmp2, tmp_n); m_bv_simplifier->mk_add(tmp_p, tmp_n, tmp1); // (pos1 + (e2 - (neg1 mod e2))) mod e2 tmp2 = e2bv; align_sizes(tmp1, tmp2); m_bv_simplifier->mk_bv_urem(tmp1, tmp2, tmp3); m_bv_simplifier->mk_bv2int(tmp3, m_arith_util.mk_int(), result); cache_result(n, result); } else { result = m_manager.mk_app(f, num_args, args); cache_result(n, result); } } expr * bit2int::get_cached(expr * n) const { return const_cast(this)->m_cache.find(n); } void bit2int::cache_result(expr * n, expr * r) { TRACE("bit2int_verbose", tout << "caching:\n" << mk_ll_pp(n, m_manager) << "======>\n" << mk_ll_pp(r, m_manager) << "\n";); m_cache.insert(n, r); } z3-4.4.0/src/ast/simplifier/array_simplifier_params.cpp0000644000175000017500000000101712540347414022775 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: array_simplifier_params.cpp Abstract: This file was created during code reorg. Author: Leonardo de Moura (leonardo) 2012-12-02. Revision History: --*/ #include"array_simplifier_params.h" #include"array_simplifier_params_helper.hpp" void array_simplifier_params::updt_params(params_ref const & _p) { array_simplifier_params_helper p(_p); m_array_canonize_simplify = p.array_canonize(); m_array_simplify = p.array_simplify(); } z3-4.4.0/src/ast/simplifier/elim_bounds.cpp0000644000175000017500000001375312540347414020403 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: elim_bounds.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-28. Revision History: --*/ #include"elim_bounds.h" #include"used_vars.h" #include"obj_hashtable.h" #include"var_subst.h" #include"ast_pp.h" elim_bounds::elim_bounds(ast_manager & m): m_manager(m), m_util(m) { } /** \brief Find bounds of the form (<= x k) (<= (+ x (* -1 y)) k) (<= (+ x (* -1 t)) k) (<= (+ t (* -1 x)) k) x and y are a bound variables, t is a ground term and k is a numeral It also detects >=, and the atom can be negated. */ bool elim_bounds::is_bound(expr * n, var * & lower, var * & upper) { upper = 0; lower = 0; bool neg = false; if (m_manager.is_not(n)) { n = to_app(n)->get_arg(0); neg = true; } bool le = false; if (m_util.is_le(n)) { SASSERT(m_util.is_numeral(to_app(n)->get_arg(1))); n = to_app(n)->get_arg(0); le = true; } else if (m_util.is_ge(n)) { SASSERT(m_util.is_numeral(to_app(n)->get_arg(1))); n = to_app(n)->get_arg(0); le = false; } else { return false; } if (neg) le = !le; if (is_var(n)) { upper = to_var(n); } else if (m_util.is_add(n) && to_app(n)->get_num_args() == 2) { expr * arg1 = to_app(n)->get_arg(0); expr * arg2 = to_app(n)->get_arg(1); if (is_var(arg1)) upper = to_var(arg1); else if (!is_ground(arg1)) return false; rational k; bool is_int; if (m_util.is_mul(arg2) && m_util.is_numeral(to_app(arg2)->get_arg(0), k, is_int) && k.is_minus_one()) { arg2 = to_app(arg2)->get_arg(1); if (is_var(arg2)) lower = to_var(arg2); else if (!is_ground(arg2)) return false; // not supported } else { return false; // not supported } } else { return false; } if (!le) std::swap(upper, lower); return true; } bool elim_bounds::is_bound(expr * n) { var * lower, * upper; return is_bound(n, lower, upper); } void elim_bounds::operator()(quantifier * q, expr_ref & r) { if (!q->is_forall()) { r = q; return; } expr * n = q->get_expr(); ptr_buffer atoms; if (m_manager.is_or(n)) atoms.append(to_app(n)->get_num_args(), to_app(n)->get_args()); else atoms.push_back(n); used_vars m_used_vars; // collect non-candidates unsigned sz = atoms.size(); for (unsigned i = 0; i < sz; i++) { expr * a = atoms[i]; if (!is_bound(a)) m_used_vars.process(a); } if (m_used_vars.uses_all_vars(q->get_num_decls())) { r = q; return; } // collect candidates obj_hashtable m_lowers; obj_hashtable m_uppers; obj_hashtable m_candidate_set; ptr_buffer m_candidates; #define ADD_CANDIDATE(V) if (!m_lowers.contains(V) && !m_uppers.contains(V)) { m_candidate_set.insert(V); m_candidates.push_back(V); } for (unsigned i = 0; i < sz; i++) { expr * a = atoms[i]; var * lower = 0; var * upper = 0; if (is_bound(a, lower, upper)) { if (lower != 0 && !m_used_vars.contains(lower->get_idx())) { ADD_CANDIDATE(lower); m_lowers.insert(lower); } if (upper != 0 && !m_used_vars.contains(upper->get_idx())) { ADD_CANDIDATE(upper); m_uppers.insert(upper); } } } TRACE("elim_bounds", tout << "candidates:\n"; for (unsigned i = 0; i < m_candidates.size(); i++) tout << mk_pp(m_candidates[i], m_manager) << "\n";); // remove candidates that have lower and upper bounds for (unsigned i = 0; i < m_candidates.size(); i++) { var * v = m_candidates[i]; if (m_lowers.contains(v) && m_uppers.contains(v)) m_candidate_set.erase(v); } TRACE("elim_bounds", tout << "candidates after filter:\n"; for (unsigned i = 0; i < m_candidates.size(); i++) tout << mk_pp(m_candidates[i], m_manager) << "\n";); if (m_candidate_set.empty()) { r = q; return; } // remove bounds that contain variables in m_candidate_set unsigned j = 0; for (unsigned i = 0; i < sz; i++) { expr * a = atoms[i]; var * lower = 0; var * upper = 0; if (is_bound(a, lower, upper) && ((lower != 0 && m_candidate_set.contains(lower)) || (upper != 0 && m_candidate_set.contains(upper)))) continue; atoms[j] = a; j++; } atoms.resize(j); expr * new_body = 0; switch (atoms.size()) { case 0: r = m_manager.mk_false(); return; case 1: new_body = atoms[0]; break; default: new_body = m_manager.mk_or(atoms.size(), atoms.c_ptr()); break; } quantifier_ref new_q(m_manager); new_q = m_manager.update_quantifier(q, new_body); elim_unused_vars(m_manager, new_q, r); TRACE("elim_bounds", tout << mk_pp(q, m_manager) << "\n" << mk_pp(r, m_manager) << "\n";); } bool elim_bounds_star::visit_quantifier(quantifier * q) { if (!q->is_forall() || q->get_num_patterns() != 0) return true; bool visited = true; visit(q->get_expr(), visited); return visited; } void elim_bounds_star::reduce1_quantifier(quantifier * q) { if (!q->is_forall() || q->get_num_patterns() != 0) { cache_result(q, q, 0); return; } quantifier_ref new_q(m); expr * new_body = 0; proof * new_pr; get_cached(q->get_expr(), new_body, new_pr); new_q = m.update_quantifier(q, new_body); expr_ref r(m); m_elim(new_q, r); if (q == r.get()) { cache_result(q, q, 0); return; } proof_ref pr(m); if (m.fine_grain_proofs()) pr = m.mk_rewrite(q, r); // TODO: improve justification cache_result(q, r, pr); } z3-4.4.0/src/ast/simplifier/simplifier_plugin.h0000644000175000017500000000534612540347414021270 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: simplifier_plugin.h Abstract: Expression simplifier plugin interface. Author: Leonardo (leonardo) 2008-01-03 --*/ #ifndef __SIMPLIFIER_PLUGIN_H__ #define __SIMPLIFIER_PLUGIN_H__ #include"ast.h" class simplifier; void expand_args(unsigned num_args, rational const * mults, expr * const * args, ptr_buffer & new_args); /** \brief Abstract simplifier for the operators in a given family. */ class simplifier_plugin { protected: ast_manager & m_manager; family_id m_fid; bool m_presimp; // true if simplifier is performing pre-simplification... bool m_reduce_invoked; // true if one of the reduce methods were invoked. void set_reduce_invoked() { m_reduce_invoked = true; } public: simplifier_plugin(symbol const & fname, ast_manager & m):m_manager(m), m_fid(m.mk_family_id(fname)), m_presimp(false), m_reduce_invoked(false) {} bool reduce_invoked() const { return m_reduce_invoked; } virtual ~simplifier_plugin() {} virtual simplifier_plugin * mk_fresh() { UNREACHABLE(); return 0; } /** \brief Return in \c result an expression \c e equivalent to (f args[0] ... args[num_args - 1]). Return true if succeeded. */ virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { set_reduce_invoked(); return false; } /** \brief Return in \c result an expression \c e equivalent to (f args[0] ... args[0] ... args[num_args - 1]). Where each args[i] occurs mults[i] times. Return true if succeeded. */ virtual bool reduce(func_decl * f, unsigned num_args, rational const * mults, expr * const * args, expr_ref & result); /** \brief Return in \c result an expression \c e equivalent to (= lhs rhs). Return true if succeeded. */ virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result) { set_reduce_invoked(); return false; } /** \brief Return in \c result an expression \c e equivalent to (distinct args[0] ... args[num_args-1]). Return true if succeeded. */ virtual bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result) { set_reduce_invoked(); return false; } family_id get_family_id() const { return m_fid; } /** \brief Simplifiers may maintain local caches. These caches must be flushed when this method is invoked. */ virtual void flush_caches() { /* do nothing */ } ast_manager & get_manager() { return m_manager; } void enable_presimp(bool flag) { m_presimp = flag; } virtual void enable_ac_support(bool flag) {} }; #endif z3-4.4.0/src/ast/simplifier/array_simplifier_plugin.h0000644000175000017500000001320112540347414022453 0ustar michaelmichael/*++ Copyright (c) 2008 Microsoft Corporation Module Name: array_simplifier_plugin.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2008-05-05 Revision History: --*/ #ifndef _ARRAY_SIMPLIFIER_PLUGIN_H_ #define _ARRAY_SIMPLIFIER_PLUGIN_H_ #include"ast.h" #include"map.h" #include"array_decl_plugin.h" #include"simplifier_plugin.h" #include"basic_simplifier_plugin.h" #include"array_simplifier_params.h" #include"simplifier.h" #include"obj_hashtable.h" #include"lbool.h" class array_simplifier_plugin : public simplifier_plugin { typedef ptr_vector entry; struct entry_hash_proc { unsigned operator()(ptr_vector * entry) const { return get_exprs_hash(entry->size(), entry->begin(), 0xbeef1010); } }; struct entry_eq_proc { bool operator()(ptr_vector * entry1, ptr_vector * entry2) const { if (entry1->size() != entry2->size()) return false; return compare_arrays(entry1->begin(), entry2->begin(), entry1->size()); } }; typedef map select_cache; struct args_entry { unsigned m_arity; expr* const* m_args; args_entry(unsigned a, expr* const* args) : m_arity(a), m_args(args) {} args_entry() : m_arity(0), m_args(0) {} }; struct args_entry_hash_proc { unsigned operator()(args_entry const& e) const { return get_exprs_hash(e.m_arity, e.m_args, 0xbeef1010); } }; struct args_entry_eq_proc { bool operator()(args_entry const& e1, args_entry const& e2) const { if (e1.m_arity != e2.m_arity) return false; return compare_arrays(e1.m_args, e2.m_args, e1.m_arity); } }; typedef hashtable arg_table; array_util m_util; basic_simplifier_plugin& m_simp; simplifier& m_simplifier; array_simplifier_params const& m_params; select_cache m_select_cache; ptr_vector m_tmp; ptr_vector m_tmp2; ptr_vector m_todo; static const unsigned m_select_cache_max_size = 100000; typedef obj_map const_map; class store_info { store_info(); store_info(store_info const&); public: const_map m_map; expr_ref m_default; store_info(ast_manager& m, expr* d): m_default(d, m) {} }; typedef obj_map store_cache; store_cache m_store_cache; unsigned m_store_cache_size; static const unsigned m_store_cache_max_size = 10000; static const unsigned m_const_store_threshold = 5; enum const_select_result { NOT_CACHED, FOUND_DEFAULT, FOUND_VALUE }; public: array_simplifier_plugin(ast_manager & m, basic_simplifier_plugin& s, simplifier& simp, array_simplifier_params const& p); virtual ~array_simplifier_plugin(); virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result); virtual bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result); virtual void flush_caches(); private: bool is_select(expr* n) const { return m_util.is_select(n); } bool is_store(expr * n) const { return m_util.is_store(n); } bool is_const_array(expr * n) const { return m_util.is_const(n); } bool is_as_array(expr * n) const { return m_util.is_as_array(n); } bool is_as_array_tree(expr * n) { return m_util.is_as_array_tree(n); } func_decl * get_as_array_func_decl(app * n) const { return m_util.get_as_array_func_decl(n); } void mk_select_as_array(unsigned num_args, expr * const * args, expr_ref & result); void mk_select_as_array_tree(unsigned num_args, expr * const * args, expr_ref & result); bool is_enumerated(expr* n, expr_ref& c, ptr_vector& keys, ptr_vector& vals); const_select_result mk_select_const(expr* m, app* index, expr_ref& result); void cache_store(unsigned num_stores, expr* nested_store); void cache_select(unsigned num_args, expr * const * args, expr * result); void prune_select_cache(); void prune_store_cache(); void flush_select_cache(); void flush_store_cache(); void mk_set_difference(unsigned num_args, expr * const * args, expr_ref & result); void mk_empty_set(sort* ty, expr_ref & result); void mk_full_set(sort* ty, expr_ref & result); void mk_select(unsigned num_args, expr * const * args, expr_ref & result); void mk_store(func_decl* f, unsigned num_args, expr * const * args, expr_ref & result); void mk_map(func_decl* f, expr* a, expr* b, expr_ref & result); void mk_map(func_decl* f, expr* a, expr_ref & result); bool same_args(unsigned num_args, expr * const * args1, expr * const * args2); void get_stores(expr* n, unsigned& arity, expr*& m, ptr_vector& stores); lbool eq_default(expr* def, unsigned arity, unsigned num_st, expr*const* const* st); bool insert_table(expr* def, unsigned arity, unsigned num_st, expr*const* const* st, arg_table& table); lbool eq_stores(expr* def, unsigned arity, unsigned num_st1, expr*const* const* st1, unsigned num_st2, expr*const* const* st2); bool same_store(unsigned num_args, expr* const* args) const; bool all_const_array(unsigned num_args, expr* const* args) const; bool all_values(unsigned num_args, expr* const* args) const; bool lex_lt(unsigned num_args, expr* const* args1, expr* const* args2); }; #endif /* _ARRAY_SIMPLIFIER_PLUGIN_H_ */ z3-4.4.0/src/ast/simplifier/poly_simplifier_plugin.h0000644000175000017500000001400312540347414022321 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: poly_simplifier_plugin.h Abstract: Abstract class for families that have polynomials. Author: Leonardo (leonardo) 2008-01-08 --*/ #ifndef _POLY_SIMPLIFIER_PLUGIN_H_ #define _POLY_SIMPLIFIER_PLUGIN_H_ #include "simplifier_plugin.h" /** \brief Abstract class that provides simplification functions for polynomials. */ class poly_simplifier_plugin : public simplifier_plugin { protected: typedef rational numeral; decl_kind m_ADD; decl_kind m_MUL; decl_kind m_SUB; decl_kind m_UMINUS; decl_kind m_NUM; sort * m_curr_sort; expr * m_curr_sort_zero; expr * mk_add(unsigned num_args, expr * const * args); expr * mk_add(expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_add(2, args); } expr * mk_mul(unsigned num_args, expr * const * args); expr * mk_mul(expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_mul(2, args); } expr * mk_sub(unsigned num_args, expr * const * args) { return m_manager.mk_app(m_fid, m_SUB, num_args, args); } expr * mk_uminus(expr * arg) { return m_manager.mk_app(m_fid, m_UMINUS, arg); } void process_monomial(unsigned num_args, expr * const * args, numeral & k, ptr_buffer & result); void mk_monomial(unsigned num_args, expr * * args, expr_ref & result); bool eq_monomials_modulo_k(expr * n1, expr * n2); bool merge_monomials(bool inv, expr * n1, expr * n2, expr_ref & result); template void add_monomial_core(expr * n, expr_ref_vector & result); void add_monomial(bool inv, expr * n, expr_ref_vector & result); template void process_sum_of_monomials_core(expr * n, expr_ref_vector & result); void process_sum_of_monomials(bool inv, expr * n, expr_ref_vector & result); void process_sum_of_monomials(bool inv, expr * n, expr_ref_vector & result, numeral & k); void mk_sum_of_monomials(expr_ref_vector & monomials, expr_ref & result); template void mk_add_core_core(unsigned num_args, expr * const * args, expr_ref & result); void mk_add_core(bool inv, unsigned num_args, expr * const * args, expr_ref & result); void append_to_monomial(expr * n, numeral & k, ptr_buffer & result); expr * mk_mul(numeral const & c, expr * body); void mk_sum_of_monomials_core(unsigned sz, expr ** ms, expr_ref & result); bool is_simple_sum_of_monomials(expr_ref_vector & monomials); bool is_simple_monomial(expr * m, expr * & x); public: poly_simplifier_plugin(symbol const & fname, ast_manager & m, decl_kind add, decl_kind mul, decl_kind uminus, decl_kind sub, decl_kind num); virtual ~poly_simplifier_plugin() {} /** \brief Return true if the given expression is a numeral, and store its value in \c val. */ virtual bool is_numeral(expr * n, numeral & val) const = 0; bool is_numeral(expr * n) const { return is_app_of(n, m_fid, m_NUM); } bool is_zero(expr * n) const { SASSERT(m_curr_sort_zero != 0); SASSERT(m_manager.get_sort(n) == m_manager.get_sort(m_curr_sort_zero)); return n == m_curr_sort_zero; } bool is_zero_safe(expr * n) { set_curr_sort(m_manager.get_sort(n)); return is_zero(n); } virtual bool is_minus_one(expr * n) const = 0; virtual expr * get_zero(sort * s) const = 0; /** \brief Return true if n is of the form (* -1 r) */ bool is_times_minus_one(expr * n, expr * & r) const { if (is_mul(n) && to_app(n)->get_num_args() == 2 && is_minus_one(to_app(n)->get_arg(0))) { r = to_app(n)->get_arg(1); return true; } return false; } /** \brief Return true if n is of the form: a <= b or a >= b. */ virtual bool is_le_ge(expr * n) const = 0; /** \brief Return a constant representing the giving numeral and sort m_curr_sort. */ virtual app * mk_numeral(numeral const & n) = 0; app * mk_zero() { return mk_numeral(numeral::zero()); } app * mk_one() { return mk_numeral(numeral::one()); } app * mk_minus_one() { return mk_numeral(numeral::minus_one()); } /** \brief Normalize the given numeral with respect to m_curr_sort */ virtual numeral norm(numeral const & n) = 0; void set_curr_sort(sort * s) { if (s != m_curr_sort) { // avoid virtual function call m_curr_sort = s; m_curr_sort_zero = get_zero(m_curr_sort); } } void set_curr_sort(expr * n) { set_curr_sort(m_manager.get_sort(n)); } bool is_add(expr const * n) const { return is_app_of(n, m_fid, m_ADD); } bool is_mul(expr const * n) const { return is_app_of(n, m_fid, m_MUL); } void mk_add(unsigned num_args, expr * const * args, expr_ref & result); void mk_add(expr * arg1, expr * arg2, expr_ref & result); void mk_sub(unsigned num_args, expr * const * args, expr_ref & result); void mk_sub(expr * arg1, expr * arg2, expr_ref & result); void mk_uminus(expr * arg, expr_ref & result); void mk_mul(unsigned num_args, expr * const * args, expr_ref & result); void mk_mul(expr * arg1, expr * arg2, expr_ref & result); virtual bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result); virtual bool reduce(func_decl * f, unsigned num_args, rational const * mults, expr * const * args, expr_ref & result); virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { return simplifier_plugin::reduce(f, num_args, args, result); } expr * get_monomial_body(expr * m); int get_monomial_body_order(expr * m); void get_monomial_coeff(expr * m, numeral & result); void inv_monomial(expr * n, expr_ref & result); bool is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t); #ifdef Z3DEBUG bool wf_monomial(expr * m) const; bool wf_polynomial(expr * m) const; #endif }; #endif /* _POLY_SIMPLIFIER_PLUGIN_H_ */ z3-4.4.0/src/ast/simplifier/bv_simplifier_params.cpp0000644000175000017500000000071612540347414022273 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bv_simplifier_params.cpp Abstract: Author: Leonardo de Moura (leonardo) 2012-12-02. Revision History: --*/ #include"bv_simplifier_params.h" #include"bv_simplifier_params_helper.hpp" void bv_simplifier_params::updt_params(params_ref const & _p) { bv_simplifier_params_helper p(_p); m_hi_div0 = p.bv_hi_div0(); m_bv2int_distribute = p.bv_bv2int_distribute(); } z3-4.4.0/src/ast/simplifier/bv_elim.h0000644000175000017500000000151012540347414017151 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bv_elim.h Abstract: Eliminate bit-vectors variables from clauses, by replacing them by bound Boolean variables. Author: Nikolaj Bjorner (nbjorner) 2008-12-16. Revision History: --*/ #ifndef _BV_ELIM_H_ #define _BV_ELIM_H_ #include "ast.h" #include "simplifier.h" class bv_elim { ast_manager& m_manager; public: bv_elim(ast_manager& m) : m_manager(m) {}; void elim(quantifier* q, quantifier_ref& r); }; class bv_elim_star : public simplifier { protected: bv_elim m_bv_elim; virtual bool visit_quantifier(quantifier* q); virtual void reduce1_quantifier(quantifier* q); public: bv_elim_star(ast_manager& m) : simplifier(m), m_bv_elim(m) { enable_ac_support(false); } virtual ~bv_elim_star() {} }; #endif /* _BV_ELIM_H_ */ z3-4.4.0/src/ast/simplifier/bv_simplifier_params.h0000644000175000017500000000132312540347414021733 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bv_simplifier_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-10-10. Revision History: --*/ #ifndef _BV_SIMPLIFIER_PARAMS_H_ #define _BV_SIMPLIFIER_PARAMS_H_ #include"params.h" struct bv_simplifier_params { bool m_hi_div0; //!< if true, uses the hardware interpretation for div0, mod0, ... if false, div0, mod0, ... are considered uninterpreted. bool m_bv2int_distribute; //!< if true allows downward propagation of bv2int. bv_simplifier_params(params_ref const & p = params_ref()) { updt_params(p); } void updt_params(params_ref const & _p); }; #endif /* _BV_SIMPLIFIER_PARAMS_H_ */ z3-4.4.0/src/ast/simplifier/pull_ite_tree.cpp0000644000175000017500000001407112540347414020731 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pull_ite_tree.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-22. Revision History: --*/ #include"pull_ite_tree.h" #include"recurse_expr_def.h" #include"for_each_expr.h" #include"ast_pp.h" pull_ite_tree::pull_ite_tree(ast_manager & m, simplifier & s): m_manager(m), m_simplifier(s), m_cache(m) { } void pull_ite_tree::cache_result(expr * n, expr * r, proof * pr) { m_cache.insert(n, r, pr); } void pull_ite_tree::visit(expr * n, bool & visited) { if (!is_cached(n)) { m_todo.push_back(n); visited = false; } } bool pull_ite_tree::visit_children(expr * n) { if (m_manager.is_ite(n)) { bool visited = true; visit(to_app(n)->get_arg(1), visited); visit(to_app(n)->get_arg(2), visited); return visited; } else { return true; } } void pull_ite_tree::reduce(expr * n) { // Remark: invoking the simplifier to build the new expression saves a lot of memory. if (m_manager.is_ite(n)) { expr * c = to_app(n)->get_arg(0); expr * t_old = to_app(n)->get_arg(1); expr * e_old = to_app(n)->get_arg(2); expr * t = 0; proof * t_pr = 0; expr * e = 0; proof * e_pr = 0; get_cached(t_old, t, t_pr); get_cached(e_old, e, e_pr); expr_ref r(m_manager); expr * args[3] = {c, t, e}; m_simplifier.mk_app(to_app(n)->get_decl(), 3, args, r); if (!m_manager.proofs_enabled()) { // expr * r = m_manager.mk_ite(c, t, e); cache_result(n, r, 0); } else { // t_pr is a proof for (m_p ... t_old ...) == t // e_pr is a proof for (m_p ... e_old ...) == e expr_ref old(m_manager); expr_ref p_t_old(m_manager); expr_ref p_e_old(m_manager); old = mk_p_arg(n); // (m_p ... n ...) where n is (ite c t_old e_old) p_t_old = mk_p_arg(t_old); // (m_p ... t_old ...) p_e_old = mk_p_arg(e_old); // (m_p ... e_old ...) expr_ref tmp1(m_manager); tmp1 = m_manager.mk_ite(c, p_t_old, p_e_old); // (ite c (m_p ... t_old ...) (m_p ... e_old ...)) proof * pr1 = m_manager.mk_rewrite(old, tmp1); // proof for (m_p ... (ite c t_old e_old) ...) = (ite c (m_p ... t_old ...) (m_p ... e_old ...)) expr_ref tmp2(m_manager); tmp2 = m_manager.mk_ite(c, t, e); // (ite c t e) proof * pr2 = 0; // it will contain a proof for (ite c (m_p ... t_old ...) (m_p ... e_old ...)) = (ite c t e) proof * pr3 = 0; // it will contain a proof for (m_p ... (ite c t_old e_old) ...) = (ite c t e) proof * proofs[2]; unsigned num_proofs = 0; if (t_pr != 0) { proofs[num_proofs] = t_pr; num_proofs++; } if (e_pr != 0) { proofs[num_proofs] = e_pr; num_proofs++; } if (num_proofs > 0) { pr2 = m_manager.mk_congruence(to_app(tmp1), to_app(tmp2), num_proofs, proofs); pr3 = m_manager.mk_transitivity(pr1, pr2); } else { pr3 = pr1; } proof * pr4 = 0; // it will contain a proof for (ite c t e) = r proof * pr5 = 0; // it will contain a proof for (m_p ... (ite c t_old e_old) ...) = r if (tmp2 != r) { pr4 = m_manager.mk_rewrite(tmp2, r); pr5 = m_manager.mk_transitivity(pr3, pr4); } else { pr5 = pr3; } cache_result(n, r, pr5); } } else { expr_ref r(m_manager); m_args[m_arg_idx] = n; m_simplifier.mk_app(m_p, m_args.size(), m_args.c_ptr(), r); if (!m_manager.proofs_enabled()) { // expr * r = m_manager.mk_app(m_p, m_args.size(), m_args.c_ptr()); cache_result(n, r, 0); } else { expr_ref old(m_manager); proof * p; old = mk_p_arg(n); if (old == r) p = 0; else p = m_manager.mk_rewrite(old, r); cache_result(n, r, p); } } } void pull_ite_tree::operator()(app * n, app_ref & r, proof_ref & pr) { unsigned num_args = n->get_num_args(); m_args.resize(num_args); m_p = n->get_decl(); expr * ite = 0; for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (ite) { m_args[i] = arg; } else if (m_manager.is_ite(arg)) { m_arg_idx = i; m_args[i] = 0; ite = arg; } else { m_args[i] = arg; } } if (!ite) { r = n; pr = 0; return; } m_todo.push_back(ite); while (!m_todo.empty()) { expr * n = m_todo.back(); if (is_cached(n)) m_todo.pop_back(); else if (visit_children(n)) { m_todo.pop_back(); reduce(n); } } SASSERT(is_cached(ite)); expr * _r = 0; proof * _pr = 0; get_cached(ite, _r, _pr); r = to_app(_r); pr = _pr; m_cache.reset(); m_todo.reset(); } pull_ite_tree_star::pull_ite_tree_star(ast_manager & m, simplifier & s): simplifier(m), m_proc(m, s) { borrow_plugins(s); } bool pull_ite_tree_star::get_subst(expr * n, expr_ref & r, proof_ref & p) { if (is_app(n) && is_target(to_app(n))) { app_ref tmp(m); m_proc(to_app(n), tmp, p); r = tmp; return true; } return false; } bool pull_cheap_ite_tree_star::is_target(app * n) const { bool r = n->get_num_args() == 2 && n->get_family_id() != null_family_id && m.is_bool(n) && (m.is_value(n->get_arg(0)) || m.is_value(n->get_arg(1))) && (m.is_term_ite(n->get_arg(0)) || m.is_term_ite(n->get_arg(1))); TRACE("pull_ite_target", tout << mk_pp(n, m) << "\nresult: " << r << "\n";); return r; } z3-4.4.0/src/ast/simplifier/base_simplifier.h0000644000175000017500000000367612540347414020710 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: base_simplifier.h Abstract: Base class for expression simplifier functors. Author: Leonardo (leonardo) 2008-01-11 Notes: --*/ #ifndef _BASE_SIMPLIFIER_H_ #define _BASE_SIMPLIFIER_H_ #include"expr_map.h" #include"ast_pp.h" /** \brief Implements basic functionality used by expression simplifiers. */ class base_simplifier { protected: ast_manager & m; expr_map m_cache; ptr_vector m_todo; void cache_result(expr * n, expr * r, proof * p) { m_cache.insert(n, r, p); CTRACE("simplifier", !is_rewrite_proof(n, r, p), tout << mk_pp(n, m) << "\n"; tout << mk_pp(r, m) << "\n"; tout << mk_pp(p, m) << "\n";); SASSERT(is_rewrite_proof(n, r, p)); } void reset_cache() { m_cache.reset(); } void flush_cache() { m_cache.flush(); } void get_cached(expr * n, expr * & r, proof * & p) const { m_cache.get(n, r, p); } void reinitialize() { m_cache.set_store_proofs(m.fine_grain_proofs()); } void visit(expr * n, bool & visited) { if (!is_cached(n)) { m_todo.push_back(n); visited = false; } } public: base_simplifier(ast_manager & m): m(m), m_cache(m, m.fine_grain_proofs()) { } bool is_cached(expr * n) const { return m_cache.contains(n); } ast_manager & get_manager() { return m; } bool is_rewrite_proof(expr* n, expr* r, proof* p) { if (p && !m.is_undef_proof(p) && !(m.has_fact(p) && (m.is_eq(m.get_fact(p)) || m.is_oeq(m.get_fact(p)) || m.is_iff(m.get_fact(p))) && to_app(m.get_fact(p))->get_arg(0) == n && to_app(m.get_fact(p))->get_arg(1) == r)) return false; return (!m.fine_grain_proofs() || p || (n == r)); } }; #endif /* _BASE_SIMPLIFIER_H_ */ z3-4.4.0/src/ast/simplifier/bit2int.h0000644000175000017500000000445412540347414017121 0ustar michaelmichael/*++ Copyright (c) 2009 Microsoft Corporation Module Name: bit2int.h Abstract: Routines for simplifying bit2int expressions. Author: Nikolaj Bjorner (nbjorner) 2009-08-28 Revision History: --*/ #ifndef _BIT2INT_H_ #define _BIT2INT_H_ #include"bv_decl_plugin.h" #include"arith_decl_plugin.h" #include"act_cache.h" #include"basic_simplifier_plugin.h" #include"bv_simplifier_plugin.h" class bit2int { protected: typedef rational numeral; enum eq_type { lt, le, eq }; class expr_reduce { bit2int& m_super; public: expr_reduce(bit2int& s) : m_super(s) {} void operator()(var* v) { m_super.cache_result(v, v); } void operator()(quantifier* q) { m_super.visit(q); } void operator()(app* a) { m_super.visit(a); } void operator()(ast* a) {} }; typedef act_cache expr_map; ast_manager & m_manager; bv_util m_bv_util; arith_util m_arith_util; bv_simplifier_plugin * m_bv_simplifier; expr_map m_cache; // map: ast -> ast ref. counters are incremented when inserted here. expr_ref m_bit0; ptr_vector m_args; void visit(app* n); void visit(quantifier* q); unsigned get_b2i_size(expr * n); bool extract_bv(expr* n, unsigned& sz, bool& sign, expr_ref& bv); unsigned get_numeral_bits(numeral const& k); bool is_bv_poly(expr* n, expr_ref& pos, expr_ref& neg); bool mk_mul(expr* a, expr* b, expr_ref& result); bool mk_comp(eq_type ty, expr* e1, expr* e2, expr_ref& result); bool mk_add(expr* e1, expr* e2, expr_ref& result); expr * get_cached(expr * n) const; bool is_cached(expr * n) const { return get_cached(n) != 0; } void cache_result(expr * n, expr * r); void reset_cache() { m_cache.reset(); } void flush_cache() { m_cache.cleanup(); } void align_size(expr* e, unsigned sz, expr_ref& result); void align_sizes(expr_ref& a, expr_ref& b); public: bit2int(ast_manager & m); void set_bv_simplifier(bv_simplifier_plugin * p) { m_bv_simplifier = p; } void operator()(expr * m, expr_ref & result, proof_ref& p); }; #endif /* _BIT2INT_H_ */ z3-4.4.0/src/ast/simplifier/bv_simplifier_plugin.cpp0000644000175000017500000023015012540347414022303 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: bv_simplifier_plugin.cpp Abstract: Simplifier for the bv family. Author: Leonardo (leonardo) 2008-01-08 Nikolaj Bjorner (nbjorner) 2008-01-05 --*/ #include"bv_simplifier_plugin.h" #include"ast_ll_pp.h" #include"ast_pp.h" #include"arith_decl_plugin.h" #include"obj_hashtable.h" #include"ast_util.h" bv_simplifier_plugin::~bv_simplifier_plugin() { flush_caches(); } bv_simplifier_plugin::bv_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b, bv_simplifier_params & p): poly_simplifier_plugin(symbol("bv"), m, OP_BADD, OP_BMUL, OP_BNEG, OP_BSUB, OP_BV_NUM), m_manager(m), m_util(m), m_arith(m), m_bsimp(b), m_params(p), m_zeros(m) { } rational bv_simplifier_plugin::norm(const numeral & n) { unsigned bv_size = get_bv_size(m_curr_sort); return norm(n, bv_size, false); } bool bv_simplifier_plugin::is_numeral(expr * n, rational & val) const { unsigned bv_size; return m_util.is_numeral(n, val, bv_size); } expr * bv_simplifier_plugin::get_zero(sort * s) const { bv_simplifier_plugin * _this = const_cast(this); unsigned bv_size = _this->get_bv_size(s); if (bv_size >= m_zeros.size()) _this->m_zeros.resize(bv_size+1); if (m_zeros.get(bv_size) == 0) _this->m_zeros.set(bv_size, _this->m_util.mk_numeral(rational(0), s)); return m_zeros.get(bv_size); } bool bv_simplifier_plugin::are_numerals(unsigned num_args, expr * const* args, unsigned& bv_size) { numeral r; if (num_args == 0) { return false; } for (unsigned i = 0; i < num_args; ++i) { if (!m_util.is_numeral(args[i], r, bv_size)) { return false; } } return true; } app * bv_simplifier_plugin::mk_numeral(numeral const & n) { unsigned bv_size = get_bv_size(m_curr_sort); return mk_numeral(n, bv_size); } app * bv_simplifier_plugin::mk_numeral(numeral const& n, unsigned bv_size) { numeral r = mod(n, rational::power_of_two(bv_size)); SASSERT(!r.is_neg()); SASSERT(r < rational::power_of_two(bv_size)); return m_util.mk_numeral(r, bv_size); } bool bv_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { set_reduce_invoked(); SASSERT(f->get_family_id() == m_fid); bv_op_kind k = static_cast(f->get_decl_kind()); switch (k) { case OP_BV_NUM: SASSERT(num_args == 0); result = mk_numeral(f->get_parameter(0).get_rational(), f->get_parameter(1).get_int()); break; case OP_BIT0: SASSERT(num_args == 0); result = mk_numeral(0, 1); break; case OP_BIT1: SASSERT(num_args == 0); result = mk_numeral(1, 1); break; case OP_BADD: SASSERT(num_args > 0); mk_add(num_args, args, result); TRACE("bv_add_bug", for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], m_manager, 10) << "\n"; tout << mk_bounded_pp(result, m_manager, 10) << "\n";); mk_add_concat(result); break; case OP_BSUB: SASSERT(num_args > 0); mk_sub(num_args, args, result); break; case OP_BNEG: SASSERT(num_args == 1); mk_uminus(args[0], result); break; case OP_BMUL: SASSERT(num_args > 0); mk_mul(num_args, args, result); break; case OP_ULEQ: if (m_presimp) return false; SASSERT(num_args == 2); mk_ule(args[0], args[1], result); break; case OP_UGEQ: if (m_presimp) return false; SASSERT(num_args == 2); mk_ule(args[1], args[0], result); break; case OP_ULT: if (m_presimp) return false; SASSERT(num_args == 2); mk_ult(args[0], args[1], result); break; case OP_UGT: if (m_presimp) return false; SASSERT(num_args == 2); mk_ult(args[1], args[0], result); break; case OP_SLEQ: if (m_presimp) return false; SASSERT(num_args == 2); mk_sle(args[0], args[1], result); break; case OP_SGEQ: if (m_presimp) return false; SASSERT(num_args == 2); mk_sle(args[1], args[0], result); break; case OP_SLT: if (m_presimp) return false; SASSERT(num_args == 2); mk_slt(args[0], args[1], result); break; case OP_SGT: if (m_presimp) return false; SASSERT(num_args == 2); mk_slt(args[1], args[0], result); break; case OP_BAND: SASSERT(num_args > 0); mk_bv_and(num_args, args, result); break; case OP_BOR: SASSERT(num_args > 0); mk_bv_or(num_args, args, result); break; case OP_BNOT: SASSERT(num_args == 1); mk_bv_not(args[0], result); break; case OP_BXOR: SASSERT(num_args > 0); mk_bv_xor(num_args, args, result); break; case OP_CONCAT: SASSERT(num_args > 0); mk_concat(num_args, args, result); break; case OP_ZERO_EXT: SASSERT(num_args == 1); SASSERT(f->get_num_parameters() == 1); mk_zeroext(f->get_parameter(0).get_int(), args[0], result); break; case OP_EXTRACT: SASSERT(num_args == 1); SASSERT(f->get_num_parameters() == 2); mk_extract(f->get_parameter(0).get_int(), f->get_parameter(1).get_int(), args[0], result); break; case OP_REPEAT: SASSERT(num_args == 1); SASSERT(f->get_num_parameters() == 1); mk_repeat(f->get_parameter(0).get_int(), args[0], result); break; case OP_BUREM: SASSERT(num_args == 2); mk_bv_urem(args[0], args[1], result); break; case OP_SIGN_EXT: SASSERT(num_args == 1); SASSERT(f->get_num_parameters() == 1); mk_sign_extend(f->get_parameter(0).get_int(), args[0], result); break; case OP_BSHL: SASSERT(num_args == 2); mk_bv_shl(args[0], args[1], result); break; case OP_BLSHR: SASSERT(num_args == 2); mk_bv_lshr(args[0], args[1], result); break; case OP_INT2BV: SASSERT(num_args == 1); mk_int2bv(args[0], f->get_range(), result); break; case OP_BV2INT: SASSERT(num_args == 1); mk_bv2int(args[0], f->get_range(), result); break; case OP_BSDIV: SASSERT(num_args == 2); mk_bv_sdiv(args[0], args[1], result); break; case OP_BUDIV: SASSERT(num_args == 2); mk_bv_udiv(args[0], args[1], result); break; case OP_BSREM: SASSERT(num_args == 2); mk_bv_srem(args[0], args[1], result); break; case OP_BSMOD: SASSERT(num_args == 2); mk_bv_smod(args[0], args[1], result); break; case OP_BNAND: SASSERT(num_args > 0); mk_bv_nand(num_args, args, result); break; case OP_BNOR: SASSERT(num_args > 0); mk_bv_nor(num_args, args, result); break; case OP_BXNOR: SASSERT(num_args > 0); mk_bv_xnor(num_args, args, result); break; case OP_ROTATE_LEFT: SASSERT(num_args == 1); mk_bv_rotate_left(f, args[0], result); break; case OP_ROTATE_RIGHT: SASSERT(num_args == 1); mk_bv_rotate_right(f, args[0], result); break; case OP_EXT_ROTATE_LEFT: SASSERT(num_args == 2); mk_bv_ext_rotate_left(args[0], args[1], result); break; case OP_EXT_ROTATE_RIGHT: SASSERT(num_args == 2); mk_bv_ext_rotate_right(args[0], args[1], result); break; case OP_BREDOR: SASSERT(num_args == 1); mk_bv_redor(args[0], result); break; case OP_BREDAND: SASSERT(num_args == 1); mk_bv_redand(args[0], result); break; case OP_BCOMP: SASSERT(num_args == 2); mk_bv_comp(args[0], args[1], result); break; case OP_BASHR: SASSERT(num_args == 2); mk_bv_ashr(args[0], args[1], result); break; case OP_BSDIV_I: SASSERT(num_args == 2); mk_bv_sdiv_i(args[0], args[1], result); break; case OP_BUDIV_I: SASSERT(num_args == 2); mk_bv_udiv_i(args[0], args[1], result); break; case OP_BSREM_I: SASSERT(num_args == 2); mk_bv_srem_i(args[0], args[1], result); break; case OP_BUREM_I: SASSERT(num_args == 2); mk_bv_urem_i(args[0], args[1], result); break; case OP_BSMOD_I: SASSERT(num_args == 2); mk_bv_smod_i(args[0], args[1], result); break; case OP_BSDIV0: case OP_BUDIV0: case OP_BSREM0: case OP_BUREM0: case OP_BSMOD0: case OP_BIT2BOOL: case OP_CARRY: case OP_XOR3: case OP_MKBV: case OP_BUMUL_NO_OVFL: case OP_BSMUL_NO_OVFL: case OP_BSMUL_NO_UDFL: result = m_manager.mk_app(f, num_args, args); break; default: UNREACHABLE(); break; } SASSERT(result.get()); TRACE("bv_simplifier", tout << mk_pp(f, m_manager) << "\n"; for (unsigned i = 0; i < num_args; ++i) { tout << mk_pp(args[i], m_manager) << " "; } tout << "\n"; tout << mk_pp(result.get(), m_manager) << "\n"; ); return true; } bool bv_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) { set_reduce_invoked(); if (m_presimp) return false; expr_ref tmp(m_manager); tmp = m_manager.mk_eq(lhs,rhs); mk_bv_eq(lhs, rhs, result); return result.get() != tmp.get(); } bool bv_simplifier_plugin::reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result) { set_reduce_invoked(); return false; } void bv_simplifier_plugin::flush_caches() { TRACE("extract_cache", tout << "flushing extract cache...\n";); extract_cache::iterator it = m_extract_cache.begin(); extract_cache::iterator end = m_extract_cache.end(); for (; it != end; ++it) { extract_entry & e = (*it).m_key; m_manager.dec_ref(e.m_arg); m_manager.dec_ref((*it).m_value); } m_extract_cache.reset(); } inline uint64 bv_simplifier_plugin::to_uint64(const numeral & n, unsigned bv_size) { SASSERT(bv_size <= 64); numeral tmp = n; if (tmp.is_neg()) { tmp = mod(tmp, rational::power_of_two(bv_size)); } SASSERT(tmp.is_nonneg()); SASSERT(tmp.is_uint64()); return tmp.get_uint64(); } #define MK_BV_OP(_oper_,_binop_) \ rational bv_simplifier_plugin::mk_bv_ ## _oper_(numeral const& a0, numeral const& b0, unsigned sz) { \ rational r(0), a(a0), b(b0); \ numeral p64 = rational::power_of_two(64); \ numeral mul(1); \ while (sz > 0) { \ numeral a1 = a % p64; \ numeral b1 = b % p64; \ uint64 u = a1.get_uint64() _binop_ b1.get_uint64(); \ if (sz < 64) { \ uint64 mask = shift_left(1ull,(uint64)sz) - 1ull; \ u = mask & u; \ } \ r += mul*rational(u,rational::ui64()); \ mul *= p64; \ a = div(a, p64); \ b = div(b, p64); \ sz -= (sz<64)?sz:64; \ } \ return r; \ } MK_BV_OP(and,&) MK_BV_OP(or,|) MK_BV_OP(xor,^) rational bv_simplifier_plugin::mk_bv_not(numeral const& a0, unsigned sz) { rational r(0), a(a0), mul(1); numeral p64 = rational::power_of_two(64); while (sz > 0) { numeral a1 = a % p64; uint64 u = ~a1.get_uint64(); if (sz < 64) { uint64 mask = shift_left(1ull,(uint64)sz) - 1ull; u = mask & u; } r += mul*rational(u,rational::ui64()); mul *= p64; a = div(a, p64); sz -= (64get_num_args() == 2) { // // c <=_u (concat 0 a) <=> c[u:l] = 0 && c[l-1:0] <=_u a // app* app = to_app(arg2); expr * arg2_1 = app->get_arg(0); expr * arg2_2 = app->get_arg(1); if (m_util.is_zero(arg2_1)) { unsigned sz1 = get_bv_size(arg2_1); unsigned sz2 = get_bv_size(arg2_2); expr_ref tmp1(m_manager); expr_ref tmp2(m_manager); mk_extract(sz2 + sz1 - 1, sz2, arg1, tmp1); mk_extract(sz2 - 1, 0, arg1, tmp2); expr_ref eq(m_manager); expr_ref zero(m_manager); zero = mk_bv0(sz1); mk_bv_eq(tmp1.get(), zero, eq); expr_ref ineq(m_manager); ineq = m_util.mk_ule(tmp2.get(), arg2_2); m_bsimp.mk_and(eq.get(), ineq.get(), result); return; } } // // TODO: // Others: // // k <=_s (concat 0 a) <=> (k[u:l] = 0 && k[l-1:0] <=_u a) || k[u:u] = bv1 // // (concat 0 a) <=_s k <=> k[u:u] = bv0 && (k[u:l] != 0 || a <=_u k[l-1:0]) // // (concat 0 a) <=_u k <=> k[u:l] != 0 || a <=_u k[l-1:0] // result = m_manager.mk_app(m_fid, k, arg1, arg2); } void bv_simplifier_plugin::mk_extract(unsigned high, unsigned low, expr* arg, expr_ref& result) { unsigned arg_sz = get_bv_size(arg); unsigned sz = high - low + 1; TRACE("bv_simplifier_plugin", tout << "mk_extract [" << high << ":" << low << "]\n"; tout << "arg_sz: " << arg_sz << " sz: " << sz << "\n"; tout << "arg:\n"; ast_ll_pp(tout, m_manager, arg);); if (arg_sz == sz) { result = arg; } else { mk_extract_core(high, low, arg, result); } if (m_extract_cache.size() > (1 << 12)) { m_extract_cache.reset(); } TRACE("bv_simplifier_plugin", tout << "mk_extract [" << high << ":" << low << "]\n"; tout << "arg_sz: " << arg_sz << " sz: " << sz << "\n"; tout << "arg:\n"; ast_ll_pp(tout, m_manager, arg); tout << "=====================>\n"; ast_ll_pp(tout, m_manager, result.get());); } void bv_simplifier_plugin::cache_extract(unsigned h, unsigned l, expr * arg, expr * result) { m_manager.inc_ref(arg); m_manager.inc_ref(result); m_extract_cache.insert(extract_entry(h, l, arg), result); } expr* bv_simplifier_plugin::get_cached_extract(unsigned h, unsigned l, expr * arg) { expr * result = 0; if (m_extract_cache.find(extract_entry(h, l, arg), result)) { return result; } return 0; } void bv_simplifier_plugin::mk_extract_core(unsigned high, unsigned low, expr * arg, expr_ref& result) { if (!lookup_mk_extract(high, low, arg, result)) { while (!m_extract_args.empty()) { unsigned low2 = m_lows.back(); unsigned high2 = m_highs.back(); expr* arg2 = m_extract_args.back(); if (try_mk_extract(high2, low2, arg2, result)) { if (!m_extract_cache.contains(extract_entry(high2, low2, arg2))) { cache_extract(high2, low2, arg2, result.get()); } m_lows.pop_back(); m_highs.pop_back(); m_extract_args.pop_back(); } } if (!lookup_mk_extract(high, low, arg, result)) { UNREACHABLE(); } } } bool bv_simplifier_plugin::lookup_mk_extract(unsigned high, unsigned low, expr * arg, expr_ref& result) { expr* cached_result = get_cached_extract(high, low, arg); if (cached_result) { result = cached_result; return true; } m_extract_args.push_back(arg); m_lows.push_back(low); m_highs.push_back(high); return false; } bool bv_simplifier_plugin::try_mk_extract(unsigned high, unsigned low, expr * arg, expr_ref& result) { SASSERT(low <= high); unsigned arg_sz = get_bv_size(arg); unsigned sz = high - low + 1; numeral r; unsigned num_bits; if (arg_sz == sz) { result = arg; return true; } expr* cached_result = get_cached_extract(high, low, arg); if (cached_result) { result = cached_result; return true; } if (!is_app(arg)) { result = m_util.mk_extract(high, low, arg); return true; } app* a = to_app(arg); if (m_util.is_numeral(a, r, num_bits)) { if (r.is_neg()) { r = mod(r, rational::power_of_two(sz)); } SASSERT(r.is_nonneg()); if (r.is_uint64()) { uint64 u = r.get_uint64(); uint64 e = shift_right(u, low) & (shift_left(1ull, sz) - 1ull); TRACE("mk_extract_bug", tout << u << "[" << high << ":" << low << "] " << e << " (u >> low): " << shift_right(u, low) << " (1ull << sz): " << shift_left(1ull, sz) << " ((1ull << sz) - 1ull)" << (shift_left(1ull, sz) - 1ull) << "\n";); result = mk_numeral(numeral(e, numeral::ui64()), sz); return true; } result = mk_numeral(div(r, rational::power_of_two(low)), sz); return true; } // (extract[high:low] (extract[high2:low2] x)) == (extract[high+low2 : low+low2] x) else if (is_app_of(a, m_fid, OP_EXTRACT)) { expr * x = a->get_arg(0); unsigned low2 = a->get_decl()->get_parameter(1).get_int(); return lookup_mk_extract(high + low2, low + low2, x, result); } // // (extract[hi:lo] (bvXshr A c:bv[n])) -> (extract[hi+c:lo+c] A) // if c < n, c <= lo <= hi < n - c // else if ((is_app_of(a, m_fid, OP_BASHR) || is_app_of(a, m_fid, OP_BLSHR)) && is_numeral(a->get_arg(1), r) && r.is_unsigned()) { unsigned c = r.get_unsigned(); unsigned bv_size = get_bv_size(a); if (c < bv_size && c <= low && high < bv_size - c) { return lookup_mk_extract(high+c, low+c, a->get_arg(0), result); } } // (concat a_0 ... a_{n-1}) // Remark: the least significant bits are stored in a_{n-1} else if (is_app_of(a, m_fid, OP_CONCAT)) { expr_ref_buffer new_args(m_manager); unsigned i = a->get_num_args(); // look for first argument while (i > 0) { --i; expr * a_i = a->get_arg(i); unsigned a_sz = get_bv_size(a_i); TRACE("extract_bug", tout << "FIRST a_sz: " << a_sz << " high: " << high << " low: " << low << "\n" << mk_pp(a_i, m_manager) << "\n";); if (a_sz <= low) { low -= a_sz; high -= a_sz; } else { // found first argument if (a_sz <= high) { expr_ref new_arg(m_manager); if (!lookup_mk_extract(a_sz - 1, low, a_i, new_arg)) { return false; } new_args.push_back(new_arg.get()); unsigned num_consumed_bytes = a_sz - low; // I have to apply extract[sz - num_consumed_bytes - 1, 0] on the rest of concat high = (sz - num_consumed_bytes - 1); break; } else { return lookup_mk_extract(high, low, a_i, result); } } } TRACE("extract_bug", tout << " high: " << high << " low: " << low << "\n";); // look for remaining arguments while (i > 0) { --i; expr * a_i = a->get_arg(i); unsigned a_sz = get_bv_size(a_i); TRACE("extract_bug", tout << "SECOND a_sz: " << a_sz << " high: " << high << " " << mk_pp( a_i, m_manager) << "\n";); if (a_sz <= high) { high -= a_sz; new_args.push_back(a_i); } else { // found last argument expr_ref new_arg(m_manager); if (!lookup_mk_extract(high, 0, a_i, new_arg)) { return false; } new_args.push_back(new_arg.get()); // The arguments in new_args are in reverse order. ptr_buffer rev_new_args; unsigned i = new_args.size(); while (i > 0) { --i; rev_new_args.push_back(new_args[i]); } mk_concat(rev_new_args.size(), rev_new_args.c_ptr(), result); return true; } } UNREACHABLE(); } else if (is_app_of(a, m_fid, OP_SIGN_EXT)) { SASSERT(a->get_num_args() == 1); unsigned bv_size = get_bv_size(a->get_arg(0)); if (high < bv_size) { return lookup_mk_extract(high, low, a->get_arg(0), result); } } else if (is_app_of(a, m_fid, OP_BAND) || is_app_of(a, m_fid, OP_BOR) || is_app_of(a, m_fid, OP_BXOR) || is_app_of(a, m_fid, OP_BNOR) || is_app_of(a, m_fid, OP_BNAND) || is_app_of(a, m_fid, OP_BNOT) || (low == 0 && is_app_of(a, m_fid, OP_BADD)) || (low == 0 && is_app_of(a, m_fid, OP_BMUL)) || (low == 0 && is_app_of(a, m_fid, OP_BSUB))) { expr_ref_buffer new_args(m_manager); bool all_found = true; for (unsigned i = 0; i < a->get_num_args(); ++i) { expr_ref new_arg(m_manager); if (!lookup_mk_extract(high, low, a->get_arg(i), new_arg)) { all_found = false; } new_args.push_back(new_arg.get()); } if (!all_found) { return false; } // We should not use mk_app because it does not guarantee that the result would be in simplified form. // result = m_manager.mk_app(m_fid, a->get_decl_kind(), new_args.size(), new_args.c_ptr()); if (is_app_of(a, m_fid, OP_BAND)) mk_bv_and(new_args.size(), new_args.c_ptr(), result); else if (is_app_of(a, m_fid, OP_BOR)) mk_bv_or(new_args.size(), new_args.c_ptr(), result); else if (is_app_of(a, m_fid, OP_BXOR)) mk_bv_xor(new_args.size(), new_args.c_ptr(), result); else if (is_app_of(a, m_fid, OP_BNOR)) mk_bv_nor(new_args.size(), new_args.c_ptr(), result); else if (is_app_of(a, m_fid, OP_BNAND)) mk_bv_nand(new_args.size(), new_args.c_ptr(), result); else if (is_app_of(a, m_fid, OP_BNOT)) { SASSERT(new_args.size() == 1); mk_bv_not(new_args[0], result); } else if (is_app_of(a, m_fid, OP_BADD)) mk_add(new_args.size(), new_args.c_ptr(), result); else if (is_app_of(a, m_fid, OP_BMUL)) mk_mul(new_args.size(), new_args.c_ptr(), result); else if (is_app_of(a, m_fid, OP_BSUB)) mk_sub(new_args.size(), new_args.c_ptr(), result); else { UNREACHABLE(); } return true; } else if (m_manager.is_ite(a)) { expr_ref then_b(m_manager), else_b(m_manager); bool ok = lookup_mk_extract(high, low, a->get_arg(1), then_b); ok = lookup_mk_extract(high, low, a->get_arg(2), else_b) && ok; if (ok) { m_bsimp.mk_ite(a->get_arg(0), then_b.get(), else_b.get(), result); } return ok; } result = m_util.mk_extract(high, low, arg); return true; } /** \brief Let f be the operator fid:k. Then, this function store in result the flat args of n. If n is not an f application, then store n in result. Example: if n is (f (f a b) (f c (f d e))), then a b c d e are stored in result. */ template void get_assoc_args(family_id fid, decl_kind k, expr * n, T & result) { ptr_buffer todo; todo.push_back(n); while (!todo.empty()) { expr * n = todo.back(); todo.pop_back(); if (is_app_of(n, fid, k)) { app * app = to_app(n); unsigned i = app->get_num_args(); while (i > 0) { --i; todo.push_back(app->get_arg(i)); } } else { result.push_back(n); } } } /** \brief Similar to get_assoc_args, but the arguments are stored in reverse other in result. */ template void get_inv_assoc_args(family_id fid, decl_kind k, expr * n, T & result) { ptr_buffer todo; todo.push_back(n); while (!todo.empty()) { expr * n = todo.back(); todo.pop_back(); if (is_app_of(n, fid, k)) { app * app = to_app(n); unsigned num = app->get_num_args(); for (unsigned i = 0; i < num; i++) todo.push_back(app->get_arg(i)); } else { result.push_back(n); } } } void bv_simplifier_plugin::mk_bv_eq(expr* a1, expr* a2, expr_ref& result) { rational val1; rational val2; bool is_num1 = is_numeral(a1, val1); bool is_num2 = is_numeral(a2, val2); if (is_num1 && is_num2 && val1 != val2) { result = m_manager.mk_false(); return; } if (!m_util.is_concat(a1) && !is_num1) { mk_eq_core(a1, a2, result); return; } if (!m_util.is_concat(a2) && !is_num2) { mk_eq_core(a1, a2, result); return; } ptr_buffer args1, args2; get_inv_assoc_args(m_fid, OP_CONCAT, a1, args1); get_inv_assoc_args(m_fid, OP_CONCAT, a2, args2); TRACE("mk_bv_eq_concat", tout << mk_ll_pp(a1, m_manager) << "\n" << mk_ll_pp(a2, m_manager) << "\n"; tout << "args1:\n"; for (unsigned i = 0; i < args1.size(); i++) tout << mk_ll_pp(args1[i], m_manager) << "\n"; tout << "args2:\n"; for (unsigned i = 0; i < args2.size(); i++) tout << mk_ll_pp(args2[i], m_manager) << "\n";); expr_ref lhs(m_manager), rhs(m_manager), eq(m_manager); expr_ref_buffer eqs(m_manager); unsigned low1 = 0, low2 = 0; ptr_buffer::iterator it1 = args1.begin(); ptr_buffer::iterator end1 = args1.end(); ptr_buffer::iterator it2 = args2.begin(); ptr_buffer::iterator end2 = args2.end(); while (it1 != end1 && it2 != end2) { SASSERT(it1 != end1); SASSERT(it2 != end2); expr * arg1 = *it1; expr * arg2 = *it2; TRACE("expr_bv_util", tout << "low1: " << low1 << " low2: " << low2 << "\n"; tout << mk_pp(arg1, m_manager) << "\n"; tout << mk_pp(arg2, m_manager) << "\n";); unsigned sz1 = get_bv_size(arg1); unsigned sz2 = get_bv_size(arg2); SASSERT(low1 < sz1 && low2 < sz2); unsigned rsz1 = sz1 - low1; unsigned rsz2 = sz2 - low2; TRACE("expr_bv_util", tout << "rsz1: " << rsz1 << " rsz2: " << rsz2 << "\n"; tout << mk_pp(arg1, m_manager) << "\n"; tout << mk_pp(arg2, m_manager) << "\n";); if (rsz1 == rsz2) { mk_extract(sz1 - 1, low1, arg1, lhs); mk_extract(sz2 - 1, low2, arg2, rhs); low1 = 0; low2 = 0; ++it1; ++it2; } else if (rsz1 < rsz2) { mk_extract(sz1 - 1, low1, arg1, lhs); mk_extract(rsz1 + low2 - 1, low2, arg2, rhs); low1 = 0; low2 += rsz1; ++it1; } else { mk_extract(rsz2 + low1 - 1, low1, arg1, lhs); mk_extract(sz2 - 1, low2, arg2, rhs); low1 += rsz2; low2 = 0; ++it2; } mk_eq_core(lhs.get(), rhs.get(), eq); eqs.push_back(eq.get()); } m_bsimp.mk_and(eqs.size(), eqs.c_ptr(), result); } void bv_simplifier_plugin::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) { TRACE("mk_eq_core", ast_ll_pp(tout, m_manager, arg1 ); ast_ll_pp(tout, m_manager, arg2);); if (arg1 == arg2) { result = m_manager.mk_true(); return; } if ((m_util.is_bv_and(arg1) && m_util.is_allone(arg2)) || (m_util.is_bv_or(arg1) && m_util.is_zero(arg2))) { mk_args_eq_numeral(to_app(arg1), arg2, result); return; } if ((m_util.is_bv_and(arg2) && m_util.is_allone(arg1)) || (m_util.is_bv_or(arg2) && m_util.is_zero(arg1))) { mk_args_eq_numeral(to_app(arg2), arg1, result); return; } #if 1 rational r; unsigned num_bits = 0; if (m_util.is_numeral(arg2, r, num_bits)) { std::swap(arg1, arg2); } if (m_util.is_numeral(arg1, r, num_bits) && (m_util.is_bv_and(arg2) || m_util.is_bv_or(arg2) || m_util.is_bv_not(arg2))) { rational two(2); expr_ref tmp(m_manager); expr_ref_vector tmps(m_manager); for (unsigned i = 0; i < num_bits; ++i) { bool is_neg = (r % two).is_zero(); bit2bool_simplify(i, arg2, tmp); if (is_neg) { expr_ref tmp2(m_manager); m_bsimp.mk_not(tmp, tmp2); tmp = tmp2; } tmps.push_back(tmp); r = div(r, two); } m_bsimp.mk_and(tmps.size(), tmps.c_ptr(), result); TRACE("mk_eq_bb", tout << mk_pp(arg1, m_manager) << "\n"; tout << mk_pp(arg2, m_manager) << "\n"; tout << mk_pp(result, m_manager) << "\n";); return; } #endif if (!m_util.is_bv_add(arg1) && !m_util.is_bv_add(arg2) && !m_util.is_bv_mul(arg1) && !m_util.is_bv_mul(arg2)) { m_bsimp.mk_eq(arg1, arg2, result); return; } set_curr_sort(arg1); expr_ref_vector args1(m_manager); expr_ref_vector args2(m_manager); get_assoc_args(m_fid, OP_BADD, arg1, args1); get_assoc_args(m_fid, OP_BADD, arg2, args2); TRACE("mk_eq_core", tout << mk_pp(arg1, m_manager) << "\n" << mk_pp(arg2, m_manager) << "\n"; tout << args1.size() << " " << args2.size() << "\n";); unsigned idx2 = 0; while (idx2 < args2.size()) { expr * m2 = args2.get(idx2); unsigned sz1 = args1.size(); unsigned idx1 = 0; for (; idx1 < sz1; ++idx1) { expr * m1 = args1.get(idx1); if (eq_monomials_modulo_k(m1, m2)) { expr_ref tmp(m_manager); if (merge_monomials(true, m1, m2, tmp)) { args1.set(idx1, tmp.get()); } else { // the monomial cancelled each other. args1.erase(idx1); } break; } } if (idx1 == sz1) { ++idx2; } else { args2.erase(idx2); } } expr_ref lhs(m_manager); expr_ref rhs(m_manager); mk_sum_of_monomials(args1, lhs); mk_sum_of_monomials(args2, rhs); m_bsimp.mk_eq(lhs.get(), rhs.get(), result); } void bv_simplifier_plugin::mk_args_eq_numeral(app * app, expr * n, expr_ref & result) { expr_ref_buffer eqs(m_manager); expr_ref eq(m_manager); unsigned num = app->get_num_args(); for (unsigned i = 0; i < num; i++) { mk_bv_eq(app->get_arg(i), n, eq); eqs.push_back(eq.get()); } m_bsimp.mk_and(eqs.size(), eqs.c_ptr(), result); } void bv_simplifier_plugin::mk_concat(unsigned num_args, expr * const * args, expr_ref & result) { TRACE("bv_simplifier_plugin", tout << "mk_concat:\n"; for (unsigned i = 0; i < num_args; i++) ast_ll_pp(tout, m_manager, args[i]);); unsigned shift = 0; numeral val(0), arg_val; for (unsigned i = num_args; i > 0; ) { --i; expr * arg = args[i]; if (is_numeral(arg, arg_val)) { arg_val *= rational::power_of_two(shift); val += arg_val; shift += get_bv_size(arg); TRACE("bv_simplifier_plugin", tout << "val: " << val << " arg_val: " << arg_val << " shift: " << shift << "\n";); } else { // one of the arguments is not a number result = m_manager.mk_app(m_fid, OP_CONCAT, num_args, args); return; } } // all arguments are numerals result = mk_numeral(val, shift); } void bv_simplifier_plugin::mk_bv_and(unsigned num_args, expr * const* args, expr_ref & result) { ptr_buffer flat_args; for (unsigned i = 0; i < num_args; ++i) { flat_args.push_back(args[i]); } // expr_lt_proc is a total order on expressions. std::sort(flat_args.begin(), flat_args.end(), expr_lt_proc(m_fid, OP_BNOT)); SASSERT(num_args > 0); unsigned bv_size = get_bv_size(args[0]); numeral allone = mk_allone(bv_size); numeral val; uint64 unit = bv_size <= 64 ? to_uint64(numeral(-1), bv_size) : 0; numeral n_unit(allone); expr * prev = 0; ptr_buffer::iterator it = flat_args.begin(); ptr_buffer::iterator it2 = it; ptr_buffer::iterator end = flat_args.end(); for (; it != end; ++it) { expr* n = *it; if (prev && ((is_app_of(n, m_fid, OP_BNOT) && to_app(n)->get_arg(0) == prev) || (is_app_of(prev, m_fid, OP_BNOT) && to_app(prev)->get_arg(0) == n))) { result = mk_bv0(bv_size); return; } else if (bv_size <= 64 && is_numeral(n, val)) { unit &= to_uint64(val, bv_size); if (unit == 0) { result = mk_bv0(bv_size); return; } } else if (bv_size > 64 && is_numeral(n, val)) { n_unit = mk_bv_and(val, n_unit, bv_size); if (n_unit.is_zero()) { result = mk_bv0(bv_size); return; } } else if (!prev || prev != n) { *it2 = n; prev = *it2; ++it2; } } if (bv_size <= 64) { n_unit = numeral(unit, numeral::ui64()); } flat_args.set_end(it2); if (n_unit != allone) { flat_args.push_back(mk_numeral(n_unit, bv_size)); } unsigned sz = flat_args.size(); switch(sz) { case 0: result = mk_numeral(n_unit, bv_size); break; case 1: result = flat_args.back(); break; default: result = mk_list_assoc_app(m_manager, m_fid, OP_BAND, sz, flat_args.c_ptr()); break; } } void bv_simplifier_plugin::mk_bv_or(unsigned num_args, expr * const* args, expr_ref & result) { #if 0 // Transformations for SAGE // (bvor (concat 0 x) (concat y 0)) ==> (concat y x) // (bvor (concat x 0) (concat 0 y)) ==> (concat x y) if (num_args == 2 && m_util.is_concat(args[0]) && m_util.is_concat(args[1]) && to_app(args[0])->get_num_args() == 2 && to_app(args[1])->get_num_args() == 2) { expr * x1 = to_app(args[0])->get_arg(0); expr * x2 = to_app(args[0])->get_arg(1); expr * y1 = to_app(args[1])->get_arg(0); expr * y2 = to_app(args[1])->get_arg(1); if (get_bv_size(x1) == get_bv_size(y1) && get_bv_size(x2) == get_bv_size(y2)) { if (m_util.is_zero(x1) && m_util.is_zero(y2)) { // (bvor (concat 0 x) (concat y 0)) ==> (concat y x) mk_concat(y1, x2, result); return; } if (m_util.is_zero(x2) && m_util.is_zero(y1)) { // (bvor (concat x 0) (concat 0 y)) ==> (concat x y) mk_concat(x1, y2, result); return; } } } // Investigate why it did not work. #endif ptr_buffer flat_args; for (unsigned i = 0; i < num_args; ++i) { flat_args.push_back(args[i]); } std::sort(flat_args.begin(), flat_args.end(), expr_lt_proc(m_fid, OP_BNOT)); SASSERT(num_args > 0); unsigned bv_size = get_bv_size(args[0]), sz; numeral allone = mk_allone(bv_size); numeral val; uint64 unit = 0; numeral n_unit(0); expr * prev = 0; ptr_buffer::iterator it = flat_args.begin(); ptr_buffer::iterator it2 = it; ptr_buffer::iterator end = flat_args.end(); for (; it != end; ++it) { expr* n = *it; if (prev && ((is_app_of(n, m_fid, OP_BNOT) && to_app(n)->get_arg(0) == prev) || (is_app_of(prev, m_fid, OP_BNOT) && to_app(prev)->get_arg(0) == n))) { result = mk_numeral(allone, bv_size); return; } else if (bv_size <= 64 && is_numeral(n, val)) { unit |= to_uint64(val, bv_size); } else if (bv_size > 64 && is_numeral(n, val)) { n_unit = mk_bv_or(val, n_unit, bv_size); } else if (!prev || prev != n) { *it2 = n; prev = *it2; ++it2; } } if (bv_size <= 64) { n_unit = numeral(unit, numeral::ui64()); } if (allone == n_unit) { result = mk_numeral(allone, bv_size); return; } flat_args.set_end(it2); if (!n_unit.is_zero()) { flat_args.push_back(mk_numeral(n_unit, bv_size)); } sz = flat_args.size(); switch(sz) { case 0: result = mk_numeral(n_unit, bv_size); break; case 1: result = flat_args.back(); break; default: result = mk_list_assoc_app(m_manager, m_fid, OP_BOR, sz, flat_args.c_ptr()); break; } } void bv_simplifier_plugin::mk_bv_xor(unsigned num_args, expr * const * args, expr_ref & result) { ptr_buffer flat_args; for (unsigned i = 0; i < num_args; ++i) { flat_args.push_back(args[i]); } std::sort(flat_args.begin(), flat_args.end(), expr_lt_proc()); SASSERT(num_args > 0); unsigned bv_size = get_bv_size(args[0]); numeral val; uint64 unit = 0; numeral n_unit(0); expr * prev = 0; ptr_buffer::iterator it = flat_args.begin(); ptr_buffer::iterator it2 = it; ptr_buffer::iterator end = flat_args.end(); for (; it != end; ++it) { if (bv_size <= 64 && is_numeral(*it, val)) { uint64 u = to_uint64(val, bv_size); unit = u ^ unit; } else if (bv_size > 64 && is_numeral(*it, val)) { n_unit = mk_bv_xor(n_unit, val, bv_size); } else if (prev != 0 && prev == *it) { --it2; // remove prev prev = 0; } else { *it2 = *it; prev = *it2; ++it2; } } flat_args.set_end(it2); if (bv_size <= 64) { n_unit = numeral(numeral(unit,numeral::ui64())); } if (!n_unit.is_zero()) { flat_args.push_back(mk_numeral(n_unit, bv_size)); } unsigned sz = flat_args.size(); switch(sz) { case 0: result = mk_numeral(n_unit, bv_size); break; case 1: result = flat_args.back(); break; default: result = mk_list_assoc_app(m_manager, m_fid, OP_BXOR, flat_args.size(), flat_args.c_ptr()); break; } } void bv_simplifier_plugin::mk_bv_not(expr * arg, expr_ref & result) { numeral val; unsigned bv_size; if (m_util.is_numeral(arg, val, bv_size)) { if (bv_size <= 64) { uint64 l = bv_size; uint64 mask = shift_left(1ull,l) - 1ull; uint64 u = val.get_uint64(); u = mask & (~u); result = mk_numeral(numeral(u, numeral::ui64()), bv_size); TRACE("bv_not_bug", tout << l << " " << mask << " " << u << "\n"; tout << mk_pp(arg, m_manager) << "\n" << mk_pp(result, m_manager) << "\n";); } else { numeral r = mk_bv_not(val, bv_size); result = mk_numeral(r, bv_size); TRACE("bv_not_bug", tout << mk_pp(arg, m_manager) << "\n" << mk_pp(result, m_manager) << "\n";); } } else if (is_app_of(arg, m_fid, OP_BNOT)) { result = to_app(arg)->get_arg(0); } else { result = m_manager.mk_app(m_fid, OP_BNOT, arg); } } void bv_simplifier_plugin::mk_zeroext(unsigned n, expr * arg, expr_ref & result) { if (n == 0) { result = arg; } else { expr_ref zero(m_manager); zero = mk_bv0(n); mk_concat(zero.get(), arg, result); } } void bv_simplifier_plugin::mk_repeat(unsigned n, expr * arg, expr_ref & result) { ptr_buffer args; for (unsigned i = 0; i < n; i++) { args.push_back(arg); } mk_concat(args.size(), args.c_ptr(), result); } bool bv_simplifier_plugin::is_minus_one_core(expr * arg) const { numeral r; unsigned bv_size; if (m_util.is_numeral(arg, r, bv_size)) { numeral minus_one(-1); minus_one = mod(minus_one, rational::power_of_two(bv_size)); return r == minus_one; } return false; } bool bv_simplifier_plugin::is_x_minus_one(expr * arg, expr * & x) { if (is_add(arg) && to_app(arg)->get_num_args() == 2) { if (is_minus_one_core(to_app(arg)->get_arg(0))) { x = to_app(arg)->get_arg(1); return true; } if (is_minus_one_core(to_app(arg)->get_arg(1))) { x = to_app(arg)->get_arg(0); return true; } } return false; } void bv_simplifier_plugin::mk_bv_urem(expr * arg1, expr * arg2, expr_ref & result) { numeral r1, r2; unsigned bv_size; bool is_num1 = m_util.is_numeral(arg1, r1, bv_size); bool is_num2 = m_util.is_numeral(arg2, r2, bv_size); bv_size = get_bv_size(arg1); if (is_num2 && r2.is_zero() && !m_params.m_hi_div0) { result = m_manager.mk_app(m_fid, OP_BUREM0, arg1); return; } if (is_num1 && is_num2 && !r2.is_zero()) { SASSERT(r1.is_nonneg() && r2.is_pos()); r1 %= r2; result = mk_numeral(r1, bv_size); return; } if (!m_params.m_hi_div0) { // TODO: implement the optimization in this branch for the case the hardware interpretation is used for (x urem 0) // urem(0, x) ==> ite(x = 0, urem0(x), 0) if (is_num1 && r1.is_zero()) { expr * zero = arg1; expr_ref urem0(m_manager), eq0(m_manager); urem0 = m_manager.mk_app(m_fid, OP_BUREM0, 1, &zero); m_bsimp.mk_eq(arg2, zero, eq0); m_bsimp.mk_ite(eq0.get(), urem0.get(), zero, result); TRACE("urem", tout << "urem:\n"; ast_ll_pp(tout, m_manager, arg1); ast_ll_pp(tout, m_manager, arg2); tout << "result:\n"; ast_ll_pp(tout, m_manager, result.get());); return; } // urem(x - 1, x) ==> ite(x = 0, urem0(x-1), x - 1) ==> ite(x = 0, urem0(-1), x - 1) expr * x; if (is_x_minus_one(arg1, x) && x == arg2) { expr * x_minus_1 = arg1; expr_ref zero(m_manager); zero = mk_bv0(bv_size); expr_ref minus_one(m_manager), urem0(m_manager), eq0(m_manager); minus_one = mk_numeral(numeral::minus_one(), bv_size); expr * minus_1 = minus_one.get(); urem0 = m_manager.mk_app(m_fid, OP_BUREM0, 1, &minus_1); m_bsimp.mk_eq(arg2, zero.get(), eq0); m_bsimp.mk_ite(eq0.get(), urem0.get(), x_minus_1, result); TRACE("urem", tout << "urem:\n"; ast_ll_pp(tout, m_manager, arg1); ast_ll_pp(tout, m_manager, arg2); tout << "result:\n"; ast_ll_pp(tout, m_manager, result.get());); return; } } if (is_num2 || m_params.m_hi_div0) { result = m_manager.mk_app(m_fid, OP_BUREM_I, arg1, arg2); } else { bv_size = get_bv_size(arg2); result = m_manager.mk_ite(m_manager.mk_eq(arg2, mk_numeral(0, bv_size)), m_manager.mk_app(m_fid, OP_BUREM0, arg1), m_manager.mk_app(m_fid, OP_BUREM_I, arg1, arg2)); } } void bv_simplifier_plugin::mk_sign_extend(unsigned n, expr * arg, expr_ref & result) { numeral r; unsigned bv_size; if (m_util.is_numeral(arg, r, bv_size)) { unsigned result_bv_size = bv_size + n; r = norm(r, bv_size, true); r = mod(r, rational::power_of_two(result_bv_size)); result = mk_numeral(r, result_bv_size); TRACE("mk_sign_extend", tout << "n: " << n << "\n"; ast_ll_pp(tout, m_manager, arg); tout << "====>\n"; ast_ll_pp(tout, m_manager, result.get());); return; } parameter param(n); result = m_manager.mk_app(m_fid, OP_SIGN_EXT, 1, ¶m, 1, &arg); } /** Implement the following reductions (bvashr (bvashr a n1) n2) ==> (bvashr a (+ n1 n2)) (bvlshr (bvlshr a n1) n2) ==> (bvlshr a (+ n1 n2)) (bvshl (bvshl a n1) n2) ==> (bvshl a (+ n1 n2)) when n1 and n2 are numerals. Remark if (+ n1 n2) is greater than bv_size, we set (+ n1 n2) to bv_size Return true if the transformation was applied and the result stored in 'result'. Return false otherwise. */ bool bv_simplifier_plugin::shift_shift(bv_op_kind k, expr* arg1, expr* arg2, expr_ref& result) { SASSERT(k == OP_BASHR || k == OP_BSHL || k == OP_BLSHR); if (!is_app_of(arg1, m_fid, k)) return false; expr * a = to_app(arg1)->get_arg(0); expr * n1 = to_app(arg1)->get_arg(1); expr * n2 = arg2; numeral r1, r2; unsigned bv_size = UINT_MAX; bool is_num1 = m_util.is_numeral(n1, r1, bv_size); bool is_num2 = m_util.is_numeral(n2, r2, bv_size); if (!is_num1 || !is_num2) return false; SASSERT(bv_size != UINT_MAX); numeral r = r1 + r2; if (r > numeral(bv_size)) r = numeral(bv_size); switch (k) { case OP_BASHR: mk_bv_ashr(a, m_util.mk_numeral(r, bv_size), result); break; case OP_BLSHR: mk_bv_lshr(a, m_util.mk_numeral(r, bv_size), result); break; default: SASSERT(k == OP_BSHL); mk_bv_shl(a, m_util.mk_numeral(r, bv_size), result); break; } return true; } void bv_simplifier_plugin::mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result) { // x << 0 == x numeral r1, r2; unsigned bv_size = get_bv_size(arg1); bool is_num1 = is_numeral(arg1, r1); bool is_num2 = is_numeral(arg2, r2); if (is_num2 && r2.is_zero()) { result = arg1; } else if (is_num2 && r2 >= rational(bv_size)) { result = mk_numeral(0, bv_size); } else if (is_num2 && is_num1 && bv_size <= 64) { SASSERT(r1.is_uint64() && r2.is_uint64()); SASSERT(r2.get_uint64() < bv_size); uint64 r = shift_left(r1.get_uint64(), r2.get_uint64()); result = mk_numeral(r, bv_size); } else if (is_num1 && is_num2) { SASSERT(r2 < rational(bv_size)); SASSERT(r2.is_unsigned()); result = mk_numeral(r1 * rational::power_of_two(r2.get_unsigned()), bv_size); } // // (bvshl x k) -> (concat (extract [n-1-k:0] x) bv0:k) // else if (is_num2 && r2.is_pos() && r2 < numeral(bv_size)) { SASSERT(r2.is_unsigned()); unsigned r = r2.get_unsigned(); expr_ref tmp1(m_manager); mk_extract(bv_size - r - 1, 0, arg1, tmp1); expr_ref zero(m_manager); zero = mk_bv0(r); expr* args[2] = { tmp1.get(), zero.get() }; mk_concat(2, args, result); } else if (shift_shift(OP_BSHL, arg1, arg2, result)) { // done } else { result = m_manager.mk_app(m_fid, OP_BSHL, arg1, arg2); } TRACE("mk_bv_shl", tout << mk_pp(arg1, m_manager) << " << " << mk_pp(arg2, m_manager) << " = " << mk_pp(result.get(), m_manager) << "\n";); } void bv_simplifier_plugin::mk_bv_lshr(expr * arg1, expr * arg2, expr_ref & result) { // x >> 0 == x numeral r1, r2; unsigned bv_size = get_bv_size(arg1); bool is_num1 = is_numeral(arg1, r1); bool is_num2 = is_numeral(arg2, r2); if (is_num2 && r2.is_zero()) { result = arg1; } else if (is_num2 && r2 >= rational(bv_size)) { result = mk_numeral(rational(0), bv_size); } else if (is_num1 && is_num2 && bv_size <= 64) { SASSERT(r1.is_uint64()); SASSERT(r2.is_uint64()); uint64 r = shift_right(r1.get_uint64(), r2.get_uint64()); result = mk_numeral(r, bv_size); } else if (is_num1 && is_num2) { SASSERT(r2.is_unsigned()); unsigned sh = r2.get_unsigned(); r1 = div(r1, rational::power_of_two(sh)); result = mk_numeral(r1, bv_size); } // // (bvlshr x k) -> (concat bv0:k (extract [n-1:k] x)) // else if (is_num2 && r2.is_pos() && r2 < numeral(bv_size)) { SASSERT(r2.is_unsigned()); unsigned r = r2.get_unsigned(); expr_ref tmp1(m_manager); mk_extract(bv_size - 1, r, arg1, tmp1); expr_ref zero(m_manager); zero = mk_bv0(r); expr* args[2] = { zero.get(), tmp1.get() }; mk_concat(2, args, result); } else if (shift_shift(OP_BLSHR, arg1, arg2, result)) { // done } else { result = m_manager.mk_app(m_fid, OP_BLSHR, arg1, arg2); } TRACE("mk_bv_lshr", tout << mk_pp(arg1, m_manager) << " >> " << mk_pp(arg2, m_manager) << " = " << mk_pp(result.get(), m_manager) << "\n";); } void bv_simplifier_plugin::mk_int2bv(expr * arg, sort* range, expr_ref & result) { numeral val; bool is_int; unsigned bv_size = get_bv_size(range); if (m_arith.is_numeral(arg, val, is_int)) { result = mk_numeral(val, bv_size); } // (int2bv (bv2int x)) == x else if (is_app_of(arg, m_fid, OP_BV2INT) && bv_size == get_bv_size(to_app(arg)->get_arg(0))) { result = to_app(arg)->get_arg(0); } else { parameter parameter(bv_size); result = m_manager.mk_app(m_fid, OP_INT2BV, 1, ¶meter, 1, &arg); SASSERT(result.get()); } } void bv_simplifier_plugin::mk_bv2int(expr * arg, sort* range, expr_ref & result) { if (!m_params.m_bv2int_distribute) { parameter parameter(range); result = m_manager.mk_app(m_fid, OP_BV2INT, 1, ¶meter, 1, &arg); return; } numeral v; if (is_numeral(arg, v)) { result = m_arith.mk_numeral(v, true); } else if (is_mul_no_overflow(arg)) { expr_ref tmp1(m_manager), tmp2(m_manager); mk_bv2int(to_app(arg)->get_arg(0), range, tmp1); mk_bv2int(to_app(arg)->get_arg(1), range, tmp2); result = m_arith.mk_mul(tmp1, tmp2); } else if (is_add_no_overflow(arg)) { expr_ref tmp1(m_manager), tmp2(m_manager); mk_bv2int(to_app(arg)->get_arg(0), range, tmp1); mk_bv2int(to_app(arg)->get_arg(1), range, tmp2); result = m_arith.mk_add(tmp1, tmp2); } // commented out to reproduce bug in reduction of int2bv/bv2int else if (m_util.is_concat(arg)) { expr_ref tmp1(m_manager), tmp2(m_manager); unsigned sz2 = get_bv_size(to_app(arg)->get_arg(1)); mk_bv2int(to_app(arg)->get_arg(0), range, tmp1); mk_bv2int(to_app(arg)->get_arg(1), range, tmp2); tmp1 = m_arith.mk_mul(m_arith.mk_numeral(power(numeral(2), sz2), true), tmp1); result = m_arith.mk_add(tmp1, tmp2); } else { parameter parameter(range); result = m_manager.mk_app(m_fid, OP_BV2INT, 1, ¶meter, 1, &arg); } SASSERT(m_arith.is_int(m_manager.get_sort(result.get()))); } unsigned bv_simplifier_plugin::num_leading_zero_bits(expr* e) { numeral v; unsigned sz = get_bv_size(e); if (is_numeral(e, v)) { while (v.is_pos()) { SASSERT(sz > 0); --sz; v = div(v, numeral(2)); } return sz; } else if (m_util.is_concat(e)) { app* a = to_app(e); unsigned sz1 = get_bv_size(a->get_arg(0)); unsigned nb1 = num_leading_zero_bits(a->get_arg(0)); if (sz1 == nb1) { nb1 += num_leading_zero_bits(a->get_arg(1)); } return nb1; } return 0; } bool bv_simplifier_plugin::is_mul_no_overflow(expr* e) { if (!is_mul(e)) { return false; } expr* e1 = to_app(e)->get_arg(0); expr* e2 = to_app(e)->get_arg(1); unsigned sz = get_bv_size(e1); unsigned nb1 = num_leading_zero_bits(e1); unsigned nb2 = num_leading_zero_bits(e2); return nb1 + nb2 >= sz; } bool bv_simplifier_plugin::is_add_no_overflow(expr* e) { if (!is_add(e)) { return false; } expr* e1 = to_app(e)->get_arg(0); expr* e2 = to_app(e)->get_arg(1); unsigned nb1 = num_leading_zero_bits(e1); unsigned nb2 = num_leading_zero_bits(e2); return nb1 > 0 && nb2 > 0; } // Macro for generating mk_bv_sdiv_i, mk_bv_udiv_i, mk_bv_srem_i, mk_bv_urem_i and mk_bv_smod_i. // These are essentially evaluators for the arg1 and arg2 are numerals. // Q: Why do we need them? // A: A constant may be eliminated using substitution. Its value is computed using the evaluator. // Example: Suppose we have the top-level atom (= x (bvsrem_i a b)), and x is eliminated. #define MK_FIXED_DIV_I(NAME, OP) \ void bv_simplifier_plugin::NAME##_i(expr * arg1, expr * arg2, expr_ref & result) { \ numeral r1, r2; \ unsigned bv_size; \ bool is_num1 = m_util.is_numeral(arg1, r1, bv_size); \ bool is_num2 = m_util.is_numeral(arg2, r2, bv_size); \ if (is_num1 && is_num2 && !r2.is_zero()) { \ NAME(arg1, arg2, result); \ } \ else { \ result = m_manager.mk_app(m_fid, OP, arg1, arg2); \ } \ } MK_FIXED_DIV_I(mk_bv_sdiv, OP_BSDIV_I) MK_FIXED_DIV_I(mk_bv_udiv, OP_BUDIV_I) MK_FIXED_DIV_I(mk_bv_srem, OP_BSREM_I) MK_FIXED_DIV_I(mk_bv_urem, OP_BUREM_I) MK_FIXED_DIV_I(mk_bv_smod, OP_BSMOD_I) void bv_simplifier_plugin::mk_bv_sdiv(expr* arg1, expr* arg2, expr_ref& result) { numeral r1, r2; unsigned bv_size; bool is_num1 = m_util.is_numeral(arg1, r1, bv_size); bool is_num2 = m_util.is_numeral(arg2, r2, bv_size); if (is_num2 && r2.is_zero() && !m_params.m_hi_div0) { result = m_manager.mk_app(m_fid, OP_BSDIV0, arg1); } else if (is_num1 && is_num2 && !r2.is_zero()) { r1 = norm(r1, bv_size, true); r2 = norm(r2, bv_size, true); result = mk_numeral(machine_div(r1, r2), bv_size); } else if (is_num2 || m_params.m_hi_div0) { result = m_manager.mk_app(m_fid, OP_BSDIV_I, arg1, arg2); } else { bv_size = get_bv_size(arg2); result = m_manager.mk_ite(m_manager.mk_eq(arg2, mk_numeral(0, bv_size)), m_manager.mk_app(m_fid, OP_BSDIV0, arg1), m_manager.mk_app(m_fid, OP_BSDIV_I, arg1, arg2)); } } void bv_simplifier_plugin::mk_bv_udiv(expr* arg1, expr* arg2, expr_ref& result) { numeral r1, r2; unsigned bv_size; bool is_num1 = m_util.is_numeral(arg1, r1, bv_size); bool is_num2 = m_util.is_numeral(arg2, r2, bv_size); if (is_num2 && r2.is_zero() && !m_params.m_hi_div0) { result = m_manager.mk_app(m_fid, OP_BUDIV0, arg1); } else if (is_num1 && is_num2 && !r2.is_zero()) { SASSERT(r1.is_nonneg()); SASSERT(r2.is_nonneg()); result = mk_numeral(machine_div(r1, r2), bv_size); } else if (is_num2 || m_params.m_hi_div0) { result = m_manager.mk_app(m_fid, OP_BUDIV_I, arg1, arg2); } else { bv_size = get_bv_size(arg2); result = m_manager.mk_ite(m_manager.mk_eq(arg2, mk_numeral(0, bv_size)), m_manager.mk_app(m_fid, OP_BUDIV0, arg1), m_manager.mk_app(m_fid, OP_BUDIV_I, arg1, arg2)); } } void bv_simplifier_plugin::mk_bv_srem(expr* arg1, expr* arg2, expr_ref& result) { numeral r1, r2; unsigned bv_size; bool is_num1 = m_util.is_numeral(arg1, r1, bv_size); bool is_num2 = m_util.is_numeral(arg2, r2, bv_size); if (is_num2 && r2.is_zero() && !m_params.m_hi_div0) { result = m_manager.mk_app(m_fid, OP_BSREM0, arg1); } else if (is_num1 && is_num2 && !r2.is_zero()) { r1 = norm(r1, bv_size, true); r2 = norm(r2, bv_size, true); result = mk_numeral(r1 % r2, bv_size); } else if (is_num2 || m_params.m_hi_div0) { result = m_manager.mk_app(m_fid, OP_BSREM_I, arg1, arg2); } else { bv_size = get_bv_size(arg2); result = m_manager.mk_ite(m_manager.mk_eq(arg2, mk_numeral(0, bv_size)), m_manager.mk_app(m_fid, OP_BSREM0, arg1), m_manager.mk_app(m_fid, OP_BSREM_I, arg1, arg2)); } } void bv_simplifier_plugin::mk_bv_smod(expr* arg1, expr* arg2, expr_ref& result) { numeral r1, r2; unsigned bv_size; bool is_num1 = m_util.is_numeral(arg1, r1, bv_size); bool is_num2 = m_util.is_numeral(arg2, r2, bv_size); if (is_num1) r1 = m_util.norm(r1, bv_size, true); if (is_num2) r2 = m_util.norm(r2, bv_size, true); TRACE("bv_simplifier", tout << mk_pp(arg1, m_manager) << " smod " << mk_pp(arg2, m_manager) << "\n"; ); if (is_num2 && r2.is_zero()) { if (!m_params.m_hi_div0) result = m_manager.mk_app(m_fid, OP_BSMOD0, arg1); else result = arg1; } else if (is_num1 && is_num2) { SASSERT(!r2.is_zero()); numeral abs_r1 = m_util.norm(abs(r1), bv_size); numeral abs_r2 = m_util.norm(abs(r2), bv_size); numeral u = m_util.norm(abs_r1 % abs_r2, bv_size); numeral r; if (u.is_zero()) r = u; else if (r1.is_pos() && r2.is_pos()) r = u; else if (r1.is_neg() && r2.is_pos()) r = m_util.norm(-u + r2, bv_size); else if (r1.is_pos() && r2.is_neg()) r = m_util.norm(u + r2, bv_size); else r = m_util.norm(-u, bv_size); result = mk_numeral(r, bv_size); } else if (is_num2 || m_params.m_hi_div0) { result = m_manager.mk_app(m_fid, OP_BSMOD_I, arg1, arg2); } else { bv_size = get_bv_size(arg2); result = m_manager.mk_ite(m_manager.mk_eq(arg2, mk_numeral(0, bv_size)), m_manager.mk_app(m_fid, OP_BSMOD0, arg1), m_manager.mk_app(m_fid, OP_BSMOD_I, arg1, arg2)); } } uint64 bv_simplifier_plugin::n64(expr* e) { numeral r; unsigned bv_size; if (m_util.is_numeral(e, r, bv_size) && bv_size <= 64) { return r.get_uint64(); } UNREACHABLE(); return 0; } rational bv_simplifier_plugin::num(expr* e) { numeral r; unsigned bv_size; if (!m_util.is_numeral(e, r, bv_size)) { UNREACHABLE(); } return r; } void bv_simplifier_plugin::mk_bv_nand(unsigned num_args, expr* const* args, expr_ref& result) { unsigned bv_size; if (are_numerals(num_args, args, bv_size)) { if (bv_size <= 64) { uint64 r = n64(args[0]); for (unsigned i = 1; i < num_args; i++) { r &= n64(args[i]); } result = mk_numeral(~r, bv_size); } else { numeral r = num(args[0]); for (unsigned i = 1; i < num_args; i++) { r = mk_bv_and(r, num(args[i]), bv_size); } result = mk_numeral(mk_bv_not(r, bv_size), bv_size); } } else { result = m_manager.mk_app(m_fid, OP_BNAND, num_args, args); } } void bv_simplifier_plugin::mk_bv_nor(unsigned num_args, expr* const* args, expr_ref& result) { unsigned bv_size; if (are_numerals(num_args, args, bv_size)) { if (bv_size <= 64) { uint64 r = n64(args[0]); for (unsigned i = 1; i < num_args; i++) { r |= n64(args[i]); } result = mk_numeral(~r, bv_size); } else { numeral r = num(args[0]); for (unsigned i = 1; i < num_args; i++) { r = mk_bv_or(r, num(args[i]), bv_size); } result = mk_numeral(mk_bv_not(r, bv_size), bv_size); } } else { result = m_manager.mk_app(m_fid, OP_BNOR, num_args, args); } } void bv_simplifier_plugin::mk_bv_xnor(unsigned num_args, expr* const* args, expr_ref& result) { unsigned bv_size; if (are_numerals(num_args, args, bv_size)) { if (bv_size <= 64) { uint64 r = n64(args[0]); for (unsigned i = 1; i < num_args; i++) { r ^= n64(args[i]); } result = mk_numeral(~r, bv_size); } else { numeral r = num(args[0]); for (unsigned i = 1; i < num_args; i++) { r = mk_bv_xor(r, num(args[i]), bv_size); } result = mk_numeral(mk_bv_not(r, bv_size), bv_size); } } else { result = m_manager.mk_app(m_fid, OP_BXNOR, num_args, args); } } void bv_simplifier_plugin::mk_bv_rotate_left_core(unsigned shift, numeral r, unsigned bv_size, expr_ref& result) { SASSERT(shift < bv_size); if (bv_size <= 64) { uint64 a = r.get_uint64(); uint64 r = shift_left(a, shift) | shift_right(a, bv_size - shift); result = mk_numeral(r, bv_size); } else { rational r1 = div(r, rational::power_of_two(bv_size - shift)); // shift right rational r2 = (r * rational::power_of_two(shift)) % rational::power_of_two(bv_size); // shift left result = mk_numeral(r1 + r2, bv_size); } } void bv_simplifier_plugin::mk_bv_rotate_left(func_decl* f, expr* arg, expr_ref& result) { numeral r; unsigned bv_size; SASSERT(f->get_decl_kind() == OP_ROTATE_LEFT); if (m_util.is_numeral(arg, r, bv_size)) { unsigned shift = f->get_parameter(0).get_int() % bv_size; mk_bv_rotate_left_core(shift, r, bv_size, result); } else { result = m_manager.mk_app(f, arg); } } void bv_simplifier_plugin::mk_bv_rotate_right_core(unsigned shift, numeral r, unsigned bv_size, expr_ref& result) { SASSERT(shift < bv_size); if (bv_size <= 64) { uint64 a = r.get_uint64(); uint64 r = shift_right(a, shift) | shift_left(a, bv_size - shift); result = mk_numeral(r, bv_size); } else { rational r1 = div(r, rational::power_of_two(shift)); // shift right rational r2 = (r * rational::power_of_two(bv_size - shift)) % rational::power_of_two(bv_size); // shift left result = mk_numeral(r1 + r2, bv_size); } } void bv_simplifier_plugin::mk_bv_rotate_right(func_decl* f, expr* arg, expr_ref& result) { numeral r; unsigned bv_size; SASSERT(f->get_decl_kind() == OP_ROTATE_RIGHT); if (m_util.is_numeral(arg, r, bv_size)) { unsigned shift = f->get_parameter(0).get_int() % bv_size; mk_bv_rotate_right_core(shift, r, bv_size, result); } else { result = m_manager.mk_app(f, arg); } } void bv_simplifier_plugin::mk_bv_redor(expr* arg, expr_ref& result) { if (is_numeral(arg)) { result = m_util.is_zero(arg)?mk_numeral(0, 1):mk_numeral(1,1); } else { result = m_manager.mk_app(m_fid, OP_BREDOR, arg); } } void bv_simplifier_plugin::mk_bv_redand(expr* arg, expr_ref& result) { numeral r; unsigned bv_size; if (m_util.is_numeral(arg, r, bv_size)) { numeral allone = mk_allone(bv_size); result = mk_numeral((r == allone)?1:0, 1); } else { result = m_manager.mk_app(m_fid, OP_BREDAND, arg); } } void bv_simplifier_plugin::mk_bv_comp(expr* arg1, expr* arg2, expr_ref& result) { numeral r1, r2; if (arg1 == arg2) { result = mk_numeral(1,1); } else if (is_numeral(arg1, r1) && is_numeral(arg2, r2)) { result = mk_numeral((r1 == r2)?1:0, 1); } else { result = m_manager.mk_app(m_fid, OP_BCOMP, arg1, arg2); } } void bv_simplifier_plugin::mk_bv_ashr(expr* arg1, expr* arg2, expr_ref& result) { numeral r1, r2; unsigned bv_size = get_bv_size(arg1); bool is_num1 = m_util.is_numeral(arg1, r1, bv_size); bool is_num2 = m_util.is_numeral(arg2, r2, bv_size); if (bv_size == 0) { result = mk_numeral(rational(0), bv_size); } else if (is_num2 && r2.is_zero()) { result = arg1; } else if (bv_size <= 64 && is_num1 && is_num2) { uint64 n1 = n64(arg1); uint64 n2_orig = n64(arg2); uint64 n2 = n2_orig % bv_size; SASSERT(n2 < bv_size); uint64 r = shift_right(n1, n2); bool sign = (n1 & shift_left(1ull, bv_size - 1ull)) != 0; if (n2_orig > n2) { if (sign) { r = shift_left(1ull, bv_size) - 1ull; } else { r = 0; } } else if (sign) { uint64 allone = shift_left(1ull, bv_size) - 1ull; uint64 mask = ~(shift_left(1ull, bv_size - n2) - 1ull); mask &= allone; r |= mask; } result = mk_numeral(r, bv_size); TRACE("bv", tout << mk_pp(arg1, m_manager) << " >> " << mk_pp(arg2, m_manager) << " = " << mk_pp(result.get(), m_manager) << "\n"; tout << n1 << " >> " << n2 << " = " << r << "\n"; ); } else if (is_num1 && is_num2 && rational(bv_size) <= r2) { if (has_sign_bit(r1, bv_size)) { result = mk_numeral(mk_allone(bv_size), bv_size); } else { result = mk_bv0(bv_size); } } else if (is_num1 && is_num2) { SASSERT(r2 < rational(bv_size)); bool sign = has_sign_bit(r1, bv_size); r1 = div(r1, rational::power_of_two(r2.get_unsigned())); if (sign) { // pad ones. rational p(1); for (unsigned i = 0; i < bv_size; ++i) { if (r1 < p) { r1 += p; } p *= rational(2); } } result = mk_numeral(r1, bv_size); } else if (shift_shift(OP_BASHR, arg1, arg2, result)) { // done } else { result = m_manager.mk_app(m_fid, OP_BASHR, arg1, arg2); } } void bv_simplifier_plugin::mk_bv_ext_rotate_right(expr* arg1, expr* arg2, expr_ref& result) { numeral r2; unsigned bv_size; if (m_util.is_numeral(arg2, r2, bv_size)) { unsigned shift = static_cast((r2 % numeral(bv_size)).get_uint64() % static_cast(bv_size)); numeral r1; if (is_numeral(arg1, r1)) { mk_bv_rotate_right_core(shift, r1, bv_size, result); } else { parameter p(shift); result = m_manager.mk_app(m_fid, OP_ROTATE_RIGHT, 1, &p, 1, &arg1); } } else { result = m_manager.mk_app(m_fid, OP_EXT_ROTATE_RIGHT, arg1, arg2); } } void bv_simplifier_plugin::mk_bv_ext_rotate_left(expr* arg1, expr* arg2, expr_ref& result) { numeral r2; unsigned bv_size; if (m_util.is_numeral(arg2, r2, bv_size)) { unsigned shift = static_cast((r2 % numeral(bv_size)).get_uint64() % static_cast(bv_size)); numeral r1; if (is_numeral(arg1, r1)) { mk_bv_rotate_left_core(shift, r1, bv_size, result); } else { parameter p(shift); result = m_manager.mk_app(m_fid, OP_ROTATE_LEFT, 1, &p, 1, &arg1); } } else { result = m_manager.mk_app(m_fid, OP_EXT_ROTATE_LEFT, arg1, arg2); } } void bv_simplifier_plugin::bit2bool_simplify(unsigned idx, expr* e, expr_ref& result) { parameter p(idx); ptr_vector todo; expr_ref_vector pinned(m_manager); ptr_vector cache; todo.push_back(e); expr* e0 = e; ptr_vector argv; expr_ref tmp(m_manager); while (!todo.empty()) { e = todo.back(); unsigned e_id = e->get_id(); if (e_id >= cache.size()) { cache.resize(e_id+1,0); } if (cache[e_id]) { todo.pop_back(); continue; } if (!m_util.is_numeral(e) && !m_util.is_bv_and(e) && !m_util.is_bv_or(e) && !(is_app_of(e, m_fid, OP_BXOR) && to_app(e)->get_num_args() == 2) && !m_manager.is_ite(e) && !m_util.is_concat(e) && !m_util.is_bv_not(e)) { expr_ref extr(m_manager); extr = m_util.mk_extract(idx, idx, e); cache[e_id] = m_manager.mk_eq(m_util.mk_numeral(1, 1), extr); pinned.push_back(cache[e_id]); todo.pop_back(); continue; } app* a = to_app(e); unsigned sz = a->get_num_args(); if (m_util.is_concat(e)) { // look for first argument unsigned idx1 = idx; while (sz > 0) { --sz; expr * a_i = a->get_arg(sz); unsigned a_sz = get_bv_size(a_i); if (a_sz <= idx1) { idx1 -= a_sz; } else { // idx < a_sz; bit2bool_simplify(idx1, a_i, tmp); pinned.push_back(tmp); cache[e_id] = to_app(tmp); break; } } todo.pop_back(); continue; } argv.reset(); for (unsigned i = 0; i < sz; ++i) { expr* arg_i = a->get_arg(i); if (i == 0 && m_manager.is_ite(e)) { argv.push_back(arg_i); } else if (cache.size() > arg_i->get_id() && cache[arg_i->get_id()]) { argv.push_back(cache[arg_i->get_id()]); } else { todo.push_back(arg_i); } } if (sz != argv.size()) { continue; } todo.pop_back(); rational val; unsigned num_bits; if (m_util.is_numeral(e, val, num_bits)) { rational two(2); for (unsigned i = 0; i < idx; ++i) { val = div(val, two); } bool is_pos = !(val % two).is_zero(); tmp = is_pos?m_manager.mk_true():m_manager.mk_false(); } else if (m_util.is_bv_and(e)) { //tmp = m_manager.mk_and(sz, argv.c_ptr()); m_bsimp.mk_and(sz, argv.c_ptr(), tmp); pinned.push_back(tmp); } else if (m_util.is_bv_or(e)) { //tmp = m_manager.mk_or(sz, argv.c_ptr()); m_bsimp.mk_or(sz, argv.c_ptr(), tmp); pinned.push_back(tmp); } else if (m_util.is_bv_not(e)) { //tmp = m_manager.mk_not(argv[0]); m_bsimp.mk_not(argv[0], tmp); pinned.push_back(tmp); } else if (is_app_of(e, m_fid, OP_BXOR)) { SASSERT(argv.size() == 2); m_bsimp.mk_xor(argv[0], argv[1], tmp); pinned.push_back(tmp); } else if (m_manager.is_ite(e)) { //tmp = m_manager.mk_ite(argv[0], argv[1], argv[2]); m_bsimp.mk_ite(argv[0], argv[1], argv[2], tmp); pinned.push_back(tmp); } else { UNREACHABLE(); } cache[e_id] = to_app(tmp); } result = cache[e0->get_id()]; } // replace addition by concatenation. void bv_simplifier_plugin::mk_add_concat(expr_ref& result) { if (!m_util.is_bv_add(result)) { return; } app* a = to_app(result); if (a->get_num_args() != 2) { return; } expr* x = a->get_arg(0); expr* y = a->get_arg(1); if (!m_util.is_concat(x)) { std::swap(x, y); } if (!m_util.is_concat(x)) { return; } unsigned sz = m_util.get_bv_size(x); #if 0 // optimzied version. Seems not worth it.. #define UPDATE_CURR(_curr1, _idx1,_x,_is_num, _i) \ if (_idx1 >= m_util.get_bv_size(_curr1)) { \ _curr1 = _x; \ _idx1 = _i; \ _is_num = false; \ } \ while (m_util.is_concat(_curr1)) { \ _is_num = false; \ unsigned num_args = to_app(_curr1)->get_num_args(); \ while (true) { \ --num_args; \ expr* c1 = to_app(_curr1)->get_arg(num_args); \ unsigned sz1 = m_util.get_bv_size(c1); \ if (sz1 < _idx1) { \ _idx1 -= sz1; \ } \ else { \ _curr1 = c1; \ break; \ } \ } \ } unsigned idx1 = 0, idx2 = 0; expr* curr1 = x, *curr2 = y; bool is_num1 = false, is_num2 = false; rational val1, val2; rational two(2); for (unsigned i = 0; i < sz; ++i, ++idx1, ++idx2) { UPDATE_CURR(curr1, idx1, x, is_num1, i); UPDATE_CURR(curr2, idx2, y, is_num2, i); if (idx1 == 0 && m_util.is_numeral(curr1, val1, bv_size)) { is_num1 = true; } if (idx2 == 0 && m_util.is_numeral(curr2, val2, bv_size)) { is_num2 = true; } if ((is_num1 && (val1 % two).is_zero()) || (is_num2 && (val2 % two).is_zero())) { val1 = div(val1, two); val2 = div(val2, two); continue; } return; } mk_bv_or(2, a->get_args(), result); #endif for (unsigned i = 0; i < sz; ++i) { if (!is_zero_bit(x,i) && !is_zero_bit(y,i)) { return; } } mk_bv_or(2, a->get_args(), result); } bool bv_simplifier_plugin::is_zero_bit(expr* x, unsigned idx) { rational val; unsigned bv_size; if (m_util.is_numeral(x, val, bv_size)) { if (val.is_zero()) { return true; } rational two(2); while (idx > 0) { val = div(val, two); idx--; } return (val % two).is_zero(); } if (m_util.is_concat(x)) { unsigned num_args = to_app(x)->get_num_args(); while (num_args > 0) { --num_args; expr* y = to_app(x)->get_arg(num_args); bv_size = m_util.get_bv_size(y); if (bv_size <= idx) { idx -= bv_size; } else { return is_zero_bit(y, idx); } } UNREACHABLE(); } return false; } z3-4.4.0/src/ast/simplifier/simplifier_plugin.cpp0000644000175000017500000000204212540347414021611 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: simplifier_plugin.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-12-29. Revision History: --*/ #include"simplifier_plugin.h" /** \brief Copy every args[i] mult[i] times to new_args. */ void expand_args(unsigned num_args, rational const * mults, expr * const * args, ptr_buffer & new_args) { for (unsigned i = 0; i < num_args; i++) { rational const & c = mults[i]; SASSERT(c.is_int()); rational j(0); while (j < c) { new_args.push_back(args[i]); j++; } } } bool simplifier_plugin::reduce(func_decl * f, unsigned num_args, rational const * mults, expr * const * args, expr_ref & result) { set_reduce_invoked(); if (f->is_idempotent()) { return reduce(f, num_args, args, result); } else { ptr_buffer new_args; expand_args(num_args, mults, args, new_args); return reduce(f, new_args.size(), new_args.c_ptr(), result); } } z3-4.4.0/src/ast/simplifier/README0000644000175000017500000000022712540347414016247 0ustar michaelmichaelSimplifier module is now obsolete. It is still being used in many places, but we will eventually replace all occurrences with the new rewriter module. z3-4.4.0/src/ast/simplifier/bv_simplifier_params_helper.pyg0000644000175000017500000000104312540347414023641 0ustar michaelmichaeldef_module_params(class_name='bv_simplifier_params_helper', module_name="old_simplify", # Parameters will be in the old_simplify module export=True, params=( ('bv.hi_div0', BOOL, True, 'if true, then Z3 uses the usual hardware interpretation for division (rem, mod) by zero; otherwise, these operations are considered uninterpreted'), ('bv.bv2int_distribute', BOOL, True, 'if true, then int2bv is distributed over arithmetical operators'))) z3-4.4.0/src/ast/simplifier/bv_simplifier_plugin.h0000644000175000017500000002023612540347414021752 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: bv_simplifier_plugin.h Abstract: Simplifier for the bv family. Author: Leonardo (leonardo) 2008-01-08 --*/ #ifndef _BV_SIMPLIFIER_PLUGIN_H_ #define _BV_SIMPLIFIER_PLUGIN_H_ #include"basic_simplifier_plugin.h" #include"poly_simplifier_plugin.h" #include"bv_decl_plugin.h" #include"map.h" #include"bv_simplifier_params.h" #include"arith_decl_plugin.h" /** \brief Simplifier for the bv family. */ class bv_simplifier_plugin : public poly_simplifier_plugin { typedef rational numeral; struct extract_entry { unsigned m_high; unsigned m_low; expr * m_arg; extract_entry():m_high(0), m_low(0), m_arg(0) {} extract_entry(unsigned h, unsigned l, expr * n):m_high(h), m_low(l), m_arg(n) {} unsigned hash() const { unsigned a = m_high; unsigned b = m_low; unsigned c = m_arg->get_id(); mix(a,b,c); return c; } bool operator==(const extract_entry & e) const { return m_high == e.m_high && m_low == e.m_low && m_arg == e.m_arg; } struct hash_proc { unsigned operator()(extract_entry const& e) const { return e.hash(); } }; struct eq_proc { bool operator()(extract_entry const& a, extract_entry const& b) const { return a == b; } }; }; typedef map extract_cache; protected: ast_manager& m_manager; bv_util m_util; arith_util m_arith; basic_simplifier_plugin & m_bsimp; bv_simplifier_params & m_params; expr_ref_vector m_zeros; extract_cache m_extract_cache; unsigned_vector m_lows, m_highs; ptr_vector m_extract_args; rational mk_bv_and(numeral const& a0, numeral const& b0, unsigned sz); rational mk_bv_or(numeral const& a0, numeral const& b0, unsigned sz); rational mk_bv_xor(numeral const& a0, numeral const& b0, unsigned sz); rational mk_bv_not(numeral const& a0, unsigned sz); rational num(expr* e); bool has_sign_bit(numeral const& n, unsigned bv_size) { return m_util.has_sign_bit(n, bv_size); } bool shift_shift(bv_op_kind k, expr* arg1, expr* arg2, expr_ref& result); void bit2bool_simplify(unsigned idx, expr* e, expr_ref& result); void mk_add_concat(expr_ref& result); bool is_zero_bit(expr* x, unsigned idx); void mk_bv_rotate_left_core(unsigned shift, numeral r, unsigned bv_size, expr_ref& result); void mk_bv_rotate_right_core(unsigned shift, numeral r, unsigned bv_size, expr_ref& result); public: bv_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b, bv_simplifier_params & p); virtual ~bv_simplifier_plugin(); // simplifier_plugin: virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result); virtual bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result); virtual void flush_caches(); // poly_simplifier_plugin virtual rational norm(const rational & n); virtual bool is_numeral(expr * n, rational & val) const; bool is_numeral(expr * n) const { return m_util.is_numeral(n); } virtual bool is_minus_one(expr * n) const { return is_minus_one_core(n); } virtual expr * get_zero(sort * s) const; virtual app * mk_numeral(rational const & n); bool is_bv(expr * n) const { return m_util.is_bv(n); } bool is_bv_sort(sort * s) const { return m_util.is_bv_sort(s); } bool is_le(expr * n) const { return m_util.is_bv_ule(n) || m_util.is_bv_sle(n); } // REMARK: simplified bv expressions are never of the form a >= b. virtual bool is_le_ge(expr * n) const { return is_le(n); } uint64 to_uint64(const numeral & n, unsigned bv_size); rational norm(rational const& n, unsigned bv_size, bool is_signed) { return m_util.norm(n, bv_size, is_signed); } unsigned get_bv_size(expr const * n) { return get_bv_size(m_manager.get_sort(n)); } unsigned get_bv_size(sort const * s) { return m_util.get_bv_size(s); } void mk_leq_core(bool is_signed, expr * arg1, expr * arg2, expr_ref & result); void mk_ule(expr* a, expr* b, expr_ref& result); void mk_ult(expr* a, expr* b, expr_ref& result); void mk_sle(expr* a, expr* b, expr_ref& result); void mk_slt(expr* a, expr* b, expr_ref& result); void mk_bv_and(unsigned num_args, expr * const* args, expr_ref & result); void mk_bv_or(unsigned num_args, expr * const* args, expr_ref & result); void mk_bv_xor(unsigned num_args, expr * const* args, expr_ref & result); void mk_bv_not(expr * arg, expr_ref & result); void mk_extract(unsigned hi,unsigned lo, expr* bv, expr_ref& result); void mk_extract_core(unsigned high, unsigned low, expr * arg, expr_ref& result); void cache_extract(unsigned h, unsigned l, expr * arg, expr * result); expr* get_cached_extract(unsigned h, unsigned l, expr * arg); bool lookup_mk_extract(unsigned high, unsigned low, expr * arg, expr_ref& result); bool try_mk_extract(unsigned high, unsigned low, expr * arg, expr_ref& result); void mk_bv_eq(expr* a1, expr* a2, expr_ref& result); void mk_eq_core(expr * arg1, expr * arg2, expr_ref & result); void mk_args_eq_numeral(app * app, expr * n, expr_ref & result); void mk_concat(unsigned num_args, expr * const * args, expr_ref & result); void mk_concat(expr * arg1, expr * arg2, expr_ref & result) { expr * args[2] = { arg1, arg2 }; mk_concat(2, args, result); } void mk_zeroext(unsigned n, expr * arg, expr_ref & result); void mk_repeat(unsigned n, expr * arg, expr_ref & result); void mk_sign_extend(unsigned n, expr * arg, expr_ref & result); void mk_bv_lshr(expr * arg1, expr * arg2, expr_ref & result); void mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result); void mk_bv_ashr(expr* arg1, expr* arg2, expr_ref& result); void mk_bv_smod(expr* arg1, expr* arg2, expr_ref& result); void mk_bv_urem(expr * arg1, expr * arg2, expr_ref & result); void mk_bv_srem(expr* arg1, expr* arg2, expr_ref& result); void mk_bv_udiv(expr* arg1, expr* arg2, expr_ref& result); void mk_bv_sdiv(expr* arg1, expr* arg2, expr_ref& result); void mk_bv_smod_i(expr* arg1, expr* arg2, expr_ref& result); void mk_bv_urem_i(expr * arg1, expr * arg2, expr_ref & result); void mk_bv_srem_i(expr* arg1, expr* arg2, expr_ref& result); void mk_bv_udiv_i(expr* arg1, expr* arg2, expr_ref& result); void mk_bv_sdiv_i(expr* arg1, expr* arg2, expr_ref& result); void mk_bv_nand(unsigned num_args, expr* const* args, expr_ref& result); void mk_bv_nor(unsigned num_args, expr* const* args, expr_ref& result); void mk_bv_xnor(unsigned num_args, expr* const* args, expr_ref& result); void mk_bv_rotate_right(func_decl* f, expr* arg, expr_ref& result); void mk_bv_rotate_left(func_decl* f, expr* arg, expr_ref& result); void mk_bv_ext_rotate_right(expr* arg1, expr* arg2, expr_ref& result); void mk_bv_ext_rotate_left(expr* arg1, expr* arg2, expr_ref& result); void mk_bv_redor(expr* arg, expr_ref& result); void mk_bv_redand(expr* arg, expr_ref& result); void mk_bv_comp(expr* arg1, expr* arg2, expr_ref& result); bool are_numerals(unsigned num_args, expr * const* args, unsigned& bv_size); app * mk_numeral(rational const & n, unsigned bv_size); app * mk_numeral(uint64 n, unsigned bv_size) { return mk_numeral(numeral(n, numeral::ui64()), bv_size); } app* mk_bv0(unsigned bv_size) { return m_util.mk_numeral(numeral(0), bv_size); } rational mk_allone(unsigned bv_size) { return rational::power_of_two(bv_size) - numeral(1); } bool is_minus_one_core(expr * arg) const; bool is_x_minus_one(expr * arg, expr * & x); void mk_int2bv(expr * arg, sort* range, expr_ref & result); void mk_bv2int(expr * arg, sort* range, expr_ref & result); uint64 n64(expr* e); bool is_mul_no_overflow(expr* e); bool is_add_no_overflow(expr* e); unsigned num_leading_zero_bits(expr* e); }; #endif /* _BV_SIMPLIFIER_PLUGIN_H_ */ z3-4.4.0/src/ast/simplifier/arith_simplifier_plugin.cpp0000644000175000017500000003426712540347414023016 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: arith_simplifier_plugin.cpp Abstract: Simplifier for the arithmetic family. Author: Leonardo (leonardo) 2008-01-08 --*/ #include"arith_simplifier_plugin.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"ast_smt2_pp.h" arith_simplifier_plugin::~arith_simplifier_plugin() { } arith_simplifier_plugin::arith_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b, arith_simplifier_params & p): poly_simplifier_plugin(symbol("arith"), m, OP_ADD, OP_MUL, OP_UMINUS, OP_SUB, OP_NUM), m_params(p), m_util(m), m_bsimp(b), m_int_zero(m), m_real_zero(m) { m_int_zero = m_util.mk_numeral(rational(0), true); m_real_zero = m_util.mk_numeral(rational(0), false); } /** \brief Return true if the first monomial of t is negative. */ bool arith_simplifier_plugin::is_neg_poly(expr * t) const { if (m_util.is_add(t)) { t = to_app(t)->get_arg(0); } if (m_util.is_mul(t)) { t = to_app(t)->get_arg(0); rational r; bool is_int; if (m_util.is_numeral(t, r, is_int)) return r.is_neg(); } return false; } void arith_simplifier_plugin::get_monomial_gcd(expr_ref_vector& monomials, numeral& g) { g = numeral::zero(); numeral n; for (unsigned i = 0; !g.is_one() && i < monomials.size(); ++i) { expr* e = monomials[i].get(); if (is_numeral(e, n)) { g = gcd(abs(n), g); } else if (is_mul(e) && is_numeral(to_app(e)->get_arg(0), n)) { g = gcd(abs(n), g); } else { g = numeral::one(); return; } } if (g.is_zero()) { g = numeral::one(); } } void arith_simplifier_plugin::div_monomial(expr_ref_vector& monomials, numeral const& g) { numeral n; for (unsigned i = 0; i < monomials.size(); ++i) { expr* e = monomials[i].get(); if (is_numeral(e, n)) { SASSERT((n/g).is_int()); monomials[i] = mk_numeral(n/g); } else if (is_mul(e) && is_numeral(to_app(e)->get_arg(0), n)) { SASSERT((n/g).is_int()); monomials[i] = mk_mul(n/g, to_app(e)->get_arg(1)); } else { UNREACHABLE(); } } } void arith_simplifier_plugin::gcd_reduce_monomial(expr_ref_vector& monomials, numeral& k) { numeral g, n; get_monomial_gcd(monomials, g); g = gcd(abs(k), g); if (g.is_one()) { return; } SASSERT(g.is_pos()); k = k / g; div_monomial(monomials, g); } template void arith_simplifier_plugin::mk_le_ge_eq_core(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(arg1); bool is_int = m_curr_sort->get_decl_kind() == INT_SORT; expr_ref_vector monomials(m_manager); rational k; TRACE("arith_eq_bug", tout << mk_ismt2_pp(arg1, m_manager) << "\n" << mk_ismt2_pp(arg2, m_manager) << "\n";); process_sum_of_monomials(false, arg1, monomials, k); process_sum_of_monomials(true, arg2, monomials, k); k.neg(); if (is_int) { numeral g; get_monomial_gcd(monomials, g); if (!g.is_one()) { div_monomial(monomials, g); switch(Kind) { case LE: // // g*monmials' <= k // <=> // monomials' <= floor(k/g) // k = floor(k/g); break; case GE: // // g*monmials' >= k // <=> // monomials' >= ceil(k/g) // k = ceil(k/g); break; case EQ: k = k/g; if (!k.is_int()) { result = m_manager.mk_false(); return; } break; } } } expr_ref lhs(m_manager); mk_sum_of_monomials(monomials, lhs); if (m_util.is_numeral(lhs)) { SASSERT(lhs == mk_zero()); if (( Kind == LE && numeral::zero() <= k) || ( Kind == GE && numeral::zero() >= k) || ( Kind == EQ && numeral::zero() == k)) result = m_manager.mk_true(); else result = m_manager.mk_false(); } else { if (is_neg_poly(lhs)) { expr_ref neg_lhs(m_manager); mk_uminus(lhs, neg_lhs); lhs = neg_lhs; k.neg(); expr * rhs = m_util.mk_numeral(k, is_int); switch (Kind) { case LE: result = m_util.mk_ge(lhs, rhs); break; case GE: result = m_util.mk_le(lhs, rhs); break; case EQ: result = m_manager.mk_eq(lhs, rhs); break; } } else { expr * rhs = m_util.mk_numeral(k, is_int); switch (Kind) { case LE: result = m_util.mk_le(lhs, rhs); break; case GE: result = m_util.mk_ge(lhs, rhs); break; case EQ: result = m_manager.mk_eq(lhs, rhs); break; } } } } void arith_simplifier_plugin::mk_arith_eq(expr * arg1, expr * arg2, expr_ref & result) { mk_le_ge_eq_core(arg1, arg2, result); } void arith_simplifier_plugin::mk_le(expr * arg1, expr * arg2, expr_ref & result) { mk_le_ge_eq_core(arg1, arg2, result); } void arith_simplifier_plugin::mk_ge(expr * arg1, expr * arg2, expr_ref & result) { mk_le_ge_eq_core(arg1, arg2, result); } void arith_simplifier_plugin::mk_lt(expr * arg1, expr * arg2, expr_ref & result) { expr_ref tmp(m_manager); mk_le(arg2, arg1, tmp); m_bsimp.mk_not(tmp, result); } void arith_simplifier_plugin::mk_gt(expr * arg1, expr * arg2, expr_ref & result) { expr_ref tmp(m_manager); mk_le(arg1, arg2, tmp); m_bsimp.mk_not(tmp, result); } void arith_simplifier_plugin::gcd_normalize(numeral & coeff, expr_ref& term) { if (!abs(coeff).is_one()) { set_curr_sort(term); SASSERT(m_curr_sort->get_decl_kind() == INT_SORT); expr_ref_vector monomials(m_manager); rational k; monomials.push_back(mk_numeral(numeral(coeff), true)); process_sum_of_monomials(false, term, monomials, k); gcd_reduce_monomial(monomials, k); numeral coeff1; if (!is_numeral(monomials[0].get(), coeff1)) { UNREACHABLE(); } if (coeff1 == coeff) { return; } monomials[0] = mk_numeral(k, true); coeff = coeff1; mk_sum_of_monomials(monomials, term); } } void arith_simplifier_plugin::mk_div(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(arg1); numeral v1, v2; bool is_int; if (m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) { SASSERT(!is_int); if (m_util.is_numeral(arg1, v1, is_int)) result = m_util.mk_numeral(v1/v2, false); else { numeral k(1); k /= v2; expr_ref inv_arg2(m_util.mk_numeral(k, false), m_manager); mk_mul(inv_arg2, arg1, result); } } else result = m_util.mk_div(arg1, arg2); } void arith_simplifier_plugin::mk_idiv(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(arg1); numeral v1, v2; bool is_int; if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) result = m_util.mk_numeral(div(v1, v2), is_int); else result = m_util.mk_idiv(arg1, arg2); } void arith_simplifier_plugin::prop_mod_const(expr * e, unsigned depth, numeral const& k, expr_ref& result) { SASSERT(m_util.is_int(e)); SASSERT(k.is_int() && k.is_pos()); numeral n; bool is_int; if (depth == 0) { result = e; } else if (is_add(e) || is_mul(e)) { expr_ref_vector args(m_manager); expr_ref tmp(m_manager); app* a = to_app(e); for (unsigned i = 0; i < a->get_num_args(); ++i) { prop_mod_const(a->get_arg(i), depth - 1, k, tmp); args.push_back(tmp); } reduce(a->get_decl(), args.size(), args.c_ptr(), result); } else if (m_util.is_numeral(e, n, is_int) && is_int) { result = mk_numeral(mod(n, k), true); } else { result = e; } } void arith_simplifier_plugin::mk_mod(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(arg1); numeral v1, v2; bool is_int; if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) { result = m_util.mk_numeral(mod(v1, v2), is_int); } else if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_one()) { result = m_util.mk_numeral(numeral(0), true); } else if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_pos()) { expr_ref tmp(m_manager); prop_mod_const(arg1, 5, v2, tmp); result = m_util.mk_mod(tmp, arg2); } else { result = m_util.mk_mod(arg1, arg2); } } void arith_simplifier_plugin::mk_rem(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(arg1); numeral v1, v2; bool is_int; if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) { numeral m = mod(v1, v2); // // rem(v1,v2) = if v2 >= 0 then mod(v1,v2) else -mod(v1,v2) // if (v2.is_neg()) { m.neg(); } result = m_util.mk_numeral(m, is_int); } else if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_one()) { result = m_util.mk_numeral(numeral(0), true); } else if (m_util.is_numeral(arg2, v2, is_int) && is_int && !v2.is_zero()) { expr_ref tmp(m_manager); prop_mod_const(arg1, 5, v2, tmp); result = m_util.mk_mod(tmp, arg2); if (v2.is_neg()) { result = m_util.mk_uminus(result); } } else { result = m_util.mk_rem(arg1, arg2); } } void arith_simplifier_plugin::mk_to_real(expr * arg, expr_ref & result) { numeral v; if (m_util.is_numeral(arg, v)) result = m_util.mk_numeral(v, false); else result = m_util.mk_to_real(arg); } void arith_simplifier_plugin::mk_to_int(expr * arg, expr_ref & result) { numeral v; if (m_util.is_numeral(arg, v)) result = m_util.mk_numeral(floor(v), true); else if (m_util.is_to_real(arg)) result = to_app(arg)->get_arg(0); else result = m_util.mk_to_int(arg); } void arith_simplifier_plugin::mk_is_int(expr * arg, expr_ref & result) { numeral v; if (m_util.is_numeral(arg, v)) result = v.is_int()?m_manager.mk_true():m_manager.mk_false(); else if (m_util.is_to_real(arg)) result = m_manager.mk_true(); else result = m_util.mk_is_int(arg); } bool arith_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { set_reduce_invoked(); SASSERT(f->get_family_id() == m_fid); TRACE("arith_simplifier_plugin", tout << mk_pp(f, m_manager) << "\n"; for (unsigned i = 0; i < num_args; i++) tout << mk_pp(args[i], m_manager) << "\n";); arith_op_kind k = static_cast(f->get_decl_kind()); switch (k) { case OP_NUM: return false; case OP_LE: if (m_presimp) return false; SASSERT(num_args == 2); mk_le(args[0], args[1], result); break; case OP_GE: if (m_presimp) return false; SASSERT(num_args == 2); mk_ge(args[0], args[1], result); break; case OP_LT: if (m_presimp) return false; SASSERT(num_args == 2); mk_lt(args[0], args[1], result); break; case OP_GT: if (m_presimp) return false; SASSERT(num_args == 2); mk_gt(args[0], args[1], result); break; case OP_ADD: mk_add(num_args, args, result); break; case OP_SUB: mk_sub(num_args, args, result); break; case OP_UMINUS: SASSERT(num_args == 1); mk_uminus(args[0], result); break; case OP_MUL: mk_mul(num_args, args, result); TRACE("arith_simplifier_plugin", tout << mk_pp(result, m_manager) << "\n";); break; case OP_DIV: SASSERT(num_args == 2); mk_div(args[0], args[1], result); break; case OP_IDIV: SASSERT(num_args == 2); mk_idiv(args[0], args[1], result); break; case OP_REM: SASSERT(num_args == 2); mk_rem(args[0], args[1], result); break; case OP_MOD: SASSERT(num_args == 2); mk_mod(args[0], args[1], result); break; case OP_TO_REAL: SASSERT(num_args == 1); mk_to_real(args[0], result); break; case OP_TO_INT: SASSERT(num_args == 1); mk_to_int(args[0], result); break; case OP_IS_INT: SASSERT(num_args == 1); mk_is_int(args[0], result); break; case OP_POWER: return false; case OP_ABS: SASSERT(num_args == 1); mk_abs(args[0], result); break; case OP_IRRATIONAL_ALGEBRAIC_NUM: return false; default: UNREACHABLE(); return false; } TRACE("arith_simplifier_plugin", tout << mk_pp(result.get(), m_manager) << "\n";); return true; } void arith_simplifier_plugin::mk_abs(expr * arg, expr_ref & result) { expr_ref c(m_manager); expr_ref m_arg(m_manager); mk_uminus(arg, m_arg); mk_ge(arg, m_util.mk_numeral(rational(0), m_util.is_int(arg)), c); m_bsimp.mk_ite(c, arg, m_arg, result); } bool arith_simplifier_plugin::is_arith_term(expr * n) const { return n->get_kind() == AST_APP && to_app(n)->get_family_id() == m_fid; } bool arith_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) { TRACE("reduce_eq_bug", tout << mk_ismt2_pp(lhs, m_manager) << "\n" << mk_ismt2_pp(rhs, m_manager) << "\n";); set_reduce_invoked(); if (m_presimp) { return false; } if (m_params.m_arith_expand_eqs) { expr_ref le(m_manager), ge(m_manager); mk_le_ge_eq_core(lhs, rhs, le); mk_le_ge_eq_core(lhs, rhs, ge); m_bsimp.mk_and(le, ge, result); return true; } if (m_params.m_arith_process_all_eqs || is_arith_term(lhs) || is_arith_term(rhs)) { mk_arith_eq(lhs, rhs, result); return true; } return false; } z3-4.4.0/src/ast/simplifier/pull_ite_tree.h0000644000175000017500000000534112540347414020376 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pull_ite_tree.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-22. Revision History: --*/ #ifndef _PULL_ITE_TREE_H_ #define _PULL_ITE_TREE_H_ #include"ast.h" #include"simplifier.h" #include"recurse_expr.h" #include"obj_hashtable.h" #include"expr_map.h" /** \brief Functor for applying the following transformation F[(p (ite c t1 t2) args)] = F'[(ite c t1 t2), p, args] F'[(ite c t1 t2), p, args] = (ite c F'[t1, p, args] F'[t2, p, args]) F'[t, p, args] = (p t args) */ class pull_ite_tree { ast_manager & m_manager; simplifier & m_simplifier; func_decl * m_p; ptr_vector m_args; unsigned m_arg_idx; //!< position of the ite argument expr_map m_cache; ptr_vector m_todo; bool is_cached(expr * n) const { return m_cache.contains(n); } void get_cached(expr * n, expr * & r, proof * & p) const { m_cache.get(n, r, p); } void cache_result(expr * n, expr * r, proof * pr); void visit(expr * n, bool & visited); bool visit_children(expr * n); void reduce(expr * n); /** \brief Creante an application (m_p ... n ...) where n is the argument m_arg_idx and the other arguments are in m_args. */ expr * mk_p_arg(expr * n) { m_args[m_arg_idx] = n; return m_manager.mk_app(m_p, m_args.size(), m_args.c_ptr()); } public: pull_ite_tree(ast_manager & m, simplifier & s); /** \brief Apply the transformation above if n contains an ite-expression. Store the result in r. If n does not contain an ite-expression, then store n in r. When proof generation is enabled, pr is a proof for n = r. */ void operator()(app * n, app_ref & r, proof_ref & pr); }; /** \brief Functor for applying the pull_ite_tree on subexpressions n that satisfy the is_target virtual predicate. */ class pull_ite_tree_star : public simplifier { protected: pull_ite_tree m_proc; virtual bool get_subst(expr * n, expr_ref & r, proof_ref & p); public: pull_ite_tree_star(ast_manager & m, simplifier & s); virtual ~pull_ite_tree_star() { release_plugins(); } virtual bool is_target(app * n) const = 0; }; /** \brief Apply pull_ite_tree on predicates of the form (p ite v) and (p v ite) where: - p is an interpreted predicate - ite is an ite-term expression - v is a value */ class pull_cheap_ite_tree_star : public pull_ite_tree_star { public: pull_cheap_ite_tree_star(ast_manager & m, simplifier & s):pull_ite_tree_star(m, s) {} virtual ~pull_cheap_ite_tree_star() {} virtual bool is_target(app * n) const; }; #endif /* _PULL_ITE_TREE_H_ */ z3-4.4.0/src/ast/simplifier/arith_simplifier_plugin.h0000644000175000017500000000715412540347414022456 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: arith_simplifier_plugin.h Abstract: Simplifier for the arithmetic family. Author: Leonardo (leonardo) 2008-01-08 --*/ #ifndef _ARITH_SIMPLIFIER_PLUGIN_H_ #define _ARITH_SIMPLIFIER_PLUGIN_H_ #include"basic_simplifier_plugin.h" #include"poly_simplifier_plugin.h" #include"arith_decl_plugin.h" #include"arith_simplifier_params.h" /** \brief Simplifier for the arith family. */ class arith_simplifier_plugin : public poly_simplifier_plugin { public: enum op_kind { LE, GE, EQ }; protected: arith_simplifier_params & m_params; arith_util m_util; basic_simplifier_plugin & m_bsimp; expr_ref m_int_zero; expr_ref m_real_zero; bool is_neg_poly(expr * t) const; template void mk_le_ge_eq_core(expr * arg1, expr * arg2, expr_ref & result); void prop_mod_const(expr * e, unsigned depth, numeral const& k, expr_ref& result); void gcd_reduce_monomial(expr_ref_vector& monomials, numeral& k); void div_monomial(expr_ref_vector& monomials, numeral const& g); void get_monomial_gcd(expr_ref_vector& monomials, numeral& g); public: arith_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b, arith_simplifier_params & p); ~arith_simplifier_plugin(); arith_util & get_arith_util() { return m_util; } virtual numeral norm(const numeral & n) { return n; } virtual bool is_numeral(expr * n, rational & val) const { bool f; return m_util.is_numeral(n, val, f); } bool is_numeral(expr * n) const { return m_util.is_numeral(n); } virtual bool is_minus_one(expr * n) const { numeral tmp; return is_numeral(n, tmp) && tmp.is_minus_one(); } virtual expr * get_zero(sort * s) const { return m_util.is_int(s) ? m_int_zero.get() : m_real_zero.get(); } virtual app * mk_numeral(numeral const & n) { return m_util.mk_numeral(n, m_curr_sort->get_decl_kind() == INT_SORT); } app * mk_numeral(numeral const & n, bool is_int) { return m_util.mk_numeral(n, is_int); } bool is_int_sort(sort const * s) const { return m_util.is_int(s); } bool is_real_sort(sort const * s) const { return m_util.is_real(s); } bool is_arith_sort(sort const * s) const { return is_int_sort(s) || is_real_sort(s); } bool is_int(expr const * n) const { return m_util.is_int(n); } bool is_le(expr const * n) const { return m_util.is_le(n); } bool is_ge(expr const * n) const { return m_util.is_ge(n); } virtual bool is_le_ge(expr * n) const { return is_le(n) || is_ge(n); } void mk_le(expr * arg1, expr * arg2, expr_ref & result); void mk_ge(expr * arg1, expr * arg2, expr_ref & result); void mk_lt(expr * arg1, expr * arg2, expr_ref & result); void mk_gt(expr * arg1, expr * arg2, expr_ref & result); void mk_arith_eq(expr * arg1, expr * arg2, expr_ref & result); void mk_div(expr * arg1, expr * arg2, expr_ref & result); void mk_idiv(expr * arg1, expr * arg2, expr_ref & result); void mk_mod(expr * arg1, expr * arg2, expr_ref & result); void mk_rem(expr * arg1, expr * arg2, expr_ref & result); void mk_to_real(expr * arg, expr_ref & result); void mk_to_int(expr * arg, expr_ref & result); void mk_is_int(expr * arg, expr_ref & result); void mk_abs(expr * arg, expr_ref & result); virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result); bool is_arith_term(expr * n) const; void gcd_normalize(numeral & coeff, expr_ref& term); }; #endif /* _ARITH_SIMPLIFIER_PLUGIN_H_ */ z3-4.4.0/src/ast/simplifier/inj_axiom.h0000644000175000017500000000054012540347414017513 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: inj_axiom.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-23. Revision History: --*/ #ifndef _INJ_AXIOM_H_ #define _INJ_AXIOM_H_ #include"ast.h" bool simplify_inj_axiom(ast_manager & m, quantifier * q, expr_ref & result); #endif /* _INJ_AXIOM_H_ */ z3-4.4.0/src/ast/simplifier/distribute_forall.cpp0000644000175000017500000001101312540347414021603 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: distribute_forall.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-04-02. Revision History: Christoph Wintersteiger 2010-04-06: Added implementation. --*/ #include"var_subst.h" #include"ast_ll_pp.h" #include"distribute_forall.h" distribute_forall::distribute_forall(ast_manager & m, basic_simplifier_plugin & p) : m_manager(m), m_bsimp(p), m_cache(m) { } void distribute_forall::visit(expr * n, bool & visited) { if (!is_cached(n)) { m_todo.push_back(n); visited = false; } } bool distribute_forall::visit_children(expr * n) { bool visited = true; unsigned j; switch(n->get_kind()) { case AST_VAR: break; case AST_APP: j = to_app(n)->get_num_args(); while (j > 0) { --j; visit(to_app(n)->get_arg(j), visited); } break; case AST_QUANTIFIER: visit(to_quantifier(n)->get_expr(), visited); break; default: UNREACHABLE(); } return visited; } void distribute_forall::reduce1(expr * n) { switch (n->get_kind()) { case AST_VAR: cache_result(n, n); break; case AST_APP: reduce1_app(to_app(n)); break; case AST_QUANTIFIER: reduce1_quantifier(to_quantifier(n)); break; default: UNREACHABLE(); } } void distribute_forall::reduce1_app(app * a) { SASSERT(a); unsigned num_args = a->get_num_args(); unsigned j = num_args; bool reduced = false; m_new_args.reserve(num_args); app * na = a; while(j > 0) { --j; SASSERT(is_cached(a->get_arg(j))); expr * c = get_cached(a->get_arg(j)); SASSERT(c!=0); if (c != a->get_arg(j)) reduced = true; m_new_args[j] = c; } if (reduced) { na = m_manager.mk_app(a->get_decl(), num_args, m_new_args.c_ptr()); } cache_result(a, na); } void distribute_forall::reduce1_quantifier(quantifier * q) { // This transformation is applied after skolemization/quantifier elimination. So, all quantifiers are universal. SASSERT(q->is_forall()); // This transformation is applied after basic pre-processing steps. // So, we can assume that // 1) All (and f1 ... fn) are already encoded as (not (or (not f1 ... fn))) // 2) All or-formulas are flat (or f1 (or f2 f3)) is encoded as (or f1 f2 f3) expr * e = get_cached(q->get_expr()); if (m_manager.is_not(e) && m_manager.is_or(to_app(e)->get_arg(0))) { // found target for simplification // (forall X (not (or F1 ... Fn))) // --> // (and (forall X (not F1)) // ... // (forall X (not Fn))) app * or_e = to_app(to_app(e)->get_arg(0)); unsigned num_args = or_e->get_num_args(); expr_ref_buffer new_args(m_manager); for (unsigned i = 0; i < num_args; i++) { expr * arg = or_e->get_arg(i); expr_ref not_arg(m_manager); // m_bsimp.mk_not applies basic simplifications. For example, if arg is of the form (not a), then it will return a. m_bsimp.mk_not(arg, not_arg); quantifier_ref tmp_q(m_manager); tmp_q = m_manager.update_quantifier(q, not_arg); expr_ref new_q(m_manager); elim_unused_vars(m_manager, tmp_q, new_q); new_args.push_back(new_q); } expr_ref result(m_manager); // m_bsimp.mk_and actually constructs a (not (or ...)) formula, // it will also apply basic simplifications. m_bsimp.mk_and(new_args.size(), new_args.c_ptr(), result); cache_result(q, result); } else { cache_result(q, m_manager.update_quantifier(q, e)); } } void distribute_forall::operator()(expr * f, expr_ref & result) { m_todo.reset(); flush_cache(); m_todo.push_back(f); while (!m_todo.empty()) { expr * e = m_todo.back(); if (visit_children(e)) { m_todo.pop_back(); reduce1(e); } } result = get_cached(f); SASSERT(result!=0); TRACE("distribute_forall", tout << mk_ll_pp(f, m_manager) << "======>\n" << mk_ll_pp(result, m_manager);); } expr * distribute_forall::get_cached(expr * n) const { return const_cast(this)->m_cache.find(n); } void distribute_forall::cache_result(expr * n, expr * r) { SASSERT(r != 0); m_cache.insert(n, r); } z3-4.4.0/src/ast/simplifier/basic_simplifier_plugin.h0000644000175000017500000000516312540347414022426 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: basic_simplifier_plugin.h Abstract: Simplifier for the basic family. Author: Leonardo (leonardo) 2008-01-07 --*/ #ifndef _BASIC_SIMPLIFIER_PLUGIN_H_ #define _BASIC_SIMPLIFIER_PLUGIN_H_ #include"simplifier_plugin.h" class bool_rewriter; /** \brief Simplifier for the basic family. */ class basic_simplifier_plugin : public simplifier_plugin { bool_rewriter * m_rewriter; public: basic_simplifier_plugin(ast_manager & m); virtual ~basic_simplifier_plugin(); bool_rewriter & get_rewriter() { return *m_rewriter; } bool eliminate_and() const; void set_eliminate_and(bool f); void mk_eq(expr * lhs, expr * rhs, expr_ref & result); void mk_iff(expr * lhs, expr * rhs, expr_ref & result); void mk_xor(expr * lhs, expr * rhs, expr_ref & result); void mk_implies(expr * lhs, expr * rhs, expr_ref & result); void mk_ite(expr * c, expr * t, expr * e, expr_ref & result); void mk_and(unsigned num_args, expr * const * args, expr_ref & result); void mk_or(unsigned num_args, expr * const * args, expr_ref & result); void mk_and(expr * arg1, expr * arg2, expr_ref & result); void mk_or(expr * arg1, expr * arg2, expr_ref & result); void mk_and(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); void mk_or(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); void mk_nand(unsigned num_args, expr * const * args, expr_ref & result); void mk_nor(unsigned num_args, expr * const * args, expr_ref & result); void mk_nand(expr * arg1, expr * arg2, expr_ref & result); void mk_nor(expr * arg1, expr * arg2, expr_ref & result); void mk_distinct(unsigned num_args, expr * const * args, expr_ref & result); void mk_not(expr * n, expr_ref & result); virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); virtual void enable_ac_support(bool flag); }; /** \brief Functor that compares expressions, but puts the expressions e and f(e) close to each other, where f is in family m_fid, and has kind m_dkind; */ struct expr_lt_proc { family_id m_fid; decl_kind m_dkind; expr_lt_proc(family_id fid = null_family_id, decl_kind k = null_decl_kind):m_fid(fid), m_dkind(k) {} unsigned get_id(expr * n) const { if (m_fid != null_family_id && is_app_of(n, m_fid, m_dkind)) return (to_app(n)->get_arg(0)->get_id() << 1) + 1; else return n->get_id() << 1; } bool operator()(expr * n1, expr * n2) const { return get_id(n1) < get_id(n2); } }; #endif /* _BASIC_SIMPLIFIER_PLUGIN_H_ */ z3-4.4.0/src/ast/simplifier/basic_simplifier_plugin.cpp0000644000175000017500000001416212540347414022760 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: basic_simplifier_plugin.cpp Abstract: Simplifier for the basic family. Author: Leonardo (leonardo) 2008-01-07 --*/ #include"basic_simplifier_plugin.h" #include"ast_ll_pp.h" #include"bool_rewriter.h" basic_simplifier_plugin::basic_simplifier_plugin(ast_manager & m): simplifier_plugin(symbol("basic"), m), m_rewriter(alloc(bool_rewriter, m)) { } basic_simplifier_plugin::~basic_simplifier_plugin() { dealloc(m_rewriter); } bool basic_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { set_reduce_invoked(); SASSERT(f->get_family_id() == m_manager.get_basic_family_id()); basic_op_kind k = static_cast(f->get_decl_kind()); switch (k) { case OP_FALSE: case OP_TRUE: return false; case OP_EQ: SASSERT(num_args == 2); mk_eq(args[0], args[1], result); return true; case OP_DISTINCT: mk_distinct(num_args, args, result); return true; case OP_ITE: SASSERT(num_args == 3); mk_ite(args[0], args[1], args[2], result); return true; case OP_AND: mk_and(num_args, args, result); return true; case OP_OR: mk_or(num_args, args, result); return true; case OP_IMPLIES: mk_implies(args[0], args[1], result); return true; case OP_IFF: mk_iff(args[0], args[1], result); return true; case OP_XOR: mk_xor(args[0], args[1], result); return true; case OP_NOT: SASSERT(num_args == 1); mk_not(args[0], result); return true; default: UNREACHABLE(); return false; } } /** \brief Return true if \c rhs is of the form (ite c t1 t2) and are_distinct(lhs, t1) and are_distinct(lhs, t2). */ static bool is_lhs_diseq_rhs_ite_branches(ast_manager & m, expr * lhs, expr * rhs) { return m.is_ite(rhs) && m.are_distinct(lhs, to_app(rhs)->get_arg(1)) && m.are_distinct(lhs, to_app(rhs)->get_arg(2)); } /** \brief Return true if \c rhs is of the form (ite c t1 t2) and lhs = t1 && are_distinct(lhs, t2) */ static bool is_lhs_eq_rhs_ite_then(ast_manager & m, expr * lhs, expr * rhs) { return m.is_ite(rhs) && lhs == to_app(rhs)->get_arg(1) && m.are_distinct(lhs, to_app(rhs)->get_arg(2)); } /** \brief Return true if \c rhs is of the form (ite c t1 t2) and are_distinct(lhs,t1) && lhs = t2 */ static bool is_lhs_eq_rhs_ite_else(ast_manager & m, expr * lhs, expr * rhs) { return m.is_ite(rhs) && lhs == to_app(rhs)->get_arg(2) && m.are_distinct(lhs, to_app(rhs)->get_arg(1)); } void basic_simplifier_plugin::mk_eq(expr * lhs, expr * rhs, expr_ref & result) { // (= t1 (ite C t2 t3)) --> false if are_distinct(t1, t2) && are_distinct(t1, t3) if (is_lhs_diseq_rhs_ite_branches(m_manager, lhs, rhs) || is_lhs_diseq_rhs_ite_branches(m_manager, rhs, lhs)) { result = m_manager.mk_false(); } // (= t1 (ite C t2 t3)) --> C if t1 = t2 && are_distinct(t1, t3) else if (is_lhs_eq_rhs_ite_then(m_manager, lhs, rhs)) { result = to_app(rhs)->get_arg(0); } // (= t1 (ite C t2 t3)) --> C if t1 = t2 && are_distinct(t1, t3) else if (is_lhs_eq_rhs_ite_then(m_manager, rhs, lhs)) { result = to_app(lhs)->get_arg(0); } // (= t1 (ite C t2 t3)) --> (not C) if t1 = t3 && are_distinct(t1, t2) else if (is_lhs_eq_rhs_ite_else(m_manager, lhs, rhs)) { mk_not(to_app(rhs)->get_arg(0), result); } // (= t1 (ite C t2 t3)) --> (not C) if t1 = t3 && are_distinct(t1, t2) else if (is_lhs_eq_rhs_ite_else(m_manager, rhs, lhs)) { mk_not(to_app(lhs)->get_arg(0), result); } else { m_rewriter->mk_eq(lhs, rhs, result); } } bool basic_simplifier_plugin::eliminate_and() const { return m_rewriter->elim_and(); } void basic_simplifier_plugin::set_eliminate_and(bool f) { m_rewriter->set_elim_and(f); } void basic_simplifier_plugin::mk_iff(expr * lhs, expr * rhs, expr_ref & result) { mk_eq(lhs, rhs, result); } void basic_simplifier_plugin::mk_xor(expr * lhs, expr * rhs, expr_ref & result) { m_rewriter->mk_xor(lhs, rhs, result); } void basic_simplifier_plugin::mk_implies(expr * lhs, expr * rhs, expr_ref & result) { m_rewriter->mk_implies(lhs, rhs, result); } void basic_simplifier_plugin::mk_ite(expr * c, expr * t, expr * e, expr_ref & result) { m_rewriter->mk_ite(c, t, e, result); } void basic_simplifier_plugin::mk_and(unsigned num_args, expr * const * args, expr_ref & result) { m_rewriter->mk_and(num_args, args, result); } void basic_simplifier_plugin::mk_or(unsigned num_args, expr * const * args, expr_ref & result) { m_rewriter->mk_or(num_args, args, result); } void basic_simplifier_plugin::mk_and(expr * arg1, expr * arg2, expr_ref & result) { m_rewriter->mk_and(arg1, arg2, result); } void basic_simplifier_plugin::mk_or(expr * arg1, expr * arg2, expr_ref & result) { m_rewriter->mk_or(arg1, arg2, result); } void basic_simplifier_plugin::mk_and(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { m_rewriter->mk_and(arg1, arg2, arg3, result); } void basic_simplifier_plugin::mk_or(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { m_rewriter->mk_or(arg1, arg2, arg3, result); } void basic_simplifier_plugin::mk_nand(unsigned num_args, expr * const * args, expr_ref & result) { m_rewriter->mk_nand(num_args, args, result); } void basic_simplifier_plugin::mk_nor(unsigned num_args, expr * const * args, expr_ref & result) { m_rewriter->mk_nor(num_args, args, result); } void basic_simplifier_plugin::mk_nand(expr * arg1, expr * arg2, expr_ref & result) { m_rewriter->mk_nand(arg1, arg2, result); } void basic_simplifier_plugin::mk_nor(expr * arg1, expr * arg2, expr_ref & result) { m_rewriter->mk_nor(arg1, arg2, result); } void basic_simplifier_plugin::mk_distinct(unsigned num_args, expr * const * args, expr_ref & result) { m_rewriter->mk_distinct(num_args, args, result); } void basic_simplifier_plugin::mk_not(expr * n, expr_ref & result) { m_rewriter->mk_not(n, result); } void basic_simplifier_plugin::enable_ac_support(bool flag) { m_rewriter->set_flat(flag); } z3-4.4.0/src/ast/simplifier/simplifier.cpp0000644000175000017500000007712512540347414020251 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: simplifier.cpp Abstract: Expression simplifier. Author: Leonardo (leonardo) 2008-01-03 Notes: --*/ #include"simplifier.h" #include"var_subst.h" #include"ast_ll_pp.h" #include"ast_pp.h" #include"well_sorted.h" #include"ast_smt_pp.h" simplifier::simplifier(ast_manager & m): base_simplifier(m), m_proofs(m), m_subst_proofs(m), m_need_reset(false), m_use_oeq(false), m_visited_quantifier(false), m_ac_support(true) { } void simplifier::register_plugin(plugin * p) { m_plugins.register_plugin(p); } simplifier::~simplifier() { flush_cache(); } void simplifier::enable_ac_support(bool flag) { m_ac_support = flag; ptr_vector::const_iterator it = m_plugins.begin(); ptr_vector::const_iterator end = m_plugins.end(); for (; it != end; ++it) { if (*it != 0) (*it)->enable_ac_support(flag); } } /** \brief External interface for the simplifier. A client will invoke operator()(s, r, p) to simplify s. The result is stored in r. When proof generation is enabled, a proof for the equivalence (or equisatisfiability) of s and r is stored in p. When proof generation is disabled, this method stores the "undefined proof" object in p. */ void simplifier::operator()(expr * s, expr_ref & r, proof_ref & p) { m_need_reset = true; reinitialize(); expr * s_orig = s; expr * old_s; expr * result; proof * result_proof; switch (m.proof_mode()) { case PGM_DISABLED: // proof generation is disabled. reduce_core(s); // after executing reduce_core, the result of the simplification is in the cache get_cached(s, result, result_proof); r = result; p = m.mk_undef_proof(); break; case PGM_COARSE: // coarse proofs... in this case, we do not produce a step by step (fine grain) proof to show the equivalence (or equisatisfiability) of s an r. m_subst_proofs.reset(); // m_subst_proofs is an auxiliary vector that is used to justify substitutions. See comment on method get_subst. reduce_core(s); get_cached(s, result, result_proof); r = result; if (result == s) p = m.mk_reflexivity(s); else { remove_duplicates(m_subst_proofs); p = m.mk_rewrite_star(s, result, m_subst_proofs.size(), m_subst_proofs.c_ptr()); } break; case PGM_FINE: // fine grain proofs... in this mode, every proof step (or most of them) is described. m_proofs.reset(); old_s = 0; // keep simplyfing until no further simplifications are possible. while (s != old_s) { TRACE("simplifier", tout << "simplification pass... " << s->get_id() << "\n";); TRACE("simplifier_loop", tout << mk_ll_pp(s, m) << "\n";); reduce_core(s); get_cached(s, result, result_proof); SASSERT(is_rewrite_proof(s, result, result_proof)); if (result_proof != 0) { m_proofs.push_back(result_proof); } old_s = s; s = result; } SASSERT(s != 0); r = s; p = m_proofs.empty() ? m.mk_reflexivity(s) : m.mk_transitivity(m_proofs.size(), m_proofs.c_ptr()); SASSERT(is_rewrite_proof(s_orig, r, p)); break; default: UNREACHABLE(); } } void simplifier::flush_cache() { m_cache.flush(); ptr_vector::const_iterator it = m_plugins.begin(); ptr_vector::const_iterator end = m_plugins.end(); for (; it != end; ++it) { if (*it != 0) { (*it)->flush_caches(); } } } bool simplifier::get_subst(expr * n, expr_ref & r, proof_ref & p) { return false; } void simplifier::reduce_core(expr * n) { if (!is_cached(n)) { // We do not assume m_todo is empty... So, we store the current size of the todo-stack. unsigned sz = m_todo.size(); m_todo.push_back(n); while (m_todo.size() != sz) { expr * n = m_todo.back(); if (is_cached(n)) m_todo.pop_back(); else if (visit_children(n)) { // if all children were already simplified, then remove n from the todo stack and apply a // simplification step to it. m_todo.pop_back(); reduce1(n); } } } } /** \brief Return true if all children of n have been already simplified. */ bool simplifier::visit_children(expr * n) { switch(n->get_kind()) { case AST_VAR: return true; case AST_APP: // The simplifier has support for flattening AC (associative-commutative) operators. // The method ast_manager::mk_app is used to create the flat version of an AC operator. // In Z3 1.x, we used multi-ary operators. This creates problems for the superposition engine. // So, starting at Z3 2.x, only boolean operators can be multi-ary. // Example: // (and (and a b) (and c d)) --> (and a b c d) // (+ (+ a b) (+ c d)) --> (+ a (+ b (+ c d))) // Remark: The flattening is only applied if m_ac_support is true. if (m_ac_support && to_app(n)->get_decl()->is_associative() && to_app(n)->get_decl()->is_commutative()) return visit_ac(to_app(n)); else { bool visited = true; unsigned j = to_app(n)->get_num_args(); while (j > 0) { --j; visit(to_app(n)->get_arg(j), visited); } return visited; } case AST_QUANTIFIER: return visit_quantifier(to_quantifier(n)); default: UNREACHABLE(); return true; } } /** \brief Visit the children of n assuming it is an AC (associative-commutative) operator. For example, if n is of the form (+ (+ a b) (+ c d)), this method will return true if the nodes a, b, c and d have been already simplified. The nodes (+ a b) and (+ c d) are not really checked. */ bool simplifier::visit_ac(app * n) { bool visited = true; func_decl * decl = n->get_decl(); SASSERT(m_ac_support); SASSERT(decl->is_associative()); SASSERT(decl->is_commutative()); m_ac_marked.reset(); ptr_buffer todo; todo.push_back(n); while (!todo.empty()) { app * n = todo.back(); todo.pop_back(); if (m_ac_mark.is_marked(n)) continue; m_ac_mark.mark(n, true); m_ac_marked.push_back(n); SASSERT(n->get_decl() == decl); unsigned i = n->get_num_args(); while (i > 0) { --i; expr * arg = n->get_arg(i); if (is_app_of(arg, decl)) todo.push_back(to_app(arg)); else visit(arg, visited); } } ptr_vector::const_iterator it = m_ac_marked.begin(); ptr_vector::const_iterator end = m_ac_marked.end(); for (; it != end; ++it) m_ac_mark.mark(*it, false); return visited; } bool simplifier::visit_quantifier(quantifier * n) { m_visited_quantifier = true; bool visited = true; unsigned j = to_quantifier(n)->get_num_patterns(); while (j > 0) { --j; visit(to_quantifier(n)->get_pattern(j), visited); } j = to_quantifier(n)->get_num_no_patterns(); while (j > 0) { --j; visit(to_quantifier(n)->get_no_pattern(j), visited); } visit(to_quantifier(n)->get_expr(), visited); return visited; } /** \brief Simplify n and store the result in the cache. */ void simplifier::reduce1(expr * n) { switch (n->get_kind()) { case AST_VAR: cache_result(n, n, 0); break; case AST_APP: reduce1_app(to_app(n)); break; case AST_QUANTIFIER: reduce1_quantifier(to_quantifier(n)); break; default: UNREACHABLE(); } } /** \brief Simplify the given application using the cached values, associativity flattening, the given substitution, and family/theory specific simplifications via plugins. */ void simplifier::reduce1_app(app * n) { expr_ref r(m); proof_ref p(m); TRACE("reduce", tout << "reducing...\n" << mk_pp(n, m) << "\n";); if (get_subst(n, r, p)) { TRACE("reduce", tout << "applying substitution...\n";); cache_result(n, r, p); return; } func_decl * decl = n->get_decl(); if (m_ac_support && decl->is_associative() && decl->is_commutative()) reduce1_ac_app_core(n); else reduce1_app_core(n); } void simplifier::reduce1_app_core(app * n) { m_args.reset(); func_decl * decl = n->get_decl(); proof_ref p1(m); // Stores the new arguments of n in m_args. // Let n be of the form // (decl arg_0 ... arg_{n-1}) // then // m_args contains [arg_0', ..., arg_{n-1}'], // where arg_i' is the simplification of arg_i // and // p1 is a proof for // (decl arg_0 ... arg_{n-1}) is equivalente/equisatisfiable to (decl arg_0' ... arg_{n-1}') // p1 is built using the congruence proof step and the proofs for arg_0' ... arg_{n-1}'. // Of course, p1 is 0 if proofs are not enabled or coarse grain proofs are used. bool has_new_args = get_args(n, m_args, p1); // The following if implements a simple trick. // If none of the arguments have been simplified, and n is not a theory symbol, // Then no simplification is possible, and we can cache the result of the simplification of n as n. if (has_new_args || decl->get_family_id() != null_family_id) { expr_ref r(m); TRACE("reduce", tout << "reduce1_app\n"; for(unsigned i = 0; i < m_args.size(); i++) tout << mk_ll_pp(m_args[i], m);); // the method mk_app invokes get_subst and plugins to simplify // (decl arg_0' ... arg_{n-1}') mk_app(decl, m_args.size(), m_args.c_ptr(), r); if (!m.fine_grain_proofs()) { cache_result(n, r, 0); } else { expr * s = m.mk_app(decl, m_args.size(), m_args.c_ptr()); proof * p; if (n == r) p = 0; else if (r != s) // we use a "theory rewrite generic proof" to justify the step // s = (decl arg_0' ... arg_{n-1}') --> r p = m.mk_transitivity(p1, m.mk_rewrite(s, r)); else p = p1; cache_result(n, r, p); } } else { cache_result(n, n, 0); } } bool is_ac_list(app * n, ptr_vector & args) { args.reset(); func_decl * f = n->get_decl(); app * curr = n; while (true) { if (curr->get_num_args() != 2) return false; expr * arg1 = curr->get_arg(0); if (is_app_of(arg1, f)) return false; args.push_back(arg1); expr * arg2 = curr->get_arg(1); if (!is_app_of(arg2, f)) { args.push_back(arg2); return true; } curr = to_app(arg2); } } bool is_ac_vector(app * n) { func_decl * f = n->get_decl(); unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { if (is_app_of(n->get_arg(i), f)) return false; } return true; } void simplifier::reduce1_ac_app_core(app * n) { app_ref n_c(m); proof_ref p1(m); mk_ac_congruent_term(n, n_c, p1); TRACE("ac", tout << "expr:\n" << mk_pp(n, m) << "\ncongruent term:\n" << mk_pp(n_c, m) << "\n";); expr_ref r(m); func_decl * decl = n->get_decl(); family_id fid = decl->get_family_id(); plugin * p = get_plugin(fid); if (is_ac_vector(n_c)) { if (p != 0 && p->reduce(decl, n_c->get_num_args(), n_c->get_args(), r)) { // done... } else { r = n_c; } } else if (is_ac_list(n_c, m_args)) { // m_args contains the arguments... if (p != 0 && p->reduce(decl, m_args.size(), m_args.c_ptr(), r)) { // done... } else { r = m.mk_app(decl, m_args.size(), m_args.c_ptr()); } } else { m_args.reset(); m_mults.reset(); get_ac_args(n_c, m_args, m_mults); TRACE("ac", tout << "AC args:\n"; for (unsigned i = 0; i < m_args.size(); i++) { tout << mk_pp(m_args[i], m) << " * " << m_mults[i] << "\n"; }); if (p != 0 && p->reduce(decl, m_args.size(), m_mults.c_ptr(), m_args.c_ptr(), r)) { // done... } else { ptr_buffer new_args; expand_args(m_args.size(), m_mults.c_ptr(), m_args.c_ptr(), new_args); r = m.mk_app(decl, new_args.size(), new_args.c_ptr()); } } TRACE("ac", tout << "AC result:\n" << mk_pp(r, m) << "\n";); if (!m.fine_grain_proofs()) { cache_result(n, r, 0); } else { proof * p; if (n == r.get()) p = 0; else if (r.get() != n_c.get()) p = m.mk_transitivity(p1, m.mk_rewrite(n_c, r)); else p = p1; cache_result(n, r, p); } } static unsigned g_rewrite_lemma_id = 0; void simplifier::dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr * const * args, expr* result) { expr_ref arg(m); arg = m.mk_app(decl, num_args, args); if (arg.get() != result) { char buffer[128]; #ifdef _WINDOWS sprintf_s(buffer, ARRAYSIZE(buffer), "lemma_%d.smt", g_rewrite_lemma_id); #else sprintf(buffer, "rewrite_lemma_%d.smt", g_rewrite_lemma_id); #endif ast_smt_pp pp(m); pp.set_benchmark_name("rewrite_lemma"); pp.set_status("unsat"); expr_ref n(m); n = m.mk_not(m.mk_eq(arg.get(), result)); std::ofstream out(buffer); pp.display(out, n); out.close(); ++g_rewrite_lemma_id; } } /** \brief Return in \c result an expression \c e equivalent to (f args[0] ... args[num_args - 1]), and store in \c pr a proof for (= (f args[0] ... args[num_args - 1]) e) If e is identical to (f args[0] ... args[num_args - 1]), then pr is set to 0. */ void simplifier::mk_app(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & result) { m_need_reset = true; if (m.is_eq(decl)) { sort * s = m.get_sort(args[0]); plugin * p = get_plugin(s->get_family_id()); if (p != 0 && p->reduce_eq(args[0], args[1], result)) return; } else if (m.is_distinct(decl)) { sort * s = m.get_sort(args[0]); plugin * p = get_plugin(s->get_family_id()); if (p != 0 && p->reduce_distinct(num_args, args, result)) return; } family_id fid = decl->get_family_id(); plugin * p = get_plugin(fid); if (p != 0 && p->reduce(decl, num_args, args, result)) { //uncomment this line if you want to trace rewrites as lemmas: //dump_rewrite_lemma(decl, num_args, args, result.get()); return; } result = m.mk_app(decl, num_args, args); } /** \brief Create a term congruence to n (f a[0] ... a[num_args-1]) using the cached values for the a[i]'s. Store the result in r, and the proof for (= n r) in p. If n and r are identical, then set p to 0. */ void simplifier::mk_congruent_term(app * n, app_ref & r, proof_ref & p) { bool has_new_args = false; ptr_vector args; ptr_vector proofs; unsigned num = n->get_num_args(); for (unsigned j = 0; j < num; j++) { expr * arg = n->get_arg(j); expr * new_arg; proof * arg_proof; get_cached(arg, new_arg, arg_proof); CTRACE("simplifier_bug", (arg != new_arg) != (arg_proof != 0), tout << mk_ll_pp(arg, m) << "\n---->\n" << mk_ll_pp(new_arg, m) << "\n"; tout << "#" << arg->get_id() << " #" << new_arg->get_id() << "\n"; tout << arg << " " << new_arg << "\n";); if (arg != new_arg) { has_new_args = true; proofs.push_back(arg_proof); SASSERT(arg_proof); } else { SASSERT(arg_proof == 0); } args.push_back(new_arg); } if (has_new_args) { r = m.mk_app(n->get_decl(), args.size(), args.c_ptr()); if (m_use_oeq) p = m.mk_oeq_congruence(n, r, proofs.size(), proofs.c_ptr()); else p = m.mk_congruence(n, r, proofs.size(), proofs.c_ptr()); } else { r = n; p = 0; } } /** \brief Store the new arguments of \c n in result. Store in p a proof for (= n (f result[0] ... result[num_args - 1])), where f is the function symbol of n. If there are no new arguments or fine grain proofs are disabled, then p is set to 0. Return true there are new arguments. */ bool simplifier::get_args(app * n, ptr_vector & result, proof_ref & p) { bool has_new_args = false; unsigned num = n->get_num_args(); if (m.fine_grain_proofs()) { app_ref r(m); mk_congruent_term(n, r, p); result.append(r->get_num_args(), r->get_args()); SASSERT(n->get_num_args() == result.size()); has_new_args = r != n; } else { p = 0; for (unsigned j = 0; j < num; j++) { expr * arg = n->get_arg(j); expr * new_arg; proof * arg_proof; get_cached(arg, new_arg, arg_proof); if (arg != new_arg) { has_new_args = true; } result.push_back(new_arg); } } return has_new_args; } /** \brief Create a term congruence to n (where n is an expression such as: (f (f a_1 a_2) (f a_3 (f a_4 a_5))) using the cached values for the a_i's. Store the result in r, and the proof for (= n r) in p. If n and r are identical, then set p to 0. */ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) { SASSERT(m_ac_support); func_decl * f = n->get_decl(); m_ac_cache.reset(); m_ac_pr_cache.reset(); ptr_buffer todo; ptr_buffer new_args; ptr_buffer new_arg_prs; todo.push_back(n); while (!todo.empty()) { app * curr = todo.back(); if (m_ac_cache.contains(curr)) { todo.pop_back(); continue; } bool visited = true; bool has_new_arg = false; new_args.reset(); new_arg_prs.reset(); unsigned num_args = curr->get_num_args(); for (unsigned j = 0; j < num_args; j ++) { expr * arg = curr->get_arg(j); if (is_app_of(arg, f)) { app * new_arg = 0; if (m_ac_cache.find(to_app(arg), new_arg)) { SASSERT(new_arg != 0); new_args.push_back(new_arg); if (arg != new_arg) has_new_arg = true; if (m.fine_grain_proofs()) { proof * pr = 0; m_ac_pr_cache.find(to_app(arg), pr); if (pr != 0) new_arg_prs.push_back(pr); } } else { visited = false; todo.push_back(to_app(arg)); } } else { expr * new_arg = 0; proof * pr; get_cached(arg, new_arg, pr); new_args.push_back(new_arg); if (arg != new_arg) has_new_arg = true; if (m.fine_grain_proofs() && pr != 0) new_arg_prs.push_back(pr); } } if (visited) { SASSERT(new_args.size() == curr->get_num_args()); todo.pop_back(); if (!has_new_arg) { m_ac_cache.insert(curr, curr); if (m.fine_grain_proofs()) m_ac_pr_cache.insert(curr, 0); } else { app * new_curr = m.mk_app(f, new_args.size(), new_args.c_ptr()); m_ac_cache.insert(curr, new_curr); if (m.fine_grain_proofs()) { proof * p = m.mk_congruence(curr, new_curr, new_arg_prs.size(), new_arg_prs.c_ptr()); m_ac_pr_cache.insert(curr, p); } } } } SASSERT(m_ac_cache.contains(n)); app * new_n = 0; m_ac_cache.find(n, new_n); r = new_n; if (m.fine_grain_proofs()) { proof * new_pr = 0; m_ac_pr_cache.find(n, new_pr); p = new_pr; } } #define White 0 #define Grey 1 #define Black 2 static int get_color(obj_map & colors, expr * n) { obj_map::obj_map_entry * entry = colors.insert_if_not_there2(n, White); return entry->get_data().m_value; } static bool visit_ac_children(func_decl * f, expr * n, obj_map & colors, ptr_buffer & todo, ptr_buffer & result) { if (is_app_of(n, f)) { unsigned num_args = to_app(n)->get_num_args(); bool visited = true; // Put the arguments in 'result' in reverse order. // Reason: preserve the original order of the arguments in the final result. // Remark: get_ac_args will traverse 'result' backwards. for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(n)->get_arg(i); obj_map::obj_map_entry * entry = colors.insert_if_not_there2(arg, White); if (entry->get_data().m_value == White) { todo.push_back(arg); visited = false; } } return visited; } else { return true; } } void simplifier::ac_top_sort(app * n, ptr_buffer & result) { ptr_buffer todo; func_decl * f = n->get_decl(); obj_map & colors = m_colors; colors.reset(); todo.push_back(n); while (!todo.empty()) { expr * curr = todo.back(); int color; obj_map::obj_map_entry * entry = colors.insert_if_not_there2(curr, White); SASSERT(entry); color = entry->get_data().m_value; switch (color) { case White: // Remark: entry becomes invalid if an element is inserted into the hashtable. // So, I must set Grey before executing visit_ac_children. entry->get_data().m_value = Grey; SASSERT(get_color(colors, curr) == Grey); if (visit_ac_children(f, curr, colors, todo, result)) { // If visit_ac_children succeeded, then the hashtable was not modified, // and entry is still valid. SASSERT(todo.back() == curr); entry->get_data().m_value = Black; SASSERT(get_color(colors, curr) == Black); result.push_back(curr); todo.pop_back(); } break; case Grey: SASSERT(visit_ac_children(f, curr, colors, todo, result)); SASSERT(entry); entry->get_data().m_value = Black; SASSERT(get_color(colors, curr) == Black); result.push_back(curr); SASSERT(todo.back() == curr); todo.pop_back(); break; case Black: todo.pop_back(); break; default: UNREACHABLE(); } } } void simplifier::get_ac_args(app * n, ptr_vector & args, vector & mults) { SASSERT(m_ac_support); ptr_buffer sorted_exprs; ac_top_sort(n, sorted_exprs); SASSERT(!sorted_exprs.empty()); SASSERT(sorted_exprs[sorted_exprs.size()-1] == n); TRACE("ac", tout << mk_ll_pp(n, m, true, false) << "#" << n->get_id() << "\nsorted expressions...\n"; for (unsigned i = 0; i < sorted_exprs.size(); i++) { tout << "#" << sorted_exprs[i]->get_id() << " "; } tout << "\n";); m_ac_mults.reset(); m_ac_mults.insert(n, rational(1)); func_decl * decl = n->get_decl(); unsigned j = sorted_exprs.size(); while (j > 0) { --j; expr * curr = sorted_exprs[j]; rational mult; m_ac_mults.find(curr, mult); SASSERT(!mult.is_zero()); if (is_app_of(curr, decl)) { unsigned num_args = to_app(curr)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(curr)->get_arg(i); rational zero; obj_map::obj_map_entry * entry = m_ac_mults.insert_if_not_there2(arg, zero); entry->get_data().m_value += mult; } } else { args.push_back(curr); mults.push_back(mult); } } } void simplifier::reduce1_quantifier(quantifier * q) { expr * new_body; proof * new_body_pr; SASSERT(is_well_sorted(m, q)); get_cached(q->get_expr(), new_body, new_body_pr); quantifier_ref q1(m); proof * p1 = 0; if (is_quantifier(new_body) && to_quantifier(new_body)->is_forall() == q->is_forall() && !to_quantifier(q)->has_patterns() && !to_quantifier(new_body)->has_patterns()) { quantifier * nested_q = to_quantifier(new_body); ptr_buffer sorts; buffer names; sorts.append(q->get_num_decls(), q->get_decl_sorts()); names.append(q->get_num_decls(), q->get_decl_names()); sorts.append(nested_q->get_num_decls(), nested_q->get_decl_sorts()); names.append(nested_q->get_num_decls(), nested_q->get_decl_names()); q1 = m.mk_quantifier(q->is_forall(), sorts.size(), sorts.c_ptr(), names.c_ptr(), nested_q->get_expr(), std::min(q->get_weight(), nested_q->get_weight()), q->get_qid(), q->get_skid(), 0, 0, 0, 0); SASSERT(is_well_sorted(m, q1)); if (m.fine_grain_proofs()) { quantifier * q0 = m.update_quantifier(q, new_body); proof * p0 = q == q0 ? 0 : m.mk_quant_intro(q, q0, new_body_pr); p1 = m.mk_pull_quant(q0, q1); p1 = m.mk_transitivity(p0, p1); } } else { ptr_buffer new_patterns; ptr_buffer new_no_patterns; expr * new_pattern; proof * new_pattern_pr; // Remark: we can ignore the proofs for the patterns. unsigned num = q->get_num_patterns(); for (unsigned i = 0; i < num; i++) { get_cached(q->get_pattern(i), new_pattern, new_pattern_pr); if (m.is_pattern(new_pattern)) { new_patterns.push_back(new_pattern); } } num = q->get_num_no_patterns(); for (unsigned i = 0; i < num; i++) { get_cached(q->get_no_pattern(i), new_pattern, new_pattern_pr); new_no_patterns.push_back(new_pattern); } remove_duplicates(new_patterns); remove_duplicates(new_no_patterns); q1 = m.mk_quantifier(q->is_forall(), q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), new_body, q->get_weight(), q->get_qid(), q->get_skid(), new_patterns.size(), new_patterns.c_ptr(), new_no_patterns.size(), new_no_patterns.c_ptr()); SASSERT(is_well_sorted(m, q1)); TRACE("simplifier", tout << mk_pp(q, m) << "\n" << mk_pp(q1, m) << "\n";); if (m.fine_grain_proofs()) { if (q != q1 && !new_body_pr) { new_body_pr = m.mk_rewrite(q->get_expr(), new_body); } p1 = q == q1 ? 0 : m.mk_quant_intro(q, q1, new_body_pr); } } expr_ref r(m); elim_unused_vars(m, q1, r); proof * pr = 0; if (m.fine_grain_proofs()) { proof * p2 = 0; if (q1.get() != r.get()) p2 = m.mk_elim_unused_vars(q1, r); pr = m.mk_transitivity(p1, p2); } cache_result(q, r, pr); } /** \see release_plugins */ void simplifier::borrow_plugins(simplifier const & s) { ptr_vector::const_iterator it = s.begin_plugins(); ptr_vector::const_iterator end = s.end_plugins(); for (; it != end; ++it) register_plugin(*it); } /** \brief Make the simplifier behave as a pre-simplifier: No AC, and plugins are marked in pre-simplification mode. */ void simplifier::enable_presimp() { enable_ac_support(false); ptr_vector::const_iterator it = begin_plugins(); ptr_vector::const_iterator end = end_plugins(); for (; it != end; ++it) (*it)->enable_presimp(true); } /** \brief This method should be invoked if the plugins of this simplifier were borrowed from a different simplifier. */ void simplifier::release_plugins() { m_plugins.release(); } void subst_simplifier::set_subst_map(expr_map * s) { flush_cache(); m_subst_map = s; } bool subst_simplifier::get_subst(expr * n, expr_ref & r, proof_ref & p) { if (m_subst_map && m_subst_map->contains(n)) { expr * _r; proof * _p = 0; m_subst_map->get(n, _r, _p); r = _r; p = _p; if (m.coarse_grain_proofs()) m_subst_proofs.push_back(p); return true; } return false; } static void push_core(ast_manager & m, expr * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs) { SASSERT(pr == 0 || m.is_undef_proof(pr) || e == m.get_fact(pr)); TRACE("preprocessor", tout << mk_pp(e, m) << "\n"; if (pr) tout << mk_ll_pp(pr, m) << "\n\n";); if (m.is_true(e)) return; result.push_back(e); if (m.proofs_enabled()) result_prs.push_back(pr); } static void push_and(ast_manager & m, app * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs) { unsigned num = e->get_num_args(); TRACE("push_and", tout << mk_pp(e, m) << "\n";); for (unsigned i = 0; i < num; i++) push_assertion(m, e->get_arg(i), m.mk_and_elim(pr, i), result, result_prs); } static void push_not_or(ast_manager & m, app * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs) { unsigned num = e->get_num_args(); TRACE("push_not_or", tout << mk_pp(e, m) << "\n";); for (unsigned i = 0; i < num; i++) { expr * child = e->get_arg(i); if (m.is_not(child)) { expr * not_child = to_app(child)->get_arg(0); push_assertion(m, not_child, m.mk_not_or_elim(pr, i), result, result_prs); } else { expr_ref not_child(m); not_child = m.mk_not(child); push_assertion(m, not_child, m.mk_not_or_elim(pr, i), result, result_prs); } } } void push_assertion(ast_manager & m, expr * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs) { CTRACE("push_assertion", !(pr == 0 || m.is_undef_proof(pr) || m.get_fact(pr) == e), tout << mk_pp(e, m) << "\n" << mk_pp(m.get_fact(pr), m) << "\n";); SASSERT(pr == 0 || m.is_undef_proof(pr) || m.get_fact(pr) == e); if (m.is_and(e)) push_and(m, to_app(e), pr, result, result_prs); else if (m.is_not(e) && m.is_or(to_app(e)->get_arg(0))) push_not_or(m, to_app(to_app(e)->get_arg(0)), pr, result, result_prs); else push_core(m, e, pr, result, result_prs); } z3-4.4.0/src/ast/simplifier/distribute_forall.h0000644000175000017500000000422112540347414021253 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: distribute_forall.h Abstract: Author: Leonardo de Moura (leonardo) 2010-04-02. Revision History: Christoph Wintersteiger 2010-04-06: Added implementation --*/ #ifndef _DISTRIBUTE_FORALL_H_ #define _DISTRIBUTE_FORALL_H_ #include"ast.h" #include"basic_simplifier_plugin.h" #include"act_cache.h" /** \brief Apply the following transformation (forall X (and F1 ... Fn)) --> (and (forall X F1) ... (forall X Fn)) The actual transformation is slightly different since the "and" connective is eliminated and replaced with a "not or". So, the actual transformation is: (forall X (not (or F1 ... Fn))) --> (not (or (not (forall X (not F1))) ... (not (forall X (not Fn))))) The implementation uses the visit_children/reduce1 idiom. A cache is used as usual. */ class distribute_forall { typedef act_cache expr_map; ast_manager & m_manager; basic_simplifier_plugin & m_bsimp; // useful for constructing formulas and/or/not in simplified form. ptr_vector m_todo; expr_map m_cache; ptr_vector m_new_args; // The new expressions are stored in a mapping that increments their reference counter. So, we do not need to store them in // m_new_exprs // expr_ref_vector m_new_exprs; public: distribute_forall(ast_manager & m, basic_simplifier_plugin & p); /** \brief Apply the distribute_forall transformation (when possible) to all universal quantifiers in \c f. Store the result in \c result. */ void operator()(expr * f, expr_ref & result); protected: inline void visit(expr * n, bool & visited); bool visit_children(expr * n); void reduce1(expr * n); void reduce1_quantifier(quantifier * q); void reduce1_app(app * a); expr * get_cached(expr * n) const; bool is_cached(expr * n) const { return get_cached(n) != 0; } void cache_result(expr * n, expr * r); void reset_cache() { m_cache.reset(); } void flush_cache() { m_cache.cleanup(); } }; #endif /* _DISTRIBUTE_FORALL_H_ */ z3-4.4.0/src/ast/simplifier/datatype_simplifier_plugin.cpp0000644000175000017500000000603012540347414023505 0ustar michaelmichael/*++ Copyright (c) 2008 Microsoft Corporation Module Name: datatype_simplifier_plugin.cpp Abstract: Simplifier for algebraic datatypes. Author: nbjorner 2008-11-6 --*/ #include"datatype_simplifier_plugin.h" datatype_simplifier_plugin::datatype_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b): simplifier_plugin(symbol("datatype"), m), m_util(m), m_bsimp(b) { } datatype_simplifier_plugin::~datatype_simplifier_plugin() { } bool datatype_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { set_reduce_invoked(); SASSERT(f->get_family_id() == get_family_id()); switch(f->get_decl_kind()) { case OP_DT_CONSTRUCTOR: { return false; } case OP_DT_RECOGNISER: { // // simplify is_cons(cons(x,y)) -> true // simplify is_cons(nil) -> false // SASSERT(num_args == 1); if (!is_app_of(args[0], get_family_id(), OP_DT_CONSTRUCTOR)) { return false; } app* a = to_app(args[0]); func_decl* f1 = a->get_decl(); func_decl* f2 = m_util.get_recognizer_constructor(f); if (f1 == f2) { result = m_manager.mk_true(); } else { result = m_manager.mk_false(); } return true; } case OP_DT_ACCESSOR: { // // simplify head(cons(x,y)) -> x // SASSERT(num_args == 1); if (!is_app_of(args[0], get_family_id(), OP_DT_CONSTRUCTOR)) { return false; } app* a = to_app(args[0]); func_decl* f1 = a->get_decl(); func_decl* f2 = m_util.get_accessor_constructor(f); if (f1 != f2) { return false; } ptr_vector const* acc = m_util.get_constructor_accessors(f1); SASSERT(acc && acc->size() == a->get_num_args()); for (unsigned i = 0; i < acc->size(); ++i) { if (f == (*acc)[i]) { // found it. result = a->get_arg(i); return true; } } UNREACHABLE(); } case OP_DT_UPDATE_FIELD: return false; default: UNREACHABLE(); } return false; } bool datatype_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) { set_reduce_invoked(); if (is_app_of(lhs, get_family_id(), OP_DT_CONSTRUCTOR) && is_app_of(rhs, get_family_id(), OP_DT_CONSTRUCTOR)) { app* a = to_app(lhs); app* b = to_app(rhs); if (a->get_decl() != b->get_decl()) { result = m_manager.mk_false(); return true; } expr_ref_vector eqs(m_manager); for (unsigned i = 0; i < a->get_num_args(); ++i) { m_bsimp.mk_eq(a->get_arg(i),b->get_arg(i), result); eqs.push_back(result); } m_bsimp.mk_and(eqs.size(), eqs.c_ptr(), result); return true; } // TBD: occurs check, constructor check. return false; } z3-4.4.0/src/ast/simplifier/push_app_ite.cpp0000644000175000017500000001325612540347414020561 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: push_app_ite.cpp Abstract: TODO: Write a better ite lifter Author: Leonardo de Moura (leonardo) 2008-05-14. Revision History: --*/ #include"push_app_ite.h" #include"ast_pp.h" push_app_ite::push_app_ite(simplifier & s, bool conservative): simplifier(s.get_manager()), m_conservative(conservative) { borrow_plugins(s); } push_app_ite::~push_app_ite() { // the plugins were borrowed. So, release ownership. m_plugins.release(); } int push_app_ite::has_ite_arg(unsigned num_args, expr * const * args) { for (unsigned i = 0; i < num_args; i++) if (m.is_ite(args[i])) return i; return -1; } void push_app_ite::apply(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & r) { TRACE("push_app_ite", tout << "pushing app...\n";); int ite_arg_idx = has_ite_arg(num_args, args); if (ite_arg_idx < 0) { mk_app(decl, num_args, args, r); return; } app * ite = to_app(args[ite_arg_idx]); expr * c = ite->get_arg(0); expr * t = ite->get_arg(1); expr * e = ite->get_arg(2); expr ** args_prime = const_cast(args); expr * old = args_prime[ite_arg_idx]; args_prime[ite_arg_idx] = t; expr_ref t_new(m); apply(decl, num_args, args_prime, t_new); args_prime[ite_arg_idx] = e; expr_ref e_new(m); apply(decl, num_args, args_prime, e_new); args_prime[ite_arg_idx] = old; expr * new_args[3] = { c, t_new, e_new }; mk_app(ite->get_decl(), 3, new_args, r); } /** \brief Default (conservative) implementation. Return true if there one and only one ite-term argument. */ bool push_app_ite::is_target(func_decl * decl, unsigned num_args, expr * const * args) { if (m.is_ite(decl)) return false; bool found_ite = false; for (unsigned i = 0; i < num_args; i++) { if (m.is_ite(args[i]) && !m.is_bool(args[i])) { if (found_ite) { if (m_conservative) return false; } else { found_ite = true; } } } CTRACE("push_app_ite", found_ite, tout << "found target for push app ite:\n"; tout << decl->get_name(); for (unsigned i = 0; i < num_args; i++) tout << " " << mk_pp(args[i], m); tout << "\n";); return found_ite; } void push_app_ite::operator()(expr * s, expr_ref & r, proof_ref & p) { expr * result; proof * result_proof; reduce_core(s); get_cached(s, result, result_proof); r = result; switch (m.proof_mode()) { case PGM_DISABLED: p = m.mk_undef_proof(); break; case PGM_COARSE: if (result == s) p = m.mk_reflexivity(s); else p = m.mk_rewrite_star(s, result, 0, 0); break; case PGM_FINE: if (result == s) p = m.mk_reflexivity(s); else p = result_proof; break; } } void push_app_ite::reduce_core(expr * n) { if (!is_cached(n)) { unsigned sz = m_todo.size(); m_todo.push_back(n); while (m_todo.size() != sz) { expr * n = m_todo.back(); if (is_cached(n)) m_todo.pop_back(); else if (visit_children(n)) { m_todo.pop_back(); reduce1(n); } } } } bool push_app_ite::visit_children(expr * n) { bool visited = true; unsigned j; switch(n->get_kind()) { case AST_VAR: return true; case AST_APP: j = to_app(n)->get_num_args(); while (j > 0) { --j; visit(to_app(n)->get_arg(j), visited); } return visited; case AST_QUANTIFIER: visit(to_quantifier(n)->get_expr(), visited); return visited; default: UNREACHABLE(); return true; } } void push_app_ite::reduce1(expr * n) { switch (n->get_kind()) { case AST_VAR: cache_result(n, n, 0); break; case AST_APP: reduce1_app(to_app(n)); break; case AST_QUANTIFIER: reduce1_quantifier(to_quantifier(n)); break; default: UNREACHABLE(); } } void push_app_ite::reduce1_app(app * n) { m_args.reset(); func_decl * decl = n->get_decl(); proof_ref p1(m); get_args(n, m_args, p1); expr_ref r(m); if (is_target(decl, m_args.size(), m_args.c_ptr())) apply(decl, m_args.size(), m_args.c_ptr(), r); else mk_app(decl, m_args.size(), m_args.c_ptr(), r); if (!m.fine_grain_proofs()) cache_result(n, r, 0); else { expr * s = m.mk_app(decl, m_args.size(), m_args.c_ptr()); proof * p; if (n == r) p = 0; else if (r != s) p = m.mk_transitivity(p1, m.mk_rewrite(s, r)); else p = p1; cache_result(n, r, p); } } void push_app_ite::reduce1_quantifier(quantifier * q) { expr * new_body; proof * new_body_pr; get_cached(q->get_expr(), new_body, new_body_pr); quantifier * new_q = m.update_quantifier(q, new_body); proof * p = q == new_q ? 0 : m.mk_quant_intro(q, new_q, new_body_pr); cache_result(q, new_q, p); } bool ng_push_app_ite::is_target(func_decl * decl, unsigned num_args, expr * const * args) { bool r = push_app_ite::is_target(decl, num_args, args); if (!r) return false; for (unsigned i = 0; i < num_args; i++) if (!is_ground(args[i])) return true; return false; } ng_push_app_ite::ng_push_app_ite(simplifier & s, bool conservative): push_app_ite(s, conservative) { } z3-4.4.0/src/ast/simplifier/arith_simplifier_params.h0000644000175000017500000000111412540347414022431 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: arith_simplifier_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-09. Revision History: --*/ #ifndef _ARITH_SIMPLIFIER_PARAMS_H_ #define _ARITH_SIMPLIFIER_PARAMS_H_ #include"params.h" struct arith_simplifier_params { bool m_arith_expand_eqs; bool m_arith_process_all_eqs; arith_simplifier_params(params_ref const & p = params_ref()) { updt_params(p); } void updt_params(params_ref const & _p); }; #endif /* _ARITH_SIMPLIFIER_PARAMS_H_ */ z3-4.4.0/src/ast/simplifier/poly_simplifier_plugin.cpp0000644000175000017500000006105312540347414022663 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: poly_simplifier_plugin.cpp Abstract: Abstract class for families that have polynomials. Author: Leonardo (leonardo) 2008-01-08 --*/ #include"poly_simplifier_plugin.h" #include"ast_pp.h" #include"ast_util.h" #include"ast_smt2_pp.h" #include"ast_ll_pp.h" poly_simplifier_plugin::poly_simplifier_plugin(symbol const & fname, ast_manager & m, decl_kind add, decl_kind mul, decl_kind uminus, decl_kind sub, decl_kind num): simplifier_plugin(fname, m), m_ADD(add), m_MUL(mul), m_SUB(sub), m_UMINUS(uminus), m_NUM(num), m_curr_sort(0), m_curr_sort_zero(0) { } expr * poly_simplifier_plugin::mk_add(unsigned num_args, expr * const * args) { SASSERT(num_args > 0); #ifdef Z3DEBUG // check for incorrect use of mk_add for (unsigned i = 0; i < num_args; i++) { SASSERT(!is_zero(args[i])); } #endif if (num_args == 1) return args[0]; else return m_manager.mk_app(m_fid, m_ADD, num_args, args); } expr * poly_simplifier_plugin::mk_mul(unsigned num_args, expr * const * args) { SASSERT(num_args > 0); #ifdef Z3DEBUG // check for incorrect use of mk_mul set_curr_sort(args[0]); SASSERT(!is_zero(args[0])); numeral k; for (unsigned i = 0; i < num_args; i++) { SASSERT(!is_numeral(args[i], k) || !k.is_one()); SASSERT(i == 0 || !is_numeral(args[i])); } #endif if (num_args == 1) return args[0]; else if (num_args == 2) return m_manager.mk_app(m_fid, m_MUL, args[0], args[1]); else if (is_numeral(args[0])) return m_manager.mk_app(m_fid, m_MUL, args[0], m_manager.mk_app(m_fid, m_MUL, num_args - 1, args+1)); else return m_manager.mk_app(m_fid, m_MUL, num_args, args); } expr * poly_simplifier_plugin::mk_mul(numeral const & c, expr * body) { numeral c_prime; c_prime = norm(c); if (c_prime.is_zero()) return 0; if (body == 0) return mk_numeral(c_prime); if (c_prime.is_one()) return body; set_curr_sort(body); expr * args[2] = { mk_numeral(c_prime), body }; return mk_mul(2, args); } /** \brief Traverse args, and copy the non-numeral exprs to result, and accumulate the value of the numerals in k. */ void poly_simplifier_plugin::process_monomial(unsigned num_args, expr * const * args, numeral & k, ptr_buffer & result) { rational v; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (is_numeral(arg, v)) k *= v; else result.push_back(arg); } } #ifdef Z3DEBUG /** \brief Return true if m is a wellformed monomial. */ bool poly_simplifier_plugin::wf_monomial(expr * m) const { SASSERT(!is_add(m)); if (is_mul(m)) { app * curr = to_app(m); expr * pp = 0; if (is_numeral(curr->get_arg(0))) pp = curr->get_arg(1); else pp = curr; if (is_mul(pp)) { for (unsigned i = 0; i < to_app(pp)->get_num_args(); i++) { expr * arg = to_app(pp)->get_arg(i); CTRACE("wf_monomial_bug", is_mul(arg), tout << "m: " << mk_ismt2_pp(m, m_manager) << "\n"; tout << "pp: " << mk_ismt2_pp(pp, m_manager) << "\n"; tout << "arg: " << mk_ismt2_pp(arg, m_manager) << "\n"; tout << "i: " << i << "\n"; ); SASSERT(!is_mul(arg)); SASSERT(!is_numeral(arg)); } } } return true; } /** \brief Return true if m is a wellformed polynomial. */ bool poly_simplifier_plugin::wf_polynomial(expr * m) const { if (is_add(m)) { for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); SASSERT(!is_add(arg)); SASSERT(wf_monomial(arg)); } } else if (is_mul(m)) { SASSERT(wf_monomial(m)); } return true; } #endif /** \brief Functor used to sort the elements of a monomial. Force numeric constants to be in the beginning. */ struct monomial_element_lt_proc { poly_simplifier_plugin & m_plugin; monomial_element_lt_proc(poly_simplifier_plugin & p):m_plugin(p) {} bool operator()(expr * m1, expr * m2) const { SASSERT(!m_plugin.is_numeral(m1) || !m_plugin.is_numeral(m2)); if (m_plugin.is_numeral(m1)) return true; if (m_plugin.is_numeral(m2)) return false; return m1->get_id() < m2->get_id(); } }; /** \brief Create a monomial (* args). */ void poly_simplifier_plugin::mk_monomial(unsigned num_args, expr * * args, expr_ref & result) { switch(num_args) { case 0: result = mk_one(); break; case 1: result = args[0]; break; default: std::stable_sort(args, args + num_args, monomial_element_lt_proc(*this)); result = mk_mul(num_args, args); SASSERT(wf_monomial(result)); break; } } /** \brief Return the body of the monomial. That is, the monomial without a coefficient. Examples: (* 2 (* x y)) ==> (* x y) (* x x) ==> (* x x) x ==> x 10 ==> 10 */ expr * poly_simplifier_plugin::get_monomial_body(expr * m) { TRACE("get_monomial_body_bug", tout << mk_pp(m, m_manager) << "\n";); SASSERT(wf_monomial(m)); if (!is_mul(m)) return m; if (is_numeral(to_app(m)->get_arg(0))) return to_app(m)->get_arg(1); return m; } inline bool is_essentially_var(expr * n, family_id fid) { SASSERT(is_var(n) || is_app(n)); return is_var(n) || to_app(n)->get_family_id() != fid; } /** \brief Hack for ordering monomials. We want an order << where - (* c1 m1) << (* c2 m2) when m1->get_id() < m2->get_id(), and c1 and c2 are numerals. - c << m when c is a numeral, and m is not. So, this method returns -1 for numerals, and the id of the body of the monomial */ int poly_simplifier_plugin::get_monomial_body_order(expr * m) { if (is_essentially_var(m, m_fid)) { return m->get_id(); } else if (is_mul(m)) { if (is_numeral(to_app(m)->get_arg(0))) return to_app(m)->get_arg(1)->get_id(); else return m->get_id(); } else if (is_numeral(m)) { return -1; } else { return m->get_id(); } } void poly_simplifier_plugin::get_monomial_coeff(expr * m, numeral & result) { SASSERT(!is_numeral(m)); SASSERT(wf_monomial(m)); if (!is_mul(m)) result = numeral::one(); else if (is_numeral(to_app(m)->get_arg(0), result)) return; else result = numeral::one(); } /** \brief Return true if n1 and n2 can be written as k1 * t and k2 * t, where k1 and k2 are numerals, or n1 and n2 are both numerals. */ bool poly_simplifier_plugin::eq_monomials_modulo_k(expr * n1, expr * n2) { bool is_num1 = is_numeral(n1); bool is_num2 = is_numeral(n2); if (is_num1 != is_num2) return false; if (is_num1 && is_num2) return true; return get_monomial_body(n1) == get_monomial_body(n2); } /** \brief Return (k1 + k2) * t (or (k1 - k2) * t when inv = true), where n1 = k1 * t, and n2 = k2 * t Return false if the monomials cancel each other. */ bool poly_simplifier_plugin::merge_monomials(bool inv, expr * n1, expr * n2, expr_ref & result) { numeral k1; numeral k2; bool is_num1 = is_numeral(n1, k1); bool is_num2 = is_numeral(n2, k2); SASSERT(is_num1 == is_num2); if (!is_num1 && !is_num2) { get_monomial_coeff(n1, k1); get_monomial_coeff(n2, k2); SASSERT(eq_monomials_modulo_k(n1, n2)); } if (inv) k1 -= k2; else k1 += k2; if (k1.is_zero()) return false; if (is_num1 && is_num2) { result = mk_numeral(k1); } else { expr * b = get_monomial_body(n1); if (k1.is_one()) result = b; else result = m_manager.mk_app(m_fid, m_MUL, mk_numeral(k1), b); } TRACE("merge_monomials", tout << mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager) << "\n" << mk_pp(result, m_manager) << "\n";); return true; } /** \brief Return a monomial equivalent to -n. */ void poly_simplifier_plugin::inv_monomial(expr * n, expr_ref & result) { set_curr_sort(n); SASSERT(wf_monomial(n)); rational v; SASSERT(n != 0); TRACE("inv_monomial_bug", tout << "n:\n" << mk_ismt2_pp(n, m_manager) << "\n";); if (is_numeral(n, v)) { TRACE("inv_monomial_bug", tout << "is numeral\n";); v.neg(); result = mk_numeral(v); } else { TRACE("inv_monomial_bug", tout << "is not numeral\n";); numeral k; get_monomial_coeff(n, k); expr * b = get_monomial_body(n); k.neg(); if (k.is_one()) result = b; else result = m_manager.mk_app(m_fid, m_MUL, mk_numeral(k), b); } } /** \brief Add a monomial n to result. */ template void poly_simplifier_plugin::add_monomial_core(expr * n, expr_ref_vector & result) { if (is_zero(n)) return; if (Inv) { expr_ref n_prime(m_manager); inv_monomial(n, n_prime); result.push_back(n_prime); } else { result.push_back(n); } } void poly_simplifier_plugin::add_monomial(bool inv, expr * n, expr_ref_vector & result) { if (inv) add_monomial_core(n, result); else add_monomial_core(n, result); } /** \brief Copy the monomials in n to result. The monomials are inverted if inv is true. Equivalent monomials are merged. */ template void poly_simplifier_plugin::process_sum_of_monomials_core(expr * n, expr_ref_vector & result) { SASSERT(wf_polynomial(n)); if (is_add(n)) { for (unsigned i = 0; i < to_app(n)->get_num_args(); i++) add_monomial_core(to_app(n)->get_arg(i), result); } else { add_monomial_core(n, result); } } void poly_simplifier_plugin::process_sum_of_monomials(bool inv, expr * n, expr_ref_vector & result) { if (inv) process_sum_of_monomials_core(n, result); else process_sum_of_monomials_core(n, result); } /** \brief Copy the (non-numeral) monomials in n to result. The monomials are inverted if inv is true. Equivalent monomials are merged. The constant (numeral) monomials are accumulated in k. */ void poly_simplifier_plugin::process_sum_of_monomials(bool inv, expr * n, expr_ref_vector & result, numeral & k) { SASSERT(wf_polynomial(n)); numeral val; if (is_add(n)) { for (unsigned i = 0; i < to_app(n)->get_num_args(); i++) { expr * arg = to_app(n)->get_arg(i); if (is_numeral(arg, val)) { k += inv ? -val : val; } else { add_monomial(inv, arg, result); } } } else if (is_numeral(n, val)) { k += inv ? -val : val; } else { add_monomial(inv, n, result); } } /** \brief Functor used to sort monomials. Force numeric constants to be in the beginning of a polynomial. */ struct monomial_lt_proc { poly_simplifier_plugin & m_plugin; monomial_lt_proc(poly_simplifier_plugin & p):m_plugin(p) {} bool operator()(expr * m1, expr * m2) const { return m_plugin.get_monomial_body_order(m1) < m_plugin.get_monomial_body_order(m2); } }; void poly_simplifier_plugin::mk_sum_of_monomials_core(unsigned sz, expr ** ms, expr_ref & result) { switch (sz) { case 0: result = mk_zero(); break; case 1: result = ms[0]; break; default: result = mk_add(sz, ms); break; } } /** \brief Return true if m is essentially a variable, or is of the form (* c x), where c is a numeral and x is essentially a variable. Store the "variable" in x. */ bool poly_simplifier_plugin::is_simple_monomial(expr * m, expr * & x) { if (is_essentially_var(m, m_fid)) { x = m; return true; } if (is_app(m) && to_app(m)->get_num_args() == 2) { expr * arg1 = to_app(m)->get_arg(0); expr * arg2 = to_app(m)->get_arg(1); if (is_numeral(arg1) && is_essentially_var(arg2, m_fid)) { x = arg2; return true; } } return false; } /** \brief Return true if all monomials are simple, and each "variable" occurs only once. The method assumes the monomials were sorted using monomial_lt_proc. */ bool poly_simplifier_plugin::is_simple_sum_of_monomials(expr_ref_vector & monomials) { expr * last_var = 0; expr * curr_var = 0; unsigned size = monomials.size(); for (unsigned i = 0; i < size; i++) { expr * m = monomials.get(i); if (!is_simple_monomial(m, curr_var)) return false; if (curr_var == last_var) return false; last_var = curr_var; } return true; } /** \brief Store in result the sum of the given monomials. */ void poly_simplifier_plugin::mk_sum_of_monomials(expr_ref_vector & monomials, expr_ref & result) { switch (monomials.size()) { case 0: result = mk_zero(); break; case 1: result = monomials.get(0); break; default: { TRACE("mk_sum_sort", tout << "before\n"; for (unsigned i = 0; i < monomials.size(); i++) tout << mk_ll_pp(monomials.get(i), m_manager) << "\n";); std::stable_sort(monomials.c_ptr(), monomials.c_ptr() + monomials.size(), monomial_lt_proc(*this)); TRACE("mk_sum_sort", tout << "after\n"; for (unsigned i = 0; i < monomials.size(); i++) tout << mk_ll_pp(monomials.get(i), m_manager) << "\n";); if (is_simple_sum_of_monomials(monomials)) { mk_sum_of_monomials_core(monomials.size(), monomials.c_ptr(), result); return; } ptr_buffer new_monomials; expr * last_body = 0; numeral last_coeff; numeral coeff; unsigned sz = monomials.size(); for (unsigned i = 0; i < sz; i++) { expr * m = monomials.get(i); expr * body = 0; if (!is_numeral(m, coeff)) { body = get_monomial_body(m); get_monomial_coeff(m, coeff); } if (last_body == body) { last_coeff += coeff; continue; } expr * new_m = mk_mul(last_coeff, last_body); if (new_m) new_monomials.push_back(new_m); last_body = body; last_coeff = coeff; } expr * new_m = mk_mul(last_coeff, last_body); if (new_m) new_monomials.push_back(new_m); TRACE("mk_sum", for (unsigned i = 0; i < monomials.size(); i++) tout << mk_pp(monomials.get(i), m_manager) << "\n"; tout << "======>\n"; for (unsigned i = 0; i < new_monomials.size(); i++) tout << mk_pp(new_monomials.get(i), m_manager) << "\n";); mk_sum_of_monomials_core(new_monomials.size(), new_monomials.c_ptr(), result); break; } } } /** \brief Auxiliary template for mk_add_core */ template void poly_simplifier_plugin::mk_add_core_core(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args >= 2); expr_ref_vector monomials(m_manager); process_sum_of_monomials_core(args[0], monomials); for (unsigned i = 1; i < num_args; i++) { process_sum_of_monomials_core(args[i], monomials); } TRACE("mk_add_core_bug", for (unsigned i = 0; i < monomials.size(); i++) { SASSERT(monomials.get(i) != 0); tout << mk_ismt2_pp(monomials.get(i), m_manager) << "\n"; }); mk_sum_of_monomials(monomials, result); } /** \brief Return a sum of monomials. The method assume that each arg in args is a sum of monomials. If inv is true, then all but the first argument in args are inverted. */ void poly_simplifier_plugin::mk_add_core(bool inv, unsigned num_args, expr * const * args, expr_ref & result) { TRACE("mk_add_core_bug", for (unsigned i = 0; i < num_args; i++) { SASSERT(args[i] != 0); tout << mk_ismt2_pp(args[i], m_manager) << "\n"; }); switch (num_args) { case 0: result = mk_zero(); break; case 1: result = args[0]; break; default: if (inv) mk_add_core_core(num_args, args, result); else mk_add_core_core(num_args, args, result); break; } } void poly_simplifier_plugin::mk_add(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args > 0); set_curr_sort(args[0]); mk_add_core(false, num_args, args, result); } void poly_simplifier_plugin::mk_add(expr * arg1, expr * arg2, expr_ref & result) { expr * args[2] = { arg1, arg2 }; mk_add(2, args, result); } void poly_simplifier_plugin::mk_sub(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args > 0); set_curr_sort(args[0]); mk_add_core(true, num_args, args, result); } void poly_simplifier_plugin::mk_sub(expr * arg1, expr * arg2, expr_ref & result) { expr * args[2] = { arg1, arg2 }; mk_sub(2, args, result); } void poly_simplifier_plugin::mk_uminus(expr * arg, expr_ref & result) { set_curr_sort(arg); rational v; if (is_numeral(arg, v)) { v.neg(); result = mk_numeral(v); } else { expr_ref zero(mk_zero(), m_manager); mk_sub(zero.get(), arg, result); } } /** \brief Add monomial n to result, the coeff of n is stored in k. */ void poly_simplifier_plugin::append_to_monomial(expr * n, numeral & k, ptr_buffer & result) { SASSERT(wf_monomial(n)); rational val; if (is_numeral(n, val)) { k *= val; return; } get_monomial_coeff(n, val); k *= val; n = get_monomial_body(n); if (is_mul(n)) { for (unsigned i = 0; i < to_app(n)->get_num_args(); i++) result.push_back(to_app(n)->get_arg(i)); } else { result.push_back(n); } } /** \brief Return a sum of monomials that is equivalent to (* args[0] ... args[num_args-1]). This method assumes that each arg[i] is a sum of monomials. */ void poly_simplifier_plugin::mk_mul(unsigned num_args, expr * const * args, expr_ref & result) { if (num_args == 1) { result = args[0]; return; } rational val; if (num_args == 2 && is_numeral(args[0], val) && is_essentially_var(args[1], m_fid)) { if (val.is_one()) result = args[1]; else if (val.is_zero()) result = args[0]; else result = mk_mul(num_args, args); return; } if (num_args == 2 && is_essentially_var(args[0], m_fid) && is_numeral(args[1], val)) { if (val.is_one()) result = args[0]; else if (val.is_zero()) result = args[1]; else { expr * inv_args[2] = { args[1], args[0] }; result = mk_mul(2, inv_args); } return; } TRACE("mk_mul_bug", for (unsigned i = 0; i < num_args; i++) { tout << mk_pp(args[i], m_manager) << "\n"; }); set_curr_sort(args[0]); buffer szs; buffer it; vector > sums; for (unsigned i = 0; i < num_args; i ++) { it.push_back(0); expr * arg = args[i]; SASSERT(wf_polynomial(arg)); sums.push_back(ptr_vector()); ptr_vector & v = sums.back(); if (is_add(arg)) { v.append(to_app(arg)->get_num_args(), to_app(arg)->get_args()); } else { v.push_back(arg); } szs.push_back(v.size()); } expr_ref_vector monomials(m_manager); do { rational k(1); ptr_buffer m; for (unsigned i = 0; i < num_args; i++) { ptr_vector & v = sums[i]; expr * arg = v[it[i]]; TRACE("mk_mul_bug", tout << "k: " << k << " arg: " << mk_pp(arg, m_manager) << "\n";); append_to_monomial(arg, k, m); TRACE("mk_mul_bug", tout << "after k: " << k << "\n";); } expr_ref num(m_manager); if (!k.is_zero() && !k.is_one()) { num = mk_numeral(k); m.push_back(num); // bit-vectors can normalize // to 1 during // internalization. if (is_numeral(num, k) && k.is_one()) { m.pop_back(); } } if (!k.is_zero()) { expr_ref new_monomial(m_manager); TRACE("mk_mul_bug", for (unsigned i = 0; i < m.size(); i++) { tout << mk_pp(m[i], m_manager) << "\n"; }); mk_monomial(m.size(), m.c_ptr(), new_monomial); TRACE("mk_mul_bug", tout << "new_monomial:\n" << mk_pp(new_monomial, m_manager) << "\n";); add_monomial_core(new_monomial, monomials); } } while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr())); mk_sum_of_monomials(monomials, result); } void poly_simplifier_plugin::mk_mul(expr * arg1, expr * arg2, expr_ref & result) { expr * args[2] = { arg1, arg2 }; mk_mul(2, args, result); } bool poly_simplifier_plugin::reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result) { set_reduce_invoked(); unsigned i = 0; for (; i < num_args; i++) if (!is_numeral(args[i])) break; if (i == num_args) { // all arguments are numerals // check if arguments are different... ptr_buffer buffer; buffer.append(num_args, args); std::sort(buffer.begin(), buffer.end(), ast_lt_proc()); for (unsigned i = 0; i < num_args; i++) { if (i > 0 && buffer[i] == buffer[i-1]) { result = m_manager.mk_false(); return true; } } result = m_manager.mk_true(); return true; } return false; } bool poly_simplifier_plugin::reduce(func_decl * f, unsigned num_args, rational const * mults, expr * const * args, expr_ref & result) { set_reduce_invoked(); if (is_decl_of(f, m_fid, m_ADD)) { SASSERT(num_args > 0); set_curr_sort(args[0]); expr_ref_buffer args1(m_manager); for (unsigned i = 0; i < num_args; ++i) { expr * arg = args[i]; rational m = norm(mults[i]); if (m.is_zero()) { // skip } else if (m.is_one()) { args1.push_back(arg); } else { expr_ref k(m_manager); k = mk_numeral(m); expr_ref new_arg(m_manager); mk_mul(k, args[i], new_arg); args1.push_back(new_arg); } } if (args1.empty()) { result = mk_zero(); } else { mk_add(args1.size(), args1.c_ptr(), result); } return true; } else { return simplifier_plugin::reduce(f, num_args, mults, args, result); } } /** \brief Return true if n is can be put into the form (+ v t) or (+ (- v) t) \c inv = true will contain true if (- v) is found, and false otherwise. */ bool poly_simplifier_plugin::is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t) { if (!is_add(n) || is_ground(n)) return false; ptr_buffer args; v = 0; expr * curr = to_app(n); bool stop = false; inv = false; while (!stop) { expr * arg; expr * neg_arg; if (is_add(curr)) { arg = to_app(curr)->get_arg(0); curr = to_app(curr)->get_arg(1); } else { arg = curr; stop = true; } if (is_ground(arg)) { TRACE("model_checker_bug", tout << "pushing:\n" << mk_pp(arg, m_manager) << "\n";); args.push_back(arg); } else if (is_var(arg)) { if (v != 0) return false; // already found variable v = to_var(arg); } else if (is_times_minus_one(arg, neg_arg) && is_var(neg_arg)) { if (v != 0) return false; // already found variable v = to_var(neg_arg); inv = true; } else { return false; // non ground term. } } if (v == 0) return false; // did not find variable SASSERT(!args.empty()); mk_add(args.size(), args.c_ptr(), t); return true; } z3-4.4.0/src/ast/simplifier/maximise_ac_sharing.cpp0000644000175000017500000001241312540347414022065 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: maximise_ac_sharing.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-10-22. Revision History: --*/ #include"maximise_ac_sharing.h" #include"ast_pp.h" #include"basic_simplifier_plugin.h" maximise_ac_sharing::ac_plugin::ac_plugin(symbol const & fname, ast_manager & m, maximise_ac_sharing & owner): simplifier_plugin(fname, m), m_owner(owner) { } void maximise_ac_sharing::ac_plugin::register_kind(decl_kind k) { m_kinds.push_back(k); } maximise_ac_sharing::ac_plugin::~ac_plugin() { } bool maximise_ac_sharing::ac_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { decl_kind k = f->get_kind(); if (!f->is_associative()) return false; if (num_args <= 2) return false; if (std::find(m_kinds.begin(), m_kinds.end(), k) == m_kinds.end()) return false; ptr_buffer _args; expr * numeral = 0; if (m_owner.is_numeral(args[0])) { numeral = args[0]; for (unsigned i = 1; i < num_args; i++) _args.push_back(args[i]); num_args--; } else { _args.append(num_args, args); } TRACE("ac_sharing_detail", tout << "before-reuse: num_args: " << num_args << "\n";); #define MAX_NUM_ARGS_FOR_OPT 128 // Try to reuse already created circuits. TRACE("ac_sharing_detail", tout << "args: "; for (unsigned i = 0; i < num_args; i++) tout << mk_pp(_args[i], m_manager) << "\n";); try_to_reuse: if (num_args > 1 && num_args < MAX_NUM_ARGS_FOR_OPT) { for (unsigned i = 0; i < num_args - 1; i++) { for (unsigned j = i + 1; j < num_args; j++) { if (m_owner.contains(f, _args[i], _args[j])) { TRACE("ac_sharing_detail", tout << "reusing args: " << i << " " << j << "\n";); _args[i] = m_manager.mk_app(f, _args[i], _args[j]); SASSERT(num_args > 1); for (unsigned w = j; w < num_args - 1; w++) { _args[w] = _args[w+1]; } num_args--; goto try_to_reuse; } } } } // Create "tree-like circuit" while (true) { TRACE("ac_sharing_detail", tout << "tree-loop: num_args: " << num_args << "\n";); unsigned j = 0; for (unsigned i = 0; i < num_args; i += 2, j++) { if (i == num_args - 1) { _args[j] = _args[i]; } else { m_owner.insert(f, _args[i], _args[i+1]); _args[j] = m_manager.mk_app(f, _args[i], _args[i+1]); } } num_args = j; if (num_args == 1) { if (numeral == 0) { result = _args[0]; } else { result = m_manager.mk_app(f, numeral, _args[0]); } TRACE("ac_sharing_detail", tout << "result: " << mk_pp(result, m_manager) << "\n";); return true; } } UNREACHABLE(); return false; } bool maximise_ac_sharing::contains(func_decl * f, expr * arg1, expr * arg2) { entry e(f, arg1, arg2); return m_cache.contains(&e); } void maximise_ac_sharing::insert(func_decl * f, expr * arg1, expr * arg2) { entry * e = new (m_region) entry(f, arg1, arg2); m_entries.push_back(e); m_cache.insert(e); m_manager.inc_ref(arg1); m_manager.inc_ref(arg2); } maximise_ac_sharing::maximise_ac_sharing(ast_manager & m): m_manager(m), m_simplifier(m), m_init(false) { basic_simplifier_plugin* basic_simp = alloc(basic_simplifier_plugin,m); m_simplifier.register_plugin(basic_simp); } maximise_ac_sharing::~maximise_ac_sharing() { restore_entries(0); } void maximise_ac_sharing::operator()(expr * s, expr_ref & r, proof_ref & p) { init(); m_simplifier.operator()(s, r, p); } void maximise_ac_sharing::push_scope() { init(); m_scopes.push_back(m_entries.size()); m_region.push_scope(); } void maximise_ac_sharing::pop_scope(unsigned num_scopes) { SASSERT(num_scopes <= m_scopes.size()); unsigned new_lvl = m_scopes.size() - num_scopes; unsigned old_lim = m_scopes[new_lvl]; restore_entries(old_lim); m_region.pop_scope(num_scopes); m_scopes.shrink(new_lvl); } void maximise_ac_sharing::restore_entries(unsigned old_lim) { unsigned i = m_entries.size(); while (i != old_lim) { --i; entry * e = m_entries[i]; m_manager.dec_ref(e->m_arg1); m_manager.dec_ref(e->m_arg2); } m_entries.shrink(old_lim); } void maximise_ac_sharing::reset() { restore_entries(0); m_entries.reset(); m_cache.reset(); m_simplifier.reset(); m_region.reset(); m_scopes.reset(); } void maximise_bv_sharing::init_core() { maximise_ac_sharing::ac_plugin * p = alloc(maximise_ac_sharing::ac_plugin, symbol("bv"), get_manager(), *this); p->register_kind(OP_BADD); p->register_kind(OP_BMUL); p->register_kind(OP_BOR); p->register_kind(OP_BAND); register_plugin(p); } bool maximise_bv_sharing::is_numeral(expr * n) const { return m_util.is_numeral(n); } maximise_bv_sharing::maximise_bv_sharing(ast_manager & m): maximise_ac_sharing(m), m_util(m) { } z3-4.4.0/src/ast/simplifier/array_simplifier_plugin.cpp0000644000175000017500000007010512540347414023014 0ustar michaelmichael/*++ Copyright (c) 2008 Microsoft Corporation Module Name: array_simplifier_plugin.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2008-05-05 Revision History: Notes TODO: Examine quadratic cost of simplification vs. model-based procedure. Parameterize cache replacement strategy. Some parameters are hard-wired. --*/ #include "array_simplifier_plugin.h" #include "ast_ll_pp.h" #include "ast_pp.h" array_simplifier_plugin::array_simplifier_plugin( ast_manager & m, basic_simplifier_plugin& s, simplifier& simp, array_simplifier_params const& p) : simplifier_plugin(symbol("array"),m), m_util(m), m_simp(s), m_simplifier(simp), m_params(p), m_store_cache_size(0) {} array_simplifier_plugin::~array_simplifier_plugin() { select_cache::iterator it = m_select_cache.begin(); select_cache::iterator end = m_select_cache.end(); for ( ; it != end; ++it) { m_manager.dec_array_ref(it->m_key->size(), it->m_key->c_ptr()); m_manager.dec_ref(it->m_value); dealloc(it->m_key); } store_cache::iterator it2 = m_store_cache.begin(); store_cache::iterator end2 = m_store_cache.end(); for (; it2 != end2; ++it2) { m_manager.dec_ref(it->m_value); dealloc(it->m_key); } } bool array_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (!m_params.m_array_simplify) return false; set_reduce_invoked(); if (m_presimp) return false; #if Z3DEBUG for (unsigned i = 0; i < num_args && i < f->get_arity(); ++i) { SASSERT(m_manager.get_sort(args[i]) == f->get_domain(i)); } #endif TRACE("array_simplifier", { tout << mk_pp(f, m_manager) << " "; for (unsigned i = 0; i < num_args; ++i) { tout << mk_pp(args[i], m_manager) << " "; } tout << "\n"; } ); SASSERT(f->get_family_id() == m_fid); switch(f->get_decl_kind()) { case OP_SELECT: mk_select(num_args, args, result); break; case OP_STORE: mk_store(f, num_args, args, result); break; case OP_SET_UNION: { sort* s = f->get_range(); expr_ref empty(m_manager); mk_empty_set(s, empty); switch(num_args) { case 0: result = empty; break; case 1: result = args[0]; break; default: { result = args[0]; func_decl* f_or = m_manager.mk_or_decl(); for (unsigned i = 1; i < num_args; ++i) { mk_map(f_or, result, args[i], result); } break; } } break; } case OP_SET_INTERSECT: { expr_ref full(m_manager); mk_full_set(f->get_range(), full); switch(num_args) { case 0: result = full; break; case 1: result = args[0]; break; default: { result = args[0]; func_decl* f_and = m_manager.mk_and_decl(); for (unsigned i = 1; i < num_args; ++i) { mk_map(f_and, result, args[i], result); } break; } } TRACE("array_simplifier", tout << "sort " << mk_pp(result.get(), m_manager) << "\n";); break; } case OP_SET_SUBSET: { SASSERT(num_args == 2); expr_ref diff(m_manager), emp(m_manager); mk_set_difference(num_args, args, diff); mk_empty_set(m_manager.get_sort(args[0]), emp); m_simp.mk_eq(diff.get(), emp.get(), result); break; } case OP_SET_COMPLEMENT: { SASSERT(num_args == 1); func_decl* f_not = m_manager.mk_not_decl(); mk_map(f_not, args[0], result); break; } case OP_SET_DIFFERENCE: { SASSERT(num_args == 2); expr_ref r1(m_manager); mk_map(m_manager.mk_not_decl(), args[1], r1); mk_map(m_manager.mk_and_decl(), args[0], r1, result); break; } case OP_ARRAY_MAP: { SASSERT(f->get_num_parameters() == 1); SASSERT(f->get_parameter(0).is_ast()); SASSERT(is_func_decl(f->get_parameter(0).get_ast())); // // map_d (store a j v) = (store (map_f a) v (d v)) // if (num_args == 1 && is_store(args[0])) { app* store_expr = to_app(args[0]); unsigned num_args = store_expr->get_num_args(); SASSERT(num_args >= 3); parameter p = f->get_parameter(0); func_decl* d = to_func_decl(p.get_ast()); expr* a = store_expr->get_arg(0); expr* v = store_expr->get_arg(num_args-1); // expr*const* args = store_expr->get_args()+1; expr_ref r1(m_manager), r2(m_manager); ptr_vector new_args; reduce(f, 1, &a, r1); m_simplifier.mk_app(d, 1, &v, r2); new_args.push_back(r1); for (unsigned i = 1; i + 1 < num_args; ++i) { new_args.push_back(store_expr->get_arg(i)); } new_args.push_back(r2); mk_store(store_expr->get_decl(), num_args, new_args.c_ptr(), result); break; } // // map_d (store a j v) (store b j w) = (store (map_f a b) j (d v w)) // if (num_args > 1 && same_store(num_args, args)) { app* store_expr1 = to_app(args[0]); unsigned num_indices = store_expr1->get_num_args(); SASSERT(num_indices >= 3); parameter p = f->get_parameter(0); func_decl* d = to_func_decl(p.get_ast()); ptr_vector arrays; ptr_vector values; for (unsigned i = 0; i < num_args; ++i) { arrays.push_back(to_app(args[i])->get_arg(0)); values.push_back(to_app(args[i])->get_arg(num_indices-1)); } expr_ref r1(m_manager), r2(m_manager); reduce(f, arrays.size(), arrays.c_ptr(), r1); m_simplifier.mk_app(d, values.size(), values.c_ptr(), r2); ptr_vector new_args; new_args.push_back(r1); for (unsigned i = 1; i + 1 < num_indices; ++i) { new_args.push_back(store_expr1->get_arg(i)); } new_args.push_back(r2); mk_store(store_expr1->get_decl(), new_args.size(), new_args.c_ptr(), result); break; } // // map_d (const v) = (const (d v)) // if (num_args == 1 && is_const_array(args[0])) { app* const_expr = to_app(args[0]); SASSERT(const_expr->get_num_args() == 1); parameter p = f->get_parameter(0); func_decl* d = to_func_decl(p.get_ast()); expr* v = const_expr->get_arg(0); expr_ref r1(m_manager); m_simplifier.mk_app(d, 1, &v, r1); expr* arg = r1.get(); parameter param(f->get_range()); result = m_manager.mk_app(m_fid, OP_CONST_ARRAY, 1, ¶m, 1, &arg); break; } // // map_d (const v) (const w) = (const (d v w)) // if (num_args > 1 && all_const_array(num_args, args)) { parameter p = f->get_parameter(0); func_decl* d = to_func_decl(p.get_ast()); ptr_vector values; for (unsigned i = 0; i < num_args; ++i) { values.push_back(to_app(args[i])->get_arg(0)); } expr_ref r1(m_manager); m_simplifier.mk_app(d, values.size(), values.c_ptr(), r1); expr* arg = r1.get(); parameter param(f->get_range()); result = m_manager.mk_app(m_fid, OP_CONST_ARRAY, 1, ¶m, 1, &arg); break; } result = m_manager.mk_app(f, num_args, args); break; } default: result = m_manager.mk_app(f, num_args, args); break; } TRACE("array_simplifier", tout << mk_pp(result.get(), m_manager) << "\n";); return true; } bool array_simplifier_plugin::same_store(unsigned num_args, expr* const* args) const { if (num_args == 0) { return true; } if (!is_store(args[0])) { return false; } SASSERT(to_app(args[0])->get_num_args() >= 3); unsigned num_indices = to_app(args[0])->get_num_args() - 2; for (unsigned i = 1; i < num_args; ++i) { if (!is_store(args[i])) { return false; } for (unsigned j = 1; j < num_indices + 1; ++j) { if (to_app(args[0])->get_arg(j) != to_app(args[i])->get_arg(j)) { return false; } } } return true; } bool array_simplifier_plugin::all_const_array(unsigned num_args, expr* const* args) const { bool is_const = true; for (unsigned i = 0; is_const && i < num_args; ++i) { is_const = is_const_array(args[i]); } return is_const; } bool array_simplifier_plugin::all_values(unsigned num_args, expr* const* args) const { for (unsigned i = 0; i < num_args; ++i) { if (!m_manager.is_unique_value(args[i])) { return false; } } return true; } bool array_simplifier_plugin::lex_lt(unsigned num_args, expr* const* args1, expr* const* args2) { for (unsigned i = 0; i < num_args; ++i) { TRACE("array_simplifier", tout << mk_pp(args1[i], m_manager) << "\n"; tout << mk_pp(args2[i], m_manager) << "\n"; tout << args1[i]->get_id() << " " << args2[i]->get_id() << "\n"; ); if (args1[i]->get_id() < args2[i]->get_id()) return true; if (args1[i]->get_id() > args2[i]->get_id()) return false; } return false; } void array_simplifier_plugin::get_stores(expr* n, unsigned& arity, expr*& m, ptr_vector& stores) { while (is_store(n)) { app* a = to_app(n); SASSERT(a->get_num_args() > 2); arity = a->get_num_args()-2; n = a->get_arg(0); stores.push_back(a->get_args()+1); } m = n; } lbool array_simplifier_plugin::eq_default(expr* def, unsigned arity, unsigned num_st, expr*const* const* st) { bool all_diseq = m_manager.is_unique_value(def) && num_st > 0; bool all_eq = true; for (unsigned i = 0; i < num_st; ++i) { all_eq &= (st[i][arity] == def); all_diseq &= m_manager.is_unique_value(st[i][arity]) && (st[i][arity] != def); TRACE("array_simplifier", tout << m_manager.is_unique_value(st[i][arity]) << " " << mk_pp(st[i][arity], m_manager) << "\n";); } if (all_eq) { return l_true; } if (all_diseq) { return l_false; } return l_undef; } bool array_simplifier_plugin::insert_table(expr* def, unsigned arity, unsigned num_st, expr*const* const* st, arg_table& table) { for (unsigned i = 0; i < num_st; ++i ) { for (unsigned j = 0; j < arity; ++j) { if (!m_manager.is_unique_value(st[i][j])) { return false; } } TRACE("array_simplifier", tout << "inserting: "; for (unsigned j = 0; j < arity; ++j) { tout << mk_pp(st[i][j], m_manager) << " "; } tout << " |-> " << mk_pp(def, m_manager) << "\n"; ); args_entry e(arity, st[i]); table.insert_if_not_there(e); } return true; } lbool array_simplifier_plugin::eq_stores(expr* def, unsigned arity, unsigned num_st1, expr*const* const* st1, unsigned num_st2, expr*const* const* st2) { if (num_st1 == 0) { return eq_default(def, arity, num_st2, st2); } if (num_st2 == 0) { return eq_default(def, arity, num_st1, st1); } arg_table table1, table2; if (!insert_table(def, arity, num_st1, st1, table1)) { return l_undef; } if (!insert_table(def, arity, num_st2, st2, table2)) { return l_undef; } arg_table::iterator it = table1.begin(); arg_table::iterator end = table1.end(); for (; it != end; ++it) { args_entry const & e1 = *it; args_entry e2; expr* v1 = e1.m_args[arity]; if (table2.find(e1, e2)) { expr* v2 = e2.m_args[arity]; if (v1 == v2) { table2.erase(e1); continue; } if (m_manager.is_unique_value(v1) && m_manager.is_unique_value(v2)) { return l_false; } return l_undef; } else if (m_manager.is_unique_value(v1) && m_manager.is_unique_value(def) && v1 != def) { return l_false; } } it = table2.begin(); end = table2.end(); for (; it != end; ++it) { args_entry const & e = *it; expr* v = e.m_args[arity]; if (m_manager.is_unique_value(v) && m_manager.is_unique_value(def) && v != def) { return l_false; } } if (!table2.empty() || !table1.empty()) { return l_undef; } return l_true; } bool array_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) { set_reduce_invoked(); expr* c1, *c2; ptr_vector st1, st2; unsigned arity1 = 0; unsigned arity2 = 0; get_stores(lhs, arity1, c1, st1); get_stores(rhs, arity2, c2, st2); if (arity1 == arity2 && is_const_array(c1) && is_const_array(c2)) { c1 = to_app(c1)->get_arg(0); c2 = to_app(c2)->get_arg(0); if (c1 == c2) { lbool eq = eq_stores(c1, arity2, st1.size(), st1.c_ptr(), st2.size(), st2.c_ptr()); TRACE("array_simplifier", tout << mk_pp(lhs, m_manager) << " = " << mk_pp(rhs, m_manager) << " := " << eq << "\n"; tout << "arity: " << arity1 << "\n";); switch(eq) { case l_false: result = m_manager.mk_false(); return true; case l_true: result = m_manager.mk_true(); return true; default: return false; } } else if (m_manager.is_unique_value(c1) && m_manager.is_unique_value(c2)) { result = m_manager.mk_false(); return true; } } return false; } bool array_simplifier_plugin::reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result) { set_reduce_invoked(); return false; } array_simplifier_plugin::const_select_result array_simplifier_plugin::mk_select_const(expr* m, app* index, expr_ref& result) { store_info* info = 0; expr* r = 0, *a = 0; if (!is_store(m)) { return NOT_CACHED; } if (!m_store_cache.find(m, info)) { return NOT_CACHED; } if (info->m_map.find(index, r)) { result = r; return FOUND_VALUE; } a = info->m_default.get(); // // Unfold and cache the store while searching for value of index. // while (is_store(a) && m_manager.is_unique_value(to_app(a)->get_arg(1))) { app* b = to_app(a); app* c = to_app(b->get_arg(1)); if (!info->m_map.contains(c)) { info->m_map.insert(c, b->get_arg(2)); m_manager.inc_ref(b->get_arg(2)); ++m_store_cache_size; } a = b->get_arg(0); info->m_default = a; if (c == index) { result = b->get_arg(2); return FOUND_VALUE; } } result = info->m_default.get(); return FOUND_DEFAULT; } void array_simplifier_plugin::cache_store(unsigned num_stores, expr* store_term) { if (num_stores <= m_const_store_threshold) { return; } prune_store_cache(); if (!m_store_cache.contains(store_term)) { store_info * info = alloc(store_info, m_manager, store_term); m_manager.inc_ref(store_term); m_store_cache.insert(store_term, info); TRACE("cache_store", tout << m_store_cache.size() << "\n";); ++m_store_cache_size; } } void array_simplifier_plugin::cache_select(unsigned num_args, expr * const * args, expr * result) { ptr_vector * entry = alloc(ptr_vector); entry->append(num_args, const_cast(args)); const select_cache::key_data & kd = m_select_cache.insert_if_not_there(entry, result); if (kd.m_key != entry) { dealloc(entry); return; } m_manager.inc_array_ref(num_args, args); m_manager.inc_ref(result); TRACE("cache_select", tout << m_select_cache.size() << "\n";); } void array_simplifier_plugin::prune_select_cache() { if (m_select_cache.size() > m_select_cache_max_size) { flush_select_cache(); } } void array_simplifier_plugin::prune_store_cache() { if (m_store_cache_size > m_store_cache_max_size) { flush_store_cache(); } } void array_simplifier_plugin::flush_select_cache() { select_cache::iterator it = m_select_cache.begin(); select_cache::iterator end = m_select_cache.end(); for (; it != end; ++it) { ptr_vector * e = (*it).m_key; m_manager.dec_array_ref(e->size(), e->begin()); m_manager.dec_ref((*it).m_value); dealloc(e); } m_select_cache.reset(); } void array_simplifier_plugin::flush_store_cache() { store_cache::iterator it = m_store_cache.begin(); store_cache::iterator end = m_store_cache.end(); for (; it != end; ++it) { m_manager.dec_ref((*it).m_key); const_map::iterator mit = (*it).m_value->m_map.begin(); const_map::iterator mend = (*it).m_value->m_map.end(); for (; mit != mend; ++mit) { m_manager.dec_ref((*mit).m_value); } dealloc((*it).m_value); } m_store_cache.reset(); m_store_cache_size = 0; } void array_simplifier_plugin::flush_caches() { flush_select_cache(); flush_store_cache(); } void array_simplifier_plugin::mk_set_difference(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args == 2); result = m_manager.mk_app(m_fid, OP_SET_DIFFERENCE, 0, 0, num_args, args); } void array_simplifier_plugin::mk_empty_set(sort* ty, expr_ref & result) { parameter param(ty); expr* args[1] = { m_manager.mk_false() }; result = m_manager.mk_app(m_fid, OP_CONST_ARRAY, 1, ¶m, 1, args); } void array_simplifier_plugin::mk_full_set(sort* ty, expr_ref & result) { parameter param(ty); expr* args[1] = { m_manager.mk_true() }; result = m_manager.mk_app(m_fid, OP_CONST_ARRAY, 1, ¶m, 1, args); } bool array_simplifier_plugin::same_args(unsigned num_args, expr * const * args1, expr * const * args2) { for (unsigned i = 0; i < num_args; ++i) { if (args1[i] != args2[i]) { return false; } } return true; } void array_simplifier_plugin::mk_store(func_decl* f, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args >= 3); expr* arg0 = args[0]; expr* argn = args[num_args-1]; // // store(store(a,i,v),i,w) = store(a,i,w) // if (is_store(arg0) && same_args(num_args-2, args+1, to_app(arg0)->get_args()+1)) { expr_ref_buffer new_args(m_manager); new_args.push_back(to_app(arg0)->get_arg(0)); for (unsigned i = 1; i < num_args; ++i) { new_args.push_back(args[i]); } reduce(f, num_args, new_args.c_ptr(), result); TRACE("array_simplifier", tout << mk_pp(result.get(), m_manager) << "\n";); return; } // // store(const(v),i,v) = const(v) // if (is_const_array(arg0) && to_app(arg0)->get_arg(0) == args[num_args-1]) { result = arg0; TRACE("array_simplifier", tout << mk_pp(result.get(), m_manager) << "\n";); return; } // // store(a, i, select(a, i)) = a // if (is_select(argn) && (to_app(argn)->get_num_args() == num_args - 1) && same_args(num_args-1, args, to_app(argn)->get_args())) { TRACE("dummy_store", tout << "dummy store simplified mk_store(\n"; for (unsigned i = 0; i < num_args; i++) ast_ll_pp(tout, m_manager, args[i]); tout << ") =====>\n"; ast_ll_pp(tout, m_manager, arg0);); result = arg0; TRACE("array_simplifier", tout << mk_pp(result.get(), m_manager) << "\n";); return; } // // store(store(a,i,v),j,w) -> store(store(a,j,w),i,v) // if i, j are values, i->get_id() < j->get_id() // if (m_params.m_array_canonize_simplify && is_store(arg0) && all_values(num_args-2, args+1) && all_values(num_args-2, to_app(arg0)->get_args()+1) && lex_lt(num_args-2, args+1, to_app(arg0)->get_args()+1)) { expr* const* args2 = to_app(arg0)->get_args(); expr_ref_buffer new_args(m_manager); new_args.push_back(args2[0]); for (unsigned i = 1; i < num_args; ++i) { new_args.push_back(args[i]); } reduce(f, num_args, new_args.c_ptr(), result); new_args.reset(); new_args.push_back(result); for (unsigned i = 1; i < num_args; ++i) { new_args.push_back(args2[i]); } result = m_manager.mk_app(m_fid, OP_STORE, num_args, new_args.c_ptr()); TRACE("array_simplifier", tout << mk_pp(result.get(), m_manager) << "\n";); return; } result = m_manager.mk_app(m_fid, OP_STORE, num_args, args); TRACE("array_simplifier", tout << "default: " << mk_pp(result.get(), m_manager) << "\n";); } void array_simplifier_plugin::mk_select_as_array(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(is_as_array(args[0])); func_decl * f = get_as_array_func_decl(to_app(args[0])); result = m_manager.mk_app(f, num_args - 1, args+1); } void array_simplifier_plugin::mk_select_as_array_tree(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(is_as_array_tree(args[0])); SASSERT(m_manager.is_ite(args[0])); ptr_buffer todo; obj_map cache; app_ref_buffer trail(m_manager); todo.push_back(to_app(args[0])); while (!todo.empty()) { app * curr = todo.back(); SASSERT(m_manager.is_ite(curr)); expr * branches[2] = {0, 0}; bool visited = true; for (unsigned i = 0; i < 2; i++) { expr * arg = curr->get_arg(i+1); if (is_as_array(arg)) { branches[i] = m_manager.mk_app(get_as_array_func_decl(to_app(arg)), num_args - 1, args+1); } else { SASSERT(m_manager.is_ite(arg)); app * new_arg = 0; if (!cache.find(to_app(arg), new_arg)) { todo.push_back(to_app(arg)); visited = false; } else { branches[i] = new_arg; } } } if (visited) { todo.pop_back(); app * new_curr = m_manager.mk_ite(curr->get_arg(0), branches[0], branches[1]); trail.push_back(new_curr); cache.insert(curr, new_curr); } } SASSERT(cache.contains(to_app(args[0]))); app * r = 0; cache.find(to_app(args[0]), r); result = r; } void array_simplifier_plugin::mk_select(unsigned num_args, expr * const * args, expr_ref & result) { expr * r = 0; if (is_as_array(args[0])) { mk_select_as_array(num_args, args, result); return; } if (is_as_array_tree(args[0])) { mk_select_as_array_tree(num_args, args, result); return; } bool is_const_select = num_args == 2 && m_manager.is_unique_value(args[1]); app* const_index = is_const_select?to_app(args[1]):0; unsigned num_const_stores = 0; expr_ref tmp(m_manager); expr* args2[2]; if (is_const_select) { switch(mk_select_const(args[0], const_index, tmp)) { case NOT_CACHED: break; case FOUND_VALUE: TRACE("mk_select", tout << "found value\n"; ast_ll_pp(tout, m_manager, tmp.get()); ); result = tmp.get(); // value of select is stored under result. return; case FOUND_DEFAULT: args2[0] = tmp.get(); args2[1] = args[1]; args = args2; is_const_select = false; break; } } SASSERT(num_args > 0); ptr_vector & entry = m_tmp2; entry.reset(); entry.append(num_args, args); expr * entry0 = entry[0]; SASSERT(m_todo.empty()); m_todo.push_back(entry0); while (!m_todo.empty()) { expr * m = m_todo.back(); TRACE("array_simplifier", tout << mk_bounded_pp(m, m_manager) << "\n";); if (is_store(m)) { expr * nested_array = to_app(m)->get_arg(0); expr * else_branch = 0; entry[0] = nested_array; if (is_const_select) { if (m_manager.is_unique_value(to_app(m)->get_arg(1))) { app* const_index2 = to_app(to_app(m)->get_arg(1)); // // we found the value, all other stores are different. // there is no need to recurse. // if (const_index == const_index2) { result = to_app(m)->get_arg(2); cache_store(num_const_stores, args[0]); m_todo.reset(); return; } ++num_const_stores; } else { is_const_select = false; } } if (m_select_cache.find(&entry, else_branch)) { expr_ref_buffer eqs(m_manager); for (unsigned i = 1; i < num_args ; ++i) { expr * a = args[i]; expr * b = to_app(m)->get_arg(i); expr_ref eq(m_manager); m_simp.mk_eq(a, b, eq); eqs.push_back(eq.get()); } expr_ref cond(m_manager); m_simp.mk_and(eqs.size(), eqs.c_ptr(), cond); expr * then_branch = to_app(m)->get_arg(num_args); if (m_manager.is_true(cond.get())) { result = then_branch; } else if (m_manager.is_false(cond.get())) { result = else_branch; } else { m_simp.mk_ite(cond.get(), then_branch, else_branch, result); } entry[0] = m; cache_select(entry.size(), entry.c_ptr(), result.get()); m_todo.pop_back(); } else { m_todo.push_back(nested_array); } } else if (is_const_array(m)) { entry[0] = m; cache_select(entry.size(), entry.c_ptr(), to_app(m)->get_arg(0)); m_todo.pop_back(); } else { entry[0] = m; TRACE("array_simplifier", { for (unsigned i = 0; i < entry.size(); ++i) { tout << mk_bounded_pp(entry[i], m_manager) << ": " << mk_bounded_pp(m_manager.get_sort(entry[i]), m_manager) << "\n"; }} ); r = m_manager.mk_app(m_fid, OP_SELECT, 0, 0, entry.size(), entry.c_ptr()); cache_select(entry.size(), entry.c_ptr(), r); m_todo.pop_back(); } } cache_store(num_const_stores, args[0]); entry[0] = entry0; #ifdef Z3DEBUG bool f = #endif m_select_cache.find(&entry, r); SASSERT(f); result = r; prune_select_cache(); prune_store_cache(); TRACE("mk_select", for (unsigned i = 0; i < num_args; i++) { ast_ll_pp(tout, m_manager, args[i]); tout << "\n"; }; tout << "is_store: " << is_store(args[0]) << "\n"; ast_ll_pp(tout, m_manager, r);); } void array_simplifier_plugin::mk_map(func_decl* f, expr* a, expr* b, expr_ref& result) { expr* exprs[2] = { a, b }; parameter param(f); result = m_manager.mk_app(m_fid, OP_ARRAY_MAP, 1, ¶m, 2, exprs ); } void array_simplifier_plugin::mk_map(func_decl* f, expr* a, expr_ref& result) { parameter param(f); result = m_manager.mk_app(m_fid, OP_ARRAY_MAP, 1, ¶m, 1, &a ); } z3-4.4.0/src/ast/simplifier/datatype_simplifier_plugin.h0000644000175000017500000000155412540347414023160 0ustar michaelmichael/*++ Copyright (c) 2008 Microsoft Corporation Module Name: datatype_simplifier_plugin.h Abstract: Simplifier for algebraic datatypes. Author: nbjorner 2008-11-6 --*/ #ifndef _DATATYPE_SIMPLIFIER_PLUGIN_H_ #define _DATATYPE_SIMPLIFIER_PLUGIN_H_ #include"basic_simplifier_plugin.h" #include"datatype_decl_plugin.h" /** \brief Simplifier for the arith family. */ class datatype_simplifier_plugin : public simplifier_plugin { datatype_util m_util; basic_simplifier_plugin & m_bsimp; public: datatype_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b); ~datatype_simplifier_plugin(); virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result); }; #endif /* _DATATYPE_SIMPLIFIER_PLUGIN_H_ */ z3-4.4.0/src/ast/simplifier/array_simplifier_params.h0000644000175000017500000000126212540347414022444 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: array_simplifier_params.h Abstract: This file was created during code reorg. Author: Leonardo de Moura (leonardo) 2012-12-02. Revision History: --*/ #ifndef _ARRAY_SIMPLIFIER_PARAMS_H_ #define _ARRAY_SIMPLIFIER_PARAMS_H_ #include"params.h" struct array_simplifier_params { bool m_array_canonize_simplify; bool m_array_simplify; // temporary hack for disabling array simplifier plugin. array_simplifier_params(params_ref const & p = params_ref()) { updt_params(p); } void updt_params(params_ref const & _p); }; #endif /* _ARITH_SIMPLIFIER_PARAMS_H_ */ z3-4.4.0/src/ast/simplifier/maximise_ac_sharing.h0000644000175000017500000000644512540347414021542 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: maximise_ac_sharing.h Abstract: Author: Leonardo de Moura (leonardo) 2008-10-22. Revision History: --*/ #ifndef _MAXIMISE_AC_SHARING_H_ #define _MAXIMISE_AC_SHARING_H_ #include"simplifier.h" #include"hashtable.h" #include"bv_decl_plugin.h" #include"region.h" /** \brief Functor used to maximise the amount of shared terms in an expression. The idea is to rewrite AC terms to maximise sharing. Example: (f (bvadd a (bvadd b c)) (bvadd a (bvadd b d))) is rewritten to: (f (bvadd (bvadd a b) c) (bvadd (bvadd a b) d)) \warning This class uses an opportunistic heuristic to maximise sharing. There is no guarantee that the optimal expression will be produced. */ class maximise_ac_sharing { struct entry { func_decl * m_decl; expr * m_arg1; expr * m_arg2; entry(func_decl * d = 0, expr * arg1 = 0, expr * arg2 = 0):m_decl(d), m_arg1(arg1), m_arg2(arg2) { SASSERT((d == 0 && arg1 == 0 && arg2 == 0) || (d != 0 && arg1 != 0 && arg2 != 0)); if (arg1->get_id() > arg2->get_id()) std::swap(m_arg1, m_arg2); } unsigned hash() const { unsigned a = m_decl->get_id(); unsigned b = m_arg1->get_id(); unsigned c = m_arg2->get_id(); mix(a,b,c); return c; } bool operator==(entry const & e) const { return m_decl == e.m_decl && m_arg1 == e.m_arg1 && m_arg2 == e.m_arg2; } }; typedef ptr_hashtable, deref_eq > cache; protected: class ac_plugin : public simplifier_plugin { maximise_ac_sharing & m_owner; svector m_kinds; //!< kinds to be processed public: ac_plugin(symbol const & fname, ast_manager & m, maximise_ac_sharing & owner); void register_kind(decl_kind k); virtual ~ac_plugin(); virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); }; friend class ac_plugin; private: ast_manager & m_manager; simplifier m_simplifier; bool m_init; region m_region; cache m_cache; ptr_vector m_entries; unsigned_vector m_scopes; bool contains(func_decl * f, expr * arg1, expr * arg2); void insert(func_decl * f, expr * arg1, expr * arg2); void restore_entries(unsigned old_lim); void init() { if (!m_init) { init_core(); m_init = true; } } protected: virtual void init_core() = 0; virtual bool is_numeral(expr * n) const = 0; void register_plugin(ac_plugin * p) { m_simplifier.register_plugin(p); } public: maximise_ac_sharing(ast_manager & m); virtual ~maximise_ac_sharing(); void operator()(expr * s, expr_ref & r, proof_ref & p); void push_scope(); void pop_scope(unsigned num_scopes); void reset(); ast_manager & get_manager() { return m_manager; } }; class maximise_bv_sharing : public maximise_ac_sharing { bv_util m_util; protected: virtual void init_core(); virtual bool is_numeral(expr * n) const; public: maximise_bv_sharing(ast_manager & m); }; #endif /* _MAXIMISE_AC_SHARING_H_ */ z3-4.4.0/src/ast/simplifier/arith_simplifier_params.cpp0000644000175000017500000000076612540347414023000 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: arith_simplifier_params.cpp Abstract: Author: Leonardo de Moura (leonardo) 2012-12-02. Revision History: --*/ #include"arith_simplifier_params.h" #include"arith_simplifier_params_helper.hpp" void arith_simplifier_params::updt_params(params_ref const & _p) { arith_simplifier_params_helper p(_p); m_arith_expand_eqs = p.arith_expand_eqs(); m_arith_process_all_eqs = p.arith_process_all_eqs(); } z3-4.4.0/src/ast/simplifier/elim_bounds.h0000644000175000017500000000311212540347414020034 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: elim_bounds.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-28. Revision History: --*/ #ifndef _ELIM_BOUNDS_H_ #define _ELIM_BOUNDS_H_ #include"ast.h" #include"arith_decl_plugin.h" #include"simplifier.h" /** \brief Functor for eliminating irrelevant bounds in quantified formulas. Example: (forall (x Int) (y Int) (or (not (>= y x) (not (>= x 0)) (= (select a x) 1)))) The bound (>= y x) is irrelevant and can be eliminated. This can be easily proved by using Fourier-Motzkin elimination. Limitations & Assumptions: - It assumes the input formula was already simplified. - It can only handle bounds in the diff-logic fragment. \remark This operation is subsumed by Fourier-Motzkin elimination. */ class elim_bounds { ast_manager & m_manager; arith_util m_util; bool is_bound(expr * n, var * & lower, var * & upper); bool is_bound(expr * n); public: elim_bounds(ast_manager & m); void operator()(quantifier * q, expr_ref & r); }; /** \brief Functor for applying elim_bounds in all universal quantifiers in an expression. Assumption: the formula was already skolemized. */ class elim_bounds_star : public simplifier { protected: elim_bounds m_elim; virtual bool visit_quantifier(quantifier * q); virtual void reduce1_quantifier(quantifier * q); public: elim_bounds_star(ast_manager & m):simplifier(m), m_elim(m) { enable_ac_support(false); } virtual ~elim_bounds_star() {} }; #endif /* _ELIM_BOUNDS_H_ */ z3-4.4.0/src/ast/simplifier/push_app_ite.h0000644000175000017500000000311412540347414020216 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: push_app_ite.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-14. Revision History: --*/ #ifndef _PUSH_APP_ITE_H_ #define _PUSH_APP_ITE_H_ #include"ast.h" #include"simplifier.h" /** \brief Functor for applying the following transformation: (f s (ite c t1 t2)) ==> (ite c (f s t1) (f s t2)) */ class push_app_ite : public simplifier { protected: bool m_conservative; int has_ite_arg(unsigned num_args, expr * const * args); void apply(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & result); virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args); void reduce_core(expr * n); bool visit_children(expr * n); void reduce1(expr * n); void reduce1_app(app * n); void reduce1_quantifier(quantifier * q); public: push_app_ite(simplifier & s, bool conservative = true); virtual ~push_app_ite(); void operator()(expr * s, expr_ref & r, proof_ref & p); }; /** \brief Variation of push_app_ite that applies the transformation on nonground terms only. \remark This functor uses the app::is_ground method. This method is not completly precise, for instance, any term containing a quantifier is marked as non ground. */ class ng_push_app_ite : public push_app_ite { protected: virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args); public: ng_push_app_ite(simplifier & s, bool conservative = true); virtual ~ng_push_app_ite() {} }; #endif /* _PUSH_APP_ITE_H_ */ z3-4.4.0/src/ast/simplifier/array_simplifier_params_helper.pyg0000644000175000017500000000066512540347414024361 0ustar michaelmichaeldef_module_params(class_name='array_simplifier_params_helper', module_name="old_simplify", # Parameters will be in the old_simplify module export=True, params=( ('array.canonize', BOOL, False, 'normalize array terms into normal form during simplification'), ('array.simplify', BOOL, True, 'enable/disable array simplifications'))) z3-4.4.0/src/ast/simplifier/bv_elim.cpp0000644000175000017500000000730612540347414017515 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "bv_elim.h" #include "bv_decl_plugin.h" #include "var_subst.h" #include void bv_elim::elim(quantifier* q, quantifier_ref& r) { svector names, _names; sort_ref_buffer sorts(m_manager), _sorts(m_manager); expr_ref_buffer pats(m_manager); expr_ref_buffer no_pats(m_manager); expr_ref_buffer subst_map(m_manager), _subst_map(m_manager); var_subst subst(m_manager); bv_util bv(m_manager); expr_ref new_body(m_manager); expr* old_body = q->get_expr(); unsigned num_decls = q->get_num_decls(); family_id bfid = m_manager.mk_family_id("bv"); // // Traverse sequence of bound variables to eliminate // bit-vecctor variables and replace them by // Booleans. // unsigned var_idx = 0; for (unsigned i = num_decls; i > 0; ) { --i; sort* s = q->get_decl_sort(i); symbol nm = q->get_decl_name(i); if (bv.is_bv_sort(s)) { // convert n-bit bit-vector variable into sequence of n-Booleans. unsigned num_bits = bv.get_bv_size(s); expr_ref_buffer args(m_manager); expr_ref bv(m_manager); for (unsigned j = 0; j < num_bits; ++j) { std::ostringstream new_name; new_name << nm.str(); new_name << "_"; new_name << j; var* v = m_manager.mk_var(var_idx++, m_manager.mk_bool_sort()); args.push_back(v); _sorts.push_back(m_manager.mk_bool_sort()); _names.push_back(symbol(new_name.str().c_str())); } bv = m_manager.mk_app(bfid, OP_MKBV, 0, 0, args.size(), args.c_ptr()); _subst_map.push_back(bv.get()); } else { _subst_map.push_back(m_manager.mk_var(var_idx++, s)); _sorts.push_back(s); _names.push_back(nm); } } // // reverse the vectors. // SASSERT(_names.size() == _sorts.size()); for (unsigned i = _names.size(); i > 0; ) { --i; names.push_back(_names[i]); sorts.push_back(_sorts[i]); } for (unsigned i = _subst_map.size(); i > 0; ) { --i; subst_map.push_back(_subst_map[i]); } expr* const* sub = subst_map.c_ptr(); unsigned sub_size = subst_map.size(); subst(old_body, sub_size, sub, new_body); for (unsigned j = 0; j < q->get_num_patterns(); j++) { expr_ref pat(m_manager); subst(q->get_pattern(j), sub_size, sub, pat); pats.push_back(pat); } for (unsigned j = 0; j < q->get_num_no_patterns(); j++) { expr_ref nopat(m_manager); subst(q->get_no_pattern(j), sub_size, sub, nopat); no_pats.push_back(nopat); } r = m_manager.mk_quantifier(true, names.size(), sorts.c_ptr(), names.c_ptr(), new_body.get(), q->get_weight(), q->get_qid(), q->get_skid(), pats.size(), pats.c_ptr(), no_pats.size(), no_pats.c_ptr()); } bool bv_elim_star::visit_quantifier(quantifier* q) { // behave like destructive resolution, do not recurse. return true; } void bv_elim_star::reduce1_quantifier(quantifier* q) { quantifier_ref r(m); proof_ref pr(m); m_bv_elim.elim(q, r); if (m.fine_grain_proofs()) { pr = m.mk_rewrite(q, r.get()); } else { pr = 0; } cache_result(q, r, pr); } z3-4.4.0/src/ast/simplifier/simplifier.h0000644000175000017500000002453412540347414017712 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: simplifier.h Abstract: Generic expression simplifier with support for theory specific "plugins". Author: Leonardo (leonardo) 2008-01-03 Notes: --*/ #ifndef _SIMPLIFIER_H_ #define _SIMPLIFIER_H_ #include"base_simplifier.h" #include"simplifier_plugin.h" #include"plugin_manager.h" #include"ast_util.h" #include"obj_hashtable.h" /** \brief Local simplifier. Proof production can be enabled/disabled. The simplifier can also apply substitutions during the simplification. A substitution is a mapping from expression to expression+proof, where for each entry e_1->(e_2,p) p is a proof for (= e_1 e_2). The simplifier can also generate coarse grain proofs. In a coarse proof, local rewrite steps are omitted, and only the substitutions used are tracked. Example: Consider the expression (+ a b), and the substitution b->(0, p) When fine grain proofs are enabled, the simplifier will produce the following proof Assume the id of the proof object p is $0. Note that p is a proof for (= b 0). $1: [reflexivity] |- (= a a) $2: [congruence] $1 $0 |- (= (+ a b) (+ a 0)) $3: [plus-0] |- (= (+ a 0) a) $4: [transitivity] $2 $3 |- (= (+ a b) a) When coarse grain proofs are enabled, the simplifier produces the following proof: $1: [simplifier] $0 |- (= (+ a b) a) */ class simplifier : public base_simplifier { protected: typedef simplifier_plugin plugin; plugin_manager m_plugins; ptr_vector m_args; vector m_mults; ptr_vector m_args2; proof_ref_vector m_proofs; // auxiliary vector for implementing exhaustive simplification. proof_ref_vector m_subst_proofs; // in coarse grain proof generation mode, this vector tracks the justification for substitutions (see method get_subst). bool m_need_reset; bool m_use_oeq; bool m_visited_quantifier; //!< true, if the simplifier found a quantifier bool m_ac_support; expr_mark m_ac_mark; ptr_vector m_ac_marked; obj_map m_ac_cache; // temporary cache for ac obj_map m_ac_pr_cache; // temporary cache for ac obj_map m_colors; // temporary cache for topological sort. obj_map m_ac_mults; /* Simplifier uses an idiom for rewriting ASTs without using recursive calls. - It uses a cache (field m_cache in base_simplifier) and a todo-stack (field m_todo in base_simplifier). - The cache is a mapping from AST to (AST + Proof). An entry [n -> (n',pr)] is used to store the fact that n and n' are equivalent and pr is a proof for that. If proofs are disabled, then pr is 0. We say n' is the result of the simplification of n. Note: Some simplifications do not preserve equivalence, but equisatisfiability. For saving space, we use pr = 0 also to represent the reflexivity proof [n -> (n, 0)]. - The simplifier can be extended using plugin (subclasses of the class simplifier_plugin). Each theory has a family ID. All operators (func_decls) and sorts from a given theory have the same family_id. Given an application (object of the class app), we use the method get_family_id() to obtain the family id of the operator in this application. The simplifier uses plugin to apply theory specific simplifications. The basic idea is: whenever an AST with family_id X is found, invoke the plugin for this family_id. A simplifier_plugin implements the following API: 1) bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) This method is invoked when the simplifier is trying to reduce/simplify an application of the form (f args[0] ... args[num_args - 1]), and f has a family_id associated with the plugin. The plugin may return false to indicate it could not simplify this application. If it returns true (success), the result should be stored in the argument result. 2) bool reduce(func_decl * f, unsigned num_args, rational const * mults, expr * const * args, expr_ref & result); This method is a similar to the previous one, and it is used to handle associative operators. A plugin does not need to implement this method, the default implementation will use the previous one. The arguments mults indicates the multiplicity of every argument in args. For example, suppose this reduce is invoked with the arguments (f, 2, [3, 2], [a, b], result). This represents the application (f a a a b b). Some theory simplifiers may have efficient ways to encode this multiplicity. For example, the arithmetic solver, if f is "+", the multiplicity can be encoded using "*". This optimization is used because some benchmarks can create term that are very huge when flattened. One "real" example (that motivated this optimization) is: let a1 = x1 + x1 let a2 = a1 + a1 ... let an = a{n-1} + a{n-1} an In this example, n was 32, so after AC flattening, we had an application (+ x1 ... x1) with 2^32 arguments. Using the simple reduce would produce a stack overflow. This class uses a topological sort for computing the multiplicities efficiently. So, the field m_colors is used to implement the topological sort. 3) bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result) This method is invoked when the sort of lhs and rhs has a family_id associated with the plugin. This method allows theory specific simplifications such as: (= (+ a b) b) --> (= a 0) Assuming n1 is a reference to (+ a b) and n2 to b, the simplifier would invoke reduce_eq(n1, n2, result) Like reduce, false can be returned if a simplification could not be applied. And if true is returned, then the result is stored in the argument result. 4) bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result) It is similar to reduce_eq, but it used for theory specific simplifications for (distinct args[0] ... args[num_args-1]) Example: (distinct 0 1 ... n) --> true - The idiom used in this class is implemented in the methdo reduce_core. See reduce_core for more details. The basic idea is: 1) Get the next ast to be simplified from the todo-stack. 2) If it is already cached, then do nothing. That is, this expression was already simplified. 3) Otherwise, check whether all arguments already have been simplified (method visit_children). 3a) The arguments that have not been simplified are added to the todo-stack by visit_children. In this case visit_children will return false. 3b) If all arguments have already been simplified, then invoke reduce1 to perform a reduction/simplification step. The cache is updated with the result. - After invoking reduce_core(n), the cache contains an entry [n -> (n', pr)]. */ void flush_cache(); /** \brief This method can be redefined in subclasses of simplifier to implement substitutions. It returns true if n should be substituted by r, where the substitution is justified by the proof p. The field m_subst_proofs is used to store these justifications when coarse proofs are used (PGM_COARSE). This method is redefined in the class subst_simplifier. It is used in asserted_formulas for implementing constant elimination. For example, if asserted_formulas contains the atoms (= a (+ b 1)) (p a c), then the constant "a" can be eliminated. This is achieved by set (+ b 1) as a substitution for "a". */ virtual bool get_subst(expr * n, expr_ref & r, proof_ref & p); void reduce_core(expr * n); bool visit_children(expr * n); bool visit_ac(app * n); virtual bool visit_quantifier(quantifier * q); void reduce1(expr * n); void reduce1_app(app * n); void reduce1_app_core(app * n); void reduce1_ac_app_core(app * n); void mk_congruent_term(app * n, app_ref & r, proof_ref & p); void mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p); bool get_args(app * n, ptr_vector & result, proof_ref & p); void get_ac_args(app * n, ptr_vector & args, vector & mults); virtual void reduce1_quantifier(quantifier * q); void dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr * const * args, expr* result); void ac_top_sort(app * n, ptr_buffer & result); public: simplifier(ast_manager & manager); virtual ~simplifier(); void enable_ac_support(bool flag); /** \brief Simplify the expression \c s. Store the result in \c r, and a proof that (= s r) in \c p. */ void operator()(expr * s, expr_ref & r, proof_ref & p); void reset() { if (m_need_reset) { flush_cache(); m_need_reset = false; } } bool visited_quantifier() const { return m_visited_quantifier; } void mk_app(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & r); void cache_result(expr * n, expr * r, proof * p) { m_need_reset = true; base_simplifier::cache_result(n, r, p); } void register_plugin(plugin * p); ptr_vector::const_iterator begin_plugins() const { return m_plugins.begin(); } ptr_vector::const_iterator end_plugins() const { return m_plugins.end(); } plugin * get_plugin(family_id fid) const { return m_plugins.get_plugin(fid); } ast_manager & get_manager() { return m; } void borrow_plugins(simplifier const & s); void release_plugins(); void enable_presimp(); }; class subst_simplifier : public simplifier { protected: expr_map * m_subst_map; virtual bool get_subst(expr * n, expr_ref & r, proof_ref & p); public: subst_simplifier(ast_manager & manager):simplifier(manager), m_subst_map(0) {} void set_subst_map(expr_map * s); }; void push_assertion(ast_manager & m, expr * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs); #endif z3-4.4.0/src/ast/simplifier/fpa_simplifier_plugin.h0000644000175000017500000000142612540347414022111 0ustar michaelmichael/*++ Copyright (c) 2015 Microsoft Corporation Module Name: fpa_simplifier_plugin.h Abstract: Simplifier for the floating-point theory Author: Christoph (cwinter) 2015-01-14 --*/ #ifndef _FPA_SIMPLIFIER_PLUGIN_H_ #define _FPA_SIMPLIFIER_PLUGIN_H_ #include"basic_simplifier_plugin.h" #include"fpa_decl_plugin.h" #include"fpa_rewriter.h" class fpa_simplifier_plugin : public simplifier_plugin { fpa_util m_util; fpa_rewriter m_rw; public: fpa_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b); ~fpa_simplifier_plugin(); virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result); }; #endif /* _FPA_SIMPLIFIER_PLUGIN_H_ */ z3-4.4.0/src/ast/simplifier/fpa_simplifier_plugin.cpp0000644000175000017500000000155212540347414022444 0ustar michaelmichael/*++ Copyright (c) 2015 Microsoft Corporation Module Name: fpa_simplifier_plugin.cpp Abstract: Simplifier for the floating-point theory Author: Christoph (cwinter) 2015-01-14 --*/ #include"fpa_simplifier_plugin.h" fpa_simplifier_plugin::fpa_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b) : simplifier_plugin(symbol("fpa"), m), m_util(m), m_rw(m) {} fpa_simplifier_plugin::~fpa_simplifier_plugin() {} bool fpa_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { set_reduce_invoked(); SASSERT(f->get_family_id() == get_family_id()); return m_rw.mk_app_core(f, num_args, args, result) == BR_DONE; } bool fpa_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) { set_reduce_invoked(); return m_rw.mk_eq_core(lhs, rhs, result) == BR_DONE; } z3-4.4.0/src/ast/simplifier/inj_axiom.cpp0000644000175000017500000001310512540347414020047 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: inj_axiom.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-23. Revision History: --*/ #include"inj_axiom.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"has_free_vars.h" #include"well_sorted.h" /** \brief Little HACK for simplifying injectivity axioms \remark It is not covering all possible cases. */ bool simplify_inj_axiom(ast_manager & m, quantifier * q, expr_ref & result) { expr * n = q->get_expr(); if (q->is_forall() && m.is_or(n) && to_app(n)->get_num_args() == 2) { expr * arg1 = to_app(n)->get_arg(0); expr * arg2 = to_app(n)->get_arg(1); if (m.is_not(arg2)) std::swap(arg1, arg2); if (m.is_not(arg1) && m.is_eq(to_app(arg1)->get_arg(0)) && m.is_eq(arg2)) { expr * app1 = to_app(to_app(arg1)->get_arg(0))->get_arg(0); expr * app2 = to_app(to_app(arg1)->get_arg(0))->get_arg(1); expr * var1 = to_app(arg2)->get_arg(0); expr * var2 = to_app(arg2)->get_arg(1); if (is_app(app1) && is_app(app2) && to_app(app1)->get_decl() == to_app(app2)->get_decl() && to_app(app1)->get_num_args() == to_app(app2)->get_num_args() && to_app(app1)->get_family_id() == null_family_id && to_app(app1)->get_num_args() > 0 && is_var(var1) && is_var(var2) && var1 != var2) { app * f1 = to_app(app1); app * f2 = to_app(app2); bool found_vars = false; unsigned num = f1->get_num_args(); unsigned idx = UINT_MAX; unsigned num_vars = 1; for (unsigned i = 0; i < num; i++) { expr * c1 = f1->get_arg(i); expr * c2 = f2->get_arg(i); if (!is_var(c1) && !is_uninterp_const(c1)) return false; if ((c1 == var1 && c2 == var2) || (c1 == var2 && c2 == var1)) { if (found_vars) return false; found_vars = true; idx = i; } else if (c1 == c2 && c1 != var1 && c1 != var2) { if (is_var(c1)) { ++num_vars; } } else { return false; } } if (found_vars && !has_free_vars(q)) { TRACE("inj_axiom", tout << "Cadidate for simplification:\n" << mk_ll_pp(q, m) << mk_pp(app1, m) << "\n" << mk_pp(app2, m) << "\n" << mk_pp(var1, m) << "\n" << mk_pp(var2, m) << "\nnum_vars: " << num_vars << "\n";); // Building new (optimized) axiom func_decl * decl = f1->get_decl(); unsigned var_idx = 0; ptr_buffer f_args, inv_vars; ptr_buffer decls; buffer names; expr * var = 0; for (unsigned i = 0; i < num; i++) { expr * c = f1->get_arg(i); if (is_var(c)) { names.push_back(symbol(i)); sort * s = decl->get_domain(i); decls.push_back(s); expr * new_c = m.mk_var(var_idx, s); var_idx++; f_args.push_back(new_c); if (i == idx) { var = new_c; } else { inv_vars.push_back(new_c); } } else { SASSERT(is_uninterp_const(c)); f_args.push_back(c); } } SASSERT(var != 0); app * f = m.mk_app(decl, f_args.size(), f_args.c_ptr()); ptr_vector domain; inv_vars.push_back(f); for (unsigned i = 0; i < inv_vars.size(); ++i) { domain.push_back(m.get_sort(inv_vars[i])); } sort * d = decl->get_domain(idx); func_decl * inv_decl = m.mk_fresh_func_decl("inj", domain.size(), domain.c_ptr(), d); expr * proj = m.mk_app(inv_decl, inv_vars.size(), inv_vars.c_ptr()); expr * eq = m.mk_eq(proj, var); expr * p = m.mk_pattern(f); // decls are in the wrong order... // Remark: the sort of the var 0 must be in the last position. std::reverse(decls.begin(), decls.end()); result = m.mk_forall(decls.size(), decls.c_ptr(), names.c_ptr(), eq, 0, symbol(), symbol(), 1, &p); TRACE("inj_axiom", tout << "new axiom:\n" << mk_pp(result, m) << "\n";); SASSERT(is_well_sorted(m, result)); return true; } } } } return false; } z3-4.4.0/src/ast/reg_decl_plugins.h0000644000175000017500000000060012540347414016675 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: reg_decl_plugins Abstract: Goodie for installing all available declarations plugins in an ast_manager Author: Leonardo de Moura (leonardo) 2012-10-24. Revision History: --*/ #ifndef _REG_DECL_PLUGINS_H_ #define _REG_DECL_PLUGINS_H_ class ast_manager; void reg_decl_plugins(ast_manager & m); #endif z3-4.4.0/src/ast/ast_smt_pp.h0000644000175000017500000000615112540347414015550 0ustar michaelmichael/*++ Copyright (c) 2008 Microsoft Corporation Module Name: ast_smt_pp.h Abstract: Pretty printer of AST formulas as SMT benchmarks. Author: Nikolaj Bjorner 2008-04-09. Revision History: --*/ #ifndef _AST_SMT_PP_H_ #define _AST_SMT_PP_H_ #include"ast.h" #include #include"map.h" class smt_renaming { typedef map symbol2symbol; symbol2symbol m_translate; symbol2symbol m_rev_translate; symbol fix_symbol(symbol s, int k); bool is_legal(char c); bool is_special(char const* s); bool is_numerical(char const* s); bool all_is_legal(char const* s); public: smt_renaming(); symbol get_symbol(symbol s0); symbol operator()(symbol const & s) { return get_symbol(s); } }; class ast_smt_pp { public: class is_declared { public: virtual bool operator()(func_decl* d) const { return false; } virtual bool operator()(sort* s) const { return false; } }; private: ast_manager& m_manager; expr_ref_vector m_assumptions; expr_ref_vector m_assumptions_star; symbol m_benchmark_name; symbol m_source_info; symbol m_status; symbol m_category; symbol m_logic; std::string m_attributes; family_id m_dt_fid; is_declared m_is_declared_default; is_declared* m_is_declared; bool m_simplify_implies; public: ast_smt_pp(ast_manager& m); void set_benchmark_name(const char* bn) { if (bn) m_benchmark_name = bn; } void set_source_info(const char* si) { if (si) m_source_info = si; } void set_status(const char* s) { if (s) m_status = s; } void set_category(const char* c) { if (c) m_category = c; } void set_logic(const char* l) { if (l) m_logic = l; } void add_attributes(const char* s) { if (s) m_attributes += s; } void add_assumption(expr* n) { m_assumptions.push_back(n); } void add_assumption_star(expr* n) { m_assumptions_star.push_back(n); } void set_simplify_implies(bool f) { m_simplify_implies = f; } void set_is_declared(is_declared* id) { m_is_declared = id; } void display(std::ostream& strm, expr* n); void display_smt2(std::ostream& strm, expr* n); void display_expr(std::ostream& strm, expr* n); void display_expr_smt2(std::ostream& strm, expr* n, unsigned indent = 0, unsigned num_var_names = 0, char const* const* var_names = 0); void display_ast_smt2(std::ostream& strm, ast* n, unsigned indent = 0, unsigned num_var_names = 0, char const* const* var_names = 0); }; struct mk_smt_pp { ast* m_ast; ast_manager& m_manager; unsigned m_indent; unsigned m_num_var_names; char const* const* m_var_names; mk_smt_pp(ast* e, ast_manager & m, unsigned indent = 0, unsigned num_var_names = 0, char const* const* var_names = 0) : m_ast(e), m_manager(m), m_indent(indent), m_num_var_names(num_var_names), m_var_names(var_names) {} }; inline std::ostream& operator<<(std::ostream& out, const mk_smt_pp & p) { ast_smt_pp pp(p.m_manager); pp.display_ast_smt2(out, p.m_ast, p.m_indent, p.m_num_var_names, p.m_var_names); return out; } #endif z3-4.4.0/src/ast/num_occurs.h0000644000175000017500000000211012540347414015543 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: num_occurs.h Abstract: Author: Leonardo de Moura (leonardo) 2008-01-27. Revision History: --*/ #ifndef _NUM_OCCURS_H_ #define _NUM_OCCURS_H_ #include"ast.h" #include"obj_hashtable.h" /** \brief Functor for computing the number of occurrences of each sub-expression in a expression F. */ class num_occurs { protected: bool m_ignore_ref_count1; bool m_ignore_quantifiers; obj_map m_num_occurs; void process(expr * t, expr_fast_mark1 & visited); public: num_occurs(bool ignore_ref_count1 = false, bool ignore_quantifiers = false): m_ignore_ref_count1(ignore_ref_count1), m_ignore_quantifiers(ignore_quantifiers) { } void reset() { m_num_occurs.reset(); } void operator()(expr * t); void operator()(unsigned num, expr * const * ts); unsigned get_num_occs(expr * n) const { unsigned val; if (m_num_occurs.find(n, val)) return val; return 0; } }; #endif /* _NUM_OCCURS_H_ */ z3-4.4.0/src/ast/datatype_decl_plugin.h0000644000175000017500000002073512540347414017563 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: datatype_decl_plugin.h Abstract: Author: Leonardo de Moura (leonardo) 2008-01-09. Revision History: --*/ #ifndef _DATATYPE_DECL_PLUGIN_H_ #define _DATATYPE_DECL_PLUGIN_H_ #include"ast.h" #include"tptr.h" #include"buffer.h" #include"obj_hashtable.h" enum datatype_sort_kind { DATATYPE_SORT }; enum datatype_op_kind { OP_DT_CONSTRUCTOR, OP_DT_RECOGNISER, OP_DT_ACCESSOR, OP_DT_UPDATE_FIELD, LAST_DT_OP }; /** \brief Auxiliary class used to declare inductive datatypes. It may be a sort or an integer. If it is an integer, then it represents a reference to a recursive type. For example, consider the datatypes Datatype Tree = tree(value:Real, children:TreeList) TreeList = cons_t(first_t:Tree, rest_t:Tree) | nil_t End The recursive occurrences of Tree and TreeList will have idx 0 and 1 respectively. This is a transient value, it is only used to declare a set of recursive datatypes. */ class type_ref { void * m_data; public: type_ref():m_data(TAG(void *, static_cast(0), 1)) {} type_ref(int idx):m_data(BOXINT(void *, idx)) {} type_ref(sort * s):m_data(TAG(void *, s, 1)) {} bool is_idx() const { return GET_TAG(m_data) == 0; } bool is_sort() const { return GET_TAG(m_data) == 1; } sort * get_sort() const { return UNTAG(sort *, m_data); } int get_idx() const { return UNBOXINT(m_data); } }; class accessor_decl; class constructor_decl; class datatype_decl; class datatype_util; accessor_decl * mk_accessor_decl(symbol const & n, type_ref const & t); void del_accessor_decl(accessor_decl * d); void del_accessor_decls(unsigned num, accessor_decl * const * as); // Remark: the constructor becomes the owner of the accessor_decls constructor_decl * mk_constructor_decl(symbol const & n, symbol const & r, unsigned num_accessors, accessor_decl * const * acs); void del_constructor_decl(constructor_decl * d); void del_constructor_decls(unsigned num, constructor_decl * const * cs); // Remark: the datatype becomes the owner of the constructor_decls datatype_decl * mk_datatype_decl(symbol const & n, unsigned num_constructors, constructor_decl * const * cs); void del_datatype_decl(datatype_decl * d); void del_datatype_decls(unsigned num, datatype_decl * const * ds); class datatype_decl_plugin : public decl_plugin { mutable scoped_ptr m_util; datatype_util & get_util() const; public: datatype_decl_plugin() {} virtual ~datatype_decl_plugin(); virtual void finalize(); virtual decl_plugin * mk_fresh() { return alloc(datatype_decl_plugin); } /** Contract for sort: parameters[0] - (int) n - number of recursive types. parameters[1] - (int) i - index 0..n-1 of which type is defined. for j in 0..n-1 parameters[2 + 2*j] - (symbol) name of the type parameters[2 + 2*j + 1] - (int) o - offset where the constructors are defined. for each offset o at parameters[2 + 2*j + 1] for some j in 0..n-1 parameters[o] - (int) m - number of constructors parameters[o+1] - (int) k_1 - offset for constructor definition ... parameters[o+m] - (int) k_m - offset ofr constructor definition for each offset k_i at parameters[o+s] for some s in 0..m-1 parameters[k_i] - (symbol) name of the constructor parameters[k_i+1] - (symbol) name of the recognizer parameters[k_i+2] - (int) m' - number of accessors parameters[k_i+3+2*r] - (symbol) name of the r accessor parameters[k_i+3+2*r+1] - (int or type_ast) type of the accessor. If integer, then the value must be in [0..n-1], and it represents an reference to the recursive type. The idea with the additional offsets is that access to relevant constructors and types can be performed using a few address calculations. */ virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); /** Contract for constructors parameters[0] - (ast) datatype ast. parmaeters[1] - (int) constructor idx. Contract for accessors parameters[0] - (ast) datatype ast. parameters[1] - (int) constructor idx. parameters[2] - (int) accessor idx. Contract for tester parameters[0] - (ast) datatype ast. parameters[1] - (int) constructor idx. */ virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); bool mk_datatypes(unsigned num_datatypes, datatype_decl * const * datatypes, sort_ref_vector & new_sorts); virtual expr * get_some_value(sort * s); virtual bool is_fully_interp(sort const * s) const; virtual bool is_value(app* e) const; virtual bool is_unique_value(app * e) const { return is_value(e); } virtual void get_op_names(svector & op_names, symbol const & logic); private: bool is_value_visit(expr * arg, ptr_buffer & todo) const; func_decl * mk_update_field( unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); }; class datatype_util { ast_manager & m_manager; family_id m_family_id; func_decl * get_constructor(sort * ty, unsigned c_id) const; obj_map *> m_datatype2constructors; obj_map m_datatype2nonrec_constructor; obj_map *> m_constructor2accessors; obj_map m_constructor2recognizer; obj_map m_recognizer2constructor; obj_map m_accessor2constructor; obj_map m_is_recursive; ast_ref_vector m_asts; ptr_vector > m_vectors; func_decl * get_non_rec_constructor_core(sort * ty, ptr_vector & forbidden_set); func_decl * get_constructor(sort * ty, unsigned c_id); public: datatype_util(ast_manager & m); ~datatype_util(); ast_manager & get_manager() const { return m_manager; } bool is_datatype(sort * s) const { return is_sort_of(s, m_family_id, DATATYPE_SORT); } bool is_recursive(sort * ty); bool is_constructor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_CONSTRUCTOR); } bool is_recognizer(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_RECOGNISER); } bool is_accessor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_ACCESSOR); } bool is_update_field(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_UPDATE_FIELD); } bool is_constructor(app * f) const { return is_app_of(f, m_family_id, OP_DT_CONSTRUCTOR); } bool is_recognizer(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER); } bool is_accessor(app * f) const { return is_app_of(f, m_family_id, OP_DT_ACCESSOR); } bool is_update_field(app * f) const { return is_app_of(f, m_family_id, OP_DT_UPDATE_FIELD); } ptr_vector const * get_datatype_constructors(sort * ty); unsigned get_datatype_num_constructors(sort * ty) { SASSERT(is_datatype(ty)); unsigned tid = ty->get_parameter(1).get_int(); unsigned o = ty->get_parameter(3 + 2 * tid).get_int(); return ty->get_parameter(o).get_int(); } unsigned get_constructor_idx(func_decl * f) const { SASSERT(is_constructor(f)); return f->get_parameter(1).get_int(); } unsigned get_recognizer_constructor_idx(func_decl * f) const { SASSERT(is_recognizer(f)); return f->get_parameter(1).get_int(); } func_decl * get_non_rec_constructor(sort * ty); func_decl * get_constructor_recognizer(func_decl * constructor); ptr_vector const * get_constructor_accessors(func_decl * constructor); func_decl * get_accessor_constructor(func_decl * accessor); func_decl * get_recognizer_constructor(func_decl * recognizer); family_id get_family_id() const { return m_family_id; } bool are_siblings(sort * s1, sort * s2); void reset(); void display_datatype(sort *s, std::ostream& strm); }; #endif /* _DATATYPE_DECL_PLUGIN_H_ */ z3-4.4.0/src/ast/dl_decl_plugin.cpp0000644000175000017500000007304512540347414016704 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_decl_plugin.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2010-04-10 Revision History: --*/ #include #include "ast_pp.h" #include "array_decl_plugin.h" #include "datatype_decl_plugin.h" #include "dl_decl_plugin.h" #include "warning.h" #include "reg_decl_plugins.h" namespace datalog { dl_decl_plugin::dl_decl_plugin() : m_store_sym("store"), m_empty_sym("empty"), m_is_empty_sym("is_empty"), m_join_sym("join"), m_union_sym("union"), m_widen_sym("widen"), m_project_sym("project"), m_filter_sym("filter"), m_negation_filter_sym("negation_filter"), m_rename_sym("rename"), m_complement_sym("complement"), m_select_sym("select"), m_clone_sym("clone"), m_num_sym("N"), m_lt_sym("<"), m_le_sym("<="), m_rule_sym("R"), m_min_sym("min") { } bool dl_decl_plugin::check_bounds(char const* msg, unsigned low, unsigned up, unsigned val) const { if (low <= val && val <= up) { return true; } std::ostringstream buffer; buffer << msg << ", value is not within bound " << low << " <= " << val << " <= " << up; m_manager->raise_exception(buffer.str().c_str()); return false; } bool dl_decl_plugin::check_domain(unsigned low, unsigned up, unsigned val) const { return check_bounds("unexpected number of arguments", low, up, val); } bool dl_decl_plugin::check_params(unsigned low, unsigned up, unsigned val) const { return check_bounds("unexpected number of parameters", low, up, val); } sort * dl_decl_plugin::mk_relation_sort( unsigned num_parameters, parameter const * parameters) { bool is_finite = true; rational r(1); for (unsigned i = 0; is_finite && i < num_parameters; ++i) { if (!parameters[i].is_ast() || !is_sort(parameters[i].get_ast())) { m_manager->raise_exception("expecting sort parameters"); return 0; } sort* s = to_sort(parameters[i].get_ast()); sort_size sz1 = s->get_num_elements(); if (sz1.is_finite()) { r *= rational(sz1.size(),rational::ui64()); } else { is_finite = false; } } sort_size sz; if (is_finite && r.is_uint64()) { sz = sort_size::mk_finite(r.get_uint64()); } else { sz = sort_size::mk_very_big(); } sort_info info(m_family_id, DL_RELATION_SORT, sz, num_parameters, parameters); return m_manager->mk_sort(symbol("Table"),info); } sort * dl_decl_plugin::mk_finite_sort(unsigned num_params, parameter const* params) { if (num_params != 2) { m_manager->raise_exception("expecting two parameters"); return 0; } if (!params[0].is_symbol()) { m_manager->raise_exception("expecting symbol"); return 0; } if (!params[1].is_rational() || !params[1].get_rational().is_uint64()) { m_manager->raise_exception("expecting rational"); return 0; } sort_size sz = sort_size::mk_finite(params[1].get_rational().get_uint64()); sort_info info(m_family_id, DL_FINITE_SORT, sz, num_params, params); return m_manager->mk_sort(params[0].get_symbol(),info); } sort* dl_decl_plugin::mk_rule_sort() { sort_size sz(sort_size::mk_infinite()); sort_info info(m_family_id, DL_RULE_SORT, sz, 0, 0); return m_manager->mk_sort(m_rule_sym, info); } sort * dl_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { switch(k) { case DL_RELATION_SORT: return mk_relation_sort(num_parameters, parameters); case DL_FINITE_SORT: return mk_finite_sort(num_parameters, parameters); case DL_RULE_SORT: return mk_rule_sort(); default: UNREACHABLE(); } return 0; } bool dl_decl_plugin::is_rel_sort(sort* r) { ptr_vector sorts; return is_rel_sort(r, sorts); } bool dl_decl_plugin::is_rel_sort(sort* r, ptr_vector& sorts) { if (!is_sort_of(r, m_family_id, DL_RELATION_SORT)) { m_manager->raise_exception("expected relation sort"); return false; } unsigned n = r->get_num_parameters(); for (unsigned i = 0; i < n; ++i) { parameter const& p = r->get_parameter(i); if (!p.is_ast() || !is_sort(p.get_ast())) { m_manager->raise_exception("exptected sort parameter"); return false; } sorts.push_back(to_sort(p.get_ast())); } return true; } bool dl_decl_plugin::is_fin_sort(sort* r) { if (!is_sort_of(r, m_family_id, DL_FINITE_SORT)) { m_manager->raise_exception("expected finite sort"); return false; } return true; } func_decl* dl_decl_plugin::mk_store_select(decl_kind k, unsigned arity, sort* const* domain) { bool is_store = (k == OP_RA_STORE); ast_manager& m = *m_manager; symbol sym = is_store?m_store_sym:m_select_sym; sort * r = domain[0]; if (!is_store) { r = m.mk_bool_sort(); } ptr_vector sorts; if (!is_rel_sort(r, sorts)) { return 0; } if (sorts.size() + 1 != arity) { m_manager->raise_exception("wrong arity supplied to relational access"); return 0; } for (unsigned i = 0; i < sorts.size(); ++i) { if (sorts[i] != domain[i+1]) { IF_VERBOSE(0, verbose_stream() << "Domain: " << mk_pp(domain[0], m) << "\n" << mk_pp(sorts[i], m) << "\n" << mk_pp(domain[i+1], m) << "\n";); m_manager->raise_exception("sort miss-match for relational access"); return 0; } } func_decl_info info(m_family_id, k, 0, 0); return m.mk_func_decl(sym, arity, domain, r, info); } func_decl * dl_decl_plugin::mk_empty(parameter const& p) { ast_manager& m = *m_manager; if (!p.is_ast() || !is_sort(p.get_ast())) { m_manager->raise_exception("expected sort parameter"); return 0; } sort* r = to_sort(p.get_ast()); if (!is_rel_sort(r)) { return 0; } func_decl_info info(m_family_id, OP_RA_EMPTY, 1, &p); return m.mk_func_decl(m_empty_sym, 0, (sort*const*)0, r, info); } func_decl* dl_decl_plugin::mk_project(unsigned num_params, parameter const* params, sort* r) { ast_manager& m = *m_manager; ptr_vector sorts; vector ps; TRACE("dl_decl_plugin", tout << mk_pp(r, m) << " "; for (unsigned i = 0; i < num_params; ++i) { tout << params[i] << " "; } tout << "\n"; ); if (!is_rel_sort(r, sorts)) { return 0; } SASSERT(sorts.size() >= num_params); // populate ps unsigned j = 0, i = 0; for (; i < num_params; ++i) { if (!params[i].is_int()) { m_manager->raise_exception("expecting integer parameter"); return 0; } unsigned k = params[i].get_int(); if (j > k) { m_manager->raise_exception("arguments to projection should be increasing"); return 0; } while (j < k) { ps.push_back(parameter(sorts[j])); ++j; } ++j; } for (; j < sorts.size(); ++j) { ps.push_back(parameter(sorts[j])); } SASSERT(ps.size() + num_params == sorts.size()); sort* r2 = m.mk_sort(m_family_id, DL_RELATION_SORT, ps.size(), ps.c_ptr()); func_decl_info info(m_family_id, OP_RA_PROJECT, num_params, params); return m.mk_func_decl(m_project_sym, 1, &r, r2, info); } func_decl * dl_decl_plugin::mk_unionw(decl_kind k, sort* s1, sort* s2) { ast_manager& m = *m_manager; if (s1 != s2) { m_manager->raise_exception("sort miss-match for arguments to union"); return 0; } if (!is_rel_sort(s1)) { return 0; } sort* domain[2] = { s1, s2 }; func_decl_info info(m_family_id, k, 0, 0); return m.mk_func_decl(m_union_sym, 2, domain, s1, info); } func_decl * dl_decl_plugin::mk_filter(parameter const& p, sort* r) { ast_manager& m = *m_manager; ptr_vector sorts; if (!is_rel_sort(r, sorts)) { return 0; } if (!p.is_ast() || !is_expr(p.get_ast())) { m_manager->raise_exception("ast expression expected to filter"); } expr* f = to_expr(p.get_ast()); // 1. f is of Boolean type. // 2. the free variables in f correspond to column types of r. if (!m.is_bool(f)) { m_manager->raise_exception("filter predicate should be of Boolean type"); return 0; } ptr_vector todo; todo.push_back(f); ast_mark mark; while (!todo.empty()) { expr* e = todo.back(); todo.pop_back(); if (mark.is_marked(e)) { continue; } mark.mark(e, true); unsigned idx; switch(e->get_kind()) { case AST_VAR: idx = to_var(e)->get_idx(); if (idx >= sorts.size()) { m_manager->raise_exception("illegal index"); return 0; } if (sorts[idx] != m.get_sort(e)) { m_manager->raise_exception("sort miss-match in filter"); return 0; } break; case AST_APP: for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { todo.push_back(to_app(e)->get_arg(i)); } break; case AST_QUANTIFIER: m_manager->raise_exception("quantifiers are not allowed in filter expressions"); return 0; default: m_manager->raise_exception("unexpected filter expression kind"); return 0; } } func_decl_info info(m_family_id, OP_RA_FILTER, 1, &p); return m.mk_func_decl(m_filter_sym, 1, &r, r, info); } func_decl * dl_decl_plugin::mk_rename(unsigned num_params, parameter const* params, sort* r) { ptr_vector sorts; if (!is_rel_sort(r, sorts)) { return 0; } unsigned index0; sort* last_sort = 0; SASSERT(num_params > 0); for (unsigned i = 0; i < num_params; ++i) { parameter const& p = params[i]; if (!p.is_int()) { m_manager->raise_exception("expected integer parameter"); return 0; } unsigned j = p.get_int(); if (j >= sorts.size()) { // We should not use ast_pp anymore on error messages. // m_manager->raise_exception("index %d out of bound %s : %d", j, ast_pp(r, *m_manager).c_str(), sorts.size()); m_manager->raise_exception("index out of bound"); return 0; } if (i == 0) { index0 = j; last_sort = sorts[j]; } else { std::swap(last_sort, sorts[j]); } } sorts[index0] = last_sort; vector params2; for (unsigned i = 0; i < sorts.size(); ++i) { params2.push_back(parameter(sorts[i])); } sort* rng = m_manager->mk_sort(m_family_id, DL_RELATION_SORT, params2.size(), params2.c_ptr()); func_decl_info info(m_family_id, OP_RA_RENAME, num_params, params); return m_manager->mk_func_decl(m_rename_sym, 1, &r, rng, info); } func_decl * dl_decl_plugin::mk_join(unsigned num_params, parameter const* params, sort* r1, sort* r2) { vector params2; ptr_vector sorts1, sorts2; if (!is_rel_sort(r1, sorts1)) { return 0; } if (!is_rel_sort(r2, sorts2)) { return 0; } for (unsigned i = 0; i < sorts1.size(); ++i) { params2.push_back(parameter(sorts1[i])); } for (unsigned i = 0; i < sorts2.size(); ++i) { params2.push_back(parameter(sorts2[i])); } if (0 != num_params % 2) { m_manager->raise_exception("expecting an even number of parameters to join"); return 0; } for (unsigned i = 0; i + 1 < num_params; i += 2) { parameter const& p1 = params[i]; parameter const& p2 = params[i+1]; if (!p1.is_int() || !p2.is_int()) { m_manager->raise_exception("encountered non-integer parameter"); return 0; } unsigned i1 = p1.get_int(); unsigned i2 = p2.get_int(); if (i1 >= sorts1.size() || i2 >= sorts2.size()) { m_manager->raise_exception("index out of bounds"); return 0; } if (sorts1[i1] != sorts2[i2]) { m_manager->raise_exception("sort miss-match in join"); return 0; } } sort* args[2] = { r1, r2 }; sort* rng = m_manager->mk_sort(m_family_id, DL_RELATION_SORT, params2.size(), params2.c_ptr()); func_decl_info info(m_family_id, OP_RA_JOIN, num_params, params); return m_manager->mk_func_decl(m_join_sym, 2, args, rng, info); } func_decl* dl_decl_plugin::mk_complement(sort* s) { if (!is_rel_sort(s)) { return 0; } func_decl_info info(m_family_id, OP_RA_COMPLEMENT, 0, 0); return m_manager->mk_func_decl(m_complement_sym, 1, &s, s, info); } func_decl * dl_decl_plugin::mk_negation_filter(unsigned num_params, parameter const* params, sort* r1, sort* r2) { ptr_vector sorts1, sorts2; if (!is_rel_sort(r1, sorts1)) { return 0; } if (!is_rel_sort(r2, sorts2)) { return 0; } if (0 != num_params % 2) { m_manager->raise_exception("expecting an even number of parameters to negation filter"); return 0; } for (unsigned i = 0; i + 1 < num_params; i += 2) { parameter const& p1 = params[i]; parameter const& p2 = params[i+1]; if (!p1.is_int() || !p2.is_int()) { m_manager->raise_exception("encountered non-integer parameter"); return 0; } unsigned i1 = p1.get_int(); unsigned i2 = p2.get_int(); if (i1 >= sorts1.size() || i2 >= sorts2.size()) { m_manager->raise_exception("index out of bounds"); return 0; } if (sorts1[i1] != sorts2[i2]) { m_manager->raise_exception("sort miss-match in join"); return 0; } } sort* args[2] = { r1, r2 }; func_decl_info info(m_family_id, OP_RA_NEGATION_FILTER, num_params, params); return m_manager->mk_func_decl(m_negation_filter_sym, 2, args, r1, info); } func_decl * dl_decl_plugin::mk_is_empty(sort* s) { if (!is_rel_sort(s)) { return 0; } func_decl_info info(m_family_id, OP_RA_IS_EMPTY, 0, 0); sort* rng = m_manager->mk_bool_sort(); return m_manager->mk_func_decl(m_is_empty_sym, 1, &s, rng, info); } func_decl * dl_decl_plugin::mk_constant(parameter const* params) { parameter const& p = params[0]; parameter const& ps = params[1]; if (!p.is_rational() || !p.get_rational().is_uint64()) { m_manager->raise_exception("first parameter should be a rational"); return 0; } if (!ps.is_ast() || !is_sort(ps.get_ast()) || !is_fin_sort(to_sort(ps.get_ast()))) { m_manager->raise_exception("second paramter should be a finite domain sort"); return 0; } sort* s = to_sort(ps.get_ast()); func_decl_info info(m_family_id, OP_DL_CONSTANT, 2, params); return m_manager->mk_func_decl(m_num_sym, 0, (sort*const*)0, s, info); } func_decl * dl_decl_plugin::mk_compare(decl_kind k, symbol const& sym, sort *const* domain) { if (!is_sort_of(domain[0], m_family_id, DL_FINITE_SORT)) { m_manager->raise_exception("expecting finite domain sort"); return 0; } if (domain[0] != domain[1]) { m_manager->raise_exception("expecting two identical finite domain sorts"); return 0; } func_decl_info info(m_family_id, k, 0, 0); return m_manager->mk_func_decl(sym, 2, domain, m_manager->mk_bool_sort(), info); } func_decl * dl_decl_plugin::mk_clone(sort* s) { if (!is_rel_sort(s)) { return 0; } func_decl_info info(m_family_id, OP_RA_CLONE, 0, 0); return m_manager->mk_func_decl(m_clone_sym, 1, &s, s, info); } /** In SMT2 syntax, we can write \c ((_ min R N) v_0 v_1 ... v_k)) where 0 <= N <= k, R is a relation of sort V_0 x V_1 x ... x V_k and each v_i is a zero-arity function (also known as a "constant" in SMT2 parlance) whose range is of sort V_i. Example: (define-sort number_t () (_ BitVec 2)) (declare-rel numbers (number_t number_t)) (declare-rel is_min (number_t number_t)) (declare-var x number_t) (declare-var y number_t) (rule (numbers #b00 #b11)) (rule (numbers #b00 #b01)) (rule (=> (and (numbers x y) ((_ min numbers 1) x y)) (is_min x y))) This says that we want to find the mininum y grouped by x. */ func_decl * dl_decl_plugin::mk_min(decl_kind k, unsigned num_parameters, parameter const * parameters) { if (num_parameters < 2) { m_manager->raise_exception("invalid min aggregate definition due to missing parameters"); return 0; } parameter const & relation_parameter = parameters[0]; if (!relation_parameter.is_ast() || !is_func_decl(relation_parameter.get_ast())) { m_manager->raise_exception("invalid min aggregate definition, first parameter is not a function declaration"); return 0; } func_decl* f = to_func_decl(relation_parameter.get_ast()); if (!m_manager->is_bool(f->get_range())) { m_manager->raise_exception("invalid min aggregate definition, first paramater must be a predicate"); return 0; } parameter const & min_col_parameter = parameters[1]; if (!min_col_parameter.is_int()) { m_manager->raise_exception("invalid min aggregate definition, second parameter must be an integer"); return 0; } if (min_col_parameter.get_int() < 0) { m_manager->raise_exception("invalid min aggregate definition, second parameter must be non-negative"); return 0; } if ((unsigned)min_col_parameter.get_int() >= f->get_arity()) { m_manager->raise_exception("invalid min aggregate definition, second parameter exceeds the arity of the relation"); return 0; } func_decl_info info(m_family_id, k, num_parameters, parameters); SASSERT(f->get_info() == 0); return m_manager->mk_func_decl(m_min_sym, f->get_arity(), f->get_domain(), f->get_range(), info); } func_decl * dl_decl_plugin::mk_func_decl( decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { func_decl* result = 0; switch(k) { case OP_RA_STORE: case OP_RA_SELECT: if (!check_params(0, 0, num_parameters) || !check_domain(1, UINT_MAX, arity)) { return 0; } result = mk_store_select(k, arity, domain); break; case OP_RA_EMPTY: if (!check_params( 1, 1, num_parameters) || !check_domain(0, 0, arity)) { return 0; } result = mk_empty(parameters[0]); break; case OP_RA_JOIN: if (!check_params(0, UINT_MAX, num_parameters) || !check_domain(2, 2, arity)) { return 0; } result = mk_join(num_parameters, parameters, domain[0], domain[1]); break; case OP_RA_UNION: case OP_RA_WIDEN: if (!check_params( 0, 0, num_parameters) || !check_domain(2, 2, arity)) { return 0; } result = mk_unionw(k, domain[0], domain[1]); break; case OP_RA_PROJECT: if (!check_params( 1, UINT_MAX, num_parameters) || !check_domain(1, 1, arity)) { return 0; } result = mk_project(num_parameters, parameters, domain[0]); break; case OP_RA_FILTER: if (!check_params( 1, 1, num_parameters) || !check_domain(1, 1, arity)) { return 0; } result = mk_filter(parameters[0], domain[0]); break; case OP_RA_IS_EMPTY: if (!check_params( 0, 0, num_parameters) || !check_domain(1, 1, arity)) { return 0; } result = mk_is_empty(domain[0]); break; case OP_RA_RENAME: if (!check_params( 2, UINT_MAX, num_parameters) || !check_domain(1, 1, arity)) { return 0; } result = mk_rename(num_parameters, parameters, domain[0]); break; case OP_RA_COMPLEMENT: if (!check_params( 0, 0, num_parameters) || !check_domain(1, 1, arity)) { return 0; } result = mk_complement(domain[0]); break; case OP_RA_NEGATION_FILTER: if (!check_params(1, UINT_MAX, num_parameters) || !check_domain(2, 2, arity)) { return 0; } result = mk_negation_filter(num_parameters, parameters, domain[0], domain[1]); break; case OP_RA_CLONE: if (!check_params(0, 0, num_parameters) || !check_domain(1, 1, arity)) { return 0; } result = mk_clone(domain[0]); break; case OP_DL_CONSTANT: if (!check_params( 2, 2, num_parameters) || !check_domain(0, 0, arity)) { return 0; } result = mk_constant(parameters); break; case OP_DL_LT: if (!check_params( 0, 0, num_parameters) || !check_domain(2, 2, arity)) { return 0; } result = mk_compare(OP_DL_LT, m_lt_sym, domain); break; case OP_DL_REP: { if (!check_domain(0, 0, num_parameters) || !check_domain(1, 1, arity)) return 0; func_decl_info info(m_family_id, k, 0, 0); result = m_manager->mk_func_decl(symbol("rep"), 1, domain, range, info); break; } case OP_DL_ABS: { if (!check_domain(0, 0, num_parameters) || !check_domain(1, 1, arity)) return 0; func_decl_info info(m_family_id, k, 0, 0); result = m_manager->mk_func_decl(symbol("abs"), 1, domain, range, info); break; } case OP_DL_MIN: return mk_min(k, num_parameters, parameters); default: m_manager->raise_exception("operator not recognized"); return 0; } TRACE("dl_decl_plugin", tout << mk_pp(result, *m_manager) << "\n";); return result; } void dl_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { op_names.push_back(builtin_name(m_min_sym.bare_str(), OP_DL_MIN)); } void dl_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { } dl_decl_util::ast_plugin_registrator::ast_plugin_registrator(ast_manager& m) { // ensure required plugins are installed into the ast_manager reg_decl_plugins(m); } dl_decl_util::dl_decl_util(ast_manager& m): m_plugin_registrator(m), m(m), m_arith(m), m_bv(m), m_fid(m.mk_family_id(symbol("datalog_relation"))) {} // create a constant belonging to a given finite domain. app* dl_decl_util::mk_numeral(uint64 value, sort* s) { if (is_finite_sort(s)) { uint64 sz = 0; if (try_get_size(s, sz) && sz <= value) { m.raise_exception("value is out of bounds"); } parameter params[2] = { parameter(rational(value, rational::ui64())), parameter(s) }; return m.mk_const(m.mk_func_decl(m_fid, OP_DL_CONSTANT, 2, params, 0, (sort*const*)0)); } if (m_arith.is_int(s) || m_arith.is_real(s)) { return m_arith.mk_numeral(rational(value, rational::ui64()), s); } if (m_bv.is_bv_sort(s)) { return m_bv.mk_numeral(rational(value, rational::ui64()), s); } if (m.is_bool(s)) { if (value == 0) { return m.mk_false(); } SASSERT(value == 1); return m.mk_true(); } std::stringstream strm; strm << "sort '" << mk_pp(s, m) << "' is not recognized as a sort that contains numeric values.\nUse Bool, BitVec, Int, Real, or a Finite domain sort"; m.raise_exception(strm.str().c_str()); return 0; } bool dl_decl_util::is_numeral(const expr* e, uint64& v) const { if (is_numeral(e)) { const app* c = to_app(e); SASSERT(c->get_decl()->get_num_parameters() == 2); parameter const& p = c->get_decl()->get_parameter(0); SASSERT(p.is_rational()); SASSERT(p.get_rational().is_uint64()); v = p.get_rational().get_uint64(); return true; } return false; } bool dl_decl_util::is_numeral_ext(expr* e, uint64& v) const { if (is_numeral(e, v)) { return true; } rational val; unsigned bv_size = 0; if (m_bv.is_numeral(e, val, bv_size) && bv_size < 64) { SASSERT(val.is_uint64()); v = val.get_uint64(); return true; } if (m.is_true(e)) { v = 1; return true; } if (m.is_false(e)) { v = 0; return true; } return false; } bool dl_decl_util::is_numeral_ext(expr* c) const { if (is_numeral(c)) return true; rational val; unsigned bv_size = 0; if (m_arith.is_numeral(c, val) && val.is_uint64()) return true; if (m_bv.is_numeral(c, val, bv_size) && bv_size < 64) return true; return m.is_true(c) || m.is_false(c); } sort* dl_decl_util::mk_sort(const symbol& name, uint64 domain_size) { if (domain_size == 0) { std::stringstream sstm; sstm << "Domain size of sort '" << name << "' may not be 0"; throw default_exception(sstm.str()); } parameter params[2] = { parameter(name), parameter(rational(domain_size, rational::ui64())) }; return m.mk_sort(m_fid, DL_FINITE_SORT, 2, params); } bool dl_decl_util::try_get_size(const sort * s, uint64& size) const { sort_size sz = s->get_info()->get_num_elements(); if (sz.is_finite()) { size = sz.size(); return true; } return false; } app* dl_decl_util::mk_lt(expr* a, expr* b) { expr* args[2] = { a, b }; return m.mk_app(m_fid, OP_DL_LT, 0, 0, 2, args); } app* dl_decl_util::mk_le(expr* a, expr* b) { expr* args[2] = { b, a }; return m.mk_not(m.mk_app(m_fid, OP_DL_LT, 0, 0, 2, args)); } sort* dl_decl_util::mk_rule_sort() { return m.mk_sort(m_fid, DL_RULE_SORT); } app* dl_decl_util::mk_rule(symbol const& name, unsigned num_args, expr* const* args) { ptr_buffer sorts; for (unsigned i = 0; i < num_args; ++i) { sorts.push_back(m.get_sort(args[i])); } func_decl* f = m.mk_func_decl(name, num_args, sorts.c_ptr(), mk_rule_sort()); return m.mk_app(f, num_args, args); } }; z3-4.4.0/src/ast/ast_smt2_pp.cpp0000644000175000017500000012317012540347414016166 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ast_smt2_pp.cpp Abstract: Pretty printer of AST formulas using SMT2 format. This printer is more expensive than the one in ast_smt_pp.h, but is supposed to generated a "prettier" and SMT2 compliant output. Author: Leonardo de Moura (leonardo) Revision History: --*/ #include"ast_smt2_pp.h" #include"shared_occs.h" #include"pp.h" #include"ast_ll_pp.h" #include"ast_pp.h" #include"algebraic_numbers.h" #include"pp_params.hpp" using namespace format_ns; #define ALIAS_PREFIX "a" #define MAX_INDENT 16 #define SMALL_INDENT 2 format * smt2_pp_environment::pp_fdecl_name(symbol const & s, unsigned & len) const { ast_manager & m = get_manager(); if (is_smt2_quoted_symbol(s)) { std::string str = mk_smt2_quoted_symbol(s); len = static_cast(str.length()); return mk_string(m, str.c_str()); } else if (s.is_numerical()) { std::string str = s.str(); len = static_cast(str.length()); return mk_string(m, str.c_str()); } else { len = static_cast(strlen(s.bare_str())); return mk_string(m, s.bare_str()); } } format * smt2_pp_environment::pp_fdecl_name(func_decl * f, unsigned & len) const { ast_manager & m = get_manager(); if (m.is_implies(f)) { len = 2; return mk_string(m, "=>"); } else if (m.is_ite(f)) { len = 3; return mk_string(m, "ite"); } else if (m.is_iff(f)) { len = 1; return mk_string(m, "="); } else { symbol s = f->get_name(); return pp_fdecl_name(s, len); } } bool smt2_pp_environment::is_indexed_fdecl(func_decl * f) const { if (f->get_family_id() == null_family_id) return false; unsigned num = f->get_num_parameters(); unsigned i; for (i = 0; i < num; i++) { if (f->get_parameter(i).is_int()) continue; if (f->get_parameter(i).is_rational()) continue; if (f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast())) continue; break; } return i == num && num > 0; } bool smt2_pp_environment::is_sort_param(func_decl * f) const { return f->get_family_id() != null_family_id && f->get_num_parameters() == 1 && f->get_parameter(0).is_ast() && is_sort(f->get_parameter(0).get_ast()) && f->get_range() == to_sort(f->get_parameter(0).get_ast()); } format * smt2_pp_environment::pp_as(format * fname, sort * s) { format * buf[2] = { fname, pp_sort(s) }; SASSERT(buf[0] != 0 && buf[1] != 0); return mk_seq1(get_manager(), buf, buf + 2, f2f(), "as"); } format * smt2_pp_environment::pp_fdecl_params(format * fname, func_decl * f) { SASSERT(is_indexed_fdecl(f)); unsigned num = f->get_num_parameters(); ptr_buffer fs; fs.push_back(fname); for (unsigned i = 0; i < num; i++) { SASSERT(f->get_parameter(i).is_int() || f->get_parameter(i).is_rational() || (f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast()))); if (f->get_parameter(i).is_int()) fs.push_back(mk_int(get_manager(), f->get_parameter(i).get_int())); else if (f->get_parameter(i).is_rational()) fs.push_back(mk_string(get_manager(), f->get_parameter(i).get_rational().to_string().c_str())); else fs.push_back(pp_fdecl_ref(to_func_decl(f->get_parameter(i).get_ast()))); } return mk_seq1(get_manager(), fs.begin(), fs.end(), f2f(), "_"); } format * smt2_pp_environment::pp_fdecl(func_decl * f, unsigned & len) { format * fname = pp_fdecl_name(f, len); if (f->get_family_id() == null_family_id) return fname; if (is_sort_param(f)) { len = UINT_MAX; return pp_as(fname, f->get_range()); } if (is_indexed_fdecl(f)) { len = UINT_MAX; return pp_fdecl_params(fname, f); } return fname; } format * smt2_pp_environment::pp_signature(format * f_name, func_decl * f) { if (is_indexed_fdecl(f)) { f_name = pp_fdecl_params(f_name, f); } ptr_buffer f_domain; for (unsigned i = 0; i < f->get_arity(); i++) f_domain.push_back(pp_sort(f->get_domain(i))); ptr_buffer args; args.push_back(f_name); args.push_back(mk_seq5(get_manager(), f_domain.begin(), f_domain.end(), f2f())); args.push_back(pp_sort(f->get_range())); return mk_seq5(get_manager(), args.begin(), args.end(), f2f()); } format * smt2_pp_environment::pp_fdecl_ref(func_decl * f) { unsigned len; format * f_name = pp_fdecl_name(f, len); if (f->get_family_id() == null_family_id) { return f_name; } return pp_signature(f_name, f); } format * smt2_pp_environment::pp_bv_literal(app * t, bool use_bv_lits, bool bv_neg) { bv_util & u = get_bvutil(); SASSERT(u.is_numeral(t)); rational val; unsigned bv_size = 1; u.is_numeral(t, val, bv_size); SASSERT(val.is_int()); val = u.norm(val, bv_size, bv_neg); bool is_neg = false; if (val.is_neg()) { val.neg(); is_neg = true; } SASSERT(val.is_nonneg()); format * vf; if (!use_bv_lits) { string_buffer<> buf; buf << "(_ bv" << val.to_string().c_str() << " " << bv_size << ")"; vf = mk_string(get_manager(), buf.c_str()); } else { sbuffer buf; unsigned sz = 0; buf.push_back('#'); if (bv_size % 4 == 0) { buf.push_back('x'); while (val.is_pos()) { rational c = val % rational(16); val = div(val, rational(16)); SASSERT(rational(0) <= c && c < rational(16)); if (c <= rational(9)) buf.push_back('0' + c.get_unsigned()); else buf.push_back('a' + (c.get_unsigned() - 10)); sz+=4; } while (sz < bv_size) { buf.push_back('0'); sz+=4; } } else { buf.push_back('b'); while (val.is_pos()) { rational c = val % rational(2); val = div(val, rational(2)); SASSERT(rational(0) <= c && c < rational(2)); if (c.is_zero()) buf.push_back('0'); else buf.push_back('1'); sz += 1; } while (sz < bv_size) { buf.push_back('0'); sz += 1; } } SASSERT(sz == bv_size); std::reverse(buf.begin()+2, buf.end()); buf.push_back(0); vf = mk_string(get_manager(), buf.begin()); } if (is_neg) { format * buffer[1] = {vf}; return mk_seq1(get_manager(), buffer, buffer+1, f2f(), "bvneg"); } return vf; } format * smt2_pp_environment::pp_float_literal(app * t, bool use_bv_lits, bool use_float_real_lits) { mpf_manager & fm = get_futil().fm(); scoped_mpf v(fm); ast_manager & m = get_manager(); format * body = 0; string_buffer<> buf; VERIFY(get_futil().is_numeral(t, v)); if (fm.is_nan(v)) { buf << "(_ NaN " << v.get().get_ebits() << " " << v.get().get_sbits() << ")"; return mk_string(m, buf.c_str()); } else if (fm.is_pinf(v)) { buf << "(_ +oo " << v.get().get_ebits() << " " << v.get().get_sbits() << ")"; return mk_string(m, buf.c_str()); } else if (fm.is_ninf(v)) { buf << "(_ -oo " << v.get().get_ebits() << " " << v.get().get_sbits() << ")"; return mk_string(m, buf.c_str()); } else if (fm.is_pzero(v)) { buf << "(_ +zero " << v.get().get_ebits() << " " << v.get().get_sbits() << ")"; return mk_string(m, buf.c_str()); } else if (fm.is_nzero(v)) { buf << "(_ -zero " << v.get().get_ebits() << " " << v.get().get_sbits() << ")"; return mk_string(m, buf.c_str()); } else if (use_float_real_lits) { buf << "((_ to_fp " << v.get().get_ebits() << " " << v.get().get_sbits() << ") RTZ " << fm.to_string(v).c_str() << ")"; return mk_string(m, buf.c_str()); } else { if (use_bv_lits) buf << "(fp #b" << (fm.sgn(v) ? 1 : 0); else buf << "(fp (_ bv" << (fm.sgn(v) ? 1 : 0) << " 1)"; body = mk_string(m, buf.c_str()); body = mk_compose(m, body, mk_string(m, " ")); mpf_exp_t exp = fm.exp(v); const mpz & bias = fm.m_powers2.m1(v.get().get_ebits() - 1); mpf_exp_t biased_exp = exp + fm.mpz_manager().get_int64(bias); app_ref e_biased_exp(m); e_biased_exp = get_bvutil().mk_numeral(biased_exp, v.get().get_ebits()); body = mk_compose(m, body, pp_bv_literal(e_biased_exp, use_bv_lits, false)); body = mk_compose(m, body, mk_string(m, " ")); scoped_mpz sig(fm.mpz_manager()); sig = fm.sig(v); app_ref e_sig(m); e_sig = get_bvutil().mk_numeral(rational(sig), v.get().get_sbits() - 1); body = mk_compose(m, body, pp_bv_literal(e_sig, use_bv_lits, false)); body = mk_compose(m, body, mk_string(m, ")")); return body; } } // generate (- f) format * smt2_pp_environment::mk_neg(format * f) const { format * buffer[1] = {f}; return mk_seq1(get_manager(), buffer, buffer+1, f2f(), "-"); } // Return the format string .0 where num is the value of val. format * smt2_pp_environment::mk_float(rational const & val) const { SASSERT(val.is_nonneg()); SASSERT(val.is_int()); std::string s = val.to_string(); s += ".0"; return mk_string(get_manager(), s.c_str()); } format * smt2_pp_environment::pp_arith_literal(app * t, bool decimal, unsigned decimal_prec) { arith_util & u = get_autil(); SASSERT(u.is_numeral(t) || u.is_irrational_algebraic_numeral(t)); rational val; bool is_int = true; if (u.is_numeral(t, val, is_int)) { if (is_int) { if (val.is_nonneg()) { return mk_string(get_manager(), val.to_string().c_str()); } else { val.neg(); return mk_neg(mk_string(get_manager(), val.to_string().c_str())); } } else { bool is_neg = val.is_neg(); if (is_neg) val.neg(); format * vf; if (val.is_int()) { vf = mk_float(val); } else if (decimal) { std::ostringstream buffer; val.display_decimal(buffer, decimal_prec); vf = mk_string(get_manager(), buffer.str().c_str()); } else { format * buffer[2] = { mk_float(numerator(val)), mk_float(denominator(val)) }; vf = mk_seq1(get_manager(), buffer, buffer+2, f2f(), "/"); } return is_neg ? mk_neg(vf) : vf; } } else { SASSERT(u.is_irrational_algebraic_numeral(t)); anum const & val2 = u.to_irrational_algebraic_numeral(t); algebraic_numbers::manager & am = u.am(); format * vf; std::ostringstream buffer; bool is_neg = false; if (decimal) { scoped_anum abs_val(am); am.set(abs_val, val2); if (am.is_neg(val2)) { is_neg = true; am.neg(abs_val); } am.display_decimal(buffer, abs_val, decimal_prec); } else { am.display_root_smt2(buffer, val2); } vf = mk_string(get_manager(), buffer.str().c_str()); return is_neg ? mk_neg(vf) : vf; } } format * smt2_pp_environment::pp_datalog_literal(app * t) { uint64 v; VERIFY (get_dlutil().is_numeral(t, v)); std::ostringstream buffer; buffer << v; return mk_string(get_manager(), buffer.str().c_str()); } format_ns::format * smt2_pp_environment::pp_sort(sort * s) { // Basic sort pretty printing. // This method is redefined in cmd_context::pp_env: support for parametric sorts. // Here, we just pretty print builtin sorts: Bool, Int, Real, BitVec and Array. ast_manager & m = get_manager(); if (m.is_bool(s)) return mk_string(m, "Bool"); if (get_autil().is_int(s)) return mk_string(m, "Int"); if (get_autil().is_real(s)) return mk_string(m, "Real"); if (get_bvutil().is_bv_sort(s)) { unsigned sz = get_bvutil().get_bv_size(s); ptr_buffer fs; fs.push_back(mk_string(m, "BitVec")); fs.push_back(mk_unsigned(m, sz)); return mk_seq1(m, fs.begin(), fs.end(), f2f(), "_"); } if (get_arutil().is_array(s)) { ptr_buffer fs; unsigned sz = get_array_arity(s); for (unsigned i = 0; i < sz; i++) { fs.push_back(pp_sort(get_array_domain(s, i))); } fs.push_back(pp_sort(get_array_range(s))); return mk_seq1(m, fs.begin(), fs.end(), f2f(), "Array"); } if (get_futil().is_float(s)) { unsigned ebits = get_futil().get_ebits(s); unsigned sbits = get_futil().get_sbits(s); ptr_buffer fs; fs.push_back(mk_string(m, "FloatingPoint")); fs.push_back(mk_unsigned(m, ebits)); fs.push_back(mk_unsigned(m, sbits)); return mk_seq1(m, fs.begin(), fs.end(), f2f(), "_"); } return format_ns::mk_string(get_manager(), s->get_name().str().c_str()); } typedef app_ref_vector format_ref_vector; class smt2_printer { ast_manager & m_manager; smt2_pp_environment & m_env; shared_occs m_soccs; expr * m_root; typedef obj_map expr2alias; // expr -> position @ m_aliased_exprs, m_aliased_pps, m_aliased_lvls_names. ptr_vector m_expr2alias_stack; expr2alias * m_expr2alias; // expr -> position @ m_aliased_exprs, m_aliased_pps, m_aliased_lvls_names. ptr_vector m_aliased_exprs; format_ref_vector m_aliased_pps; svector > m_aliased_lvls_names; unsigned m_next_alias_idx; struct scope { unsigned m_aliased_exprs_lim; unsigned m_old_next_alias_idx; expr * m_old_root; scope(unsigned lim, unsigned idx, expr * r):m_aliased_exprs_lim(lim), m_old_next_alias_idx(idx), m_old_root(r) {} }; svector m_scopes; // size of m_aliased_exprs, m_aliased_pps, m_aliased_lvls_names. svector m_var_names; typedef hashtable symbol_set; symbol_set m_var_names_set; struct frame { expr * m_curr; unsigned m_idx; unsigned m_spos; bool m_use_alias; // if new aliases can be created frame(expr * c, unsigned i, unsigned s, bool use_alias):m_curr(c), m_idx(i), m_spos(s), m_use_alias(use_alias) {} }; svector m_frame_stack; format_ref_vector m_format_stack; struct info { unsigned m_lvl; unsigned m_weight; unsigned m_depth; info(unsigned l, unsigned w, unsigned d):m_lvl(l), m_weight(w), m_depth(d) {} }; svector m_info_stack; string_buffer<> m_next_name_buffer; // Config bool m_pp_decimal; unsigned m_pp_decimal_precision; bool m_pp_bv_lits; bool m_pp_float_real_lits; bool m_pp_bv_neg; unsigned m_pp_max_depth; unsigned m_pp_min_alias_size; bool m_pp_flat_assoc; symbol next_name(char const * prefix, unsigned & idx) { while (true) { m_next_name_buffer.reset(); m_next_name_buffer.append(prefix); m_next_name_buffer.append("!"); m_next_name_buffer.append(idx); symbol r(m_next_name_buffer.c_str()); idx++; if (m_env.uses(r)) continue; if (m_var_names_set.contains(r)) continue; return r; } } symbol next_alias() { return next_name(ALIAS_PREFIX, m_next_alias_idx); } void register_alias(expr * n, format * nf, unsigned lvl, symbol const & name) { SASSERT(m_aliased_exprs.size() == m_aliased_pps.size()); SASSERT(m_aliased_exprs.size() == m_aliased_lvls_names.size()); unsigned idx = m_aliased_exprs.size(); m_expr2alias->insert(n, idx); m_aliased_exprs.push_back(n); m_aliased_pps.push_back(nf); m_aliased_lvls_names.push_back(std::make_pair(lvl, name)); } void push_frame(expr * n, bool use_alias) { m_frame_stack.push_back(frame(n, 0, m_format_stack.size(), use_alias)); } void pop_frame() { m_frame_stack.pop_back(); } ast_manager & m() const { return m_manager; } ast_manager & fm() const { return format_ns::fm(m()); } std::string ensure_quote(symbol const& s) { std::string str; if (is_smt2_quoted_symbol(s)) str = mk_smt2_quoted_symbol(s); else str = s.str(); return str; } symbol ensure_quote_sym(symbol const& s) { if (is_smt2_quoted_symbol(s)) { std::string str; str = mk_smt2_quoted_symbol(s); return symbol(str.c_str()); } else return s; } void pp_var(var * v) { format * f; if (v->get_idx() < m_var_names.size()) { symbol s = m_var_names[m_var_names.size() - v->get_idx() - 1]; f = mk_string(m(), s.str().c_str()); } else { // fallback... it is not supposed to happen when the printer is correctly used. string_buffer<> buf; buf.append("(:var "); buf.append(v->get_idx()); buf.append(")"); f = mk_string(m(), buf.c_str()); } m_format_stack.push_back(f); m_info_stack.push_back(info(0, 1, 1)); } format * pp_attribute(char const * attr, format * f) { return mk_compose(m(), mk_string(m(), attr), mk_indent(m(), static_cast(strlen(attr)), f)); } format * pp_simple_attribute(char const * attr, int v) { return mk_compose(m(), mk_string(m(), attr), mk_int(m(), v)); } format * pp_simple_attribute(char const * attr, symbol const & s) { std::string str = ensure_quote(s); return mk_compose(m(), mk_string(m(), attr), mk_string(m(), str.c_str())); } format * pp_labels(bool is_pos, buffer const & names, format * f) { if (names.empty()) return f; ptr_buffer buf; buf.push_back(f); for (unsigned i = 0; i < names.size(); i++) { buf.push_back(pp_simple_attribute(is_pos ? ":lblpos " : ":lblneg ", names[i])); } return mk_seq1(m(), buf.begin(), buf.end(), f2f(), "!"); } void pp_const(app * c) { format * f; if (m_env.get_autil().is_numeral(c) || m_env.get_autil().is_irrational_algebraic_numeral(c)) { f = m_env.pp_arith_literal(c, m_pp_decimal, m_pp_decimal_precision); } else if (m_env.get_bvutil().is_numeral(c)) { f = m_env.pp_bv_literal(c, m_pp_bv_lits, m_pp_bv_neg); } else if (m_env.get_futil().is_numeral(c)) { f = m_env.pp_float_literal(c, m_pp_bv_lits, m_pp_float_real_lits); } else if (m_env.get_dlutil().is_numeral(c)) { f = m_env.pp_datalog_literal(c); } else { buffer names; if (m().is_label_lit(c, names)) { f = pp_labels(true, names, mk_string(m(), "true")); } else { unsigned len; f = m_env.pp_fdecl(c->get_decl(), len); } } m_format_stack.push_back(f); m_info_stack.push_back(info(0, 1, 1)); } bool pp_aliased(expr * t) { unsigned idx; if (m_expr2alias->find(t, idx)) { unsigned lvl = m_aliased_lvls_names[idx].first; symbol const & s = m_aliased_lvls_names[idx].second; m_format_stack.push_back(mk_string(m(), s.str().c_str())); m_info_stack.push_back(info(lvl+1, 1, 1)); return true; } return false; } void process_var(var * v) { pp_var(v); pop_frame(); } bool process_args(app * t, frame & fr) { unsigned num = t->get_num_args(); while (fr.m_idx < num) { expr * arg = t->get_arg(fr.m_idx); fr.m_idx++; if (pp_aliased(arg)) continue; switch (arg->get_kind()) { case AST_VAR: pp_var(to_var(arg)); break; case AST_APP: if (to_app(arg)->get_num_args() == 0) { pp_const(to_app(arg)); } else { push_frame(arg, fr.m_use_alias); return false; } break; case AST_QUANTIFIER: push_frame(arg, fr.m_use_alias); return false; default: UNREACHABLE(); } } return true; } void store_result(expr * t, frame & fr, format * f, info & f_info) { m_format_stack.shrink(fr.m_spos); m_info_stack.shrink(fr.m_spos); if (fr.m_use_alias && m_root != t && ((f_info.m_depth >= m_pp_max_depth) || ((f_info.m_weight >= m_pp_min_alias_size || is_quantifier(t)) && m_soccs.is_shared(t)))) { symbol a = next_alias(); TRACE("smt2_pp", tout << "a: " << a << " depth: " << f_info.m_depth << ", weight: " << f_info.m_weight << ", lvl: " << f_info.m_lvl << " t: #" << t->get_id() << "\n" << mk_ll_pp(t, m()) << ", is-shared: " << m_soccs.is_shared(t) << "\n";); register_alias(t, f, f_info.m_lvl, a); m_format_stack.push_back(mk_string(m(), a.str().c_str())); m_info_stack.push_back(info(f_info.m_lvl + 1, 1, 1)); } else { m_format_stack.push_back(f); m_info_stack.push_back(f_info); } pop_frame(); } bool flat_assoc(app * t, frame const & fr) { if (!m_pp_flat_assoc) return false; func_decl * f = t->get_decl(); if (f->is_associative() && m_frame_stack.size() >= 2 && !m_soccs.is_shared(t)) { frame const & prev_fr = m_frame_stack[m_frame_stack.size() - 2]; return is_app(prev_fr.m_curr) && to_app(prev_fr.m_curr)->get_decl() == f; } return false; } void process_app(app * t, frame & fr) { if (fr.m_idx == 0) { if (pp_aliased(t)) { pop_frame(); return; } } if (!process_args(t, fr)) return; if (t->get_num_args() == 0) { pp_const(t); pop_frame(); return; } if (flat_assoc(t, fr)) { pop_frame(); return; } buffer labels; bool is_pos; format * f = 0; format ** it = m_format_stack.c_ptr() + fr.m_spos; format ** end = m_format_stack.c_ptr() + m_format_stack.size(); if (m().is_label(t, is_pos, labels)) { SASSERT(it + 1 == end); f = pp_labels(is_pos, labels, *it); } else if (m().is_pattern(t)) { f = mk_seq5(m(), it, end, f2f()); } else { unsigned len; SASSERT(it < end); format * fname = m_env.pp_fdecl(t->get_decl(), len); if (len > MAX_INDENT) { f = mk_group(m(), mk_compose(m(), mk_indent(m(), 1, mk_compose(m(), mk_string(m(), "("), fname)), mk_indent(m(), SMALL_INDENT, mk_compose(m(), mk_seq(m(), it, end, f2f()), mk_string(m(), ")"))))); } else { format * first = *it; ++it; f = mk_group(m(), mk_compose(m(), mk_indent(m(), 1, mk_compose(m(), mk_string(m(), "("), fname)), mk_indent(m(), len + 2, mk_compose(m(), mk_string(m(), " "), first, mk_seq(m(), it, end, f2f()), mk_string(m(), ")"))))); } } info f_info(0, 1, 1); info * it2 = m_info_stack.begin() + fr.m_spos; info * end2 = m_info_stack.end(); for (; it2 != end2; ++it2) { if (it2->m_lvl > f_info.m_lvl) f_info.m_lvl = it2->m_lvl; f_info.m_weight += it2->m_weight; if (it2->m_depth > f_info.m_depth) f_info.m_depth = it2->m_depth; } f_info.m_depth++; store_result(t, fr, f, f_info); } // Add let decls used to build f. format * pp_let(format * f, unsigned & num_lets) { unsigned old_sz = m_scopes.empty() ? 0 : m_scopes.back().m_aliased_exprs_lim; unsigned sz = m_aliased_exprs.size(); SASSERT(old_sz <= sz); num_lets = sz - old_sz; TRACE("pp_let", tout << "old_sz: " << old_sz << ", sz: " << sz << "\n";); if (old_sz == sz) return f; vector > decls; for (unsigned i = old_sz; i < sz; i++) { unsigned lvl = m_aliased_lvls_names[i].first; symbol f_name = m_aliased_lvls_names[i].second; format * f_def[1] = { m_aliased_pps.get(i) }; decls.reserve(lvl+1); ptr_vector & lvl_decls = decls[lvl]; lvl_decls.push_back(mk_seq1(m(), f_def, f_def+1, f2f(), f_name.str().c_str())); } TRACE("pp_let", tout << "decls.size(): " << decls.size() << "\n";); ptr_buffer buf; unsigned num_op = 0; vector >::iterator it = decls.begin(); vector >::iterator end = decls.end(); for (; it != end; ++it) { ptr_vector & lvl_decls = *it; if (lvl_decls.empty()) continue; if (num_op > 0) buf.push_back(mk_line_break(m())); num_op++; buf.push_back(mk_string(m(), "(let ")); buf.push_back(mk_indent(m(), 5, mk_seq5(m(), lvl_decls.begin(), lvl_decls.end(), f2f()))); } TRACE("pp_let", tout << "num_op: " << num_op << "\n";); if (num_op == 0) return f; buf.push_back(mk_indent(m(), SMALL_INDENT, mk_compose(m(), mk_line_break(m()), f))); for (unsigned i = 0; i < num_op; i++) buf.push_back(mk_string(m(), ")")); return mk_compose(m(), buf.size(), buf.c_ptr()); } format * pp_let(format * f) { unsigned num_lets; return pp_let(f, num_lets); } void begin_scope() { SASSERT(m_aliased_exprs.size() == m_aliased_pps.size()); SASSERT(m_aliased_exprs.size() == m_aliased_lvls_names.size()); TRACE("pp_scope", tout << "[begin-scope] sz: " << m_aliased_exprs.size() << ", m_root: " << m_root << "\n";); m_scopes.push_back(scope(m_aliased_exprs.size(), m_next_alias_idx, m_root)); unsigned lvl = m_scopes.size(); while (lvl >= m_expr2alias_stack.size()) m_expr2alias_stack.push_back(alloc(expr2alias)); m_expr2alias = m_expr2alias_stack[lvl]; m_next_alias_idx = 1; } void end_scope() { TRACE("pp_scope", tout << "[end-scope] before sz: " << m_aliased_exprs.size() << ", m_root: " << m_root << "\n";); m_expr2alias->reset(); scope & s = m_scopes.back(); unsigned old_sz = s.m_aliased_exprs_lim; m_root = s.m_old_root; m_next_alias_idx = s.m_old_next_alias_idx; m_scopes.pop_back(); unsigned new_lvl = m_scopes.size(); m_expr2alias = m_expr2alias_stack[new_lvl]; SASSERT(old_sz <= m_aliased_exprs.size()); m_aliased_exprs.shrink(old_sz); m_aliased_pps.shrink(old_sz); m_aliased_lvls_names.shrink(old_sz); TRACE("pp_scope", tout << "[end-scope] after sz: " << m_aliased_exprs.size() << ", m_root: " << m_root << "\n";); } void register_var_names(quantifier * q) { unsigned num_decls = q->get_num_decls(); for (unsigned i = 0; i < num_decls; i++) { symbol name = ensure_quote_sym(q->get_decl_name(i)); if (name.is_numerical()) { unsigned idx = 1; name = next_name("x", idx); } else if (m_env.uses(name) || m_var_names_set.contains(name)) { unsigned idx = 1; name = next_name(name.bare_str(), idx); } SASSERT(!m_var_names_set.contains(name)); m_var_names.push_back(name); m_var_names_set.insert(name); } } void unregister_var_names(quantifier * q) { unsigned num_decls = q->get_num_decls(); for (unsigned i = 0; i < num_decls; i++) { symbol s = m_var_names.back(); m_var_names.pop_back(); m_var_names_set.erase(s); } } format * pp_var_decls(quantifier * q) { ptr_buffer buf; unsigned num_decls = q->get_num_decls(); SASSERT(num_decls <= m_var_names.size()); symbol * it = m_var_names.end() - num_decls; for (unsigned i = 0; i < num_decls; i++, it++) { format * fs[1] = { m_env.pp_sort(q->get_decl_sort(i)) }; buf.push_back(mk_seq1(m(), fs, fs+1, f2f(), it->str().c_str())); } return mk_seq5(m(), buf.begin(), buf.end(), f2f()); } void process_quantifier(quantifier * q, frame & fr) { if (fr.m_idx == 0) { begin_scope(); m_root = q->get_expr(); register_var_names(q); } unsigned num_children = q->get_num_patterns() + q->get_num_no_patterns() + 1; if (fr.m_idx < num_children) { unsigned idx = fr.m_idx; fr.m_idx++; if (idx < q->get_num_patterns()) { push_frame(q->get_pattern(idx), false); } else if (idx < q->get_num_patterns() + q->get_num_no_patterns()) { push_frame(q->get_no_pattern(idx - q->get_num_patterns()), false); } else { push_frame(q->get_expr(), fr.m_use_alias); } return; } unsigned num_lets = 0; format * f_body = pp_let(m_format_stack.back(), num_lets); // The current SMT2 frontend uses weight 1 as default. #define MIN_WEIGHT 1 if (q->has_patterns() || q->get_weight() > MIN_WEIGHT || q->get_skid() != symbol::null || (q->get_qid() != symbol::null && !q->get_qid().is_numerical())) { ptr_buffer buf; buf.push_back(f_body); if (q->get_num_patterns() > 0) { format ** it = m_format_stack.c_ptr() + fr.m_spos; format ** end = it + q->get_num_patterns(); for (; it != end; ++it) { buf.push_back(pp_attribute(":pattern ", *it)); } } if (q->get_num_no_patterns() > 0) { format ** it = m_format_stack.c_ptr() + fr.m_spos + q->get_num_patterns(); format ** end = it + q->get_num_no_patterns(); for (; it != end; ++it) { buf.push_back(pp_attribute(":no-pattern ", *it)); } } if (q->get_weight() > MIN_WEIGHT) { buf.push_back(pp_simple_attribute(":weight ", q->get_weight())); } if (q->get_skid() != symbol::null) { buf.push_back(pp_simple_attribute(":skolemid ", q->get_skid())); } if (q->get_qid() != symbol::null) { #if 0 buf.push_back(pp_simple_attribute(":qid ", q->get_qid())); #else if (!q->get_qid().is_numerical()) buf.push_back(pp_simple_attribute(":qid ", q->get_qid())); #endif } f_body = mk_seq1(m(), buf.begin(), buf.end(), f2f(), "!"); } format * f_decls = pp_var_decls(q); format * fs[2] = { f_decls, f_body }; char const * header = q->is_forall() ? "forall" : "exists"; format * f = mk_seq3(m(), fs, fs+2, f2f(), header, 1, SMALL_INDENT); info f_info = m_info_stack.back(); f_info.m_lvl = 0; // quantifiers don't depend on any let-decls, pp_let added all dependencies for the body. f_info.m_depth++; f_info.m_weight += q->get_num_decls()*2 + num_lets*8; unregister_var_names(q); end_scope(); store_result(q, fr, f, f_info); } void init_expr2alias_stack() { SASSERT(m_expr2alias_stack.empty()); expr2alias * new_map = alloc(expr2alias); m_expr2alias_stack.push_back(new_map); m_expr2alias = new_map; } void del_expr2alias_stack() { std::for_each(m_expr2alias_stack.begin(), m_expr2alias_stack.end(), delete_proc()); m_expr2alias_stack.reset(); m_expr2alias = 0; } void reset_expr2alias_stack() { SASSERT(!m_expr2alias_stack.empty()); ptr_vector::iterator it = m_expr2alias_stack.begin(); ptr_vector::iterator end = m_expr2alias_stack.end(); for (; it != end; ++it) (*it)->reset(); m_expr2alias = m_expr2alias_stack[0]; } void reset_stacks() { m_next_alias_idx = 1; reset_expr2alias_stack(); m_aliased_exprs.reset(); m_aliased_pps.reset(); m_aliased_lvls_names.reset(); m_scopes.reset(); m_frame_stack.reset(); m_format_stack.reset(); m_info_stack.reset(); } void process(expr * n, format_ref & r) { reset_stacks(); SASSERT(&(r.get_manager()) == &(fm())); m_soccs(n); TRACE("smt2_pp_shared", tout << "shared terms for:\n" << mk_pp(n, m()) << "\n"; tout << "------>\n"; shared_occs::iterator it = m_soccs.begin_shared(); shared_occs::iterator end = m_soccs.end_shared(); for (; it != end; ++it) { tout << mk_pp(*it, m()) << "\n"; }); m_root = n; push_frame(n, true); while (!m_frame_stack.empty()) { frame & fr = m_frame_stack.back(); switch (fr.m_curr->get_kind()) { case AST_QUANTIFIER: process_quantifier(to_quantifier(fr.m_curr), fr); break; case AST_APP: process_app(to_app(fr.m_curr), fr); break; case AST_VAR: process_var(to_var(fr.m_curr)); break; default: UNREACHABLE(); } } r = pp_let(m_format_stack.back()); m_format_stack.pop_back(); } void reset_var_names() { m_var_names.reset(); m_var_names_set.reset(); } public: smt2_printer(smt2_pp_environment & env, params_ref const & params): m_manager(env.get_manager()), m_env(env), m_soccs(m_manager), m_root(0), m_aliased_pps(fm()), m_next_alias_idx(1), m_format_stack(fm()) { init_expr2alias_stack(); pp_params p(params); m_pp_decimal = p.decimal(); m_pp_decimal_precision = p.decimal_precision(); m_pp_bv_lits = p.bv_literals(); m_pp_float_real_lits = p.fp_real_literals(); m_pp_bv_neg = p.bv_neg(); m_pp_max_depth = p.max_depth(); m_pp_min_alias_size = p.min_alias_size(); m_pp_flat_assoc = p.flat_assoc(); } ~smt2_printer() { del_expr2alias_stack(); } void operator()(expr * n, format_ref & r) { reset_var_names(); process(n, r); } void operator()(expr * n, unsigned num, char const * var_prefix, format_ref & r, sbuffer & var_names) { reset_var_names(); if (var_prefix == 0) var_prefix = "x"; if (strcmp(var_prefix, ALIAS_PREFIX) == 0) { var_prefix = "_a"; } unsigned idx = 1; for (unsigned i = 0; i < num; i++) { symbol name = next_name(var_prefix, idx); name = ensure_quote_sym(name); var_names.push_back(name); m_var_names_set.insert(name); m_var_names.push_back(name); } std::reverse(m_var_names.begin(), m_var_names.end()); process(n, r); } void operator()(sort * s, format_ref & r) { r = m_env.pp_sort(s); } void operator()(func_decl * f, format_ref & r) { unsigned arity = f->get_arity(); unsigned len; format * fname = m_env.pp_fdecl_name(f, len); format * args[3]; args[0] = fname; ptr_buffer buf; for (unsigned i = 0; i < arity; i++) { buf.push_back(m_env.pp_sort(f->get_domain(i))); } args[1] = mk_seq5(m(), buf.begin(), buf.end(), f2f()); args[2] = m_env.pp_sort(f->get_range()); r = mk_seq1(m(), args, args+3, f2f(), "declare-fun"); } }; void mk_smt2_format(expr * n, smt2_pp_environment & env, params_ref const & p, unsigned num_vars, char const * var_prefix, format_ref & r, sbuffer & var_names) { smt2_printer pr(env, p); pr(n, num_vars, var_prefix, r, var_names); } void mk_smt2_format(sort * s, smt2_pp_environment & env, params_ref const & p, format_ref & r) { smt2_printer pr(env, p); pr(s, r); } void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & p, format_ref & r) { smt2_printer pr(env, p); pr(f, r); } std::ostream & ast_smt2_pp(std::ostream & out, expr * n, smt2_pp_environment & env, params_ref const & p, unsigned indent, unsigned num_vars, char const * var_prefix) { ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; mk_smt2_format(n, env, p, num_vars, var_prefix, r, var_names); if (indent > 0) r = mk_indent(m, indent, r.get()); pp(out, r.get(), m, p); return out; } std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & env, params_ref const & p, unsigned indent) { ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; mk_smt2_format(s, env, p, r); if (indent > 0) r = mk_indent(m, indent, r.get()); pp(out, r.get(), m, p); return out; } std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p, unsigned indent) { ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; mk_smt2_format(f, env, p, r); if (indent > 0) r = mk_indent(m, indent, r.get()); pp(out, r.get(), m, p); return out; } mk_ismt2_pp::mk_ismt2_pp(ast * t, ast_manager & m, params_ref const & p, unsigned indent, unsigned num_vars, char const * var_prefix): m_ast(t), m_manager(m), m_params(p), m_indent(indent), m_num_vars(num_vars), m_var_prefix(var_prefix) { } mk_ismt2_pp::mk_ismt2_pp(ast * t, ast_manager & m, unsigned indent, unsigned num_vars, char const * var_prefix): m_ast(t), m_manager(m), m_params(m_empty), m_indent(indent), m_num_vars(num_vars), m_var_prefix(var_prefix) { } std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p) { smt2_pp_environment_dbg env(p.m_manager); if (is_expr(p.m_ast)) { ast_smt2_pp(out, to_expr(p.m_ast), env, p.m_params, p.m_indent, p.m_num_vars, p.m_var_prefix); } else if (is_sort(p.m_ast)) { ast_smt2_pp(out, to_sort(p.m_ast), env, p.m_params, p.m_indent); } else { SASSERT(is_func_decl(p.m_ast)); ast_smt2_pp(out, to_func_decl(p.m_ast), env, p.m_params, p.m_indent); } return out; } std::ostream& operator<<(std::ostream& out, expr_ref const& e) { return out << mk_ismt2_pp(e.get(), e.get_manager()); } std::ostream& operator<<(std::ostream& out, app_ref const& e) { return out << mk_ismt2_pp(e.get(), e.get_manager()); } std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e) { for (unsigned i = 0; i < e.size(); ++i) out << mk_ismt2_pp(e[i], e.get_manager()) << "\n"; return out; } std::ostream& operator<<(std::ostream& out, app_ref_vector const& e) { for (unsigned i = 0; i < e.size(); ++i) out << mk_ismt2_pp(e[i], e.get_manager()) << "\n"; return out; } #ifdef Z3DEBUG void pp(expr const * n, ast_manager & m) { std::cout << mk_ismt2_pp(const_cast(n), m) << std::endl; } void pp(expr_ref const & n) { std::cout << mk_ismt2_pp(n.get(), n.m()) << std::endl; } #endif z3-4.4.0/src/ast/ast_smt_pp.cpp0000644000175000017500000010767012540347414016113 0ustar michaelmichael/*++ Copyright (c) 2008 Microsoft Corporation Module Name: ast_smt_pp.cpp Abstract: Pretty printer of AST formulas as SMT benchmarks. Author: Michal Moskal (micmo) 2008-04-09. Nikolaj Bjorner (nbjorner) Revision History: --*/ #include #include #include"ast_smt_pp.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"array_decl_plugin.h" #include"datatype_decl_plugin.h" #include"vector.h" #include"for_each_ast.h" #include"decl_collector.h" #include"smt2_util.h" // --------------------------------------- // smt_renaming static const char m_predef_names[][8] = { "=", ">=", "<=", "+", "-", "*", ">", "<", "!=", "or", "and", "implies", "not", "iff", "xor", "true", "false", "forall", "exists", "let", "flet" }; symbol smt_renaming::fix_symbol(symbol s, int k) { std::ostringstream buffer; char const * data = s.is_numerical() ? "" : s.bare_str(); if (data[0] && !data[1]) { switch (data[0]) { case '/': data = "op_div"; break; case '%': data = "op_mod"; break; default: break; } } if (k == 0 && *data) { if (s.is_numerical()) { return s; } if (is_special(data)) { return s; } if (all_is_legal(data)) { return s; } } if (s.is_numerical()) { buffer << s << k; return symbol(buffer.str().c_str()); } if (is_smt2_quoted_symbol(s)) { buffer << mk_smt2_quoted_symbol(s); } else { buffer << s; } if (k > 0) { buffer << k; } return symbol(buffer.str().c_str()); } bool smt_renaming::is_legal(char c) { return c == '.' || c == '_' || c == '\'' || c == '?' || c == '!' || isalnum(c); } bool smt_renaming::is_special(char const* s) { if (!s) return false; if (s[0] != '|') return false; ++s; while (*s) { if (s[0] == '|') { return (0 == s[1]); } ++s; } return false; } bool smt_renaming::is_numerical(char const* s) { while (*s) { if (!isdigit(*s)) { return false; } ++s; } return true; } bool smt_renaming::all_is_legal(char const* s) { if (!s) return false; if (is_numerical(s)) return false; while (*s) { if (!is_legal(*s)) return false; ++s; } return true; } smt_renaming::smt_renaming() { for (unsigned i = 0; i < ARRAYSIZE(m_predef_names); ++i) { symbol s(m_predef_names[i]); m_translate.insert(s, s); m_rev_translate.insert(s, s); } } symbol smt_renaming::get_symbol(symbol s0) { symbol s; if (m_translate.find(s0, s)) { return s; } int k = 0; do { s = fix_symbol(s0, k++); } while (m_rev_translate.contains(s)); m_translate.insert(s0, s); m_rev_translate.insert(s, s0); return s; } // --------------------------------------- // smt_printer class smt_printer { std::ostream& m_out; ast_manager& m_manager; ptr_vector& m_qlists; smt_renaming& m_renaming; unsigned m_indent; unsigned m_num_var_names; char const* const* m_var_names; ptr_vector m_todo; ast_mark m_mark; unsigned m_num_lets; arith_util m_autil; bv_util m_bvutil; family_id m_basic_fid; family_id m_bv_fid; family_id m_arith_fid; family_id m_array_fid; family_id m_dt_fid; family_id m_label_fid; symbol m_logic; symbol m_AUFLIRA; bool m_no_lets; bool m_is_smt2; bool m_simplify_implies; expr* m_top; bool is_bool(sort* s) { return m_basic_fid == s->get_family_id() && s->get_decl_kind() == BOOL_SORT; } bool is_bool(expr* e) { return is_bool(m_manager.get_sort(e)); } bool is_proof(sort* s) { return m_basic_fid == s->get_family_id() && s->get_decl_kind() == PROOF_SORT; } bool is_proof(expr* e) { return is_proof(m_manager.get_sort(e)); } void pp_id(expr* n) { if (m_is_smt2) { m_out << (is_bool(n)?"$x":(is_proof(n)?"@x":"?x")) << n->get_id(); } else { m_out << (is_bool(n)?"$x":"?x") << n->get_id(); } } void pp_decl(func_decl* d) { symbol sym = m_renaming.get_symbol(d->get_name()); if (d->get_family_id() == m_dt_fid) { m_out << sym; } else if (m_manager.is_ite(d)) { if (!m_is_smt2 && is_bool(d->get_range())) { m_out << "if_then_else"; } else { m_out << "ite"; } } else if (!m_is_smt2 && m_manager.is_implies(d)) { m_out << "implies"; } else if (m_is_smt2 && m_manager.is_iff(d)) { m_out << "="; } else if (m_is_smt2 && m_manager.is_implies(d)) { m_out << "=>"; } else if (m_is_smt2 && is_decl_of(d, m_arith_fid, OP_UMINUS)) { m_out << "-"; } else { visit_params(false, sym, d->get_num_parameters(), d->get_parameters()); } m_out << " "; } bool is_sort_param(unsigned num_params, parameter const* params) { return num_params == 1 && params[0].is_ast() && is_sort(params[0].get_ast()); } void visit_params(bool is_sort_symbol, symbol const& sym, unsigned num_params, parameter const* params) { if (0 == num_params) { m_out << sym; return; } if (m_is_smt2) { if (is_sort_symbol && sym != symbol("BitVec")) { m_out << "(" << sym << " "; } else if (!is_sort_symbol && is_sort_param(num_params, params)) { m_out << "(as " << sym << " "; } else { m_out << "(_ " << sym << " "; } } else { m_out << sym << "["; } for (unsigned i = 0; i < num_params; ++i) { parameter const& p = params[i]; if (p.is_ast()) { if (is_sort(p.get_ast())) { visit_sort(to_sort(p.get_ast())); } else if (is_expr(p.get_ast())) { pp_expr(to_expr(p.get_ast())); } else if (is_func_decl(p.get_ast())) { pp_decl(to_func_decl(p.get_ast())); } else { m_out << "#" << p.get_ast()->get_id(); } } else { m_out << p; } if (i + 1 < num_params) { if (m_is_smt2) { m_out << " "; } else { m_out << ": "; } } } if (m_is_smt2) { m_out << ")"; } else { m_out << "]"; } } bool is_auflira() const { return m_logic == m_AUFLIRA; } void visit_sort(sort* s, bool bool2int = false) { symbol sym; if (bool2int && is_bool(s) && !m_is_smt2) { sym = symbol("Int"); } else if (s->is_sort_of(m_bv_fid, BV_SORT)) { sym = symbol("BitVec"); } else if (s->is_sort_of(m_arith_fid, REAL_SORT)) { sym = s->get_name(); } else if (m_manager.is_bool(s)) { sym = symbol("Bool"); } else if (s->is_sort_of(m_arith_fid, INT_SORT)) { sym = s->get_name(); } else if (s->is_sort_of(m_array_fid, ARRAY_SORT) && m_is_smt2) { sym = "Array"; } else if (s->is_sort_of(m_array_fid, ARRAY_SORT) && !m_is_smt2) { unsigned num_params = s->get_num_parameters(); SASSERT(num_params >= 2); if (is_auflira()) { sort* rng = to_sort(s->get_parameter(1).get_ast()); if (rng->get_family_id() == m_array_fid) { m_out << "Array2"; } else { m_out << "Array1"; } return; } sort* s1 = to_sort(s->get_parameter(0).get_ast()); sort* s2 = to_sort(s->get_parameter(1).get_ast()); if (num_params == 2 && s1->is_sort_of(m_bv_fid, BV_SORT) && s2->is_sort_of(m_bv_fid, BV_SORT)) { m_out << "Array"; m_out << "[" << s1->get_parameter(0).get_int(); m_out << ":" << s2->get_parameter(0).get_int() << "]"; return; } m_out << "(Array "; for (unsigned i = 0; i < num_params; ++i) { visit_sort(to_sort(s->get_parameter(i).get_ast())); if (i + 1 < num_params) { m_out << " "; } } m_out << ")"; return; } else if (s->is_sort_of(m_dt_fid, DATATYPE_SORT)) { m_out << m_renaming.get_symbol(s->get_name()); return; } else { sym = m_renaming.get_symbol(s->get_name()); } visit_params(true, sym, s->get_num_parameters(), s->get_parameters()); } void display_rational(rational const & r, bool is_int) { bool d = !is_int; if (r.is_int()) { m_out << r << (d ? ".0" : ""); } else { m_out << "(/ " << numerator(r) << (d ? ".0" : "") << " " << denominator(r) << (d ? ".0" : "") << ")"; } } void pp_arg(expr *arg, app *parent) { if (!m_is_smt2 && is_bool(arg) && is_var(arg) && parent->get_family_id() == m_basic_fid) { m_out << "(not (= "; pp_marked_expr(arg); m_out << " 0))"; } else if (!m_is_smt2 && is_bool(arg) && !is_var(arg) && parent->get_family_id() != m_basic_fid && parent->get_family_id() != m_dt_fid) { m_out << "(ite "; pp_marked_expr(arg); m_out << " 1 0)"; } else { pp_marked_expr(arg); } } void visit_app(app* n) { rational val; bool is_int, pos; buffer names; unsigned bv_size; unsigned num_args = n->get_num_args(); func_decl* decl = n->get_decl(); if (m_autil.is_numeral(n, val, is_int)) { if (val.is_neg()) { val.neg(); if (m_is_smt2) { m_out << "(- "; } else { m_out << "(~ "; } display_rational(val, is_int); m_out << ")"; } else { display_rational(val, is_int); } } else if (m_bvutil.is_numeral(n, val, bv_size)) { if (m_is_smt2) { m_out << "(_ bv" << val << " " << bv_size << ")"; } else { m_out << "bv" << val << "[" << bv_size << "]"; } } else if (m_bvutil.is_bit2bool(n)) { unsigned bit = n->get_decl()->get_parameter(0).get_int(); if (m_is_smt2) { m_out << "(= ((_ extract " << bit << " " << bit << ") "; pp_marked_expr(n->get_arg(0)); m_out << ") (_ bv1 1))"; } else { m_out << "(= (extract[" << bit << ":" << bit << "] "; pp_marked_expr(n->get_arg(0)); m_out << ") bv1[1])"; } } else if (m_manager.is_label(n, pos, names) && names.size() >= 1) { if (m_is_smt2) { m_out << "(! "; pp_marked_expr(n->get_arg(0)); m_out << (pos?":lblpos":":lblneg") << " " << m_renaming.get_symbol(names[0]) << ")"; } else { m_out << "(" << (pos?"lblpos":"lblneg") << " " << m_renaming.get_symbol(names[0]) << " "; expr* ch = n->get_arg(0); pp_marked_expr(ch); m_out << ")"; } } else if (m_manager.is_label_lit(n, names) && names.size() >= 1) { if (m_is_smt2) { m_out << "(! true :lblpos " << m_renaming.get_symbol(names[0]) << ")"; } else { m_out << "(lblpos " << m_renaming.get_symbol(names[0]) << " true )"; } } else if (num_args == 0) { if (decl->private_parameters()) { m_out << m_renaming.get_symbol(decl->get_name()); } else { symbol sym = m_renaming.get_symbol(decl->get_name()); visit_params(false, sym, decl->get_num_parameters(), decl->get_parameters()); } } else if (num_args == 1 && n->get_family_id() == m_label_fid) { expr* ch = n->get_arg(0); pp_marked_expr(ch); } else if (m_simplify_implies && m_manager.is_implies(decl) && m_manager.is_implies(n->get_arg(1))) { expr *curr = n; expr *arg; m_out << "(implies (and"; while (m_manager.is_implies(curr)) { arg = to_app(curr)->get_arg(0); m_out << " "; pp_arg(arg, n); curr = to_app(curr)->get_arg(1); } m_out << ") "; pp_arg(curr, n); m_out << ")"; } else if (m_manager.is_distinct(decl)) { ptr_vector args(num_args, n->get_args()); unsigned idx = 0; m_out << "(and"; while (true) { while (idx < args.size() && !args[idx]) idx++; if (idx >= args.size()) break; sort * s = m_manager.get_sort(args[idx]); unsigned next = idx + 1; // check if there is only a single one while (next < args.size() && (!args[next] || m_manager.get_sort(args[next]) != s)) next++; if (next >= args.size()) { args[idx] = 0; // if so, skip it continue; } // otherwise print all of the relevant sort m_out << " (distinct"; for (unsigned i = idx; i < args.size(); ++i) { if (args[i] && s == m_manager.get_sort(args[i])) { m_out << " "; pp_marked_expr(args[i]); args[i] = 0; } } m_out << ")"; } m_out << " true)"; } else { m_out << "("; pp_decl(decl); for (unsigned i = 0; i < num_args; ++i) { pp_arg(n->get_arg(i), n); if (i + 1 < num_args) { m_out << " "; } } m_out << ")"; } } void print_no_lets(expr *e) { smt_printer p(m_out, m_manager, m_qlists, m_renaming, m_logic, true, m_simplify_implies, m_is_smt2, m_indent, m_num_var_names, m_var_names); p(e); } void print_bound(symbol const& name) { if (!m_is_smt2 && (name.is_numerical() || '?' != name.bare_str()[0])) { m_out << "?"; } m_out << name; } void visit_quantifier(quantifier* q) { m_qlists.push_back(q); m_out << "("; if (q->is_forall()) { m_out << "forall "; } else { m_out << "exists "; } if (m_is_smt2) { m_out << "("; } for (unsigned i = 0; i < q->get_num_decls(); ++i) { sort* s = q->get_decl_sort(i); m_out << "("; print_bound(m_renaming.get_symbol(q->get_decl_name(i))); m_out << " "; visit_sort(s, true); m_out << ") "; } if (m_is_smt2) { m_out << ")"; } if (m_is_smt2 && (q->get_num_patterns() > 0 || q->get_qid() != symbol::null)) { m_out << "(! "; } { smt_printer p(m_out, m_manager, m_qlists, m_renaming, m_logic, false, m_is_smt2, m_simplify_implies, m_indent, m_num_var_names, m_var_names); p(q->get_expr()); } for (unsigned i = 0; i < q->get_num_patterns(); ++i) { app *pat = reinterpret_cast (q->get_pattern(i)); if (pat->get_num_args() == 1 && is_app(pat->get_arg(0))) { app *app = to_app(pat->get_arg(0)); if (app->get_num_args() == 1 && app->get_decl()->get_name().str() == "sk_hack") { /* m_out << " :ex_act { "; print_no_lets(app->get_arg(0)); m_out << "}"; */ continue; } } if (m_is_smt2) { m_out << " :pattern ( "; } else { m_out << " :pat { "; } for (unsigned j = 0; j < pat->get_num_args(); ++j) { print_no_lets(pat->get_arg(j)); m_out << " "; } if (m_is_smt2) { m_out << ")"; } else { m_out << "}"; } } if (q->get_qid() != symbol::null) m_out << " :qid " << q->get_qid(); if (m_is_smt2 && (q->get_num_patterns() > 0 || q->get_qid() != symbol::null)) { m_out << ")"; } m_out << ")"; newline(); m_qlists.pop_back(); } void newline() { unsigned i = m_indent; m_out << "\n"; while (i > 0) { m_out << " "; --i; } } void visit_var(var* v) { unsigned idx = v->get_idx(); for (unsigned i = m_qlists.size(); ; --i) { if (i == 0) { break; } quantifier* q = m_qlists[i-1]; unsigned num_decls = q->get_num_decls(); if (idx < num_decls) { unsigned offs = num_decls-idx-1; symbol name = m_renaming.get_symbol(q->get_decl_name(offs)); print_bound(name); return; } idx -= num_decls; } if (idx < m_num_var_names) { m_out << m_var_names[m_num_var_names - idx - 1]; } else { m_out << "?" << idx; } } void pp_marked_expr(expr* n) { if (m_mark.is_marked(n)) { pp_id(n); } else { pp_expr(n); } } void pp_expr(expr* n) { switch(n->get_kind()) { case AST_QUANTIFIER: visit_quantifier(to_quantifier(n)); break; case AST_APP: visit_app(to_app(n)); break; case AST_VAR: visit_var(to_var(n)); break; default: UNREACHABLE(); } } void visit_expr(expr* n) { if (m_is_smt2) { m_out << "(let (("; } else if (is_bool(n)) { m_out << "(flet ("; } else { m_out << "(let ("; } pp_id(n); m_out << " "; pp_expr(n); if (m_is_smt2) { m_out << ")"; } m_out << ")"; newline(); } bool is_unit(expr* n) { if (n->get_ref_count() <= 2 && is_small(n)) { return true; } if (n == m_top) { return true; } switch(n->get_kind()) { case AST_VAR: return true; case AST_APP: return to_app(n)->get_num_args() == 0; default: return false; } } static const unsigned m_line_length = 80; bool is_small(expr* n) { unsigned sz = 0; return is_small(n, sz); } bool is_small(expr* n, unsigned& sz) { if (sz > m_line_length) { return false; } if (m_mark.is_marked(n)) { sz += 5; return sz <= m_line_length; } switch(n->get_kind()) { case AST_QUANTIFIER: return false; case AST_VAR: sz += 5; return sz <= m_line_length; case AST_APP: { app* a = to_app(n); func_decl* d = a->get_decl(); symbol const& s = d->get_name(); if (s.is_numerical()) { sz += 4; } if (s.is_numerical()) { sz += 7; } else { sz += 3 + static_cast(strlen(s.bare_str())); } for (unsigned i = 0; i < a->get_num_args() && sz <= m_line_length; ++i) { sz += 1; if (!is_small(a->get_arg(i), sz)) { return false; } } return sz <= m_line_length; } default: return false; } } bool visit_children(expr* n) { unsigned todo_size = m_todo.size(); switch(n->get_kind()) { case AST_QUANTIFIER: case AST_VAR: break; case AST_APP: { app* a = to_app(n); for (unsigned i = 0; i < a->get_num_args(); ++i) { expr* ch = a->get_arg(i); if (!is_unit(ch) && !m_mark.is_marked(ch)) { m_todo.push_back(ch); } } break; } default: UNREACHABLE(); break; } bool all_visited = todo_size == m_todo.size(); return all_visited; } public: smt_printer(std::ostream& out, ast_manager& m, ptr_vector& ql, smt_renaming& rn, symbol logic, bool no_lets, bool is_smt2, bool simplify_implies, unsigned indent, unsigned num_var_names = 0, char const* const* var_names = 0) : m_out(out), m_manager(m), m_qlists(ql), m_renaming(rn), m_indent(indent), m_num_var_names(num_var_names), m_var_names(var_names), m_num_lets(0), m_autil(m), m_bvutil(m), m_logic(logic), m_AUFLIRA("AUFLIRA"), // It's much easier to read those testcases with that. m_no_lets(no_lets), m_is_smt2(is_smt2), m_simplify_implies(simplify_implies) { m_basic_fid = m.get_basic_family_id(); m_label_fid = m.mk_family_id("label"); m_bv_fid = m.mk_family_id("bv"); m_arith_fid = m.mk_family_id("arith"); m_array_fid = m.mk_family_id("array"); m_dt_fid = m.mk_family_id("datatype"); } void operator()(expr* n) { m_top = n; if (!m_no_lets) { switch(n->get_kind()) { case AST_APP: for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) { m_todo.push_back(to_app(n)->get_arg(i)); } break; // Don't do this for quantifiers -- they need to have the body be // visited when the m_qlist contains the relevant quantifier. default: break; } } while (!m_todo.empty()) { expr* m = m_todo.back(); if (m_mark.is_marked(m)) { m_todo.pop_back(); } else if (is_unit(m)) { m_todo.pop_back(); } else if (visit_children(m)) { m_todo.pop_back(); m_mark.mark(m, true); visit_expr(m); ++m_num_lets; } } pp_marked_expr(n); for (unsigned i = 0; i < m_num_lets; ++i) { m_out << ")"; } m_mark.reset(); m_num_lets = 0; m_top = 0; } void pp_dt(ast_mark& mark, sort* s) { SASSERT(s->is_sort_of(m_dt_fid, DATATYPE_SORT)); datatype_util util(m_manager); ptr_vector const* decls; ptr_vector rec_sorts; rec_sorts.push_back(s); mark.mark(s, true); // collect siblings and sorts that have not already been printed. for (unsigned h = 0; h < rec_sorts.size(); ++h) { s = rec_sorts[h]; decls = util.get_datatype_constructors(s); for (unsigned i = 0; i < decls->size(); ++i) { func_decl* f = (*decls)[i]; for (unsigned j = 0; j < f->get_arity(); ++j) { sort* s2 = f->get_domain(j); if (!mark.is_marked(s2)) { if (m_manager.is_uninterp(s2)) { pp_sort_decl(mark, s2); } else if (!util.is_datatype(s2)) { // skip } else if (util.are_siblings(s, s2)) { rec_sorts.push_back(s2); mark.mark(s2, true); } else { pp_sort_decl(mark, s2); } } } } } if (m_is_smt2) { // TBD: datatypes may be declared parametrically. // get access to parametric generalization, or print // monomorphic specialization with a tag that gets reused at use-point. m_out << "(declare-datatypes () ("; } else { m_out << ":datatypes ("; } for (unsigned si = 0; si < rec_sorts.size(); ++si) { s = rec_sorts[si]; m_out << "("; m_out << m_renaming.get_symbol(s->get_name()); m_out << " "; decls = util.get_datatype_constructors(s); for (unsigned i = 0; i < decls->size(); ++i) { func_decl* f = (*decls)[i]; ptr_vector const& accs = *util.get_constructor_accessors(f); if (m_is_smt2 || accs.size() > 0) { m_out << "("; } m_out << m_renaming.get_symbol(f->get_name()); // if (accs.size() > 0) { m_out << " "; // } for (unsigned j = 0; j < accs.size(); ++j) { func_decl* a = accs[j]; m_out << "(" << m_renaming.get_symbol(a->get_name()) << " "; visit_sort(a->get_range()); m_out << ")"; if (j + 1 < accs.size()) m_out << " "; } if (m_is_smt2 || accs.size() > 0) { m_out << ")"; if (i + 1 < decls->size()) { m_out << " "; } } } m_out << ")"; if (si + 1 < rec_sorts.size()) { m_out << " "; } } if (m_is_smt2) { m_out << ")"; } m_out << ")"; newline(); } void pp_sort_decl(ast_mark& mark, sort* s) { if (mark.is_marked(s)) { return; } if (s->is_sort_of(m_dt_fid, DATATYPE_SORT)) { pp_dt(mark, s); } else { if (m_is_smt2) { m_out << "(declare-sort "; } else { m_out << ":extrasorts ("; } visit_sort(s); m_out << ")"; newline(); } mark.mark(s, true); } void operator()(sort* s) { ast_mark mark; pp_sort_decl(mark, s); } void operator()(func_decl* d) { if (m_is_smt2) { m_out << "(declare-fun "; pp_decl(d); m_out << "("; for (unsigned i = 0; i < d->get_arity(); ++i) { if (i > 0) m_out << " "; visit_sort(d->get_domain(i), true); } m_out << ") "; visit_sort(d->get_range()); m_out << ")"; } else { m_out << "("; pp_decl(d); for (unsigned i = 0; i < d->get_arity(); ++i) { m_out << " "; visit_sort(d->get_domain(i), true); } m_out << " "; visit_sort(d->get_range()); m_out << ")"; } } void visit_pred(func_decl* d) { m_out << "("; pp_decl(d); for (unsigned i = 0; i < d->get_arity(); ++i) { m_out << " "; visit_sort(d->get_domain(i), true); } m_out << ")"; } }; // --------------------------------------- // ast_smt_pp: ast_smt_pp::ast_smt_pp(ast_manager& m): m_manager(m), m_assumptions(m), m_assumptions_star(m), m_benchmark_name(), m_source_info(), m_status("unknown"), m_category(), m_logic(), m_dt_fid(m.mk_family_id("datatype")), m_is_declared(&m_is_declared_default), m_simplify_implies(true) {} void ast_smt_pp::display_expr(std::ostream& strm, expr* n) { ptr_vector ql; smt_renaming rn; smt_printer p(strm, m_manager, ql, rn, m_logic, false, false, m_simplify_implies, 0); p(n); } void ast_smt_pp::display_expr_smt2(std::ostream& strm, expr* n, unsigned indent, unsigned num_var_names, char const* const* var_names) { ptr_vector ql; smt_renaming rn; smt_printer p(strm, m_manager, ql, rn, m_logic, false, true, m_simplify_implies, indent, num_var_names, var_names); p(n); } void ast_smt_pp::display_ast_smt2(std::ostream& strm, ast* a, unsigned indent, unsigned num_var_names, char const* const* var_names) { ptr_vector ql; smt_renaming rn; smt_printer p(strm, m_manager, ql, rn, m_logic, false, true, m_simplify_implies, indent, num_var_names, var_names); if (is_expr(a)) { p(to_expr(a)); } else if (is_func_decl(a)) { p(to_func_decl(a)); } else { SASSERT(is_sort(a)); p(to_sort(a)); } } void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) { ptr_vector ql; ast_manager& m = m_manager; decl_collector decls(m); smt_renaming rn; for (unsigned i = 0; i < m_assumptions.size(); ++i) { decls.visit(m_assumptions[i].get()); } for (unsigned i = 0; i < m_assumptions_star.size(); ++i) { decls.visit(m_assumptions_star[i].get()); } decls.visit(n); if (m.is_proof(n)) { strm << "("; } if (m_benchmark_name != symbol::null) { strm << "; " << m_benchmark_name << "\n"; } if (m_source_info != symbol::null && m_source_info != symbol("")) { strm << "; :source { " << m_source_info << " }\n"; } if (m.is_bool(n)) { strm << "(set-info :status " << m_status << ")\n"; } if (m_category != symbol::null && m_category != symbol("")) { strm << "; :category { " << m_category << " }\n"; } if (m_logic != symbol::null && m_logic != symbol("")) { strm << "(set-logic " << m_logic << ")\n"; } if (m_attributes.size() > 0) { strm << "; " << m_attributes.c_str(); } ast_mark sort_mark; for (unsigned i = 0; i < decls.get_num_sorts(); ++i) { sort* s = decls.get_sorts()[i]; if (!(*m_is_declared)(s)) { smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0); p.pp_sort_decl(sort_mark, s); } } for (unsigned i = 0; i < decls.get_num_decls(); ++i) { func_decl* d = decls.get_func_decls()[i]; if (!(*m_is_declared)(d)) { smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0); p(d); strm << "\n"; } } for (unsigned i = 0; i < decls.get_num_preds(); ++i) { func_decl* d = decls.get_pred_decls()[i]; if (!(*m_is_declared)(d)) { smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0); p(d); strm << "\n"; } } for (unsigned i = 0; i < m_assumptions.size(); ++i) { smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 1); strm << "(assert\n "; p(m_assumptions[i].get()); strm << ")\n"; } for (unsigned i = 0; i < m_assumptions_star.size(); ++i) { smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 1); strm << "(assert\n "; p(m_assumptions_star[i].get()); strm << ")\n"; } smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 0); if (m.is_bool(n)) { if (!m.is_true(n)) { strm << "(assert\n "; p(n); strm << ")\n"; } strm << "(check-sat)\n"; } else if (m.is_proof(n)) { strm << "(proof\n"; p(n); strm << "))\n"; } else { p(n); } } void ast_smt_pp::display(std::ostream& strm, expr* n) { ptr_vector ql; decl_collector decls(m_manager); smt_renaming rn; for (unsigned i = 0; i < m_assumptions.size(); ++i) { decls.visit(m_assumptions[i].get()); } for (unsigned i = 0; i < m_assumptions_star.size(); ++i) { decls.visit(m_assumptions_star[i].get()); } decls.visit(n); strm << "(benchmark "; if (m_benchmark_name != symbol::null) { strm << m_benchmark_name << "\n"; } else { strm << "unnamed\n"; } if (m_source_info != symbol::null && m_source_info != symbol("")) { strm << ":source { " << m_source_info << " }\n"; } strm << ":status " << m_status << "\n"; if (m_category != symbol::null && m_category != symbol("")) { strm << ":category { " << m_category << " }\n"; } if (m_logic != symbol::null && m_logic != symbol("")) { strm << ":logic " << m_logic << "\n"; } if (m_attributes.size() > 0) { strm << m_attributes.c_str(); } ast_mark sort_mark; for (unsigned i = 0; i < decls.get_num_sorts(); ++i) { sort* s = decls.get_sorts()[i]; if (!(*m_is_declared)(s)) { smt_printer p(strm, m_manager, ql, rn, m_logic, true, false, m_simplify_implies, 0); p.pp_sort_decl(sort_mark, s); } } for (unsigned i = 0; i < decls.get_num_decls(); ++i) { func_decl* d = decls.get_func_decls()[i]; if (!(*m_is_declared)(d)) { strm << ":extrafuns ("; smt_printer p(strm, m_manager, ql, rn, m_logic, true, false, m_simplify_implies, 0); p(d); strm << ")\n"; } } for (unsigned i = 0; i < decls.get_num_preds(); ++i) { func_decl* d = decls.get_pred_decls()[i]; if (!(*m_is_declared)(d)) { strm << ":extrapreds ("; smt_printer p(strm, m_manager, ql, rn, m_logic, true, false, m_simplify_implies, 0); p.visit_pred(d); strm << ")\n"; } } for (unsigned i = 0; i < m_assumptions.size(); ++i) { expr * e = m_assumptions[i].get(); strm << ":assumption\n"; smt_printer p(strm, m_manager, ql, rn, m_logic, false, false, m_simplify_implies, 0); p(e); strm << "\n"; } for (unsigned i = 0; i < m_assumptions_star.size(); ++i) { strm << ":assumption-core\n"; smt_printer p(strm, m_manager, ql, rn, m_logic, false, false, m_simplify_implies, 0); p(m_assumptions_star[i].get()); strm << "\n"; } { strm << ":formula\n"; smt_printer p(strm, m_manager, ql, rn, m_logic, false, false, m_simplify_implies, 0); p(n); strm << "\n"; } strm << ")\n"; } z3-4.4.0/src/ast/ast_util.cpp0000644000175000017500000001200112540347414015545 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ast_util.cpp Abstract: Helper functions Author: Leonardo de Moura (leonardo) 2007-06-08. Revision History: --*/ #include "ast_util.h" app * mk_list_assoc_app(ast_manager & m, func_decl * f, unsigned num_args, expr * const * args) { SASSERT(f->is_associative()); SASSERT(num_args >= 2); if (num_args > 2) { unsigned j = num_args - 1; app * r = m.mk_app(f, args[j-1], args[j]); -- j; while (j > 0) { --j; r = m.mk_app(f, args[j], r); } return r; } else { SASSERT(num_args == 2); return m.mk_app(f, args[0], args[1]); } } app * mk_list_assoc_app(ast_manager & m, family_id fid, decl_kind k, unsigned num_args, expr * const * args) { func_decl * decl = m.mk_func_decl(fid, k, 0, 0, num_args, args, 0); SASSERT(decl != 0); SASSERT(decl->is_associative()); return mk_list_assoc_app(m, decl, num_args, args); } bool is_well_formed_vars(ptr_vector& bound, expr * top) { ptr_vector todo; ast_mark mark; todo.push_back(top); while (!todo.empty()) { expr * e = todo.back(); todo.pop_back(); if (mark.is_marked(e)) { continue; } mark.mark(e, true); if (is_quantifier(e)) { quantifier* q = to_quantifier(e); unsigned depth = q->get_num_decls(); bound.append(depth, q->get_decl_sorts()); if (!is_well_formed_vars(bound, q->get_expr())) { return false; } bound.resize(bound.size()-depth); } else if (is_app(e)) { app* a = to_app(e); for (unsigned i = 0; i < a->get_num_args(); ++i) { todo.push_back(a->get_arg(i)); } } else if (is_var(e)) { var* v = to_var(e); unsigned index = v->get_idx(); sort* s = v->get_sort(); SASSERT(index < bound.size()); index = bound.size()-1-index; if (!bound[index]) { bound[index] = s; } if (bound[index] != s) { return false; } } else { UNREACHABLE(); } } return true; } bool is_atom(ast_manager & m, expr * n) { if (is_quantifier(n) || !m.is_bool(n)) return false; if (is_var(n)) return true; SASSERT(is_app(n)); if (to_app(n)->get_family_id() != m.get_basic_family_id()) { return true; } // the other operators of the basic family are not considered atomic: distinct, ite, and, or, iff, xor, not, implies. return (m.is_eq(n) && !m.is_bool(to_app(n)->get_arg(0))) || m.is_true(n) || m.is_false(n); } bool is_literal(ast_manager & m, expr * n) { return is_atom(m, n) || (m.is_not(n) && is_atom(m, to_app(n)->get_arg(0))); } void get_literal_atom_sign(ast_manager & m, expr * n, expr * & atom, bool & sign) { SASSERT(is_literal(m, n)); if (is_atom(m, n)) { atom = n; sign = false; } else { SASSERT(m.is_not(n)); atom = to_app(n)->get_arg(0); sign = true; } } bool is_clause(ast_manager & m, expr * n) { if (is_literal(m, n)) return true; if (m.is_or(n)) { unsigned num_args = to_app(n)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { if (!is_literal(m, to_app(n)->get_arg(i))) return false; } return true; } return false; } unsigned get_clause_num_literals(ast_manager & m, expr * cls) { SASSERT(is_clause(m, cls)); if (is_literal(m, cls)) return 1; SASSERT(m.is_or(cls)); return to_app(cls)->get_num_args(); } expr * get_clause_literal(ast_manager & m, expr * cls, unsigned idx) { SASSERT(is_clause(m, cls)); SASSERT(idx < get_clause_num_literals(m, cls)); if (is_literal(m, cls)) { SASSERT(idx == 0); return cls; } SASSERT(m.is_or(cls)); return to_app(cls)->get_arg(idx); } expr * mk_and(ast_manager & m, unsigned num_args, expr * const * args) { if (num_args == 0) return m.mk_true(); else if (num_args == 1) return args[0]; else return m.mk_and(num_args, args); } expr * mk_or(ast_manager & m, unsigned num_args, expr * const * args) { if (num_args == 0) return m.mk_false(); else if (num_args == 1) return args[0]; else return m.mk_or(num_args, args); } expr * mk_not(ast_manager & m, expr * arg) { expr * atom; if (m.is_not(arg, atom)) return atom; else return m.mk_not(arg); } expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args) { expr_ref_buffer new_diseqs(m); for (unsigned i = 0; i < num_args; i++) { for (unsigned j = i + 1; j < num_args; j++) new_diseqs.push_back(m.mk_not(m.mk_eq(args[i], args[j]))); } return mk_and(m, new_diseqs.size(), new_diseqs.c_ptr()); } z3-4.4.0/src/ast/act_cache.h0000644000175000017500000000223712540347414015272 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: act_cache.h Abstract: expr -> expr activity cache It maintains at most N unused entries Author: Leonardo (leonardo) 2011-04-12 Notes: --*/ #ifndef _ACT_CACHE_H_ #define _ACT_CACHE_H_ #include"ast.h" #include"obj_hashtable.h" #include"chashtable.h" class act_cache { ast_manager & m_manager; typedef cmap, default_eq > map; map m_table; ptr_vector m_queue; // recently created queue unsigned m_qhead; unsigned m_unused; unsigned m_max_unused; void compress_queue(); void init(); void dec_refs(); void del_unused(); public: act_cache(ast_manager & m); act_cache(ast_manager & m, unsigned max_unused); ~act_cache(); void insert(expr * k, expr * v); expr * find(expr * k); void reset(); void cleanup(); unsigned size() const { return m_table.size(); } unsigned capacity() const { return m_table.capacity(); } bool empty() const { return m_table.empty(); } bool check_invariant() const; }; #endif z3-4.4.0/src/ast/expr_stat.h0000644000175000017500000000201412540347414015402 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: expr_stat.h Abstract: Expression statistics (symbol count, var count, depth, ...) All functions in these module assume expressions do not contain nested quantifiers. Author: Leonardo de Moura (leonardo) 2008-02-05. Revision History: --*/ #ifndef _EXPR_STAT_H_ #define _EXPR_STAT_H_ class expr; struct expr_stat { unsigned m_sym_count; // symbol count unsigned m_depth; // depth unsigned m_const_count; // constant count unsigned m_max_var_idx; bool m_ground; expr_stat():m_sym_count(0), m_depth(0), m_const_count(0), m_max_var_idx(0), m_ground(true) {} }; /** \brief Collect statistics regarding the given expression. \warning This function traverses the dag as a tree. */ void get_expr_stat(expr * n, expr_stat & r); /** \brief Return the number of symbols in \c n. \warning This function traverses the dag as a tree. */ unsigned get_symbol_count(expr * n); #endif /* _EXPR_STAT_H_ */ z3-4.4.0/src/ast/rewriter/0000755000175000017500000000000012540347414015066 5ustar michaelmichaelz3-4.4.0/src/ast/rewriter/poly_rewriter_def.h0000644000175000017500000007414212540347414020773 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: poly_rewriter_def.h Abstract: Basic rewriting rules for Polynomials. Author: Leonardo (leonardo) 2011-04-08 Notes: --*/ #include"poly_rewriter.h" #include"poly_rewriter_params.hpp" #include"ast_lt.h" #include"ast_ll_pp.h" #include"ast_smt2_pp.h" template void poly_rewriter::updt_params(params_ref const & _p) { poly_rewriter_params p(_p); m_flat = p.flat(); m_som = p.som(); m_hoist_mul = p.hoist_mul(); m_hoist_cmul = p.hoist_cmul(); m_som_blowup = p.som_blowup(); if (!m_flat) m_som = false; if (m_som) m_hoist_mul = false; } template void poly_rewriter::get_param_descrs(param_descrs & r) { poly_rewriter_params::collect_param_descrs(r); } template expr * poly_rewriter::mk_add_app(unsigned num_args, expr * const * args) { switch (num_args) { case 0: return mk_numeral(numeral(0)); case 1: return args[0]; default: return m().mk_app(get_fid(), add_decl_kind(), num_args, args); } } // t = (^ x y) --> return x, and set k = y if k is an integer >= 1 // Otherwise return t and set k = 1 template expr * poly_rewriter::get_power_body(expr * t, rational & k) { if (!is_power(t)) { k = rational(1); return t; } if (is_numeral(to_app(t)->get_arg(1), k) && k.is_int() && k > rational(1)) { return to_app(t)->get_arg(0); } k = rational(1); return t; } template expr * poly_rewriter::mk_mul_app(unsigned num_args, expr * const * args) { switch (num_args) { case 0: return mk_numeral(numeral(1)); case 1: return args[0]; default: if (use_power()) { rational k_prev; expr * prev = get_power_body(args[0], k_prev); rational k; ptr_buffer new_args; #define PUSH_POWER() { \ if (k_prev.is_one()) { \ new_args.push_back(prev); \ } \ else { \ expr * pargs[2] = { prev, mk_numeral(k_prev) }; \ new_args.push_back(m().mk_app(get_fid(), power_decl_kind(), 2, pargs)); \ } \ } for (unsigned i = 1; i < num_args; i++) { expr * arg = get_power_body(args[i], k); if (arg == prev) { k_prev += k; } else { PUSH_POWER(); prev = arg; k_prev = k; } } PUSH_POWER(); SASSERT(new_args.size() > 0); if (new_args.size() == 1) { return new_args[0]; } else { return m().mk_app(get_fid(), mul_decl_kind(), new_args.size(), new_args.c_ptr()); } } else { return m().mk_app(get_fid(), mul_decl_kind(), num_args, args); } } } template expr * poly_rewriter::mk_mul_app(numeral const & c, expr * arg) { if (c.is_one()) { return arg; } else { expr * new_args[2] = { mk_numeral(c), arg }; return mk_mul_app(2, new_args); } } template br_status poly_rewriter::mk_flat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args >= 2); // only try to apply flattening if it is not already in one of the flat monomial forms // - (* c x) // - (* c (* x_1 ... x_n)) if (num_args != 2 || !is_numeral(args[0]) || (is_mul(args[1]) && is_numeral(to_app(args[1])->get_arg(0)))) { unsigned i; for (i = 0; i < num_args; i++) { if (is_mul(args[i])) break; } if (i < num_args) { // input has nested monomials. ptr_buffer flat_args; // we need the todo buffer to handle: (* (* c (* x_1 ... x_n)) (* d (* y_1 ... y_n))) ptr_buffer todo; flat_args.append(i, args); for (unsigned j = i; j < num_args; j++) { if (is_mul(args[j])) { todo.push_back(args[j]); while (!todo.empty()) { expr * curr = todo.back(); todo.pop_back(); if (is_mul(curr)) { unsigned k = to_app(curr)->get_num_args(); while (k > 0) { --k; todo.push_back(to_app(curr)->get_arg(k)); } } else { flat_args.push_back(curr); } } } else { flat_args.push_back(args[j]); } } TRACE("poly_rewriter", tout << "flat mul:\n"; for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], m()) << "\n"; tout << "---->\n"; for (unsigned i = 0; i < flat_args.size(); i++) tout << mk_bounded_pp(flat_args[i], m()) << "\n";); br_status st = mk_nflat_mul_core(flat_args.size(), flat_args.c_ptr(), result); if (st == BR_FAILED) { result = mk_mul_app(flat_args.size(), flat_args.c_ptr()); return BR_DONE; } return st; } } return mk_nflat_mul_core(num_args, args, result); } template struct poly_rewriter::mon_pw_lt { poly_rewriter & m_owner; mon_pw_lt(poly_rewriter & o):m_owner(o) {} bool operator()(expr * n1, expr * n2) const { rational k; return lt(m_owner.get_power_body(n1, k), m_owner.get_power_body(n2, k)); } }; template br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args >= 2); // cheap case numeral a; if (num_args == 2 && is_numeral(args[0], a) && !a.is_one() && !a.is_zero() && (is_var(args[1]) || to_app(args[1])->get_decl()->get_family_id() != get_fid())) return BR_FAILED; numeral c(1); unsigned num_coeffs = 0; unsigned num_add = 0; expr * var = 0; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (is_numeral(arg, a)) { num_coeffs++; c *= a; } else { var = arg; if (is_add(arg)) num_add++; } } normalize(c); // (* c_1 ... c_n) --> c_1*...*c_n if (num_coeffs == num_args) { result = mk_numeral(c); return BR_DONE; } // (* s ... 0 ... r) --> 0 if (c.is_zero()) { result = mk_numeral(c); return BR_DONE; } if (num_coeffs == num_args - 1) { SASSERT(var != 0); // (* c_1 ... c_n x) --> x if c_1*...*c_n == 1 if (c.is_one()) { result = var; return BR_DONE; } numeral c_prime; if (is_mul(var)) { // apply basic simplification even when flattening is not enabled. // (* c1 (* c2 x)) --> (* c1*c2 x) if (to_app(var)->get_num_args() == 2 && is_numeral(to_app(var)->get_arg(0), c_prime)) { c *= c_prime; normalize(c); result = mk_mul_app(c, to_app(var)->get_arg(1)); return BR_REWRITE1; } else { // var is a power-product return BR_FAILED; } } if (num_add == 0 || m_hoist_cmul) { SASSERT(!is_add(var) || m_hoist_cmul); if (num_args == 2 && args[1] == var) { DEBUG_CODE({ numeral c_prime; SASSERT(is_numeral(args[0], c_prime) && c == c_prime); }); // it is already simplified return BR_FAILED; } // (* c_1 ... c_n x) --> (* c_1*...*c_n x) result = mk_mul_app(c, var); return BR_DONE; } else { SASSERT(is_add(var)); // (* c_1 ... c_n (+ t_1 ... t_m)) --> (+ (* c_1*...*c_n t_1) ... (* c_1*...*c_n t_m)) ptr_buffer new_add_args; unsigned num = to_app(var)->get_num_args(); for (unsigned i = 0; i < num; i++) { new_add_args.push_back(mk_mul_app(c, to_app(var)->get_arg(i))); } result = mk_add_app(new_add_args.size(), new_add_args.c_ptr()); TRACE("mul_bug", tout << "result: " << mk_bounded_pp(result, m(),5) << "\n";); return BR_REWRITE2; } } SASSERT(num_coeffs <= num_args - 2); if (!m_som || num_add == 0) { ptr_buffer new_args; expr * prev = 0; bool ordered = true; for (unsigned i = 0; i < num_args; i++) { expr * curr = args[i]; if (is_numeral(curr)) continue; if (prev != 0 && lt(curr, prev)) ordered = false; new_args.push_back(curr); prev = curr; } TRACE("poly_rewriter", for (unsigned i = 0; i < new_args.size(); i++) { if (i > 0) tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); tout << mk_ismt2_pp(new_args[i], m()); } tout << "\nordered: " << ordered << "\n";); if (ordered && num_coeffs == 0 && !use_power()) return BR_FAILED; if (!ordered) { if (use_power()) std::sort(new_args.begin(), new_args.end(), mon_pw_lt(*this)); else std::sort(new_args.begin(), new_args.end(), ast_to_lt()); TRACE("poly_rewriter", tout << "after sorting:\n"; for (unsigned i = 0; i < new_args.size(); i++) { if (i > 0) tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); tout << mk_ismt2_pp(new_args[i], m()); } tout << "\n";); } SASSERT(new_args.size() >= 2); result = mk_mul_app(new_args.size(), new_args.c_ptr()); result = mk_mul_app(c, result); TRACE("poly_rewriter", tout << "mk_nflat_mul_core result:\n" << mk_ismt2_pp(result, m()) << "\n";); return BR_DONE; } SASSERT(m_som && num_add > 0); sbuffer szs; sbuffer it; sbuffer sums; for (unsigned i = 0; i < num_args; i ++) { it.push_back(0); expr * arg = args[i]; if (is_add(arg)) { sums.push_back(const_cast(to_app(arg)->get_args())); szs.push_back(to_app(arg)->get_num_args()); } else { sums.push_back(const_cast(args + i)); szs.push_back(1); SASSERT(sums.back()[0] == arg); } } expr_ref_buffer sum(m()); // must be ref_buffer because we may throw an exception ptr_buffer m_args; TRACE("som", tout << "starting som...\n";); do { TRACE("som", for (unsigned i = 0; i < it.size(); i++) tout << it[i] << " "; tout << "\n";); if (sum.size() > m_som_blowup) throw rewriter_exception("sum of monomials blowup"); m_args.reset(); for (unsigned i = 0; i < num_args; i++) { expr * const * v = sums[i]; expr * arg = v[it[i]]; m_args.push_back(arg); } sum.push_back(mk_mul_app(m_args.size(), m_args.c_ptr())); } while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr())); result = mk_add_app(sum.size(), sum.c_ptr()); return BR_REWRITE2; } template br_status poly_rewriter::mk_flat_add_core(unsigned num_args, expr * const * args, expr_ref & result) { unsigned i; for (i = 0; i < num_args; i++) { if (is_add(args[i])) break; } if (i < num_args) { // has nested ADDs ptr_buffer flat_args; flat_args.append(i, args); for (; i < num_args; i++) { expr * arg = args[i]; // Remark: all rewrites are depth 1. if (is_add(arg)) { unsigned num = to_app(arg)->get_num_args(); for (unsigned j = 0; j < num; j++) flat_args.push_back(to_app(arg)->get_arg(j)); } else { flat_args.push_back(arg); } } br_status st = mk_nflat_add_core(flat_args.size(), flat_args.c_ptr(), result); if (st == BR_FAILED) { result = mk_add_app(flat_args.size(), flat_args.c_ptr()); return BR_DONE; } return st; } return mk_nflat_add_core(num_args, args, result); } template inline expr * poly_rewriter::get_power_product(expr * t) { if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0))) return to_app(t)->get_arg(1); return t; } template inline expr * poly_rewriter::get_power_product(expr * t, numeral & a) { if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0), a)) return to_app(t)->get_arg(1); a = numeral(1); return t; } template bool poly_rewriter::is_mul(expr * t, numeral & c, expr * & pp) { if (!is_mul(t) || to_app(t)->get_num_args() != 2) return false; if (!is_numeral(to_app(t)->get_arg(0), c)) return false; pp = to_app(t)->get_arg(1); return true; } template struct poly_rewriter::hoist_cmul_lt { poly_rewriter & m_r; hoist_cmul_lt(poly_rewriter & r):m_r(r) {} bool operator()(expr * t1, expr * t2) const { expr * pp1, * pp2; numeral c1, c2; bool is_mul1 = m_r.is_mul(t1, c1, pp1); bool is_mul2 = m_r.is_mul(t2, c2, pp2); if (!is_mul1 && is_mul2) return true; if (is_mul1 && !is_mul2) return false; if (!is_mul1 && !is_mul2) return t1->get_id() < t2->get_id(); if (c1 < c2) return true; if (c1 > c2) return false; return pp1->get_id() < pp2->get_id(); } }; template void poly_rewriter::hoist_cmul(expr_ref_buffer & args) { unsigned sz = args.size(); std::sort(args.c_ptr(), args.c_ptr() + sz, hoist_cmul_lt(*this)); numeral c, c_prime; ptr_buffer pps; expr * pp, * pp_prime; unsigned j = 0; unsigned i = 0; while (i < sz) { expr * mon = args[i]; if (is_mul(mon, c, pp) && i < sz - 1) { expr * mon_prime = args[i+1]; if (is_mul(mon_prime, c_prime, pp_prime) && c == c_prime) { // found target pps.reset(); pps.push_back(pp); pps.push_back(pp_prime); i += 2; while (i < sz && is_mul(args[i], c_prime, pp_prime) && c == c_prime) { pps.push_back(pp_prime); i++; } SASSERT(is_numeral(to_app(mon)->get_arg(0), c_prime) && c == c_prime); expr * mul_args[2] = { to_app(mon)->get_arg(0), mk_add_app(pps.size(), pps.c_ptr()) }; args.set(j, mk_mul_app(2, mul_args)); j++; continue; } } args.set(j, mon); j++; i++; } args.resize(j); } template br_status poly_rewriter::mk_nflat_add_core(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args >= 2); numeral c; unsigned num_coeffs = 0; numeral a; expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in args expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once bool has_multiple = false; expr * prev = 0; bool ordered = true; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (is_numeral(arg, a)) { num_coeffs++; c += a; } else { // arg is not a numeral if (m_sort_sums && ordered) { if (prev != 0 && lt(arg, prev)) ordered = false; prev = arg; } } arg = get_power_product(arg); if (visited.is_marked(arg)) { multiple.mark(arg); has_multiple = true; } else { visited.mark(arg); } } normalize(c); SASSERT(m_sort_sums || ordered); TRACE("sort_sums", tout << "ordered: " << ordered << "\n"; for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";); if (has_multiple) { // expensive case buffer coeffs; m_expr2pos.reset(); // compute the coefficient of power products that occur multiple times. for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (is_numeral(arg)) continue; expr * pp = get_power_product(arg, a); if (!multiple.is_marked(pp)) continue; unsigned pos; if (m_expr2pos.find(pp, pos)) { coeffs[pos] += a; } else { m_expr2pos.insert(pp, coeffs.size()); coeffs.push_back(a); } } expr_ref_buffer new_args(m()); if (!c.is_zero()) { new_args.push_back(mk_numeral(c)); } // copy power products with non zero coefficients to new_args visited.reset(); for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (is_numeral(arg)) continue; expr * pp = get_power_product(arg); if (!multiple.is_marked(pp)) { new_args.push_back(arg); } else if (!visited.is_marked(pp)) { visited.mark(pp); unsigned pos = UINT_MAX; m_expr2pos.find(pp, pos); SASSERT(pos != UINT_MAX); a = coeffs[pos]; normalize(a); if (!a.is_zero()) new_args.push_back(mk_mul_app(a, pp)); } } if (m_hoist_cmul) { hoist_cmul(new_args); } else if (m_sort_sums) { TRACE("sort_sums_bug", tout << "new_args.size(): " << new_args.size() << "\n";); if (c.is_zero()) std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt()); else std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt()); } result = mk_add_app(new_args.size(), new_args.c_ptr()); if (hoist_multiplication(result)) { return BR_REWRITE_FULL; } return BR_DONE; } else { SASSERT(!has_multiple); if (ordered && !m_hoist_mul && !m_hoist_cmul) { if (num_coeffs == 0) return BR_FAILED; if (num_coeffs == 1 && is_numeral(args[0], a) && !a.is_zero()) return BR_FAILED; } expr_ref_buffer new_args(m()); if (!c.is_zero()) new_args.push_back(mk_numeral(c)); for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (is_numeral(arg)) continue; new_args.push_back(arg); } if (m_hoist_cmul) { hoist_cmul(new_args); } else if (!ordered) { if (c.is_zero()) std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt()); else std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt()); } result = mk_add_app(new_args.size(), new_args.c_ptr()); if (hoist_multiplication(result)) { return BR_REWRITE_FULL; } return BR_DONE; } } template br_status poly_rewriter::mk_uminus(expr * arg, expr_ref & result) { numeral a; set_curr_sort(m().get_sort(arg)); if (is_numeral(arg, a)) { a.neg(); normalize(a); result = mk_numeral(a); return BR_DONE; } else { result = mk_mul_app(numeral(-1), arg); return BR_REWRITE1; } } template br_status poly_rewriter::mk_sub(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args > 0); if (num_args == 1) { result = args[0]; return BR_DONE; } set_curr_sort(m().get_sort(args[0])); expr * minus_one = mk_numeral(numeral(-1)); ptr_buffer new_args; new_args.push_back(args[0]); for (unsigned i = 1; i < num_args; i++) { expr * aux_args[2] = { minus_one, args[i] }; new_args.push_back(mk_mul_app(2, aux_args)); } result = mk_add_app(new_args.size(), new_args.c_ptr()); return BR_REWRITE2; } /** \brief Cancel/Combine monomials that occur is the left and right hand sides. \remark If move = true, then all non-constant monomials are moved to the left-hand-side. */ template br_status poly_rewriter::cancel_monomials(expr * lhs, expr * rhs, bool move, expr_ref & lhs_result, expr_ref & rhs_result) { set_curr_sort(m().get_sort(lhs)); unsigned lhs_sz; expr * const * lhs_monomials = get_monomials(lhs, lhs_sz); unsigned rhs_sz; expr * const * rhs_monomials = get_monomials(rhs, rhs_sz); expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in lhs or rhs expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once bool has_multiple = false; numeral c(0); numeral a; unsigned num_coeffs = 0; for (unsigned i = 0; i < lhs_sz; i++) { expr * arg = lhs_monomials[i]; if (is_numeral(arg, a)) { c += a; num_coeffs++; } else { visited.mark(get_power_product(arg)); } } if (move && num_coeffs == 0 && is_numeral(rhs)) return BR_FAILED; for (unsigned i = 0; i < rhs_sz; i++) { expr * arg = rhs_monomials[i]; if (is_numeral(arg, a)) { c -= a; num_coeffs++; } else { expr * pp = get_power_product(arg); if (visited.is_marked(pp)) { multiple.mark(pp); has_multiple = true; } } } normalize(c); if (!has_multiple && num_coeffs <= 1) { if (move) { if (is_numeral(rhs)) return BR_FAILED; } else { if (num_coeffs == 0 || is_numeral(rhs)) return BR_FAILED; } } buffer coeffs; m_expr2pos.reset(); for (unsigned i = 0; i < lhs_sz; i++) { expr * arg = lhs_monomials[i]; if (is_numeral(arg)) continue; expr * pp = get_power_product(arg, a); if (!multiple.is_marked(pp)) continue; unsigned pos; if (m_expr2pos.find(pp, pos)) { coeffs[pos] += a; } else { m_expr2pos.insert(pp, coeffs.size()); coeffs.push_back(a); } } for (unsigned i = 0; i < rhs_sz; i++) { expr * arg = rhs_monomials[i]; if (is_numeral(arg)) continue; expr * pp = get_power_product(arg, a); if (!multiple.is_marked(pp)) continue; unsigned pos = UINT_MAX; m_expr2pos.find(pp, pos); SASSERT(pos != UINT_MAX); coeffs[pos] -= a; } ptr_buffer new_lhs_monomials; new_lhs_monomials.push_back(0); // save space for coefficient if needed // copy power products with non zero coefficients to new_lhs_monomials visited.reset(); for (unsigned i = 0; i < lhs_sz; i++) { expr * arg = lhs_monomials[i]; if (is_numeral(arg)) continue; expr * pp = get_power_product(arg); if (!multiple.is_marked(pp)) { new_lhs_monomials.push_back(arg); } else if (!visited.is_marked(pp)) { visited.mark(pp); unsigned pos = UINT_MAX; m_expr2pos.find(pp, pos); SASSERT(pos != UINT_MAX); a = coeffs[pos]; if (!a.is_zero()) new_lhs_monomials.push_back(mk_mul_app(a, pp)); } } ptr_buffer new_rhs_monomials; new_rhs_monomials.push_back(0); // save space for coefficient if needed for (unsigned i = 0; i < rhs_sz; i++) { expr * arg = rhs_monomials[i]; if (is_numeral(arg)) continue; expr * pp = get_power_product(arg, a); if (!multiple.is_marked(pp)) { if (move) { if (!a.is_zero()) { if (a.is_minus_one()) { new_lhs_monomials.push_back(pp); } else { a.neg(); SASSERT(!a.is_one()); expr * args[2] = { mk_numeral(a), pp }; new_lhs_monomials.push_back(mk_mul_app(2, args)); } } } else { new_rhs_monomials.push_back(arg); } } } bool c_at_rhs = false; if (move) { if (m_sort_sums) { // + 1 to skip coefficient std::sort(new_lhs_monomials.begin() + 1, new_lhs_monomials.end(), ast_to_lt()); } c_at_rhs = true; } else if (new_rhs_monomials.size() == 1) { // rhs is empty c_at_rhs = true; } else if (new_lhs_monomials.size() > 1) { c_at_rhs = true; } if (c_at_rhs) { c.neg(); normalize(c); new_rhs_monomials[0] = mk_numeral(c); lhs_result = mk_add_app(new_lhs_monomials.size() - 1, new_lhs_monomials.c_ptr() + 1); rhs_result = mk_add_app(new_rhs_monomials.size(), new_rhs_monomials.c_ptr()); } else { new_lhs_monomials[0] = mk_numeral(c); lhs_result = mk_add_app(new_lhs_monomials.size(), new_lhs_monomials.c_ptr()); rhs_result = mk_add_app(new_rhs_monomials.size() - 1, new_rhs_monomials.c_ptr() + 1); } return BR_DONE; } #define TO_BUFFER(_tester_, _buffer_, _e_) \ _buffer_.push_back(_e_); \ for (unsigned _i = 0; _i < _buffer_.size(); ) { \ expr* _e = _buffer_[_i]; \ if (_tester_(_e)) { \ app* a = to_app(_e); \ _buffer_[_i] = a->get_arg(0); \ for (unsigned _j = 1; _j < a->get_num_args(); ++_j) { \ _buffer_.push_back(a->get_arg(_j)); \ } \ } \ else { \ ++_i; \ } \ } \ template bool poly_rewriter::hoist_multiplication(expr_ref& som) { if (!m_hoist_mul) { return false; } ptr_buffer adds, muls; TO_BUFFER(is_add, adds, som); buffer valid(adds.size(), true); obj_map mul_map; unsigned j; bool change = false; for (unsigned k = 0; k < adds.size(); ++k) { expr* e = adds[k]; muls.reset(); TO_BUFFER(is_mul, muls, e); for (unsigned i = 0; i < muls.size(); ++i) { e = muls[i]; if (is_numeral(e)) { continue; } if (mul_map.find(e, j) && valid[j] && j != k) { m_curr_sort = m().get_sort(adds[k]); adds[j] = merge_muls(adds[j], adds[k]); adds[k] = mk_numeral(rational(0)); valid[j] = false; valid[k] = false; change = true; break; } else { mul_map.insert(e, k); } } } if (!change) { return false; } som = mk_add_app(adds.size(), adds.c_ptr()); return true; } template expr* poly_rewriter::merge_muls(expr* x, expr* y) { ptr_buffer m1, m2; TO_BUFFER(is_mul, m1, x); TO_BUFFER(is_mul, m2, y); unsigned k = 0; for (unsigned i = 0; i < m1.size(); ++i) { x = m1[i]; bool found = false; unsigned j; for (j = k; j < m2.size(); ++j) { found = m2[j] == x; if (found) break; } if (found) { std::swap(m1[i],m1[k]); std::swap(m2[j],m2[k]); ++k; } } m_curr_sort = m().get_sort(x); SASSERT(k > 0); SASSERT(m1.size() >= k); SASSERT(m2.size() >= k); expr* args[2] = { mk_mul_app(m1.size()-k, m1.c_ptr()+k), mk_mul_app(m2.size()-k, m2.c_ptr()+k) }; if (k == m1.size()) { m1.push_back(0); } m1[k] = mk_add_app(2, args); return mk_mul_app(k+1, m1.c_ptr()); } z3-4.4.0/src/ast/rewriter/mk_simplified_app.cpp0000644000175000017500000000647212540347414021257 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mk_simplified_app.cpp Abstract: Functor for creating new simplified applications Author: Leonardo (leonardo) 2011-06-06 Notes: --*/ #include"mk_simplified_app.h" #include"bool_rewriter.h" #include"arith_rewriter.h" #include"bv_rewriter.h" #include"datatype_rewriter.h" #include"array_rewriter.h" #include"fpa_rewriter.h" struct mk_simplified_app::imp { ast_manager & m; bool_rewriter m_b_rw; arith_rewriter m_a_rw; bv_rewriter m_bv_rw; array_rewriter m_ar_rw; datatype_rewriter m_dt_rw; fpa_rewriter m_f_rw; imp(ast_manager & _m, params_ref const & p): m(_m), m_b_rw(m, p), m_a_rw(m, p), m_bv_rw(m, p), m_ar_rw(m, p), m_dt_rw(m), m_f_rw(m, p) { } br_status mk_core(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { family_id fid = f->get_family_id(); if (fid == null_family_id) return BR_FAILED; br_status st = BR_FAILED; if (fid == m_b_rw.get_fid()) { decl_kind k = f->get_decl_kind(); if (k == OP_EQ) { // theory dispatch for = SASSERT(num == 2); family_id s_fid = m.get_sort(args[0])->get_family_id(); if (s_fid == m_a_rw.get_fid()) st = m_a_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_bv_rw.get_fid()) st = m_bv_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_dt_rw.get_fid()) st = m_dt_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_f_rw.get_fid()) st = m_f_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_ar_rw.get_fid()) st = m_ar_rw.mk_eq_core(args[0], args[1], result); if (st != BR_FAILED) return st; } return m_b_rw.mk_app_core(f, num, args, result); } if (fid == m_a_rw.get_fid()) return m_a_rw.mk_app_core(f, num, args, result); if (fid == m_bv_rw.get_fid()) return m_bv_rw.mk_app_core(f, num, args, result); if (fid == m_ar_rw.get_fid()) return m_ar_rw.mk_app_core(f, num, args, result); if (fid == m_dt_rw.get_fid()) return m_dt_rw.mk_app_core(f, num, args, result); if (fid == m_f_rw.get_fid()) return m_f_rw.mk_app_core(f, num, args, result); return BR_FAILED; } }; mk_simplified_app::mk_simplified_app(ast_manager & m, params_ref const & p): m_imp(alloc(imp, m, p)) { } mk_simplified_app::~mk_simplified_app() { dealloc(m_imp); } br_status mk_simplified_app::mk_core(func_decl * decl, unsigned num, expr * const * args, expr_ref & result) { return m_imp->mk_core(decl, num, args, result); } void mk_simplified_app::operator()(func_decl * decl, unsigned num, expr * const * args, expr_ref & result) { result = 0; mk_core(decl, num, args, result); if (!result) result = m_imp->m.mk_app(decl, num, args); // TODO: if the result of mk_core is different from BR_FAILED, then the // result is not really simplified. } z3-4.4.0/src/ast/rewriter/dl_rewriter.h0000644000175000017500000000113612540347414017562 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dl_rewriter.h Abstract: Basic rewriting rules for atalog finite domains. Author: Nikolaj Bjorner (nbjorner) 2012-03-09 Notes: --*/ #ifndef _DL_REWRITER_H_ #define _DL_REWRITER_H_ #include"dl_decl_plugin.h" #include"rewriter_types.h" class dl_rewriter { datalog::dl_decl_util m_util; public: dl_rewriter(ast_manager & m):m_util(m) {} family_id get_fid() const { return m_util.get_family_id(); } br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); }; #endif z3-4.4.0/src/ast/rewriter/pb_rewriter_def.h0000644000175000017500000002050412540347414020402 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: pb_rewriter_def.h Abstract: Basic rewriting rules for PB constraints. Author: Nikolaj Bjorner (nbjorner) 2013-14-12 Notes: --*/ #ifndef _PB_REWRITER_DEF_H_ #define _PB_REWRITER_DEF_H_ #include"pb_rewriter.h" template void pb_rewriter_util::display(std::ostream& out, typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) { for (unsigned i = 0; i < args.size(); ++i) { out << args[i].second << " * "; m_util.display(out, args[i].first); out << " "; if (i+1 < args.size()) out << "+ "; } out << (is_eq?" = ":" >= ") << k << "\n"; } template void pb_rewriter_util::unique(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) { TRACE("pb_verbose", display(tout << "pre-unique:", args, k, is_eq);); for (unsigned i = 0; i < args.size(); ++i) { if (m_util.is_negated(args[i].first)) { args[i].first = m_util.negate(args[i].first); k -= args[i].second; args[i].second = -args[i].second; } } // remove constants for (unsigned i = 0; i < args.size(); ++i) { if (m_util.is_true(args[i].first)) { k -= args[i].second; std::swap(args[i], args[args.size()-1]); args.pop_back(); --i; } else if (m_util.is_false(args[i].first)) { std::swap(args[i], args[args.size()-1]); args.pop_back(); --i; } } // sort and coalesce arguments: typename PBU::compare cmp; std::sort(args.begin(), args.end(), cmp); // coallesce unsigned i, j; for (i = 0, j = 1; j < args.size(); ++j) { if (args[i].first == args[j].first) { args[i].second += args[j].second; } else { ++i; args[i] = args[j]; } } args.resize(i+1); // remove 0s. for (i = 0, j = 0; j < args.size(); ++j) { if (!args[j].second.is_zero()) { if (i != j) { args[i] = args[j]; } ++i; } } args.resize(i); TRACE("pb_verbose", display(tout << "post-unique:", args, k, is_eq);); } template lbool pb_rewriter_util::normalize(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) { TRACE("pb_verbose", display(tout << "pre-normalize:", args, k, is_eq);); DEBUG_CODE( bool found = false; for (unsigned i = 0; !found && i < args.size(); ++i) { found = args[i].second.is_zero(); } if (found) display(verbose_stream(), args, k, is_eq); SASSERT(!found);); // // Ensure all coefficients are positive: // c*l + y >= k // <=> // c*(1-~l) + y >= k // <=> // c - c*~l + y >= k // <=> // -c*~l + y >= k - c // typename PBU::numeral sum(0); for (unsigned i = 0; i < args.size(); ++i) { typename PBU::numeral c = args[i].second; if (c.is_neg()) { args[i].second = -c; args[i].first = m_util.negate(args[i].first); k -= c; } sum += args[i].second; } // detect tautologies: if (!is_eq && k <= PBU::numeral::zero()) { args.reset(); k = PBU::numeral::zero(); return l_true; } if (is_eq && k.is_zero() && args.empty()) { return l_true; } // detect infeasible constraints: if (sum < k) { args.reset(); k = PBU::numeral::one(); return l_false; } if (is_eq && k == sum) { for (unsigned i = 0; i < args.size(); ++i) { args[i].second = PBU::numeral::one(); } typename PBU::numeral num(args.size()); k = num; return l_undef; } bool all_int = true; for (unsigned i = 0; all_int && i < args.size(); ++i) { all_int = args[i].second.is_int(); } if (!all_int) { // normalize to integers. typename PBU::numeral d(denominator(k)); for (unsigned i = 0; i < args.size(); ++i) { d = lcm(d, denominator(args[i].second)); } SASSERT(!d.is_one()); k *= d; for (unsigned i = 0; i < args.size(); ++i) { args[i].second *= d; } } if (is_eq) { TRACE("pb_verbose", display(tout << "post-normalize:", args, k, is_eq);); return l_undef; } // Ensure the largest coefficient is not larger than k: sum = PBU::numeral::zero(); for (unsigned i = 0; i < args.size(); ++i) { typename PBU::numeral c = args[i].second; if (c > k) { args[i].second = k; } sum += args[i].second; } SASSERT(!args.empty()); // normalize tight inequalities to unit coefficients. if (sum == k) { for (unsigned i = 0; i < args.size(); ++i) { args[i].second = PBU::numeral::one(); } typename PBU::numeral num(args.size()); k = num; } // apply cutting plane reduction: typename PBU::numeral g(0); for (unsigned i = 0; !g.is_one() && i < args.size(); ++i) { typename PBU::numeral c = args[i].second; if (c != k) { if (g.is_zero()) { g = c; } else { g = gcd(g, c); } } } if (g.is_zero()) { // all coefficients are equal to k. for (unsigned i = 0; i < args.size(); ++i) { SASSERT(args[i].second == k); args[i].second = PBU::numeral::one(); } k = PBU::numeral::one(); } else if (g > PBU::numeral::one()) { // // Example 5x + 5y + 2z + 2u >= 5 // becomes 3x + 3y + z + u >= 3 // typename PBU::numeral k_new = div(k, g); if (!(k % g).is_zero()) { // k_new is the ceiling of k / g. k_new++; } for (unsigned i = 0; i < args.size(); ++i) { SASSERT(args[i].second.is_pos()); typename PBU::numeral c = args[i].second; if (c == k) { c = k_new; } else { c = div(c, g); } args[i].second = c; SASSERT(args[i].second.is_pos()); } k = k_new; } // // normalize coefficients that fall within a range // k/n <= ... < k/(n-1) for some n = 1,2,... // // e.g, k/n <= min <= max < k/(n-1) // k/min <= n, n-1 < k/max // . floor(k/max) = ceil(k/min) - 1 // . floor(k/max) < k/max // // example: k = 5, min = 3, max = 4: 5/3 -> 2 5/4 -> 1, n = 2 // replace all coefficients by 1, and k by 2. // if (!k.is_one()) { typename PBU::numeral min = args[0].second, max = args[0].second; for (unsigned i = 1; i < args.size(); ++i) { if (args[i].second < min) min = args[i].second; if (args[i].second > max) max = args[i].second; } SASSERT(min.is_pos()); typename PBU::numeral n0 = k/max; typename PBU::numeral n1 = floor(n0); typename PBU::numeral n2 = ceil(k/min) - PBU::numeral::one(); if (n1 == n2 && !n0.is_int()) { IF_VERBOSE(3, display(verbose_stream() << "set cardinality\n", args, k, is_eq);); for (unsigned i = 0; i < args.size(); ++i) { args[i].second = PBU::numeral::one(); } k = n1 + PBU::numeral::one(); } } TRACE("pb_verbose", display(tout << "post-normalize:", args, k, is_eq);); return l_undef; } template void pb_rewriter_util::prune(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) { if (is_eq) { return; } typename PBU::numeral nlt(0); unsigned occ = 0; for (unsigned i = 0; nlt < k && i < args.size(); ++i) { if (args[i].second < k) { nlt += args[i].second; ++occ; } } if (0 < occ && nlt < k) { for (unsigned i = 0; i < args.size(); ++i) { if (args[i].second < k) { args[i] = args.back(); args.pop_back(); --i; } } unique(args, k, is_eq); normalize(args, k, is_eq); } } #endif z3-4.4.0/src/ast/rewriter/bv_rewriter_params.pyg0000644000175000017500000000215012540347414021502 0ustar michaelmichaeldef_module_params(module_name='rewriter', class_name='bv_rewriter_params', export=True, params=(("udiv2mul", BOOL, False, "convert constant udiv to mul"), ("split_concat_eq", BOOL, False, "split equalities of the form (= (concat t1 t2) t3)"), ("bit2bool", BOOL, True, "try to convert bit-vector terms of size 1 into Boolean terms"), ("blast_eq_value", BOOL, False, "blast (some) Bit-vector equalities into bits"), ("elim_sign_ext", BOOL, True, "expand sign-ext operator using concat and extract"), ("hi_div0", BOOL, True, "use the 'hardware interpretation' for division by zero (for bit-vector terms)"), ("mul2concat", BOOL, False, "replace multiplication by a power of two into a concatenation"), ("bvnot2arith", BOOL, False, "replace (bvnot x) with (bvsub -1 x)"), ("bv_sort_ac", BOOL, False, "sort the arguments of all AC operators") )) z3-4.4.0/src/ast/rewriter/rewriter.h0000644000175000017500000003424512540347414017112 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: rewriter.h Abstract: Lean and mean rewriter Author: Leonardo (leonardo) 2011-03-31 Notes: --*/ #ifndef _REWRITER_H_ #define _REWRITER_H_ #include"ast.h" #include"rewriter_types.h" #include"act_cache.h" /** \brief Common infrastructure for AST rewriters. */ class rewriter_core { protected: struct frame { expr * m_curr; unsigned m_cache_result:1; // true if the result of rewriting m_expr must be cached. unsigned m_new_child:1; unsigned m_state:2; unsigned m_max_depth:2; // bounded rewrite... if m_max_depth == 0, then children are not rewritten. unsigned m_i:26; unsigned m_spos; // top of the result stack, when the frame was created. frame(expr * n, bool cache_res, unsigned st, unsigned max_depth, unsigned spos): m_curr(n), m_cache_result(cache_res), m_new_child(false), m_state(st), m_max_depth(max_depth), m_i(0), m_spos(spos) { } }; ast_manager & m_manager; bool m_proof_gen; typedef act_cache cache; ptr_vector m_cache_stack; cache * m_cache; // current cache. svector m_frame_stack; expr_ref_vector m_result_stack; // proof generation goodness ---- ptr_vector m_cache_pr_stack; cache * m_cache_pr; proof_ref_vector m_result_pr_stack; // -------------------------- expr * m_root; unsigned m_num_qvars; struct scope { expr * m_old_root; unsigned m_old_num_qvars; scope(expr * r, unsigned n):m_old_root(r), m_old_num_qvars(n) {} }; svector m_scopes; // Return true if the rewriting result of the given expression must be cached. bool must_cache(expr * t) const { return t->get_ref_count() > 1 && // t must be a shared expression t != m_root && // t must not be the root expression ((is_app(t) && to_app(t)->get_num_args() > 0) || is_quantifier(t)); // t is a (non-constant) application or a quantifier. } void push_frame_core(expr * t, bool cache_res, unsigned st = 0, unsigned max_depth = RW_UNBOUNDED_DEPTH) { SASSERT(!m_proof_gen || m_result_stack.size() == m_result_pr_stack.size()); m_frame_stack.push_back(frame(t, cache_res, st, max_depth, m_result_stack.size())); } void push_frame(expr * t, unsigned st = 0) { push_frame_core(t, must_cache(t), st); } void init_cache_stack(); void del_cache_stack(); void reset_cache(); void cache_result(expr * k, expr * v); expr * get_cached(expr * k) const { return m_cache->find(k); } void cache_result(expr * k, expr * v, proof * pr); proof * get_cached_pr(expr * k) const { return static_cast(m_cache_pr->find(k)); } void free_memory(); void begin_scope(); void end_scope(); bool is_child_of_top_frame(expr * t) const; void set_new_child_flag(expr * old_t) { CTRACE("rewriter_bug", !is_child_of_top_frame(old_t), display_stack(tout, 3);); SASSERT(is_child_of_top_frame(old_t)); if (!m_frame_stack.empty()) m_frame_stack.back().m_new_child = true; } void set_new_child_flag(expr * old_t, expr * new_t) { if (old_t != new_t) set_new_child_flag(old_t); } void elim_reflex_prs(unsigned spos); public: rewriter_core(ast_manager & m, bool proof_gen); ~rewriter_core(); ast_manager & m() const { return m_manager; } void reset(); void cleanup(); #ifdef _TRACE void display_stack(std::ostream & out, unsigned pp_depth); #endif unsigned get_cache_size() const; }; class var_shifter_core : public rewriter_core { protected: bool visit(expr * t); void process_app(app * t, frame & fr); virtual void process_var(var * v) = 0; void process_quantifier(quantifier * q, frame & fr); void main_loop(expr * t, expr_ref & r); public: var_shifter_core(ast_manager & m):rewriter_core(m, false) {} }; /** \brief Functor used to shift the free variables of an AST by a given amount. This functor is used by the var_subst functor. This functor implements the following functions: 1) shift(n, s) will return a new AST where all free variables (VAR i) in n, are replaced by (VAR (+ i s)). 2) shift(n, b, s1, s2) is a variant of the previous function such that for a each free variable (VAR i) is shifted to: - (VAR i + s1) if i >= b - (VAR i + s2) if i < b */ class var_shifter : public var_shifter_core { unsigned m_bound; unsigned m_shift1; unsigned m_shift2; virtual void process_var(var * v); public: var_shifter(ast_manager & m):var_shifter_core(m) {} void operator()(expr * t, unsigned bound, unsigned shift1, unsigned shift2, expr_ref & r); void operator()(expr * t, unsigned s, expr_ref & r) { operator()(t, 0, s, 0, r); } }; /** \brief Functor used to shift the free variables of an AST by a negative amount. Abstract implementation: inv_shift(f(c_1, ..., c_n), d, s) = f(inv_shift(c_1, d, s), ..., inv_shift(c_n, d, s)) inv_shift(var(i), d, s) = if (i < d) var(i) else assert(i - s >= d) var(i-s) inv_shift((forall (x) P), d, s) = (forall (x) inv_shift(P, d+1, s)) This functor assumes that if we are shifting an expression F by N, then F does not contain free variables #0, ... #N-1 See assertion above. */ class inv_var_shifter : public var_shifter_core { protected: unsigned m_shift; virtual void process_var(var * v); public: inv_var_shifter(ast_manager & m):var_shifter_core(m) {} void operator()(expr * t, unsigned shift, expr_ref & r); }; template class rewriter_tpl : public rewriter_core { protected: // Rewriter maintains a stack of frames. // Each frame represents an expression that is being rewritten. // The resultant expressions are store on the Result stack. // Each frame is in a particular state. // Let f(t_0,...,t_n) be the expression is the frame Fr. Then Fr can be is one of the // following states: // PROCESS_CHILDREN(i) the children t_0, ..., t_{i-1} have already been rewritten, and the result is on the Result stack. // REWRITE_BUILTIN All t_0, ..., t_n have been rewritten to t_0',...,t_n', and the builtin rewriter (or plugin) produced a new term t // that can be further rewritten. // EXPAND_DEF All t_0, ..., t_n have been rewritten to t_0',...,t_n'. // The function symbol f is a macro, and the body of the macro needs to be rewritten using the t_0',...,t_n' as bindings. // REWRITE_RULE All t_0, ..., t_n have been rewritten to t_0',...,t_n'. // There is rewrite rule lhs -> rhs s.t. f(t_0', ..., t_n') matches lhs with substitution delta. // rhs is then rewritten using delta. enum state { PROCESS_CHILDREN, REWRITE_BUILTIN, EXPAND_DEF, REWRITE_RULE }; Config & m_cfg; unsigned m_num_steps; volatile bool m_cancel; ptr_vector m_bindings; var_shifter m_shifter; expr_ref m_r; proof_ref m_pr; proof_ref m_pr2; svector & frame_stack() { return this->m_frame_stack; } svector const & frame_stack() const { return this->m_frame_stack; } expr_ref_vector & result_stack() { return this->m_result_stack; } expr_ref_vector const & result_stack() const { return this->m_result_stack; } proof_ref_vector & result_pr_stack() { return this->m_result_pr_stack; } proof_ref_vector const & result_pr_stack() const { return this->m_result_pr_stack; } void set_new_child_flag(expr * old_t) { SASSERT(frame_stack().empty() || frame_stack().back().m_state != PROCESS_CHILDREN || this->is_child_of_top_frame(old_t)); if (!frame_stack().empty()) frame_stack().back().m_new_child = true; } void set_new_child_flag(expr * old_t, expr * new_t) { if (old_t != new_t) set_new_child_flag(old_t); } // cache the result of shared non atomic expressions. bool cache_results() const { return m_cfg.cache_results(); } // cache all results share and non shared expressions non atomic expressions. bool cache_all_results() const { return m_cfg.cache_all_results(); } // flat non shared AC terms bool flat_assoc(func_decl * f) const { return m_cfg.flat_assoc(f); } // rewrite patterns bool rewrite_patterns() const { return m_cfg.rewrite_patterns(); } // check maximum number of scopes void check_max_scopes() const { if (m_cfg.max_scopes_exceeded(this->m_scopes.size())) throw rewriter_exception(Z3_MAX_SCOPES_MSG); } // check maximum size of the frame stack void check_max_frames() const { if (m_cfg.max_frames_exceeded(frame_stack().size())) throw rewriter_exception(Z3_MAX_FRAMES_MSG); } // check maximum number of rewriting steps void check_max_steps() const { if (m_cfg.max_steps_exceeded(m_num_steps)) throw rewriter_exception(Z3_MAX_STEPS_MSG); } // If pre_visit returns false, then t children are not visited/rewritten. // This should be used with care, since it may produce incorrect results // when the rewriter is used to apply variable substitution. bool pre_visit(expr * t) { return m_cfg.pre_visit(t); } // Return true if the rewriting result of the given expression must be cached. bool must_cache(expr * t) const { if (cache_all_results()) return t != this->m_root && ((is_app(t) && to_app(t)->get_num_args() > 0) || is_quantifier(t)); if (cache_results()) return rewriter_core::must_cache(t); return false; } bool get_macro(func_decl * f, expr * & def, quantifier * & q, proof * & def_pr) { return m_cfg.get_macro(f, def, q, def_pr); } void push_frame(expr * t, bool mcache, unsigned max_depth) { check_max_frames(); push_frame_core(t, mcache, PROCESS_CHILDREN, max_depth); } void begin_scope() { check_max_scopes(); rewriter_core::begin_scope(); } template void process_var(var * v); template void process_const(app * t); template bool visit(expr * t, unsigned max_depth); template void cache_result(expr * t, expr * new_t, proof * pr, bool c) { if (c) { if (!ProofGen) rewriter_core::cache_result(t, new_t); else rewriter_core::cache_result(t, new_t, pr); } } template void process_app(app * t, frame & fr); template void process_quantifier(quantifier * q, frame & fr); bool first_visit(frame & fr) const { return fr.m_state == PROCESS_CHILDREN && fr.m_i == 0; } bool not_rewriting() const; template void main_loop(expr * t, expr_ref & result, proof_ref & result_pr); template void resume_core(expr_ref & result, proof_ref & result_pr); public: rewriter_tpl(ast_manager & m, bool proof_gen, Config & cfg); ast_manager & m() const { return this->m_manager; } Config & cfg() { return m_cfg; } Config const & cfg() const { return m_cfg; } void set_cancel(bool f) { m_cancel = f; } void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } ~rewriter_tpl(); void reset(); void cleanup(); void set_bindings(unsigned num_bindings, expr * const * bindings); void set_inv_bindings(unsigned num_bindings, expr * const * bindings); void operator()(expr * t, expr_ref & result, proof_ref & result_pr); void operator()(expr * t, expr_ref & result) { operator()(t, result, m_pr); } void operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result) { SASSERT(!m_proof_gen); reset(); set_inv_bindings(num_bindings, bindings); operator()(n, result); } void resume(expr_ref & result, proof_ref & result_pr); void resume(expr_ref & result) { resume(result, m_pr); } // Return the number of steps performed by the rewriter in the last call to operator(). unsigned get_num_steps() const { return m_num_steps; } }; struct default_rewriter_cfg { bool cache_all_results() const { return false; } bool cache_results() const { return true; } bool flat_assoc(func_decl * f) const { return false; } bool rewrite_patterns() const { return true; } bool max_scopes_exceeded(unsigned num_scopes) const { return false; } bool max_frames_exceeded(unsigned num_frames) const { return false; } bool max_steps_exceeded(unsigned num_steps) const { return false; } bool pre_visit(expr * t) { return true; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { return BR_FAILED; } bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { return false; } bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { return false; } bool get_macro(func_decl * d, expr * & def, quantifier * & q, proof * & def_pr) { return false; } bool get_subst(expr * s, expr * & t, proof * & t_pr) { return false; } void reset() {} void cleanup() {} }; struct beta_reducer_cfg : public default_rewriter_cfg { bool pre_visit(expr * t) { return !is_ground(t); } }; class beta_reducer : public rewriter_tpl { beta_reducer_cfg m_cfg; public: beta_reducer(ast_manager & m): rewriter_tpl(m, false, m_cfg) {} }; #endif z3-4.4.0/src/ast/rewriter/quant_hoist.cpp0000644000175000017500000002225212540347414020133 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: quant_hoist.cpp Abstract: Quantifier hoisting utility. Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: Hoisted from quant_elim. --*/ #include "quant_hoist.h" #include "expr_functors.h" #include "ast_smt_pp.h" #include "bool_rewriter.h" #include "var_subst.h" #include "ast_pp.h" #include "ast_counter.h" #include "expr_safe_replace.h" // // Bring quantifiers of common type into prenex form. // class quantifier_hoister::impl { ast_manager& m; bool_rewriter m_rewriter; public: impl(ast_manager& m) : m(m), m_rewriter(m) {} void operator()(expr* fml, app_ref_vector& vars, bool& is_fa, expr_ref& result) { quantifier_type qt = Q_none_pos; pull_quantifier(fml, qt, vars, result); TRACE("qe_verbose", tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); SASSERT(is_positive(qt)); is_fa = (Q_forall_pos == qt); } void pull_exists(expr* fml, app_ref_vector& vars, expr_ref& result) { quantifier_type qt = Q_exists_pos; pull_quantifier(fml, qt, vars, result); TRACE("qe_verbose", tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); } void pull_quantifier(bool is_forall, expr_ref& fml, app_ref_vector& vars) { quantifier_type qt = is_forall?Q_forall_pos:Q_exists_pos; expr_ref result(m); pull_quantifier(fml, qt, vars, result); TRACE("qe_verbose", tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); fml = result; } void extract_quantifier(quantifier* q, app_ref_vector& vars, expr_ref& result) { unsigned nd = q->get_num_decls(); for (unsigned i = 0; i < nd; ++i) { sort* s = q->get_decl_sort(i); app* a = m.mk_fresh_const("x", s); vars.push_back(a); } expr * const * exprs = (expr* const*) (vars.c_ptr() + vars.size()- nd); instantiate(m, q, exprs, result); } unsigned pull_quantifier(bool is_forall, expr_ref& fml, ptr_vector* sorts, svector* names) { unsigned index = var_counter().get_next_var(fml); while (is_quantifier(fml) && (is_forall == to_quantifier(fml)->is_forall())) { quantifier* q = to_quantifier(fml); index += q->get_num_decls(); if (names) { names->append(q->get_num_decls(), q->get_decl_names()); } if (sorts) { sorts->append(q->get_num_decls(), q->get_decl_sorts()); } fml = q->get_expr(); } if (!has_quantifiers(fml)) { return index; } app_ref_vector vars(m); pull_quantifier(is_forall, fml, vars); if (vars.empty()) { return index; } // replace vars by de-bruijn indices expr_safe_replace rep(m); svector bound_names; ptr_vector bound_sorts; for (unsigned i = 0; i < vars.size(); ++i) { app* v = vars[i].get(); if (names) { bound_names.push_back(v->get_decl()->get_name()); } if (sorts) { bound_sorts.push_back(m.get_sort(v)); } rep.insert(v, m.mk_var(index++, m.get_sort(v))); } if (names && !bound_names.empty()) { bound_names.reverse(); bound_names.append(*names); names->reset(); names->append(bound_names); } if (sorts && !bound_sorts.empty()) { bound_sorts.reverse(); bound_sorts.append(*sorts); sorts->reset(); sorts->append(bound_sorts); } rep(fml); return index; } private: enum quantifier_type { Q_forall_pos = 0x10, Q_exists_pos = 0x20, Q_none_pos = 0x40, Q_forall_neg = 0x11, Q_exists_neg = 0x21, Q_none_neg = 0x41 }; void display(quantifier_type qt, std::ostream& out) { switch(qt) { case Q_forall_pos: out << "Forall+"; break; case Q_exists_pos: out << "Exists+"; break; case Q_none_pos: out << "None+"; break; case Q_forall_neg: out << "Forall-"; break; case Q_exists_neg: out << "Exists-"; break; case Q_none_neg: out << "None-"; break; } } quantifier_type& negate(quantifier_type& qt) { TRACE("qe", display(qt, tout); tout << "\n";); qt = static_cast(qt ^0x1); TRACE("qe", display(qt, tout); tout << "\n";); return qt; } static bool is_negative(quantifier_type qt) { return 0 != (qt & 0x1); } static bool is_positive(quantifier_type qt) { return 0 == (qt & 0x1); } static void set_quantifier_type(quantifier_type& qt, bool is_forall) { switch(qt) { case Q_forall_pos: SASSERT(is_forall); break; case Q_forall_neg: SASSERT(!is_forall); break; case Q_exists_pos: SASSERT(!is_forall); break; case Q_exists_neg: SASSERT(is_forall); break; case Q_none_pos: qt = is_forall?Q_forall_pos:Q_exists_pos; break; case Q_none_neg: qt = is_forall?Q_exists_neg:Q_forall_neg; break; } } bool is_compatible(quantifier_type qt, bool is_forall) { switch(qt) { case Q_forall_pos: return is_forall; case Q_forall_neg: return !is_forall; case Q_exists_pos: return !is_forall; case Q_exists_neg: return is_forall; case Q_none_pos: return true; case Q_none_neg: return true; default: UNREACHABLE(); } return false; } void pull_quantifier(expr* fml, quantifier_type& qt, app_ref_vector& vars, expr_ref& result) { if (!has_quantifiers(fml)) { result = fml; return; } switch(fml->get_kind()) { case AST_APP: { expr_ref_vector args(m); expr_ref tmp(m); unsigned num_args = 0; app* a = to_app(fml); if (m.is_and(fml)) { num_args = a->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { pull_quantifier(a->get_arg(i), qt, vars, tmp); args.push_back(tmp); } m_rewriter.mk_and(args.size(), args.c_ptr(), result); } else if (m.is_or(fml)) { num_args = to_app(fml)->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { pull_quantifier(to_app(fml)->get_arg(i), qt, vars, tmp); args.push_back(tmp); } m_rewriter.mk_or(args.size(), args.c_ptr(), result); } else if (m.is_not(fml)) { pull_quantifier(to_app(fml)->get_arg(0), negate(qt), vars, tmp); negate(qt); result = m.mk_not(tmp); } else if (m.is_implies(fml)) { pull_quantifier(to_app(fml)->get_arg(0), negate(qt), vars, tmp); negate(qt); pull_quantifier(to_app(fml)->get_arg(1), qt, vars, result); result = m.mk_implies(tmp, result); } else if (m.is_ite(fml)) { pull_quantifier(to_app(fml)->get_arg(1), qt, vars, tmp); pull_quantifier(to_app(fml)->get_arg(2), qt, vars, result); result = m.mk_ite(to_app(fml)->get_arg(0), tmp, result); } else { // the formula contains a quantifier, but it is "inaccessible" result = fml; } break; } case AST_QUANTIFIER: { quantifier* q = to_quantifier(fml); expr_ref tmp(m); if (!is_compatible(qt, q->is_forall())) { result = fml; break; } set_quantifier_type(qt, q->is_forall()); extract_quantifier(q, vars, tmp); pull_quantifier(tmp, qt, vars, result); break; } case AST_VAR: result = fml; break; default: UNREACHABLE(); result = fml; break; } } }; quantifier_hoister::quantifier_hoister(ast_manager& m) { m_impl = alloc(impl, m); } quantifier_hoister::~quantifier_hoister() { dealloc(m_impl); } void quantifier_hoister::operator()(expr* fml, app_ref_vector& vars, bool& is_fa, expr_ref& result) { (*m_impl)(fml, vars, is_fa, result); } void quantifier_hoister::pull_exists(expr* fml, app_ref_vector& vars, expr_ref& result) { m_impl->pull_exists(fml, vars, result); } void quantifier_hoister::pull_quantifier(bool is_forall, expr_ref& fml, app_ref_vector& vars) { m_impl->pull_quantifier(is_forall, fml, vars); } unsigned quantifier_hoister::pull_quantifier(bool is_forall, expr_ref& fml, ptr_vector* sorts, svector* names) { return m_impl->pull_quantifier(is_forall, fml, sorts, names); } z3-4.4.0/src/ast/rewriter/expr_safe_replace.cpp0000644000175000017500000000643112540347414021245 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: expr_safe_replace.cpp Abstract: Version of expr_replace/expr_substitution that is safe for quantifiers. Author: Nikolaj Bjorner (nbjorner) 2012-11-30 Revision History: --*/ #include "expr_safe_replace.h" #include "rewriter.h" void expr_safe_replace::insert(expr* src, expr* dst) { m_src.push_back(src); m_dst.push_back(dst); m_subst.insert(src, dst); } void expr_safe_replace::operator()(expr* e, expr_ref& res) { m_todo.push_back(e); expr* a, *b, *d; while (!m_todo.empty()) { a = m_todo.back(); if (m_cache.contains(a)) { m_todo.pop_back(); } else if (m_subst.find(a, b)) { m_cache.insert(a, b); m_todo.pop_back(); } else if (is_var(a)) { m_cache.insert(a, a); m_todo.pop_back(); } else if (is_app(a)) { app* c = to_app(a); unsigned n = c->get_num_args(); m_args.reset(); bool arg_differs = false; for (unsigned i = 0; i < n; ++i) { if (m_cache.find(c->get_arg(i), d)) { m_args.push_back(d); arg_differs |= c->get_arg(i) != d; } else { m_todo.push_back(c->get_arg(i)); } } if (m_args.size() == n) { if (arg_differs) { b = m.mk_app(c->get_decl(), m_args.size(), m_args.c_ptr()); m_refs.push_back(b); } else { b = a; } m_cache.insert(a, b); m_todo.pop_back(); } } else { SASSERT(is_quantifier(a)); quantifier* q = to_quantifier(a); expr_safe_replace replace(m); var_shifter shift(m); expr_ref new_body(m), src(m), dst(m), tmp(m); expr_ref_vector pats(m), nopats(m); unsigned num_decls = q->get_num_decls(); for (unsigned i = 0; i < m_src.size(); ++i) { shift(m_src[i].get(), num_decls, src); shift(m_dst[i].get(), num_decls, dst); replace.insert(src, dst); } unsigned np = q->get_num_patterns(); for (unsigned i = 0; i < np; ++i) { replace(q->get_pattern(i), tmp); pats.push_back(tmp); } np = q->get_num_no_patterns(); for (unsigned i = 0; i < np; ++i) { replace(q->get_no_pattern(i), tmp); nopats.push_back(tmp); } replace(q->get_expr(), new_body); b = m.update_quantifier(q, pats.size(), pats.c_ptr(), nopats.size(), nopats.c_ptr(), new_body); m_refs.push_back(b); m_cache.insert(a, b); m_todo.pop_back(); } } res = m_cache.find(e); m_cache.reset(); m_todo.reset(); m_args.reset(); m_refs.reset(); } void expr_safe_replace::reset() { m_src.reset(); m_dst.reset(); m_subst.reset(); } void expr_safe_replace::apply_substitution(expr* s, expr* def, expr_ref& t) { reset(); insert(s, def); (*this)(t, t); reset(); } z3-4.4.0/src/ast/rewriter/fpa_rewriter_params.pyg0000644000175000017500000000042512540347414021644 0ustar michaelmichaeldef_module_params(module_name='rewriter', class_name='fpa_rewriter_params', export=True, params=(("hi_fp_unspecified", BOOL, True, "use the 'hardware interpretation' for unspecified values in fp.to_ubv, fp.to_sbv, and fp.to_real"), )) z3-4.4.0/src/ast/rewriter/arith_rewriter.cpp0000644000175000017500000015127712540347414020641 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: arith_rewriter.cpp Abstract: Basic rewriting rules for arithmetic Author: Leonardo (leonardo) 2011-04-10 Notes: --*/ #include"arith_rewriter.h" #include"arith_rewriter_params.hpp" #include"poly_rewriter_def.h" #include"algebraic_numbers.h" #include"ast_pp.h" void arith_rewriter::updt_local_params(params_ref const & _p) { arith_rewriter_params p(_p); m_arith_lhs = p.arith_lhs(); m_gcd_rounding = p.gcd_rounding(); m_eq2ineq = p.eq2ineq(); m_elim_to_real = p.elim_to_real(); m_push_to_real = p.push_to_real(); m_anum_simp = p.algebraic_number_evaluator(); m_max_degree = p.max_degree(); m_expand_power = p.expand_power(); m_mul2power = p.mul_to_power(); m_elim_rem = p.elim_rem(); m_expand_tan = p.expand_tan(); set_sort_sums(p.sort_sums()); } void arith_rewriter::updt_params(params_ref const & p) { poly_rewriter::updt_params(p); updt_local_params(p); } void arith_rewriter::get_param_descrs(param_descrs & r) { poly_rewriter::get_param_descrs(r); arith_rewriter_params::collect_param_descrs(r); } br_status arith_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { br_status st = BR_FAILED; SASSERT(f->get_family_id() == get_fid()); switch (f->get_decl_kind()) { case OP_NUM: st = BR_FAILED; break; case OP_IRRATIONAL_ALGEBRAIC_NUM: st = BR_FAILED; break; case OP_LE: SASSERT(num_args == 2); st = mk_le_core(args[0], args[1], result); break; case OP_GE: SASSERT(num_args == 2); st = mk_ge_core(args[0], args[1], result); break; case OP_LT: SASSERT(num_args == 2); st = mk_lt_core(args[0], args[1], result); break; case OP_GT: SASSERT(num_args == 2); st = mk_gt_core(args[0], args[1], result); break; case OP_ADD: st = mk_add_core(num_args, args, result); break; case OP_MUL: st = mk_mul_core(num_args, args, result); break; case OP_SUB: st = mk_sub(num_args, args, result); break; case OP_DIV: SASSERT(num_args == 2); st = mk_div_core(args[0], args[1], result); break; case OP_IDIV: SASSERT(num_args == 2); st = mk_idiv_core(args[0], args[1], result); break; case OP_MOD: SASSERT(num_args == 2); st = mk_mod_core(args[0], args[1], result); break; case OP_REM: SASSERT(num_args == 2); st = mk_rem_core(args[0], args[1], result); break; case OP_UMINUS: SASSERT(num_args == 1); st = mk_uminus(args[0], result); break; case OP_TO_REAL: SASSERT(num_args == 1); st = mk_to_real_core(args[0], result); break; case OP_TO_INT: SASSERT(num_args == 1); st = mk_to_int_core(args[0], result); break; case OP_IS_INT: SASSERT(num_args == 1); st = mk_is_int(args[0], result); break; case OP_POWER: SASSERT(num_args == 2); st = mk_power_core(args[0], args[1], result); break; case OP_ABS: SASSERT(num_args == 1); st = mk_abs_core(args[0], result); break; case OP_SIN: SASSERT(num_args == 1); st = mk_sin_core(args[0], result); break; case OP_COS: SASSERT(num_args == 1); st = mk_cos_core(args[0], result); break; case OP_TAN: SASSERT(num_args == 1); st = mk_tan_core(args[0], result); break; case OP_ASIN: SASSERT(num_args == 1); st = mk_asin_core(args[0], result); break; case OP_ACOS: SASSERT(num_args == 1); st = mk_acos_core(args[0], result); break; case OP_ATAN: SASSERT(num_args == 1); st = mk_atan_core(args[0], result); break; case OP_SINH: SASSERT(num_args == 1); st = mk_sinh_core(args[0], result); break; case OP_COSH: SASSERT(num_args == 1); st = mk_cosh_core(args[0], result); break; case OP_TANH: SASSERT(num_args == 1); st = mk_tanh_core(args[0], result); break; default: st = BR_FAILED; break; } CTRACE("arith_rewriter", st != BR_FAILED, tout << mk_pp(f, m()); for (unsigned i = 0; i < num_args; ++i) tout << mk_pp(args[i], m()) << " "; tout << "\n==>\n" << mk_pp(result.get(), m()) << "\n";); return st; } void arith_rewriter::get_coeffs_gcd(expr * t, numeral & g, bool & first, unsigned & num_consts) { unsigned sz; expr * const * ms = get_monomials(t, sz); SASSERT(sz >= 1); numeral a; for (unsigned i = 0; i < sz; i++) { expr * arg = ms[i]; if (is_numeral(arg, a)) { if (!a.is_zero()) num_consts++; continue; } if (first) { get_power_product(arg, g); SASSERT(g.is_int()); first = false; } else { get_power_product(arg, a); SASSERT(a.is_int()); g = gcd(abs(a), g); } if (g.is_one()) return; } } bool arith_rewriter::div_polynomial(expr * t, numeral const & g, const_treatment ct, expr_ref & result) { SASSERT(m_util.is_int(t)); SASSERT(!g.is_one()); unsigned sz; expr * const * ms = get_monomials(t, sz); expr_ref_buffer new_args(m()); numeral a; for (unsigned i = 0; i < sz; i++) { expr * arg = ms[i]; if (is_numeral(arg, a)) { a /= g; if (!a.is_int()) { switch (ct) { case CT_FLOOR: a = floor(a); break; case CT_CEIL: a = ceil(a); break; case CT_FALSE: return false; } } if (!a.is_zero()) new_args.push_back(m_util.mk_numeral(a, true)); continue; } expr * pp = get_power_product(arg, a); a /= g; SASSERT(a.is_int()); if (!a.is_zero()) { if (a.is_one()) new_args.push_back(pp); else new_args.push_back(m_util.mk_mul(m_util.mk_numeral(a, true), pp)); } } switch (new_args.size()) { case 0: result = m_util.mk_numeral(numeral(0), true); return true; case 1: result = new_args[0]; return true; default: result = m_util.mk_add(new_args.size(), new_args.c_ptr()); return true; } } bool arith_rewriter::is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref & result) { numeral c; if (!is_add(arg1) && is_numeral(arg2, c)) { numeral a; bool r = false; expr * pp = get_power_product(arg1, a); if (a.is_neg()) { a.neg(); c.neg(); kind = inv(kind); r = true; } if (!a.is_one()) r = true; if (!r) return false; c /= a; bool is_int = m_util.is_int(arg1); if (is_int && !c.is_int()) { switch (kind) { case LE: c = floor(c); break; case GE: c = ceil(c); break; case EQ: result = m().mk_false(); return true; } } expr * k = m_util.mk_numeral(c, is_int); switch (kind) { case LE: result = m_util.mk_le(pp, k); return true; case GE: result = m_util.mk_ge(pp, k); return true; case EQ: result = m_util.mk_eq(pp, k); return true; } } return false; } bool arith_rewriter::elim_to_real_var(expr * var, expr_ref & new_var) { numeral val; if (m_util.is_numeral(var, val)) { if (!val.is_int()) return false; new_var = m_util.mk_numeral(val, true); return true; } else if (m_util.is_to_real(var)) { new_var = to_app(var)->get_arg(0); return true; } return false; } bool arith_rewriter::elim_to_real_mon(expr * monomial, expr_ref & new_monomial) { if (m_util.is_mul(monomial)) { expr_ref_buffer new_vars(m()); expr_ref new_var(m()); unsigned num = to_app(monomial)->get_num_args(); for (unsigned i = 0; i < num; i++) { if (!elim_to_real_var(to_app(monomial)->get_arg(i), new_var)) return false; new_vars.push_back(new_var); } new_monomial = m_util.mk_mul(new_vars.size(), new_vars.c_ptr()); return true; } else { return elim_to_real_var(monomial, new_monomial); } } bool arith_rewriter::elim_to_real_pol(expr * p, expr_ref & new_p) { if (m_util.is_add(p)) { expr_ref_buffer new_monomials(m()); expr_ref new_monomial(m()); unsigned num = to_app(p)->get_num_args(); for (unsigned i = 0; i < num; i++) { if (!elim_to_real_mon(to_app(p)->get_arg(i), new_monomial)) return false; new_monomials.push_back(new_monomial); } new_p = m_util.mk_add(new_monomials.size(), new_monomials.c_ptr()); return true; } else { return elim_to_real_mon(p, new_p); } } bool arith_rewriter::elim_to_real(expr * arg1, expr * arg2, expr_ref & new_arg1, expr_ref & new_arg2) { if (!m_util.is_real(arg1)) return false; return elim_to_real_pol(arg1, new_arg1) && elim_to_real_pol(arg2, new_arg2); } bool arith_rewriter::is_reduce_power_target(expr * arg, bool is_eq) { unsigned sz; expr * const * args; if (m_util.is_mul(arg)) { sz = to_app(arg)->get_num_args(); args = to_app(arg)->get_args(); } else { sz = 1; args = &arg; } for (unsigned i = 0; i < sz; i++) { expr * arg = args[i]; if (m_util.is_power(arg)) { rational k; if (m_util.is_numeral(to_app(arg)->get_arg(1), k) && k.is_int() && ((is_eq && k > rational(1)) || (!is_eq && k > rational(2)))) return true; } } return false; } expr * arith_rewriter::reduce_power(expr * arg, bool is_eq) { if (is_zero(arg)) return arg; unsigned sz; expr * const * args; if (m_util.is_mul(arg)) { sz = to_app(arg)->get_num_args(); args = to_app(arg)->get_args(); } else { sz = 1; args = &arg; } ptr_buffer new_args; rational k; for (unsigned i = 0; i < sz; i++) { expr * arg = args[i]; if (m_util.is_power(arg) && m_util.is_numeral(to_app(arg)->get_arg(1), k) && k.is_int() && ((is_eq && k > rational(1)) || (!is_eq && k > rational(2)))) { if (is_eq || !k.is_even()) new_args.push_back(to_app(arg)->get_arg(0)); else new_args.push_back(m_util.mk_power(to_app(arg)->get_arg(0), m_util.mk_numeral(rational(2), m_util.is_int(arg)))); } else { new_args.push_back(arg); } } SASSERT(new_args.size() >= 1); if (new_args.size() == 1) return new_args[0]; else return m_util.mk_mul(new_args.size(), new_args.c_ptr()); } br_status arith_rewriter::reduce_power(expr * arg1, expr * arg2, op_kind kind, expr_ref & result) { expr * new_arg1 = reduce_power(arg1, kind == EQ); expr * new_arg2 = reduce_power(arg2, kind == EQ); switch (kind) { case LE: result = m_util.mk_le(new_arg1, new_arg2); return BR_REWRITE1; case GE: result = m_util.mk_ge(new_arg1, new_arg2); return BR_REWRITE1; default: result = m().mk_eq(new_arg1, new_arg2); return BR_REWRITE1; } } br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kind, expr_ref & result) { expr_ref new_arg1(m()); expr_ref new_arg2(m()); if ((is_zero(arg1) && is_reduce_power_target(arg2, kind == EQ)) || (is_zero(arg2) && is_reduce_power_target(arg1, kind == EQ))) return reduce_power(arg1, arg2, kind, result); CTRACE("elim_to_real", m_elim_to_real, tout << "after_elim_to_real\n" << mk_ismt2_pp(arg1, m()) << "\n" << mk_ismt2_pp(arg2, m()) << "\n";); br_status st = cancel_monomials(arg1, arg2, m_arith_lhs, new_arg1, new_arg2); TRACE("mk_le_bug", tout << "st: " << st << "\n";); if (st != BR_FAILED) { arg1 = new_arg1; arg2 = new_arg2; } expr_ref new_new_arg1(m()); expr_ref new_new_arg2(m()); if (m_elim_to_real && elim_to_real(arg1, arg2, new_new_arg1, new_new_arg2)) { arg1 = new_new_arg1; arg2 = new_new_arg2; if (st == BR_FAILED) st = BR_DONE; } numeral a1, a2; if (is_numeral(arg1, a1) && is_numeral(arg2, a2)) { switch (kind) { case LE: result = a1 <= a2 ? m().mk_true() : m().mk_false(); return BR_DONE; case GE: result = a1 >= a2 ? m().mk_true() : m().mk_false(); return BR_DONE; default: result = a1 == a2 ? m().mk_true() : m().mk_false(); return BR_DONE; } } #define ANUM_LE_GE_EQ() { \ switch (kind) { \ case LE: result = am.le(v1, v2) ? m().mk_true() : m().mk_false(); return BR_DONE; \ case GE: result = am.ge(v1, v2) ? m().mk_true() : m().mk_false(); return BR_DONE; \ default: result = am.eq(v1, v2) ? m().mk_true() : m().mk_false(); return BR_DONE; \ } \ } if (m_anum_simp) { if (is_numeral(arg1, a1) && m_util.is_irrational_algebraic_numeral(arg2)) { anum_manager & am = m_util.am(); scoped_anum v1(am); am.set(v1, a1.to_mpq()); anum const & v2 = m_util.to_irrational_algebraic_numeral(arg2); ANUM_LE_GE_EQ(); } if (m_util.is_irrational_algebraic_numeral(arg1) && is_numeral(arg2, a2)) { anum_manager & am = m_util.am(); anum const & v1 = m_util.to_irrational_algebraic_numeral(arg1); scoped_anum v2(am); am.set(v2, a2.to_mpq()); ANUM_LE_GE_EQ(); } if (m_util.is_irrational_algebraic_numeral(arg1) && m_util.is_irrational_algebraic_numeral(arg2)) { anum_manager & am = m_util.am(); anum const & v1 = m_util.to_irrational_algebraic_numeral(arg1); anum const & v2 = m_util.to_irrational_algebraic_numeral(arg2); ANUM_LE_GE_EQ(); } } if (is_bound(arg1, arg2, kind, result)) return BR_DONE; if (is_bound(arg2, arg1, inv(kind), result)) return BR_DONE; bool is_int = m_util.is_int(arg1); if (is_int && m_gcd_rounding) { bool first = true; numeral g; unsigned num_consts = 0; get_coeffs_gcd(arg1, g, first, num_consts); TRACE("arith_rewriter_gcd", tout << "[step1] g: " << g << ", num_consts: " << num_consts << "\n";); if ((first || !g.is_one()) && num_consts <= 1) get_coeffs_gcd(arg2, g, first, num_consts); TRACE("arith_rewriter_gcd", tout << "[step2] g: " << g << ", num_consts: " << num_consts << "\n";); if (!first && !g.is_one() && num_consts <= 1) { bool is_sat = div_polynomial(arg1, g, (kind == LE ? CT_CEIL : (kind == GE ? CT_FLOOR : CT_FALSE)), new_arg1); if (!is_sat) { result = m().mk_false(); return BR_DONE; } is_sat = div_polynomial(arg2, g, (kind == LE ? CT_FLOOR : (kind == GE ? CT_CEIL : CT_FALSE)), new_arg2); if (!is_sat) { result = m().mk_false(); return BR_DONE; } arg1 = new_arg1.get(); arg2 = new_arg2.get(); st = BR_DONE; } } if (st != BR_FAILED) { switch (kind) { case LE: result = m_util.mk_le(arg1, arg2); return BR_DONE; case GE: result = m_util.mk_ge(arg1, arg2); return BR_DONE; default: result = m().mk_eq(arg1, arg2); return BR_DONE; } } return BR_FAILED; } br_status arith_rewriter::mk_le_core(expr * arg1, expr * arg2, expr_ref & result) { return mk_le_ge_eq_core(arg1, arg2, LE, result); } br_status arith_rewriter::mk_lt_core(expr * arg1, expr * arg2, expr_ref & result) { result = m().mk_not(m_util.mk_le(arg2, arg1)); return BR_REWRITE2; } br_status arith_rewriter::mk_ge_core(expr * arg1, expr * arg2, expr_ref & result) { return mk_le_ge_eq_core(arg1, arg2, GE, result); } br_status arith_rewriter::mk_gt_core(expr * arg1, expr * arg2, expr_ref & result) { result = m().mk_not(m_util.mk_le(arg1, arg2)); return BR_REWRITE2; } br_status arith_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) { if (m_eq2ineq) { result = m().mk_and(m_util.mk_le(arg1, arg2), m_util.mk_ge(arg1, arg2)); return BR_REWRITE2; } return mk_le_ge_eq_core(arg1, arg2, EQ, result); } bool arith_rewriter::is_anum_simp_target(unsigned num_args, expr * const * args) { if (!m_anum_simp) return false; unsigned num_irrat = 0; unsigned num_rat = 0; for (unsigned i = 0; i < num_args; i++) { if (m_util.is_numeral(args[i])) { num_rat++; if (num_irrat > 0) return true; } if (m_util.is_irrational_algebraic_numeral(args[i]) && m_util.am().degree(m_util.to_irrational_algebraic_numeral(args[i])) <= m_max_degree) { num_irrat++; if (num_irrat > 1 || num_rat > 0) return true; } } return false; } br_status arith_rewriter::mk_add_core(unsigned num_args, expr * const * args, expr_ref & result) { if (is_anum_simp_target(num_args, args)) { expr_ref_buffer new_args(m()); anum_manager & am = m_util.am(); scoped_anum r(am); scoped_anum arg(am); rational rarg; am.set(r, 0); for (unsigned i = 0; i < num_args; i ++) { unsigned d = am.degree(r); if (d > 1 && d > m_max_degree) { new_args.push_back(m_util.mk_numeral(r, false)); am.set(r, 0); } if (m_util.is_numeral(args[i], rarg)) { am.set(arg, rarg.to_mpq()); am.add(r, arg, r); continue; } if (m_util.is_irrational_algebraic_numeral(args[i])) { anum const & irarg = m_util.to_irrational_algebraic_numeral(args[i]); if (am.degree(irarg) <= m_max_degree) { am.add(r, irarg, r); continue; } } new_args.push_back(args[i]); } if (new_args.empty()) { result = m_util.mk_numeral(r, false); return BR_DONE; } new_args.push_back(m_util.mk_numeral(r, false)); br_status st = poly_rewriter::mk_add_core(new_args.size(), new_args.c_ptr(), result); if (st == BR_FAILED) { result = m().mk_app(get_fid(), OP_ADD, new_args.size(), new_args.c_ptr()); return BR_DONE; } return st; } else { return poly_rewriter::mk_add_core(num_args, args, result); } } br_status arith_rewriter::mk_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { if (is_anum_simp_target(num_args, args)) { expr_ref_buffer new_args(m()); anum_manager & am = m_util.am(); scoped_anum r(am); scoped_anum arg(am); rational rarg; am.set(r, 1); for (unsigned i = 0; i < num_args; i ++) { unsigned d = am.degree(r); if (d > 1 && d > m_max_degree) { new_args.push_back(m_util.mk_numeral(r, false)); am.set(r, 1); } if (m_util.is_numeral(args[i], rarg)) { am.set(arg, rarg.to_mpq()); am.mul(r, arg, r); continue; } if (m_util.is_irrational_algebraic_numeral(args[i])) { anum const & irarg = m_util.to_irrational_algebraic_numeral(args[i]); if (am.degree(irarg) <= m_max_degree) { am.mul(r, irarg, r); continue; } } new_args.push_back(args[i]); } if (new_args.empty()) { result = m_util.mk_numeral(r, false); return BR_DONE; } new_args.push_back(m_util.mk_numeral(r, false)); br_status st = poly_rewriter::mk_mul_core(new_args.size(), new_args.c_ptr(), result); if (st == BR_FAILED) { result = m().mk_app(get_fid(), OP_MUL, new_args.size(), new_args.c_ptr()); return BR_DONE; } return st; } else { return poly_rewriter::mk_mul_core(num_args, args, result); } } br_status arith_rewriter::mk_div_irrat_rat(expr * arg1, expr * arg2, expr_ref & result) { SASSERT(m_util.is_real(arg1)); SASSERT(m_util.is_irrational_algebraic_numeral(arg1)); SASSERT(m_util.is_numeral(arg2)); anum_manager & am = m_util.am(); anum const & val1 = m_util.to_irrational_algebraic_numeral(arg1); rational rval2; VERIFY(m_util.is_numeral(arg2, rval2)); if (rval2.is_zero()) return BR_FAILED; scoped_anum val2(am); am.set(val2, rval2.to_mpq()); scoped_anum r(am); am.div(val1, val2, r); result = m_util.mk_numeral(r, false); return BR_DONE; } br_status arith_rewriter::mk_div_rat_irrat(expr * arg1, expr * arg2, expr_ref & result) { SASSERT(m_util.is_real(arg1)); SASSERT(m_util.is_numeral(arg1)); SASSERT(m_util.is_irrational_algebraic_numeral(arg2)); anum_manager & am = m_util.am(); rational rval1; VERIFY(m_util.is_numeral(arg1, rval1)); scoped_anum val1(am); am.set(val1, rval1.to_mpq()); anum const & val2 = m_util.to_irrational_algebraic_numeral(arg2); scoped_anum r(am); am.div(val1, val2, r); result = m_util.mk_numeral(r, false); return BR_DONE; } br_status arith_rewriter::mk_div_irrat_irrat(expr * arg1, expr * arg2, expr_ref & result) { SASSERT(m_util.is_real(arg1)); SASSERT(m_util.is_irrational_algebraic_numeral(arg1)); SASSERT(m_util.is_irrational_algebraic_numeral(arg2)); anum_manager & am = m_util.am(); anum const & val1 = m_util.to_irrational_algebraic_numeral(arg1); if (am.degree(val1) > m_max_degree) return BR_FAILED; anum const & val2 = m_util.to_irrational_algebraic_numeral(arg2); if (am.degree(val2) > m_max_degree) return BR_FAILED; scoped_anum r(am); am.div(val1, val2, r); result = m_util.mk_numeral(r, false); return BR_DONE; } br_status arith_rewriter::mk_div_core(expr * arg1, expr * arg2, expr_ref & result) { if (m_anum_simp) { if (m_util.is_irrational_algebraic_numeral(arg1) && m_util.is_numeral(arg2)) return mk_div_irrat_rat(arg1, arg2, result); if (m_util.is_irrational_algebraic_numeral(arg1) && m_util.is_irrational_algebraic_numeral(arg2)) return mk_div_irrat_irrat(arg1, arg2, result); if (m_util.is_irrational_algebraic_numeral(arg2) && m_util.is_numeral(arg1)) return mk_div_rat_irrat(arg1, arg2, result); } set_curr_sort(m().get_sort(arg1)); numeral v1, v2; bool is_int; if (m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) { SASSERT(!is_int); if (m_util.is_numeral(arg1, v1, is_int)) { result = m_util.mk_numeral(v1/v2, false); return BR_DONE; } else { numeral k(1); k /= v2; result = m().mk_app(get_fid(), OP_MUL, m_util.mk_numeral(k, false), arg1); return BR_REWRITE1; } } if (!m_util.is_int(arg1)) { // (/ (* v1 b) (* v2 d)) --> (* v1/v2 (/ b d)) expr * a, * b, * c, * d; if (m_util.is_mul(arg1, a, b) && m_util.is_numeral(a, v1)) { // do nothing arg1 is of the form v1 * b } else { v1 = rational(1); b = arg1; } if (m_util.is_mul(arg2, c, d) && m_util.is_numeral(c, v2)) { // do nothing arg2 is of the form v2 * d } else { v2 = rational(1); d = arg2; } TRACE("div_bug", tout << "v1: " << v1 << ", v2: " << v2 << "\n";); if (!v1.is_one() || !v2.is_one()) { v1 /= v2; result = m_util.mk_mul(m_util.mk_numeral(v1, false), m_util.mk_div(b, d)); return BR_REWRITE2; } } return BR_FAILED; } br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(m().get_sort(arg1)); numeral v1, v2; bool is_int; if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) { result = m_util.mk_numeral(div(v1, v2), is_int); return BR_DONE; } return BR_FAILED; } br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(m().get_sort(arg1)); numeral v1, v2; bool is_int; if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) { result = m_util.mk_numeral(mod(v1, v2), is_int); return BR_DONE; } if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_one()) { result = m_util.mk_numeral(numeral(0), true); return BR_DONE; } // mod is idempotent on non-zero modulus. expr* t1, *t2; if (m_util.is_mod(arg1, t1, t2) && t2 == arg2 && m_util.is_numeral(arg2, v2, is_int) && is_int && !v2.is_zero()) { result = arg1; return BR_DONE; } // propagate mod inside only if not all arguments are not already mod. if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_pos() && (is_add(arg1) || is_mul(arg1))) { TRACE("mod_bug", tout << "mk_mod:\n" << mk_ismt2_pp(arg1, m()) << "\n" << mk_ismt2_pp(arg2, m()) << "\n";); unsigned num_args = to_app(arg1)->get_num_args(); unsigned i; rational arg_v; for (i = 0; i < num_args; i++) { expr * arg = to_app(arg1)->get_arg(i); if (m_util.is_mod(arg)) continue; if (m_util.is_numeral(arg, arg_v) && mod(arg_v, v2) == arg_v) continue; // found target for rewriting break; } TRACE("mod_bug", tout << "mk_mod target: " << i << "\n";); if (i == num_args) return BR_FAILED; // did not find any target for applying simplification ptr_buffer new_args; for (unsigned i = 0; i < num_args; i++) new_args.push_back(m_util.mk_mod(to_app(arg1)->get_arg(i), arg2)); result = m_util.mk_mod(m().mk_app(to_app(arg1)->get_decl(), new_args.size(), new_args.c_ptr()), arg2); TRACE("mod_bug", tout << "mk_mod result: " << mk_ismt2_pp(result, m()) << "\n";); return BR_REWRITE3; } return BR_FAILED; } br_status arith_rewriter::mk_rem_core(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(m().get_sort(arg1)); numeral v1, v2; bool is_int; if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) { numeral m = mod(v1, v2); // // rem(v1,v2) = if v2 >= 0 then mod(v1,v2) else -mod(v1,v2) // if (v2.is_neg()) { m.neg(); } result = m_util.mk_numeral(m, is_int); return BR_DONE; } else if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_one()) { result = m_util.mk_numeral(numeral(0), true); return BR_DONE; } else if (m_util.is_numeral(arg2, v2, is_int) && is_int && !v2.is_zero()) { if (is_add(arg1) || is_mul(arg1)) { return BR_FAILED; } else { if (v2.is_neg()) { result = m_util.mk_uminus(m_util.mk_mod(arg1, arg2)); return BR_REWRITE2; } else { result = m_util.mk_mod(arg1, arg2); return BR_REWRITE1; } } } else if (m_elim_rem) { expr * mod = m_util.mk_mod(arg1, arg2); result = m().mk_ite(m_util.mk_ge(arg2, m_util.mk_numeral(rational(0), true)), mod, m_util.mk_uminus(mod)); TRACE("elim_rem", tout << "result: " << mk_ismt2_pp(result, m()) << "\n";); return BR_REWRITE3; } return BR_FAILED; } br_status arith_rewriter::mk_power_core(expr * arg1, expr * arg2, expr_ref & result) { numeral x, y; bool is_num_x = m_util.is_numeral(arg1, x); bool is_num_y = m_util.is_numeral(arg2, y); bool is_int_sort = m_util.is_int(arg1); if ((is_num_x && x.is_one()) || (is_num_y && y.is_one())) { result = arg1; return BR_DONE; } if (is_num_x && is_num_y) { if (x.is_zero() && y.is_zero()) return BR_FAILED; if (y.is_zero()) { result = m_util.mk_numeral(rational(1), m().get_sort(arg1)); return BR_DONE; } if (x.is_zero()) { result = arg1; return BR_DONE; } if (y.is_unsigned() && y.get_unsigned() <= m_max_degree) { x = power(x, y.get_unsigned()); result = m_util.mk_numeral(x, m().get_sort(arg1)); return BR_DONE; } if (!is_int_sort && (-y).is_unsigned() && (-y).get_unsigned() <= m_max_degree) { x = power(rational(1)/x, (-y).get_unsigned()); result = m_util.mk_numeral(x, m().get_sort(arg1)); return BR_DONE; } } if (m_util.is_power(arg1) && is_num_y && y.is_int() && !y.is_zero()) { // (^ (^ t y2) y) --> (^ t (* y2 y)) If y2 > 0 && y != 0 && y and y2 are integers rational y2; if (m_util.is_numeral(to_app(arg1)->get_arg(1), y2) && y2.is_int() && y2.is_pos()) { result = m_util.mk_power(to_app(arg1)->get_arg(0), m_util.mk_numeral(y*y2, is_int_sort)); return BR_REWRITE1; } } if (!is_int_sort && is_num_y && y.is_neg()) { // (^ t -k) --> (^ (/ 1 t) k) result = m_util.mk_power(m_util.mk_div(m_util.mk_numeral(rational(1), false), arg1), m_util.mk_numeral(-y, false)); return BR_REWRITE2; } if (!is_int_sort && is_num_y && !y.is_int() && !numerator(y).is_one()) { // (^ t (/ p q)) --> (^ (^ t (/ 1 q)) p) result = m_util.mk_power(m_util.mk_power(arg1, m_util.mk_numeral(rational(1)/denominator(y), false)), m_util.mk_numeral(numerator(y), false)); return BR_REWRITE2; } if ((m_expand_power || (m_som && is_app(arg1) && to_app(arg1)->get_family_id() == get_fid())) && is_num_y && y.is_unsigned() && 1 < y.get_unsigned() && y.get_unsigned() <= m_max_degree) { ptr_buffer args; unsigned k = y.get_unsigned(); for (unsigned i = 0; i < k; i++) { args.push_back(arg1); } result = m_util.mk_mul(args.size(), args.c_ptr()); return BR_REWRITE1; } if (!is_num_y) return BR_FAILED; bool is_irrat_x = m_util.is_irrational_algebraic_numeral(arg1); if (!is_num_x && !is_irrat_x) return BR_FAILED; rational num_y = numerator(y); rational den_y = denominator(y); bool is_neg_y = false; if (num_y.is_neg()) { num_y.neg(); is_neg_y = true; } SASSERT(num_y.is_pos()); SASSERT(den_y.is_pos()); if (is_neg_y && is_int_sort) return BR_FAILED; if (!num_y.is_unsigned() || !den_y.is_unsigned()) return BR_FAILED; unsigned u_num_y = num_y.get_unsigned(); unsigned u_den_y = den_y.get_unsigned(); if (u_num_y > m_max_degree || u_den_y > m_max_degree) return BR_FAILED; if (is_num_x) { rational xk, r; xk = power(x, u_num_y); if (xk.root(u_den_y, r)) { if (is_neg_y) r = rational(1)/r; result = m_util.mk_numeral(r, m().get_sort(arg1)); return BR_DONE; } if (m_anum_simp) { anum_manager & am = m_util.am(); scoped_anum r(am); am.set(r, xk.to_mpq()); am.root(r, u_den_y, r); if (is_neg_y) am.inv(r); result = m_util.mk_numeral(r, false); return BR_DONE; } return BR_FAILED; } SASSERT(is_irrat_x); if (!m_anum_simp) return BR_FAILED; anum const & val = m_util.to_irrational_algebraic_numeral(arg1); anum_manager & am = m_util.am(); if (am.degree(val) > m_max_degree) return BR_FAILED; scoped_anum r(am); am.power(val, u_num_y, r); am.root(r, u_den_y, r); if (is_neg_y) am.inv(r); result = m_util.mk_numeral(r, false); return BR_DONE; } br_status arith_rewriter::mk_to_int_core(expr * arg, expr_ref & result) { numeral a; if (m_util.is_numeral(arg, a)) { result = m_util.mk_numeral(floor(a), true); return BR_DONE; } else if (m_util.is_to_real(arg)) { result = to_app(arg)->get_arg(0); return BR_DONE; } else { if (m_util.is_add(arg) || m_util.is_mul(arg) || m_util.is_power(arg)) { // Try to apply simplifications such as: // (to_int (+ 1.0 (to_real x))) --> (+ 1 x) // if all arguments of arg are // - integer numerals, OR // - to_real applications // then, to_int can be eliminated unsigned num_args = to_app(arg)->get_num_args(); unsigned i = 0; for (; i < num_args; i++) { expr * c = to_app(arg)->get_arg(i); if (m_util.is_numeral(c, a) && a.is_int()) continue; if (m_util.is_to_real(c)) continue; break; // failed } if (i == num_args) { // simplification can be applied expr_ref_buffer new_args(m()); for (i = 0; i < num_args; i++) { expr * c = to_app(arg)->get_arg(i); if (m_util.is_numeral(c, a) && a.is_int()) { new_args.push_back(m_util.mk_numeral(a, true)); } else { SASSERT(m_util.is_to_real(c)); new_args.push_back(to_app(c)->get_arg(0)); } } SASSERT(num_args == new_args.size()); result = m().mk_app(get_fid(), to_app(arg)->get_decl()->get_decl_kind(), new_args.size(), new_args.c_ptr()); return BR_REWRITE1; } } return BR_FAILED; } } br_status arith_rewriter::mk_to_real_core(expr * arg, expr_ref & result) { numeral a; if (m_util.is_numeral(arg, a)) { result = m_util.mk_numeral(a, false); return BR_DONE; } // push to_real over OP_ADD, OP_MUL if (m_push_to_real) { if (m_util.is_add(arg) || m_util.is_mul(arg)) { ptr_buffer new_args; unsigned num = to_app(arg)->get_num_args(); for (unsigned i = 0; i < num; i++) { new_args.push_back(m_util.mk_to_real(to_app(arg)->get_arg(i))); } if (m_util.is_add(arg)) result = m().mk_app(get_fid(), OP_ADD, new_args.size(), new_args.c_ptr()); else result = m().mk_app(get_fid(), OP_MUL, new_args.size(), new_args.c_ptr()); return BR_REWRITE2; } } return BR_FAILED; } br_status arith_rewriter::mk_is_int(expr * arg, expr_ref & result) { numeral a; if (m_util.is_numeral(arg, a)) { result = a.is_int() ? m().mk_true() : m().mk_false(); return BR_DONE; } else if (m_util.is_to_real(arg)) { result = m().mk_true(); return BR_DONE; } else { result = m().mk_eq(m().mk_app(get_fid(), OP_TO_REAL, m().mk_app(get_fid(), OP_TO_INT, arg)), arg); return BR_REWRITE3; } } br_status arith_rewriter::mk_abs_core(expr * arg, expr_ref & result) { result = m().mk_ite(m_util.mk_ge(arg, m_util.mk_numeral(rational(0), m_util.is_int(arg))), arg, m_util.mk_uminus(arg)); return BR_REWRITE2; } void arith_rewriter::set_cancel(bool f) { m_util.set_cancel(f); } // Return true if t is of the form c*Pi where c is a numeral. // Store c into k bool arith_rewriter::is_pi_multiple(expr * t, rational & k) { if (m_util.is_pi(t)) { k = rational(1); return true; } expr * a, * b; return m_util.is_mul(t, a, b) && m_util.is_pi(b) && m_util.is_numeral(a, k); } // Return true if t is of the form (+ s c*Pi) where c is a numeral. // Store c into k, and c*Pi into m. bool arith_rewriter::is_pi_offset(expr * t, rational & k, expr * & m) { if (m_util.is_add(t)) { unsigned num = to_app(t)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = to_app(t)->get_arg(i); if (is_pi_multiple(arg, k)) { m = arg; return true; } } } return false; } // Return true if t is of the form 2*pi*to_real(s). bool arith_rewriter::is_2_pi_integer(expr * t) { expr * a, * m, * b, * c; rational k; return m_util.is_mul(t, a, m) && m_util.is_numeral(a, k) && k.is_int() && mod(k, rational(2)).is_zero() && m_util.is_mul(m, b, c) && ((m_util.is_pi(b) && m_util.is_to_real(c)) || (m_util.is_to_real(b) && m_util.is_pi(c))); } // Return true if t is of the form s + 2*pi*to_real(s). // Store 2*pi*to_real(s) into m. bool arith_rewriter::is_2_pi_integer_offset(expr * t, expr * & m) { if (m_util.is_add(t)) { unsigned num = to_app(t)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = to_app(t)->get_arg(i); if (is_2_pi_integer(arg)) { m = arg; return true; } } } return false; } // Return true if t is of the form pi*to_real(s). bool arith_rewriter::is_pi_integer(expr * t) { expr * a, * b; if (m_util.is_mul(t, a, b)) { rational k; if (m_util.is_numeral(a, k)) { if (!k.is_int()) return false; expr * c, * d; if (!m_util.is_mul(b, c, d)) return false; a = c; b = d; } TRACE("tan", tout << "is_pi_integer " << mk_ismt2_pp(t, m()) << "\n"; tout << "a: " << mk_ismt2_pp(a, m()) << "\n"; tout << "b: " << mk_ismt2_pp(b, m()) << "\n";); return (m_util.is_pi(a) && m_util.is_to_real(b)) || (m_util.is_to_real(a) && m_util.is_pi(b)); } return false; } // Return true if t is of the form s + pi*to_real(s). // Store 2*pi*to_real(s) into m. bool arith_rewriter::is_pi_integer_offset(expr * t, expr * & m) { if (m_util.is_add(t)) { unsigned num = to_app(t)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = to_app(t)->get_arg(i); if (is_pi_integer(arg)) { m = arg; return true; } } } return false; } app * arith_rewriter::mk_sqrt(rational const & k) { return m_util.mk_power(m_util.mk_numeral(k, false), m_util.mk_numeral(rational(1, 2), false)); } // Return a constant representing sin(k * pi). // Return 0 if failed. expr * arith_rewriter::mk_sin_value(rational const & k) { rational k_prime = mod(floor(k), rational(2)) + k - floor(k); TRACE("sine", tout << "k: " << k << ", k_prime: " << k_prime << "\n";); SASSERT(k_prime >= rational(0) && k_prime < rational(2)); bool neg = false; if (k_prime >= rational(1)) { neg = true; k_prime = k_prime - rational(1); } SASSERT(k_prime >= rational(0) && k_prime < rational(1)); if (k_prime.is_zero() || k_prime.is_one()) { // sin(0) == sin(pi) == 0 return m_util.mk_numeral(rational(0), false); } if (k_prime == rational(1, 2)) { // sin(pi/2) == 1, sin(3/2 pi) == -1 return m_util.mk_numeral(rational(neg ? -1 : 1), false); } if (k_prime == rational(1, 6) || k_prime == rational(5, 6)) { // sin(pi/6) == sin(5/6 pi) == 1/2 // sin(7 pi/6) == sin(11/6 pi) == -1/2 return m_util.mk_numeral(rational(neg ? -1 : 1, 2), false); } if (k_prime == rational(1, 4) || k_prime == rational(3, 4)) { // sin(pi/4) == sin(3/4 pi) == Sqrt(1/2) // sin(5/4 pi) == sin(7/4 pi) == - Sqrt(1/2) expr * result = mk_sqrt(rational(1, 2)); return neg ? m_util.mk_uminus(result) : result; } if (k_prime == rational(1, 3) || k_prime == rational(2, 3)) { // sin(pi/3) == sin(2/3 pi) == Sqrt(3)/2 // sin(4/3 pi) == sin(5/3 pi) == - Sqrt(3)/2 expr * result = m_util.mk_div(mk_sqrt(rational(3)), m_util.mk_numeral(rational(2), false)); return neg ? m_util.mk_uminus(result) : result; } if (k_prime == rational(1, 12) || k_prime == rational(11, 12)) { // sin(1/12 pi) == sin(11/12 pi) == [sqrt(6) - sqrt(2)]/4 // sin(13/12 pi) == sin(23/12 pi) == -[sqrt(6) - sqrt(2)]/4 expr * result = m_util.mk_div(m_util.mk_sub(mk_sqrt(rational(6)), mk_sqrt(rational(2))), m_util.mk_numeral(rational(4), false)); return neg ? m_util.mk_uminus(result) : result; } if (k_prime == rational(5, 12) || k_prime == rational(7, 12)) { // sin(5/12 pi) == sin(7/12 pi) == [sqrt(6) + sqrt(2)]/4 // sin(17/12 pi) == sin(19/12 pi) == -[sqrt(6) + sqrt(2)]/4 expr * result = m_util.mk_div(m_util.mk_add(mk_sqrt(rational(6)), mk_sqrt(rational(2))), m_util.mk_numeral(rational(4), false)); return neg ? m_util.mk_uminus(result) : result; } return 0; } br_status arith_rewriter::mk_sin_core(expr * arg, expr_ref & result) { if (is_app_of(arg, get_fid(), OP_ASIN)) { // sin(asin(x)) == x result = to_app(arg)->get_arg(0); return BR_DONE; } rational k; if (is_numeral(arg, k) && k.is_zero()) { // sin(0) == 0 result = arg; return BR_DONE; } if (is_pi_multiple(arg, k)) { result = mk_sin_value(k); if (result.get() != 0) return BR_REWRITE_FULL; } expr * m; if (is_pi_offset(arg, k, m)) { rational k_prime = mod(floor(k), rational(2)) + k - floor(k); SASSERT(k_prime >= rational(0) && k_prime < rational(2)); if (k_prime.is_zero()) { // sin(x + 2*n*pi) == sin(x) result = m_util.mk_sin(m_util.mk_sub(arg, m)); return BR_REWRITE2; } if (k_prime == rational(1, 2)) { // sin(x + pi/2 + 2*n*pi) == cos(x) result = m_util.mk_cos(m_util.mk_sub(arg, m)); return BR_REWRITE2; } if (k_prime.is_one()) { // sin(x + pi + 2*n*pi) == -sin(x) result = m_util.mk_uminus(m_util.mk_sin(m_util.mk_sub(arg, m))); return BR_REWRITE3; } if (k_prime == rational(3, 2)) { // sin(x + 3/2*pi + 2*n*pi) == -cos(x) result = m_util.mk_uminus(m_util.mk_cos(m_util.mk_sub(arg, m))); return BR_REWRITE3; } } if (is_2_pi_integer_offset(arg, m)) { // sin(x + 2*pi*to_real(a)) == sin(x) result = m_util.mk_sin(m_util.mk_sub(arg, m)); return BR_REWRITE2; } return BR_FAILED; } br_status arith_rewriter::mk_cos_core(expr * arg, expr_ref & result) { if (is_app_of(arg, get_fid(), OP_ACOS)) { // cos(acos(x)) == x result = to_app(arg)->get_arg(0); return BR_DONE; } rational k; if (is_numeral(arg, k) && k.is_zero()) { // cos(0) == 1 result = m_util.mk_numeral(rational(1), false); return BR_DONE; } if (is_pi_multiple(arg, k)) { k = k + rational(1, 2); result = mk_sin_value(k); if (result.get() != 0) return BR_REWRITE_FULL; } expr * m; if (is_pi_offset(arg, k, m)) { rational k_prime = mod(floor(k), rational(2)) + k - floor(k); SASSERT(k_prime >= rational(0) && k_prime < rational(2)); if (k_prime.is_zero()) { // cos(x + 2*n*pi) == cos(x) result = m_util.mk_cos(m_util.mk_sub(arg, m)); return BR_REWRITE2; } if (k_prime == rational(1, 2)) { // cos(x + pi/2 + 2*n*pi) == -sin(x) result = m_util.mk_uminus(m_util.mk_sin(m_util.mk_sub(arg, m))); return BR_REWRITE3; } if (k_prime.is_one()) { // cos(x + pi + 2*n*pi) == -cos(x) result = m_util.mk_uminus(m_util.mk_cos(m_util.mk_sub(arg, m))); return BR_REWRITE3; } if (k_prime == rational(3, 2)) { // cos(x + 3/2*pi + 2*n*pi) == sin(x) result = m_util.mk_sin(m_util.mk_sub(arg, m)); return BR_REWRITE2; } } if (is_2_pi_integer_offset(arg, m)) { // cos(x + 2*pi*to_real(a)) == cos(x) result = m_util.mk_cos(m_util.mk_sub(arg, m)); return BR_REWRITE2; } return BR_FAILED; } br_status arith_rewriter::mk_tan_core(expr * arg, expr_ref & result) { if (is_app_of(arg, get_fid(), OP_ATAN)) { // tan(atan(x)) == x result = to_app(arg)->get_arg(0); return BR_DONE; } rational k; if (is_numeral(arg, k) && k.is_zero()) { // tan(0) == 0 result = arg; return BR_DONE; } if (is_pi_multiple(arg, k)) { expr_ref n(m()), d(m()); n = mk_sin_value(k); if (n.get() == 0) goto end; if (is_zero(n)) { result = n; return BR_DONE; } k = k + rational(1, 2); d = mk_sin_value(k); SASSERT(d.get() != 0); if (is_zero(d)) { goto end; } result = m_util.mk_div(n, d); return BR_REWRITE_FULL; } expr * m; if (is_pi_offset(arg, k, m)) { rational k_prime = k - floor(k); SASSERT(k_prime >= rational(0) && k_prime < rational(1)); if (k_prime.is_zero()) { // tan(x + n*pi) == tan(x) result = m_util.mk_tan(m_util.mk_sub(arg, m)); return BR_REWRITE2; } } if (is_pi_integer_offset(arg, m)) { // tan(x + pi*to_real(a)) == tan(x) result = m_util.mk_tan(m_util.mk_sub(arg, m)); return BR_REWRITE2; } end: if (m_expand_tan) { result = m_util.mk_div(m_util.mk_sin(arg), m_util.mk_cos(arg)); return BR_REWRITE2; } return BR_FAILED; } br_status arith_rewriter::mk_asin_core(expr * arg, expr_ref & result) { // Remark: we assume that ForAll x : asin(-x) == asin(x). // Mathematica uses this as an axiom. Although asin is an underspecified function for x < -1 or x > 1. // Actually, in Mathematica, asin(x) is a total function that returns a complex number fo x < -1 or x > 1. rational k; if (is_numeral(arg, k)) { if (k.is_zero()) { result = arg; return BR_DONE; } if (k < rational(-1)) { // asin(-2) == -asin(2) // asin(-3) == -asin(3) k.neg(); result = m_util.mk_uminus(m_util.mk_asin(m_util.mk_numeral(k, false))); return BR_REWRITE2; } if (k > rational(1)) return BR_FAILED; bool neg = false; if (k.is_neg()) { neg = true; k.neg(); } if (k.is_one()) { // asin(1) == pi/2 // asin(-1) == -pi/2 result = m_util.mk_mul(m_util.mk_numeral(rational(neg ? -1 : 1, 2), false), m_util.mk_pi()); return BR_REWRITE2; } if (k == rational(1, 2)) { // asin(1/2) == pi/6 // asin(-1/2) == -pi/6 result = m_util.mk_mul(m_util.mk_numeral(rational(neg ? -1 : 1, 6), false), m_util.mk_pi()); return BR_REWRITE2; } } expr * t; if (m_util.is_times_minus_one(arg, t)) { // See comment above // asin(-x) ==> -asin(x) result = m_util.mk_uminus(m_util.mk_asin(t)); return BR_REWRITE2; } return BR_FAILED; } br_status arith_rewriter::mk_acos_core(expr * arg, expr_ref & result) { rational k; if (is_numeral(arg, k)) { if (k.is_zero()) { // acos(0) = pi/2 result = m_util.mk_mul(m_util.mk_numeral(rational(1, 2), false), m_util.mk_pi()); return BR_REWRITE2; } if (k.is_one()) { // acos(1) = 0 result = m_util.mk_numeral(rational(0), false); return BR_DONE; } if (k.is_minus_one()) { // acos(-1) = pi result = m_util.mk_pi(); return BR_DONE; } if (k == rational(1, 2)) { // acos(1/2) = pi/3 result = m_util.mk_mul(m_util.mk_numeral(rational(1, 3), false), m_util.mk_pi()); return BR_REWRITE2; } if (k == rational(-1, 2)) { // acos(-1/2) = 2/3 pi result = m_util.mk_mul(m_util.mk_numeral(rational(2, 3), false), m_util.mk_pi()); return BR_REWRITE2; } } return BR_FAILED; } br_status arith_rewriter::mk_atan_core(expr * arg, expr_ref & result) { rational k; if (is_numeral(arg, k)) { if (k.is_zero()) { result = arg; return BR_DONE; } if (k.is_one()) { // atan(1) == pi/4 result = m_util.mk_mul(m_util.mk_numeral(rational(1, 4), false), m_util.mk_pi()); return BR_REWRITE2; } if (k.is_minus_one()) { // atan(-1) == -pi/4 result = m_util.mk_mul(m_util.mk_numeral(rational(-1, 4), false), m_util.mk_pi()); return BR_REWRITE2; } if (k < rational(-1)) { // atan(-2) == -tan(2) // atan(-3) == -tan(3) k.neg(); result = m_util.mk_uminus(m_util.mk_atan(m_util.mk_numeral(k, false))); return BR_REWRITE2; } return BR_FAILED; } expr * t; if (m_util.is_times_minus_one(arg, t)) { // atan(-x) ==> -atan(x) result = m_util.mk_uminus(m_util.mk_atan(t)); return BR_REWRITE2; } return BR_FAILED; } br_status arith_rewriter::mk_sinh_core(expr * arg, expr_ref & result) { if (is_app_of(arg, get_fid(), OP_ASINH)) { // sinh(asinh(x)) == x result = to_app(arg)->get_arg(0); return BR_DONE; } expr * t; if (m_util.is_times_minus_one(arg, t)) { // sinh(-t) == -sinh(t) result = m_util.mk_uminus(m_util.mk_sinh(t)); return BR_REWRITE2; } return BR_FAILED; } br_status arith_rewriter::mk_cosh_core(expr * arg, expr_ref & result) { if (is_app_of(arg, get_fid(), OP_ACOSH)) { // cosh(acosh(x)) == x result = to_app(arg)->get_arg(0); return BR_DONE; } expr * t; if (m_util.is_times_minus_one(arg, t)) { // cosh(-t) == cosh result = m_util.mk_cosh(t); return BR_DONE; } return BR_FAILED; } br_status arith_rewriter::mk_tanh_core(expr * arg, expr_ref & result) { if (is_app_of(arg, get_fid(), OP_ATANH)) { // tanh(atanh(x)) == x result = to_app(arg)->get_arg(0); return BR_DONE; } expr * t; if (m_util.is_times_minus_one(arg, t)) { // tanh(-t) == -tanh(t) result = m_util.mk_uminus(m_util.mk_tanh(t)); return BR_REWRITE2; } return BR_FAILED; } template class poly_rewriter; z3-4.4.0/src/ast/rewriter/der.h0000644000175000017500000001515212540347414016015 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: der.h Abstract: Destructive equality resolution. Author: Leonardo de Moura (leonardo) 2008-01-27. Revision History: Christoph Wintersteiger, 2010-03-30: Added Destr. Multi-Equality Resolution --*/ #ifndef _DER_H_ #define _DER_H_ #include"ast.h" #include"var_subst.h" /* New DER: the class DER (above) eliminates variables one by one. This is inefficient, and we should implement a new version that can handle efficiently examples with hundreds of variables. Suppose the target of the simplification is the quantifier (FORALL (x1 T1) (x2 T2) ... (xn Tn) (or ....)) So, the variables x1 ... xn are the candidates for elimination. First, we build a mapping of candidate substitutions Since variables x1 ... xn have ids 0 ... n-1, we can use an array m_map to implement this mapping. The idea is to traverse the children of the (or ...) looking for diseqs. The method is_var_diseq can be used for doing that. Given a child c, if is_var_diseq(c, num_decls, v, t) returns true, and m_map[v] is null, then we store t at m_map[v]. For performance reasons, we also store a mapping from child position (in the OR) to variable ID. Thus, m_pos2var[pos] contains the variable that is defined by the diseq at position pos. m_pos2var[pos] = -1, if this position does not contain a diseq. After doing that, m_map contains the variables that can be potentially eliminated using DER. We say m_map[v] is the definition of variable v. The next step is to perform a topological sort on these variables. The result is an array (m_order) of integers (variable ids) such that i < j implies that m_map[m_order[i]] does not depend on variable m_order[j]. For example, consider the case where m_map contains the following values m_map[0] = (+ (VAR 2) 0) m_map[1] = null m_map[2] = (+ (VAR 3) 0) m_map[3] = (+ (VAR 1) 1) m_map[4] = (* (VAR 5) 2) m_map[5] = null In this example, variable 0 depends on the definition of variable 2, which depends on the definition of variable 3, which depends on the definition of variable 0 (cycle). On the other hand, no cycle is found when starting at variable 4. Cycles can be broken by erasing entries from m_map. For example, the cycle above can be removed by setting m_map[0] = null. m_map[0] = null m_map[1] = null m_map[2] = (+ (VAR 3) 0) m_map[3] = (+ (VAR 1) 1) m_map[4] = (* (VAR 5) 2) m_map[5] = null The file asserted_formulas.cpp has a class top_sort for performing topological sort. This class cannot be used here, since it is meant for eliminating constants (instead of variables). We need to implement a new top_sort here, we do not need a separate class for doing that. Moreover, it is much simpler, since m_map is just an array. In the example above (after setting m_map[0] to null), top_sort will produce the following order m_order = [3, 2, 4] The next step is to use var_subst to update the definitions in var_subst. The idea is to process the variables in the order specified by m_order. When processing m_map[m_order[i]] we use the definitions of all variables in m_order[0 ... i-1]. For example: The first variable is 3, since it is at m_order[0], nothing needs to be done. Next we have variable 2, we use m_map[3] since 3 is before 2 in m_order. So, the new definition for 2 is (+ (+ (VAR 1) 1) 0). That is, we update m_map[2] with (+ (+ (VAR 1) 1) 0) Next we have variable 4, we use m_map[3] and m_map[2] since 3 and 2 are before 4 in m_order. In this case, var_subst will not do anything since m_map[4] does not contain variables 3 or 2. So, the new m_map is: m_map[0] = null m_map[1] = null m_map[2] = (+ (+ (VAR 1) 1) 0) m_map[3] = (+ (VAR 1) 1) m_map[4] = (* (VAR 5) 2) m_map[5] = null Now, we update the body of the quantifier using var_subst and the mapping above. The idea is to create a new set of children for the OR. For each child at position i, we do if m_map[m_pos2var[i]] != -1 skip this child, it is a diseq used during DER else apply var_subst using m_map to this child, and store the result in a new children array Create a new OR (new body of the quantifier) using the new children Then, we create a new quantifier using this new body, and use the function elim_unused_vars to eliminate the ununsed variables. Remark: let us implement the new version inside the class der. Use #if 0 ... #endif to comment the old version. Remark: after you are done, we can eliminate the call to occurs in is_var_diseq, since top_sort is already performing cycle detection. */ /** \brief Functor for applying Destructive Multi-Equality Resolution. (forall (X Y) (or X /= s C[X])) --> (forall (Y) C[Y]) */ class der { ast_manager & m_manager; var_subst m_subst; expr_ref_buffer m_new_exprs; ptr_vector m_map; int_vector m_pos2var; ptr_vector m_inx2var; unsigned_vector m_order; expr_ref_vector m_subst_map; expr_ref_buffer m_new_args; /** \brief Return true if e can be viewed as a variable disequality. Store the variable id in v and the definition in t. For example: if e is (not (= (VAR 1) T)), then v assigned to 1, and t to T. if e is (iff (VAR 2) T), then v is assigned to 2, and t to (not T). (not T) is used because this formula is equivalent to (not (iff (VAR 2) (not T))), and can be viewed as a disequality. */ bool is_var_diseq(expr * e, unsigned num_decls, var *& v, expr_ref & t); void get_elimination_order(); void create_substitution(unsigned sz); void apply_substitution(quantifier * q, expr_ref & r); void reduce1(quantifier * q, expr_ref & r, proof_ref & pr); public: der(ast_manager & m):m_manager(m),m_subst(m),m_new_exprs(m),m_subst_map(m),m_new_args(m) {} ast_manager & m() const { return m_manager; } void operator()(quantifier * q, expr_ref & r, proof_ref & pr); }; /** \brief Functor for applying Destructive Multi-Equality Resolution in all universal quantifiers in an expression. */ class der_rewriter { protected: struct imp; imp * m_imp; public: der_rewriter(ast_manager & m); ~der_rewriter(); ast_manager & m () const; void operator()(expr * t, expr_ref & result, proof_ref & result_pr); void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } void set_cancel(bool f); void cleanup(); void reset(); }; typedef der_rewriter der_star; #endif /* _DER_H_ */ z3-4.4.0/src/ast/rewriter/th_rewriter.h0000644000175000017500000000310412540347414017573 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: th_rewriter.h Abstract: Rewriter for applying all builtin (cheap) theory rewrite rules. Author: Leonardo (leonardo) 2011-04-07 Notes: --*/ #ifndef _TH_REWRITER_H_ #define _TH_REWRITER_H_ #include"ast.h" #include"rewriter_types.h" #include"params.h" class expr_substitution; class th_rewriter { struct imp; imp * m_imp; params_ref m_params; public: th_rewriter(ast_manager & m, params_ref const & p = params_ref()); ~th_rewriter(); ast_manager & m () const; void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); unsigned get_cache_size() const; unsigned get_num_steps() const; void operator()(expr_ref& term); void operator()(expr * t, expr_ref & result); void operator()(expr * t, expr_ref & result, proof_ref & result_pr); void operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result); void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } void set_cancel(bool f); void cleanup(); void reset(); void set_substitution(expr_substitution * s); // Dependency tracking is very coarse. // The rewriter just keeps accumulating the dependencies of the used substitutions. // The following methods are used to recover and reset them. // Remark: reset_used_dependecies will reset the internal cache if get_used_dependencies() != 0 expr_dependency * get_used_dependencies(); void reset_used_dependencies(); }; #endif z3-4.4.0/src/ast/rewriter/array_rewriter_params.pyg0000644000175000017500000000074612540347414022222 0ustar michaelmichaeldef_module_params(module_name='rewriter', class_name='array_rewriter_params', export=True, params=(("expand_select_store", BOOL, False, "replace a (select (store ...) ...) term by an if-then-else term"), ("expand_store_eq", BOOL, False, "reduce (store ...) = (store ...) with a common base into selects"), ("sort_store", BOOL, False, "sort nested stores when the indices are known to be different"))) z3-4.4.0/src/ast/rewriter/var_subst.h0000644000175000017500000000527512540347414017260 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: var_subst.h Abstract: Variable substitution. Author: Leonardo (leonardo) 2008-01-10 Notes: --*/ #ifndef _VAR_SUBST_H_ #define _VAR_SUBST_H_ #include"rewriter.h" #include"used_vars.h" /** \brief Alias for var_shifter class. */ typedef var_shifter shift_vars; /** \brief Variable substitution functor. It substitutes variables by expressions. The expressions may contain variables. */ class var_subst { beta_reducer m_reducer; bool m_std_order; public: var_subst(ast_manager & m, bool std_order = true):m_reducer(m), m_std_order(std_order) {} bool std_order() const { return m_std_order; } /** When std_order() == true, I'm using the same standard used in quantifier instantiation. (VAR 0) is stored in the last position of the array. ... (VAR (num_args - 1)) is stored in the first position of the array. Otherwise, (VAR 0) is stored in the first position, (VAR 1) in the second, and so on. */ void operator()(expr * n, unsigned num_args, expr * const * args, expr_ref & result); void reset() { m_reducer.reset(); } }; /** \brief Eliminate the unused variables from \c q. Store the result in \c r. */ class unused_vars_eliminator { ast_manager& m; var_subst m_subst; used_vars m_used; public: unused_vars_eliminator(ast_manager& m): m(m), m_subst(m) {} void operator()(quantifier* q, expr_ref& r); }; void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & r); /** \brief Instantiate quantifier q using the given exprs. The vector exprs should contain at least q->get_num_decls() expressions. I'm using the same standard used in quantifier instantiation. (VAR 0) is stored in the last position of the array. ... (VAR (q->get_num_decls() - 1)) is stored in the first position of the array. */ void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref & result); /** \brief Enumerate set of free variables in expression. Return the sorts of the free variables. */ class expr_free_vars { expr_sparse_mark m_mark; ptr_vector m_sorts; ptr_vector m_todo; public: void reset(); void operator()(expr* e); void accumulate(expr* e); bool empty() const { return m_sorts.empty(); } unsigned size() const { return m_sorts.size(); } sort* operator[](unsigned idx) const { return m_sorts[idx]; } bool contains(unsigned idx) const { return idx < m_sorts.size() && m_sorts[idx] != 0; } void set_default_sort(sort* s); void reverse() { m_sorts.reverse(); } sort*const* c_ptr() const { return m_sorts.c_ptr(); } }; #endif z3-4.4.0/src/ast/rewriter/dl_rewriter.cpp0000644000175000017500000000262712540347414020123 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_rewriter.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2010-08-10 Revision History: --*/ #include"dl_rewriter.h" br_status dl_rewriter::mk_app_core( func_decl * f, unsigned num_args, expr* const* args, expr_ref& result) { ast_manager& m = result.get_manager(); uint64 v1, v2; switch(f->get_decl_kind()) { case datalog::OP_DL_LT: if (m_util.is_numeral_ext(args[0], v1) && m_util.is_numeral_ext(args[1], v2)) { result = (v1 < v2)?m.mk_true():m.mk_false(); return BR_DONE; } // x < x <=> false if (args[0] == args[1]) { result = m.mk_false(); return BR_DONE; } // x < 0 <=> false if (m_util.is_numeral_ext(args[1], v2) && v2 == 0) { result = m.mk_false(); return BR_DONE; } // 0 < x <=> 0 != x if (m_util.is_numeral_ext(args[1], v1) && v1 == 0) { result = m.mk_not(m.mk_eq(args[0], args[1])); return BR_DONE; } break; default: break; } return BR_FAILED; } z3-4.4.0/src/ast/rewriter/poly_rewriter_params.pyg0000644000175000017500000000140012540347414022053 0ustar michaelmichaeldef_module_params(module_name='rewriter', class_name='poly_rewriter_params', export=True, params=(("som", BOOL, False, "put polynomials in som-of-monomials form"), ("som_blowup", UINT, UINT_MAX, "maximum number of monomials generated when putting a polynomial in sum-of-monomials normal form"), ("hoist_mul", BOOL, False, "hoist multiplication over summation to minimize number of multiplications"), ("hoist_cmul", BOOL, False, "hoist constant multiplication over summation to minimize number of multiplications"), ("flat", BOOL, True, "create nary applications for and,or,+,*,bvadd,bvmul,bvand,bvor,bvxor"))) z3-4.4.0/src/ast/rewriter/expr_replacer.cpp0000644000175000017500000001034612540347414020431 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: expr_replacer.cpp Abstract: Abstract (functor) for replacing constants with expressions. Author: Leonardo (leonardo) 2011-04-29 Notes: --*/ #include"expr_replacer.h" #include"rewriter_def.h" #include"th_rewriter.h" #include"cooperate.h" void expr_replacer::operator()(expr * t, expr_ref & result, proof_ref & result_pr) { expr_dependency_ref result_dep(m()); operator()(t, result, result_pr, result_dep); } void expr_replacer::operator()(expr * t, expr_ref & result) { proof_ref pr(m()); operator()(t, result, pr); } struct expr_replacer::scoped_set_subst { expr_replacer & m_r; scoped_set_subst(expr_replacer & r, expr_substitution & s):m_r(r) { m_r.set_substitution(&s); } ~scoped_set_subst() { m_r.set_substitution(0); } }; void expr_replacer::apply_substitution(expr * s, expr * def, proof * def_pr, expr_ref & t) { expr_substitution sub(m()); sub.insert(s, def, def_pr); scoped_set_subst set(*this, sub); (*this)(t); } void expr_replacer::apply_substitution(expr * s, expr * def, expr_ref & t) { expr_substitution sub(m()); sub.insert(s, def); scoped_set_subst set(*this, sub); (*this)(t); } struct default_expr_replacer_cfg : public default_rewriter_cfg { ast_manager & m; expr_substitution * m_subst; expr_dependency_ref m_used_dependencies; default_expr_replacer_cfg(ast_manager & _m): m(_m), m_subst(0), m_used_dependencies(_m) { } bool get_subst(expr * s, expr * & t, proof * & pr) { if (m_subst == 0) return false; expr_dependency * d = 0; if (m_subst->find(s, t, pr, d)) { m_used_dependencies = m.mk_join(m_used_dependencies, d); return true; } return false; } bool max_steps_exceeded(unsigned num_steps) const { cooperate("simplifier"); return false; } }; template class rewriter_tpl; class default_expr_replacer : public expr_replacer { default_expr_replacer_cfg m_cfg; rewriter_tpl m_replacer; public: default_expr_replacer(ast_manager & m): m_cfg(m), m_replacer(m, m.proofs_enabled(), m_cfg) { } virtual ast_manager & m() const { return m_replacer.m(); } virtual void set_substitution(expr_substitution * s) { m_replacer.cleanup(); m_replacer.cfg().m_subst = s; } virtual void operator()(expr * t, expr_ref & result, proof_ref & result_pr, expr_dependency_ref & result_dep) { result_dep = 0; m_replacer.operator()(t, result, result_pr); if (m_cfg.m_used_dependencies != 0) { result_dep = m_cfg.m_used_dependencies; m_replacer.reset(); // reset cache m_cfg.m_used_dependencies = 0; } } virtual void set_cancel(bool f) { m_replacer.set_cancel(f); } virtual unsigned get_num_steps() const { return m_replacer.get_num_steps(); } virtual void reset() { m_replacer.reset(); } }; expr_replacer * mk_default_expr_replacer(ast_manager & m) { return alloc(default_expr_replacer, m); } /** \brief Adapter for using th_rewriter as an expr_replacer. */ class th_rewriter2expr_replacer : public expr_replacer { th_rewriter m_r; public: th_rewriter2expr_replacer(ast_manager & m, params_ref const & p): m_r(m, p) { } virtual ~th_rewriter2expr_replacer() {} virtual ast_manager & m() const { return m_r.m(); } virtual void set_substitution(expr_substitution * s) { m_r.set_substitution(s); } virtual void operator()(expr * t, expr_ref & result, proof_ref & result_pr, expr_dependency_ref & result_dep) { m_r(t, result, result_pr); result_dep = m_r.get_used_dependencies(); m_r.reset_used_dependencies(); } virtual void set_cancel(bool f) { m_r.set_cancel(f); } virtual unsigned get_num_steps() const { return m_r.get_num_steps(); } virtual void reset() { m_r.reset(); } }; expr_replacer * mk_expr_simp_replacer(ast_manager & m, params_ref const & p) { return alloc(th_rewriter2expr_replacer, m, p); } z3-4.4.0/src/ast/rewriter/bool_rewriter.cpp0000644000175000017500000007575612540347414020474 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bool_rewriter.h Abstract: Basic rewrites for Boolean operators. Author: Leonardo (leonardo) 2011-04-04 Notes: --*/ #include"bool_rewriter.h" #include"bool_rewriter_params.hpp" #include"rewriter_def.h" void bool_rewriter::updt_params(params_ref const & _p) { bool_rewriter_params p(_p); m_flat = p.flat(); m_elim_and = p.elim_and(); m_local_ctx = p.local_ctx(); m_local_ctx_limit = p.local_ctx_limit(); m_blast_distinct = p.blast_distinct(); m_blast_distinct_threshold = p.blast_distinct_threshold(); m_ite_extra_rules = p.ite_extra_rules(); } void bool_rewriter::get_param_descrs(param_descrs & r) { bool_rewriter_params::collect_param_descrs(r); } br_status bool_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(f->get_family_id() == m().get_basic_family_id()); switch (f->get_decl_kind()) { case OP_EQ: case OP_IFF: SASSERT(num_args == 2); return mk_eq_core(args[0], args[1], result); case OP_DISTINCT: return mk_distinct_core(num_args, args, result); case OP_AND: return mk_and_core(num_args, args, result); case OP_OR: return mk_or_core(num_args, args, result); case OP_NOT: SASSERT(num_args == 1); return mk_not_core(args[0], result); case OP_ITE: SASSERT(num_args == 3); return mk_ite_core(args[0], args[1], args[2], result); case OP_IMPLIES: SASSERT(num_args == 2); mk_implies(args[0], args[1], result); return BR_DONE; case OP_XOR: SASSERT(num_args == 2); mk_xor(args[0], args[1], result); return BR_DONE; default: return BR_FAILED; } } void bool_rewriter::mk_and_as_or(unsigned num_args, expr * const * args, expr_ref & result) { expr_ref_buffer new_args(m()); for (unsigned i = 0; i < num_args; i++) { expr_ref tmp(m()); mk_not(args[i], tmp); new_args.push_back(tmp); } expr_ref tmp(m()); mk_or(new_args.size(), new_args.c_ptr(), tmp); mk_not(tmp, result); } br_status bool_rewriter::mk_nflat_and_core(unsigned num_args, expr * const * args, expr_ref & result) { bool s = false; ptr_buffer buffer; expr_fast_mark1 neg_lits; expr_fast_mark2 pos_lits; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (m().is_true(arg)) { s = true; continue; } if (m().is_false(arg)) { result = m().mk_false(); return BR_DONE; } if (m().is_not(arg)) { expr * atom = to_app(arg)->get_arg(0); if (neg_lits.is_marked(atom)) { s = true; continue; } if (pos_lits.is_marked(atom)) { result = m().mk_false(); return BR_DONE; } neg_lits.mark(atom); } else { if (pos_lits.is_marked(arg)) { s = true; continue; } if (neg_lits.is_marked(arg)) { result = m().mk_false(); return BR_DONE; } pos_lits.mark(arg); } buffer.push_back(arg); } unsigned sz = buffer.size(); switch(sz) { case 0: result = m().mk_true(); return BR_DONE; case 1: result = buffer.back(); return BR_DONE; default: if (s) { result = m().mk_and(sz, buffer.c_ptr()); return BR_DONE; } return BR_FAILED; } } br_status bool_rewriter::mk_flat_and_core(unsigned num_args, expr * const * args, expr_ref & result) { unsigned i; for (i = 0; i < num_args; i++) { if (m().is_and(args[i])) break; } if (i < num_args) { // has nested ANDs ptr_buffer flat_args; flat_args.append(i, args); for (; i < num_args; i++) { expr * arg = args[i]; // Remark: all rewrites are depth 1. if (m().is_and(arg)) { unsigned num = to_app(arg)->get_num_args(); for (unsigned j = 0; j < num; j++) flat_args.push_back(to_app(arg)->get_arg(j)); } else { flat_args.push_back(arg); } } if (mk_nflat_and_core(flat_args.size(), flat_args.c_ptr(), result) == BR_FAILED) result = m().mk_and(flat_args.size(), flat_args.c_ptr()); return BR_DONE; } return mk_nflat_and_core(num_args, args, result); } br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args, expr_ref & result) { bool s = false; ptr_buffer buffer; expr_fast_mark1 neg_lits; expr_fast_mark2 pos_lits; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (m().is_true(arg)) { result = m().mk_true(); return BR_DONE; } if (m().is_false(arg)) { s = true; continue; } if (m().is_not(arg)) { expr * atom = to_app(arg)->get_arg(0); if (neg_lits.is_marked(atom)) { s = true; continue; } if (pos_lits.is_marked(atom)) { result = m().mk_true(); return BR_DONE; } neg_lits.mark(atom); } else { if (pos_lits.is_marked(arg)) { s = true; continue; } if (neg_lits.is_marked(arg)) { result = m().mk_true(); return BR_DONE; } pos_lits.mark(arg); } buffer.push_back(arg); } unsigned sz = buffer.size(); switch(sz) { case 0: result = m().mk_false(); return BR_DONE; case 1: result = buffer.back(); return BR_DONE; default: if (m_local_ctx && m_local_ctx_cost <= m_local_ctx_limit) { neg_lits.reset(); pos_lits.reset(); if (local_ctx_simp(sz, buffer.c_ptr(), result)) return BR_DONE; } if (s) { result = m().mk_or(sz, buffer.c_ptr()); return BR_DONE; } return BR_FAILED; } } br_status bool_rewriter::mk_flat_or_core(unsigned num_args, expr * const * args, expr_ref & result) { unsigned i; for (i = 0; i < num_args; i++) { if (m().is_or(args[i])) break; } if (i < num_args) { // has nested ORs ptr_buffer flat_args; flat_args.append(i, args); for (; i < num_args; i++) { expr * arg = args[i]; // Remark: all rewrites are depth 1. if (m().is_or(arg)) { unsigned num = to_app(arg)->get_num_args(); for (unsigned j = 0; j < num; j++) flat_args.push_back(to_app(arg)->get_arg(j)); } else { flat_args.push_back(arg); } } if (mk_nflat_or_core(flat_args.size(), flat_args.c_ptr(), result) == BR_FAILED) result = m().mk_or(flat_args.size(), flat_args.c_ptr()); return BR_DONE; } return mk_nflat_or_core(num_args, args, result); } expr * bool_rewriter::mk_or_app(unsigned num_args, expr * const * args) { switch(num_args) { case 0: return m().mk_false(); case 1: return args[0]; default: return m().mk_or(num_args, args); } } /** \brief Auxiliary method for local_ctx_simp. Replace args[i] by true if marked in neg_lits. Replace args[i] by false if marked in pos_lits. */ bool bool_rewriter::simp_nested_not_or(unsigned num_args, expr * const * args, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result) { ptr_buffer new_args; bool simp = false; m_local_ctx_cost += num_args; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (neg_lits.is_marked(arg)) { result = m().mk_false(); return true; } if (pos_lits.is_marked(arg)) { simp = true; continue; } if (m().is_not(arg)) { expr * atom = to_app(arg)->get_arg(0); if (neg_lits.is_marked(atom)) { simp = true; continue; } if (pos_lits.is_marked(atom)) { result = m().mk_false(); return true; } } new_args.push_back(arg); } if (simp) { switch(new_args.size()) { case 0: result = m().mk_true(); return true; case 1: mk_not(new_args[0], result); return true; default: result = m().mk_not(m().mk_or(new_args.size(), new_args.c_ptr())); return true; } } return false; } expr * bool_rewriter::simp_arg(expr * arg, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, bool & modified) { if (m().is_not(arg)) { expr * atom = to_app(arg)->get_arg(0); if (neg_lits.is_marked(atom)) { modified = true; return m().mk_false(); } if (pos_lits.is_marked(atom)) { modified = true; return m().mk_true(); } return arg; } else { if (neg_lits.is_marked(arg)) { modified = true; return m().mk_true(); } if (pos_lits.is_marked(arg)) { modified = true; return m().mk_false(); } return arg; } } /** \brief Simpler version of mk_ite, that will not invoke mk_or/mk_and. It is used byt local_ctx_simp to prevent a recursive call to local_ctx_simp. See comment at simp_nested_eq_ite. */ void bool_rewriter::mk_nested_ite(expr * c, expr * t, expr * e, expr_ref & result) { if (m().is_true(c)) { result = t; return; } if (m().is_false(c)) { result = e; return; } if (t == e) { result = t; return; } if (m().is_bool(t)) { if (m().is_true(t)) { if (m().is_false(e)) { result = c; return; } result = m().mk_or(c, e); return; } if (m().is_false(t)) { if (m().is_true(e)) { mk_not(c, result); return; } expr_ref tmp(m()); mk_not(e, tmp); result = m().mk_not(m().mk_or(c, tmp)); return; } if (m().is_true(e)) { expr_ref tmp(m()); mk_not(c, tmp); result = m().mk_or(tmp, t); return; } if (m().is_false(e) || c == e) { expr_ref tmp1(m()); expr_ref tmp2(m()); mk_not(c, tmp1); mk_not(t, tmp2); result = m().mk_not(m().mk_or(tmp1, tmp2)); return; } if (c == t) { result = m().mk_or(c, e); return; } if (m().is_complement_core(t, e)) { // t = not(e) mk_eq(c, t, result); return; } if (m().is_complement_core(e, t)) { // e = not(t) mk_eq(c, t, result); return; } } result = m().mk_ite(c, t, e); } bool bool_rewriter::simp_nested_eq_ite(expr * t, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result) { bool neg = false; m_local_ctx_cost += 3; if (m().is_not(t)) { neg = true; t = to_app(t)->get_arg(0); } if (m().is_iff(t) || m().is_eq(t)) { bool modified = false; expr * new_lhs = simp_arg(to_app(t)->get_arg(0), neg_lits, pos_lits, modified); expr * new_rhs = simp_arg(to_app(t)->get_arg(1), neg_lits, pos_lits, modified); if (!modified) return false; mk_eq(new_lhs, new_rhs, result); if (neg) mk_not(result, result); return true; } if (m().is_ite(t)) { bool modified = false; expr * new_c = simp_arg(to_app(t)->get_arg(0), neg_lits, pos_lits, modified); expr * new_t = simp_arg(to_app(t)->get_arg(1), neg_lits, pos_lits, modified); expr * new_e = simp_arg(to_app(t)->get_arg(2), neg_lits, pos_lits, modified); if (!modified) return false; // It is not safe to invoke mk_ite here, since it can recursively call // local_ctx_simp by // - transforming the ITE into an OR // - and invoked mk_or, that will invoke local_ctx_simp // mk_ite(new_c, new_t, new_e, result); mk_nested_ite(new_c, new_t, new_e, result); if (neg) mk_not(result, result); return true; } return false; } /** \brief Apply local context simplification at (OR args[0] ... args[num_args-1]) Basic idea: - Replace args[i] by false in the other arguments - If args[i] is of the form (not t), then replace t by true in the other arguments. To make sure the simplification is efficient we bound the depth. */ bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_ref & result) { expr_ref_vector old_args(m()); expr_ref_vector new_args(m()); expr_ref new_arg(m()); expr_fast_mark1 neg_lits; expr_fast_mark2 pos_lits; bool simp = false; bool modified = false; bool forward = true; unsigned rounds = 0; while (true) { rounds++; #if 0 if (rounds > 10) verbose_stream() << "rounds: " << rounds << "\n"; #endif #define PUSH_NEW_ARG(ARG) { \ new_args.push_back(ARG); \ if (m().is_not(ARG)) \ neg_lits.mark(to_app(ARG)->get_arg(0)); \ else \ pos_lits.mark(ARG); \ } #define PROCESS_ARG() \ { \ expr * arg = args[i]; \ if (m().is_not(arg) && m().is_or(to_app(arg)->get_arg(0)) && \ simp_nested_not_or(to_app(to_app(arg)->get_arg(0))->get_num_args(), \ to_app(to_app(arg)->get_arg(0))->get_args(), \ neg_lits, \ pos_lits, \ new_arg)) { \ modified = true; simp = true; \ arg = new_arg; \ } \ if (simp_nested_eq_ite(arg, neg_lits, pos_lits, new_arg)) { \ modified = true; simp = true; \ arg = new_arg; \ } \ if (m().is_false(arg)) \ continue; \ if (m().is_true(arg)) { \ result = arg; \ return true; \ } \ if (m_flat && m().is_or(arg)) { \ unsigned sz = to_app(arg)->get_num_args(); \ for (unsigned j = 0; j < sz; j++) { \ expr * arg_arg = to_app(arg)->get_arg(j); \ PUSH_NEW_ARG(arg_arg); \ } \ } \ else { \ PUSH_NEW_ARG(arg); \ } \ } m_local_ctx_cost += 2*num_args; #if 0 static unsigned counter = 0; counter++; if (counter % 10000 == 0) verbose_stream() << "local-ctx-cost: " << m_local_ctx_cost << "\n"; #endif if (forward) { for (unsigned i = 0; i < num_args; i++) { PROCESS_ARG(); } forward = false; } else { unsigned i = num_args; while (i > 0) { --i; PROCESS_ARG(); } if (!modified) { if (simp) { result = mk_or_app(num_args, args); return true; } return false; // didn't simplify } // preserve the original order... std::reverse(new_args.c_ptr(), new_args.c_ptr() + new_args.size()); modified = false; forward = true; } pos_lits.reset(); neg_lits.reset(); old_args.reset(); old_args.swap(new_args); SASSERT(new_args.empty()); args = old_args.c_ptr(); num_args = old_args.size(); } } /** \brief Apply simplification if ite is an if-then-else tree where every leaf is a value. This is an efficient way to */ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result) { SASSERT(m().is_ite(ite)); SASSERT(m().is_value(val)); expr * t = ite->get_arg(1); expr * e = ite->get_arg(2); if (!m().is_value(t) || !m().is_value(e)) return BR_FAILED; if (t != val && e != val) { TRACE("try_ite_value", tout << mk_ismt2_pp(t, m()) << " " << mk_ismt2_pp(e, m()) << " " << mk_ismt2_pp(val, m()) << "\n"; tout << t << " " << e << " " << val << "\n";); result = m().mk_false(); return BR_DONE; } if (t == val && e == val) { result = m().mk_true(); return BR_DONE; } if (t == val) { result = ite->get_arg(0); return BR_DONE; } SASSERT(e == val); mk_not(ite->get_arg(0), result); return BR_DONE; } #if 0 // Return true if ite is an if-then-else tree where the leaves are values, // and they are all different from val static bool is_ite_value_tree_neq_value(ast_manager & m, app * ite, app * val) { SASSERT(m.is_ite(ite)); SASSERT(m.is_value(val)); expr_fast_mark1 visited; ptr_buffer todo; todo.push_back(ite); #define VISIT(ARG) { \ if (m.is_value(ARG)) { \ if (ARG == val) \ return false; \ } \ else if (m.is_ite(ARG)) { \ if (!visited.is_marked(ARG)) { \ visited.mark(ARG); \ todo.push_back(to_app(ARG)); \ } \ } \ else { \ return false; \ } \ } while (!todo.empty()) { app * ite = todo.back(); todo.pop_back(); SASSERT(m.is_ite(ite)); expr * t = ite->get_arg(1); expr * e = ite->get_arg(2); VISIT(t); VISIT(e); } return true; } #endif br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { if (lhs == rhs) { result = m().mk_true(); return BR_DONE; } if (m().are_distinct(lhs, rhs)) { result = m().mk_false(); return BR_DONE; } br_status r = BR_FAILED; if (m().is_ite(lhs) && m().is_value(rhs)) { // if (is_ite_value_tree_neq_value(m(), to_app(lhs), to_app(rhs))) { // result = m().mk_false(); // return BR_DONE; // } r = try_ite_value(to_app(lhs), to_app(rhs), result); CTRACE("try_ite_value", r != BR_FAILED, tout << mk_ismt2_pp(lhs, m()) << "\n" << mk_ismt2_pp(rhs, m()) << "\n--->\n" << mk_ismt2_pp(result, m()) << "\n";); } else if (m().is_ite(rhs) && m().is_value(lhs)) { // if (is_ite_value_tree_neq_value(m(), to_app(rhs), to_app(lhs))) { // result = m().mk_false(); // return BR_DONE; // } r = try_ite_value(to_app(rhs), to_app(lhs), result); CTRACE("try_ite_value", r != BR_FAILED, tout << mk_ismt2_pp(lhs, m()) << "\n" << mk_ismt2_pp(rhs, m()) << "\n--->\n" << mk_ismt2_pp(result, m()) << "\n";); } if (r != BR_FAILED) return r; if (m().is_bool(lhs)) { bool unfolded = false; if (m().is_not(lhs) && m().is_not(rhs)) { lhs = to_app(lhs)->get_arg(0); rhs = to_app(rhs)->get_arg(0); unfolded = true; } if (m().is_true(lhs)) { result = rhs; return BR_DONE; } if (m().is_false(lhs)) { mk_not(rhs, result); return BR_DONE; } if (m().is_true(rhs)) { result = lhs; return BR_DONE; } if (m().is_false(rhs)) { mk_not(lhs, result); return BR_DONE; } if (m().is_complement(lhs, rhs)) { result = m().mk_false(); return BR_DONE; } if (unfolded) { result = m().mk_eq(lhs, rhs); return BR_DONE; } } return BR_FAILED; } br_status bool_rewriter::mk_distinct_core(unsigned num_args, expr * const * args, expr_ref & result) { if (num_args <= 1) { result = m().mk_true(); return BR_DONE; } if (num_args == 2) { expr_ref tmp(m()); result = m().mk_not(m().mk_eq(args[0], args[1])); return BR_REWRITE2; // mk_eq may be dispatched to other rewriters. } expr_fast_mark1 visited; bool all_value = true; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (visited.is_marked(arg)) { result = m().mk_false(); return BR_DONE; } visited.mark(arg); if (!m().is_unique_value(arg)) all_value = false; } if (all_value) { result = m().mk_true(); return BR_DONE; } SASSERT(num_args > 2); if (m().is_bool(args[0])) { result = m().mk_false(); return BR_DONE; } if (m_blast_distinct && num_args < m_blast_distinct_threshold) { ptr_buffer new_diseqs; for (unsigned i = 0; i < num_args; i++) { for (unsigned j = i + 1; j < num_args; j++) new_diseqs.push_back(m().mk_not(m().mk_eq(args[i], args[j]))); } result = m().mk_and(new_diseqs.size(), new_diseqs.c_ptr()); return BR_REWRITE3; } return BR_FAILED; } br_status bool_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & result) { bool s = false; // (ite (not c) a b) ==> (ite c b a) if (m().is_not(c)) { c = to_app(c)->get_arg(0); std::swap(t, e); s = true; } // (ite c (ite c t1 t2) t3) ==> (ite c t1 t3) if (m().is_ite(t) && to_app(t)->get_arg(0) == c) { // Remark: (ite c (ite (not c) t1 t2) t3) ==> (ite c t2 t3) does not happen if applying rewrites bottom up t = to_app(t)->get_arg(1); s = true; } // (ite c t1 (ite c t2 t3)) ==> (ite c t1 t3) if (m().is_ite(e) && to_app(e)->get_arg(0) == c) { // Remark: (ite c t1 (ite (not c) t2 t3)) ==> (ite c t1 t2) does not happen if applying rewrites bottom up e = to_app(e)->get_arg(2); s = true; } if (m().is_true(c)) { result = t; return BR_DONE; } if (m().is_false(c)) { result = e; return BR_DONE; } if (t == e) { result = t; return BR_DONE; } if (m().is_bool(t)) { if (m().is_true(t)) { if (m().is_false(e)) { result = c; return BR_DONE; } mk_or(c, e, result); return BR_DONE; } if (m().is_false(t)) { if (m().is_true(e)) { mk_not(c, result); return BR_DONE; } expr_ref tmp(m()); mk_not(c, tmp); mk_and(tmp, e, result); return BR_DONE; } if (m().is_true(e)) { expr_ref tmp(m()); mk_not(c, tmp); mk_or(tmp, t, result); return BR_DONE; } if (m().is_false(e)) { mk_and(c, t, result); return BR_DONE; } if (c == e) { mk_and(c, t, result); return BR_DONE; } if (c == t) { mk_or(c, e, result); return BR_DONE; } if (m().is_complement_core(t, e)) { // t = not(e) mk_eq(c, t, result); return BR_DONE; } if (m().is_complement_core(e, t)) { // e = not(t) mk_eq(c, t, result); return BR_DONE; } } if (m().is_ite(t) && m_ite_extra_rules) { // (ite c1 (ite c2 t1 t2) t1) ==> (ite (and c1 (not c2)) t2 t1) if (e == to_app(t)->get_arg(1)) { expr_ref not_c2(m()); mk_not(to_app(t)->get_arg(0), not_c2); expr_ref new_c(m()); mk_and(c, not_c2, new_c); result = m().mk_ite(new_c, to_app(t)->get_arg(2), e); return BR_REWRITE1; } // (ite c1 (ite c2 t1 t2) t2) ==> (ite (and c1 c2) t1 t2) if (e == to_app(t)->get_arg(2)) { expr_ref new_c(m()); mk_and(c, to_app(t)->get_arg(0), new_c); result = m().mk_ite(new_c, to_app(t)->get_arg(1), e); return BR_REWRITE1; } if (m().is_ite(e)) { // (ite c1 (ite c2 t1 t2) (ite c3 t1 t2)) ==> (ite (or (and c1 c2) (and (not c1) c3)) t1 t2) if (to_app(t)->get_arg(1) == to_app(e)->get_arg(1) && to_app(t)->get_arg(2) == to_app(e)->get_arg(2)) { expr_ref and1(m()); expr_ref and2(m()); expr_ref notc(m()); mk_and(c, to_app(t)->get_arg(0), and1); mk_not(c, notc); mk_and(notc, to_app(e)->get_arg(0), and2); expr_ref new_c(m()); mk_or(and1, and2, new_c); result = m().mk_ite(new_c, to_app(t)->get_arg(1), to_app(t)->get_arg(2)); return BR_REWRITE1; } // (ite c1 (ite c2 t1 t2) (ite c3 t2 t1)) ==> (ite (or (and c1 c2) (and (not c1) (not c3))) t1 t2) if (to_app(t)->get_arg(1) == to_app(e)->get_arg(2) && to_app(t)->get_arg(2) == to_app(e)->get_arg(1)) { expr_ref and1(m()); expr_ref and2(m()); expr_ref notc(m()); mk_and(c, to_app(t)->get_arg(0), and1); mk_not(c, notc); expr_ref notc3(m()); mk_not(to_app(e)->get_arg(0), notc3); mk_and(notc, notc3, and2); expr_ref new_c(m()); mk_or(and1, and2, new_c); result = m().mk_ite(new_c, to_app(t)->get_arg(1), to_app(t)->get_arg(2)); return BR_REWRITE1; } } } if (m().is_ite(e) && m_ite_extra_rules) { // (ite c1 t1 (ite c2 t1 t2)) ==> (ite (or c1 c2) t1 t2) if (t == to_app(e)->get_arg(1)) { expr_ref new_c(m()); mk_or(c, to_app(e)->get_arg(0), new_c); result = m().mk_ite(new_c, t, to_app(e)->get_arg(2)); return BR_REWRITE1; } // (ite c1 t1 (ite c2 t2 t1)) ==> (ite (or c1 (not c2)) t1 t2) if (t == to_app(e)->get_arg(2)) { expr_ref not_c2(m()); mk_not(to_app(e)->get_arg(0), not_c2); expr_ref new_c(m()); mk_or(c, not_c2, new_c); result = m().mk_ite(new_c, t, to_app(e)->get_arg(1)); return BR_REWRITE1; } } if (s) { result = m().mk_ite(c, t, e); return BR_DONE; } return BR_FAILED; } br_status bool_rewriter::mk_not_core(expr * t, expr_ref & result) { if (m().is_not(t)) { result = to_app(t)->get_arg(0); return BR_DONE; } if (m().is_true(t)) { result = m().mk_false(); return BR_DONE; } if (m().is_false(t)) { result = m().mk_true(); return BR_DONE; } if (is_eq(t) && m().is_bool(to_app(t)->get_arg(0))) { expr_ref tmp(m()); mk_not(to_app(t)->get_arg(0), tmp); mk_eq(tmp, to_app(t)->get_arg(1), result); return BR_DONE; } return BR_FAILED; } void bool_rewriter::mk_xor(expr * lhs, expr * rhs, expr_ref & result) { expr_ref tmp(m()); mk_not(lhs, tmp); mk_eq(tmp, rhs, result); } void bool_rewriter::mk_implies(expr * lhs, expr * rhs, expr_ref & result) { expr_ref tmp(m()); mk_not(lhs, tmp); mk_or(tmp, rhs, result); } void bool_rewriter::mk_nand(unsigned num_args, expr * const * args, expr_ref & result) { expr_ref tmp(m_manager); mk_and(num_args, args, tmp); mk_not(tmp, result); } void bool_rewriter::mk_nor(unsigned num_args, expr * const * args, expr_ref & result) { expr_ref tmp(m_manager); mk_or(num_args, args, tmp); mk_not(tmp, result); } void bool_rewriter::mk_nand(expr * arg1, expr * arg2, expr_ref & result) { expr_ref tmp(m_manager); mk_and(arg1, arg2, tmp); mk_not(tmp, result); } void bool_rewriter::mk_nor(expr * arg1, expr * arg2, expr_ref & result) { expr_ref tmp(m_manager); mk_or(arg1, arg2, tmp); mk_not(tmp, result); } template class rewriter_tpl; z3-4.4.0/src/ast/rewriter/rewriter.txt0000644000175000017500000001266412540347414017503 0ustar michaelmichael The following classes implement theory specific rewriting rules: - bool_rewriter - arith_rewriter - bv_rewriter - array_rewriter - datatype_rewriter - fpa_rewriter Each of them provide the method br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) where - f is expected to be a func_decl of the given theory - If the return value if different from BR_FAILED, the result is stored in result. The template rewriter_tpl can be instantiated to implement rewriters that traverse ASTs applying some kind of transformation/simplification. The parameter Cfg is expected to be a class with the following methods: bool cache_all_results() const; bool cache_results() const; bool flat_assoc(func_decl * f) const; bool rewrite_patterns() const; bool max_scopes_exceeded(unsigned num_scopes) const; bool max_frames_exceeded(unsigned num_frames) const; bool max_steps_exceeded(unsigned num_steps) const; bool pre_visit(expr * t); br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr); bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr); bool get_macro(func_decl * d, expr * & def, quantifier * & q, proof * & def_pr); bool get_subst(expr * s, expr * & t, proof * & t_pr); void reset(); void cleanup(); The class default_rewriter_cfg provides a default implementation for these methods. The method reduce_app is the most important one. When implementing new rewriter, we usually redefine this method. We should return BR_FAILED, if we do not want to apply a "simplification" to (f args[0] ... args[num-1]) This is very important for performance reasons, since the rewriter tries to reuse AST when BR_FAILED is returned. If one wants to simply (f args[0] ... args[num-1]) to t, then use: result = t; return BR_DONE; Sometimes, by applying a simplification rule we may create new opportunites for rewriting. One can instruct the rewriter to revisit the result by returning: BR_REWRITE1, // rewrite the result (bounded by depth 1) BR_REWRITE2, // rewrite the result (bounded by depth 2) BR_REWRITE3, // rewrite the result (bounded by depth 3) BR_REWRITE_FULL, // rewrite the result unbounded Example: Suppose one wants to implement the rewriting rule (= (ite c t e) v) --> (ite c (= t v) (= e v)) This rule may create new opportunites for simplification since it creates the equalities (= t v) and (= e v). So, to force the rewriter to process these new equalities, BR_REWRITE2 should be returned. It is also correct (but unnecessary) to return BR_REWRITE3 and BR_REWRITE_FULL. To instantiate a new rewriter, one should 1) Define a new configuration class. class mycfg : public default_rewriter_cfg { ... } 2) Force template instantiation using the new cfg. templace class rewriter_tpl; 3) Define a subclass of rewriter_tpl that owns the new cfg. class myrewriter : public rewriter_tpl { mycfg m_cfg; public: myrewriter(ast_manager & m): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(...) { } }; The rewriter_tpl template supports proof production. It can be disabled even when proof productions is enabled in the ast_manager. Examples of rewriter_tpl instances: - th_rewriter.cpp - assertion_set_bit_blaster.cpp - model_evaluator.cpp - elim_distinct.cpp Notes for additional Cfg methods: - bool rewrite_patterns() const; If false is returned, then the rewriter will ignore patterns. - bool pre_visit(expr * t); If false is returned, then the rewriter will not process t. This is very useful, for example, for implementing rewriters that will only "process" the Boolean skeleton. - bool max_steps_exceeded(unsigned num_steps) const; If false, it interrupts the execution of the rewriter. The interruption is performed by throwing an exception. One can also used this callback to check whether the amount of memory used is still reasonable. - bool cache_all_results() const; By default, the rewriter only caches the results of non-atomic shared ASTs (get_ref_count() > 1). If true, is returned, the rewriter will cache all intermediate results. - bool flat_assoc(func_decl * f) const; If true is returned, then the rewriter will do flattening of non-shared nodes. For example, (+ a (+ b (+ c d))) will be treated as (+ a b c d) Shared nodes are not considered. Thus, exponential blowup in the rewriter stack is avoided. So, when implementing br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr); one should not assume that for an associative f, there is not f-application in args when true is returned by flat_assoc. when implementing br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr); If result_pr is set to 0, and proofs are enabled, then the rewriter will use a generic "rewrite" proof step for justifying that (f args[0] ... args[num-1]) is equal to result. The th_rewriter takes care of flattening. So, in principle, there is not need to return true in flat_assoc. z3-4.4.0/src/ast/rewriter/bv_rewriter.h0000644000175000017500000002022012540347414017565 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv_rewriter.h Abstract: Basic rewriting rules for bit-vectors Author: Leonardo (leonardo) 2011-04-14 Notes: --*/ #ifndef _BV_REWRITER_H_ #define _BV_REWRITER_H_ #include"poly_rewriter.h" #include"bv_decl_plugin.h" #include"arith_decl_plugin.h" class mk_extract_proc { bv_util & m_util; unsigned m_high; unsigned m_low; sort * m_domain; func_decl * m_f_cached; public: mk_extract_proc(bv_util & u); ~mk_extract_proc(); app * operator()(unsigned high, unsigned low, expr * arg); }; class bv_rewriter_core { protected: typedef rational numeral; bv_util m_util; ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } bool is_numeral(expr * n) const { return m_util.is_numeral(n); } bool is_numeral(expr * n, numeral & r) const { unsigned sz; return m_util.is_numeral(n, r, sz); } bool is_zero(expr * n) const { return m_util.is_zero(n); } bool is_minus_one(expr * n) const { return m_util.is_allone(n); } void normalize(numeral & c, sort * s) { unsigned bv_size = m_util.get_bv_size(s); c = m_util.norm(c, bv_size); } app * mk_numeral(numeral const & r, sort * s) { return m_util.mk_numeral(r, s); } decl_kind add_decl_kind() const { return OP_BADD; } decl_kind mul_decl_kind() const { return OP_BMUL; } bool use_power() const { return false; } decl_kind power_decl_kind() const { UNREACHABLE(); return static_cast(UINT_MAX); } public: bv_rewriter_core(ast_manager & m):m_util(m) {} }; class bv_rewriter : public poly_rewriter { mk_extract_proc m_mk_extract; arith_util m_autil; bool m_hi_div0; bool m_elim_sign_ext; bool m_mul2concat; bool m_bit2bool; bool m_blast_eq_value; bool m_mkbv2num; bool m_split_concat_eq; bool m_udiv2mul; bool m_bvnot2arith; bool m_bv_sort_ac; bool is_zero_bit(expr * x, unsigned idx); br_status mk_ule(expr * a, expr * b, expr_ref & result); br_status mk_uge(expr * a, expr * b, expr_ref & result); br_status mk_ult(expr * a, expr * b, expr_ref & result); br_status mk_sle(expr * a, expr * b, expr_ref & result); br_status mk_sge(expr * a, expr * b, expr_ref & result); br_status mk_slt(expr * a, expr * b, expr_ref & result); br_status mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref & result); br_status mk_concat(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_extract(unsigned high, unsigned low, expr * arg, expr_ref & result); br_status mk_repeat(unsigned n, expr * arg, expr_ref & result); br_status mk_zero_extend(unsigned n, expr * arg, expr_ref & result); br_status mk_sign_extend(unsigned n, expr * arg, expr_ref & result); br_status mk_bv_not(expr * arg, expr_ref & result); br_status mk_bv_or(unsigned num, expr * const * args, expr_ref & result); br_status mk_bv_xor(unsigned num, expr * const * args, expr_ref & result); br_status mk_bv_and(unsigned num, expr * const * args, expr_ref & result); br_status mk_bv_nand(unsigned num, expr * const * args, expr_ref & result); br_status mk_bv_nor(unsigned num, expr * const * args, expr_ref & result); br_status mk_bv_xnor(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_bv_rotate_left(unsigned n, expr * arg, expr_ref & result); br_status mk_bv_rotate_right(unsigned n, expr * arg, expr_ref & result); br_status mk_bv_ext_rotate_left(expr * arg1, expr * arg2, expr_ref & result); br_status mk_bv_ext_rotate_right(expr * arg1, expr * arg2, expr_ref & result); br_status mk_bv_add(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_bv_add(expr * arg1, expr * arg2, expr_ref & result) { expr * args[2] = { arg1, arg2 }; return mk_bv_add(2, args, result); } br_status mk_bv_mul(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result); br_status mk_bv_lshr(expr * arg1, expr * arg2, expr_ref & result); br_status mk_bv_ashr(expr * arg1, expr * arg2, expr_ref & result); bool is_minus_one_core(expr * arg) const; bool is_x_minus_one(expr * arg, expr * & x); br_status mk_bv_sdiv_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result); br_status mk_bv_udiv_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result); br_status mk_bv_srem_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result); br_status mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result); br_status mk_bv_smod_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result); br_status mk_bv_sdiv(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_sdiv_core(arg1, arg2, m_hi_div0, result); } br_status mk_bv_udiv(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_udiv_core(arg1, arg2, m_hi_div0, result); } br_status mk_bv_srem(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_srem_core(arg1, arg2, m_hi_div0, result); } br_status mk_bv_urem(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_urem_core(arg1, arg2, m_hi_div0, result); } br_status mk_bv_smod(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_smod_core(arg1, arg2, m_hi_div0, result); } br_status mk_bv_sdiv_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_sdiv_core(arg1, arg2, true, result); } br_status mk_bv_udiv_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_udiv_core(arg1, arg2, true, result); } br_status mk_bv_srem_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_srem_core(arg1, arg2, true, result); } br_status mk_bv_urem_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_urem_core(arg1, arg2, true, result); } br_status mk_bv_smod_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_smod_core(arg1, arg2, true, result); } br_status mk_int2bv(unsigned bv_size, expr * arg, expr_ref & result); br_status mk_bv2int(expr * arg, expr_ref & result); br_status mk_bv_redor(expr * arg, expr_ref & result); br_status mk_bv_redand(expr * arg, expr_ref & result); br_status mk_bv_comp(expr * arg1, expr * arg2, expr_ref & result); br_status mk_bit2bool(expr * lhs, expr * rhs, expr_ref & result); br_status mk_blast_eq_value(expr * lhs, expr * rhs, expr_ref & result); br_status mk_eq_concat(expr * lhs, expr * rhs, expr_ref & result); br_status mk_mkbv(unsigned num, expr * const * args, expr_ref & result); bool is_minus_one_times_t(expr * arg); void mk_t1_add_t2_eq_c(expr * t1, expr * t2, expr * c, expr_ref & result); bool is_concat_split_target(expr * t) const; br_status mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result); void updt_local_params(params_ref const & p); public: bv_rewriter(ast_manager & m, params_ref const & p = params_ref()): poly_rewriter(m, p), m_mk_extract(m_util), m_autil(m) { updt_local_params(p); } void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); void set_mkbv2num(bool f) { m_mkbv2num = f; } unsigned get_bv_size(expr * t) const {return m_util.get_bv_size(t); } bool is_numeral(expr * t) const { return m_util.is_numeral(t); } bool is_numeral(expr * t, numeral & r, unsigned & sz) const { return m_util.is_numeral(t, r, sz); } bool is_bv(expr * t) const { return m_util.is_bv(t); } expr * mk_numeral(numeral const & v, unsigned sz) { return m_util.mk_numeral(v, sz); } expr * mk_numeral(unsigned v, unsigned sz) { return m_util.mk_numeral(numeral(v), sz); } br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_app_core(f, num_args, args, result) == BR_FAILED) result = m().mk_app(f, num_args, args); } br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result); }; #endif z3-4.4.0/src/ast/rewriter/arith_rewriter_params.pyg0000644000175000017500000000302512540347414022204 0ustar michaelmichaeldef_module_params(module_name='rewriter', class_name='arith_rewriter_params', export=True, params=(("algebraic_number_evaluator", BOOL, True, "simplify/evaluate expressions containing (algebraic) irrational numbers."), ("mul_to_power", BOOL, False, "collpase (* t ... t) into (^ t k), it is ignored if expand_power is true."), ("expand_power", BOOL, False, "expand (^ t k) into (* t ... t) if 1 < k <= max_degree."), ("expand_tan", BOOL, False, "replace (tan x) with (/ (sin x) (cos x))."), ("max_degree", UINT, 64, "max degree of algebraic numbers (and power operators) processed by simplifier."), ("eq2ineq", BOOL, False, "split arithmetic equalities into two inequalities."), ("sort_sums", BOOL, False, "sort the arguments of + application."), ("gcd_rounding", BOOL, False, "use gcd rounding on integer arithmetic atoms."), ("arith_lhs", BOOL, False, "all monomials are moved to the left-hand-side, and the right-hand-side is just a constant."), ("elim_to_real", BOOL, False, "eliminate to_real from arithmetic predicates that contain only integers."), ("push_to_real", BOOL, True, "distribute to_real over * and +."), ("elim_rem", BOOL, False, "replace (rem x y) with (ite (>= y 0) (mod x y) (- (mod x y)))."))) z3-4.4.0/src/ast/rewriter/expr_safe_replace.h0000644000175000017500000000172312540347414020711 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: expr_safe_replace.h Abstract: Version of expr_replace/expr_substitution that is safe for quantifiers. Author: Nikolaj Bjorner (nbjorner) 2012-11-30 Revision History: --*/ #ifndef __EXPR_SAFE_REPLACE_H__ #define __EXPR_SAFE_REPLACE_H__ #include "ast.h" class expr_safe_replace { ast_manager& m; expr_ref_vector m_src; expr_ref_vector m_dst; obj_map m_subst; obj_map m_cache; ptr_vector m_todo, m_args; expr_ref_vector m_refs; public: expr_safe_replace(ast_manager& m): m(m), m_src(m), m_dst(m), m_refs(m) {} void insert(expr* src, expr* dst); void operator()(expr_ref& e) { (*this)(e.get(), e); } void operator()(expr* src, expr_ref& e); void apply_substitution(expr* s, expr* def, expr_ref& t); void reset(); bool empty() const { return m_subst.empty(); } }; #endif /* __EXPR_SAFE_REPLACE_H__ */ z3-4.4.0/src/ast/rewriter/rewriter_params.pyg0000644000175000017500000000141112540347414021012 0ustar michaelmichaeldef_module_params('rewriter', description='new formula simplification module used in the tactic framework, and new solvers', export=True, params=(max_memory_param(), max_steps_param(), ("flat", BOOL, True, "create nary applications for and,or,+,*,bvadd,bvmul,bvand,bvor,bvxor"), ("push_ite_arith", BOOL, False, "push if-then-else over arithmetic terms."), ("push_ite_bv", BOOL, False, "push if-then-else over bit-vector terms."), ("pull_cheap_ite", BOOL, False, "pull if-then-else terms when cheap."), ("cache_all", BOOL, False, "cache all intermediate results."))) z3-4.4.0/src/ast/rewriter/factor_rewriter.cpp0000644000175000017500000002457712540347414021012 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: factor_rewriter.cpp Abstract: Rewriting utilities for factoring polynomials in equations, and inequalities. Author: Nikolaj (nbjorner) 2011-19-05 Notes: --*/ #include"factor_rewriter.h" #include"ast_pp.h" #include"rewriter_def.h" factor_rewriter::factor_rewriter(ast_manager & m): m_manager(m), m_arith(m), m_factors(m) { } br_status factor_rewriter::mk_app_core( func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (m().is_eq(f)) { SASSERT(num_args == 2); return mk_eq(args[0], args[1], result); } if(f->get_family_id() == a().get_family_id()) { switch (f->get_decl_kind()) { case OP_LE: SASSERT(num_args == 2); return mk_le(args[0], args[1], result); case OP_GE: SASSERT(num_args == 2); return mk_ge(args[0], args[1], result); case OP_LT: SASSERT(num_args == 2); return mk_lt(args[0], args[1], result); case OP_GT: SASSERT(num_args == 2); return mk_gt(args[0], args[1], result); default: return BR_FAILED; } } return BR_FAILED; } br_status factor_rewriter::mk_eq(expr * arg1, expr * arg2, expr_ref & result) { if (!a().is_real(arg1) && !m_arith.is_int(arg1)) { return BR_FAILED; } mk_adds(arg1, arg2); mk_muls(); if (m_muls.empty()) { result = m().mk_true(); return BR_DONE; } if (!extract_factors()) { TRACE("factor_rewriter", tout << mk_pp(arg1, m()) << " = " << mk_pp(arg2, m()) << "\n";); return BR_FAILED; } powers_t::iterator it = m_powers.begin(), end = m_powers.end(); expr_ref_vector eqs(m()); for(; it != end; ++it) { expr* e = it->m_key; eqs.push_back(m().mk_eq(e, a().mk_numeral(rational(0), m().get_sort(e)))); } result = m().mk_or(eqs.size(), eqs.c_ptr()); return BR_DONE; } br_status factor_rewriter::mk_le(expr * arg1, expr * arg2, expr_ref & result) { mk_adds(arg1, arg2); mk_muls(); if (m_muls.empty()) { result = m().mk_true(); return BR_DONE; } if (!extract_factors()) { TRACE("factor_rewriter", tout << mk_pp(arg1, m()) << " <= " << mk_pp(arg2, m()) << "\n";); return BR_FAILED; } // a^2 * b^3 * c <= 0 -> // a = 0 \/ (b = 0 \/ b > 0 & c <= 0 \/ b < 0 & c >= 0) // expr_ref neg(m()); expr_ref_vector eqs(m()); mk_is_negative(neg, eqs); eqs.push_back(neg); result = m().mk_or(eqs.size(), eqs.c_ptr()); TRACE("factor_rewriter", tout << mk_pp(arg1, m()) << " <= " << mk_pp(arg2, m()) << "\n"; tout << mk_pp(result.get(), m()) << "\n";); return BR_DONE; } br_status factor_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) { mk_adds(arg1, arg2); mk_muls(); if (m_muls.empty()) { result = m().mk_false(); return BR_DONE; } if (!extract_factors()) { TRACE("factor_rewriter", tout << mk_pp(arg1, m()) << " < " << mk_pp(arg2, m()) << "\n";); return BR_FAILED; } // a^2 * b^3 * c < 0 -> // a != 0 /\ (b > 0 & c < 0 \/ b < 0 & c > 0) // expr_ref neg(m()); expr_ref_vector eqs(m()); mk_is_negative(neg, eqs); for (unsigned i = 0; i < eqs.size(); ++i) { eqs[i] = m().mk_not(eqs[i].get()); } eqs.push_back(neg); result = m().mk_and(eqs.size(), eqs.c_ptr()); TRACE("factor_rewriter", tout << mk_pp(result.get(), m()) << "\n";); return BR_DONE; } void factor_rewriter::mk_is_negative(expr_ref& result, expr_ref_vector& eqs) { powers_t::iterator it = m_powers.begin(), end = m_powers.end(); SASSERT(m_powers.size() >= 1); SASSERT(it != end); expr_ref neg0(m()), neg(m()), pos0(m()), pos(m()), tmp(m()); expr* e = it->m_key; expr_ref zero(a().mk_numeral(rational(0), m().get_sort(e)), m()); expr_ref_vector conjs(m()); pos0 = m().mk_true(); neg0 = m().mk_false(); for(; it != end; ++it) { e = it->m_key; eqs.push_back(m().mk_eq(zero, e)); if (!even(it->m_value)) { pos = a().mk_lt(zero, e); neg = a().mk_lt(e, zero); if (m().is_false(neg0)) { neg0 = neg; pos0 = pos; } else { tmp = m().mk_or(m().mk_and(pos, pos0), m().mk_and(neg, neg0)); neg0 = m().mk_or(m().mk_and(neg, pos0), m().mk_and(pos, neg0)); pos0 = tmp; } } } result = neg0; } // convert arg1 - arg2 into // sum of monomials // m_adds: sum of products. // m_muls: list of products void factor_rewriter::mk_adds(expr* arg1, expr* arg2) { m_adds.reset(); m_adds.push_back(std::make_pair(arg1, true)); m_adds.push_back(std::make_pair(arg2, false)); rational k; for (unsigned i = 0; i < m_adds.size();) { bool sign = m_adds[i].second; expr* _e = m_adds[i].first; TRACE("factor_rewriter", tout << i << " " << mk_pp(_e, m_manager) << "\n";); if (!is_app(_e)) { ++i; continue; } app* e = to_app(_e); if (a().is_add(e) && e->get_num_args() > 0) { m_adds[i].first = e->get_arg(0); for (unsigned j = 1; j < e->get_num_args(); ++j) { m_adds.push_back(std::make_pair(e->get_arg(j),sign)); } } else if (a().is_sub(e) && e->get_num_args() > 0) { m_adds[i].first = e->get_arg(0); for (unsigned j = 1; j < e->get_num_args(); ++j) { m_adds.push_back(std::make_pair(e->get_arg(j),!sign)); } } else if (a().is_uminus(e)) { m_adds[i].first = e->get_arg(0); m_adds[i].second = !sign; } else if (a().is_numeral(e, k) && k.is_zero()) { unsigned sz = m_adds.size(); m_adds[i] = m_adds[sz-1]; m_adds.resize(sz-1); } else { ++i; } } TRACE("factor_rewriter", for (unsigned i = 0; i < m_adds.size(); ++i) { if (!m_adds[i].second) tout << "-"; else tout << "+"; tout << mk_pp(m_adds[i].first, m()) << " "; } tout << "\n"; ); } void factor_rewriter::mk_muls() { m_muls.reset(); for (unsigned i = 0; i < m_adds.size(); ++i) { m_muls.push_back(ptr_vector()); m_muls.back().push_back(m_adds[i].first); mk_expand_muls(m_muls.back()); if (m_muls.back().empty()) { m_muls.pop_back(); m_adds.erase(m_adds.begin() + i); --i; } } TRACE("factor_rewriter", for (unsigned i = 0; i < m_muls.size(); ++i) { for (unsigned j = 0; j < m_muls[i].size(); ++j) { tout << mk_pp(m_muls[i][j], m()) << " "; } tout << "\n"; } tout << "\n"; ); } void factor_rewriter::mk_expand_muls(ptr_vector& muls) { for (unsigned i = 0; i < muls.size(); ) { expr* _e = muls[i]; if (!is_app(_e)) { ++i; continue; } app* e = to_app(_e); if (a().is_mul(e) && e->get_num_args() > 0) { muls[i] = e->get_arg(0); for (unsigned j = 1; j < e->get_num_args(); ++j) { muls.push_back(e->get_arg(j)); } } else { ++i; } } } bool factor_rewriter::extract_factors() { m_factors.reset(); unsigned_vector pos; expr* e; SASSERT(!m_muls.empty()); if (m_muls.size() == 1) { if (m_muls[0].size() > 1) { m_factors.append(m_muls[0].size(), m_muls[0].c_ptr()); if (!m_adds[0].second) { bool found_numeral = false; sort* s = m().get_sort(m_muls[0][0]); rational v; for (unsigned i = 0; !found_numeral && i < m_factors.size(); ++i) { if (a().is_numeral(m_factors[i].get(), v)) { m_factors[i] = a().mk_numeral(-v, s); found_numeral = true; } } if (!found_numeral) { m_factors.push_back(a().mk_numeral(rational(-1),s)); } } collect_powers(); return true; } return false; } for (unsigned i = 0; i < m_muls[0].size(); ++i) { pos.reset(); pos.push_back(i); e = m_muls[0][i]; bool ok = true; for (unsigned j = 1; ok && j < m_muls.size(); ++j) { ok = false; unsigned k = 0; for (k = 0; !ok && k < m_muls[j].size(); ++k) { ok = m_muls[j][k] == e; } pos.push_back(k-1); } if (ok) { SASSERT(pos.size() == m_muls.size()); m_factors.push_back(e); for (unsigned j = 0; j < pos.size(); ++j) { m_muls[j].erase(m_muls[j].begin() + pos[j]); } --i; } } if (m_factors.empty()) { return false; } SASSERT(m_muls.size() == m_adds.size()); expr_ref_vector trail(m()); sort* s = m().get_sort(m_factors[0].get()); for (unsigned i = 0; i < m_adds.size(); ++i) { switch(m_muls[i].size()) { case 0: e = a().mk_numeral(rational(1), s); break; case 1: e = m_muls[i][0]; break; default: e = a().mk_mul(m_muls[i].size(), m_muls[i].c_ptr()); break; } if (!m_adds[i].second) { e = a().mk_uminus(e); } trail.push_back(e); } switch(trail.size()) { case 0: break; case 1: m_factors.push_back(trail[0].get()); break; default: m_factors.push_back(a().mk_add(trail.size(), trail.c_ptr())); break; } TRACE("factor_rewriter", for (unsigned i = 0; i < m_factors.size(); ++i) { tout << mk_pp(m_factors[i].get(), m()) << " "; } tout << "\n"; ); collect_powers(); return true; } void factor_rewriter::collect_powers() { m_powers.reset(); for (unsigned i = 0; i < m_factors.size(); ++i) { obj_map::obj_map_entry* entry = m_powers.insert_if_not_there2(m_factors[i].get(), 0); if (entry) { ++(entry->get_data().m_value); } } } template class rewriter_tpl; z3-4.4.0/src/ast/rewriter/ast_counter.cpp0000644000175000017500000000744212540347414020127 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: ast_counter.cpp Abstract: Routines for counting features of terms, such as free variables. Author: Nikolaj Bjorner (nbjorner) 2013-03-18. Revision History: --*/ #include "ast_counter.h" void counter::update(unsigned el, int delta) { int & counter = get(el); counter += delta; } int & counter::get(unsigned el) { return m_data.insert_if_not_there2(el, 0)->get_data().m_value; } counter & counter::count(unsigned sz, const unsigned * els, int delta) { for(unsigned i=0; im_value>0 ) { cnt++; } } return cnt; } void counter::collect_positive(uint_set & acc) const { iterator eit = begin(); iterator eend = end(); for(; eit!=eend; ++eit) { if(eit->m_value>0) { acc.insert(eit->m_key); } } } bool counter::get_max_positive(unsigned & res) const { bool found = false; iterator eit = begin(); iterator eend = end(); for(; eit!=eend; ++eit) { if( eit->m_value>0 && (!found || eit->m_key>res) ) { found = true; res = eit->m_key; } } return found; } unsigned counter::get_max_positive() const { unsigned max_pos; VERIFY(get_max_positive(max_pos)); return max_pos; } int counter::get_max_counter_value() const { int res = 0; iterator eit = begin(); iterator eend = end(); for (; eit!=eend; ++eit) { if( eit->m_value>res ) { res = eit->m_value; } } return res; } void var_counter::count_vars(const app * pred, int coef) { unsigned n = pred->get_num_args(); for (unsigned i = 0; i < n; i++) { m_fv(pred->get_arg(i)); for (unsigned j = 0; j < m_fv.size(); ++j) { if (m_fv[j]) { update(j, coef); } } } m_fv.reset(); } unsigned var_counter::get_max_var(bool& has_var) { has_var = false; unsigned max_var = 0; ptr_vector qs; while (!m_todo.empty()) { expr* e = m_todo.back(); m_todo.pop_back(); if (m_visited.is_marked(e)) { continue; } m_visited.mark(e, true); switch(e->get_kind()) { case AST_QUANTIFIER: { qs.push_back(to_quantifier(e)); break; } case AST_VAR: { if (to_var(e)->get_idx() >= max_var) { has_var = true; max_var = to_var(e)->get_idx(); } break; } case AST_APP: { app* a = to_app(e); for (unsigned i = 0; i < a->get_num_args(); ++i) { m_todo.push_back(a->get_arg(i)); } break; } default: UNREACHABLE(); break; } } m_visited.reset(); while (!qs.empty()) { var_counter aux_counter; quantifier* q = qs.back(); qs.pop_back(); aux_counter.m_todo.push_back(q->get_expr()); bool has_var1 = false; unsigned max_v = aux_counter.get_max_var(has_var1); if (max_v >= max_var + q->get_num_decls()) { max_var = max_v - q->get_num_decls(); has_var = has_var || has_var1; } } return max_var; } unsigned var_counter::get_max_var(expr* e) { bool has_var = false; m_todo.push_back(e); return get_max_var(has_var); } unsigned var_counter::get_next_var(expr* e) { bool has_var = false; m_todo.push_back(e); unsigned mv = get_max_var(has_var); if (has_var) mv++; return mv; } z3-4.4.0/src/ast/rewriter/bit_blaster/0000755000175000017500000000000012540347414017360 5ustar michaelmichaelz3-4.4.0/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp0000644000175000017500000005717212540347414024315 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bit_blaster_rewriter.cpp Abstract: Bit-blasting rewriter Author: Leonardo (leonardo) 2012-10-04 Notes: --*/ #include"bit_blaster_rewriter.h" #include"bv_decl_plugin.h" #include"bit_blaster_tpl_def.h" #include"rewriter_def.h" #include"bool_rewriter.h" #include"ref_util.h" #include"ast_smt2_pp.h" struct blaster_cfg { typedef rational numeral; bool_rewriter & m_rewriter; bv_util & m_util; blaster_cfg(bool_rewriter & r, bv_util & u):m_rewriter(r), m_util(u) {} ast_manager & m() const { return m_util.get_manager(); } numeral power(unsigned n) const { return rational::power_of_two(n); } void mk_xor(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_xor(a, b, r); } void mk_xor3(expr * a, expr * b, expr * c, expr_ref & r) { expr_ref tmp(m()); mk_xor(b, c, tmp); mk_xor(a, tmp, r); } void mk_iff(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_iff(a, b, r); } void mk_and(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_and(a, b, r); } void mk_and(expr * a, expr * b, expr * c, expr_ref & r) { m_rewriter.mk_and(a, b, c, r); } void mk_and(unsigned sz, expr * const * args, expr_ref & r) { m_rewriter.mk_and(sz, args, r); } void mk_or(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_or(a, b, r); } void mk_or(expr * a, expr * b, expr * c, expr_ref & r) { m_rewriter.mk_or(a, b, c, r); } void mk_or(unsigned sz, expr * const * args, expr_ref & r) { m_rewriter.mk_or(sz, args, r); } void mk_not(expr * a, expr_ref & r) { m_rewriter.mk_not(a, r); } void mk_carry(expr * a, expr * b, expr * c, expr_ref & r) { expr_ref t1(m()), t2(m()), t3(m()); #if 1 mk_and(a, b, t1); mk_and(a, c, t2); mk_and(b, c, t3); mk_or(t1, t2, t3, r); #else mk_or(a, b, t1); mk_or(a, c, t2); mk_or(b, c, t3); mk_and(t1, t2, t3, r); #endif } void mk_ite(expr * c, expr * t, expr * e, expr_ref & r) { m_rewriter.mk_ite(c, t, e, r); } void mk_nand(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_nand(a, b, r); } void mk_nor(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_nor(a, b, r); } }; // CMW: GCC/LLVM do not like this definition because a symbol of the same name exists in assert_set_bit_blaster.o // template class bit_blaster_tpl; class blaster : public bit_blaster_tpl { bool_rewriter m_rewriter; bv_util m_util; public: blaster(ast_manager & m): bit_blaster_tpl(blaster_cfg(m_rewriter, m_util)), m_rewriter(m), m_util(m) { m_rewriter.set_flat(false); m_rewriter.set_elim_and(true); } bv_util & butil() { return m_util; } }; struct blaster_rewriter_cfg : public default_rewriter_cfg { ast_manager & m_manager; blaster & m_blaster; expr_ref_vector m_in1; expr_ref_vector m_in2; expr_ref_vector m_out; obj_map m_const2bits; expr_ref_vector m_bindings; bool m_blast_mul; bool m_blast_add; bool m_blast_quant; bool m_blast_full; unsigned long long m_max_memory; unsigned m_max_steps; ast_manager & m() const { return m_manager; } bv_util & butil() { return m_blaster.butil(); } void cleanup_buffers() { m_in1.finalize(); m_in2.finalize(); m_out.finalize(); m_bindings.finalize(); } blaster_rewriter_cfg(ast_manager & m, blaster & b, params_ref const & p): m_manager(m), m_blaster(b), m_in1(m), m_in2(m), m_out(m), m_bindings(m) { updt_params(p); } ~blaster_rewriter_cfg() { dec_ref_map_key_values(m_manager, m_const2bits); } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_max_steps = p.get_uint("max_steps", UINT_MAX); m_blast_add = p.get_bool("blast_add", true); m_blast_mul = p.get_bool("blast_mul", true); m_blast_full = p.get_bool("blast_full", false); m_blast_quant = p.get_bool("blast_quant", false); m_blaster.set_max_memory(m_max_memory); } bool rewrite_patterns() const { return true; } bool max_steps_exceeded(unsigned num_steps) const { cooperate("bit blaster"); if (memory::get_allocation_size() > m_max_memory) throw rewriter_exception(Z3_MAX_MEMORY_MSG); return num_steps > m_max_steps; } void get_bits(expr * t, expr_ref_vector & out_bits) { if (butil().is_mkbv(t)) { out_bits.append(to_app(t)->get_num_args(), to_app(t)->get_args()); } else { unsigned bv_size = butil().get_bv_size(t); for (unsigned i = 0; i < bv_size; i++) { parameter p(i); out_bits.push_back(m().mk_app(butil().get_family_id(), OP_BIT2BOOL, 1, &p, 1, &t)); } SASSERT(bv_size == out_bits.size()); } } template app * mk_mkbv(V const & bits) { return m().mk_app(butil().get_family_id(), OP_MKBV, bits.size(), bits.c_ptr()); } void mk_const(func_decl * f, expr_ref & result) { SASSERT(f->get_family_id() == null_family_id); SASSERT(f->get_arity() == 0); expr * r; if (m_const2bits.find(f, r)) { result = r; return; } sort * s = f->get_range(); SASSERT(butil().is_bv_sort(s)); unsigned bv_size = butil().get_bv_size(s); sort * b = m().mk_bool_sort(); m_out.reset(); for (unsigned i = 0; i < bv_size; i++) { m_out.push_back(m().mk_fresh_const(0, b)); } r = mk_mkbv(m_out); m_const2bits.insert(f, r); m_manager.inc_ref(f); m_manager.inc_ref(r); result = r; } #define MK_UNARY_REDUCE(OP, BB_OP) \ void OP(expr * arg, expr_ref & result) { \ m_in1.reset(); \ get_bits(arg, m_in1); \ m_out.reset(); \ m_blaster.BB_OP(m_in1.size(), m_in1.c_ptr(), m_out); \ result = mk_mkbv(m_out); \ } MK_UNARY_REDUCE(reduce_not, mk_not); MK_UNARY_REDUCE(reduce_redor, mk_redor); MK_UNARY_REDUCE(reduce_redand, mk_redand); #define MK_BIN_REDUCE(OP, BB_OP) \ void OP(expr * arg1, expr * arg2, expr_ref & result) { \ m_in1.reset(); m_in2.reset(); \ get_bits(arg1, m_in1); \ get_bits(arg2, m_in2); \ m_out.reset(); \ m_blaster.BB_OP(m_in1.size(), m_in1.c_ptr(), m_in2.c_ptr(), m_out); \ result = mk_mkbv(m_out); \ } MK_BIN_REDUCE(reduce_shl, mk_shl); MK_BIN_REDUCE(reduce_ashr, mk_ashr); MK_BIN_REDUCE(reduce_lshr, mk_lshr); MK_BIN_REDUCE(reduce_udiv, mk_udiv); MK_BIN_REDUCE(reduce_urem, mk_urem); MK_BIN_REDUCE(reduce_sdiv, mk_sdiv); MK_BIN_REDUCE(reduce_srem, mk_srem); MK_BIN_REDUCE(reduce_smod, mk_smod); MK_BIN_REDUCE(reduce_ext_rotate_left, mk_ext_rotate_left); MK_BIN_REDUCE(reduce_ext_rotate_right, mk_ext_rotate_right); #define MK_BIN_AC_REDUCE(OP, BIN_OP, BB_OP) \ MK_BIN_REDUCE(BIN_OP, BB_OP); \ void OP(unsigned num_args, expr * const * args, expr_ref & result) { \ SASSERT(num_args > 0); \ result = args[0]; \ expr_ref new_result(m_manager); \ for (unsigned i = 1; i < num_args; i++) { \ BIN_OP(result.get(), args[i], new_result); \ result = new_result; \ } \ } MK_BIN_AC_REDUCE(reduce_add, reduce_bin_add, mk_adder); MK_BIN_AC_REDUCE(reduce_mul, reduce_bin_mul, mk_multiplier); MK_BIN_AC_REDUCE(reduce_or, reduce_bin_or, mk_or); MK_BIN_AC_REDUCE(reduce_xor, reduce_bin_xor, mk_xor); #define MK_BIN_PRED_REDUCE(OP, BB_OP) \ void OP(expr * arg1, expr * arg2, expr_ref & result) { \ m_in1.reset(); m_in2.reset(); \ get_bits(arg1, m_in1); \ get_bits(arg2, m_in2); \ m_blaster.BB_OP(m_in1.size(), m_in1.c_ptr(), m_in2.c_ptr(), result); \ } MK_BIN_PRED_REDUCE(reduce_eq, mk_eq); MK_BIN_PRED_REDUCE(reduce_sle, mk_sle); MK_BIN_PRED_REDUCE(reduce_ule, mk_ule); MK_BIN_PRED_REDUCE(reduce_umul_no_overflow, mk_umul_no_overflow); MK_BIN_PRED_REDUCE(reduce_smul_no_overflow, mk_smul_no_overflow); MK_BIN_PRED_REDUCE(reduce_smul_no_underflow, mk_smul_no_underflow); #define MK_PARAMETRIC_UNARY_REDUCE(OP, BB_OP) \ void OP(expr * arg, unsigned n, expr_ref & result) { \ m_in1.reset(); \ get_bits(arg, m_in1); \ m_out.reset(); \ m_blaster.BB_OP(m_in1.size(), m_in1.c_ptr(), n, m_out); \ result = mk_mkbv(m_out); \ } MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend); void reduce_ite(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { m_in1.reset(); m_in2.reset(); get_bits(arg2, m_in1); get_bits(arg3, m_in2); m_out.reset(); m_blaster.mk_multiplexer(arg1, m_in1.size(), m_in1.c_ptr(), m_in2.c_ptr(), m_out); result = mk_mkbv(m_out); } void reduce_concat(unsigned num_args, expr * const * args, expr_ref & result) { m_out.reset(); unsigned i = num_args; while (i > 0) { i--; m_in1.reset(); get_bits(args[i], m_in1); m_out.append(m_in1.size(), m_in1.c_ptr()); } result = mk_mkbv(m_out); } void reduce_extract(unsigned start, unsigned end, expr * arg, expr_ref & result) { m_in1.reset(); get_bits(arg, m_in1); m_out.reset(); for (unsigned i = start; i <= end; ++i) m_out.push_back(m_in1.get(i)); result = mk_mkbv(m_out); } void reduce_num(func_decl * f, expr_ref & result) { SASSERT(f->get_num_parameters() == 2); SASSERT(f->get_parameter(0).is_rational()); SASSERT(f->get_parameter(1).is_int()); rational v = f->get_parameter(0).get_rational(); unsigned bv_sz = f->get_parameter(1).get_int(); m_out.reset(); m_blaster.num2bits(v, bv_sz, m_out); result = mk_mkbv(m_out); } void throw_unsupported() { throw rewriter_exception("operator is not supported, you must simplify the goal before applying bit-blasting"); } void blast_bv_term(expr * t, expr_ref & result, proof_ref & result_pr) { ptr_buffer bits; unsigned bv_size = butil().get_bv_size(t); for (unsigned i = 0; i < bv_size; i++) { parameter p(i); bits.push_back(m().mk_app(butil().get_family_id(), OP_BIT2BOOL, 1, &p, 1, &t)); } result = mk_mkbv(bits); result_pr = 0; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = 0; TRACE("bit_blaster", tout << f->get_name() << " "; for (unsigned i = 0; i < num; ++i) tout << mk_pp(args[i], m()) << " "; tout << "\n";); if (num == 0 && f->get_family_id() == null_family_id && butil().is_bv_sort(f->get_range())) { mk_const(f, result); return BR_DONE; } if (m().is_eq(f)) { SASSERT(num == 2); if (butil().is_bv(args[0])) { reduce_eq(args[0], args[1], result); return BR_DONE; } return BR_FAILED; } if (m().is_ite(f)) { SASSERT(num == 3); if (butil().is_bv(args[1])) { reduce_ite(args[0], args[1], args[2], result); return BR_DONE; } return BR_FAILED; } if (f->get_family_id() == butil().get_family_id()) { switch (f->get_decl_kind()) { case OP_BV_NUM: SASSERT(num == 0); reduce_num(f, result); return BR_DONE; case OP_BADD: if (!m_blast_add) return BR_FAILED; reduce_add(num, args, result); return BR_DONE; case OP_BMUL: if (!m_blast_mul) return BR_FAILED; reduce_mul(num, args, result); return BR_DONE; case OP_BSDIV: case OP_BUDIV: case OP_BSREM: case OP_BUREM: case OP_BSMOD: if (m_blast_mul) throw_unsupported(); // must simplify to DIV_I AND DIV0 return BR_FAILED; // keep them case OP_BSDIV0: case OP_BUDIV0: case OP_BSREM0: case OP_BUREM0: case OP_BSMOD0: return BR_FAILED; case OP_BSDIV_I: if (!m_blast_mul) return BR_FAILED; SASSERT(num == 2); reduce_sdiv(args[0], args[1], result); return BR_DONE; case OP_BUDIV_I: if (!m_blast_mul) return BR_FAILED; SASSERT(num == 2); reduce_udiv(args[0], args[1], result); return BR_DONE; case OP_BSREM_I: if (!m_blast_mul) return BR_FAILED; SASSERT(num == 2); reduce_srem(args[0], args[1], result); return BR_DONE; case OP_BUREM_I: if (!m_blast_mul) return BR_FAILED; SASSERT(num == 2); reduce_urem(args[0], args[1], result); return BR_DONE; case OP_BSMOD_I: if (!m_blast_mul) return BR_FAILED; SASSERT(num == 2); reduce_smod(args[0], args[1], result); return BR_DONE; case OP_ULEQ: SASSERT(num == 2); reduce_ule(args[0], args[1], result); return BR_DONE; case OP_SLEQ: SASSERT(num == 2); reduce_sle(args[0], args[1], result); return BR_DONE; case OP_BOR: reduce_or(num, args, result); return BR_DONE; case OP_BNOT: SASSERT(num == 1); reduce_not(args[0], result); return BR_DONE; case OP_BXOR: reduce_xor(num, args, result); return BR_DONE; case OP_CONCAT: reduce_concat(num, args, result); return BR_DONE; case OP_SIGN_EXT: SASSERT(num == 1); reduce_sign_extend(args[0], f->get_parameter(0).get_int(), result); return BR_DONE; case OP_EXTRACT: SASSERT(num == 1); reduce_extract(f->get_parameter(1).get_int(), f->get_parameter(0).get_int(), args[0], result); return BR_DONE; case OP_BREDOR: SASSERT(num == 1); reduce_redor(args[0], result); return BR_DONE; case OP_BREDAND: SASSERT(num == 1); reduce_redand(args[0], result); return BR_DONE; case OP_BSHL: SASSERT(num == 2); reduce_shl(args[0], args[1], result); return BR_DONE; case OP_BLSHR: SASSERT(num == 2); reduce_lshr(args[0], args[1], result); return BR_DONE; case OP_BASHR: SASSERT(num == 2); reduce_ashr(args[0], args[1], result); return BR_DONE; case OP_EXT_ROTATE_LEFT: SASSERT(num == 2); reduce_ext_rotate_left(args[0], args[1], result); return BR_DONE; case OP_EXT_ROTATE_RIGHT: SASSERT(num == 2); reduce_ext_rotate_right(args[0], args[1], result); return BR_DONE; case OP_BUMUL_NO_OVFL: SASSERT(num == 2); reduce_umul_no_overflow(args[0], args[1], result); return BR_DONE; case OP_BSMUL_NO_OVFL: SASSERT(num == 2); reduce_smul_no_overflow(args[0], args[1], result); return BR_DONE; case OP_BSMUL_NO_UDFL: SASSERT(num == 2); reduce_smul_no_underflow(args[0], args[1], result); return BR_DONE; case OP_BIT2BOOL: case OP_MKBV: case OP_INT2BV: case OP_BV2INT: return BR_FAILED; default: TRACE("bit_blaster", tout << "non-supported operator: " << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << std::endl;); throw_unsupported(); } } if (m_blast_full && butil().is_bv_sort(f->get_range())) { blast_bv_term(m().mk_app(f, num, args), result, result_pr); return BR_DONE; } return BR_FAILED; } bool pre_visit(expr * t) { if (m_blast_quant && is_quantifier(t)) { quantifier * q = to_quantifier(t); ptr_buffer new_bindings; ptr_buffer new_args; unsigned i = q->get_num_decls(); unsigned j = 0; while (i > 0) { --i; sort * s = q->get_decl_sort(i); if (butil().is_bv_sort(s)) { unsigned bv_size = butil().get_bv_size(s); new_args.reset(); for (unsigned k = 0; k < bv_size; k++) { new_args.push_back(m().mk_var(j, m().mk_bool_sort())); j++; } new_bindings.push_back(mk_mkbv(new_args)); } else { new_bindings.push_back(m().mk_var(j, s)); j++; } } SASSERT(new_bindings.size() == q->get_num_decls()); i = q->get_num_decls(); while (i > 0) { i--; m_bindings.push_back(new_bindings[i]); } } return true; } bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { if (m_blast_quant) { if (t->get_idx() >= m_bindings.size()) return false; result = m_bindings.get(m_bindings.size() - t->get_idx() - 1); result_pr = 0; return true; } if (m_blast_full && butil().is_bv(t)) { blast_bv_term(t, result, result_pr); return true; } return false; } bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { if (!m_blast_quant) return false; unsigned curr_sz = m_bindings.size(); SASSERT(old_q->get_num_decls() <= curr_sz); unsigned num_decls = old_q->get_num_decls(); unsigned old_sz = curr_sz - num_decls; string_buffer<> name_buffer; ptr_buffer new_decl_sorts; sbuffer new_decl_names; for (unsigned i = 0; i < num_decls; i++) { symbol const & n = old_q->get_decl_name(i); sort * s = old_q->get_decl_sort(i); if (butil().is_bv_sort(s)) { unsigned bv_size = butil().get_bv_size(s); for (unsigned j = 0; j < bv_size; j++) { name_buffer.reset(); name_buffer << n << "." << j; new_decl_names.push_back(symbol(name_buffer.c_str())); new_decl_sorts.push_back(m().mk_bool_sort()); } } else { new_decl_sorts.push_back(s); new_decl_names.push_back(n); } } result = m().mk_quantifier(old_q->is_forall(), new_decl_sorts.size(), new_decl_sorts.c_ptr(), new_decl_names.c_ptr(), new_body, old_q->get_weight(), old_q->get_qid(), old_q->get_skid(), old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns); result_pr = 0; m_bindings.shrink(old_sz); return true; } }; // CMW: GCC/LLVM do not like this definition because a symbol of the same name exists in assert_set_bit_blaster.o // template class rewriter_tpl; struct bit_blaster_rewriter::imp : public rewriter_tpl { blaster m_blaster; blaster_rewriter_cfg m_cfg; imp(ast_manager & m, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_blaster(m), m_cfg(m, m_blaster, p) { SASSERT(m_blaster.butil().get_family_id() == m.get_family_id("bv")); } }; bit_blaster_rewriter::bit_blaster_rewriter(ast_manager & m, params_ref const & p): m_imp(alloc(imp, m, p)) { } bit_blaster_rewriter::~bit_blaster_rewriter() { dealloc(m_imp); } void bit_blaster_rewriter::updt_params(params_ref const& p) { m_imp->m_cfg.updt_params(p); } void bit_blaster_rewriter::set_cancel(bool f) { m_imp->set_cancel(f); m_imp->m_blaster.set_cancel(f); } ast_manager & bit_blaster_rewriter::m() const { return m_imp->m(); } unsigned bit_blaster_rewriter::get_num_steps() const { return m_imp->get_num_steps(); } void bit_blaster_rewriter::cleanup() { m_imp->cleanup(); } obj_map const & bit_blaster_rewriter::const2bits() const { return m_imp->m_cfg.m_const2bits; } void bit_blaster_rewriter::operator()(expr * e, expr_ref & result, proof_ref & result_proof) { m_imp->operator()(e, result, result_proof); } z3-4.4.0/src/ast/rewriter/bit_blaster/bit_blaster.cpp0000644000175000017500000000756312540347414022371 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bit_blaster.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-05. Revision History: --*/ #include"bit_blaster.h" #include"bit_blaster_tpl_def.h" #include"ast_pp.h" #include"bv_decl_plugin.h" bit_blaster_cfg::bit_blaster_cfg(bv_util & u, bit_blaster_params const & p, basic_simplifier_plugin & _s): m_util(u), m_params(p), s(_s) { } static void sort_args(expr * & l1, expr * & l2, expr * & l3) { expr * args[3] = {l1, l2, l3}; // ast_lt_proc is based on the AST ids. So, it is a total order on AST nodes. // No need for stable_sort std::sort(args, args+3, ast_lt_proc()); l1 = args[0]; l2 = args[1]; l3 = args[2]; } void bit_blaster_cfg::mk_xor3(expr * l1, expr * l2, expr * l3, expr_ref & r) { TRACE("xor3", tout << "#" << l1->get_id() << " #" << l2->get_id() << " #" << l3->get_id();); sort_args(l1, l2, l3); TRACE("xor3_sorted", tout << "#" << l1->get_id() << " #" << l2->get_id() << " #" << l3->get_id();); if (m_params.m_bb_ext_gates) { if (l1 == l2) r = l3; else if (l1 == l3) r = l2; else if (l2 == l3) r = l1; else if (m().is_complement(l1, l2)) s.mk_not(l3, r); else if (m().is_complement(l1, l3)) s.mk_not(l2, r); else if (m().is_complement(l2, l3)) s.mk_not(l1, r); else if (m().is_true(l1)) s.mk_iff(l2, l3, r); else if (m().is_false(l1)) s.mk_xor(l2, l3, r); else if (m().is_true(l2)) s.mk_iff(l1, l3, r); else if (m().is_false(l2)) s.mk_xor(l1, l3, r); else if (m().is_true(l3)) s.mk_iff(l1, l2, r); else if (m().is_false(l3)) s.mk_xor(l1, l2, r); else r = m().mk_app(m_util.get_family_id(), OP_XOR3, l1, l2, l3); } else { expr_ref t(m()); s.mk_xor(l1, l2, t); s.mk_xor(t, l3, r); } } void bit_blaster_cfg::mk_carry(expr * l1, expr * l2, expr * l3, expr_ref & r) { TRACE("carry", tout << "#" << l1->get_id() << " #" << l2->get_id() << " #" << l3->get_id();); sort_args(l1, l2, l3); TRACE("carry_sorted", tout << "#" << l1->get_id() << " #" << l2->get_id() << " #" << l3->get_id();); if (m_params.m_bb_ext_gates) { if ((m().is_false(l1) && m().is_false(l2)) || (m().is_false(l1) && m().is_false(l3)) || (m().is_false(l2) && m().is_false(l3))) r = m().mk_false(); else if ((m().is_true(l1) && m().is_true(l2)) || (m().is_true(l1) && m().is_true(l3)) || (m().is_true(l2) && m().is_true(l3))) r = m().mk_true(); else if (l1 == l2 && l1 == l3) r = l1; else if (m().is_false(l1)) s.mk_and(l2, l3, r); else if (m().is_false(l2)) s.mk_and(l1, l3, r); else if (m().is_false(l3)) s.mk_and(l1, l2, r); else if (m().is_true(l1)) s.mk_or(l2, l3, r); else if (m().is_true(l2)) s.mk_or(l1, l3, r); else if (m().is_true(l3)) s.mk_or(l1, l2, r); else if (m().is_complement(l1, l2)) r = l3; else if (m().is_complement(l1, l3)) r = l2; else if (m().is_complement(l2, l3)) r = l1; else r = m().mk_app(m_util.get_family_id(), OP_CARRY, l1, l2, l3); } else { expr_ref t1(m()), t2(m()), t3(m()); s.mk_and(l1, l2, t1); s.mk_and(l1, l3, t2); s.mk_and(l2, l3, t3); s.mk_or(t1, t2, t3, r); } } template class bit_blaster_tpl; bit_blaster::bit_blaster(ast_manager & m, bit_blaster_params const & params): bit_blaster_tpl(bit_blaster_cfg(m_util, params, m_simp)), m_util(m), m_simp(m) { } z3-4.4.0/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h0000644000175000017500000000141612540347414023750 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bit_blaster_rewriter.h Abstract: Bit-blasting rewriter Author: Leonardo (leonardo) 2012-10-04 Notes: --*/ #ifndef _BIT_BLASTER_REWRITER_H_ #define _BIT_BLASTER_REWRITER_H_ #include"ast.h" #include"obj_hashtable.h" #include"params.h" class bit_blaster_rewriter { struct imp; imp * m_imp; public: bit_blaster_rewriter(ast_manager & m, params_ref const & p); ~bit_blaster_rewriter(); void updt_params(params_ref const & p); void set_cancel(bool f); ast_manager & m() const; unsigned get_num_steps() const; void cleanup(); obj_map const& const2bits() const; void operator()(expr * e, expr_ref & result, proof_ref & result_proof); }; #endif z3-4.4.0/src/ast/rewriter/bit_blaster/bit_blaster.h0000644000175000017500000000422012540347414022021 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bit_blaster.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-05. Revision History: --*/ #ifndef _BIT_BLASTER_H_ #define _BIT_BLASTER_H_ #include"basic_simplifier_plugin.h" #include"bit_blaster_params.h" #include"bit_blaster_tpl.h" #include"bv_decl_plugin.h" #include"rational.h" class bit_blaster_cfg { public: typedef rational numeral; protected: bv_util & m_util; bit_blaster_params const & m_params; basic_simplifier_plugin & s; public: bit_blaster_cfg(bv_util & u, bit_blaster_params const & p, basic_simplifier_plugin & _s); ast_manager & m() const { return m_util.get_manager(); } numeral power(unsigned n) const { return rational::power_of_two(n); } void mk_xor(expr * a, expr * b, expr_ref & r) { s.mk_xor(a, b, r); } void mk_xor3(expr * a, expr * b, expr * c, expr_ref & r); void mk_carry(expr * a, expr * b, expr * c, expr_ref & r); void mk_iff(expr * a, expr * b, expr_ref & r) { s.mk_iff(a, b, r); } void mk_and(expr * a, expr * b, expr_ref & r) { s.mk_and(a, b, r); } void mk_and(expr * a, expr * b, expr * c, expr_ref & r) { s.mk_and(a, b, c, r); } void mk_and(unsigned sz, expr * const * args, expr_ref & r) { s.mk_and(sz, args, r); } void mk_or(expr * a, expr * b, expr_ref & r) { s.mk_or(a, b, r); } void mk_or(expr * a, expr * b, expr * c, expr_ref & r) { s.mk_or(a, b, c, r); } void mk_or(unsigned sz, expr * const * args, expr_ref & r) { s.mk_or(sz, args, r); } void mk_not(expr * a, expr_ref & r) { s.mk_not(a, r); } void mk_ite(expr * c, expr * t, expr * e, expr_ref & r) { s.mk_ite(c, t, e, r); } void mk_nand(expr * a, expr * b, expr_ref & r) { s.mk_nand(a, b, r); } void mk_nor(expr * a, expr * b, expr_ref & r) { s.mk_nor(a, b, r); } }; class bit_blaster : public bit_blaster_tpl { bv_util m_util; basic_simplifier_plugin m_simp; public: bit_blaster(ast_manager & m, bit_blaster_params const & params); bit_blaster_params const & get_params() const { return this->m_params; } }; #endif /* _BIT_BLASTER_H_ */ z3-4.4.0/src/ast/rewriter/bit_blaster/bit_blaster_tpl.h0000644000175000017500000001703312540347414022706 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bit_blaster_tpl.h Abstract: Template for bit-blaster operations Author: Leonardo de Moura (leonardo) 2011-05-02. Revision History: --*/ #ifndef _BIT_BLASTER_TPL_H_ #define _BIT_BLASTER_TPL_H_ #include"rational.h" template class bit_blaster_tpl : public Cfg { public: typedef rational numeral; protected: template void mk_le(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out); template void mk_sdiv_srem_smod(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); template void mk_ext_rotate_left_right(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); unsigned long long m_max_memory; volatile bool m_cancel; bool m_use_wtm; /* Wallace Tree Multiplier */ bool m_use_bcm; /* Booth Multiplier for constants */ void checkpoint(); public: bit_blaster_tpl(Cfg const & cfg = Cfg(), unsigned long long max_memory = UINT64_MAX, bool use_wtm = false, bool use_bcm=false): Cfg(cfg), m_max_memory(max_memory), m_cancel(false), m_use_wtm(use_wtm), m_use_bcm(use_bcm) { } void set_max_memory(unsigned long long max_memory) { m_max_memory = max_memory; } void set_cancel(bool f) { m_cancel = f; } void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } // Cfg required API ast_manager & m() const { return Cfg::m(); } numeral power(unsigned n) const { return Cfg::power(n); } void mk_xor(expr * a, expr * b, expr_ref & r) { Cfg::mk_xor(a, b, r); } void mk_xor3(expr * a, expr * b, expr * c, expr_ref & r) { Cfg::mk_xor3(a, b, c, r); } void mk_carry(expr * a, expr * b, expr * c, expr_ref & r) { Cfg::mk_carry(a, b, c, r); } void mk_iff(expr * a, expr * b, expr_ref & r) { Cfg::mk_iff(a, b, r); } void mk_and(expr * a, expr * b, expr_ref & r) { Cfg::mk_and(a, b, r); } void mk_and(expr * a, expr * b, expr * c, expr_ref & r) { Cfg::mk_and(a, b, c, r); } void mk_and(unsigned sz, expr * const * args, expr_ref & r) { Cfg::mk_and(sz, args, r); } void mk_or(expr * a, expr * b, expr_ref & r) { Cfg::mk_or(a, b, r); } void mk_or(expr * a, expr * b, expr * c, expr_ref & r) { Cfg::mk_or(a, b, c, r); } void mk_or(unsigned sz, expr * const * args, expr_ref & r) { Cfg::mk_or(sz, args, r); } void mk_not(expr * a, expr_ref & r) { Cfg::mk_not(a, r); } void mk_ite(expr * c, expr * t, expr * e, expr_ref & r) { Cfg::mk_ite(c, t, e, r); } void mk_nand(expr * a, expr * b, expr_ref & r) { Cfg::mk_nand(a, b, r); } void mk_nor(expr * a, expr * b, expr_ref & r) { Cfg::mk_nor(a, b, r); } // bool is_numeral(unsigned sz, expr * const * bits) const; bool is_numeral(unsigned sz, expr * const * bits, numeral & r) const; bool is_minus_one(unsigned sz, expr * const * bits) const; void num2bits(numeral const & v, unsigned sz, expr_ref_vector & out_bits) const; void mk_half_adder(expr * a, expr * b, expr_ref & out, expr_ref & cout); void mk_full_adder(expr * a, expr * b, expr * cin, expr_ref & out, expr_ref & cout); void mk_neg(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits); void mk_adder(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_subtracter(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits, expr_ref & cout); void mk_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_udiv_urem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & q_bits, expr_ref_vector & r_bits); void mk_udiv(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & q_bits); void mk_urem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & r_bits); void mk_multiplexer(expr * c, unsigned sz, expr * const * t_bits, expr * const * e_bits, expr_ref_vector & out_bits); void mk_sdiv(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_srem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_smod(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_rotate_left(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits); void mk_rotate_right(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits); void mk_ext_rotate_left(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_ext_rotate_right(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_sign_extend(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits); void mk_zero_extend(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits); void mk_eq(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out); void mk_is_eq(unsigned sz, expr * const * a_bits, unsigned n, expr_ref & out); void mk_eqs(unsigned sz, expr * const * a_bits, expr_ref_vector & eqs); void mk_shl(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_lshr(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_ashr(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_sle(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out); void mk_ule(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out); void mk_not(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits); void mk_and(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_or(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_xor(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_xnor(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_nand(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_nor(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_redand(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits); void mk_redor(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits); void mk_umul_no_overflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out); void mk_smul_no_overflow_core(unsigned sz, expr * const * a_bits, expr * const * b_bits, bool is_overflow, expr_ref & result); void mk_smul_no_overflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out); void mk_smul_no_underflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out); void mk_comp(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_carry_save_adder(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr * const * c_bits, expr_ref_vector & sum_bits, expr_ref_vector & carry_bits); void mk_const_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_abs(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits); }; #endif z3-4.4.0/src/ast/rewriter/bit_blaster/bit_blaster_params.h0000644000175000017500000000137012540347414023367 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bit_blaster_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-10-02. Revision History: --*/ #ifndef _BIT_BLASTER_PARAMS_H_ #define _BIT_BLASTER_PARAMS_H_ struct bit_blaster_params { bool m_bb_ext_gates; bool m_bb_quantifiers; bit_blaster_params(): m_bb_ext_gates(false), m_bb_quantifiers(false) { } #if 0 void register_params(ini_params & p) { p.register_bool_param("bb_ext_gates", m_bb_ext_gates, "use extended gates during bit-blasting"); p.register_bool_param("bb_quantifiers", m_bb_quantifiers, "convert bit-vectors to Booleans in quantifiers"); } #endif }; #endif /* _BIT_BLASTER_PARAMS_H_ */ z3-4.4.0/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h0000644000175000017500000012640112540347414023524 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bit_blaster_tpl_def.h Abstract: Template for bit-blaster operations Author: Leonardo de Moura (leonardo) 2011-05-02. Revision History: --*/ #include"bit_blaster_tpl.h" #include"rational.h" #include"ast_pp.h" #include"cooperate.h" #include"common_msgs.h" #include"rewriter_types.h" template void bit_blaster_tpl::checkpoint() { if (memory::get_allocation_size() > m_max_memory) throw rewriter_exception(Z3_MAX_MEMORY_MSG); if (m_cancel) throw rewriter_exception(Z3_CANCELED_MSG); cooperate("bit-blaster"); } /** \brief Return true if all bits are true or false. */ template bool bit_blaster_tpl::is_numeral(unsigned sz, expr * const * bits) const { for (unsigned i = 0; i < sz; i++) if (!m().is_true(bits[i]) && !m().is_false(bits[i])) return false; return true; } /** \brief Return true if all bits are true or false, and store the number represent by these bits in r. */ template bool bit_blaster_tpl::is_numeral(unsigned sz, expr * const * bits, numeral & r) const { r.reset(); for (unsigned i = 0; i < sz; i++) { if (m().is_true(bits[i])) r += power(i); else if (!m().is_false(bits[i])) return false; } return true; } /** \brief Return true if all bits are true. */ template bool bit_blaster_tpl::is_minus_one(unsigned sz, expr * const * bits) const { for (unsigned i = 0; i < sz; i++) if (!m().is_true(bits[i])) return false; return true; } // hack to avoid GCC compilation error. static void _num2bits(ast_manager & m, rational const & v, unsigned sz, expr_ref_vector & out_bits) { SASSERT(v.is_nonneg()); rational aux = v; rational two(2); for (unsigned i = 0; i < sz; i++) { if ((aux % two).is_zero()) out_bits.push_back(m.mk_false()); else out_bits.push_back(m.mk_true()); aux = div(aux, two); } } template void bit_blaster_tpl::num2bits(numeral const & v, unsigned sz, expr_ref_vector & out_bits) const { _num2bits(m(), v, sz, out_bits); } template void bit_blaster_tpl::mk_half_adder(expr * a, expr * b, expr_ref & out, expr_ref & cout) { mk_xor(a, b, out); mk_and(a, b, cout); } template void bit_blaster_tpl::mk_full_adder(expr * a, expr * b, expr * cin, expr_ref & out, expr_ref & cout) { mk_xor3(a, b, cin, out); mk_carry(a, b, cin, cout); } template void bit_blaster_tpl::mk_neg(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits) { SASSERT(sz > 0); expr_ref cin(m()), cout(m()), out(m()); cin = m().mk_true(); for (unsigned idx = 0; idx < sz; idx++) { expr_ref not_a(m()); mk_not(a_bits[idx], not_a); if (idx < sz - 1) mk_half_adder(not_a, cin, out, cout); else mk_xor(not_a, cin, out); out_bits.push_back(out); cin = cout; } } template void bit_blaster_tpl::mk_adder(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { SASSERT(sz > 0); expr_ref cin(m()), cout(m()), out(m()); cin = m().mk_false(); for (unsigned idx = 0; idx < sz; idx++) { if (idx < sz - 1) mk_full_adder(a_bits[idx], b_bits[idx], cin, out, cout); else mk_xor3(a_bits[idx], b_bits[idx], cin, out); out_bits.push_back(out); cin = cout; } #if 0 static unsigned counter = 0; counter++; verbose_stream() << "MK_ADDER: " << counter << std::endl; #endif } template void bit_blaster_tpl::mk_subtracter(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits, expr_ref & cout) { SASSERT(sz > 0); expr_ref cin(m()), out(m()); cin = m().mk_true(); for (unsigned j = 0; j < sz; j++) { expr_ref not_b(m()); mk_not(b_bits[j], not_b); mk_full_adder(a_bits[j], not_b, cin, out, cout); out_bits.push_back(out); cin = cout; } SASSERT(out_bits.size() == sz); } template void bit_blaster_tpl::mk_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { SASSERT(sz > 0); if (!m_use_bcm) { numeral n_a, n_b; if (is_numeral(sz, a_bits, n_b)) std::swap(a_bits, b_bits); if (is_minus_one(sz, b_bits)) { mk_neg(sz, a_bits, out_bits); return; } if (is_numeral(sz, a_bits, n_a)) { n_a *= n_b; num2bits(n_a, sz, out_bits); return; } } else { numeral n_a, n_b; if (is_numeral(sz, a_bits, n_a)) { mk_const_multiplier(sz, a_bits, b_bits, out_bits); return; } else if (is_numeral(sz, b_bits, n_b)) { mk_const_multiplier(sz, b_bits, a_bits, out_bits); return; } } if (!m_use_wtm) { #if 0 static unsigned counter = 0; counter++; verbose_stream() << "MK_MULTIPLIER: " << counter << std::endl; #endif expr_ref_vector cins(m()), couts(m()); expr_ref out(m()), cout(m()); mk_and(a_bits[0], b_bits[0], out); out_bits.push_back(out); /* out = a*b is encoded using the following circuit. a[0]&b[0] a[0]&b[1] a[0]&b[2] a[0]&b[3] ... | | | | | a[1]&b[0] - HA a[1]&b[1] - HA a[1]&b[2] - HA | | \ | \ | \ | | --------------- | -------------- | --- ... | | \| \ | | a[2]&b[0] - FA a[2]&b[1] - FA | | | \ | \ | | | -------------- | -- ... | | | \| | | | a[3]&b[0] - FA | | | | \ | | | | -- .... ... ... ... ... out[0] out[1] out[2] out[3] HA denotes a half-adder. FA denotes a full-adder. */ for (unsigned i = 1; i < sz; i++) { checkpoint(); couts.reset(); expr_ref i1(m()), i2(m()); mk_and(a_bits[0], b_bits[i], i1); mk_and(a_bits[1], b_bits[i-1], i2); if (i < sz - 1) { mk_half_adder(i1, i2, out, cout); couts.push_back(cout); for (unsigned j = 2; j <= i; j++) { expr_ref prev_out(m()); prev_out = out; expr_ref i3(m()); mk_and(a_bits[j], b_bits[i-j], i3); mk_full_adder(i3, prev_out, cins.get(j-2), out, cout); couts.push_back(cout); } out_bits.push_back(out); cins.swap(couts); } else { // last step --> I don't need to generate/store couts. mk_xor(i1, i2, out); for (unsigned j = 2; j <= i; j++) { expr_ref i3(m()); mk_and(a_bits[j], b_bits[i-j], i3); mk_xor3(i3, out, cins.get(j-2), out); } out_bits.push_back(out); } } } else { // WALLACE TREE MULTIPLIER if (sz == 1) { expr_ref t(m()); mk_and(a_bits[0], b_bits[0], t); out_bits.push_back(t); return; } // There are sz numbers to add and we use a Wallace tree to reduce that to two. // In this tree, we reduce as early as possible, as opposed to the Dada tree where some // additions may be delayed if they don't increase the propagation delay [which may be // a little bit more efficient, but it's tricky to find out which additions create // additional delays]. expr_ref zero(m()); zero = m().mk_false(); vector< expr_ref_vector > pps; pps.resize(sz, m()); for (unsigned i = 0; i < sz; i++) { checkpoint(); // The partial product is a_bits AND b_bits[i] // [or alternatively ITE(b_bits[i], a_bits, bv0[sz])] expr_ref_vector & pp = pps[i]; expr_ref t(m()); for (unsigned j = 0; j < i; j++) pp.push_back(zero); // left shift by i bits for (unsigned j = 0; j < (sz - i); j++) { mk_and(a_bits[j], b_bits[i], t); pp.push_back(t); } SASSERT(pps[i].size() == sz); } while (pps.size() != 2) { unsigned save_inx = 0; unsigned i = 0; unsigned end = pps.size() - 3; for ( ; i <= end; i += 3) { checkpoint(); expr_ref_vector pp1(m()), pp2(m()), pp3(m()); pp1.swap(pps[i]); pp2.swap(pps[i+1]); pp3.swap(pps[i+2]); expr_ref_vector & sum_bits = pps[save_inx]; expr_ref_vector & carry_bits = pps[save_inx+1]; SASSERT(sum_bits.empty() && carry_bits.empty()); carry_bits.push_back(zero); mk_carry_save_adder(pp1.size(), pp1.c_ptr(), pp2.c_ptr(), pp3.c_ptr(), sum_bits, carry_bits); carry_bits.pop_back(); save_inx += 2; } if (i == pps.size()-2) { pps[save_inx++].swap(pps[i++]); pps[save_inx++].swap(pps[i++]); } else if (i == pps.size()-1) { pps[save_inx++].swap(pps[i++]); } SASSERT (save_inx < pps.size() && i == pps.size()); pps.shrink(save_inx); } SASSERT(pps.size() == 2); // Now there are only two numbers to add, we can use a ripple carry adder here. mk_adder(sz, pps[0].c_ptr(), pps[1].c_ptr(), out_bits); } } template void bit_blaster_tpl::mk_umul_no_overflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & result) { SASSERT(sz > 0); expr_ref zero(m()); zero = m().mk_false(); ptr_buffer ext_a_bits; ptr_buffer ext_b_bits; ext_a_bits.append(sz, a_bits); ext_b_bits.append(sz, b_bits); ext_a_bits.push_back(zero); ext_b_bits.push_back(zero); SASSERT(ext_a_bits.size() == 1 + sz); SASSERT(ext_b_bits.size() == 1 + sz); expr_ref_vector mult_cout(m()); // // mk_multiplier will simplify output taking into account that // the most significant bits of ext_a_bits and ext_b_bits are zero. // mk_multiplier(1 + sz, ext_a_bits.c_ptr(), ext_b_bits.c_ptr(), mult_cout); expr_ref overflow1(m()), overflow2(m()), overflow(m()); // // ignore bits [0, sz-1] of mult_cout // overflow1 = mult_cout[sz].get(); expr_ref ovf(m()), v(m()), tmp(m()); ovf = m().mk_false(); v = m().mk_false(); for (unsigned i = 1; i < sz; ++i) { mk_or(ovf, a_bits[sz-i], ovf); mk_and(ovf, b_bits[i], tmp); mk_or(tmp, v, v); } overflow2 = v; mk_or(overflow1, overflow2, overflow); mk_not(overflow, result); } template void bit_blaster_tpl::mk_smul_no_overflow_core(unsigned sz, expr * const * a_bits, expr * const * b_bits, bool is_overflow, expr_ref & result) { SASSERT(sz > 0); expr_ref zero(m()); zero = m().mk_false(); ptr_buffer ext_a_bits; ptr_buffer ext_b_bits; ext_a_bits.append(sz, a_bits); ext_b_bits.append(sz, b_bits); ext_a_bits.push_back(a_bits[sz-1]); ext_b_bits.push_back(b_bits[sz-1]); SASSERT(ext_a_bits.size() == 1 + sz); SASSERT(ext_b_bits.size() == 1 + sz); expr_ref_vector mult_cout(m()); mk_multiplier(1 + sz, ext_a_bits.c_ptr(), ext_b_bits.c_ptr(), mult_cout); expr_ref overflow1(m()), overflow2(m()), overflow(m()); // // The two most significant bits are different. // mk_xor(mult_cout[sz].get(), mult_cout[sz-1].get(), overflow1); // // let // a_i = a[sz-1] xor a[i] // b_i = b[sz-1] xor b[i] // a_acc_i = a_{sz-2} or ... or a_{sz-1-i} // b = (a_acc_1 and b_1) or (a_acc_2 and b_2) or ... or (a_acc_{n-2} and b_{n-2}) // expr_ref v(m()), tmp(m()), a(m()), b(m()), a_acc(m()), sign(m()); a_acc = m().mk_false(); v = m().mk_false(); for (unsigned i = 1; i + 1 < sz; ++i) { mk_xor(b_bits[sz-1], b_bits[i], b); mk_xor(a_bits[sz-1], a_bits[sz-1-i], a); mk_or(a, a_acc, a_acc); mk_and(a_acc, b, tmp); mk_or(tmp, v, v); } overflow2 = v; mk_or(overflow1, overflow2, overflow); if (is_overflow) { // check for proper overflow // can only happen when the sign bits are the same. mk_iff(a_bits[sz-1], b_bits[sz-1], sign); } else { // check for proper underflow // can only happen when the sign bits are different. mk_xor(a_bits[sz-1], b_bits[sz-1], sign); } mk_and(sign, overflow, overflow); mk_not(overflow, result); } template void bit_blaster_tpl::mk_smul_no_overflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & result) { mk_smul_no_overflow_core(sz, a_bits, b_bits, true, result); } template void bit_blaster_tpl::mk_smul_no_underflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & result) { mk_smul_no_overflow_core(sz, a_bits, b_bits, false, result); } template void bit_blaster_tpl::mk_udiv_urem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & q_bits, expr_ref_vector & r_bits) { SASSERT(sz > 0); // p is the residual of each stage of the division. expr_ref_vector & p = r_bits; // t is an auxiliary vector used to store the result of a subtraction expr_ref_vector t(m()); // init p p.push_back(a_bits[sz-1]); for (unsigned i = 1; i < sz; i++) p.push_back(m().mk_false()); q_bits.resize(sz); for (unsigned i = 0; i < sz; i++) { checkpoint(); // generate p - b expr_ref q(m()); t.reset(); mk_subtracter(sz, p.c_ptr(), b_bits, t, q); q_bits.set(sz - i - 1, q); // update p if (i < sz - 1) { for (unsigned j = sz - 1; j > 0; j--) { expr_ref ie(m()); mk_ite(q, t.get(j-1), p.get(j-1), ie); p.set(j, ie); } p.set(0, a_bits[sz - i - 2]); } else { // last step: p contains the remainder for (unsigned j = 0; j < sz; j++) { expr_ref ie(m()); mk_ite(q, t.get(j), p.get(j), ie); p.set(j, ie); } } } DEBUG_CODE({ for (unsigned i = 0; i < sz; i++) { SASSERT(q_bits.get(i) != 0); }}); TRACE("bit_blaster", tout << "a: "; for (unsigned i = 0; i < sz; ++i) tout << mk_pp(a_bits[i], m()) << " "; tout << "\nb: "; for (unsigned i = 0; i < sz; ++i) tout << mk_pp(b_bits[i], m()) << " "; tout << "\nq: "; for (unsigned i = 0; i < sz; ++i) tout << mk_pp(q_bits[i].get(), m()) << " "; tout << "\nr: "; for (unsigned i = 0; i < sz; ++i) tout << mk_pp(r_bits[i].get(), m()) << " "; tout << "\n"; ); } template void bit_blaster_tpl::mk_udiv(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & q_bits) { expr_ref_vector aux(m()); mk_udiv_urem(sz, a_bits, b_bits, q_bits, aux); } template void bit_blaster_tpl::mk_urem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & r_bits) { expr_ref_vector aux(m()); mk_udiv_urem(sz, a_bits, b_bits, aux, r_bits); } template void bit_blaster_tpl::mk_multiplexer(expr * c, unsigned sz, expr * const * t_bits, expr * const * e_bits, expr_ref_vector & out_bits) { for (unsigned i = 0; i < sz; i++) { expr_ref t(m()); mk_ite(c, t_bits[i], e_bits[i], t); out_bits.push_back(t); } } template void bit_blaster_tpl::mk_abs(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits) { expr * a_msb = a_bits[sz - 1]; if (m().is_false(a_msb)) { out_bits.append(sz, a_bits); } else if (m().is_true(a_msb)) { mk_neg(sz, a_bits, out_bits); } else { expr_ref_vector neg_a_bits(m()); mk_neg(sz, a_bits, neg_a_bits); mk_multiplexer(a_msb, sz, neg_a_bits.c_ptr(), a_bits, out_bits); } } #define SDIV 0 #define SREM 1 #define SMOD 2 template template void bit_blaster_tpl::mk_sdiv_srem_smod(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { // This definition is only good when the most significant bits are set. // Otherwise, it will create 4 copies of the expensive sdiv/srem/smod SASSERT(k == SDIV || k == SREM || k == SMOD); expr * a_msb = a_bits[sz - 1]; expr * b_msb = b_bits[sz - 1]; expr_ref_vector neg_a_bits(m()); expr_ref_vector neg_b_bits(m()); mk_neg(sz, a_bits, neg_a_bits); mk_neg(sz, b_bits, neg_b_bits); expr_ref_vector pp_q(m()), pp_r(m()), pn_q(m()), pn_r(m()), np_q(m()), np_r(m()), nn_q(m()), nn_r(m()); if (!m().is_true(a_msb) && !m().is_true(b_msb)) { mk_udiv_urem(sz, a_bits, b_bits, pp_q, pp_r); } else { pp_q.resize(sz, m().mk_false()); pp_r.resize(sz, m().mk_false()); } if (!m().is_false(a_msb) && !m().is_true(b_msb)) { mk_udiv_urem(sz, neg_a_bits.c_ptr(), b_bits, np_q, np_r); } else { np_q.resize(sz, m().mk_false()); np_r.resize(sz, m().mk_false()); } if (!m().is_true(a_msb) && !m().is_false(b_msb)) { mk_udiv_urem(sz, a_bits, neg_b_bits.c_ptr(), pn_q, pn_r); } else { pn_q.resize(sz, m().mk_false()); pn_r.resize(sz, m().mk_false()); } if (!m().is_false(a_msb) && !m().is_false(b_msb)) { mk_udiv_urem(sz, neg_a_bits.c_ptr(), neg_b_bits.c_ptr(), nn_q, nn_r); } else { nn_q.resize(sz, m().mk_false()); nn_r.resize(sz, m().mk_false()); } expr_ref_vector ite1(m()), ite2(m()); if (k == SDIV) { expr_ref_vector & pp_out = pp_q; expr_ref_vector np_out(m()); expr_ref_vector pn_out(m()); expr_ref_vector & nn_out = nn_q; if (!m().is_false(a_msb) && !m().is_true(b_msb)) mk_neg(sz, np_q.c_ptr(), np_out); else np_out.resize(sz, m().mk_false()); if (!m().is_true(a_msb) && !m().is_false(b_msb)) mk_neg(sz, pn_q.c_ptr(), pn_out); else pn_out.resize(sz, m().mk_false()); #define MK_MULTIPLEXER() \ mk_multiplexer(b_msb, sz, nn_out.c_ptr(), np_out.c_ptr(), ite1); \ mk_multiplexer(b_msb, sz, pn_out.c_ptr(), pp_out.c_ptr(), ite2); \ mk_multiplexer(a_msb, sz, ite1.c_ptr(), ite2.c_ptr(), out_bits) MK_MULTIPLEXER(); } else if (k == SREM) { expr_ref_vector & pp_out = pp_r; expr_ref_vector np_out(m()); expr_ref_vector & pn_out = pn_r; expr_ref_vector nn_out(m()); if (!m().is_false(a_msb) && !m().is_true(b_msb)) mk_neg(sz, np_r.c_ptr(), np_out); else np_out.resize(sz, m().mk_false()); if (!m().is_false(a_msb) && !m().is_false(b_msb)) mk_neg(sz, nn_r.c_ptr(), nn_out); else nn_out.resize(sz, m().mk_false()); MK_MULTIPLEXER(); } else { SASSERT(k == SMOD); expr_ref_vector & pp_out = pp_r; expr_ref_vector np_out(m()); expr_ref_vector pn_out(m()); expr_ref_vector nn_out(m()); if (!m().is_false(a_msb) && !m().is_true(b_msb)) { expr_ref cout(m()); mk_subtracter(sz, b_bits, np_r.c_ptr(), np_out, cout); } else np_out.resize(sz, m().mk_false()); if (!m().is_true(a_msb) && !m().is_false(b_msb)) mk_adder(sz, b_bits, pn_r.c_ptr(), pn_out); else pn_out.resize(sz, m().mk_false()); if (!m().is_false(a_msb) && !m().is_false(b_msb)) mk_neg(sz, nn_r.c_ptr(), nn_out); else nn_out.resize(sz, m().mk_false()); MK_MULTIPLEXER(); } } template void bit_blaster_tpl::mk_sdiv(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { expr * a_msb = a_bits[sz - 1]; expr * b_msb = b_bits[sz - 1]; if (m().is_false(a_msb) && m().is_false(b_msb)) { mk_udiv(sz, a_bits, b_bits, out_bits); } else if (m().is_false(a_msb) && m().is_true(b_msb)) { expr_ref_vector neg_b_bits(m()); mk_neg(sz, b_bits, neg_b_bits); expr_ref_vector tmp(m()); mk_udiv(sz, a_bits, neg_b_bits.c_ptr(), tmp); mk_neg(sz, tmp.c_ptr(), out_bits); } else if (m().is_true(a_msb) && m().is_false(b_msb)) { expr_ref_vector neg_a_bits(m()); mk_neg(sz, a_bits, neg_a_bits); expr_ref_vector tmp(m()); mk_udiv(sz, neg_a_bits.c_ptr(), b_bits, tmp); mk_neg(sz, tmp.c_ptr(), out_bits); } else if (m().is_true(a_msb) && m().is_true(b_msb)) { expr_ref_vector neg_a_bits(m()); mk_neg(sz, a_bits, neg_a_bits); expr_ref_vector neg_b_bits(m()); mk_neg(sz, b_bits, neg_b_bits); mk_udiv(sz, neg_a_bits.c_ptr(), neg_b_bits.c_ptr(), out_bits); } else { #if 0 // creates 4 dividers mk_sdiv_srem_smod(sz, a_bits, b_bits, out_bits); #else // creates only 1 expr_ref_vector abs_a_bits(m()); expr_ref_vector abs_b_bits(m()); mk_abs(sz, a_bits, abs_a_bits); mk_abs(sz, b_bits, abs_b_bits); expr_ref_vector udiv_bits(m()); mk_udiv(sz, abs_a_bits.c_ptr(), abs_b_bits.c_ptr(), udiv_bits); expr_ref_vector neg_udiv_bits(m()); mk_neg(sz, udiv_bits.c_ptr(), neg_udiv_bits); expr_ref c(m()); mk_iff(a_msb, b_msb, c); mk_multiplexer(c, sz, udiv_bits.c_ptr(), neg_udiv_bits.c_ptr(), out_bits); #endif } } template void bit_blaster_tpl::mk_srem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { expr * a_msb = a_bits[sz - 1]; expr * b_msb = b_bits[sz - 1]; if (m().is_false(a_msb) && m().is_false(b_msb)) { mk_urem(sz, a_bits, b_bits, out_bits); } else if (m().is_false(a_msb) && m().is_true(b_msb)) { expr_ref_vector neg_b_bits(m()); mk_neg(sz, b_bits, neg_b_bits); mk_urem(sz, a_bits, neg_b_bits.c_ptr(), out_bits); } else if (m().is_true(a_msb) && m().is_false(b_msb)) { expr_ref_vector neg_a_bits(m()); mk_neg(sz, a_bits, neg_a_bits); expr_ref_vector tmp(m()); mk_urem(sz, neg_a_bits.c_ptr(), b_bits, tmp); mk_neg(sz, tmp.c_ptr(), out_bits); } else if (m().is_true(a_msb) && m().is_true(b_msb)) { expr_ref_vector neg_a_bits(m()); mk_neg(sz, a_bits, neg_a_bits); expr_ref_vector neg_b_bits(m()); mk_neg(sz, b_bits, neg_b_bits); expr_ref_vector tmp(m()); mk_urem(sz, neg_a_bits.c_ptr(), neg_b_bits.c_ptr(), tmp); mk_neg(sz, tmp.c_ptr(), out_bits); } else { #if 0 // creates 4 urem mk_sdiv_srem_smod(sz, a_bits, b_bits, out_bits); #else // creates only 1 expr_ref_vector abs_a_bits(m()); expr_ref_vector abs_b_bits(m()); mk_abs(sz, a_bits, abs_a_bits); mk_abs(sz, b_bits, abs_b_bits); expr_ref_vector urem_bits(m()); mk_urem(sz, abs_a_bits.c_ptr(), abs_b_bits.c_ptr(), urem_bits); expr_ref_vector neg_urem_bits(m()); mk_neg(sz, urem_bits.c_ptr(), neg_urem_bits); mk_multiplexer(a_msb, sz, neg_urem_bits.c_ptr(), urem_bits.c_ptr(), out_bits); #endif } } /** \brief Generate circuit for signed mod. This function implements the semantics of bvsmod given below for two bits: (define-fun bvsmod_def ((s (_ BitVec 2)) (t (_ BitVec 2))) (_ BitVec 2) (let ((msb_s ((_ extract 1 1) s)) (msb_t ((_ extract 1 1) t))) (let ((abs_s (ite (= msb_s #b0) s (bvneg s))) (abs_t (ite (= msb_t #b0) t (bvneg t)))) (let ((u (bvurem abs_s abs_t))) (ite (= u (_ bv0 2)) u (ite (and (= msb_s #b0) (= msb_t #b0)) u (ite (and (= msb_s #b1) (= msb_t #b0)) (bvadd (bvneg u) t) (ite (and (= msb_s #b0) (= msb_t #b1)) (bvadd u t) (bvneg u))))))))) Note: The semantics is sensitive to the order of these tests. It is unsound to test first for whether the most significant bits of s and t are known and use the cases for those. If u is 0 then the result is 0. */ template void bit_blaster_tpl::mk_smod(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { expr * a_msb = a_bits[sz - 1]; expr * b_msb = b_bits[sz - 1]; expr_ref_vector abs_a_bits(m()); expr_ref_vector abs_b_bits(m()); mk_abs(sz, a_bits, abs_a_bits); mk_abs(sz, b_bits, abs_b_bits); expr_ref_vector u_bits(m()); mk_urem(sz, abs_a_bits.c_ptr(), abs_b_bits.c_ptr(), u_bits); expr_ref_vector neg_u_bits(m()); mk_neg(sz, u_bits.c_ptr(), neg_u_bits); expr_ref_vector neg_u_add_b(m()); mk_adder(sz, neg_u_bits.c_ptr(), b_bits, neg_u_add_b); expr_ref_vector u_add_b(m()); mk_adder(sz, u_bits.c_ptr(), b_bits, u_add_b); expr_ref_vector zero(m()); num2bits(numeral(0), sz, zero); expr_ref u_eq_0(m()); mk_eq(sz, u_bits.c_ptr(), zero.c_ptr(), u_eq_0); expr_ref_vector & pp_bits = u_bits; // pos & pos case expr_ref_vector & pn_bits = u_add_b; // pos & neg case expr_ref_vector & np_bits = neg_u_add_b; // neg & pos case expr_ref_vector & nn_bits = neg_u_bits; // neg & neg case expr_ref_vector ite1(m()); expr_ref_vector ite2(m()); expr_ref_vector body(m()); mk_multiplexer(b_msb, sz, nn_bits.c_ptr(), np_bits.c_ptr(), ite1); mk_multiplexer(b_msb, sz, pn_bits.c_ptr(), pp_bits.c_ptr(), ite2); mk_multiplexer(a_msb, sz, ite1.c_ptr(), ite2.c_ptr(), body); mk_multiplexer(u_eq_0, sz, u_bits.c_ptr(), body.c_ptr(), out_bits); } template void bit_blaster_tpl::mk_eq(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out) { expr_ref_vector out_bits(m()); for (unsigned i = 0; i < sz; i++) { mk_iff(a_bits[i], b_bits[i], out); out_bits.push_back(out); } mk_and(out_bits.size(), out_bits.c_ptr(), out); } template void bit_blaster_tpl::mk_rotate_left(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits) { TRACE("bit_blaster", tout << n << ": " << sz << " "; for (unsigned i = 0; i < sz; ++i) { tout << mk_pp(a_bits[i], m()) << " "; } tout << "\n"; ); n = n % sz; for (unsigned i = sz - n; i < sz; i++) out_bits.push_back(a_bits[i]); for (unsigned i = 0 ; i < sz - n; i++) out_bits.push_back(a_bits[i]); } template void bit_blaster_tpl::mk_rotate_right(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits) { n = n % sz; mk_rotate_left(sz, a_bits, sz - n, out_bits); } template void bit_blaster_tpl::mk_sign_extend(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits) { for (unsigned i = 0; i < sz; i++) out_bits.push_back(a_bits[i]); expr * high_bit = a_bits[sz - 1]; for (unsigned i = sz; i < sz + n; i++) out_bits.push_back(high_bit); } template void bit_blaster_tpl::mk_zero_extend(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits) { for (unsigned i = 0; i < sz; i++) out_bits.push_back(a_bits[i]); expr * high_bit = m().mk_false(); for (unsigned i = sz; i < sz + n; i++) out_bits.push_back(high_bit); } /** \brief Return an expression that is true iff a_bits represents the number n. */ template void bit_blaster_tpl::mk_is_eq(unsigned sz, expr * const * a_bits, unsigned n, expr_ref & out) { numeral two(2); expr_ref_vector out_bits(m()); for (unsigned i = 0; i < sz; i++) { if (n % 2 == 0) { expr_ref not_a(m()); mk_not(a_bits[i], not_a); out_bits.push_back(not_a); } else { out_bits.push_back(a_bits[i]); } n = n / 2; } mk_and(out_bits.size(), out_bits.c_ptr(), out); } /** \brief Store in eqs the equalities a_bits = 0, a_bits = 1, ..., a_bits = sz -1. */ template void bit_blaster_tpl::mk_eqs(unsigned sz, expr * const * a_bits, expr_ref_vector & eqs) { for (unsigned i = 0; i < sz; i++) { expr_ref eq(m()); mk_is_eq(sz, a_bits, i, eq); eqs.push_back(eq); } } template void bit_blaster_tpl::mk_shl(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { numeral k; if (is_numeral(sz, b_bits, k)) { if (k > numeral(sz)) k = numeral(sz); unsigned n = static_cast(k.get_int64()); if (n >= sz) n = sz; unsigned pos; for (pos = 0; pos < n; pos++) out_bits.push_back(m().mk_false()); for (unsigned i = 0; pos < sz; pos++, i++) out_bits.push_back(a_bits[i]); } else { out_bits.append(sz, a_bits); unsigned i = 0; expr_ref_vector new_out_bits(m()); for (; i < sz; ++i) { checkpoint(); unsigned shift_i = 1 << i; if (shift_i >= sz) break; for (unsigned j = 0; j < sz; ++j) { expr_ref new_out(m()); expr* a_j = m().mk_false(); if (shift_i <= j) a_j = out_bits[j-shift_i].get(); mk_ite(b_bits[i], a_j, out_bits[j].get(), new_out); new_out_bits.push_back(new_out); } out_bits.reset(); out_bits.append(new_out_bits); new_out_bits.reset(); } expr_ref is_large(m()); is_large = m().mk_false(); for (; i < sz; ++i) { mk_or(is_large, b_bits[i], is_large); } for (unsigned j = 0; j < sz; ++j) { expr_ref new_out(m()); mk_ite(is_large, m().mk_false(), out_bits[j].get(), new_out); out_bits[j] = new_out; } } } template void bit_blaster_tpl::mk_lshr(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { numeral k; if (is_numeral(sz, b_bits, k)) { if (k > numeral(sz)) k = numeral(sz); unsigned n = static_cast(k.get_int64()); unsigned pos = 0; for (unsigned i = n; i < sz; pos++, i++) out_bits.push_back(a_bits[i]); for (; pos < sz; pos++) out_bits.push_back(m().mk_false()); } else { out_bits.append(sz, a_bits); unsigned i = 0; for (; i < sz; ++i) { checkpoint(); expr_ref_vector new_out_bits(m()); unsigned shift_i = 1 << i; if (shift_i >= sz) break; for (unsigned j = 0; j < sz; ++j) { expr_ref new_out(m()); expr* a_j = m().mk_false(); if (shift_i + j < sz) a_j = out_bits[j+shift_i].get(); mk_ite(b_bits[i], a_j, out_bits[j].get(), new_out); new_out_bits.push_back(new_out); } out_bits.reset(); out_bits.append(new_out_bits); } expr_ref is_large(m()); is_large = m().mk_false(); for (; i < sz; ++i) { mk_or(is_large, b_bits[i], is_large); } for (unsigned j = 0; j < sz; ++j) { expr_ref new_out(m()); mk_ite(is_large, m().mk_false(), out_bits[j].get(), new_out); out_bits[j] = new_out; } } } template void bit_blaster_tpl::mk_ashr(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { numeral k; if (is_numeral(sz, b_bits, k)) { if (k > numeral(sz)) k = numeral(sz); unsigned n = static_cast(k.get_int64()); unsigned pos = 0; for (unsigned i = n; i < sz; pos++, i++) out_bits.push_back(a_bits[i]); for (; pos < sz; pos++) out_bits.push_back(a_bits[sz-1]); } else { out_bits.append(sz, a_bits); unsigned i = 0; for (; i < sz; ++i) { checkpoint(); expr_ref_vector new_out_bits(m()); unsigned shift_i = 1 << i; if (shift_i >= sz) break; for (unsigned j = 0; j < sz; ++j) { expr_ref new_out(m()); expr* a_j = a_bits[sz-1]; if (shift_i + j < sz) a_j = out_bits[j+shift_i].get(); mk_ite(b_bits[i], a_j, out_bits[j].get(), new_out); new_out_bits.push_back(new_out); } out_bits.reset(); out_bits.append(new_out_bits); } expr_ref is_large(m()); is_large = m().mk_false(); for (; i < sz; ++i) { mk_or(is_large, b_bits[i], is_large); } for (unsigned j = 0; j < sz; ++j) { expr_ref new_out(m()); mk_ite(is_large, a_bits[sz-1], out_bits[j].get(), new_out); out_bits[j] = new_out; } } } template template void bit_blaster_tpl::mk_ext_rotate_left_right(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { numeral k; if (is_numeral(sz, b_bits, k) && k.is_unsigned()) { if (Left) mk_rotate_left(sz, a_bits, static_cast(k.get_uint64()), out_bits); else mk_rotate_right(sz, a_bits, static_cast(k.get_uint64()), out_bits); } else { // // Review: a better tuned implementation is possible by using shifts by power of two. // e.g., looping over the bits of b_bits, then rotate by a power of two depending // on the bit-position. This would get rid of the mk_urem. // expr_ref_vector sz_bits(m()); expr_ref_vector masked_b_bits(m()); expr_ref_vector eqs(m()); numeral sz_numeral(sz); num2bits(sz_numeral, sz, sz_bits); mk_urem(sz, b_bits, sz_bits.c_ptr(), masked_b_bits); mk_eqs(sz, masked_b_bits.c_ptr(), eqs); for (unsigned i = 0; i < sz; i++) { checkpoint(); expr_ref out(m()); out = a_bits[i]; for (unsigned j = 1; j < sz; j++) { expr_ref new_out(m()); unsigned src = (Left ? (sz + i - j) : (i + j)) % sz; mk_ite(eqs.get(j), a_bits[src], out, new_out); out = new_out; } out_bits.push_back(out); } } } template void bit_blaster_tpl::mk_ext_rotate_left(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { mk_ext_rotate_left_right(sz, a_bits, b_bits, out_bits); } template void bit_blaster_tpl::mk_ext_rotate_right(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { mk_ext_rotate_left_right(sz, a_bits, b_bits, out_bits); } template template void bit_blaster_tpl::mk_le(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out) { SASSERT(sz > 0); expr_ref i1(m()), i2(m()), i3(m()), not_a(m()); mk_not(a_bits[0], not_a); mk_or(not_a, b_bits[0], out); for (unsigned idx = 1; idx < (Signed ? sz - 1 : sz); idx++) { mk_not(a_bits[idx], not_a); mk_and(not_a, b_bits[idx], i1); mk_and(not_a, out, i2); mk_and(b_bits[idx], out, i3); mk_or(i1, i2, i3, out); } if (Signed) { expr_ref not_b(m()); mk_not(b_bits[sz-1], not_b); mk_and(not_b, a_bits[sz-1], i1); mk_and(not_b, out, i2); mk_and(a_bits[sz-1], out, i3); mk_or(i1, i2, i3, out); } } template void bit_blaster_tpl::mk_sle(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out) { mk_le(sz, a_bits, b_bits, out); } template void bit_blaster_tpl::mk_ule(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out) { mk_le(sz, a_bits, b_bits, out); } template void bit_blaster_tpl::mk_not(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits) { for (unsigned i = 0; i < sz; i++) { expr_ref t(m()); mk_not(a_bits[i], t); out_bits.push_back(t); } } #define MK_BINARY(NAME, OP) \ template \ void bit_blaster_tpl::NAME(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { \ for (unsigned i = 0; i < sz; i++) { \ expr_ref t(m()); \ OP(a_bits[i], b_bits[i], t); \ out_bits.push_back(t); \ } \ } MK_BINARY(mk_and, mk_and); MK_BINARY(mk_or, mk_or); MK_BINARY(mk_xor, mk_xor); MK_BINARY(mk_xnor, mk_iff); MK_BINARY(mk_nand, mk_nand); MK_BINARY(mk_nor, mk_nor); template void bit_blaster_tpl::mk_redand(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits) { expr_ref tmp(m()); mk_and(sz, a_bits, tmp); out_bits.push_back(tmp); } template void bit_blaster_tpl::mk_redor(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits) { expr_ref tmp(m()); mk_or(sz, a_bits, tmp); out_bits.push_back(tmp); } template void bit_blaster_tpl::mk_comp(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { expr_ref tmp(m()); mk_eq(sz, a_bits, b_bits, tmp); out_bits.push_back(tmp); } template void bit_blaster_tpl::mk_carry_save_adder(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr * const * c_bits, expr_ref_vector & sum_bits, expr_ref_vector & carry_bits) { expr_ref t(m()); for (unsigned i = 0; i < sz; i++) { mk_xor3(a_bits[i], b_bits[i], c_bits[i], t); sum_bits.push_back(t); mk_carry(a_bits[i], b_bits[i], c_bits[i], t); carry_bits.push_back(t); } } template void bit_blaster_tpl::mk_const_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { DEBUG_CODE({ numeral x; SASSERT(is_numeral(sz, a_bits, x)); SASSERT(out_bits.empty()); }); expr_ref_vector minus_b_bits(m()), tmp(m()); mk_neg(sz, b_bits, minus_b_bits); out_bits.resize(sz, m().mk_false()); #if 1 bool last=false, now; for (unsigned i = 0; i < sz; i++) { now = m().is_true(a_bits[i]); SASSERT(now || m().is_false(a_bits[i])); tmp.reset(); if (now && !last) { mk_adder(sz - i, out_bits.c_ptr() + i, minus_b_bits.c_ptr(), tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); // do not use [], it does not work on Linux. } else if (!now && last) { mk_adder(sz - i, out_bits.c_ptr() + i, b_bits, tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); // do not use [], it does not work on Linux. } last = now; } #else // Radix 4 Booth encoder // B = b_bits, -B = minus_b_bits // 2B = b2_bits, -2B = minus_b2_bits expr_ref_vector b2_bits(m()); expr_ref_vector minus_b2_bits(m()); b2_bits.push_back(m().mk_false()); minus_b2_bits.push_back(m().mk_false()); for (unsigned i = 0; i < sz-1; i++) { b2_bits.push_back(b_bits[i]); minus_b2_bits.push_back(minus_b_bits.get(i)); } bool last=false, now1, now2; for (unsigned i = 0; i < sz; i += 2) { now1 = m().is_true(a_bits[i]); now2 = m().is_true(a_bits[i+1]); SASSERT(now1 || m().is_false(a_bits[i])); SASSERT(now2 || m().is_false(a_bits[i+1])); tmp.reset(); if ((!now2 && !now1 && last) || (!now2 && now1 && !last)) { // Add B mk_adder(sz - i, out_bits.c_ptr() + i, b_bits, tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); } else if (!now2 && now1 && last) { // Add 2B mk_adder(sz - i, out_bits.c_ptr() + i, b2_bits.c_ptr(), tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); } else if (now2 && !now1 && !last) { // Add -2B mk_adder(sz - i, out_bits.c_ptr() + i, minus_b2_bits.c_ptr(), tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); } else if ((now2 && !now1 && last) || (now2 && now1 && !last)) { // Add -B mk_adder(sz - i, out_bits.c_ptr() + i, minus_b_bits.c_ptr(), tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); } last = now2; } #endif TRACE("bit_blaster_tpl_booth", for (unsigned i=0; i class poly_rewriter : public Config { protected: typedef typename Config::numeral numeral; sort * m_curr_sort; obj_map m_expr2pos; bool m_flat; bool m_som; unsigned m_som_blowup; bool m_sort_sums; bool m_hoist_mul; bool m_hoist_cmul; bool is_numeral(expr * n) const { return Config::is_numeral(n); } bool is_numeral(expr * n, numeral & r) const { return Config::is_numeral(n, r); } bool is_zero(expr * n) const { return Config::is_zero(n); } bool is_minus_one(expr * n) const { return Config::is_minus_one(n); } void normalize(numeral & c) { Config::normalize(c, m_curr_sort); } app * mk_numeral(numeral const & r) { return Config::mk_numeral(r, m_curr_sort); } decl_kind add_decl_kind() const { return Config::add_decl_kind(); } decl_kind mul_decl_kind() const { return Config::mul_decl_kind(); } bool use_power() const { return Config::use_power(); } decl_kind power_decl_kind() const { return Config::power_decl_kind(); } bool is_power(expr * t) const { return is_app_of(t, get_fid(), power_decl_kind()); } expr * get_power_body(expr * t, rational & k); struct mon_pw_lt; // functor used to sort monomial elements when use_power() == true expr * mk_mul_app(unsigned num_args, expr * const * args); expr * mk_mul_app(numeral const & c, expr * arg); expr * mk_add_app(unsigned num_args, expr * const * args); br_status mk_flat_mul_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_nflat_mul_core(unsigned num_args, expr * const * args, expr_ref & result); expr * get_power_product(expr * t); expr * get_power_product(expr * t, numeral & a); br_status mk_flat_add_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_nflat_add_core(unsigned num_args, expr * const * args, expr_ref & result); void set_curr_sort(sort * s) { m_curr_sort = s; } expr * const * get_monomials(expr * & t, unsigned & sz) { if (is_add(t)) { sz = to_app(t)->get_num_args(); return to_app(t)->get_args(); } else { sz = 1; return &t; } } br_status cancel_monomials(expr * lhs, expr * rhs, bool move, expr_ref & lhs_result, expr_ref & rhs_result); bool hoist_multiplication(expr_ref& som); expr* merge_muls(expr* x, expr* y); struct hoist_cmul_lt; bool is_mul(expr * t, numeral & c, expr * & pp); void hoist_cmul(expr_ref_buffer & args); public: poly_rewriter(ast_manager & m, params_ref const & p = params_ref()): Config(m), m_curr_sort(0), m_sort_sums(false) { updt_params(p); SASSERT(!m_som || m_flat); // som of monomials form requires flattening to be enabled. SASSERT(!m_som || !m_hoist_mul); // som is mutually exclusive with hoisting multiplication. updt_params(p); } ast_manager & m() const { return Config::m(); } family_id get_fid() const { return Config::get_fid(); } void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); void set_flat(bool f) { m_flat = f; } void set_sort_sums(bool f) { m_sort_sums = f; } bool is_add(expr * n) const { return is_app_of(n, get_fid(), add_decl_kind()); } bool is_mul(expr * n) const { return is_app_of(n, get_fid(), mul_decl_kind()); } bool is_add(func_decl * f) const { return is_decl_of(f, get_fid(), add_decl_kind()); } bool is_mul(func_decl * f) const { return is_decl_of(f, get_fid(), mul_decl_kind()); } br_status mk_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args > 0); if (num_args == 1) { result = args[0]; return BR_DONE; } set_curr_sort(m().get_sort(args[0])); return m_flat ? mk_flat_mul_core(num_args, args, result) : mk_nflat_mul_core(num_args, args, result); } br_status mk_add_core(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args > 0); if (num_args == 1) { result = args[0]; return BR_DONE; } set_curr_sort(m().get_sort(args[0])); return m_flat ? mk_flat_add_core(num_args, args, result) : mk_nflat_add_core(num_args, args, result); } void mk_add(unsigned num_args, expr * const * args, expr_ref & result) { if (mk_add_core(num_args, args, result) == BR_FAILED) result = mk_add_app(num_args, args); } void mk_add(expr* a1, expr* a2, expr_ref& result) { expr* args[2] = { a1, a2 }; mk_add(2, args, result); } void mk_mul(unsigned num_args, expr * const * args, expr_ref & result) { if (mk_mul_core(num_args, args, result) == BR_FAILED) result = mk_mul_app(num_args, args); } void mk_mul(expr* a1, expr* a2, expr_ref& result) { expr* args[2] = { a1, a2 }; mk_mul(2, args, result); } // The result of the following functions is never BR_FAILED br_status mk_uminus(expr * arg, expr_ref & result); br_status mk_sub(unsigned num_args, expr * const * args, expr_ref & result); void mk_sub(expr* a1, expr* a2, expr_ref& result) { expr* args[2] = { a1, a2 }; mk_sub(2, args, result); } }; #endif z3-4.4.0/src/ast/rewriter/quant_hoist.h0000644000175000017500000000353312540347414017601 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: quant_hoist.h Abstract: Quantifier hoisting utility. Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: Hoisted from quant_elim. --*/ #ifndef __QUANTIFIER_HOISTER_H_ #define __QUANTIFIER_HOISTER_H_ #include "ast.h" class quantifier_hoister { class impl; impl* m_impl; public: quantifier_hoister(ast_manager& m); ~quantifier_hoister(); /** \brief Pull top-most quantifier up. Create fresh constants for the bound variables. Return the constants, the quantifier type (forall or exists), and the sub-formula under the quantifier. The list of variables is empty if the formula is quantifier free or if the existing quantifiers occur under a connective other than or, and, implies, ite (then and else branch only). */ void operator()(expr* fml, app_ref_vector& vars, bool& is_fa, expr_ref& result); /** \brief Pull top-most existential quantifier up. The list of variables is empty if there are no top-level existential quantifier. */ void pull_exists(expr* fml, app_ref_vector& vars, expr_ref& result); /** \brief Pull top-most universal (is_forall=true) or existential (is_forall=false) quantifier up. The list of variables is empty if there are no top-level universal/existential quantifier. */ void pull_quantifier(bool is_forall, expr_ref& fml, app_ref_vector& vars); /** \brief Pull top-most universal (is_forall true) or existential (is_forall=false) quantifier up. Return an expression with de-Bruijn indices and the list of names that were used. Return index of maximal variable. */ unsigned pull_quantifier(bool is_forall, expr_ref& fml, ptr_vector* sorts, svector* names); }; #endif z3-4.4.0/src/ast/rewriter/rewriter_def.h0000644000175000017500000005310112540347414017720 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: rewriter_def.h Abstract: Lean and mean rewriter Author: Leonardo (leonardo) 2011-03-31 Notes: --*/ #include"rewriter.h" #include"ast_smt2_pp.h" template template void rewriter_tpl::process_var(var * v) { if (m_cfg.reduce_var(v, m_r, m_pr)) { result_stack().push_back(m_r); if (ProofGen) { result_pr_stack().push_back(m_pr); m_pr = 0; } set_new_child_flag(v); m_r = 0; return; } if (!ProofGen) { // bindings are only used when Proof Generation is not enabled. unsigned idx = v->get_idx(); if (idx < m_bindings.size()) { expr * r = m_bindings[m_bindings.size() - idx - 1]; TRACE("process_var", if (r) tout << "idx: " << idx << " --> " << mk_ismt2_pp(r, m()) << "\n"; tout << "bindings:\n"; for (unsigned i = 0; i < m_bindings.size(); i++) if (m_bindings[i]) tout << i << ": " << mk_ismt2_pp(m_bindings[i], m()) << "\n";); if (r != 0) { if (m_num_qvars == 0 || is_ground(r)) { result_stack().push_back(r); } else { expr_ref new_term(m()); m_shifter(r, m_num_qvars, new_term); result_stack().push_back(new_term); } set_new_child_flag(v); return; } } } result_stack().push_back(v); if (ProofGen) result_pr_stack().push_back(0); // implicit reflexivity } template template void rewriter_tpl::process_const(app * t) { SASSERT(t->get_num_args() == 0); br_status st = m_cfg.reduce_app(t->get_decl(), 0, 0, m_r, m_pr); SASSERT(st == BR_FAILED || st == BR_DONE); if (st == BR_DONE) { result_stack().push_back(m_r.get()); if (ProofGen) { if (m_pr) result_pr_stack().push_back(m_pr); else result_pr_stack().push_back(m().mk_rewrite(t, m_r)); m_pr = 0; } m_r = 0; set_new_child_flag(t); } else { result_stack().push_back(t); if (ProofGen) result_pr_stack().push_back(0); // implicit reflexivity } } /** \brief visit expression t. Return true if t was rewritten and the result is on the top m_result_stack. We may skip t if: - pre_visit(t) returns false - max_depth == 0 - t is already in the cache Otherwise, return false and add a new frame stack for t with the updated max_depth. */ template template bool rewriter_tpl::visit(expr * t, unsigned max_depth) { TRACE("rewriter_visit", tout << "visiting\n" << mk_ismt2_pp(t, m()) << "\n";); expr * new_t; proof * new_t_pr; if (m_cfg.get_subst(t, new_t, new_t_pr)) { TRACE("rewriter_subst", tout << "subst\n" << mk_ismt2_pp(t, m()) << "\n---->\n" << mk_ismt2_pp(new_t, m()) << "\n";); result_stack().push_back(new_t); set_new_child_flag(t, new_t); if (ProofGen) result_pr_stack().push_back(new_t_pr); return true; } if (max_depth == 0) { result_stack().push_back(t); if (ProofGen) result_pr_stack().push_back(0); // implicit reflexivity return true; // t is not going to be processed } SASSERT(max_depth > 0); SASSERT(max_depth <= RW_UNBOUNDED_DEPTH); bool c = must_cache(t); if (c) { #if 0 static unsigned checked_cache = 0; checked_cache ++; if (checked_cache % 100000 == 0) std::cerr << "[rewriter] num-cache-checks: " << checked_cache << std::endl; #endif expr * r = get_cached(t); if (r) { result_stack().push_back(r); set_new_child_flag(t, r); if (ProofGen) { proof * pr = get_cached_pr(t); result_pr_stack().push_back(pr); } return true; } } if (!pre_visit(t)) { result_stack().push_back(t); if (ProofGen) result_pr_stack().push_back(0); // implicit reflexivity return true; // t is not going to be processed } switch (t->get_kind()) { case AST_APP: if (to_app(t)->get_num_args() == 0) { process_const(to_app(t)); return true; } else { if (max_depth != RW_UNBOUNDED_DEPTH) max_depth--; push_frame(t, c, max_depth); return false; } case AST_VAR: process_var(to_var(t)); return true; case AST_QUANTIFIER: if (max_depth != RW_UNBOUNDED_DEPTH) max_depth--; push_frame(t, c, max_depth); return false; default: UNREACHABLE(); return true; } } template template void rewriter_tpl::process_app(app * t, frame & fr) { SASSERT(t->get_num_args() > 0); SASSERT(!frame_stack().empty()); switch (fr.m_state) { case PROCESS_CHILDREN: { unsigned num_args = t->get_num_args(); while (fr.m_i < num_args) { expr * arg = t->get_arg(fr.m_i); fr.m_i++; if (!visit(arg, fr.m_max_depth)) return; } func_decl * f = t->get_decl(); // If AC flattening is enabled, f is associative, t is not shared, and there is a previous frame on the stack. if (!ProofGen) { // this optimization is only used when Proof generation is disabled. if (f->is_associative() && t->get_ref_count() <= 1 && frame_stack().size() > 1) { frame & prev_fr = frame_stack()[frame_stack().size() - 2]; if (is_app(prev_fr.m_curr) && to_app(prev_fr.m_curr)->get_decl() == f && prev_fr.m_state == PROCESS_CHILDREN && flat_assoc(f)) { frame_stack().pop_back(); set_new_child_flag(t); return; } } } unsigned new_num_args = result_stack().size() - fr.m_spos; expr * const * new_args = result_stack().c_ptr() + fr.m_spos; app * new_t; if (ProofGen) { elim_reflex_prs(fr.m_spos); unsigned num_prs = result_pr_stack().size() - fr.m_spos; if (num_prs == 0) { new_t = t; m_pr = 0; } else { new_t = m().mk_app(f, new_num_args, new_args); m_pr = m().mk_congruence(t, new_t, num_prs, result_pr_stack().c_ptr() + fr.m_spos); } } br_status st = m_cfg.reduce_app(f, new_num_args, new_args, m_r, m_pr2); TRACE("reduce_app", tout << mk_ismt2_pp(t, m()) << "\n"; tout << "st: " << st; if (m_r) tout << " --->\n" << mk_ismt2_pp(m_r, m()); tout << "\n";); if (st != BR_FAILED) { result_stack().shrink(fr.m_spos); result_stack().push_back(m_r); if (ProofGen) { result_pr_stack().shrink(fr.m_spos); if (!m_pr2) m_pr2 = m().mk_rewrite(new_t, m_r); m_pr = m().mk_transitivity(m_pr, m_pr2); m_pr2 = 0; result_pr_stack().push_back(m_pr); } if (st == BR_DONE) { cache_result(t, m_r, m_pr, fr.m_cache_result); frame_stack().pop_back(); set_new_child_flag(t); m_r = 0; if (ProofGen) m_pr = 0; return; } else { fr.m_state = REWRITE_BUILTIN; SASSERT(st == BR_REWRITE1 || st == BR_REWRITE2 || st == BR_REWRITE3 || st == BR_REWRITE_FULL); unsigned max_depth = static_cast(st); SASSERT(0 <= max_depth && max_depth <= RW_UNBOUNDED_DEPTH); SASSERT((st == BR_REWRITE1) == (max_depth == 0)); SASSERT((st == BR_REWRITE2) == (max_depth == 1)); SASSERT((st == BR_REWRITE3) == (max_depth == 2)); SASSERT((st == BR_REWRITE_FULL) == (max_depth == RW_UNBOUNDED_DEPTH)); if (max_depth != RW_UNBOUNDED_DEPTH) max_depth++; if (visit(m_r, max_depth)) { if (ProofGen) { proof_ref pr2(m()), pr1(m()); pr2 = result_pr_stack().back(); result_pr_stack().pop_back(); pr1 = result_pr_stack().back(); result_pr_stack().pop_back(); m_pr = m().mk_transitivity(pr1, pr2); result_pr_stack().push_back(m_pr); } m_r = result_stack().back(); result_stack().pop_back(); result_stack().pop_back(); result_stack().push_back(m_r); cache_result(t, m_r, m_pr, fr.m_cache_result); frame_stack().pop_back(); set_new_child_flag(t); m_r = 0; if (ProofGen) m_pr = 0; return; } else { // frame was created for processing m_r m_r = 0; if (ProofGen) m_pr = 0; return; } } UNREACHABLE(); } // TODO: add rewrite rules support expr * def; proof * def_pr; quantifier * def_q; // When get_macro succeeds, then // we know that: // forall X. f(X) = def[X] // and def_pr is a proof for this quantifier. // // Remark: def_q is only used for proof generation. // It is the quantifier forall X. f(X) = def[X] if (get_macro(f, def, def_q, def_pr)) { SASSERT(!f->is_associative() || !flat_assoc(f)); SASSERT(new_num_args == t->get_num_args()); if (is_ground(def)) { m_r = def; if (ProofGen) { SASSERT(def_pr); m_pr = m().mk_transitivity(m_pr, def_pr); } } else { if (ProofGen) { NOT_IMPLEMENTED_YET(); // We do not support the use of bindings in proof generation mode. // Thus we have to apply the subsitution here, and // beta_reducer subst(m()); // subst.set_bindings(new_num_args, new_args); // expr_ref r2(m()); // subst(def, r2); } else { fr.m_state = EXPAND_DEF; TRACE("get_macro", tout << "f: " << f->get_name() << ", def: \n" << mk_ismt2_pp(def, m()) << "\n"; tout << "Args num: " << num_args << "\n"; for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(new_args[i], m()) << "\n";); unsigned i = num_args; while (i > 0) { --i; m_bindings.push_back(new_args[i]); } result_stack().push_back(def); TRACE("get_macro", tout << "bindings:\n"; for (unsigned j = 0; j < m_bindings.size(); j++) tout << j << ": " << mk_ismt2_pp(m_bindings[j], m()) << "\n";); begin_scope(); m_num_qvars = 0; m_root = def; push_frame(def, false, RW_UNBOUNDED_DEPTH); return; } } } else { if (ProofGen) { m_r = new_t; } else { if (fr.m_new_child) { m_r = m().mk_app(f, new_num_args, new_args); } else { TRACE("rewriter_reuse", tout << "reusing:\n" << mk_ismt2_pp(t, m()) << "\n";); m_r = t; } } } result_stack().shrink(fr.m_spos); result_stack().push_back(m_r); cache_result(t, m_r, m_pr, fr.m_cache_result); if (ProofGen) { result_pr_stack().shrink(fr.m_spos); result_pr_stack().push_back(m_pr); m_pr = 0; } frame_stack().pop_back(); set_new_child_flag(t, m_r); m_r = 0; return; } case REWRITE_BUILTIN: SASSERT(fr.m_spos + 2 == result_stack().size()); if (ProofGen) { proof_ref pr2(m()), pr1(m()); pr2 = result_pr_stack().back(); result_pr_stack().pop_back(); pr1 = result_pr_stack().back(); result_pr_stack().pop_back(); m_pr = m().mk_transitivity(pr1, pr2); result_pr_stack().push_back(m_pr); } m_r = result_stack().back(); result_stack().pop_back(); result_stack().pop_back(); result_stack().push_back(m_r); cache_result(t, m_r, m_pr, fr.m_cache_result); frame_stack().pop_back(); set_new_child_flag(t); return; case EXPAND_DEF: SASSERT(fr.m_spos + t->get_num_args() + 2 == result_stack().size()); SASSERT(t->get_num_args() <= m_bindings.size()); if (!ProofGen) { m_bindings.shrink(m_bindings.size() - t->get_num_args()); end_scope(); m_r = result_stack().back(); result_stack().shrink(fr.m_spos); result_stack().push_back(m_r); cache_result(t, m_r, m_pr, fr.m_cache_result); frame_stack().pop_back(); set_new_child_flag(t); } else { // TODO NOT_IMPLEMENTED_YET(); } return; case REWRITE_RULE: // support for rewriting rules was not implemented yet. NOT_IMPLEMENTED_YET(); break; default: UNREACHABLE(); break; } } template template void rewriter_tpl::process_quantifier(quantifier * q, frame & fr) { SASSERT(fr.m_state == PROCESS_CHILDREN); if (fr.m_i == 0) { if (!ProofGen) { begin_scope(); m_root = q->get_expr(); } m_num_qvars += q->get_num_decls(); if (!ProofGen) { for (unsigned i = 0; i < q->get_num_decls(); i++) m_bindings.push_back(0); } } unsigned num_children = rewrite_patterns() ? q->get_num_children() : 1; while (fr.m_i < num_children) { expr * child = q->get_child(fr.m_i); fr.m_i++; if (!visit(child, fr.m_max_depth)) return; } SASSERT(fr.m_spos + num_children == result_stack().size()); expr * const * it = result_stack().c_ptr() + fr.m_spos; expr * new_body = *it; expr * const * new_pats; expr * const * new_no_pats; if (rewrite_patterns()) { new_pats = it + 1; new_no_pats = new_pats + q->get_num_patterns(); } else { new_pats = q->get_patterns(); new_no_pats = q->get_no_patterns(); } if (ProofGen) { quantifier * new_q = m().update_quantifier(q, q->get_num_patterns(), new_pats, q->get_num_no_patterns(), new_no_pats, new_body); m_pr = q == new_q ? 0 : m().mk_quant_intro(q, new_q, result_pr_stack().get(fr.m_spos)); m_r = new_q; proof_ref pr2(m()); if (m_cfg.reduce_quantifier(new_q, new_body, new_pats, new_no_pats, m_r, pr2)) { m_pr = m().mk_transitivity(m_pr, pr2); } TRACE("reduce_quantifier_bug", tout << "m_pr is_null: " << (m_pr.get() == 0) << "\n"; if (m_pr) tout << mk_ismt2_pp(m_pr, m()) << "\n";); result_pr_stack().shrink(fr.m_spos); result_pr_stack().push_back(m_pr); } else { if (!m_cfg.reduce_quantifier(q, new_body, new_pats, new_no_pats, m_r, m_pr)) { if (fr.m_new_child) { m_r = m().update_quantifier(q, q->get_num_patterns(), new_pats, q->get_num_no_patterns(), new_no_pats, new_body); } else { TRACE("rewriter_reuse", tout << "reusing:\n" << mk_ismt2_pp(q, m()) << "\n";); m_r = q; } } } result_stack().shrink(fr.m_spos); result_stack().push_back(m_r.get()); if (!ProofGen) { SASSERT(q->get_num_decls() <= m_bindings.size()); m_bindings.shrink(m_bindings.size() - q->get_num_decls()); end_scope(); cache_result(q, m_r, m_pr, fr.m_cache_result); } else { cache_result(q, m_r, m_pr, fr.m_cache_result); m_pr = 0; } m_r = 0; frame_stack().pop_back(); set_new_child_flag(q, m_r); } template bool rewriter_tpl::not_rewriting() const { SASSERT(frame_stack().empty()); SASSERT(m_cache == m_cache_stack[0]); return true; } template rewriter_tpl::rewriter_tpl(ast_manager & m, bool proof_gen, Config & cfg): rewriter_core(m, proof_gen), m_cfg(cfg), m_num_steps(0), m_cancel(false), m_shifter(m), m_r(m), m_pr(m), m_pr2(m) { } template rewriter_tpl::~rewriter_tpl() { } template void rewriter_tpl::reset() { m_cfg.reset(); rewriter_core::reset(); m_bindings.reset(); m_shifter.reset(); } template void rewriter_tpl::cleanup() { m_cfg.cleanup(); rewriter_core::cleanup(); m_bindings.finalize(); m_shifter.cleanup(); } template void rewriter_tpl::set_bindings(unsigned num_bindings, expr * const * bindings) { SASSERT(!m_proof_gen); SASSERT(not_rewriting()); m_bindings.reset(); unsigned i = num_bindings; while (i > 0) { --i; m_bindings.push_back(bindings[i]); } } template void rewriter_tpl::set_inv_bindings(unsigned num_bindings, expr * const * bindings) { SASSERT(!m_proof_gen); SASSERT(not_rewriting()); m_bindings.reset(); for (unsigned i = 0; i < num_bindings; i++) { m_bindings.push_back(bindings[i]); } } template template void rewriter_tpl::main_loop(expr * t, expr_ref & result, proof_ref & result_pr) { SASSERT(!ProofGen || result_stack().size() == result_pr_stack().size()); SASSERT(not_rewriting()); m_root = t; m_num_qvars = 0; m_num_steps = 0; if (visit(t, RW_UNBOUNDED_DEPTH)) { result = result_stack().back(); result_stack().pop_back(); SASSERT(result_stack().empty()); if (ProofGen) { result_pr = result_pr_stack().back(); result_pr_stack().pop_back(); if (result_pr.get() == 0) result_pr = m().mk_reflexivity(t); SASSERT(result_pr_stack().empty()); } return; } resume_core(result, result_pr); } /** \brief Resume the execution when it was interrupted by cancel() or check_max_steps(). */ template template void rewriter_tpl::resume_core(expr_ref & result, proof_ref & result_pr) { SASSERT(!frame_stack().empty()); while (!frame_stack().empty()) { if (m_cancel) throw rewriter_exception(Z3_CANCELED_MSG); SASSERT(!ProofGen || result_stack().size() == result_pr_stack().size()); frame & fr = frame_stack().back(); expr * t = fr.m_curr; TRACE("rewriter_step", tout << "step\n" << mk_ismt2_pp(t, m()) << "\n";); m_num_steps++; check_max_steps(); if (first_visit(fr) && fr.m_cache_result) { expr * r = get_cached(t); if (r) { result_stack().push_back(r); if (ProofGen) { proof * pr = get_cached_pr(t); result_pr_stack().push_back(pr); } frame_stack().pop_back(); set_new_child_flag(t, r); continue; } } switch (t->get_kind()) { case AST_APP: process_app(to_app(t), fr); break; case AST_QUANTIFIER: process_quantifier(to_quantifier(t), fr); break; case AST_VAR: frame_stack().pop_back(); process_var(to_var(t)); break; default: UNREACHABLE(); break; } } result = result_stack().back(); result_stack().pop_back(); SASSERT(result_stack().empty()); if (ProofGen) { result_pr = result_pr_stack().back(); result_pr_stack().pop_back(); if (result_pr.get() == 0) result_pr = m().mk_reflexivity(m_root); SASSERT(result_pr_stack().empty()); } } template void rewriter_tpl::operator()(expr * t, expr_ref & result, proof_ref & result_pr) { if (m_proof_gen) main_loop(t, result, result_pr); else main_loop(t, result, result_pr); } template void rewriter_tpl::resume(expr_ref & result, proof_ref & result_pr) { if (m_proof_gen) resume_core(result, result_pr); else resume_core(result, result_pr); } z3-4.4.0/src/ast/rewriter/bv_rewriter.cpp0000644000175000017500000020300712540347414020126 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv_rewriter.cpp Abstract: Basic rewriting rules for bit-vectors Author: Leonardo (leonardo) 2011-04-14 Notes: --*/ #include"bv_rewriter.h" #include"bv_rewriter_params.hpp" #include"poly_rewriter_def.h" #include"ast_smt2_pp.h" mk_extract_proc::mk_extract_proc(bv_util & u): m_util(u), m_high(0), m_low(UINT_MAX), m_domain(0), m_f_cached(0) { } mk_extract_proc::~mk_extract_proc() { if (m_f_cached) { // m_f_cached has a reference to m_domain, so, I don't need to inc_ref m_domain ast_manager & m = m_util.get_manager(); m.dec_ref(m_f_cached); } } app * mk_extract_proc::operator()(unsigned high, unsigned low, expr * arg) { ast_manager & m = m_util.get_manager(); sort * s = m.get_sort(arg); if (m_low == low && m_high == high && m_domain == s) return m.mk_app(m_f_cached, arg); // m_f_cached has a reference to m_domain, so, I don't need to inc_ref m_domain if (m_f_cached) m.dec_ref(m_f_cached); app * r = to_app(m_util.mk_extract(high, low, arg)); m_high = high; m_low = low; m_domain = s; m_f_cached = r->get_decl(); m.inc_ref(m_f_cached); return r; } void bv_rewriter::updt_local_params(params_ref const & _p) { bv_rewriter_params p(_p); m_hi_div0 = p.hi_div0(); m_elim_sign_ext = p.elim_sign_ext(); m_mul2concat = p.mul2concat(); m_bit2bool = p.bit2bool(); m_blast_eq_value = p.blast_eq_value(); m_split_concat_eq = p.split_concat_eq(); m_udiv2mul = p.udiv2mul(); m_bvnot2arith = p.bvnot2arith(); m_bv_sort_ac = p.bv_sort_ac(); m_mkbv2num = _p.get_bool("mkbv2num", false); } void bv_rewriter::updt_params(params_ref const & p) { poly_rewriter::updt_params(p); updt_local_params(p); } void bv_rewriter::get_param_descrs(param_descrs & r) { poly_rewriter::get_param_descrs(r); bv_rewriter_params::collect_param_descrs(r); #ifndef _EXTERNAL_RELEASE r.insert("mkbv2num", CPK_BOOL, "(default: false) convert (mkbv [true/false]*) into a numeral"); #endif } br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(f->get_family_id() == get_fid()); switch(f->get_decl_kind()) { case OP_BIT0: SASSERT(num_args == 0); result = m_util.mk_numeral(0, 1); return BR_DONE; case OP_BIT1: SASSERT(num_args == 0); result = m_util.mk_numeral(1, 1); return BR_DONE; case OP_ULEQ: SASSERT(num_args == 2); return mk_ule(args[0], args[1], result); case OP_UGEQ: SASSERT(num_args == 2); return mk_uge(args[0], args[1], result); case OP_ULT: SASSERT(num_args == 2); return mk_ult(args[0], args[1], result); case OP_UGT: SASSERT(num_args == 2); return mk_ult(args[1], args[0], result); case OP_SLEQ: SASSERT(num_args == 2); return mk_sle(args[0], args[1], result); case OP_SGEQ: SASSERT(num_args == 2); return mk_sge(args[0], args[1], result); case OP_SLT: SASSERT(num_args == 2); return mk_slt(args[0], args[1], result); case OP_SGT: SASSERT(num_args == 2); return mk_slt(args[1], args[0], result); case OP_BADD: SASSERT(num_args > 0); return mk_bv_add(num_args, args, result); case OP_BMUL: SASSERT(num_args > 0); return mk_bv_mul(num_args, args, result); case OP_BSUB: SASSERT(num_args > 0); return mk_sub(num_args, args, result); case OP_BNEG: SASSERT(num_args == 1); return mk_uminus(args[0], result); case OP_BSHL: SASSERT(num_args == 2); return mk_bv_shl(args[0], args[1], result); case OP_BLSHR: SASSERT(num_args == 2); return mk_bv_lshr(args[0], args[1], result); case OP_BASHR: SASSERT(num_args == 2); return mk_bv_ashr(args[0], args[1], result); case OP_BSDIV: SASSERT(num_args == 2); return mk_bv_sdiv(args[0], args[1], result); case OP_BUDIV: SASSERT(num_args == 2); return mk_bv_udiv(args[0], args[1], result); case OP_BSREM: SASSERT(num_args == 2); return mk_bv_srem(args[0], args[1], result); case OP_BUREM: SASSERT(num_args == 2); return mk_bv_urem(args[0], args[1], result); case OP_BSMOD: SASSERT(num_args == 2); return mk_bv_smod(args[0], args[1], result); case OP_BSDIV_I: SASSERT(num_args == 2); return mk_bv_sdiv_i(args[0], args[1], result); case OP_BUDIV_I: SASSERT(num_args == 2); return mk_bv_udiv_i(args[0], args[1], result); case OP_BSREM_I: SASSERT(num_args == 2); return mk_bv_srem_i(args[0], args[1], result); case OP_BUREM_I: SASSERT(num_args == 2); return mk_bv_urem_i(args[0], args[1], result); case OP_BSMOD_I: SASSERT(num_args == 2); return mk_bv_smod_i(args[0], args[1], result); case OP_CONCAT: return mk_concat(num_args, args, result); case OP_EXTRACT: SASSERT(num_args == 1); return mk_extract(m_util.get_extract_high(f), m_util.get_extract_low(f), args[0], result); case OP_REPEAT: SASSERT(num_args == 1); return mk_repeat(f->get_parameter(0).get_int(), args[0], result); case OP_ZERO_EXT: SASSERT(num_args == 1); return mk_zero_extend(f->get_parameter(0).get_int(), args[0], result); case OP_SIGN_EXT: SASSERT(num_args == 1); return mk_sign_extend(f->get_parameter(0).get_int(), args[0], result); case OP_BOR: return mk_bv_or(num_args, args, result); case OP_BXOR: return mk_bv_xor(num_args, args, result); case OP_BNOT: SASSERT(num_args == 1); return mk_bv_not(args[0], result); case OP_BAND: return mk_bv_and(num_args, args, result); case OP_BNAND: return mk_bv_nand(num_args, args, result); case OP_BNOR: return mk_bv_nor(num_args, args, result); case OP_BXNOR: return mk_bv_xnor(num_args, args, result); case OP_ROTATE_LEFT: SASSERT(num_args == 1); return mk_bv_rotate_left(f->get_parameter(0).get_int(), args[0], result); case OP_ROTATE_RIGHT: SASSERT(num_args == 1); return mk_bv_rotate_right(f->get_parameter(0).get_int(), args[0], result); case OP_EXT_ROTATE_LEFT: SASSERT(num_args == 2); return mk_bv_ext_rotate_left(args[0], args[1], result); case OP_EXT_ROTATE_RIGHT: SASSERT(num_args == 2); return mk_bv_ext_rotate_right(args[0], args[1], result); case OP_BV2INT: SASSERT(num_args == 1); return mk_bv2int(args[0], result); case OP_INT2BV: SASSERT(num_args == 1); return mk_int2bv(m_util.get_bv_size(f->get_range()), args[0], result); case OP_BREDOR: SASSERT(num_args == 1); return mk_bv_redor(args[0], result); case OP_BREDAND: SASSERT(num_args == 1); return mk_bv_redand(args[0], result); case OP_BCOMP: SASSERT(num_args == 2); return mk_bv_comp(args[0], args[1], result); case OP_MKBV: return mk_mkbv(num_args, args, result); default: return BR_FAILED; } } br_status bv_rewriter::mk_ule(expr * a, expr * b, expr_ref & result) { return mk_leq_core(false, a, b, result); } br_status bv_rewriter::mk_uge(expr * a, expr * b, expr_ref & result) { br_status st = mk_ule(b, a, result); if (st != BR_FAILED) return st; result = m_util.mk_ule(b, a); return BR_DONE; } br_status bv_rewriter::mk_ult(expr * a, expr * b, expr_ref & result) { result = m().mk_not(m_util.mk_ule(b, a)); return BR_REWRITE2; } br_status bv_rewriter::mk_sle(expr * a, expr * b, expr_ref & result) { return mk_leq_core(true, a, b, result); } br_status bv_rewriter::mk_sge(expr * a, expr * b, expr_ref & result) { br_status st = mk_sle(b, a, result); if (st != BR_FAILED) return st; result = m_util.mk_sle(b, a); return BR_DONE; } br_status bv_rewriter::mk_slt(expr * a, expr * b, expr_ref & result) { result = m().mk_not(m_util.mk_sle(b, a)); return BR_REWRITE2; } br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref & result) { numeral r1, r2, r3; unsigned sz; bool is_num1 = is_numeral(a, r1, sz); bool is_num2 = is_numeral(b, r2, sz); if (a == b) { result = m().mk_true(); return BR_DONE; } if (is_num1) r1 = m_util.norm(r1, sz, is_signed); if (is_num2) r2 = m_util.norm(r2, sz, is_signed); if (is_num1 && is_num2) { result = r1 <= r2 ? m().mk_true() : m().mk_false(); return BR_DONE; } numeral lower, upper; if (is_num1 || is_num2) { if (is_signed) { lower = - rational::power_of_two(sz - 1); upper = rational::power_of_two(sz - 1) - numeral(1); } else { lower = numeral(0); upper = rational::power_of_two(sz) - numeral(1); } } if (is_num2) { if (r2 == lower) { result = m().mk_eq(a, b); return BR_REWRITE1; } if (r2 == upper) { result = m().mk_true(); return BR_DONE; } } if (is_num1) { // 0 <= b is true if (r1 == lower) { result = m().mk_true(); return BR_DONE; } // 2^n-1 <= b is a = b if (r1 == upper) { result = m().mk_eq(a, b); return BR_REWRITE1; } } #if 0 if (!is_signed && m_util.is_concat(b) && to_app(b)->get_num_args() == 2 && m_util.is_zero(to_app(b)->get_arg(0))) { // // a <=_u (concat 0 c) ---> a[h:l] = 0 && a[l-1:0] <=_u c // expr * b_1 = to_app(b)->get_arg(0); expr * b_2 = to_app(b)->get_arg(1); unsigned sz1 = get_bv_size(b_1); unsigned sz2 = get_bv_size(b_2); result = m().mk_and(m().mk_eq(m_mk_extract(sz2+sz1-1, sz2, a), b_1), m_util.mk_ule(m_mk_extract(sz2-1, 0, a), b_2)); return BR_REWRITE3; } #else if (!is_signed) { // Extended version of the rule above using is_zero_bit. // It also catches examples atoms such as: // // a <=_u #x000f // unsigned bv_sz = m_util.get_bv_size(b); unsigned i = bv_sz; unsigned first_non_zero = UINT_MAX; while (i > 0) { --i; if (!is_zero_bit(b, i)) { first_non_zero = i; break; } } if (first_non_zero == UINT_MAX) { // all bits are zero result = m().mk_eq(a, m_util.mk_numeral(numeral(0), bv_sz)); return BR_REWRITE1; } else if (first_non_zero < bv_sz - 1) { result = m().mk_and(m().mk_eq(m_mk_extract(bv_sz - 1, first_non_zero + 1, a), m_util.mk_numeral(numeral(0), bv_sz - first_non_zero - 1)), m_util.mk_ule(m_mk_extract(first_non_zero, 0, a), m_mk_extract(first_non_zero, 0, b))); return BR_REWRITE3; } } #endif // Investigate if we need: // // k <=_s (concat 0 a) <=> (k[u:l] = 0 && k[l-1:0] <=_u a) || k[u:u] = bv1 // // (concat 0 a) <=_s k <=> k[u:u] = bv0 && (k[u:l] != 0 || a <=_u k[l-1:0]) // // (concat 0 a) <=_u k <=> k[u:l] != 0 || a <=_u k[l-1:0] // return BR_FAILED; } br_status bv_rewriter::mk_extract(unsigned high, unsigned low, expr * arg, expr_ref & result) { unsigned sz = get_bv_size(arg); SASSERT(sz > 0); if (low == 0 && high == sz - 1) { result = arg; return BR_DONE; } numeral v; if (is_numeral(arg, v, sz)) { sz = high - low + 1; if (v.is_neg()) mod(v, rational::power_of_two(sz), v); if (v.is_uint64()) { uint64 u = v.get_uint64(); uint64 e = shift_right(u, low) & (shift_left(1ull, sz) - 1ull); result = mk_numeral(numeral(e, numeral::ui64()), sz); return BR_DONE; } div(v, rational::power_of_two(low), v); result = mk_numeral(v, sz); return BR_DONE; } // (extract[high:low] (extract[high2:low2] x)) == (extract[high+low2 : low+low2] x) if (m_util.is_extract(arg)) { unsigned low2 = m_util.get_extract_low(arg); result = m_mk_extract(high + low2, low + low2, to_app(arg)->get_arg(0)); return BR_DONE; } // (extract (concat ....)) --> (concat (extract ...) ... (extract ...) ) if (m_util.is_concat(arg)) { unsigned num = to_app(arg)->get_num_args(); unsigned idx = sz; for (unsigned i = 0; i < num; i++) { expr * curr = to_app(arg)->get_arg(i); unsigned curr_sz = get_bv_size(curr); idx -= curr_sz; if (idx > high) continue; // found first argument if (idx <= low) { // result is a fragment of this argument if (low == idx && high - idx == curr_sz - 1) { result = curr; return BR_DONE; } else { result = m_mk_extract(high - idx, low - idx, curr); return BR_REWRITE1; } } else { // look for remaining arguments ptr_buffer new_args; bool used_extract = false; if (high - idx == curr_sz - 1) { new_args.push_back(curr); } else { used_extract = true; new_args.push_back(m_mk_extract(high - idx, 0, curr)); } for (unsigned j = i + 1; j < num; j++) { curr = to_app(arg)->get_arg(j); unsigned curr_sz = get_bv_size(curr); idx -= curr_sz; if (idx > low) { new_args.push_back(curr); continue; } if (idx == low) { new_args.push_back(curr); result = m_util.mk_concat(new_args.size(), new_args.c_ptr()); return used_extract ? BR_REWRITE2 : BR_DONE; } new_args.push_back(m_mk_extract(curr_sz - 1, low - idx, curr)); result = m_util.mk_concat(new_args.size(), new_args.c_ptr()); return BR_REWRITE2; } UNREACHABLE(); } } UNREACHABLE(); } if (m_util.is_bv_not(arg) || m_util.is_bv_or(arg) || m_util.is_bv_xor(arg) || (low == 0 && (m_util.is_bv_add(arg) || m_util.is_bv_mul(arg)))) { ptr_buffer new_args; unsigned num = to_app(arg)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * curr = to_app(arg)->get_arg(i); new_args.push_back(m_mk_extract(high, low, curr)); } result = m().mk_app(get_fid(), to_app(arg)->get_decl()->get_decl_kind(), new_args.size(), new_args.c_ptr()); return BR_REWRITE2; } if (m().is_ite(arg)) { result = m().mk_ite(to_app(arg)->get_arg(0), m_mk_extract(high, low, to_app(arg)->get_arg(1)), m_mk_extract(high, low, to_app(arg)->get_arg(2))); return BR_REWRITE2; } return BR_FAILED; } br_status bv_rewriter::mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result) { numeral r1, r2; unsigned bv_size = get_bv_size(arg1); unsigned sz; if (is_numeral(arg2, r2, sz)) { if (r2.is_zero()) { // x << 0 == x result = arg1; return BR_DONE; } if (r2 >= numeral(bv_size)) { result = mk_numeral(0, bv_size); return BR_DONE; } if (is_numeral(arg1, r1, sz)) { if (bv_size <= 64) { SASSERT(r1.is_uint64() && r2.is_uint64()); SASSERT(r2.get_uint64() < bv_size); uint64 r = shift_left(r1.get_uint64(), r2.get_uint64()); numeral rn(r, numeral::ui64()); rn = m_util.norm(rn, bv_size); result = mk_numeral(rn, bv_size); return BR_DONE; } SASSERT(r2 < numeral(bv_size)); SASSERT(r2.is_unsigned()); r1 = m_util.norm(r1 * rational::power_of_two(r2.get_unsigned()), bv_size); result = mk_numeral(r1, bv_size); return BR_DONE; } SASSERT(r2.is_pos()); SASSERT(r2 < numeral(bv_size)); // (bvshl x k) -> (concat (extract [n-1-k:0] x) bv0:k) unsigned k = r2.get_unsigned(); expr * new_args[2] = { m_mk_extract(bv_size - k - 1, 0, arg1), mk_numeral(0, k) }; result = m_util.mk_concat(2, new_args); return BR_REWRITE2; } return BR_FAILED; } br_status bv_rewriter::mk_bv_lshr(expr * arg1, expr * arg2, expr_ref & result) { numeral r1, r2; unsigned bv_size = get_bv_size(arg1); unsigned sz; if (is_numeral(arg2, r2, sz)) { if (r2.is_zero()) { // x >> 0 == x result = arg1; return BR_DONE; } if (r2 >= numeral(bv_size)) { result = mk_numeral(0, bv_size); return BR_DONE; } if (is_numeral(arg1, r1, sz)) { if (bv_size <= 64) { SASSERT(r1.is_uint64()); SASSERT(r2.is_uint64()); uint64 r = shift_right(r1.get_uint64(), r2.get_uint64()); numeral rn(r, numeral::ui64()); rn = m_util.norm(rn, bv_size); result = mk_numeral(rn, bv_size); return BR_DONE; } SASSERT(r2.is_unsigned()); unsigned sh = r2.get_unsigned(); div(r1, rational::power_of_two(sh), r1); result = mk_numeral(r1, bv_size); return BR_DONE; } SASSERT(r2.is_pos()); SASSERT(r2 < numeral(bv_size)); // (bvlshr x k) -> (concat bv0:k (extract [n-1:k] x)) SASSERT(r2.is_unsigned()); unsigned k = r2.get_unsigned(); expr * new_args[2] = { mk_numeral(0, k), m_mk_extract(bv_size - 1, k, arg1) }; result = m_util.mk_concat(2, new_args); return BR_REWRITE2; } return BR_FAILED; } br_status bv_rewriter::mk_bv_ashr(expr * arg1, expr * arg2, expr_ref & result) { numeral r1, r2; unsigned bv_size = get_bv_size(arg1); SASSERT(bv_size > 0); bool is_num2 = is_numeral(arg2, r2, bv_size); if (is_num2 && r2.is_zero()) { result = arg1; return BR_DONE; } bool is_num1 = is_numeral(arg1, r1, bv_size); if (bv_size <= 64 && is_num1 && is_num2) { uint64 n1 = r1.get_uint64(); uint64 n2_orig = r2.get_uint64(); uint64 n2 = n2_orig % bv_size; SASSERT(n2 < bv_size); uint64 r = shift_right(n1, n2); bool sign = (n1 & shift_left(1ull, bv_size - 1ull)) != 0; if (n2_orig > n2) { if (sign) { r = shift_left(1ull, bv_size) - 1ull; } else { r = 0; } } else if (sign) { uint64 allone = shift_left(1ull, bv_size) - 1ull; uint64 mask = ~(shift_left(1ull, bv_size - n2) - 1ull); mask &= allone; r |= mask; } result = mk_numeral(numeral(r, numeral::ui64()), bv_size); return BR_DONE; } if (is_num1 && is_num2 && numeral(bv_size) <= r2) { if (m_util.has_sign_bit(r1, bv_size)) result = mk_numeral(rational::power_of_two(bv_size) - numeral(1), bv_size); else result = mk_numeral(0, bv_size); return BR_DONE; } if (is_num1 && is_num2) { SASSERT(r2 < numeral(bv_size)); bool sign = m_util.has_sign_bit(r1, bv_size); div(r1, rational::power_of_two(r2.get_unsigned()), r1); if (sign) { // pad ones. numeral p(1); for (unsigned i = 0; i < bv_size; ++i) { if (r1 < p) { r1 += p; } p *= numeral(2); } } result = mk_numeral(r1, bv_size); return BR_DONE; } // (bvashr (bvashr x r1) r2) --> (bvashr x r1+r2) if (is_num2 && m_util.is_bv_ashr(arg1) && is_numeral(to_app(arg1)->get_arg(1), r1, bv_size)) { r1 += r2; if (r1 > numeral(bv_size)) r1 = numeral(bv_size); result = m().mk_app(get_fid(), OP_BASHR, to_app(arg1)->get_arg(0), mk_numeral(r1, bv_size)); return BR_REWRITE1; // not really needed at this time. } #if 0 // (bvashr x k) --> (concat extract[sz-1:sz-1](x) ... extract[sz-1:sz-1](x) extract[sz-1:k](x)) if (is_num2) { ptr_buffer new_args; if (r2 > numeral(bv_size)) r2 = numeral(bv_size); SASSERT(r2 <= numeral(bv_size)); unsigned k = r2.get_unsigned(); expr * sign = m_mk_extract(bv_size-1, bv_size-1, arg1); for (unsigned i = 0; i < k; i++) new_args.push_back(sign); if (k != bv_size) new_args.push_back(m_mk_extract(bv_size-1, k, arg1)); result = m_util.mk_concat(new_args.size(), new_args.c_ptr()); return BR_REWRITE2; } #endif return BR_FAILED; } br_status bv_rewriter::mk_bv_sdiv_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result) { numeral r1, r2; unsigned bv_size; if (is_numeral(arg2, r2, bv_size)) { r2 = m_util.norm(r2, bv_size, true); if (r2.is_zero()) { if (!hi_div0) { result = m().mk_app(get_fid(), OP_BSDIV0, arg1); return BR_DONE; } else { // The "hardware interpretation" for (bvsdiv x 0) is (ite (bvslt x #x0000) #x0001 #xffff) result = m().mk_ite(m().mk_app(get_fid(), OP_SLT, arg1, mk_numeral(0, bv_size)), mk_numeral(1, bv_size), mk_numeral(rational::power_of_two(bv_size) - numeral(1), bv_size)); return BR_REWRITE2; } } if (r2.is_one()) { result = arg1; return BR_DONE; } if (!r2.is_zero() && is_numeral(arg1, r1, bv_size)) { r1 = m_util.norm(r1, bv_size, true); result = mk_numeral(machine_div(r1, r2), bv_size); return BR_DONE; } result = m().mk_app(get_fid(), OP_BSDIV_I, arg1, arg2); return BR_DONE; } if (hi_div0) { result = m().mk_app(get_fid(), OP_BSDIV_I, arg1, arg2); return BR_DONE; } bv_size = get_bv_size(arg2); result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)), m().mk_app(get_fid(), OP_BSDIV0, arg1), m().mk_app(get_fid(), OP_BSDIV_I, arg1, arg2)); return BR_REWRITE2; } br_status bv_rewriter::mk_bv_udiv_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result) { numeral r1, r2; unsigned bv_size; TRACE("bv_udiv", tout << "hi_div0: " << hi_div0 << "\n";); TRACE("udiv2mul", tout << mk_ismt2_pp(arg2, m()) << " udiv2mul: " << m_udiv2mul << "\n";); if (is_numeral(arg2, r2, bv_size)) { r2 = m_util.norm(r2, bv_size); if (r2.is_zero()) { if (!hi_div0) { result = m().mk_app(get_fid(), OP_BUDIV0, arg1); return BR_DONE; } else { // The "hardware interpretation" for (bvudiv x 0) is #xffff result = mk_numeral(rational::power_of_two(bv_size) - numeral(1), bv_size); return BR_DONE; } } if (r2.is_one()) { result = arg1; return BR_DONE; } if (!r2.is_zero() && is_numeral(arg1, r1, bv_size)) { r1 = m_util.norm(r1, bv_size); result = mk_numeral(machine_div(r1, r2), bv_size); return BR_DONE; } unsigned shift; if (r2.is_power_of_two(shift)) { result = m().mk_app(get_fid(), OP_BLSHR, arg1, mk_numeral(shift, bv_size)); return BR_REWRITE1; } if (m_udiv2mul) { TRACE("udiv2mul", tout << "using udiv2mul\n";); numeral inv_r2; if (m_util.mult_inverse(r2, bv_size, inv_r2)) { result = m().mk_app(get_fid(), OP_BMUL, mk_numeral(inv_r2, bv_size), arg1); return BR_REWRITE1; } } result = m().mk_app(get_fid(), OP_BUDIV_I, arg1, arg2); return BR_DONE; } if (hi_div0) { result = m().mk_app(get_fid(), OP_BUDIV_I, arg1, arg2); return BR_DONE; } bv_size = get_bv_size(arg2); result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)), m().mk_app(get_fid(), OP_BUDIV0, arg1), m().mk_app(get_fid(), OP_BUDIV_I, arg1, arg2)); TRACE("bv_udiv", tout << mk_ismt2_pp(arg1, m()) << "\n" << mk_ismt2_pp(arg2, m()) << "\n---->\n" << mk_ismt2_pp(result, m()) << "\n";); return BR_REWRITE2; } br_status bv_rewriter::mk_bv_srem_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result) { numeral r1, r2; unsigned bv_size; if (is_numeral(arg2, r2, bv_size)) { r2 = m_util.norm(r2, bv_size, true); if (r2.is_zero()) { if (!hi_div0) { result = m().mk_app(get_fid(), OP_BSREM0, arg1); return BR_DONE; } else { // The "hardware interpretation" for (bvsrem x 0) is x result = arg1; return BR_DONE; } } if (r2.is_one()) { result = mk_numeral(0, bv_size); return BR_DONE; } if (!r2.is_zero() && is_numeral(arg1, r1, bv_size)) { r1 = m_util.norm(r1, bv_size, true); result = mk_numeral(r1 % r2, bv_size); return BR_DONE; } result = m().mk_app(get_fid(), OP_BSREM_I, arg1, arg2); return BR_DONE; } if (hi_div0) { result = m().mk_app(get_fid(), OP_BSREM_I, arg1, arg2); return BR_DONE; } bv_size = get_bv_size(arg2); result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)), m().mk_app(get_fid(), OP_BSREM0, arg1), m().mk_app(get_fid(), OP_BSREM_I, arg1, arg2)); return BR_REWRITE2; } bool bv_rewriter::is_minus_one_core(expr * arg) const { numeral r; unsigned bv_size; if (is_numeral(arg, r, bv_size)) { return r == (rational::power_of_two(bv_size) - numeral(1)); } return false; } bool bv_rewriter::is_x_minus_one(expr * arg, expr * & x) { if (is_add(arg) && to_app(arg)->get_num_args() == 2) { if (is_minus_one_core(to_app(arg)->get_arg(0))) { x = to_app(arg)->get_arg(1); return true; } if (is_minus_one_core(to_app(arg)->get_arg(1))) { x = to_app(arg)->get_arg(0); return true; } } return false; } br_status bv_rewriter::mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result) { numeral r1, r2; unsigned bv_size; bool is_num1 = is_numeral(arg1, r1, bv_size); if (is_numeral(arg2, r2, bv_size)) { r2 = m_util.norm(r2, bv_size); if (r2.is_zero()) { if (!hi_div0) { result = m().mk_app(get_fid(), OP_BUREM0, arg1); return BR_DONE; } else { // The "hardware interpretation" for (bvurem x 0) is x result = arg1; return BR_DONE; } } if (r2.is_one()) { result = mk_numeral(0, bv_size); return BR_DONE; } if (!r2.is_zero() && is_num1) { r1 = m_util.norm(r1, bv_size); r1 %= r2; result = mk_numeral(r1, bv_size); return BR_DONE; } unsigned shift; if (r2.is_power_of_two(shift)) { expr * args[2] = { mk_numeral(0, bv_size - shift), m_mk_extract(shift-1, 0, arg1) }; result = m_util.mk_concat(2, args); return BR_REWRITE2; } result = m().mk_app(get_fid(), OP_BUREM_I, arg1, arg2); return BR_DONE; } if (!hi_div0) { // urem(0, x) ==> ite(x = 0, urem0(x), 0) if (is_num1 && r1.is_zero()) { expr * zero = arg1; result = m().mk_ite(m().mk_eq(arg2, zero), m().mk_app(get_fid(), OP_BUREM0, zero), zero); return BR_REWRITE2; } // urem(x - 1, x) ==> ite(x = 0, urem0(x-1), x - 1) ==> ite(x = 0, urem0(-1), x - 1) expr * x; if (is_x_minus_one(arg1, x) && x == arg2) { bv_size = get_bv_size(arg1); expr * x_minus_1 = arg1; expr * minus_one = mk_numeral(rational::power_of_two(bv_size) - numeral(1), bv_size); result = m().mk_ite(m().mk_eq(x, mk_numeral(0, bv_size)), m().mk_app(get_fid(), OP_BUREM0, minus_one), x_minus_1); return BR_REWRITE2; } } else { // Remark: when HI_DIV0=true is used, (bvurem x 0) --> x if (is_num1 && r1.is_zero()) { // urem(0, x) --> 0 expr * zero = arg1; result = zero; return BR_DONE; } // urem(x - 1, x) --> x - 1 expr * x; if (is_x_minus_one(arg1, x) && x == arg2) { expr * x_minus_1 = arg1; result = x_minus_1; return BR_DONE; } } if (hi_div0) { result = m().mk_app(get_fid(), OP_BUREM_I, arg1, arg2); return BR_DONE; } bv_size = get_bv_size(arg2); result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)), m().mk_app(get_fid(), OP_BUREM0, arg1), m().mk_app(get_fid(), OP_BUREM_I, arg1, arg2)); return BR_REWRITE2; } br_status bv_rewriter::mk_bv_smod_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result) { numeral r1, r2; unsigned bv_size; bool is_num1 = is_numeral(arg1, r1, bv_size); if (is_num1) { r1 = m_util.norm(r1, bv_size, true); if (r1.is_zero()) { result = m().mk_app(get_fid(), OP_BUREM, arg1, arg2); return BR_REWRITE1; } } if (is_numeral(arg2, r2, bv_size)) { r2 = m_util.norm(r2, bv_size, true); if (r2.is_zero()) { if (!hi_div0) result = m().mk_app(get_fid(), OP_BSMOD0, arg1); else result = arg1; return BR_DONE; } if (is_num1) { numeral abs_r1 = m_util.norm(abs(r1), bv_size); numeral abs_r2 = m_util.norm(abs(r2), bv_size); numeral u = m_util.norm(abs_r1 % abs_r2, bv_size); numeral r; if (u.is_zero()) r = u; else if (r1.is_pos() && r2.is_pos()) r = u; else if (r1.is_neg() && r2.is_pos()) r = m_util.norm(-u + r2, bv_size); else if (r1.is_pos() && r2.is_neg()) r = m_util.norm(u + r2, bv_size); else r = m_util.norm(-u, bv_size); result = mk_numeral(r, bv_size); return BR_DONE; } if (r2.is_one()) { // (bvsmod x 1) --> 0 result = mk_numeral(0, bv_size); return BR_REWRITE2; } } if (hi_div0) { result = m().mk_app(get_fid(), OP_BSMOD_I, arg1, arg2); return BR_DONE; } bv_size = get_bv_size(arg2); result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)), m().mk_app(get_fid(), OP_BSMOD0, arg1), m().mk_app(get_fid(), OP_BSMOD_I, arg1, arg2)); return BR_REWRITE2; } br_status bv_rewriter::mk_int2bv(unsigned bv_size, expr * arg, expr_ref & result) { numeral val; bool is_int; if (m_autil.is_numeral(arg, val, is_int)) { val = m_util.norm(val, bv_size); result = mk_numeral(val, bv_size); return BR_DONE; } // (int2bv (bv2int x)) --> x if (m_util.is_bv2int(arg) && bv_size == get_bv_size(to_app(arg)->get_arg(0))) { result = to_app(arg)->get_arg(0); return BR_DONE; } return BR_FAILED; } br_status bv_rewriter::mk_bv2int(expr * arg, expr_ref & result) { numeral v; unsigned sz; if (is_numeral(arg, v, sz)) { result = m_autil.mk_numeral(v, true); return BR_DONE; } // TODO: add other simplifications return BR_FAILED; } br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_ref & result) { expr_ref_buffer new_args(m()); numeral v1; numeral v2; unsigned sz1, sz2; bool fused_numeral = false; bool expanded = false; bool fused_extract = false; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; expr * prev = 0; if (i > 0) prev = new_args.back(); if (is_numeral(arg, v1, sz1) && prev != 0 && is_numeral(prev, v2, sz2)) { v2 *= rational::power_of_two(sz1); v2 += v1; new_args.pop_back(); new_args.push_back(mk_numeral(v2, sz1+sz2)); fused_numeral = true; } else if (m_flat && m_util.is_concat(arg)) { unsigned num2 = to_app(arg)->get_num_args(); for (unsigned j = 0; j < num2; j++) { new_args.push_back(to_app(arg)->get_arg(j)); } expanded = true; } else if (m_util.is_extract(arg) && prev != 0 && m_util.is_extract(prev) && to_app(arg)->get_arg(0) == to_app(prev)->get_arg(0) && m_util.get_extract_low(prev) == m_util.get_extract_high(arg) + 1) { // (concat (extract[h1,l1] a) (extract[h2,l2] a)) --> (extract[h1,l2] a) if l1 == h2+1 expr * new_arg = m_mk_extract(m_util.get_extract_high(prev), m_util.get_extract_low(arg), to_app(arg)->get_arg(0)); new_args.pop_back(); new_args.push_back(new_arg); fused_extract = true; } else { new_args.push_back(arg); } } if (!fused_numeral && !expanded && !fused_extract) return BR_FAILED; SASSERT(!new_args.empty()); if (new_args.size() == 1) { result = new_args.back(); return fused_extract ? BR_REWRITE1 : BR_DONE; } result = m_util.mk_concat(new_args.size(), new_args.c_ptr()); if (fused_extract) return BR_REWRITE2; else if (expanded) return BR_REWRITE1; else return BR_DONE; } br_status bv_rewriter::mk_zero_extend(unsigned n, expr * arg, expr_ref & result) { if (n == 0) { result = arg; return BR_DONE; } else { expr * args[2] = { mk_numeral(0, n), arg }; result = m_util.mk_concat(2, args); return BR_REWRITE1; } } br_status bv_rewriter::mk_sign_extend(unsigned n, expr * arg, expr_ref & result) { if (n == 0) { result = arg; return BR_DONE; } numeral r; unsigned bv_size; if (is_numeral(arg, r, bv_size)) { unsigned result_bv_size = bv_size + n; r = m_util.norm(r, bv_size, true); mod(r, rational::power_of_two(result_bv_size), r); result = mk_numeral(r, result_bv_size); return BR_DONE; } if (m_elim_sign_ext) { unsigned sz = get_bv_size(arg); expr * sign = m_mk_extract(sz-1, sz-1, arg); ptr_buffer args; for (unsigned i = 0; i < n; i++) args.push_back(sign); args.push_back(arg); result = m_util.mk_concat(args.size(), args.c_ptr()); return BR_REWRITE2; } return BR_FAILED; } br_status bv_rewriter::mk_repeat(unsigned n, expr * arg, expr_ref & result) { if (n == 1) { result = arg; return BR_DONE; } ptr_buffer args; for (unsigned i = 0; i < n; i++) args.push_back(arg); result = m_util.mk_concat(args.size(), args.c_ptr()); return BR_REWRITE1; } br_status bv_rewriter::mk_bv_or(unsigned num, expr * const * args, expr_ref & result) { SASSERT(num > 0); if (num == 1) { result = args[0]; return BR_DONE; } unsigned sz = get_bv_size(args[0]); bool flattened = false; ptr_buffer flat_args; if (m_flat) { for (unsigned i = 0; i < num; i++) { expr * arg = args[i]; if (m_util.is_bv_or(arg)) { unsigned num2 = to_app(arg)->get_num_args(); for (unsigned j = 0; j < num2; j++) flat_args.push_back(to_app(arg)->get_arg(j)); } else { flat_args.push_back(arg); } } if (flat_args.size() != num) { flattened = true; num = flat_args.size(); args = flat_args.c_ptr(); } } ptr_buffer new_args; expr_fast_mark1 pos_args; expr_fast_mark2 neg_args; bool merged = false; unsigned num_coeffs = 0; numeral v1, v2; for (unsigned i = 0; i < num; i++) { expr * arg = args[i]; if (is_numeral(arg, v2, sz)) { num_coeffs++; v1 = bitwise_or(v1, v2); continue; } if (m_util.is_bv_not(arg)) { expr * atom = to_app(arg)->get_arg(0); if (pos_args.is_marked(atom)) { result = mk_numeral(rational::power_of_two(sz) - numeral(1), sz); return BR_DONE; } else if (neg_args.is_marked(atom)) { merged = true; continue; } neg_args.mark(atom, true); new_args.push_back(arg); } else { if (pos_args.is_marked(arg)) { merged = true; continue; } else if (neg_args.is_marked(arg)) { result = mk_numeral(rational::power_of_two(sz) - numeral(1), sz); return BR_DONE; } pos_args.mark(arg, true); new_args.push_back(arg); } } if (v1 == rational::power_of_two(sz) - numeral(1)) { result = mk_numeral(v1, sz); return BR_DONE; } // Simplifications of the form: // (bvor (concat x #x00) (concat #x00 y)) --> (concat x y) if (new_args.size() == 2 && num_coeffs == 0 && m_util.is_concat(new_args[0]) && m_util.is_concat(new_args[1])) { app * concat1 = to_app(new_args[0]); app * concat2 = to_app(new_args[1]); unsigned i = 0; for (i = 0; i < sz; i++) if (!is_zero_bit(concat1, i) && !is_zero_bit(concat2, i)) break; if (i == sz) { // is target ptr_buffer non_zero_args; int j = sz; j--; while (j >= 0) { int high = j; while (j >= 0 && is_zero_bit(concat1, j)) --j; if (j != high) non_zero_args.push_back(m_mk_extract(high, j+1, concat2)); high = j; while (j >= 0 && is_zero_bit(concat2, j)) --j; if (j != high) non_zero_args.push_back(m_mk_extract(high, j+1, concat1)); } result = m_util.mk_concat(non_zero_args.size(), non_zero_args.c_ptr()); return BR_REWRITE2; } } if (!v1.is_zero() && new_args.size() == 1) { v1 = m_util.norm(v1, sz); #ifdef _TRACE numeral old_v1 = v1; #endif // OR is a mask expr * t = new_args[0]; numeral two(2); ptr_buffer exs; unsigned low = 0; unsigned i = 0; while (i < sz) { while (i < sz && mod(v1, two).is_one()) { i++; div(v1, two, v1); } if (i != low) { unsigned num_sz = i - low; exs.push_back(m_util.mk_numeral(rational::power_of_two(num_sz) - numeral(1), num_sz)); low = i; } while (i < sz && mod(v1, two).is_zero()) { i++; div(v1, two, v1); } if (i != low) { exs.push_back(m_mk_extract(i-1, low, t)); low = i; } } std::reverse(exs.begin(), exs.end()); result = m_util.mk_concat(exs.size(), exs.c_ptr()); TRACE("mask_bug", tout << "(assert (distinct (bvor (_ bv" << old_v1 << " " << sz << ")\n" << mk_ismt2_pp(t, m()) << ")\n"; tout << mk_ismt2_pp(result, m()) << "))\n";); return BR_REWRITE2; } if (!flattened && !merged && (num_coeffs == 0 || (num_coeffs == 1 && !v1.is_zero())) && (!m_bv_sort_ac || is_sorted(num, args))) { return BR_FAILED; } if (!v1.is_zero()) { new_args.push_back(mk_numeral(v1, sz)); } switch (new_args.size()) { case 0: result = mk_numeral(0, sz); return BR_DONE; case 1: result = new_args[0]; return BR_DONE; default: if (m_bv_sort_ac) std::sort(new_args.begin(), new_args.end(), ast_to_lt()); result = m_util.mk_bv_or(new_args.size(), new_args.c_ptr()); return BR_DONE; } } br_status bv_rewriter::mk_bv_xor(unsigned num, expr * const * args, expr_ref & result) { SASSERT(num > 0); if (num == 1) { result = args[0]; return BR_DONE; } unsigned sz = get_bv_size(args[0]); bool flattened = false; ptr_buffer flat_args; if (m_flat) { for (unsigned i = 0; i < num; i++) { expr * arg = args[i]; if (m_util.is_bv_xor(arg)) { unsigned num2 = to_app(arg)->get_num_args(); for (unsigned j = 0; j < num2; j++) flat_args.push_back(to_app(arg)->get_arg(j)); } else { flat_args.push_back(arg); } } if (flat_args.size() != num) { flattened = true; num = flat_args.size(); args = flat_args.c_ptr(); } } expr_fast_mark1 pos_args; expr_fast_mark2 neg_args; bool merged = false; numeral v1, v2; unsigned num_coeffs = 0; for (unsigned i = 0; i < num; i++) { expr * arg = args[i]; if (is_numeral(arg, v2, sz)) { v1 = bitwise_xor(v1, v2); num_coeffs++; continue; } if (m_util.is_bv_not(arg)) { expr * atom = to_app(arg)->get_arg(0); if (neg_args.is_marked(atom)) { neg_args.mark(atom, false); merged = true; } else if (pos_args.is_marked(atom)) { pos_args.mark(atom, false); merged = true; v1 = bitwise_xor(v1, rational::power_of_two(sz) - numeral(1)); } else { neg_args.mark(atom, true); } } else { if (pos_args.is_marked(arg)) { pos_args.mark(arg, false); merged = true; } else if (neg_args.is_marked(arg)) { neg_args.mark(arg, false); merged = true; v1 = bitwise_xor(v1, rational::power_of_two(sz) - numeral(1)); } else { pos_args.mark(arg, true); } } } // XOR is a mask // All arguments but one is a numeral. // // Apply a transformation of the form: // // (bvxor a 0011) --> (concat ((_ extract 3 2) a) ((_ extract 1 0) (bvnot a))) // if (!v1.is_zero() && num_coeffs == num - 1) { // find argument that is not a numeral expr * t = 0; for (unsigned i = 0; i < num; i++) { t = args[i]; if (!is_numeral(t)) break; } SASSERT(t != 0); numeral two(2); expr_ref_buffer exs(m()); expr_ref not_t(m()); not_t = m_util.mk_bv_not(t); unsigned low = 0; unsigned i = 0; while (i < sz) { while (i < sz && mod(v1, two).is_one()) { i++; div(v1, two, v1); } if (i != low) { exs.push_back(m_mk_extract(i-1, low, not_t)); low = i; } while (i < sz && mod(v1, two).is_zero()) { i++; div(v1, two, v1); } if (i != low) { exs.push_back(m_mk_extract(i-1, low, t)); low = i; } } std::reverse(exs.c_ptr(), exs.c_ptr() + exs.size()); if (exs.size() == 1) result = exs[0]; else result = m_util.mk_concat(exs.size(), exs.c_ptr()); return BR_REWRITE3; } if (!merged && !flattened && (num_coeffs == 0 || (num_coeffs == 1 && !v1.is_zero() && v1 != (rational::power_of_two(sz) - numeral(1)))) && (!m_bv_sort_ac || is_sorted(num, args))) return BR_FAILED; ptr_buffer new_args; expr_ref c(m()); // may not be used if (!v1.is_zero()) { c = mk_numeral(v1, sz); new_args.push_back(c); } for (unsigned i = 0; i < num; i++) { expr * arg = args[i]; if (is_numeral(arg)) continue; if (m_util.is_bv_not(arg)) { expr * atom = to_app(arg)->get_arg(0); if (neg_args.is_marked(atom)) { new_args.push_back(arg); neg_args.mark(atom, false); } } else if (pos_args.is_marked(arg)) { new_args.push_back(arg); pos_args.mark(arg, false); } } switch (new_args.size()) { case 0: result = mk_numeral(0, sz); return BR_DONE; case 1: result = new_args[0]; return BR_DONE; case 2: if (m_util.is_allone(new_args[0])) { result = m_util.mk_bv_not(new_args[1]); return BR_DONE; } __fallthrough; default: if (m_bv_sort_ac) std::sort(new_args.begin(), new_args.end(), ast_to_lt()); result = m_util.mk_bv_xor(new_args.size(), new_args.c_ptr()); return BR_DONE; } } br_status bv_rewriter::mk_bv_not(expr * arg, expr_ref & result) { if (m_util.is_bv_not(arg)) { result = to_app(arg)->get_arg(0); return BR_DONE; } numeral val; unsigned bv_size; if (is_numeral(arg, val, bv_size)) { val = bitwise_not(bv_size, val); result = mk_numeral(val, bv_size); return BR_DONE; } #if 1 if (m_util.is_concat(arg)) { ptr_buffer new_args; unsigned num = to_app(arg)->get_num_args(); for (unsigned i = 0; i < num; i++) { new_args.push_back(m_util.mk_bv_not(to_app(arg)->get_arg(i))); } result = m_util.mk_concat(new_args.size(), new_args.c_ptr()); return BR_REWRITE2; } #endif if (m_bvnot2arith) { // (bvnot x) --> (bvsub -1 x) bv_size = get_bv_size(arg); rational minus_one = (rational::power_of_two(bv_size) - numeral(1)); result = m_util.mk_bv_sub(m_util.mk_numeral(minus_one, bv_size), arg); return BR_REWRITE1; } return BR_FAILED; } br_status bv_rewriter::mk_bv_and(unsigned num, expr * const * args, expr_ref & result) { ptr_buffer new_args; for (unsigned i = 0; i < num; i++) { new_args.push_back(m_util.mk_bv_not(args[i])); } SASSERT(num == new_args.size()); result = m_util.mk_bv_not(m_util.mk_bv_or(new_args.size(), new_args.c_ptr())); return BR_REWRITE3; } br_status bv_rewriter::mk_bv_nand(unsigned num, expr * const * args, expr_ref & result) { ptr_buffer new_args; for (unsigned i = 0; i < num; i++) { new_args.push_back(m_util.mk_bv_not(args[i])); } result = m_util.mk_bv_or(new_args.size(), new_args.c_ptr()); return BR_REWRITE2; } br_status bv_rewriter::mk_bv_nor(unsigned num, expr * const * args, expr_ref & result) { result = m_util.mk_bv_not(m_util.mk_bv_or(num, args)); return BR_REWRITE2; } br_status bv_rewriter::mk_bv_xnor(unsigned num_args, expr * const * args, expr_ref & result) { result = m_util.mk_bv_not(m_util.mk_bv_xor(num_args, args)); return BR_REWRITE2; } br_status bv_rewriter::mk_bv_rotate_left(unsigned n, expr * arg, expr_ref & result) { unsigned sz = get_bv_size(arg); SASSERT(sz > 0); n = n % sz; if (n == 0 || sz == 1) { result = arg; return BR_DONE; } expr * args[2] = { m_mk_extract(sz - n - 1, 0, arg), m_mk_extract(sz - 1, sz - n, arg) }; result = m_util.mk_concat(2, args); return BR_REWRITE2; } br_status bv_rewriter::mk_bv_rotate_right(unsigned n, expr * arg, expr_ref & result) { unsigned sz = get_bv_size(arg); SASSERT(sz > 0); n = n % sz; return mk_bv_rotate_left(sz - n, arg, result); } br_status bv_rewriter::mk_bv_ext_rotate_left(expr * arg1, expr * arg2, expr_ref & result) { numeral r2; unsigned bv_size; if (is_numeral(arg2, r2, bv_size)) { unsigned shift = static_cast((r2 % numeral(bv_size)).get_uint64() % static_cast(bv_size)); return mk_bv_rotate_left(shift, arg1, result); } return BR_FAILED; } br_status bv_rewriter::mk_bv_ext_rotate_right(expr * arg1, expr * arg2, expr_ref & result) { numeral r2; unsigned bv_size; if (is_numeral(arg2, r2, bv_size)) { unsigned shift = static_cast((r2 % numeral(bv_size)).get_uint64() % static_cast(bv_size)); return mk_bv_rotate_right(shift, arg1, result); } return BR_FAILED; } br_status bv_rewriter::mk_bv_redor(expr * arg, expr_ref & result) { if (is_numeral(arg)) { result = m_util.is_zero(arg) ? mk_numeral(0, 1) : mk_numeral(1, 1); return BR_DONE; } return BR_FAILED; } br_status bv_rewriter::mk_bv_redand(expr * arg, expr_ref & result) { numeral r; unsigned bv_size; if (is_numeral(arg, r, bv_size)) { result = (r == rational::power_of_two(bv_size) - numeral(1)) ? mk_numeral(1, 1) : mk_numeral(0, 1); return BR_DONE; } return BR_FAILED; } br_status bv_rewriter::mk_bv_comp(expr * arg1, expr * arg2, expr_ref & result) { if (arg1 == arg2) { result = mk_numeral(1,1); return BR_DONE; } if (is_numeral(arg1) && is_numeral(arg2)) { SASSERT(arg1 != arg2); result = mk_numeral(0, 1); return BR_DONE; } result = m().mk_ite(m().mk_eq(arg1, arg2), mk_numeral(1, 1), mk_numeral(0, 1)); return BR_REWRITE2; } br_status bv_rewriter::mk_bv_add(unsigned num_args, expr * const * args, expr_ref & result) { br_status st = mk_add_core(num_args, args, result); if (st != BR_FAILED && st != BR_DONE) return st; #if 0 expr * x; expr * y; if (st == BR_FAILED && num_args == 2) { x = args[0]; y = args[1]; } else if (st == BR_DONE && is_add(result) && to_app(result)->get_num_args() == 2) { x = to_app(result)->get_arg(0); y = to_app(result)->get_arg(1); } else { return st; } if (!m_util.is_concat(x) && !is_numeral(x)) return st; if (!m_util.is_concat(y) && !is_numeral(y)) return st; unsigned sz = get_bv_size(x); for (unsigned i = 0; i < sz; i++) { if (!is_zero_bit(x,i) && !is_zero_bit(y,i)) return st; } result = m().mk_app(get_fid(), OP_BOR, x, y); return BR_REWRITE1; #else unsigned _num_args; expr * const * _args; if (st == BR_FAILED) { _num_args = num_args; _args = args; } else if (st == BR_DONE && is_add(result)) { _num_args = to_app(result)->get_num_args(); _args = to_app(result)->get_args(); } else { return st; } if (_num_args < 2) return st; unsigned sz = get_bv_size(_args[0]); for (unsigned i = 0; i < sz; i++) { bool found_non_zero = false; for (unsigned j = 0; j < _num_args; j++) { if (!is_zero_bit(_args[j], i)) { // at most one of the arguments may have a non-zero bit. if (found_non_zero) return st; found_non_zero = true; } } } result = m().mk_app(get_fid(), OP_BOR, _num_args, _args); return BR_REWRITE1; #endif } bool bv_rewriter::is_zero_bit(expr * x, unsigned idx) { numeral val; unsigned bv_size; loop: if (is_numeral(x, val, bv_size)) { if (val.is_zero()) return true; div(val, rational::power_of_two(idx), val); return (val % numeral(2)).is_zero(); } if (m_util.is_concat(x)) { unsigned i = to_app(x)->get_num_args(); while (i > 0) { --i; expr * y = to_app(x)->get_arg(i); bv_size = get_bv_size(y); if (bv_size <= idx) { idx -= bv_size; } else { x = y; goto loop; } } UNREACHABLE(); } return false; } br_status bv_rewriter::mk_bv_mul(unsigned num_args, expr * const * args, expr_ref & result) { br_status st = mk_mul_core(num_args, args, result); if (st != BR_FAILED && st != BR_DONE) return st; expr * x; expr * y; if (st == BR_FAILED && num_args == 2) { x = args[0]; y = args[1]; } else if (st == BR_DONE && is_mul(result) && to_app(result)->get_num_args() == 2) { x = to_app(result)->get_arg(0); y = to_app(result)->get_arg(1); } else { return st; } if (m_mul2concat) { numeral v; unsigned bv_size; unsigned shift; if (is_numeral(x, v, bv_size) && v.is_power_of_two(shift)) { SASSERT(shift >= 1); expr * args[2] = { m_mk_extract(bv_size-shift-1, 0, y), mk_numeral(0, shift) }; result = m_util.mk_concat(2, args); return BR_REWRITE2; } } return st; } br_status bv_rewriter::mk_bit2bool(expr * lhs, expr * rhs, expr_ref & result) { unsigned sz = get_bv_size(lhs); if (sz != 1) return BR_FAILED; if (is_numeral(lhs)) std::swap(lhs, rhs); numeral v; if (!is_numeral(rhs, v, sz)) return BR_FAILED; if (is_numeral(lhs)) { SASSERT(is_numeral(rhs)); result = lhs == rhs ? m().mk_true() : m().mk_false(); return BR_DONE; } if (m().is_ite(lhs)) { result = m().mk_ite(to_app(lhs)->get_arg(0), m().mk_eq(to_app(lhs)->get_arg(1), rhs), m().mk_eq(to_app(lhs)->get_arg(2), rhs)); return BR_REWRITE2; } if (m_util.is_bv_not(lhs)) { SASSERT(v.is_one() || v.is_zero()); result = m().mk_eq(to_app(lhs)->get_arg(0), mk_numeral(numeral(1) - v, 1)); return BR_REWRITE1; } bool is_one = v.is_one(); expr_ref bit1(m()); bit1 = is_one ? rhs : mk_numeral(numeral(1), 1); if (m_util.is_bv_or(lhs)) { ptr_buffer new_args; unsigned num = to_app(lhs)->get_num_args(); for (unsigned i = 0; i < num; i++) { new_args.push_back(m().mk_eq(to_app(lhs)->get_arg(i), bit1)); } result = m().mk_or(new_args.size(), new_args.c_ptr()); if (is_one) { return BR_REWRITE2; } else { result = m().mk_not(result); return BR_REWRITE3; } } if (m_util.is_bv_xor(lhs)) { ptr_buffer new_args; unsigned num = to_app(lhs)->get_num_args(); for (unsigned i = 0; i < num; i++) { new_args.push_back(m().mk_eq(to_app(lhs)->get_arg(i), bit1)); } // TODO: bool xor is not flat_assoc... must fix that. result = m().mk_xor(new_args.size(), new_args.c_ptr()); if (is_one) { return BR_REWRITE2; } else { result = m().mk_not(result); return BR_REWRITE3; } } return BR_FAILED; } br_status bv_rewriter::mk_blast_eq_value(expr * lhs, expr * rhs, expr_ref & result) { unsigned sz = get_bv_size(lhs); if (sz == 1) return BR_FAILED; TRACE("blast_eq_value", tout << "sz: " << sz << "\n" << mk_ismt2_pp(lhs, m()) << "\n";); if (is_numeral(lhs)) std::swap(lhs, rhs); numeral v; if (!is_numeral(rhs, v, sz)) return BR_FAILED; if (!m_util.is_bv_or(lhs) && !m_util.is_bv_xor(lhs) && !m_util.is_bv_not(lhs)) return BR_FAILED; numeral two(2); ptr_buffer new_args; for (unsigned i = 0; i < sz; i++) { bool bit0 = (v % two).is_zero(); new_args.push_back(m().mk_eq(m_mk_extract(i,i, lhs), mk_numeral(bit0 ? 0 : 1, 1))); div(v, two, v); } result = m().mk_and(new_args.size(), new_args.c_ptr()); return BR_REWRITE3; } br_status bv_rewriter::mk_eq_concat(expr * lhs, expr * rhs, expr_ref & result) { SASSERT(m_util.is_concat(lhs) || m_util.is_concat(rhs)); unsigned num1, num2; expr * const * args1, * const * args2; if (m_util.is_concat(lhs)) { num1 = to_app(lhs)->get_num_args(); args1 = to_app(lhs)->get_args(); } else { num1 = 1; args1 = &lhs; } if (m_util.is_concat(rhs)) { num2 = to_app(rhs)->get_num_args(); args2 = to_app(rhs)->get_args(); } else { num2 = 1; args2 = &rhs; } ptr_buffer new_eqs; unsigned low1 = 0; unsigned low2 = 0; unsigned i1 = num1; unsigned i2 = num2; while (i1 > 0 && i2 > 0) { expr * arg1 = args1[i1-1]; expr * arg2 = args2[i2-1]; unsigned sz1 = get_bv_size(arg1); unsigned sz2 = get_bv_size(arg2); SASSERT(low1 < sz1 && low2 < sz2); unsigned rsz1 = sz1 - low1; unsigned rsz2 = sz2 - low2; if (rsz1 == rsz2) { new_eqs.push_back(m().mk_eq(m_mk_extract(sz1 - 1, low1, arg1), m_mk_extract(sz2 - 1, low2, arg2))); low1 = 0; low2 = 0; --i1; --i2; continue; } else if (rsz1 < rsz2) { new_eqs.push_back(m().mk_eq(m_mk_extract(sz1 - 1, low1, arg1), m_mk_extract(rsz1 + low2 - 1, low2, arg2))); low1 = 0; low2 += rsz1; --i1; } else { new_eqs.push_back(m().mk_eq(m_mk_extract(rsz2 + low1 - 1, low1, arg1), m_mk_extract(sz2 - 1, low2, arg2))); low1 += rsz2; low2 = 0; --i2; } } SASSERT(i1 == 0 && i2 == 0); SASSERT(new_eqs.size() >= 1); result = m().mk_and(new_eqs.size(), new_eqs.c_ptr()); return BR_REWRITE3; } bool bv_rewriter::is_concat_split_target(expr * t) const { return m_split_concat_eq || m_util.is_concat(t) || m_util.is_numeral(t) || m_util.is_bv_or(t); } bool bv_rewriter::is_minus_one_times_t(expr * arg) { expr * t1, * t2; return (m_util.is_bv_mul(arg, t1, t2) && is_minus_one(t1)); } void bv_rewriter::mk_t1_add_t2_eq_c(expr * t1, expr * t2, expr * c, expr_ref & result) { SASSERT(is_numeral(c)); if (is_minus_one_times_t(t1)) result = m().mk_eq(t2, m_util.mk_bv_sub(c, t1)); else result = m().mk_eq(t1, m_util.mk_bv_sub(c, t2)); } br_status bv_rewriter::mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result) { expr * c, * x; numeral c_val, c_inv_val; unsigned sz; if (m_util.is_bv_mul(lhs, c, x) && m_util.is_numeral(c, c_val, sz) && m_util.mult_inverse(c_val, sz, c_inv_val)) { SASSERT(m_util.norm(c_val * c_inv_val, sz).is_one()); numeral rhs_val; // c * x = a if (m_util.is_numeral(rhs, rhs_val, sz)) { // x = c_inv * a result = m().mk_eq(x, m_util.mk_numeral(c_inv_val * rhs_val, sz)); return BR_REWRITE1; } expr * c2, * x2; numeral c2_val; // c * x = c2 * x2 if (m_util.is_bv_mul(rhs, c2, x2) && m_util.is_numeral(c2, c2_val, sz)) { // x = c_inv * c2 * x2 numeral new_c2 = m_util.norm(c_inv_val * c2_val, sz); if (new_c2.is_one()) result = m().mk_eq(x, x2); else result = m().mk_eq(x, m_util.mk_bv_mul(m_util.mk_numeral(c_inv_val * c2_val, sz), x2)); return BR_REWRITE1; } // c * x = t_1 + ... + t_n // and t_i's have non-unary coefficients (this condition is used to make sure we are actually reducing the number of multipliers). if (m_util.is_bv_add(rhs)) { // Potential problem: this simplification may increase the number of adders by reducing the amount of sharing. unsigned num = to_app(rhs)->get_num_args(); unsigned i; for (i = 0; i < num; i++) { expr * arg = to_app(rhs)->get_arg(i); expr * c2, * x2; if (m_util.is_numeral(arg)) continue; if (m_util.is_bv_mul(arg, c2, x2) && m_util.is_numeral(c2)) continue; break; } if (i == num) { result = m().mk_eq(x, m_util.mk_bv_mul(m_util.mk_numeral(c_inv_val, sz), rhs)); return BR_REWRITE2; } } } return BR_FAILED; } br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { if (lhs == rhs) { result = m().mk_true(); return BR_DONE; } if (is_numeral(lhs) && is_numeral(rhs)) { result = m().mk_false(); return BR_DONE; } bool swapped = false; if (is_numeral(lhs)) { swapped = true; std::swap(lhs, rhs); } br_status st; if (m_bit2bool) { st = mk_bit2bool(lhs, rhs, result); if (st != BR_FAILED) return st; } st = mk_mul_eq(lhs, rhs, result); if (st != BR_FAILED) { TRACE("mk_mul_eq", tout << mk_ismt2_pp(lhs, m()) << "\n=\n" << mk_ismt2_pp(rhs, m()) << "\n----->\n" << mk_ismt2_pp(result,m()) << "\n";); return st; } st = mk_mul_eq(rhs, lhs, result); if (st != BR_FAILED) { TRACE("mk_mul_eq", tout << mk_ismt2_pp(lhs, m()) << "\n=\n" << mk_ismt2_pp(rhs, m()) << "\n----->\n" << mk_ismt2_pp(result,m()) << "\n";); return st; } if (m_blast_eq_value) { st = mk_blast_eq_value(lhs, rhs, result); if (st != BR_FAILED) return st; } if (m_util.is_bv_add(lhs) || m_util.is_bv_mul(lhs) || m_util.is_bv_add(rhs) || m_util.is_bv_mul(rhs)) { expr_ref new_lhs(m()); expr_ref new_rhs(m()); st = cancel_monomials(lhs, rhs, false, new_lhs, new_rhs); if (st != BR_FAILED) { if (is_numeral(new_lhs) && is_numeral(new_rhs)) { result = new_lhs == new_rhs ? m().mk_true() : m().mk_false(); return BR_DONE; } } else { new_lhs = lhs; new_rhs = rhs; } // Try to rewrite t1 + t2 = c --> t1 = c - t2 // Reason: it is much cheaper to bit-blast. expr * t1, * t2; if (m_util.is_bv_add(new_lhs, t1, t2) && is_numeral(new_rhs)) { mk_t1_add_t2_eq_c(t1, t2, new_rhs, result); return BR_REWRITE2; } if (m_util.is_bv_add(new_rhs, t1, t2) && is_numeral(new_lhs)) { mk_t1_add_t2_eq_c(t1, t2, new_lhs, result); return BR_REWRITE2; } if (st != BR_FAILED) { result = m().mk_eq(new_lhs, new_rhs); return BR_DONE; } } if ((m_util.is_concat(lhs) && is_concat_split_target(rhs)) || (m_util.is_concat(rhs) && is_concat_split_target(lhs))) return mk_eq_concat(lhs, rhs, result); if (swapped) { result = m().mk_eq(lhs, rhs); return BR_DONE; } return BR_FAILED; } br_status bv_rewriter::mk_mkbv(unsigned num, expr * const * args, expr_ref & result) { if (m_mkbv2num) { unsigned i; for (i = 0; i < num; i++) if (!m().is_true(args[i]) && !m().is_false(args[i])) return BR_FAILED; numeral val; numeral two(2); i = num; while (i > 0) { --i; val *= two; if (m().is_true(args[i])) val++; } result = mk_numeral(val, num); return BR_DONE; } return BR_FAILED; } template class poly_rewriter; z3-4.4.0/src/ast/rewriter/pb_rewriter.h0000644000175000017500000000323312540347414017564 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: pb_rewriter.h Abstract: Basic rewriting rules for PB constraints. Author: Nikolaj Bjorner (nbjorner) 2013-14-12 Notes: --*/ #ifndef _PB_REWRITER_H_ #define _PB_REWRITER_H_ #include"pb_decl_plugin.h" #include"rewriter_types.h" #include"params.h" #include"lbool.h" template class pb_rewriter_util { PBU& m_util; void display(std::ostream& out, typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq); public: pb_rewriter_util(PBU& u) : m_util(u) {} void unique(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq); lbool normalize(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq); void prune(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq); }; /** \brief Cheap rewrite rules for PB constraints */ class pb_rewriter { pb_util m_util; vector m_coeffs; ptr_vector m_args; void validate_rewrite(func_decl* f, unsigned sz, expr*const* args, expr_ref& fml); public: pb_rewriter(ast_manager & m, params_ref const & p = params_ref()): m_util(m) { } ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } void updt_params(params_ref const & p) {} static void get_param_descrs(param_descrs & r) {} br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); expr_ref translate_pb2lia(obj_map& vars, expr* fml); expr_ref mk_validate_rewrite(app_ref& e1, app_ref& e2); void dump_pb_rewrite(expr* fml); }; #endif z3-4.4.0/src/ast/rewriter/bool_rewriter.h0000644000175000017500000001721212540347414020120 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bool_rewriter.h Abstract: Basic rewriting rules for Boolean operators. Author: Leonardo (leonardo) 2011-04-04 Notes: --*/ #ifndef _BOOL_REWRITER_H_ #define _BOOL_REWRITER_H_ #include"ast.h" #include"rewriter.h" #include"params.h" /** \brief Apply basic Boolean rewriting operations. Only depth 1 simplifications are performed. Note: there are no recursive calls. Note: arguments of AC operators are not sorted. Note: arguments of = and xor are also not sorted. Note: By default, (AND A B) is not rewritten as (NOT (OR (NOT A) (NOT B))) Note: AND OR operators are flattened only if mk_flat_app, mk_flat_or, mk_flat_and are used. The following operators are expanded: - => (implies) - xor - nand - nor - iff All methods run in time almost linear on the number of arguments. Actually, this is not true when flattening is enabled. A better approximation is O(Sum_{t \in args} size1(t)). Where size1(t) = max{t->get_num_args(), 1}. */ class bool_rewriter { ast_manager & m_manager; bool m_flat; bool m_local_ctx; bool m_elim_and; bool m_blast_distinct; unsigned m_blast_distinct_threshold; bool m_ite_extra_rules; unsigned m_local_ctx_limit; unsigned m_local_ctx_cost; br_status mk_flat_and_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_flat_or_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_nflat_and_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_nflat_or_core(unsigned num_args, expr * const * args, expr_ref & result); void mk_and_as_or(unsigned num_args, expr * const * args, expr_ref & result); expr * mk_or_app(unsigned num_args, expr * const * args); bool simp_nested_not_or(unsigned num_args, expr * const * args, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result); expr * simp_arg(expr * arg, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, bool & modified); void mk_nested_ite(expr * new_c, expr * new_t, expr * new_e, expr_ref & result); bool simp_nested_eq_ite(expr * t, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result); bool local_ctx_simp(unsigned num_args, expr * const * args, expr_ref & result); br_status try_ite_value(app * ite, app * val, expr_ref & result); public: bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_local_ctx_cost(0) { updt_params(p); } ast_manager & m() const { return m_manager; } family_id get_fid() const { return m().get_basic_family_id(); } bool is_eq(expr * t) const { return m().is_eq(t) || m().is_iff(t); } bool flat() const { return m_flat; } void set_flat(bool f) { m_flat = f; } bool elim_and() const { return m_elim_and; } void set_elim_and(bool f) { m_elim_and = f; } void reset_local_ctx_cost() { m_local_ctx_cost = 0; } void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); // The core methods return true if a rewrite-step/simplification was applied // to the arguments, and the result is stored in 'result'. Otherwise, they return false // and result.get == 0. br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_app_core(f, num_args, args, result) == BR_FAILED) result = m().mk_app(f, num_args, args); } br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result); br_status mk_distinct_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_iff_core(expr * lhs, expr * rhs, expr_ref & result) { return mk_eq_core(lhs, rhs, result); } br_status mk_and_core(unsigned num_args, expr * const * args, expr_ref & result) { if (m_elim_and) { mk_and_as_or(num_args, args, result); return BR_DONE; } else if (m_flat) { return mk_flat_and_core(num_args, args, result); } else { return mk_nflat_and_core(num_args, args, result); } } br_status mk_or_core(unsigned num_args, expr * const * args, expr_ref & result) { return m_flat ? mk_flat_or_core(num_args, args, result) : mk_nflat_or_core(num_args, args, result); } br_status mk_ite_core(expr * c, expr * t, expr * e, expr_ref & result); br_status mk_not_core(expr * t, expr_ref & result); void mk_eq(expr * lhs, expr * rhs, expr_ref & result) { if (mk_eq_core(lhs, rhs, result) == BR_FAILED) result = m().mk_eq(lhs, rhs); } void mk_iff(expr * lhs, expr * rhs, expr_ref & result) { mk_eq(lhs, rhs, result); } void mk_xor(expr * lhs, expr * rhs, expr_ref & result); void mk_and(unsigned num_args, expr * const * args, expr_ref & result) { if (mk_and_core(num_args, args, result) == BR_FAILED) { SASSERT(!m_elim_and); result = m().mk_and(num_args, args); } } void mk_or(unsigned num_args, expr * const * args, expr_ref & result) { if (mk_or_core(num_args, args, result) == BR_FAILED) result = m().mk_or(num_args, args); } void mk_and(expr * arg1, expr * arg2, expr_ref & result) { expr * args[2] = {arg1, arg2}; mk_and(2, args, result); } void mk_or(expr * arg1, expr * arg2, expr_ref & result) { expr * args[2] = {arg1, arg2}; mk_or(2, args, result); } void mk_and(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { expr * args[3] = {arg1, arg2, arg3}; mk_and(3, args, result); } void mk_or(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { expr * args[3] = {arg1, arg2, arg3}; mk_or(3, args, result); } void mk_implies(expr * lhs, expr * rhs, expr_ref & result); void mk_ite(expr * c, expr * t, expr * e, expr_ref & result) { if (mk_ite_core(c, t, e, result) == BR_FAILED) result = m().mk_ite(c, t, e); } void mk_distinct(unsigned num_args, expr * const * args, expr_ref & result) { if (mk_distinct_core(num_args, args, result) == BR_FAILED) result = m().mk_distinct(num_args, args); } void mk_not(expr * t, expr_ref & result) { if (mk_not_core(t, result) == BR_FAILED) result = m().mk_not(t); } void mk_nand(unsigned num_args, expr * const * args, expr_ref & result); void mk_nor(unsigned num_args, expr * const * args, expr_ref & result); void mk_nand(expr * arg1, expr * arg2, expr_ref & result); void mk_nor(expr * arg1, expr * arg2, expr_ref & result); }; struct bool_rewriter_cfg : public default_rewriter_cfg { bool_rewriter m_r; bool flat_assoc(func_decl * f) const { return m_r.flat() && (m_r.m().is_and(f) || m_r.m().is_or(f)); } bool rewrite_patterns() const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = 0; if (f->get_family_id() != m_r.get_fid()) return BR_FAILED; return m_r.mk_app_core(f, num, args, result); } bool_rewriter_cfg(ast_manager & m, params_ref const & p):m_r(m, p) {} }; class bool_rewriter_star : public rewriter_tpl { bool_rewriter_cfg m_cfg; public: bool_rewriter_star(ast_manager & m, params_ref const & p): rewriter_tpl(m, false, m_cfg), m_cfg(m, p) {} }; #endif z3-4.4.0/src/ast/rewriter/rewriter.cpp0000644000175000017500000002522012540347414017436 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: rewriter.cpp Abstract: Lean and mean rewriter Author: Leonardo (leonardo) 2011-03-31 Notes: --*/ #include"rewriter_def.h" #include"ast_ll_pp.h" #include"ast_smt2_pp.h" void rewriter_core::init_cache_stack() { SASSERT(m_cache_stack.empty()); m_cache = alloc(cache, m()); m_cache_stack.push_back(m_cache); if (m_proof_gen) { SASSERT(m_cache_pr_stack.empty()); m_cache_pr = alloc(cache, m()); m_cache_pr_stack.push_back(m_cache_pr); } } void rewriter_core::del_cache_stack() { std::for_each(m_cache_stack.begin(), m_cache_stack.end(), delete_proc()); m_cache_stack.finalize(); m_cache = 0; if (m_proof_gen) { std::for_each(m_cache_pr_stack.begin(), m_cache_pr_stack.end(), delete_proc()); m_cache_pr_stack.finalize(); m_cache_pr = 0; } } void rewriter_core::cache_result(expr * k, expr * v) { #if 0 // trace for tracking cache usage verbose_stream() << "1 " << k->get_id() << std::endl; #endif SASSERT(!m_proof_gen); TRACE("rewriter_cache_result", tout << mk_ismt2_pp(k, m()) << "\n--->\n" << mk_ismt2_pp(v, m()) << "\n";); m_cache->insert(k, v); #if 0 static unsigned num_cached = 0; num_cached ++; if (num_cached % 100000 == 0) verbose_stream() << "[rewriter] :num-cached " << num_cached << " :capacity " << m_cache->capacity() << " :size " << m_cache->size() << " :frame-stack-size " << m_frame_stack.size() << std::endl; #endif } void rewriter_core::cache_result(expr * k, expr * v, proof * pr) { m_cache->insert(k, v); SASSERT(m_proof_gen); m_cache_pr->insert(k, pr); } unsigned rewriter_core::get_cache_size() const { return m_cache->size(); } void rewriter_core::reset_cache() { m_cache = m_cache_stack[0]; m_cache->reset(); if (m_proof_gen) { m_cache_pr = m_cache_pr_stack[0]; m_cache_pr->reset(); } } // free memory allocated by the rewriter void rewriter_core::free_memory() { del_cache_stack(); m_frame_stack.finalize(); m_result_stack.finalize(); m_scopes.finalize(); } void rewriter_core::begin_scope() { m_scopes.push_back(scope(m_root, m_num_qvars)); unsigned lvl = m_scopes.size(); SASSERT(lvl <= m_cache_stack.size()); SASSERT(!m_proof_gen || m_cache_pr_stack.size() == m_cache_stack.size()); if (lvl == m_cache_stack.size()) { m_cache_stack.push_back(alloc(cache, m())); if (m_proof_gen) m_cache_pr_stack.push_back(alloc(cache, m())); } m_cache = m_cache_stack[lvl]; m_cache->reset(); SASSERT(m_cache->empty()); if (m_proof_gen) { m_cache_pr = m_cache_pr_stack[lvl]; m_cache_pr->reset(); SASSERT(m_cache_pr->empty()); } } void rewriter_core::end_scope() { m_cache->reset(); if (m_proof_gen) m_cache_pr->reset(); scope & s = m_scopes.back(); m_root = s.m_old_root; m_num_qvars = s.m_old_num_qvars; m_scopes.pop_back(); unsigned new_lvl = m_scopes.size(); m_cache = m_cache_stack[new_lvl]; if (m_proof_gen) m_cache_pr = m_cache_pr_stack[new_lvl]; } bool rewriter_core::is_child_of_top_frame(expr * t) const { if (m_frame_stack.empty()) return true; frame const & fr = m_frame_stack.back(); expr * parent = fr.m_curr; unsigned num; switch (parent->get_kind()) { case AST_APP: num = to_app(parent)->get_num_args(); for (unsigned i = 0; i < num; i++) { if (to_app(parent)->get_arg(i) == t) return true; } return false; case AST_QUANTIFIER: num = to_quantifier(parent)->get_num_children(); for (unsigned i = 0; i < num; i++) { if (to_quantifier(parent)->get_child(i) == t) return true; } return false; default: return false; } } /** \brief Eliminate (implicit) reflexivity proofs from m_result_pr_stack starting at position spos. The implicit reflexivity proof is 0. */ void rewriter_core::elim_reflex_prs(unsigned spos) { SASSERT(m_proof_gen); unsigned sz = m_result_pr_stack.size(); SASSERT(spos <= sz); unsigned j = spos; for (unsigned i = spos; i < sz; i++) { proof * pr = m_result_pr_stack.get(i); if (pr != 0) { if (i != j) m_result_pr_stack.set(j, pr); j++; } } m_result_pr_stack.shrink(j); } rewriter_core::rewriter_core(ast_manager & m, bool proof_gen): m_manager(m), m_proof_gen(proof_gen), m_result_stack(m), m_result_pr_stack(m), m_num_qvars(0) { init_cache_stack(); } rewriter_core::~rewriter_core() { del_cache_stack(); } // reset rewriter (macro definitions are not erased) void rewriter_core::reset() { SASSERT(!m_cache_stack.empty()); reset_cache(); m_frame_stack.reset(); m_result_stack.reset(); if (m_proof_gen) m_result_pr_stack.reset(); m_root = 0; m_num_qvars = 0; m_scopes.reset(); } // free memory & reset (macro definitions are not erased) void rewriter_core::cleanup() { free_memory(); init_cache_stack(); m_root = 0; m_num_qvars = 0; } #ifdef _TRACE void rewriter_core::display_stack(std::ostream & out, unsigned pp_depth) { svector::iterator it = m_frame_stack.begin(); svector::iterator end = m_frame_stack.end(); for (; it != end; ++it) { out << mk_bounded_pp(it->m_curr, m(), pp_depth) << "\n"; out << "state: " << it->m_state << "\n"; out << "cache: " << it->m_cache_result << ", new_child: " << it->m_new_child << ", max-depth: " << it->m_max_depth << ", i: " << it->m_i << "\n"; out << "------------------\n"; } } #endif bool var_shifter_core::visit(expr * t) { if (is_ground(t)) { m_result_stack.push_back(t); return true; } bool c = must_cache(t); if (c) { expr * r = get_cached(t); if (r) { m_result_stack.push_back(r); set_new_child_flag(t, r); return true; } } switch (t->get_kind()) { case AST_APP: SASSERT(to_app(t)->get_num_args() > 0); push_frame(t, c); return false; case AST_VAR: process_var(to_var(t)); return true; case AST_QUANTIFIER: push_frame(t, c); return false; default: UNREACHABLE(); return true; } } void var_shifter_core::process_app(app * t, frame & fr) { unsigned num_args = t->get_num_args(); while (fr.m_i < num_args) { expr * arg = t->get_arg(fr.m_i); fr.m_i++; if (!visit(arg)) return; } SASSERT(fr.m_spos + num_args == m_result_stack.size()); expr * new_t; if (fr.m_new_child) { expr * const * new_args = m_result_stack.c_ptr() + fr.m_spos; new_t = m().mk_app(t->get_decl(), num_args, new_args); } else { new_t = t; } m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(new_t); m_frame_stack.pop_back(); set_new_child_flag(t, new_t); if (fr.m_cache_result) cache_result(t, new_t); } void var_shifter_core::process_quantifier(quantifier * q, frame & fr) { if (fr.m_i == 0) { begin_scope(); m_num_qvars += q->get_num_decls(); m_root = q->get_expr(); } unsigned num_children = q->get_num_children(); while (fr.m_i < num_children) { expr * child = q->get_child(fr.m_i); fr.m_i++; if (!visit(child)) return; } SASSERT(fr.m_spos + num_children == m_result_stack.size()); expr * new_q; if (fr.m_new_child) { expr * const * it = m_result_stack.c_ptr() + fr.m_spos; expr * new_expr = *it; ++it; expr * const * new_pats = it; expr * const * new_no_pats = new_pats + q->get_num_patterns(); new_q = m().update_quantifier(q, q->get_num_patterns(), new_pats, q->get_num_no_patterns(), new_no_pats, new_expr); } else { new_q = q; } m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(new_q); m_frame_stack.pop_back(); set_new_child_flag(q, new_q); end_scope(); if (fr.m_cache_result) cache_result(q, new_q); } void var_shifter_core::main_loop(expr * t, expr_ref & r) { SASSERT(m_cache == m_cache_stack[0]); SASSERT(m_frame_stack.empty()); SASSERT(m_result_stack.empty()); m_root = t; if (visit(t)) { r = m_result_stack.back(); m_result_stack.pop_back(); SASSERT(m_result_stack.empty()); return; } SASSERT(!m_frame_stack.empty()); while (!m_frame_stack.empty()) { frame & fr = m_frame_stack.back(); expr * t = fr.m_curr; if (fr.m_i == 0 && fr.m_cache_result) { expr * r = get_cached(t); if (r) { m_result_stack.push_back(r); m_frame_stack.pop_back(); set_new_child_flag(t, r); continue; } } switch (t->get_kind()) { case AST_APP: process_app(to_app(t), fr); break; case AST_QUANTIFIER: process_quantifier(to_quantifier(t), fr); break; default: UNREACHABLE(); } } r = m_result_stack.back(); m_result_stack.pop_back(); SASSERT(m_result_stack.empty()); } void var_shifter::operator()(expr * t, unsigned bound, unsigned shift1, unsigned shift2, expr_ref & r) { if (is_ground(t)) { r = t; return; } reset_cache(); m_bound = bound; m_shift1 = shift1; m_shift2 = shift2; main_loop(t, r); } void var_shifter::process_var(var * v) { unsigned vidx = v->get_idx(); if (vidx < m_num_qvars) { m_result_stack.push_back(v); } else { unsigned nvidx = vidx - m_num_qvars; if (nvidx >= m_bound) vidx += m_shift1; else vidx += m_shift2; m_result_stack.push_back(m().mk_var(vidx, v->get_sort())); set_new_child_flag(v); } } void inv_var_shifter::operator()(expr * t, unsigned shift, expr_ref & r) { if (is_ground(t)) { r = t; return; } reset_cache(); m_shift = shift; main_loop(t, r); } void inv_var_shifter::process_var(var * v) { unsigned vidx = v->get_idx(); if (vidx < m_num_qvars) { m_result_stack.push_back(v); } else { SASSERT(vidx >= m_num_qvars + m_shift); vidx -= m_shift; m_result_stack.push_back(m().mk_var(vidx, v->get_sort())); set_new_child_flag(v); } } template class rewriter_tpl; z3-4.4.0/src/ast/rewriter/fpa_rewriter.cpp0000644000175000017500000006563012540347414020275 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa_rewriter.cpp Abstract: Basic rewriting rules for floating point numbers. Author: Leonardo (leonardo) 2012-02-02 Notes: --*/ #include"fpa_rewriter.h" #include"fpa_rewriter_params.hpp" fpa_rewriter::fpa_rewriter(ast_manager & m, params_ref const & p) : m_util(m), m_fm(m_util.fm()), m_hi_fp_unspecified(true) { updt_params(p); } fpa_rewriter::~fpa_rewriter() { } void fpa_rewriter::updt_params(params_ref const & _p) { fpa_rewriter_params p(_p); m_hi_fp_unspecified = p.hi_fp_unspecified(); } void fpa_rewriter::get_param_descrs(param_descrs & r) { } br_status fpa_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { br_status st = BR_FAILED; SASSERT(f->get_family_id() == get_fid()); fpa_op_kind k = (fpa_op_kind)f->get_decl_kind(); switch (k) { case OP_FPA_RM_NEAREST_TIES_TO_EVEN: case OP_FPA_RM_NEAREST_TIES_TO_AWAY: case OP_FPA_RM_TOWARD_POSITIVE: case OP_FPA_RM_TOWARD_NEGATIVE: case OP_FPA_RM_TOWARD_ZERO: SASSERT(num_args == 0); result = m().mk_app(f, (expr * const *)0); st = BR_DONE; break; case OP_FPA_PLUS_INF: case OP_FPA_MINUS_INF: case OP_FPA_NAN: case OP_FPA_PLUS_ZERO: case OP_FPA_MINUS_ZERO: SASSERT(num_args == 0); result = m().mk_app(f, (expr * const *)0); st = BR_DONE; break; case OP_FPA_NUM: SASSERT(num_args == 0); result = m().mk_app(f, (expr * const *)0); st = BR_DONE; break; case OP_FPA_ADD: SASSERT(num_args == 3); st = mk_add(args[0], args[1], args[2], result); break; case OP_FPA_SUB: SASSERT(num_args == 3); st = mk_sub(args[0], args[1], args[2], result); break; case OP_FPA_NEG: SASSERT(num_args == 1); st = mk_neg(args[0], result); break; case OP_FPA_MUL: SASSERT(num_args == 3); st = mk_mul(args[0], args[1], args[2], result); break; case OP_FPA_DIV: SASSERT(num_args == 3); st = mk_div(args[0], args[1], args[2], result); break; case OP_FPA_REM: SASSERT(num_args == 2); st = mk_rem(args[0], args[1], result); break; case OP_FPA_ABS: SASSERT(num_args == 1); st = mk_abs(args[0], result); break; case OP_FPA_MIN: SASSERT(num_args == 2); st = mk_min(args[0], args[1], result); break; case OP_FPA_MAX: SASSERT(num_args == 2); st = mk_max(args[0], args[1], result); break; case OP_FPA_FMA: SASSERT(num_args == 4); st = mk_fma(args[0], args[1], args[2], args[3], result); break; case OP_FPA_SQRT: SASSERT(num_args == 2); st = mk_sqrt(args[0], args[1], result); break; case OP_FPA_ROUND_TO_INTEGRAL: SASSERT(num_args == 2); st = mk_round_to_integral(args[0], args[1], result); break; case OP_FPA_EQ: SASSERT(num_args == 2); st = mk_float_eq(args[0], args[1], result); break; case OP_FPA_LT: SASSERT(num_args == 2); st = mk_lt(args[0], args[1], result); break; case OP_FPA_GT: SASSERT(num_args == 2); st = mk_gt(args[0], args[1], result); break; case OP_FPA_LE: SASSERT(num_args == 2); st = mk_le(args[0], args[1], result); break; case OP_FPA_GE: SASSERT(num_args == 2); st = mk_ge(args[0], args[1], result); break; case OP_FPA_IS_ZERO: SASSERT(num_args == 1); st = mk_is_zero(args[0], result); break; case OP_FPA_IS_NAN: SASSERT(num_args == 1); st = mk_is_nan(args[0], result); break; case OP_FPA_IS_INF: SASSERT(num_args == 1); st = mk_is_inf(args[0], result); break; case OP_FPA_IS_NORMAL: SASSERT(num_args == 1); st = mk_is_normal(args[0], result); break; case OP_FPA_IS_SUBNORMAL: SASSERT(num_args == 1); st = mk_is_subnormal(args[0], result); break; case OP_FPA_IS_NEGATIVE: SASSERT(num_args == 1); st = mk_is_negative(args[0], result); break; case OP_FPA_IS_POSITIVE: SASSERT(num_args == 1); st = mk_is_positive(args[0], result); break; case OP_FPA_FP: SASSERT(num_args == 3); st = mk_fp(args[0], args[1], args[2], result); break; case OP_FPA_TO_FP: st = mk_to_fp(f, num_args, args, result); break; case OP_FPA_TO_FP_UNSIGNED: SASSERT(num_args == 2); st = mk_to_fp_unsigned(f, args[0], args[1], result); break; case OP_FPA_TO_UBV: SASSERT(num_args == 2); st = mk_to_ubv(f, args[0], args[1], result); break; case OP_FPA_TO_SBV: SASSERT(num_args == 2); st = mk_to_sbv(f, args[0], args[1], result); break; case OP_FPA_TO_REAL: SASSERT(num_args == 1); st = mk_to_real(args[0], result); break; case OP_FPA_TO_IEEE_BV: SASSERT(num_args == 1); st = mk_to_ieee_bv(args[0], result); break; case OP_FPA_INTERNAL_TO_UBV_UNSPECIFIED: SASSERT(num_args == 0); st = mk_to_ubv_unspecified(f, result); break; case OP_FPA_INTERNAL_TO_SBV_UNSPECIFIED: SASSERT(num_args == 0); st = mk_to_sbv_unspecified(f, result); break; case OP_FPA_INTERNAL_TO_REAL_UNSPECIFIED: SASSERT(num_args == 0); st = mk_to_real_unspecified(result); break; case OP_FPA_INTERNAL_BVWRAP: case OP_FPA_INTERNAL_BVUNWRAP: st = BR_FAILED; break; default: NOT_IMPLEMENTED_YET(); } return st; } br_status fpa_rewriter::mk_to_ubv_unspecified(func_decl * f, expr_ref & result) { SASSERT(f->get_num_parameters() == 1); SASSERT(f->get_parameter(0).is_int()); unsigned bv_sz = f->get_parameter(0).get_int(); bv_util bu(m()); if (m_hi_fp_unspecified) // The "hardware interpretation" is 0. result = bu.mk_numeral(0, bv_sz); else result = m_util.mk_internal_to_real_unspecified(); return BR_DONE; } br_status fpa_rewriter::mk_to_sbv_unspecified(func_decl * f, expr_ref & result) { SASSERT(f->get_num_parameters() == 1); SASSERT(f->get_parameter(0).is_int()); unsigned bv_sz = f->get_parameter(0).get_int(); bv_util bu(m()); if (m_hi_fp_unspecified) // The "hardware interpretation" is 0. result = bu.mk_numeral(0, bv_sz); else result = m_util.mk_internal_to_real_unspecified(); return BR_DONE; } br_status fpa_rewriter::mk_to_real_unspecified(expr_ref & result) { if (m_hi_fp_unspecified) // The "hardware interpretation" is 0. result = m_util.au().mk_numeral(rational(0), false); else result = m_util.mk_internal_to_real_unspecified(); return BR_DONE; } br_status fpa_rewriter::mk_to_fp(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(f->get_num_parameters() == 2); SASSERT(f->get_parameter(0).is_int()); SASSERT(f->get_parameter(1).is_int()); bv_util bu(m()); scoped_mpf v(m_fm); mpf_rounding_mode rmv; rational r1, r2, r3; unsigned bvs1, bvs2, bvs3; unsigned ebits = f->get_parameter(0).get_int(); unsigned sbits = f->get_parameter(1).get_int(); if (num_args == 1) { if (bu.is_numeral(args[0], r1, bvs1)) { // BV -> float SASSERT(bvs1 == sbits + ebits); unsynch_mpz_manager & mpzm = m_fm.mpz_manager(); unsynch_mpq_manager & mpqm = m_fm.mpq_manager(); scoped_mpz sig(mpzm), exp(mpzm); const mpz & sm1 = m_fm.m_powers2(sbits - 1); const mpz & em1 = m_fm.m_powers2(ebits); scoped_mpq q(mpqm); mpqm.set(q, r1.to_mpq()); SASSERT(mpzm.is_one(q.get().denominator())); scoped_mpz z(mpzm); z = q.get().numerator(); mpzm.rem(z, sm1, sig); mpzm.div(z, sm1, z); mpzm.rem(z, em1, exp); mpzm.div(z, em1, z); SASSERT(mpzm.is_int64(exp)); mpf_exp_t mpf_exp = mpzm.get_int64(exp); mpf_exp = m_fm.unbias_exp(ebits, mpf_exp); m_fm.set(v, ebits, sbits, !mpzm.is_zero(z), sig, mpf_exp); TRACE("fp_rewriter", tout << "sgn: " << !mpzm.is_zero(z) << std::endl; tout << "sig: " << mpzm.to_string(sig) << std::endl; tout << "exp: " << mpf_exp << std::endl; tout << "v: " << m_fm.to_string(v) << std::endl;); result = m_util.mk_value(v); return BR_DONE; } } else if (num_args == 2) { if (!m_util.is_rm_numeral(args[0], rmv)) return BR_FAILED; if (m_util.au().is_numeral(args[1], r1)) { // rm + real -> float TRACE("fp_rewriter", tout << "r: " << r1 << std::endl;); scoped_mpf v(m_fm); m_fm.set(v, ebits, sbits, rmv, r1.to_mpq()); result = m_util.mk_value(v); // TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); return BR_DONE; } else if (m_util.is_numeral(args[1], v)) { // rm + float -> float TRACE("fp_rewriter", tout << "v: " << m_fm.to_string(v) << std::endl; ); scoped_mpf vf(m_fm); m_fm.set(vf, ebits, sbits, rmv, v); result = m_util.mk_value(vf); // TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); return BR_DONE; } else if (bu.is_numeral(args[1], r1, bvs1)) { // rm + signed bv -> float TRACE("fp_rewriter", tout << "r1: " << r1 << std::endl;); r1 = bu.norm(r1, bvs1, true); TRACE("fp_rewriter", tout << "r1 norm: " << r1 << std::endl;); m_fm.set(v, ebits, sbits, rmv, r1.to_mpq()); result = m_util.mk_value(v); return BR_DONE; } } else if (num_args == 3) { if (m_util.is_rm_numeral(args[0], rmv) && m_util.au().is_real(args[1]) && m_util.au().is_int(args[2])) { // rm + real + int -> float if (!m_util.is_rm_numeral(args[0], rmv) || !m_util.au().is_numeral(args[1], r1) || !m_util.au().is_numeral(args[2], r2)) return BR_FAILED; TRACE("fp_rewriter", tout << "r1: " << r1 << ", r2: " << r2 << "\n";); m_fm.set(v, ebits, sbits, rmv, r1.to_mpq(), r2.to_mpq().numerator()); result = m_util.mk_value(v); return BR_DONE; } else if (bu.is_numeral(args[0], r1, bvs1) && bu.is_numeral(args[1], r2, bvs2) && bu.is_numeral(args[2], r3, bvs3)) { // 3 BV -> float SASSERT(m_fm.mpz_manager().is_one(r2.to_mpq().denominator())); SASSERT(m_fm.mpz_manager().is_one(r3.to_mpq().denominator())); SASSERT(m_fm.mpz_manager().is_int64(r3.to_mpq().numerator())); mpf_exp_t biased_exp = m_fm.mpz_manager().get_int64(r2.to_mpq().numerator()); m_fm.set(v, bvs2, bvs3 + 1, r1.is_one(), r3.to_mpq().numerator(), m_fm.unbias_exp(bvs2, biased_exp)); TRACE("fp_rewriter", tout << "v = " << m_fm.to_string(v) << std::endl;); result = m_util.mk_value(v); return BR_DONE; } } return BR_FAILED; } br_status fpa_rewriter::mk_to_fp_unsigned(func_decl * f, expr * arg1, expr * arg2, expr_ref & result) { SASSERT(f->get_num_parameters() == 2); SASSERT(f->get_parameter(0).is_int()); SASSERT(f->get_parameter(1).is_int()); bv_util bu(m()); unsigned ebits = f->get_parameter(0).get_int(); unsigned sbits = f->get_parameter(1).get_int(); mpf_rounding_mode rmv; rational r; unsigned bvs; if (m_util.is_rm_numeral(arg1, rmv) && bu.is_numeral(arg2, r, bvs)) { scoped_mpf v(m_fm); m_fm.set(v, ebits, sbits, rmv, r.to_mpq()); result = m_util.mk_value(v); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_add(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { mpf_rounding_mode rm; if (m_util.is_rm_numeral(arg1, rm)) { scoped_mpf v2(m_fm), v3(m_fm); if (m_util.is_numeral(arg2, v2) && m_util.is_numeral(arg3, v3)) { scoped_mpf t(m_fm); m_fm.add(rm, v2, v3, t); result = m_util.mk_value(t); return BR_DONE; } } return BR_FAILED; } br_status fpa_rewriter::mk_sub(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { // a - b = a + (-b) result = m_util.mk_add(arg1, arg2, m_util.mk_neg(arg3)); return BR_REWRITE2; } br_status fpa_rewriter::mk_mul(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { mpf_rounding_mode rm; if (m_util.is_rm_numeral(arg1, rm)) { scoped_mpf v2(m_fm), v3(m_fm); if (m_util.is_numeral(arg2, v2) && m_util.is_numeral(arg3, v3)) { scoped_mpf t(m_fm); m_fm.mul(rm, v2, v3, t); result = m_util.mk_value(t); return BR_DONE; } } return BR_FAILED; } br_status fpa_rewriter::mk_div(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { mpf_rounding_mode rm; if (m_util.is_rm_numeral(arg1, rm)) { scoped_mpf v2(m_fm), v3(m_fm); if (m_util.is_numeral(arg2, v2) && m_util.is_numeral(arg3, v3)) { scoped_mpf t(m_fm); m_fm.div(rm, v2, v3, t); result = m_util.mk_value(t); return BR_DONE; } } return BR_FAILED; } br_status fpa_rewriter::mk_neg(expr * arg1, expr_ref & result) { if (m_util.is_nan(arg1)) { // -nan --> nan result = arg1; return BR_DONE; } if (m_util.is_pinf(arg1)) { // - +oo --> -oo result = m_util.mk_ninf(m().get_sort(arg1)); return BR_DONE; } if (m_util.is_ninf(arg1)) { // - -oo -> +oo result = m_util.mk_pinf(m().get_sort(arg1)); return BR_DONE; } if (m_util.is_neg(arg1)) { // - - a --> a result = to_app(arg1)->get_arg(0); return BR_DONE; } scoped_mpf v1(m_fm); if (m_util.is_numeral(arg1, v1)) { m_fm.neg(v1); result = m_util.mk_value(v1); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_rem(expr * arg1, expr * arg2, expr_ref & result) { scoped_mpf v1(m_fm), v2(m_fm); if (m_util.is_numeral(arg1, v1) && m_util.is_numeral(arg2, v2)) { scoped_mpf t(m_fm); m_fm.rem(v1, v2, t); result = m_util.mk_value(t); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_abs(expr * arg1, expr_ref & result) { if (m_util.is_nan(arg1)) { result = arg1; return BR_DONE; } scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { if (m_fm.is_neg(v)) m_fm.neg(v); result = m_util.mk_value(v); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_min(expr * arg1, expr * arg2, expr_ref & result) { if (m_util.is_nan(arg1)) { result = arg2; return BR_DONE; } if (m_util.is_nan(arg2)) { result = arg1; return BR_DONE; } if (m_util.is_zero(arg1) && m_util.is_zero(arg2)) { result = arg2; return BR_DONE; } result = m().mk_ite(mk_eq_nan(arg1), arg2, m().mk_ite(mk_eq_nan(arg2), arg1, // min(-0.0, +0.0) = min(+0.0, -0.0) = +0.0 m().mk_ite(m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2), m().mk_not(m().mk_eq(m_util.mk_is_positive(arg1), m_util.mk_is_positive(arg2)))), m_util.mk_pzero(m().get_sort(arg1)), m().mk_ite(m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2)), arg2, m().mk_ite(m_util.mk_lt(arg1, arg2), arg1, arg2))))); return BR_REWRITE_FULL; } br_status fpa_rewriter::mk_max(expr * arg1, expr * arg2, expr_ref & result) { if (m_util.is_nan(arg1)) { result = arg2; return BR_DONE; } if (m_util.is_nan(arg2)) { result = arg1; return BR_DONE; } if (m_util.is_zero(arg1) && m_util.is_zero(arg2)) { result = arg2; return BR_DONE; } result = m().mk_ite(mk_eq_nan(arg1), arg2, m().mk_ite(mk_eq_nan(arg2), arg1, // max(-0.0, +0.0) = max(+0.0, -0.0) = +0.0 m().mk_ite(m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2), m().mk_not(m().mk_eq(m_util.mk_is_positive(arg1), m_util.mk_is_positive(arg2)))), m_util.mk_pzero(m().get_sort(arg1)), m().mk_ite(m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2)), arg2, m().mk_ite(m_util.mk_gt(arg1, arg2), arg1, arg2))))); return BR_REWRITE_FULL; } br_status fpa_rewriter::mk_fma(expr * arg1, expr * arg2, expr * arg3, expr * arg4, expr_ref & result) { mpf_rounding_mode rm; if (m_util.is_rm_numeral(arg1, rm)) { scoped_mpf v2(m_fm), v3(m_fm), v4(m_fm); if (m_util.is_numeral(arg2, v2) && m_util.is_numeral(arg3, v3) && m_util.is_numeral(arg4, v4)) { scoped_mpf t(m_fm); m_fm.fused_mul_add(rm, v2, v3, v4, t); result = m_util.mk_value(t); return BR_DONE; } } return BR_FAILED; } br_status fpa_rewriter::mk_sqrt(expr * arg1, expr * arg2, expr_ref & result) { mpf_rounding_mode rm; if (m_util.is_rm_numeral(arg1, rm)) { scoped_mpf v2(m_fm); if (m_util.is_numeral(arg2, v2)) { scoped_mpf t(m_fm); m_fm.sqrt(rm, v2, t); result = m_util.mk_value(t); return BR_DONE; } } return BR_FAILED; } br_status fpa_rewriter::mk_round_to_integral(expr * arg1, expr * arg2, expr_ref & result) { mpf_rounding_mode rm; if (m_util.is_rm_numeral(arg1, rm)) { scoped_mpf v2(m_fm); if (m_util.is_numeral(arg2, v2)) { scoped_mpf t(m_fm); m_fm.round_to_integral(rm, v2, t); result = m_util.mk_value(t); return BR_DONE; } } return BR_FAILED; } // This the floating point theory == br_status fpa_rewriter::mk_float_eq(expr * arg1, expr * arg2, expr_ref & result) { scoped_mpf v1(m_fm), v2(m_fm); if (m_util.is_numeral(arg1, v1) && m_util.is_numeral(arg2, v2)) { result = (m_fm.eq(v1, v2)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } // Return (= arg NaN) app * fpa_rewriter::mk_eq_nan(expr * arg) { return m().mk_eq(arg, m_util.mk_nan(m().get_sort(arg))); } // Return (not (= arg NaN)) app * fpa_rewriter::mk_neq_nan(expr * arg) { return m().mk_not(mk_eq_nan(arg)); } br_status fpa_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) { if (m_util.is_nan(arg1) || m_util.is_nan(arg2)) { result = m().mk_false(); return BR_DONE; } if (m_util.is_ninf(arg1)) { // -oo < arg2 --> not(arg2 = -oo) and not(arg2 = NaN) result = m().mk_and(m().mk_not(m().mk_eq(arg2, arg1)), mk_neq_nan(arg2)); return BR_REWRITE3; } if (m_util.is_ninf(arg2)) { // arg1 < -oo --> false result = m().mk_false(); return BR_DONE; } if (m_util.is_pinf(arg1)) { // +oo < arg2 --> false result = m().mk_false(); return BR_DONE; } if (m_util.is_pinf(arg2)) { // arg1 < +oo --> not(arg1 = +oo) and not(arg1 = NaN) result = m().mk_and(m().mk_not(m().mk_eq(arg1, arg2)), mk_neq_nan(arg1)); return BR_REWRITE3; } scoped_mpf v1(m_fm), v2(m_fm); if (m_util.is_numeral(arg1, v1) && m_util.is_numeral(arg2, v2)) { result = (m_fm.lt(v1, v2)) ? m().mk_true() : m().mk_false(); return BR_DONE; } // TODO: more simplifications return BR_FAILED; } br_status fpa_rewriter::mk_gt(expr * arg1, expr * arg2, expr_ref & result) { result = m_util.mk_lt(arg2, arg1); return BR_REWRITE1; } br_status fpa_rewriter::mk_le(expr * arg1, expr * arg2, expr_ref & result) { if (m_util.is_nan(arg1) || m_util.is_nan(arg2)) { result = m().mk_false(); return BR_DONE; } scoped_mpf v1(m_fm), v2(m_fm); if (m_util.is_numeral(arg1, v1) && m_util.is_numeral(arg2, v2)) { result = (m_fm.le(v1, v2)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_ge(expr * arg1, expr * arg2, expr_ref & result) { result = m_util.mk_le(arg2, arg1); return BR_REWRITE1; } br_status fpa_rewriter::mk_is_zero(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_zero(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_is_nzero(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_nzero(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_is_pzero(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_pzero(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_is_nan(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_nan(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_is_inf(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_inf(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_is_normal(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_normal(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_is_subnormal(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_denormal(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_is_negative(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_neg(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_is_positive(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_neg(v) || m_fm.is_nan(v)) ? m().mk_false() : m().mk_true(); return BR_DONE; } return BR_FAILED; } // This the SMT = br_status fpa_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) { scoped_mpf v1(m_fm), v2(m_fm); if (m_util.is_numeral(arg1, v1) && m_util.is_numeral(arg2, v2)) { // Note: == is the floats-equality, here we need normal equality. result = (m_fm.is_nan(v1) && m_fm.is_nan(v2)) ? m().mk_true() : (m_fm.is_zero(v1) && m_fm.is_zero(v2) && m_fm.sgn(v1)!=m_fm.sgn(v2)) ? m().mk_false() : (v1 == v2) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_to_ieee_bv(expr * arg1, expr_ref & result) { return BR_FAILED; } br_status fpa_rewriter::mk_fp(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { unsynch_mpz_manager & mpzm = m_fm.mpz_manager(); bv_util bu(m()); rational r1, r2, r3; unsigned bvs1, bvs2, bvs3; if (bu.is_numeral(arg1, r1, bvs1) && bu.is_numeral(arg2, r2, bvs2) && bu.is_numeral(arg3, r3, bvs3)) { SASSERT(mpzm.is_one(r2.to_mpq().denominator())); SASSERT(mpzm.is_one(r3.to_mpq().denominator())); SASSERT(mpzm.is_int64(r3.to_mpq().numerator())); scoped_mpf v(m_fm); mpf_exp_t biased_exp = mpzm.get_int64(r2.to_mpq().numerator()); m_fm.set(v, bvs2, bvs3 + 1, r1.is_one(), r3.to_mpq().numerator(), m_fm.unbias_exp(bvs2, biased_exp)); TRACE("fp_rewriter", tout << "simplified (fp ...) to " << m_fm.to_string(v) << std::endl;); result = m_util.mk_value(v); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_to_ubv(func_decl * f, expr * arg1, expr * arg2, expr_ref & result) { SASSERT(f->get_num_parameters() == 1); SASSERT(f->get_parameter(0).is_int()); int bv_sz = f->get_parameter(0).get_int(); mpf_rounding_mode rmv; scoped_mpf v(m_fm); if (m_util.is_rm_numeral(arg1, rmv) && m_util.is_numeral(arg2, v)) { if (m_fm.is_nan(v) || m_fm.is_inf(v) || m_fm.is_neg(v)) { result = m_util.mk_internal_to_ubv_unspecified(bv_sz); return BR_REWRITE_FULL; } bv_util bu(m()); scoped_mpq q(m_fm.mpq_manager()); m_fm.to_sbv_mpq(rmv, v, q); rational r(q); rational ul, ll; ul = m_fm.m_powers2.m1(bv_sz); ll = rational(0); if (r >= ll && r <= ul) result = bu.mk_numeral(r, bv_sz); else result = m_util.mk_internal_to_ubv_unspecified(bv_sz); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_to_sbv(func_decl * f, expr * arg1, expr * arg2, expr_ref & result) { SASSERT(f->get_num_parameters() == 1); SASSERT(f->get_parameter(0).is_int()); int bv_sz = f->get_parameter(0).get_int(); mpf_rounding_mode rmv; scoped_mpf v(m_fm); if (m_util.is_rm_numeral(arg1, rmv) && m_util.is_numeral(arg2, v)) { if (m_fm.is_nan(v) || m_fm.is_inf(v)) { result = m_util.mk_internal_to_sbv_unspecified(bv_sz); return BR_REWRITE_FULL; } bv_util bu(m()); scoped_mpq q(m_fm.mpq_manager()); m_fm.to_sbv_mpq(rmv, v, q); rational r(q); rational ul, ll; ul = m_fm.m_powers2.m1(bv_sz - 1); ll = - m_fm.m_powers2(bv_sz - 1); if (r >= ll && r <= ul) result = bu.mk_numeral(r, bv_sz); else result = m_util.mk_internal_to_sbv_unspecified(bv_sz); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_to_real(expr * arg, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg, v)) { if (m_fm.is_nan(v) || m_fm.is_inf(v)) { result = m_util.mk_internal_to_real_unspecified(); } else { scoped_mpq r(m_fm.mpq_manager()); m_fm.to_rational(v, r); result = m_util.au().mk_numeral(r.get(), false); } return BR_DONE; } return BR_FAILED; } z3-4.4.0/src/ast/rewriter/var_subst.cpp0000644000175000017500000001650612540347414017612 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: var_subst.cpp Abstract: Variable substitution. Author: Leonardo (leonardo) 2008-01-10 Notes: --*/ #include"var_subst.h" #include"ast_ll_pp.h" #include"ast_pp.h" #include"ast_smt2_pp.h" #include"well_sorted.h" #include"for_each_expr.h" void var_subst::operator()(expr * n, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(is_well_sorted(result.m(), n)); m_reducer.reset(); if (m_std_order) m_reducer.set_inv_bindings(num_args, args); else m_reducer.set_bindings(num_args, args); m_reducer(n, result); SASSERT(is_well_sorted(m_reducer.m(), result)); TRACE("var_subst_bug", tout << "m_std_order: " << m_std_order << "\n" << mk_ismt2_pp(n, m_reducer.m()) << "\nusing\n"; for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m_reducer.m()) << "\n"; tout << "\n------>\n"; tout << mk_ismt2_pp(result, m_reducer.m()) << "\n";); } void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { SASSERT(is_well_sorted(m, q)); if (is_ground(q->get_expr())) { // ignore patterns if the body is a ground formula. result = q->get_expr(); return; } if (!q->may_have_unused_vars()) { result = q; return; } m_used.reset(); m_used.process(q->get_expr()); unsigned num_patterns = q->get_num_patterns(); for (unsigned i = 0; i < num_patterns; i++) m_used.process(q->get_pattern(i)); unsigned num_no_patterns = q->get_num_no_patterns(); for (unsigned i = 0; i < num_no_patterns; i++) m_used.process(q->get_no_pattern(i)); unsigned num_decls = q->get_num_decls(); if (m_used.uses_all_vars(num_decls)) { q->set_no_unused_vars(); result = q; return; } ptr_buffer used_decl_sorts; buffer used_decl_names; for (unsigned i = 0; i < num_decls; ++i) { if (m_used.contains(num_decls - i - 1)) { used_decl_sorts.push_back(q->get_decl_sort(i)); used_decl_names.push_back(q->get_decl_name(i)); } } unsigned num_removed = 0; expr_ref_buffer var_mapping(m); int next_idx = 0; unsigned sz = m_used.get_max_found_var_idx_plus_1(); for (unsigned i = 0; i < num_decls; ++i) { sort * s = m_used.contains(i); if (s) { var_mapping.push_back(m.mk_var(next_idx, s)); next_idx++; } else { num_removed++; var_mapping.push_back(0); } } // (VAR 0) is in the first position of var_mapping. for (unsigned i = num_decls; i < sz; i++) { sort * s = m_used.contains(i); if (s) var_mapping.push_back(m.mk_var(i - num_removed, s)); else var_mapping.push_back(0); } // Remark: // (VAR 0) should be in the last position of var_mapping. // ... // (VAR (var_mapping.size() - 1)) should be in the first position. std::reverse(var_mapping.c_ptr(), var_mapping.c_ptr() + var_mapping.size()); expr_ref new_expr(m); m_subst(q->get_expr(), var_mapping.size(), var_mapping.c_ptr(), new_expr); if (num_removed == num_decls) { result = new_expr; return; } expr_ref tmp(m); expr_ref_buffer new_patterns(m); expr_ref_buffer new_no_patterns(m); for (unsigned i = 0; i < num_patterns; i++) { m_subst(q->get_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp); new_patterns.push_back(tmp); } for (unsigned i = 0; i < num_no_patterns; i++) { m_subst(q->get_no_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp); new_no_patterns.push_back(tmp); } result = m.mk_quantifier(q->is_forall(), used_decl_sorts.size(), used_decl_sorts.c_ptr(), used_decl_names.c_ptr(), new_expr, q->get_weight(), q->get_qid(), q->get_skid(), num_patterns, new_patterns.c_ptr(), num_no_patterns, new_no_patterns.c_ptr()); to_quantifier(result)->set_no_unused_vars(); SASSERT(is_well_sorted(m, result)); } void elim_unused_vars(ast_manager & m, quantifier * q, expr_ref & result) { unused_vars_eliminator el(m); el(q, result); } void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref & result) { var_subst subst(m); expr_ref new_expr(m); subst(q->get_expr(), q->get_num_decls(), exprs, new_expr); TRACE("var_subst", tout << mk_pp(q, m) << "\n" << mk_pp(new_expr, m) << "\n";); inv_var_shifter shift(m); shift(new_expr, q->get_num_decls(), result); SASSERT(is_well_sorted(m, result)); TRACE("instantiate_bug", tout << mk_ismt2_pp(q, m) << "\nusing\n"; for (unsigned i = 0; i < q->get_num_decls(); i++) tout << mk_ismt2_pp(exprs[i], m) << "\n"; tout << "\n----->\n" << mk_ismt2_pp(result, m) << "\n";); } static void get_free_vars_offset(expr_sparse_mark& mark, ptr_vector& todo, unsigned offset, expr* e, ptr_vector& sorts) { todo.push_back(e); while (!todo.empty()) { e = todo.back(); todo.pop_back(); if (mark.is_marked(e)) { continue; } mark.mark(e, true); switch(e->get_kind()) { case AST_QUANTIFIER: { quantifier* q = to_quantifier(e); expr_sparse_mark mark1; ptr_vector todo1; get_free_vars_offset(mark1, todo1, offset+q->get_num_decls(), q->get_expr(), sorts); break; } case AST_VAR: { var* v = to_var(e); if (v->get_idx() >= offset) { unsigned idx = v->get_idx()-offset; if (sorts.size() <= idx) { sorts.resize(idx+1); } if (!sorts[idx]) { sorts[idx] = v->get_sort(); } SASSERT(sorts[idx] == v->get_sort()); } break; } case AST_APP: { app* a = to_app(e); for (unsigned i = 0; i < a->get_num_args(); ++i) { todo.push_back(a->get_arg(i)); } break; } default: UNREACHABLE(); } } } void get_free_vars(expr* e, ptr_vector& sorts) { expr_sparse_mark mark; ptr_vector todo; get_free_vars_offset(mark, todo, 0, e, sorts); } void get_free_vars(expr_sparse_mark& mark, ptr_vector& todo, expr* e, ptr_vector& sorts) { get_free_vars_offset(mark, todo, 0, e, sorts); } void expr_free_vars::reset() { m_mark.reset(); m_sorts.reset(); SASSERT(m_todo.empty()); } void expr_free_vars::set_default_sort(sort *s) { for (unsigned i = 0; i < m_sorts.size(); ++i) { if (!m_sorts[i]) m_sorts[i] = s; } } void expr_free_vars::operator()(expr* e) { reset(); get_free_vars_offset(m_mark, m_todo, 0, e, m_sorts); } void expr_free_vars::accumulate(expr* e) { SASSERT(m_todo.empty()); get_free_vars_offset(m_mark, m_todo, 0, e, m_sorts); } z3-4.4.0/src/ast/rewriter/ast_counter.h0000644000175000017500000000467012540347414017574 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: ast_counter.h Abstract: Routines for counting features of terms, such as free variables. Author: Nikolaj Bjorner (nbjorner) 2013-03-18. Krystof Hoder (t-khoder) 2010-10-10. Revision History: Hoisted from dl_util.h 2013-03-18. --*/ #ifndef _AST_COUNTER_H_ #define _AST_COUNTER_H_ #include "ast.h" #include "map.h" #include "uint_set.h" #include "var_subst.h" class counter { protected: typedef u_map map_impl; map_impl m_data; public: typedef map_impl::iterator iterator; counter() {} void reset() { m_data.reset(); } iterator begin() const { return m_data.begin(); } iterator end() const { return m_data.end(); } void update(unsigned el, int delta); int & get(unsigned el); /** \brief Increase values of elements in \c els by \c delta. The function returns a reference to \c *this to allow for expressions like counter().count(sz, arr).get_positive_count() */ counter & count(unsigned sz, const unsigned * els, int delta = 1); counter & count(const unsigned_vector & els, int delta = 1) { return count(els.size(), els.c_ptr(), delta); } void collect_positive(uint_set & acc) const; unsigned get_positive_count() const; bool get_max_positive(unsigned & res) const; unsigned get_max_positive() const; /** Since the default counter value of a counter is zero, the result is never negative. */ int get_max_counter_value() const; }; class var_counter : public counter { protected: expr_fast_mark1 m_visited; expr_free_vars m_fv; ptr_vector m_todo; unsigned_vector m_scopes; unsigned get_max_var(bool & has_var); public: var_counter() {} void count_vars(const app * t, int coef = 1); unsigned get_max_var(expr* e); unsigned get_next_var(expr* e); }; class ast_counter { typedef obj_map map_impl; map_impl m_data; public: typedef map_impl::iterator iterator; ast_counter() {} iterator begin() const { return m_data.begin(); } iterator end() const { return m_data.end(); } int & get(ast * el) { return m_data.insert_if_not_there2(el, 0)->get_data().m_value; } void update(ast * el, int delta){ get(el) += delta; } void inc(ast * el) { update(el, 1); } void dec(ast * el) { update(el, -1); } }; #endif z3-4.4.0/src/ast/rewriter/pb_rewriter.cpp0000644000175000017500000001640112540347414020120 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: pb_rewriter.cpp Abstract: Basic rewriting rules for PB constraints. Author: Nikolaj Bjorner (nbjorner) 2013-14-12 Notes: --*/ #include "pb_rewriter.h" #include "pb_rewriter_def.h" #include "ast_pp.h" #include "ast_smt_pp.h" class pb_ast_rewriter_util { ast_manager& m; expr_ref_vector m_refs; public: typedef std::pair arg_t; typedef vector args_t; typedef rational numeral; pb_ast_rewriter_util(ast_manager& m): m(m), m_refs(m) {} expr* negate(expr* e) { if (m.is_true(e)) { return m.mk_false(); } if (m.is_false(e)) { return m.mk_true(); } if (m.is_not(e, e)) { return e; } m_refs.push_back(m.mk_not(e)); return m_refs.back(); } void display(std::ostream& out, expr* e) { out << mk_pp(e, m); } bool is_negated(expr* e) const { return m.is_not(e); } bool is_true(expr* e) const { return m.is_true(e); } bool is_false(expr* e) const { return m.is_false(e); } struct compare { bool operator()(std::pair const& a, std::pair const& b) { return a.first->get_id() < b.first->get_id(); } }; }; expr_ref pb_rewriter::translate_pb2lia(obj_map& vars, expr* fml) { pb_util util(m()); arith_util a(m()); expr_ref result(m()), tmp(m()); expr_ref_vector es(m()); expr*const* args = to_app(fml)->get_args(); unsigned sz = to_app(fml)->get_num_args(); for (unsigned i = 0; i < sz; ++i) { expr* e = args[i]; if (m().is_not(e, e)) { es.push_back(a.mk_sub(a.mk_numeral(rational(1),true),vars.find(e))); } else { es.push_back(vars.find(e)); } } if (util.is_at_most_k(fml) || util.is_at_least_k(fml)) { if (es.empty()) { tmp = a.mk_numeral(rational(0), true); } else { tmp = a.mk_add(es.size(), es.c_ptr()); } if (util.is_at_most_k(fml)) { result = a.mk_le(tmp, a.mk_numeral(util.get_k(fml), false)); } else { result = a.mk_ge(tmp, a.mk_numeral(util.get_k(fml), false)); } } else if (util.is_le(fml) || util.is_ge(fml) || util.is_eq(fml)) { for (unsigned i = 0; i < sz; ++i) { es[i] = a.mk_mul(a.mk_numeral(util.get_coeff(fml, i),false), es[i].get()); } if (es.empty()) { tmp = a.mk_numeral(rational(0), true); } else { tmp = a.mk_add(es.size(), es.c_ptr()); } if (util.is_le(fml)) { result = a.mk_le(tmp, a.mk_numeral(util.get_k(fml), false)); } else if (util.is_ge(fml)) { result = a.mk_ge(tmp, a.mk_numeral(util.get_k(fml), false)); } else { result = m().mk_eq(tmp, a.mk_numeral(util.get_k(fml), false)); } } else { result = fml; } return result; } expr_ref pb_rewriter::mk_validate_rewrite(app_ref& e1, app_ref& e2) { ast_manager& m = e1.get_manager(); arith_util a(m); symbol name; obj_map vars; expr_ref_vector trail(m), fmls(m); unsigned sz = to_app(e1)->get_num_args(); expr*const*args = to_app(e1)->get_args(); for (unsigned i = 0; i < sz; ++i) { expr* e = args[i]; if (m.is_true(e)) { if (!vars.contains(e)) { trail.push_back(a.mk_numeral(rational(1), true)); vars.insert(e, trail.back()); } continue; } if (m.is_false(e)) { if (!vars.contains(e)) { trail.push_back(a.mk_numeral(rational(0), true)); vars.insert(e, trail.back()); } continue; } std::ostringstream strm; strm << "x" << i; name = symbol(strm.str().c_str()); trail.push_back(m.mk_const(name, a.mk_int())); expr* x = trail.back(); m.is_not(e,e); vars.insert(e, x); fmls.push_back(a.mk_le(a.mk_numeral(rational(0), true), x)); fmls.push_back(a.mk_le(x, a.mk_numeral(rational(1), true))); } expr_ref tmp(m); expr_ref fml1 = translate_pb2lia(vars, e1); expr_ref fml2 = translate_pb2lia(vars, e2); tmp = m.mk_not(m.mk_eq(fml1, fml2)); fmls.push_back(tmp); tmp = m.mk_and(fmls.size(), fmls.c_ptr()); return tmp; } static unsigned s_lemma = 0; void pb_rewriter::validate_rewrite(func_decl* f, unsigned sz, expr*const* args, expr_ref& fml) { ast_manager& m = fml.get_manager(); app_ref tmp1(m), tmp2(m); tmp1 = m.mk_app(f, sz, args); tmp2 = to_app(fml); expr_ref tmp = mk_validate_rewrite(tmp1, tmp2); dump_pb_rewrite(tmp); } void pb_rewriter::dump_pb_rewrite(expr* fml) { std::ostringstream strm; strm << "pb_rewrite_" << (s_lemma++) << ".smt2"; std::ofstream out(strm.str().c_str()); ast_smt_pp pp(m()); pp.display_smt2(out, fml); out.close(); } br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { ast_manager& m = result.get_manager(); rational sum(0), maxsum(0); for (unsigned i = 0; i < num_args; ++i) { if (m.is_true(args[i])) { sum += m_util.get_coeff(f, i); maxsum += m_util.get_coeff(f, i); } else if (!m.is_false(args[i])) { maxsum += m_util.get_coeff(f, i); } } rational k = m_util.get_k(f); vector > vec; for (unsigned i = 0; i < num_args; ++i) { vec.push_back(std::make_pair(args[i], m_util.get_coeff(f, i))); } switch(f->get_decl_kind()) { case OP_AT_MOST_K: case OP_PB_LE: for (unsigned i = 0; i < num_args; ++i) { vec[i].second.neg(); } k.neg(); break; case OP_AT_LEAST_K: case OP_PB_GE: case OP_PB_EQ: break; default: UNREACHABLE(); return BR_FAILED; } bool is_eq = f->get_decl_kind() == OP_PB_EQ; pb_ast_rewriter_util pbu(m); pb_rewriter_util util(pbu); util.unique(vec, k, is_eq); lbool is_sat = util.normalize(vec, k, is_eq); util.prune(vec, k, is_eq); switch (is_sat) { case l_true: result = m.mk_true(); break; case l_false: result = m.mk_false(); break; default: m_args.reset(); m_coeffs.reset(); for (unsigned i = 0; i < vec.size(); ++i) { m_args.push_back(vec[i].first); m_coeffs.push_back(vec[i].second); } if (is_eq) { result = m_util.mk_eq(vec.size(), m_coeffs.c_ptr(), m_args.c_ptr(), k); } else { result = m_util.mk_ge(vec.size(), m_coeffs.c_ptr(), m_args.c_ptr(), k); } break; } TRACE("pb", expr_ref tmp(m); tmp = m.mk_app(f, num_args, args); tout << tmp << "\n"; tout << result << "\n"; ); TRACE("pb_validate", validate_rewrite(f, num_args, args, result);); return BR_DONE; } z3-4.4.0/src/ast/rewriter/array_rewriter.h0000644000175000017500000000420712540347414020303 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: array_rewriter.h Abstract: Basic rewriting rules for Arrays. Author: Leonardo (leonardo) 2011-04-06 Notes: --*/ #ifndef _ARRAY_REWRITER_H_ #define _ARRAY_REWRITER_H_ #include"array_decl_plugin.h" #include"rewriter_types.h" #include"lbool.h" #include"params.h" /** \brief Cheap rewrite rules for Arrays */ class array_rewriter { array_util m_util; bool m_sort_store; bool m_expand_select_store; bool m_expand_store_eq; template lbool compare_args(unsigned num_args, expr * const * args1, expr * const * args2); public: array_rewriter(ast_manager & m, params_ref const & p = params_ref()): m_util(m) { updt_params(p); } ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); br_status mk_store_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_select_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_map_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); void mk_store(unsigned num_args, expr * const * args, expr_ref & result); void mk_select(unsigned num_args, expr * const * args, expr_ref & result); void mk_map(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); // The following methods never return BR_FAILED br_status mk_set_union(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_set_intersect(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_set_complement(expr * arg, expr_ref & result); br_status mk_set_difference(expr * arg1, expr * arg2, expr_ref & result); br_status mk_set_subset(expr * arg1, expr * arg2, expr_ref & result); br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result); }; #endif z3-4.4.0/src/ast/rewriter/array_rewriter.cpp0000644000175000017500000003404312540347414020637 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: array_rewriter.cpp Abstract: Basic rewriting rules for Arrays. Author: Leonardo (leonardo) 2011-04-06 Notes: --*/ #include"array_rewriter.h" #include"array_rewriter_params.hpp" #include"ast_lt.h" #include"ast_pp.h" void array_rewriter::updt_params(params_ref const & _p) { array_rewriter_params p(_p); m_sort_store = p.sort_store(); m_expand_select_store = p.expand_select_store(); m_expand_store_eq = p.expand_store_eq(); } void array_rewriter::get_param_descrs(param_descrs & r) { array_rewriter_params::collect_param_descrs(r); } br_status array_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(f->get_family_id() == get_fid()); TRACE("array_rewriter", tout << mk_pp(f, m()) << "\n"; for (unsigned i = 0; i < num_args; ++i) { tout << mk_pp(args[i], m()) << "\n"; }); switch (f->get_decl_kind()) { case OP_SELECT: return mk_select_core(num_args, args, result); case OP_STORE: return mk_store_core(num_args, args, result); case OP_ARRAY_MAP: SASSERT(f->get_num_parameters() == 1); SASSERT(f->get_parameter(0).is_ast()); SASSERT(is_func_decl(f->get_parameter(0).get_ast())); return mk_map_core(to_func_decl(f->get_parameter(0).get_ast()), num_args, args, result); case OP_SET_UNION: return mk_set_union(num_args, args, result); case OP_SET_INTERSECT: return mk_set_intersect(num_args, args, result); case OP_SET_SUBSET: SASSERT(num_args == 2); return mk_set_subset(args[0], args[1], result); case OP_SET_COMPLEMENT: SASSERT(num_args == 1); return mk_set_complement(args[0], result); case OP_SET_DIFFERENCE: SASSERT(num_args == 2); return mk_set_difference(args[0], args[1], result); default: return BR_FAILED; } } // l_true -- all equal // l_false -- at least one disequal // l_undef -- don't know template lbool array_rewriter::compare_args(unsigned num_args, expr * const * args1, expr * const * args2) { for (unsigned i = 0; i < num_args; i++) { if (args1[i] == args2[i]) continue; if (CHECK_DISEQ && m().are_distinct(args1[i], args2[i])) return l_false; return l_undef; } return l_true; } br_status array_rewriter::mk_store_core(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args >= 3); if (m_util.is_store(args[0])) { lbool r = m_sort_store ? compare_args(num_args - 2, args + 1, to_app(args[0])->get_args() + 1) : compare_args(num_args - 2, args + 1, to_app(args[0])->get_args() + 1); switch (r) { case l_true: { // // store(store(a,i,v),i,w) --> store(a,i,w) // ptr_buffer new_args; new_args.push_back(to_app(args[0])->get_arg(0)); new_args.append(num_args-1, args+1); SASSERT(new_args.size() == num_args); result = m().mk_app(get_fid(), OP_STORE, num_args, new_args.c_ptr()); return BR_DONE; } case l_false: SASSERT(m_sort_store); // // store(store(a,i,v),j,w) -> store(store(a,j,w),i,v) // if i, j are different, lt(i,j) // if (lex_lt(num_args-2, args+1, to_app(args[0])->get_args() + 1)) { ptr_buffer new_args; new_args.push_back(to_app(args[0])->get_arg(0)); new_args.append(num_args-1, args+1); expr * nested_store = m().mk_app(get_fid(), OP_STORE, num_args, new_args.c_ptr()); new_args.reset(); new_args.push_back(nested_store); new_args.append(num_args - 1, to_app(args[0])->get_args() + 1); result = m().mk_app(get_fid(), OP_STORE, num_args, new_args.c_ptr()); return BR_REWRITE2; } break; case l_undef: break; } } // // store(const(v),i,v) --> const(v) // if (m_util.is_const(args[0]) && to_app(args[0])->get_arg(0) == args[num_args-1]) { result = args[0]; return BR_DONE; } expr * v = args[num_args-1]; // // store(a, i, select(a, i)) --> a // if (m_util.is_select(v) && compare_args(num_args-1, args, to_app(v)->get_args())) { result = args[0]; return BR_DONE; } return BR_FAILED; } br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args >= 2); if (m_util.is_store(args[0])) { SASSERT(to_app(args[0])->get_num_args() == num_args+1); switch (compare_args(num_args - 1, args+1, to_app(args[0])->get_args()+1)) { case l_true: // select(store(a, I, v), I) --> v result = to_app(args[0])->get_arg(num_args); return BR_DONE; case l_false: { // select(store(a, I, v), J) --> select(a, J) if I != J ptr_buffer new_args; new_args.push_back(to_app(args[0])->get_arg(0)); new_args.append(num_args-1, args+1); result = m().mk_app(get_fid(), OP_SELECT, num_args, new_args.c_ptr()); return BR_REWRITE1; } default: if (m_expand_select_store) { // select(store(a, I, v), J) --> ite(I=J, v, select(a, J)) ptr_buffer new_args; new_args.push_back(to_app(args[0])->get_arg(0)); new_args.append(num_args-1, args+1); expr * sel_a_j = m().mk_app(get_fid(), OP_SELECT, num_args, new_args.c_ptr()); expr * v = to_app(args[0])->get_arg(num_args); ptr_buffer eqs; unsigned num_indices = num_args-1; for (unsigned i = 0; i < num_indices; i++) { eqs.push_back(m().mk_eq(to_app(args[0])->get_arg(i+1), args[i+1])); } if (num_indices == 1) { result = m().mk_ite(eqs[0], v, sel_a_j); return BR_REWRITE2; } else { result = m().mk_ite(m().mk_and(eqs.size(), eqs.c_ptr()), v, sel_a_j); return BR_REWRITE3; } } return BR_FAILED; } } if (m_util.is_const(args[0])) { // select(const(v), I) --> v result = to_app(args[0])->get_arg(0); return BR_DONE; } if (m_util.is_as_array(args[0])) { // select(as-array[f], I) --> f(I) func_decl * f = m_util.get_as_array_func_decl(to_app(args[0])); result = m().mk_app(f, num_args - 1, args + 1); return BR_REWRITE1; } return BR_FAILED; } br_status array_rewriter::mk_map_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args >= 0); bool is_store0 = m_util.is_store(args[0]); bool is_const0 = m_util.is_const(args[0]); if (num_args == 1) { // // map_f (store a j v) = (store (map_f a) j (f v)) // if (is_store0) { app * store_expr = to_app(args[0]); unsigned num_args = store_expr->get_num_args(); SASSERT(num_args >= 3); expr * a = store_expr->get_arg(0); expr * v = store_expr->get_arg(num_args-1); ptr_buffer new_args; new_args.push_back(m_util.mk_map(f, 1, &a)); // (map_f a) new_args.append(num_args - 2, store_expr->get_args() + 1); // j new_args.push_back(m().mk_app(f, v)); // (f v) result = m().mk_app(get_fid(), OP_STORE, new_args.size(), new_args.c_ptr()); return BR_REWRITE2; } // // map_f (const v) = (const (f v)) // if (is_const0) { expr * fv = m().mk_app(f, to_app(args[0])->get_arg(0)); result = m_util.mk_const_array(m().get_sort(args[0]), fv); return BR_REWRITE2; } return BR_FAILED; } SASSERT(num_args > 1); if (is_store0) { unsigned num_indices = to_app(args[0])->get_num_args() - 2; unsigned i; for (i = 1; i < num_args; i++) { if (!m_util.is_store(args[i])) break; unsigned j; for (j = 1; j < num_indices+1; j++) { if (to_app(args[0])->get_arg(j) != to_app(args[i])->get_arg(j)) break; } if (j < num_indices+1) break; } // // map_f (store a_1 j v_1) ... (store a_n j v_n) --> (store (map_f a_1 ... a_n) j (f v_1 ... v_n)) // if (i == num_args) { ptr_buffer arrays; ptr_buffer values; for (unsigned i = 0; i < num_args; i++) { arrays.push_back(to_app(args[i])->get_arg(0)); values.push_back(to_app(args[i])->get_arg(num_indices+1)); } ptr_buffer new_args; new_args.push_back(m_util.mk_map(f, arrays.size(), arrays.c_ptr())); new_args.append(num_indices, to_app(args[0])->get_args() + 1); new_args.push_back(m().mk_app(f, values.size(), values.c_ptr())); result = m().mk_app(get_fid(), OP_STORE, new_args.size(), new_args.c_ptr()); return BR_REWRITE2; } return BR_FAILED; } if (is_const0) { unsigned i; for (i = 1; i < num_args; i++) { if (!m_util.is_const(args[i])) break; } if (i == num_args) { // // map_f (const v_1) ... (const v_n) = (const (f v_1 ... v_n)) // ptr_buffer values; for (unsigned i = 0; i < num_args; i++) { values.push_back(to_app(args[i])->get_arg(0)); } expr * fv = m().mk_app(f, values.size(), values.c_ptr()); sort * in_s = get_sort(args[0]); ptr_vector domain; unsigned domain_sz = get_array_arity(in_s); for (unsigned i = 0; i < domain_sz; i++) domain.push_back(get_array_domain(in_s, i)); sort_ref out_s(m()); out_s = m_util.mk_array_sort(domain_sz, domain.c_ptr(), f->get_range()); parameter p(out_s.get()); result = m().mk_app(get_fid(), OP_CONST_ARRAY, 1, &p, 1, &fv); return BR_REWRITE2; } return BR_FAILED; } return BR_FAILED; } void array_rewriter::mk_store(unsigned num_args, expr * const * args, expr_ref & result) { if (mk_store_core(num_args, args, result) == BR_FAILED) result = m().mk_app(get_fid(), OP_STORE, num_args, args); } void array_rewriter::mk_select(unsigned num_args, expr * const * args, expr_ref & result) { if (mk_select_core(num_args, args, result) == BR_FAILED) result = m().mk_app(get_fid(), OP_SELECT, num_args, args); } void array_rewriter::mk_map(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_map_core(f, num_args, args, result) == BR_FAILED) result = m_util.mk_map(f, num_args, args); } br_status array_rewriter::mk_set_union(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args > 0); if (num_args == 1) { result = args[0]; return BR_DONE; } SASSERT(num_args >= 2); br_status r = unsigned2br_status(num_args - 2); result = m_util.mk_map(m().mk_or_decl(), num_args, args); return r; } br_status array_rewriter::mk_set_intersect(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args > 0); if (num_args == 1) { result = args[0]; return BR_DONE; } SASSERT(num_args >= 2); br_status r = unsigned2br_status(num_args - 2); result = m_util.mk_map(m().mk_and_decl(), num_args, args); return r; } br_status array_rewriter::mk_set_complement(expr * arg, expr_ref & result) { return mk_map_core(m().mk_not_decl(), 1, &arg, result); } br_status array_rewriter::mk_set_difference(expr * arg1, expr * arg2, expr_ref & result) { expr * args[2] = { arg1, m_util.mk_map(m().mk_not_decl(), 1, &arg2) }; result = m_util.mk_map(m().mk_and_decl(), 2, args); return BR_REWRITE2; } br_status array_rewriter::mk_set_subset(expr * arg1, expr * arg2, expr_ref & result) { mk_set_difference(arg1, arg2, result); result = m().mk_eq(result.get(), m_util.mk_empty_set(m().get_sort(arg1))); return BR_REWRITE3; } br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { if (!m_expand_store_eq) { return BR_FAILED; } expr* lhs1 = lhs; while (m_util.is_store(lhs1)) { lhs1 = to_app(lhs1)->get_arg(0); } expr* rhs1 = rhs; while (m_util.is_store(rhs1)) { rhs1 = to_app(rhs1)->get_arg(0); } if (lhs1 != rhs1) { return BR_FAILED; } ptr_buffer fmls, args; expr* e; expr_ref tmp1(m()), tmp2(m()); #define MK_EQ() \ while (m_util.is_store(e)) { \ args.push_back(lhs); \ args.append(to_app(e)->get_num_args()-2,to_app(e)->get_args()+1); \ mk_select(args.size(), args.c_ptr(), tmp1); \ args[0] = rhs; \ mk_select(args.size(), args.c_ptr(), tmp2); \ fmls.push_back(m().mk_eq(tmp1, tmp2)); \ e = to_app(e)->get_arg(0); \ args.reset(); \ } \ e = lhs; MK_EQ(); e = rhs; MK_EQ(); result = m().mk_and(fmls.size(), fmls.c_ptr()); return BR_REWRITE_FULL; } z3-4.4.0/src/ast/rewriter/datatype_rewriter.cpp0000644000175000017500000001076712540347414021343 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: datatype_rewriter.cpp Abstract: Basic rewriting rules for Datatypes. Author: Leonardo (leonardo) 2011-04-06 Notes: --*/ #include"datatype_rewriter.h" br_status datatype_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(f->get_family_id() == get_fid()); switch(f->get_decl_kind()) { case OP_DT_CONSTRUCTOR: return BR_FAILED; case OP_DT_RECOGNISER: // // simplify is_cons(cons(x,y)) -> true // simplify is_cons(nil) -> false // SASSERT(num_args == 1); if (!is_app(args[0]) || !m_util.is_constructor(to_app(args[0]))) return BR_FAILED; if (to_app(args[0])->get_decl() == m_util.get_recognizer_constructor(f)) result = m().mk_true(); else result = m().mk_false(); return BR_DONE; case OP_DT_ACCESSOR: { // // simplify head(cons(x,y)) -> x // SASSERT(num_args == 1); if (!is_app(args[0]) || !m_util.is_constructor(to_app(args[0]))) return BR_FAILED; app * a = to_app(args[0]); func_decl * c_decl = a->get_decl(); if (c_decl != m_util.get_accessor_constructor(f)) return BR_FAILED; ptr_vector const * acc = m_util.get_constructor_accessors(c_decl); SASSERT(acc && acc->size() == a->get_num_args()); unsigned num = acc->size(); for (unsigned i = 0; i < num; ++i) { if (f == (*acc)[i]) { // found it. result = a->get_arg(i); return BR_DONE; } } UNREACHABLE(); break; } case OP_DT_UPDATE_FIELD: { SASSERT(num_args == 2); if (!is_app(args[0]) || !m_util.is_constructor(to_app(args[0]))) return BR_FAILED; app * a = to_app(args[0]); func_decl * c_decl = a->get_decl(); if (c_decl != m_util.get_accessor_constructor(f)) { result = a; return BR_DONE; } ptr_vector const * acc = m_util.get_constructor_accessors(c_decl); SASSERT(acc && acc->size() == a->get_num_args()); unsigned num = acc->size(); ptr_buffer new_args; for (unsigned i = 0; i < num; ++i) { if (f == (*acc)[i]) { new_args.push_back(args[1]); } else { new_args.push_back(a->get_arg(i)); } } result = m().mk_app(c_decl, num, new_args.c_ptr()); return BR_DONE; } default: UNREACHABLE(); } return BR_FAILED; } br_status datatype_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { if (!is_app(lhs) || !is_app(rhs) || !m_util.is_constructor(to_app(lhs)) || !m_util.is_constructor(to_app(rhs))) return BR_FAILED; if (to_app(lhs)->get_decl() != to_app(rhs)->get_decl()) { result = m().mk_false(); return BR_DONE; } // Remark: In datatype_simplifier_plugin, we used // m_basic_simplifier to create '=' and 'and' applications in the // following code. This trick not guarantee that the final expression // will be fully simplified. // // Example: // The assertion // (assert (= (cons a1 (cons a2 (cons a3 (cons (+ a4 1) (cons (+ a5 c5) (cons a6 nil)))))) // (cons b1 (cons b2 (cons b3 (cons b4 (cons b5 (cons b6 nil)))))))) // // After applying asserted_formulas::reduce(), the following formula was generated. // // (= a1 b1) // (= a2 b2) // (= a3 b3) // (= (+ a4 (* (- 1) b4)) (- 1)) // (= (+ c5 a5) b5) <<< NOT SIMPLIFIED WITH RESPECT TO ARITHMETIC // (= (cons a6 nil) (cons b6 nil))) <<< NOT SIMPLIFIED WITH RESPECT TO DATATYPE theory // // Note that asserted_formulas::reduce() applied the simplier many times. // After the first simplification step we had: // (= a1 b1) // (= (cons a2 (cons a3 (cons (+ a4 1) (cons (+ a5 c5) (cons a6 nil)))))) // (cons b2 (cons b3 (cons b4 (cons b5 (cons b6 nil)))))) ptr_buffer eqs; unsigned num = to_app(lhs)->get_num_args(); SASSERT(num == to_app(rhs)->get_num_args()); for (unsigned i = 0; i < num; ++i) { eqs.push_back(m().mk_eq(to_app(lhs)->get_arg(i), to_app(rhs)->get_arg(i))); } result = m().mk_and(eqs.size(), eqs.c_ptr()); return BR_REWRITE2; } z3-4.4.0/src/ast/rewriter/th_rewriter.cpp0000644000175000017500000006457612540347414020152 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: th_rewriter.h Abstract: Rewriter for applying all builtin (cheap) theory rewrite rules. Author: Leonardo (leonardo) 2011-04-07 Notes: --*/ #include"th_rewriter.h" #include"rewriter_params.hpp" #include"bool_rewriter.h" #include"arith_rewriter.h" #include"bv_rewriter.h" #include"datatype_rewriter.h" #include"array_rewriter.h" #include"fpa_rewriter.h" #include"dl_rewriter.h" #include"pb_rewriter.h" #include"rewriter_def.h" #include"expr_substitution.h" #include"ast_smt2_pp.h" #include"cooperate.h" #include"var_subst.h" #include"ast_util.h" #include"well_sorted.h" struct th_rewriter_cfg : public default_rewriter_cfg { bool_rewriter m_b_rw; arith_rewriter m_a_rw; bv_rewriter m_bv_rw; array_rewriter m_ar_rw; datatype_rewriter m_dt_rw; fpa_rewriter m_f_rw; dl_rewriter m_dl_rw; pb_rewriter m_pb_rw; arith_util m_a_util; bv_util m_bv_util; unsigned long long m_max_memory; // in bytes unsigned m_max_steps; bool m_pull_cheap_ite; bool m_flat; bool m_cache_all; bool m_push_ite_arith; bool m_push_ite_bv; // substitution support expr_dependency_ref m_used_dependencies; // set of dependencies of used substitutions expr_substitution * m_subst; ast_manager & m() const { return m_b_rw.m(); } void updt_local_params(params_ref const & _p) { rewriter_params p(_p); m_flat = p.flat(); m_max_memory = megabytes_to_bytes(p.max_memory()); m_max_steps = p.max_steps(); m_pull_cheap_ite = p.pull_cheap_ite(); m_cache_all = p.cache_all(); m_push_ite_arith = p.push_ite_arith(); m_push_ite_bv = p.push_ite_bv(); } void updt_params(params_ref const & p) { m_b_rw.updt_params(p); m_a_rw.updt_params(p); m_bv_rw.updt_params(p); m_ar_rw.updt_params(p); m_f_rw.updt_params(p); updt_local_params(p); } bool flat_assoc(func_decl * f) const { if (!m_flat) return false; family_id fid = f->get_family_id(); if (fid == null_family_id) return false; decl_kind k = f->get_decl_kind(); if (fid == m_b_rw.get_fid()) return k == OP_AND || k == OP_OR; if (fid == m_a_rw.get_fid()) return k == OP_ADD; if (fid == m_bv_rw.get_fid()) return k == OP_BADD || k == OP_BOR || k == OP_BAND || k == OP_BXOR; return false; } bool rewrite_patterns() const { return false; } bool cache_all_results() const { return m_cache_all; } bool max_steps_exceeded(unsigned num_steps) const { cooperate("simplifier"); if (memory::get_allocation_size() > m_max_memory) throw rewriter_exception(Z3_MAX_MEMORY_MSG); return num_steps > m_max_steps; } // Return true if t is of the form // (= t #b0) // (= t #b1) // (= #b0 t) // (= #b1 t) bool is_eq_bit(expr * t, expr * & x, unsigned & val) { if (!m().is_eq(t)) return false; expr * lhs = to_app(t)->get_arg(0); if (!m_bv_rw.is_bv(lhs)) return false; if (m_bv_rw.get_bv_size(lhs) != 1) return false; expr * rhs = to_app(t)->get_arg(1); rational v; unsigned sz; if (m_bv_rw.is_numeral(lhs, v, sz)) { x = rhs; val = v.get_unsigned(); SASSERT(val == 0 || val == 1); return true; } if (m_bv_rw.is_numeral(rhs, v, sz)) { x = lhs; val = v.get_unsigned(); SASSERT(val == 0 || val == 1); return true; } return false; } // (iff (= x bit1) A) // ---> // (= x (ite A bit1 bit0)) br_status apply_tamagotchi(expr * lhs, expr * rhs, expr_ref & result) { expr * x; unsigned val; if (is_eq_bit(lhs, x, val)) { result = m().mk_eq(x, m().mk_ite(rhs, m_bv_rw.mk_numeral(val, 1), m_bv_rw.mk_numeral(1-val, 1))); return BR_REWRITE2; } if (is_eq_bit(rhs, x, val)) { result = m().mk_eq(x, m().mk_ite(lhs, m_bv_rw.mk_numeral(val, 1), m_bv_rw.mk_numeral(1-val, 1))); return BR_REWRITE2; } return BR_FAILED; } br_status reduce_app_core(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { family_id fid = f->get_family_id(); if (fid == null_family_id) return BR_FAILED; br_status st = BR_FAILED; if (fid == m_b_rw.get_fid()) { decl_kind k = f->get_decl_kind(); if (k == OP_EQ) { // theory dispatch for = SASSERT(num == 2); family_id s_fid = m().get_sort(args[0])->get_family_id(); if (s_fid == m_a_rw.get_fid()) st = m_a_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_bv_rw.get_fid()) st = m_bv_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_dt_rw.get_fid()) st = m_dt_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_f_rw.get_fid()) st = m_f_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_ar_rw.get_fid()) st = m_ar_rw.mk_eq_core(args[0], args[1], result); if (st != BR_FAILED) return st; } if (k == OP_EQ || k == OP_IFF) { SASSERT(num == 2); st = apply_tamagotchi(args[0], args[1], result); if (st != BR_FAILED) return st; } return m_b_rw.mk_app_core(f, num, args, result); } if (fid == m_a_rw.get_fid()) return m_a_rw.mk_app_core(f, num, args, result); if (fid == m_bv_rw.get_fid()) return m_bv_rw.mk_app_core(f, num, args, result); if (fid == m_ar_rw.get_fid()) return m_ar_rw.mk_app_core(f, num, args, result); if (fid == m_dt_rw.get_fid()) return m_dt_rw.mk_app_core(f, num, args, result); if (fid == m_f_rw.get_fid()) return m_f_rw.mk_app_core(f, num, args, result); if (fid == m_dl_rw.get_fid()) return m_dl_rw.mk_app_core(f, num, args, result); if (fid == m_pb_rw.get_fid()) return m_pb_rw.mk_app_core(f, num, args, result); return BR_FAILED; } // auxiliary function for pull_ite_core expr * mk_eq_value(expr * lhs, expr * value) { SASSERT(m().is_value(value)); if (m().is_value(lhs)) { if (m().are_equal(lhs, value)) { return m().mk_true(); } else if (m().are_distinct(lhs, value)) { return m().mk_false(); } } return m().mk_eq(lhs, value); } template br_status pull_ite_core(func_decl * p, app * ite, app * value, expr_ref & result) { if (m().is_eq(p)) { result = m().mk_ite(ite->get_arg(0), mk_eq_value(ite->get_arg(1), value), mk_eq_value(ite->get_arg(2), value)); return BR_REWRITE2; } else { if (SWAP) { result = m().mk_ite(ite->get_arg(0), m().mk_app(p, value, ite->get_arg(1)), m().mk_app(p, value, ite->get_arg(2))); return BR_REWRITE2; } else { result = m().mk_ite(ite->get_arg(0), m().mk_app(p, ite->get_arg(1), value), m().mk_app(p, ite->get_arg(2), value)); return BR_REWRITE2; } } } // Return true if t is an ite-value-tree form defined as: // ite-value-tree := (ite c ) // subtree := value // | (ite c ) // bool is_ite_value_tree(expr * t) { if (!m().is_ite(t)) return false; ptr_buffer todo; todo.push_back(to_app(t)); while (!todo.empty()) { app * ite = todo.back(); todo.pop_back(); expr * arg1 = ite->get_arg(1); expr * arg2 = ite->get_arg(2); if (m().is_ite(arg1) && arg1->get_ref_count() == 1) // do not apply on shared terms, since it may blowup todo.push_back(to_app(arg1)); else if (!m().is_value(arg1)) return false; if (m().is_ite(arg2) && arg2->get_ref_count() == 1) // do not apply on shared terms, since it may blowup todo.push_back(to_app(arg2)); else if (!m().is_value(arg2)) return false; } return true; } br_status pull_ite(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { if (num == 2 && m().is_bool(f->get_range()) && !m().is_bool(args[0])) { if (m().is_ite(args[0])) { if (m().is_value(args[1])) return pull_ite_core(f, to_app(args[0]), to_app(args[1]), result); if (m().is_ite(args[1]) && to_app(args[0])->get_arg(0) == to_app(args[1])->get_arg(0)) { // (p (ite C A1 B1) (ite C A2 B2)) --> (ite (p A1 A2) (p B1 B2)) result = m().mk_ite(to_app(args[0])->get_arg(0), m().mk_app(f, to_app(args[0])->get_arg(1), to_app(args[1])->get_arg(1)), m().mk_app(f, to_app(args[0])->get_arg(2), to_app(args[1])->get_arg(2))); return BR_REWRITE2; } } if (m().is_ite(args[1]) && m().is_value(args[0])) return pull_ite_core(f, to_app(args[1]), to_app(args[0]), result); } family_id fid = f->get_family_id(); if (num == 2 && (fid == m().get_basic_family_id() || fid == m_a_rw.get_fid() || fid == m_bv_rw.get_fid())) { // (f v3 (ite c v1 v2)) --> (ite v (f v3 v1) (f v3 v2)) if (m().is_value(args[0]) && is_ite_value_tree(args[1])) return pull_ite_core(f, to_app(args[1]), to_app(args[0]), result); // (f (ite c v1 v2) v3) --> (ite v (f v1 v3) (f v2 v3)) if (m().is_value(args[1]) && is_ite_value_tree(args[0])) return pull_ite_core(f, to_app(args[0]), to_app(args[1]), result); } return BR_FAILED; } br_status pull_ite(expr_ref & result) { expr * t = result.get(); if (is_app(t)) { br_status st = pull_ite(to_app(t)->get_decl(), to_app(t)->get_num_args(), to_app(t)->get_args(), result); if (st != BR_FAILED) return st; } return BR_DONE; } bool is_arith_bv_app(expr * t) const { if (!is_app(t)) return false; family_id fid = to_app(t)->get_family_id(); return ((fid == m_a_rw.get_fid() && m_push_ite_arith) || (fid == m_bv_rw.get_fid() && m_push_ite_bv)); } bool get_neutral_elem(app * t, expr_ref & n) { family_id fid = t->get_family_id(); if (fid == m_a_rw.get_fid()) { switch (t->get_decl_kind()) { case OP_ADD: n = m_a_util.mk_numeral(rational(0), m().get_sort(t)); return true; case OP_MUL: n = m_a_util.mk_numeral(rational(1), m().get_sort(t)); return true; default: return false; } } if (fid == m_bv_rw.get_fid()) { switch (t->get_decl_kind()) { case OP_BADD: n = m_bv_util.mk_numeral(rational(0), m().get_sort(t)); return true; case OP_BMUL: n = m_bv_util.mk_numeral(rational(1), m().get_sort(t)); return true; default: return false; } } return false; } /** \brief Try to "unify" t1 and t2 Examples (+ 2 a) (+ 3 a) --> 2, 3, a (+ 2 a) a --> 2, 0, a ... */ bool unify_core(app * t1, expr * t2, expr_ref & new_t1, expr_ref & new_t2, expr_ref & c, bool & first) { if (t1->get_num_args() != 2) return false; expr * a1 = t1->get_arg(0); expr * b1 = t1->get_arg(1); if (t2 == b1) { if (get_neutral_elem(t1, new_t2)) { new_t1 = a1; c = b1; first = false; return true; } } else if (t2 == a1) { if (get_neutral_elem(t1, new_t2)) { new_t1 = b1; c = a1; first = true; return true; } } else if (is_app_of(t2, t1->get_decl()) && to_app(t2)->get_num_args() == 2) { expr * a2 = to_app(t2)->get_arg(0); expr * b2 = to_app(t2)->get_arg(1); if (b1 == b2) { new_t1 = a1; new_t2 = a2; c = b2; first = false; return true; } if (a1 == a2) { new_t1 = b1; new_t2 = b2; c = a1; first = true; return true; } if (t1->get_decl()->is_commutative()) { if (a1 == b2) { new_t1 = b1; new_t2 = a2; c = a1; first = true; // doesn't really matter for commutative ops. return true; } if (b1 == a2) { new_t1 = a1; new_t2 = b2; c = b1; first = false; // doesn't really matter for commutative ops. return true; } } } return false; } // Return true if t1 and t2 are of the form: // t + a1*x1 + ... + an*xn // t' + a1*x1 + ... + an*xn // Store t in new_t1, t' in new_t2 and (a1*x1 + ... + an*xn) in c. bool unify_add(app * t1, expr * t2, expr_ref & new_t1, expr_ref & new_t2, expr_ref & c) { unsigned num1 = t1->get_num_args(); expr * const * ms1 = t1->get_args(); if (num1 < 2) return false; unsigned num2; expr * const * ms2; if (m_a_util.is_add(t2)) { num2 = to_app(t2)->get_num_args(); ms2 = to_app(t2)->get_args(); } else { num2 = 1; ms2 = &t2; } if (num1 != num2 && num1 != num2 + 1 && num1 != num2 - 1) return false; new_t1 = 0; new_t2 = 0; expr_fast_mark1 visited1; expr_fast_mark2 visited2; for (unsigned i = 0; i < num1; i++) { expr * arg = ms1[i]; visited1.mark(arg); } for (unsigned i = 0; i < num2; i++) { expr * arg = ms2[i]; visited2.mark(arg); if (visited1.is_marked(arg)) continue; if (new_t2) return false; // more than one missing term new_t2 = arg; } for (unsigned i = 0; i < num1; i++) { expr * arg = ms1[i]; if (visited2.is_marked(arg)) continue; if (new_t1) return false; // more than one missing term new_t1 = arg; } // terms matched... bool is_int = m_a_util.is_int(t1); if (!new_t1) new_t1 = m_a_util.mk_numeral(rational(0), is_int); if (!new_t2) new_t2 = m_a_util.mk_numeral(rational(0), is_int); // mk common part ptr_buffer args; for (unsigned i = 0; i < num1; i++) { expr * arg = ms1[i]; if (arg == new_t1.get()) continue; args.push_back(arg); } SASSERT(!args.empty()); if (args.size() == 1) c = args[0]; else c = m_a_util.mk_add(args.size(), args.c_ptr()); return true; } bool unify(expr * t1, expr * t2, func_decl * & f, expr_ref & new_t1, expr_ref & new_t2, expr_ref & c, bool & first) { #if 0 // Did not work for ring benchmarks // Hack for handling more complex cases of + apps // such as (+ 2 t1 t2 t3) and (+ 3 t3 t2 t1) if (m_a_util.is_add(t1)) { first = true; // doesn't matter for AC ops f = to_app(t1)->get_decl(); if (unify_add(to_app(t1), t2, new_t1, new_t2, c)) return true; } if (m_a_util.is_add(t2)) { first = true; // doesn't matter for AC ops f = to_app(t2)->get_decl(); if (unify_add(to_app(t2), t1, new_t2, new_t1, c)) return true; } #endif if (is_arith_bv_app(t1)) { f = to_app(t1)->get_decl(); return unify_core(to_app(t1), t2, new_t1, new_t2, c, first); } else if (is_arith_bv_app(t2)) { f = to_app(t2)->get_decl(); return unify_core(to_app(t2), t1, new_t2, new_t1, c, first); } else { return false; } } // Apply transformations of the form // // (ite c (+ k1 a) (+ k2 a)) --> (+ (ite c k1 k2) a) // (ite c (* k1 a) (* k2 a)) --> (* (ite c k1 k2) a) // // These transformations are useful for bit-vector problems, since // they will minimize the number of adders/multipliers/etc br_status push_ite(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { if (!m().is_ite(f)) return BR_FAILED; expr * c = args[0]; expr * t = args[1]; expr * e = args[2]; func_decl * f_prime = 0; expr_ref new_t(m()), new_e(m()), common(m()); bool first; TRACE("push_ite", tout << "unifying:\n" << mk_ismt2_pp(t, m()) << "\n" << mk_ismt2_pp(e, m()) << "\n";); if (unify(t, e, f_prime, new_t, new_e, common, first)) { if (first) result = m().mk_app(f_prime, common, m().mk_ite(c, new_t, new_e)); else result = m().mk_app(f_prime, m().mk_ite(c, new_t, new_e), common); return BR_DONE; } TRACE("push_ite", tout << "failed\n";); return BR_FAILED; } br_status push_ite(expr_ref & result) { expr * t = result.get(); if (m().is_ite(t)) { br_status st = push_ite(to_app(t)->get_decl(), to_app(t)->get_num_args(), to_app(t)->get_args(), result); if (st != BR_FAILED) return st; } return BR_DONE; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = 0; br_status st = reduce_app_core(f, num, args, result); if (st != BR_DONE && st != BR_FAILED) { CTRACE("th_rewriter_step", st != BR_FAILED, tout << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << "\n"; tout << "---------->\n" << mk_ismt2_pp(result, m()) << "\n";); return st; } if (m_push_ite_bv || m_push_ite_arith) { if (st == BR_FAILED) st = push_ite(f, num, args, result); else st = push_ite(result); } if (m_pull_cheap_ite) { if (st == BR_FAILED) st = pull_ite(f, num, args, result); else st = pull_ite(result); } CTRACE("th_rewriter_step", st != BR_FAILED, tout << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << "\n"; tout << "---------->\n" << mk_ismt2_pp(result, m()) << "\n";); return st; } bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { quantifier_ref q1(m()); proof * p1 = 0; if (is_quantifier(new_body) && to_quantifier(new_body)->is_forall() == old_q->is_forall() && !old_q->has_patterns() && !to_quantifier(new_body)->has_patterns()) { quantifier * nested_q = to_quantifier(new_body); ptr_buffer sorts; buffer names; sorts.append(old_q->get_num_decls(), old_q->get_decl_sorts()); names.append(old_q->get_num_decls(), old_q->get_decl_names()); sorts.append(nested_q->get_num_decls(), nested_q->get_decl_sorts()); names.append(nested_q->get_num_decls(), nested_q->get_decl_names()); q1 = m().mk_quantifier(old_q->is_forall(), sorts.size(), sorts.c_ptr(), names.c_ptr(), nested_q->get_expr(), std::min(old_q->get_weight(), nested_q->get_weight()), old_q->get_qid(), old_q->get_skid(), 0, 0, 0, 0); SASSERT(is_well_sorted(m(), q1)); if (m().proofs_enabled()) { SASSERT(old_q->get_expr() == new_body); p1 = m().mk_pull_quant(old_q, q1); } } else { ptr_buffer new_patterns_buf; ptr_buffer new_no_patterns_buf; new_patterns_buf.append(old_q->get_num_patterns(), new_patterns); new_no_patterns_buf.append(old_q->get_num_no_patterns(), new_no_patterns); remove_duplicates(new_patterns_buf); remove_duplicates(new_no_patterns_buf); q1 = m().update_quantifier(old_q, new_patterns_buf.size(), new_patterns_buf.c_ptr(), new_no_patterns_buf.size(), new_no_patterns_buf.c_ptr(), new_body); TRACE("reduce_quantifier", tout << mk_ismt2_pp(old_q, m()) << "\n----->\n" << mk_ismt2_pp(q1, m()) << "\n";); SASSERT(is_well_sorted(m(), q1)); } elim_unused_vars(m(), q1, result); TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << mk_ismt2_pp(result, m()) << "\n";); result_pr = 0; if (m().proofs_enabled()) { proof * p2 = 0; if (q1.get() != result.get()) p2 = m().mk_elim_unused_vars(q1, result); result_pr = m().mk_transitivity(p1, p2); } return true; } th_rewriter_cfg(ast_manager & m, params_ref const & p): m_b_rw(m, p), m_a_rw(m, p), m_bv_rw(m, p), m_ar_rw(m, p), m_dt_rw(m), m_f_rw(m, p), m_dl_rw(m), m_pb_rw(m), m_a_util(m), m_bv_util(m), m_used_dependencies(m), m_subst(0) { updt_local_params(p); } void set_substitution(expr_substitution * s) { reset(); m_subst = s; } void reset() { m_subst = 0; } bool get_subst(expr * s, expr * & t, proof * & pr) { if (m_subst == 0) return false; expr_dependency * d = 0; if (m_subst->find(s, t, pr, d)) { m_used_dependencies = m().mk_join(m_used_dependencies, d); return true; } return false; } void set_cancel(bool f) { m_a_rw.set_cancel(f); } }; template class rewriter_tpl; struct th_rewriter::imp : public rewriter_tpl { th_rewriter_cfg m_cfg; imp(ast_manager & m, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { } }; th_rewriter::th_rewriter(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } ast_manager & th_rewriter::m() const { return m_imp->m(); } void th_rewriter::updt_params(params_ref const & p) { m_params = p; m_imp->cfg().updt_params(p); } void th_rewriter::get_param_descrs(param_descrs & r) { bool_rewriter::get_param_descrs(r); arith_rewriter::get_param_descrs(r); bv_rewriter::get_param_descrs(r); array_rewriter::get_param_descrs(r); rewriter_params::collect_param_descrs(r); } th_rewriter::~th_rewriter() { dealloc(m_imp); } unsigned th_rewriter::get_cache_size() const { return m_imp->get_cache_size(); } unsigned th_rewriter::get_num_steps() const { return m_imp->get_num_steps(); } void th_rewriter::set_cancel(bool f) { #pragma omp critical (th_rewriter) { m_imp->set_cancel(f); m_imp->cfg().set_cancel(f); } } void th_rewriter::cleanup() { ast_manager & m = m_imp->m(); #pragma omp critical (th_rewriter) { dealloc(m_imp); m_imp = alloc(imp, m, m_params); } } void th_rewriter::reset() { m_imp->reset(); m_imp->cfg().reset(); } void th_rewriter::operator()(expr_ref & term) { expr_ref result(term.get_manager()); m_imp->operator()(term, result); term = result; } void th_rewriter::operator()(expr * t, expr_ref & result) { m_imp->operator()(t, result); } void th_rewriter::operator()(expr * t, expr_ref & result, proof_ref & result_pr) { m_imp->operator()(t, result, result_pr); } void th_rewriter::operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result) { m_imp->operator()(n, num_bindings, bindings, result); } void th_rewriter::set_substitution(expr_substitution * s) { m_imp->reset(); // reset the cache m_imp->cfg().set_substitution(s); } expr_dependency * th_rewriter::get_used_dependencies() { return m_imp->cfg().m_used_dependencies; } void th_rewriter::reset_used_dependencies() { if (get_used_dependencies() != 0) { set_substitution(m_imp->cfg().m_subst); // reset cache preserving subst m_imp->cfg().m_used_dependencies = 0; } } z3-4.4.0/src/ast/rewriter/datatype_rewriter.h0000644000175000017500000000135112540347414020775 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: datatype_rewriter.h Abstract: Basic rewriting rules for Datatypes. Author: Leonardo (leonardo) 2011-04-06 Notes: --*/ #ifndef _DATATYPE_REWRITER_H_ #define _DATATYPE_REWRITER_H_ #include"datatype_decl_plugin.h" #include"rewriter_types.h" class datatype_rewriter { datatype_util m_util; public: datatype_rewriter(ast_manager & m):m_util(m) {} ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result); }; #endif z3-4.4.0/src/ast/rewriter/arith_rewriter.h0000644000175000017500000001643612540347414020303 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: arith_rewriter.h Abstract: Basic rewriting rules for arithmetic Author: Leonardo (leonardo) 2011-04-10 Notes: --*/ #ifndef _ARITH_REWRITER_H_ #define _ARITH_REWRITER_H_ #include"poly_rewriter.h" #include"arith_decl_plugin.h" class arith_rewriter_core { protected: typedef rational numeral; arith_util m_util; bool m_expand_power; bool m_mul2power; bool m_expand_tan; ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } bool is_numeral(expr * n) const { return m_util.is_numeral(n); } bool is_numeral(expr * n, numeral & r) const { return m_util.is_numeral(n, r); } bool is_zero(expr * n) const { return m_util.is_zero(n); } bool is_minus_one(expr * n) const { return m_util.is_minus_one(n); } void normalize(numeral & c, sort * s) {} app * mk_numeral(numeral const & r, sort * s) { return m_util.mk_numeral(r, s); } decl_kind add_decl_kind() const { return OP_ADD; } decl_kind mul_decl_kind() const { return OP_MUL; } bool use_power() const { return m_mul2power && !m_expand_power; } decl_kind power_decl_kind() const { return OP_POWER; } public: arith_rewriter_core(ast_manager & m):m_util(m) {} }; class arith_rewriter : public poly_rewriter { bool m_arith_lhs; bool m_gcd_rounding; bool m_eq2ineq; bool m_elim_to_real; bool m_push_to_real; bool m_anum_simp; bool m_elim_rem; unsigned m_max_degree; void get_coeffs_gcd(expr * t, numeral & g, bool & first, unsigned & num_consts); enum const_treatment { CT_FLOOR, CT_CEIL, CT_FALSE }; bool div_polynomial(expr * t, numeral const & g, const_treatment ct, expr_ref & result); enum op_kind { LE, GE, EQ }; static op_kind inv(op_kind k) { return k == LE ? GE : (k == GE ? LE : EQ); } bool is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref & result); br_status mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kind, expr_ref & result); bool elim_to_real_var(expr * var, expr_ref & new_var); bool elim_to_real_mon(expr * monomial, expr_ref & new_monomial); bool elim_to_real_pol(expr * p, expr_ref & new_p); bool elim_to_real(expr * arg1, expr * arg2, expr_ref & new_arg1, expr_ref & new_arg2); void updt_local_params(params_ref const & p); bool is_anum_simp_target(unsigned num_args, expr * const * args); br_status mk_div_irrat_rat(expr * arg1, expr * arg2, expr_ref & result); br_status mk_div_rat_irrat(expr * arg1, expr * arg2, expr_ref & result); br_status mk_div_irrat_irrat(expr * arg1, expr * arg2, expr_ref & result); bool is_reduce_power_target(expr * arg, bool is_eq); expr * reduce_power(expr * arg, bool is_eq); br_status reduce_power(expr * arg1, expr * arg2, op_kind kind, expr_ref & result); bool is_pi_multiple(expr * t, rational & k); bool is_pi_offset(expr * t, rational & k, expr * & m); bool is_2_pi_integer(expr * t); bool is_2_pi_integer_offset(expr * t, expr * & m); bool is_pi_integer(expr * t); bool is_pi_integer_offset(expr * t, expr * & m); expr * mk_sin_value(rational const & k); app * mk_sqrt(rational const & k); public: arith_rewriter(ast_manager & m, params_ref const & p = params_ref()): poly_rewriter(m, p) { updt_local_params(p); } void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_app_core(f, num_args, args, result) == BR_FAILED) result = m().mk_app(f, num_args, args); } br_status mk_eq_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_le_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_lt_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_ge_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_gt_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_add_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_mul_core(unsigned num_args, expr * const * args, expr_ref & result); void mk_eq(expr * arg1, expr * arg2, expr_ref & result) { if (mk_eq_core(arg1, arg2, result) == BR_FAILED) result = m_util.mk_eq(arg1, arg2); } void mk_le(expr * arg1, expr * arg2, expr_ref & result) { if (mk_le_core(arg1, arg2, result) == BR_FAILED) result = m_util.mk_le(arg1, arg2); } void mk_lt(expr * arg1, expr * arg2, expr_ref & result) { mk_lt_core(arg1, arg2, result); } void mk_ge(expr * arg1, expr * arg2, expr_ref & result) { if (mk_ge_core(arg1, arg2, result) == BR_FAILED) result = m_util.mk_ge(arg1, arg2); } void mk_gt(expr * arg1, expr * arg2, expr_ref & result) { mk_gt_core(arg1, arg2, result); } br_status mk_abs_core(expr * arg, expr_ref & result); br_status mk_div_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_idiv_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_mod_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_rem_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_power_core(expr* arg1, expr* arg2, expr_ref & result); void mk_div(expr * arg1, expr * arg2, expr_ref & result) { if (mk_div_core(arg1, arg2, result) == BR_FAILED) result = m().mk_app(get_fid(), OP_DIV, arg1, arg2); } void mk_idiv(expr * arg1, expr * arg2, expr_ref & result) { if (mk_idiv_core(arg1, arg2, result) == BR_FAILED) result = m().mk_app(get_fid(), OP_IDIV, arg1, arg2); } void mk_mod(expr * arg1, expr * arg2, expr_ref & result) { if (mk_mod_core(arg1, arg2, result) == BR_FAILED) result = m().mk_app(get_fid(), OP_MOD, arg1, arg2); } void mk_rem(expr * arg1, expr * arg2, expr_ref & result) { if (mk_rem_core(arg1, arg2, result) == BR_FAILED) result = m().mk_app(get_fid(), OP_REM, arg1, arg2); } br_status mk_to_int_core(expr * arg, expr_ref & result); br_status mk_to_real_core(expr * arg, expr_ref & result); void mk_to_int(expr * arg, expr_ref & result) { if (mk_to_int_core(arg, result) == BR_FAILED) result = m().mk_app(get_fid(), OP_TO_INT, 1, &arg); } void mk_to_real(expr * arg, expr_ref & result) { if (mk_to_real_core(arg, result) == BR_FAILED) result = m().mk_app(get_fid(), OP_TO_REAL, 1, &arg); } br_status mk_is_int(expr * arg, expr_ref & result); void set_cancel(bool f); br_status mk_sin_core(expr * arg, expr_ref & result); br_status mk_cos_core(expr * arg, expr_ref & result); br_status mk_tan_core(expr * arg, expr_ref & result); br_status mk_asin_core(expr * arg, expr_ref & result); br_status mk_acos_core(expr * arg, expr_ref & result); br_status mk_atan_core(expr * arg, expr_ref & result); br_status mk_sinh_core(expr * arg, expr_ref & result); br_status mk_cosh_core(expr * arg, expr_ref & result); br_status mk_tanh_core(expr * arg, expr_ref & result); }; #endif z3-4.4.0/src/ast/rewriter/bool_rewriter_params.pyg0000644000175000017500000000210512540347414022026 0ustar michaelmichaeldef_module_params(module_name='rewriter', class_name='bool_rewriter_params', export=True, params=(("ite_extra_rules", BOOL, False, "extra ite simplifications, these additional simplifications may reduce size locally but increase globally"), ("flat", BOOL, True, "create nary applications for and,or,+,*,bvadd,bvmul,bvand,bvor,bvxor"), ("elim_and", BOOL, False, "conjunctions are rewritten using negation and disjunctions"), ("local_ctx", BOOL, False, "perform local (i.e., cheap) context simplifications"), ("local_ctx_limit", UINT, UINT_MAX, "limit for applying local context simplifier"), ("blast_distinct", BOOL, False, "expand a distinct predicate into a quadratic number of disequalities"), ("blast_distinct_threshold", UINT, UINT_MAX, "when blast_distinct is true, only distinct expressions with less than this number of arguments are blasted") )) z3-4.4.0/src/ast/rewriter/fpa_rewriter.h0000644000175000017500000000705212540347414017734 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: float_rewriter.h Abstract: Basic rewriting rules for floating point numbers. Author: Leonardo (leonardo) 2012-02-02 Notes: --*/ #ifndef _FLOAT_REWRITER_H_ #define _FLOAT_REWRITER_H_ #include"ast.h" #include"rewriter.h" #include"params.h" #include"fpa_decl_plugin.h" #include"mpf.h" class fpa_rewriter { fpa_util m_util; mpf_manager & m_fm; bool m_hi_fp_unspecified; app * mk_eq_nan(expr * arg); app * mk_neq_nan(expr * arg); public: fpa_rewriter(ast_manager & m, params_ref const & p = params_ref()); ~fpa_rewriter(); ast_manager & m() const { return m_util.m(); } family_id get_fid() const { return m_util.get_fid(); } void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); br_status mk_eq_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_add(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); br_status mk_sub(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); br_status mk_mul(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); br_status mk_div(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); br_status mk_neg(expr * arg1, expr_ref & result); br_status mk_rem(expr * arg1, expr * arg2, expr_ref & result); br_status mk_abs(expr * arg1, expr_ref & result); br_status mk_min(expr * arg1, expr * arg2, expr_ref & result); br_status mk_max(expr * arg1, expr * arg2, expr_ref & result); br_status mk_fma(expr * arg1, expr * arg2, expr * arg3, expr * arg4, expr_ref & result); br_status mk_sqrt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_round_to_integral(expr * arg1, expr * arg2, expr_ref & result); br_status mk_float_eq(expr * arg1, expr * arg2, expr_ref & result); br_status mk_lt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_gt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_le(expr * arg1, expr * arg2, expr_ref & result); br_status mk_ge(expr * arg1, expr * arg2, expr_ref & result); br_status mk_is_zero(expr * arg1, expr_ref & result); br_status mk_is_nzero(expr * arg1, expr_ref & result); br_status mk_is_pzero(expr * arg1, expr_ref & result); br_status mk_is_nan(expr * arg1, expr_ref & result); br_status mk_is_inf(expr * arg1, expr_ref & result); br_status mk_is_normal(expr * arg1, expr_ref & result); br_status mk_is_subnormal(expr * arg1, expr_ref & result); br_status mk_is_negative(expr * arg1, expr_ref & result); br_status mk_is_positive(expr * arg1, expr_ref & result); br_status mk_to_ieee_bv(expr * arg1, expr_ref & result); br_status mk_to_fp(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); br_status mk_to_fp_unsigned(func_decl * f, expr * arg1, expr * arg2, expr_ref & result); br_status mk_fp(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); br_status mk_to_fp_unsigned(expr * arg1, expr * arg2, expr_ref & result); br_status mk_to_ubv(func_decl * f, expr * arg1, expr * arg2, expr_ref & result); br_status mk_to_sbv(func_decl * f, expr * arg1, expr * arg2, expr_ref & result); br_status mk_to_real(expr * arg, expr_ref & result); br_status mk_to_ubv_unspecified(func_decl * f, expr_ref & result); br_status mk_to_sbv_unspecified(func_decl * f, expr_ref & result); br_status mk_to_real_unspecified(expr_ref & result); }; #endif z3-4.4.0/src/ast/rewriter/expr_replacer.h0000644000175000017500000000310012540347414020064 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: expr_replacer.h Abstract: Abstract (functor) for replacing expressions. Author: Leonardo (leonardo) 2011-04-29 Notes: --*/ #ifndef _EXPR_REPLACER_H_ #define _EXPR_REPLACER_H_ #include"ast.h" #include"expr_substitution.h" #include"params.h" /** \brief Abstract interface for functors that replace constants with expressions. */ class expr_replacer { struct scoped_set_subst; public: virtual ~expr_replacer() {} virtual ast_manager & m() const = 0; virtual void set_substitution(expr_substitution * s) = 0; virtual void operator()(expr * t, expr_ref & result, proof_ref & result_pr, expr_dependency_ref & deps) = 0; virtual void operator()(expr * t, expr_ref & result, proof_ref & result_pr); virtual void operator()(expr * t, expr_ref & result); virtual void operator()(expr_ref & t) { expr_ref s(t, m()); (*this)(s, t); } void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } virtual void set_cancel(bool f) = 0; virtual unsigned get_num_steps() const { return 0; } virtual void reset() = 0; void apply_substitution(expr * s, expr * def, proof * def_pr, expr_ref & t); void apply_substitution(expr * s, expr * def, expr_ref & t); }; /** \brief Create a vanilla replacer. It just applies the substitution. */ expr_replacer * mk_default_expr_replacer(ast_manager & m); /** \brief Apply substitution and simplify. */ expr_replacer * mk_expr_simp_replacer(ast_manager & m, params_ref const & p = params_ref()); #endif z3-4.4.0/src/ast/rewriter/rewriter_types.h0000644000175000017500000000245612540347414020335 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: rewriter_types.h Abstract: Lean and mean rewriter Author: Leonardo (leonardo) 2011-04-10 Notes: --*/ #ifndef _REWRITER_TYPES_H_ #define _REWRITER_TYPES_H_ #include"z3_exception.h" #include"common_msgs.h" /** \brief Builtin rewrite result status */ enum br_status { BR_REWRITE1, // rewrite the result (bounded by depth 1) BR_REWRITE2, // rewrite the result (bounded by depth 2) BR_REWRITE3, // rewrite the result (bounded by depth 3) BR_REWRITE_FULL, // rewrite the result unbounded BR_DONE, // done, the result is simplified BR_FAILED // no builtin rewrite is available }; #define RW_UNBOUNDED_DEPTH 3 inline br_status unsigned2br_status(unsigned u) { br_status r = u >= RW_UNBOUNDED_DEPTH ? BR_REWRITE_FULL : static_cast(u); SASSERT((u == 0) == (r == BR_REWRITE1)); SASSERT((u == 1) == (r == BR_REWRITE2)); SASSERT((u == 2) == (r == BR_REWRITE3)); SASSERT((u >= 3) == (r == BR_REWRITE_FULL)); return r; } class rewriter_exception : public default_exception { public: rewriter_exception(char const * msg):default_exception(msg) {} }; #endif z3-4.4.0/src/ast/rewriter/mk_simplified_app.h0000644000175000017500000000127712540347414020722 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mk_simplified_app.h Abstract: Functor for creating new simplified applications Author: Leonardo (leonardo) 2011-06-06 Notes: --*/ #ifndef _MK_SIMPLIFIED_APP_H_ #define _MK_SIMPLIFIED_APP_H_ #include"ast.h" #include"params.h" #include"rewriter_types.h" class mk_simplified_app { struct imp; imp * m_imp; public: mk_simplified_app(ast_manager & m, params_ref const & p = params_ref()); ~mk_simplified_app(); br_status mk_core(func_decl * decl, unsigned num, expr * const * args, expr_ref & result); void operator()(func_decl * decl, unsigned num, expr * const * args, expr_ref & result); }; #endif z3-4.4.0/src/ast/rewriter/factor_rewriter.h0000644000175000017500000000440212540347414020440 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: factor_rewriter.h Abstract: Rewriting utilities for factoring polynomials in equations, and inequalities. Author: Nikolaj (nbjorner) 2011-19-05 Notes: --*/ #ifndef _FACTOR_REWRITER_H_ #define _FACTOR_REWRITER_H_ #include"ast.h" #include"rewriter.h" #include"arith_decl_plugin.h" class factor_rewriter { typedef obj_map powers_t; ast_manager & m_manager; arith_util m_arith; powers_t m_powers; vector > m_adds; vector > m_muls; expr_ref_vector m_factors; public: factor_rewriter(ast_manager & m); ast_manager & m() const { return m_manager; } arith_util & a() { return m_arith; } br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); private: br_status mk_eq(expr * arg1, expr * arg2, expr_ref & result); br_status mk_le(expr * arg1, expr * arg2, expr_ref & result); br_status mk_lt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_ge(expr * a1, expr * a2, expr_ref & r) { return mk_le(a2,a1,r); } br_status mk_gt(expr * a1, expr * a2, expr_ref & r) { return mk_lt(a2,a1,r); } void mk_adds(expr* arg1, expr* arg2); void mk_muls(); void mk_expand_muls(ptr_vector& muls); void collect_powers(); bool extract_factors(); bool even(unsigned n) const { return 0 == (n & 0x1); } void mk_is_negative(expr_ref& result, expr_ref_vector& eqs); }; struct factor_rewriter_cfg : public default_rewriter_cfg { factor_rewriter m_r; bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = 0; return m_r.mk_app_core(f, num, args, result); } factor_rewriter_cfg(ast_manager & m):m_r(m) {} }; class factor_rewriter_star : public rewriter_tpl { factor_rewriter_cfg m_cfg; public: factor_rewriter_star(ast_manager & m): rewriter_tpl(m, false, m_cfg), m_cfg(m) {} }; #endif z3-4.4.0/src/ast/rewriter/der.cpp0000644000175000017500000003471712540347414016360 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: der.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-27. Revision History: Christoph Wintersteiger, 2010-03-30: Added Destr. Multi-Equality Resolution --*/ #include"der.h" #include"occurs.h" #include"for_each_expr.h" #include"rewriter_def.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"ast_smt2_pp.h" static bool is_var(expr * e, unsigned num_decls) { return is_var(e) && to_var(e)->get_idx() < num_decls; } static bool is_neg_var(ast_manager & m, expr * e, unsigned num_decls) { return m.is_not(e) && is_var(to_app(e)->get_arg(0)) && to_var(to_app(e)->get_arg(0))->get_idx() < num_decls; } /** \brief Return true if \c e is of the form (not (= VAR t)) or (not (iff VAR t)) or (iff VAR t) or (iff (not VAR) t) or (VAR IDX) or (not (VAR IDX)). The last case can be viewed */ bool der::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) { // (not (= VAR t)) and (not (iff VAR t)) cases if (m_manager.is_not(e) && (m_manager.is_eq(to_app(e)->get_arg(0)) || m_manager.is_iff(to_app(e)->get_arg(0)))) { app * eq = to_app(to_app(e)->get_arg(0)); SASSERT(m_manager.is_eq(eq) || m_manager.is_iff(eq)); expr * lhs = eq->get_arg(0); expr * rhs = eq->get_arg(1); if (!is_var(lhs, num_decls) && !is_var(rhs, num_decls)) return false; if (!is_var(lhs, num_decls)) std::swap(lhs, rhs); SASSERT(is_var(lhs, num_decls)); // Remark: Occurs check is not necessary here... the top-sort procedure will check for cycles... // if (occurs(lhs, rhs)) { // return false; // } v = to_var(lhs); t = rhs; TRACE("der", tout << mk_pp(e, m_manager) << "\n";); return true; } // (iff VAR t) and (iff (not VAR) t) cases else if (m_manager.is_iff(e)) { expr * lhs = to_app(e)->get_arg(0); expr * rhs = to_app(e)->get_arg(1); // (iff VAR t) case if (is_var(lhs, num_decls) || is_var(rhs, num_decls)) { if (!is_var(lhs, num_decls)) std::swap(lhs, rhs); SASSERT(is_var(lhs, num_decls)); // Remark: Occurs check is not necessary here... the top-sort procedure will check for cycles... // if (occurs(lhs, rhs)) { // return false; // } v = to_var(lhs); t = m_manager.mk_not(rhs); m_new_exprs.push_back(t); TRACE("der", tout << mk_pp(e, m_manager) << "\n";); return true; } // (iff (not VAR) t) case else if (is_neg_var(m_manager, lhs, num_decls) || is_neg_var(m_manager, rhs, num_decls)) { if (!is_neg_var(m_manager, lhs, num_decls)) std::swap(lhs, rhs); SASSERT(is_neg_var(m_manager, lhs, num_decls)); expr * lhs_var = to_app(lhs)->get_arg(0); // Remark: Occurs check is not necessary here... the top-sort procedure will check for cycles... // if (occurs(lhs_var, rhs)) { // return false; // } v = to_var(lhs_var); t = rhs; TRACE("der", tout << mk_pp(e, m_manager) << "\n";); return true; } else { return false; } } // VAR != false case else if (is_var(e, num_decls)) { t = m_manager.mk_false(); v = to_var(e); TRACE("der", tout << mk_pp(e, m_manager) << "\n";); return true; } // VAR != true case else if (is_neg_var(m_manager, e, num_decls)) { t = m_manager.mk_true(); v = to_var(to_app(e)->get_arg(0)); TRACE("der", tout << mk_pp(e, m_manager) << "\n";); return true; } else { return false; } } void der::operator()(quantifier * q, expr_ref & r, proof_ref & pr) { bool reduced = false; pr = 0; r = q; TRACE("der", tout << mk_pp(q, m_manager) << "\n";); // Keep applying it until r doesn't change anymore do { proof_ref curr_pr(m_manager); q = to_quantifier(r); reduce1(q, r, curr_pr); if (q != r) reduced = true; if (m_manager.proofs_enabled()) { pr = m_manager.mk_transitivity(pr, curr_pr); } } while (q != r && is_quantifier(r)); // Eliminate variables that have become unused if (reduced && is_forall(r)) { quantifier * q = to_quantifier(r); elim_unused_vars(m_manager, q, r); if (m_manager.proofs_enabled()) { proof * p1 = m_manager.mk_elim_unused_vars(q, r); pr = m_manager.mk_transitivity(pr, p1); } } m_new_exprs.reset(); } void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) { if (!is_forall(q)) { pr = 0; r = q; return; } expr * e = q->get_expr(); unsigned num_decls = q->get_num_decls(); var * v = 0; expr_ref t(m_manager); if (m_manager.is_or(e)) { unsigned num_args = to_app(e)->get_num_args(); unsigned i = 0; unsigned diseq_count = 0; unsigned largest_vinx = 0; m_map.reset(); m_pos2var.reset(); m_inx2var.reset(); m_pos2var.reserve(num_args, -1); // Find all disequalities for (; i < num_args; i++) { if (is_var_diseq(to_app(e)->get_arg(i), num_decls, v, t)) { unsigned idx = v->get_idx(); if(m_map.get(idx, 0) == 0) { m_map.reserve(idx + 1, 0); m_inx2var.reserve(idx + 1, 0); m_map[idx] = t; m_inx2var[idx] = v; m_pos2var[i] = idx; diseq_count++; largest_vinx = (idx>largest_vinx) ? idx : largest_vinx; } } } if (diseq_count > 0) { get_elimination_order(); SASSERT(m_order.size() <= diseq_count); // some might be missing because of cycles if (!m_order.empty()) { create_substitution(largest_vinx + 1); apply_substitution(q, r); } } else { TRACE("der_bug", tout << "Did not find any diseq\n" << mk_pp(q, m_manager) << "\n";); r = q; } } // Remark: get_elimination_order/top-sort checks for cycles, but it is not invoked for unit clauses. // So, we must perform a occurs check here. else if (is_var_diseq(e, num_decls, v, t) && !occurs(v, t)) { r = m_manager.mk_false(); } else r = q; if (m_manager.proofs_enabled()) { pr = r == q ? 0 : m_manager.mk_der(q, r); } } void der_sort_vars(ptr_vector & vars, ptr_vector & definitions, unsigned_vector & order) { order.reset(); // eliminate self loops, and definitions containing quantifiers. bool found = false; for (unsigned i = 0; i < definitions.size(); i++) { var * v = vars[i]; expr * t = definitions[i]; if (t == 0 || has_quantifiers(t) || occurs(v, t)) definitions[i] = 0; else found = true; // found at least one candidate } if (!found) return; typedef std::pair frame; svector todo; expr_fast_mark1 visiting; expr_fast_mark2 done; unsigned vidx, num; for (unsigned i = 0; i < definitions.size(); i++) { if (definitions[i] == 0) continue; var * v = vars[i]; SASSERT(v->get_idx() == i); SASSERT(todo.empty()); todo.push_back(frame(v, 0)); while (!todo.empty()) { start: frame & fr = todo.back(); expr * t = fr.first; if (t->get_ref_count() > 1 && done.is_marked(t)) { todo.pop_back(); continue; } switch (t->get_kind()) { case AST_VAR: vidx = to_var(t)->get_idx(); if (fr.second == 0) { CTRACE("der_bug", vidx >= definitions.size(), tout << "vidx: " << vidx << "\n";); // Remark: The size of definitions may be smaller than the number of variables occuring in the quantified formula. if (definitions.get(vidx, 0) != 0) { if (visiting.is_marked(t)) { // cycle detected: remove t visiting.reset_mark(t); definitions[vidx] = 0; } else { visiting.mark(t); fr.second = 1; todo.push_back(frame(definitions[vidx], 0)); goto start; } } } else { SASSERT(fr.second == 1); if (definitions.get(vidx, 0) != 0) { visiting.reset_mark(t); order.push_back(vidx); } else { // var was removed from the list of candidate vars to elim cycle // do nothing } } if (t->get_ref_count() > 1) done.mark(t); todo.pop_back(); break; case AST_QUANTIFIER: UNREACHABLE(); todo.pop_back(); break; case AST_APP: num = to_app(t)->get_num_args(); while (fr.second < num) { expr * arg = to_app(t)->get_arg(fr.second); fr.second++; if (arg->get_ref_count() > 1 && done.is_marked(arg)) continue; todo.push_back(frame(arg, 0)); goto start; } if (t->get_ref_count() > 1) done.mark(t); todo.pop_back(); break; default: UNREACHABLE(); todo.pop_back(); break; } } } } void der::get_elimination_order() { m_order.reset(); TRACE("top_sort", tout << "DEFINITIONS: " << std::endl; for(unsigned i = 0; i < m_map.size(); i++) if(m_map[i]) tout << "VAR " << i << " = " << mk_pp(m_map[i], m_manager) << std::endl; ); // der::top_sort ts(m_manager); der_sort_vars(m_inx2var, m_map, m_order); TRACE("der", tout << "Elimination m_order:" << std::endl; for(unsigned i=0; iget_expr(); unsigned num_args=to_app(e)->get_num_args(); // get a new expression m_new_args.reset(); for(unsigned i = 0; i < num_args; i++) { int x = m_pos2var[i]; if (x != -1 && m_map[x] != 0) continue; // this is a disequality with definition (vanishes) m_new_args.push_back(to_app(e)->get_arg(i)); } unsigned sz = m_new_args.size(); expr_ref t(m_manager); t = (sz == 1) ? m_new_args[0] : m_manager.mk_or(sz, m_new_args.c_ptr()); expr_ref new_e(m_manager); m_subst(t, m_subst_map.size(), m_subst_map.c_ptr(), new_e); // don't forget to update the quantifier patterns expr_ref_buffer new_patterns(m_manager); expr_ref_buffer new_no_patterns(m_manager); for (unsigned j = 0; j < q->get_num_patterns(); j++) { expr_ref new_pat(m_manager); m_subst(q->get_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_pat); new_patterns.push_back(new_pat); } for (unsigned j = 0; j < q->get_num_no_patterns(); j++) { expr_ref new_nopat(m_manager); m_subst(q->get_no_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_nopat); new_no_patterns.push_back(new_nopat); } r = m_manager.update_quantifier(q, new_patterns.size(), new_patterns.c_ptr(), new_no_patterns.size(), new_no_patterns.c_ptr(), new_e); } struct der_rewriter_cfg : public default_rewriter_cfg { der m_der; der_rewriter_cfg(ast_manager & m):m_der(m) {} ast_manager & m() const { return m_der.m(); } bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { quantifier_ref q1(m()); q1 = m().update_quantifier(old_q, old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns, new_body); m_der(q1, result, result_pr); return true; } }; template class rewriter_tpl; struct der_rewriter::imp : public rewriter_tpl { der_rewriter_cfg m_cfg; imp(ast_manager & m): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m) { } }; der_rewriter::der_rewriter(ast_manager & m) { m_imp = alloc(imp, m); } der_rewriter::~der_rewriter() { dealloc(m_imp); } ast_manager & der_rewriter::m() const { return m_imp->m(); } void der_rewriter::operator()(expr * t, expr_ref & result, proof_ref & result_pr) { m_imp->operator()(t, result, result_pr); } void der_rewriter::set_cancel(bool f) { #pragma omp critical (der_rewriter) { m_imp->set_cancel(f); } } void der_rewriter::cleanup() { ast_manager & m = m_imp->m(); #pragma omp critical (th_rewriter) { dealloc(m_imp); m_imp = alloc(imp, m); } } void der_rewriter::reset() { m_imp->reset(); } z3-4.4.0/src/ast/used_symbols.h0000644000175000017500000000575212540347414016115 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: used_symbols.h Abstract: Collect the symbols used in an expression. Author: Leonardo de Moura (leonardo) 2011-01-11. Revision History: --*/ #ifndef _USED_SYMBOLS_H_ #define _USED_SYMBOLS_H_ #include"ast.h" #include"hashtable.h" #include"obj_hashtable.h" struct do_nothing_rename_proc { symbol operator()(symbol const & s) const { return s; } }; /** \brief Functor for collecting the symbols used in an expression. */ template class used_symbols : public RENAME_PROC { typedef hashtable symbol_set; symbol_set m_used; obj_hashtable m_visited; ptr_vector m_todo; void found(symbol const & s) { m_used.insert(RENAME_PROC::operator()(s)); } void visit(expr * n) { if (!m_visited.contains(n)) { m_visited.insert(n); m_todo.push_back(n); } } public: used_symbols(RENAME_PROC const & p = RENAME_PROC()): RENAME_PROC(p) { } void operator()(expr * n, bool ignore_quantifiers = false) { m_visited.reset(); m_used.reset(); m_todo.reset(); visit(n); while (!m_todo.empty()) { n = m_todo.back(); m_todo.pop_back(); unsigned j; switch (n->get_kind()) { case AST_APP: found(to_app(n)->get_decl()->get_name()); j = to_app(n)->get_num_args(); while (j > 0) { --j; visit(to_app(n)->get_arg(j)); } break; case AST_QUANTIFIER: if (!ignore_quantifiers) { found(to_quantifier(n)->get_qid()); unsigned num_decls = to_quantifier(n)->get_num_decls(); for (unsigned i = 0; i < num_decls; i++) found(to_quantifier(n)->get_decl_name(i)); unsigned num_pats = to_quantifier(n)->get_num_patterns(); for (unsigned i = 0; i < num_pats; i++) visit(to_quantifier(n)->get_pattern(i)); unsigned num_no_pats = to_quantifier(n)->get_num_no_patterns(); for (unsigned i = 0; i < num_no_pats; i++) visit(to_quantifier(n)->get_no_pattern(i)); visit(to_quantifier(n)->get_expr()); } break; default: break; } } } bool contains(symbol const & s) const { return m_used.contains(RENAME_PROC::operator()(s)); } bool contains_core(symbol const & s) const { return m_used.contains(s); } void insert(symbol const & s) { m_used.insert(RENAME_PROC::operator()(s)); } void insert_core(symbol const & s) { m_used.insert(s); } void erase_core(symbol const & s) { m_used.erase(s); } }; #endif /* _USED_SYMBOLS_H_ */ z3-4.4.0/src/ast/expr_abstract.cpp0000644000175000017500000000547712540347414016605 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: expr_abstract.h Abstract: Abstract occurrences of constants to bound variables. Author: Nikolaj Bjorner (nbjorner) 2008-03-08 Notes: --*/ #include "expr_abstract.h" #include "map.h" void expr_abstractor::operator()(unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { expr * curr = 0, *b = 0; SASSERT(n->get_ref_count() > 0); m_stack.push_back(n); for (unsigned i = 0; i < num_bound; ++i) { b = bound[i]; expr* v = m.mk_var(base + num_bound - i - 1, m.get_sort(b)); m_pinned.push_back(v); m_map.insert(b, v); } while(!m_stack.empty()) { curr = m_stack.back(); if (m_map.contains(curr)) { m_stack.pop_back(); continue; } switch(curr->get_kind()) { case AST_VAR: { m_map.insert(curr, curr); m_stack.pop_back(); break; } case AST_APP: { app* a = to_app(curr); bool all_visited = true; m_args.reset(); for (unsigned i = 0; i < a->get_num_args(); ++i) { if (!m_map.find(a->get_arg(i), b)) { m_stack.push_back(a->get_arg(i)); all_visited = false; } else { m_args.push_back(b); } } if (all_visited) { b = m.mk_app(a->get_decl(), m_args.size(), m_args.c_ptr()); m_pinned.push_back(b); m_map.insert(curr, b); m_stack.pop_back(); } break; } case AST_QUANTIFIER: { quantifier* q = to_quantifier(curr); expr_ref_buffer patterns(m); expr_ref result1(m); unsigned new_base = base + q->get_num_decls(); for (unsigned i = 0; i < q->get_num_patterns(); ++i) { expr_abstract(m, new_base, num_bound, bound, q->get_pattern(i), result1); patterns.push_back(result1.get()); } expr_abstract(m, new_base, num_bound, bound, q->get_expr(), result1); b = m.update_quantifier(q, patterns.size(), patterns.c_ptr(), result1.get()); m_pinned.push_back(b); m_map.insert(curr, b); m_stack.pop_back(); break; } default: UNREACHABLE(); } } VERIFY (m_map.find(n, b)); result = b; m_pinned.reset(); m_map.reset(); m_stack.reset(); m_args.reset(); } void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { expr_abstractor abs(m); abs(base, num_bound, bound, n, result); } z3-4.4.0/src/ast/substitution/0000755000175000017500000000000012540347414015777 5ustar michaelmichaelz3-4.4.0/src/ast/substitution/var_offset_map.h0000644000175000017500000000565112540347414021152 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: var_offset_map.h Abstract: A generic mapping from (var, offset) to a value T. Author: Leonardo de Moura (leonardo) 2008-02-01. Revision History: --*/ #ifndef _VAR_OFFSET_MAP_H_ #define _VAR_OFFSET_MAP_H_ #include"ast.h" #include"vector.h" /** \brief A mapping from variable-id + offset to some value of type T. */ template class var_offset_map { protected: struct data { T m_data; unsigned m_timestamp; data():m_timestamp(0) {} }; svector m_map; unsigned m_num_offsets; unsigned m_num_vars; unsigned m_timestamp; public: var_offset_map(): m_num_offsets(0), m_num_vars(0), m_timestamp(1) { } void reset() { m_timestamp++; if (m_timestamp == UINT_MAX) { typename svector::iterator it = m_map.begin(); typename svector::iterator end = m_map.end(); for (; it != end; ++it) it->m_timestamp = 0; m_timestamp = 1; } } unsigned offsets_capacity() const { return m_num_offsets; } unsigned vars_capacity() const { return m_num_vars; } void reserve(unsigned num_offsets, unsigned num_vars) { if (num_offsets > m_num_offsets || num_vars > m_num_vars) { unsigned sz = num_offsets * num_vars; m_map.resize(sz); m_num_vars = num_vars; m_num_offsets = num_offsets; } reset(); } void reserve_offsets(unsigned num_offsets) { reserve(num_offsets, m_num_vars); } void reserve_vars(unsigned num_vars) { reserve(m_num_offsets, num_vars); } void insert(unsigned v_idx, unsigned offset, T const & t) { SASSERT(v_idx < m_num_vars); SASSERT(offset < m_num_offsets); unsigned idx = v_idx + offset * m_num_vars; SASSERT(idx < m_map.size()); data & d = m_map[idx]; d.m_data = t; d.m_timestamp = m_timestamp; } void insert(var * v, unsigned offset, T const & t) { insert(v->get_idx(), offset, t); } bool find(unsigned v_idx, unsigned offset, T & r) const { SASSERT(v_idx < m_num_vars); SASSERT(offset < m_num_offsets); unsigned idx = v_idx + offset * m_num_vars; data const & d = m_map[idx]; SASSERT(d.m_timestamp <= m_timestamp); if (d.m_timestamp == m_timestamp) { r = d.m_data; return true; } return false; } bool find(var * v, unsigned offset, T & r) const { return find(v->get_idx(), offset, r); } void erase(unsigned v_idx, unsigned offset) { SASSERT(v_idx < m_num_vars); SASSERT(offset < m_num_offsets); unsigned idx = v_idx + offset * m_num_vars; m_map[idx].m_timestamp = 0; } }; #endif /* _VAR_OFFSET_MAP_H_ */ z3-4.4.0/src/ast/substitution/substitution.h0000644000175000017500000001624212540347414020731 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: substitution.h Abstract: A substitution, that is, a mapping from (variable, offset) to (expr, offset). We use offsets in order to avoid creating variants of terms. Author: Leonardo de Moura (leonardo) 2008-02-01. Revision History: nbjorner 2013-01-21: - reset the apply cache on pop_scope to make sure that popped substitutions are invalidated. - reset_cache if a new binding was added after application of a substitution. - Add m_refs to make sure terms in the range of the substitution have the same life-time as the substitution. - Remove reset_subst() function. If called without resetting the cache, then results of applying substitutions are incoherent. - Replace UNREACHABLE segment for quantifiers by recursive invocation of substitution that updates expressions within quantifiers. It shifts all variables in the domain of the current substitution by the number of quantified variables. --*/ #ifndef _SUBSTITUTION_H_ #define _SUBSTITUTION_H_ #include"expr_offset_map.h" #include"var_offset_map.h" #include"ast_pp.h" /** \brief A mapping from (variable,offset) to expr_offset. */ class substitution { ast_manager & m_manager; var_offset_map m_subst; // field for backtracking typedef std::pair var_offset; svector m_vars; expr_ref_vector m_refs; unsigned_vector m_scopes; // fields for applying substitutions svector m_todo; expr_offset_map m_apply_cache; expr_ref_vector m_new_exprs; // fields for checking for cycles enum color { White, Grey, Black }; expr_offset_map m_color; // keep track of how substitution state was last updated. enum state { CLEAN, APPLY, INSERT }; state m_state; #ifdef Z3DEBUG unsigned m_max_offset_since_reset; #endif void apply_visit(expr_offset const & n, bool & visited); color get_color(expr_offset const & p) const; void set_color(expr_offset const & p, color c); void visit(expr_offset const & p, bool & visited); bool visit_children(expr_offset const & p); bool acyclic(expr_offset p); public: substitution(ast_manager & m); ast_manager & get_manager() const { return m_manager; } // ----------------------------------- // // Reserve memory for the given number of // offsets and variables. // // ----------------------------------- void reserve(unsigned num_offsets, unsigned num_vars) { m_subst.reserve(num_offsets, num_vars); } void reserve_offsets(unsigned num_offsets) { m_subst.reserve_offsets(num_offsets); } void reserve_vars(unsigned num_vars) { m_subst.reserve_vars(num_vars); } // ----------------------------------- // // Reset functions // // ----------------------------------- // reset everything void reset(); // reset only the substitution application cache void reset_cache(); // ----------------------------------- // // Backtracking // // ----------------------------------- void push_scope() { m_scopes.push_back(m_vars.size()); } void pop_scope(unsigned num_scopes = 1); unsigned get_scope_lvl() { return m_scopes.size(); } bool top_scope_has_bindings() const { return m_scopes.empty() ? !m_vars.empty() : m_scopes.back() < m_vars.size(); } unsigned get_num_bindings() const { return m_vars.size(); } // ----------------------------------- // // Cycle detection // // ----------------------------------- bool acyclic(); // ----------------------------------- // // Insertion & Lookup // // get_binding supplies a way to inspect the substitution. // // ----------------------------------- void insert(unsigned v_idx, unsigned offset, expr_offset const & t) { TRACE("subst_insert", tout << "inserting: #" << v_idx << ":" << offset << " --> " << mk_pp(t.get_expr(), m_manager) << ":" << t.get_offset() << "\n";); m_vars.push_back(var_offset(v_idx, offset)); m_refs.push_back(t.get_expr()); m_subst.insert(v_idx, offset, t); m_state = INSERT; } void insert(var * v, unsigned offset, expr_offset const & t) { insert(v->get_idx(), offset, t); } void insert(expr_offset v, expr_offset const & t) { SASSERT(is_var(v.get_expr())); insert(to_var(v.get_expr()), v.get_offset(), t); } bool find(unsigned v_idx, unsigned offset, expr_offset & r) const { return m_subst.find(v_idx, offset, r); } bool find(var * v, unsigned offset, expr_offset & r) const { return find(v->get_idx(), offset, r); } bool find(expr_offset v, expr_offset & r) const { SASSERT(is_var(v.get_expr())); return find(to_var(v.get_expr()), v.get_offset(), r); } void get_binding(unsigned binding_num, var_offset& var, expr_offset& r) { var = m_vars[binding_num]; VERIFY(m_subst.find(var.first, var.second, r)); } bool contains(var * v, unsigned offset) { expr_offset r; return find(v, offset, r); } // ----------------------------------- // // Application // // ----------------------------------- /** \brief Apply the current substitution to the given expression+offset. The result is an expression. The argument num_actual_offsets is the maximum offset used in a insert method since the last reset. The argument deltas is an array of size num_actual_offsets. It contains the variable delta for each offset. A free variable x:i in an expression offset t:j is mapped to the variable x+delta[i]. */ void apply(unsigned num_actual_offsets, unsigned const * deltas, expr_offset const & n, expr_ref & result) { apply(num_actual_offsets, deltas, n, expr_offset(0, 0), expr_offset(0, 0), result); } /** \brief Similar to the previous method, but occurrences of s in n are substituted by t. If s != expr_offset(0,0), then the cache is reset before and after the execution of this procedure. */ void apply(unsigned num_actual_offsets, unsigned const* deltas, expr_offset const & n, expr_offset const & s, expr_offset const & t, expr_ref & result); void apply(expr * n, expr_ref & result) { unsigned deltas[1] = { 0 }; apply(1, deltas, expr_offset(n, 0), result); } // ----------------------------------- // // Debugging // // ----------------------------------- /** \brief Dump the current substitution (for debugging purposes). */ void display(std::ostream & out, unsigned num_actual_offsets, unsigned const * deltas); /** \brief Dump the current substitution without normalizing expressions (for debugging purposes). */ void display(std::ostream & out); // ----------------------------------- // // Compare terms modulo a substitution // // ----------------------------------- bool compare(expr_offset t1, expr_offset t2); }; #endif z3-4.4.0/src/ast/substitution/substitution_tree.cpp0000644000175000017500000007063012540347414022304 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: substitution_tree.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-04. Revision History: --*/ #include"substitution_tree.h" #include"ast_pp.h" #include"ast_smt2_pp.h" /** \brief Return the next available register. */ unsigned substitution_tree::next_reg() { while(true) { unsigned curr = m_next_reg; if (curr > m_max_reg) m_max_reg = curr; m_next_reg++; if (curr >= m_used_regs.size() || !m_used_regs.get(curr)) return curr; } } inline void substitution_tree::push(svector & sv, subst const & s) { sv.push_back(s); m_manager.inc_ref(s.first); m_manager.inc_ref(s.second); } inline expr * substitution_tree::get_reg_value(unsigned ridx) { return m_registers.get(ridx, 0); } inline void substitution_tree::set_reg_value(unsigned ridx, expr * e) { m_registers.setx(ridx, e, 0); } inline void substitution_tree::erase_reg_from_todo(unsigned ridx) { SASSERT(m_registers[ridx]); m_registers[ridx] = 0; SASSERT(m_todo.contains(ridx)); m_todo.erase(ridx); } /** \brief Linearize the expressions in the registers stored in m_todo. Store the result in \c result. Example: m_todo = { 3, 4 } m_registers[3] = (f (g a)) m_registers[4] = b next_regs are 5 6 7 result: #3 -> (f #5); #4 -> b; #5 -> (g #6); #6 -> a */ void substitution_tree::linearize(svector & result) { ptr_buffer new_args; for (unsigned i = 0; i < m_todo.size(); i++) { unsigned ireg_idx = m_todo[i]; expr * n = get_reg_value(ireg_idx); var * ireg = m_manager.mk_var(ireg_idx, m_manager.get_sort(n)); if (is_var(n)) push(result, subst(ireg, n)); else { SASSERT(is_app(n)); app * new_app; unsigned num = to_app(n)->get_num_args(); if (num == 0) new_app = to_app(n); else { for (unsigned j = 0; j < num; j++) { unsigned oreg = next_reg(); set_reg_value(oreg, to_app(n)->get_arg(j)); m_todo.push_back(oreg); sort * s = m_manager.get_sort(get_reg_value(oreg)); new_args.push_back(m_manager.mk_var(oreg, s)); } new_app = m_manager.mk_app(to_app(n)->get_decl(), new_args.size(), new_args.c_ptr()); new_args.reset(); } push(result, subst(ireg, new_app)); } } } /** \brief Process the pair in := (f t_1 ... t_n) and out := (f r_1 ... r_n), where r_i's are variables (register ids), and t_i's are arbitrary expressions. The r_i's are added to the m_todo list, and m_registers[r_i] is assigned to t_i. If save_set_registers == true, then r_i's are stored in m_to_reset. */ void substitution_tree::process_args(app * in, app * out) { CTRACE("subst_tree_bug", in->get_num_args() != out->get_num_args(), tout << mk_ismt2_pp(in, m_manager) << "\n" << mk_ismt2_pp(out, m_manager) << "\n";); unsigned num = out->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * in_arg = in->get_arg(i); expr * out_arg = out->get_arg(i); SASSERT(is_var(out_arg)); unsigned oreg = to_var(out_arg)->get_idx(); set_reg_value(oreg, in_arg); m_todo.push_back(oreg); } } /** \brief Reset registers in m_todo at [old_size, m_todo.size()) */ void substitution_tree::reset_registers(unsigned old_size) { SASSERT(m_todo.size() >= old_size); unsigned_vector::iterator it2 = m_todo.begin() + old_size; unsigned_vector::iterator end2 = m_todo.end(); for (; it2 != end2; ++it2) m_registers[*it2] = 0; m_todo.shrink(old_size); } /** \brief Return a measure on how compatible sv and the expressions to be processed are. */ unsigned substitution_tree::get_compatibility_measure(svector const & sv) { unsigned old_size = m_todo.size(); unsigned measure = 0; svector::const_iterator it = sv.begin(); svector::const_iterator end = sv.end(); for (; it != end; ++it) { subst const & s = *it; unsigned ireg = s.first->get_idx(); expr * out = s.second; expr * in = get_reg_value(ireg); if (is_var(out)) { if (out == in) measure += 1; } else { SASSERT(is_app(out)); if (in && is_app(in) && to_app(out)->get_decl() == to_app(in)->get_decl()) { measure += 2; process_args(to_app(in), to_app(out)); } } } reset_registers(old_size); return measure; } /** \brief Find the child of r that is most compatible with the expressions stored in the registers in m_todo. Return 0 if none of the children has any compatible substitution entry. */ substitution_tree::node * substitution_tree::find_best_child(node * r) { SASSERT(!r->m_leaf); #ifdef Z3DEBUG unsigned todo_size = m_todo.size(); #endif node * best_child = 0; unsigned max_measure = 0; node * curr_child = r->m_first_child; while (curr_child) { unsigned measure = get_compatibility_measure(curr_child->m_subst); if (measure > max_measure) { best_child = curr_child; max_measure = measure; } curr_child = curr_child->m_next_sibling; } SASSERT(todo_size == m_todo.size()); return best_child; } /** \brief Reset datastructures used to insert/erase elements from the substitution tree. */ void substitution_tree::reset_compiler() { m_todo.reset(); m_used_regs.reset(); m_next_reg = 1; // register 0 is reserved for input. DEBUG_CODE({ ptr_vector::iterator it = m_registers.begin(); ptr_vector::iterator end = m_registers.end(); for (; it != end; ++it) { SASSERT(*it == 0); } }); } /** \brief Create a node with the linearization for all registers in todo. Attach new_expr to it. */ substitution_tree::node * substitution_tree::mk_node_for(expr * new_expr) { node * n = alloc(node, true); linearize(n->m_subst); n->m_expr = new_expr; m_manager.inc_ref(new_expr); return n; } /** \brief Mark register ridx as used. */ void substitution_tree::mark_used_reg(unsigned ridx) { if (ridx >= m_used_regs.size()) m_used_regs.resize(ridx+1); m_used_regs.set(ridx); } /** \brief Mark (m_used_regs) all registers used in \c sv. */ void substitution_tree::mark_used_regs(svector const & sv) { svector::const_iterator it = sv.begin(); svector::const_iterator end = sv.end(); for (; it != end; ++it) { subst const & s = *it; mark_used_reg(s.first->get_idx()); if (is_app(s.second)) { unsigned num_args = to_app(s.second)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(s.second)->get_arg(i); SASSERT(is_var(arg)); mark_used_reg(to_var(arg)->get_idx()); } } } } /** \brief Insert a new expression in the substitution tree. */ void substitution_tree::insert(expr * new_expr) { if (is_app(new_expr)) { insert(to_app(new_expr)); } else { SASSERT(is_var(new_expr)); sort * s = to_var(new_expr)->get_sort(); unsigned id = s->get_decl_id(); if (id >= m_vars.size()) m_vars.resize(id+1, 0); if (m_vars[id] == 0) m_vars[id] = alloc(var_ref_vector, m_manager); var_ref_vector * v = m_vars[id]; if (!v->contains(to_var(new_expr))) v->push_back(to_var(new_expr)); } } /** \brief Insert a new application in the substitution tree. */ void substitution_tree::insert(app * new_expr) { reset_compiler(); set_reg_value(0, new_expr); m_todo.push_back(0); func_decl * d = new_expr->get_decl(); unsigned id = d->get_decl_id(); if (id >= m_roots.size()) m_roots.resize(id+1, 0); if (!m_roots[id]) { // there is no tree for the function symbol heading new_expr m_roots[id] = mk_node_for(new_expr); reset_registers(0); m_size++; return; } node * r = m_roots[id]; while (true) { m_compatible.reset(); m_incompatible.reset(); svector & sv = r->m_subst; // separate sv in the set of compatible & incompatible instructions svector::iterator it = sv.begin(); svector::iterator end = sv.end(); for (; it != end; ++it) { subst & s = *it; unsigned ireg = s.first->get_idx(); expr * out = s.second; expr * in = get_reg_value(ireg); SASSERT(is_var(out) || is_app(out)); if (is_var(out)) { if (out == in) { erase_reg_from_todo(ireg); m_compatible.push_back(s); } else { m_incompatible.push_back(s); } } else { if (in && is_app(in) && to_app(out)->get_decl() == to_app(in)->get_decl()) { erase_reg_from_todo(ireg); m_compatible.push_back(s); process_args(to_app(in), to_app(out)); } else { m_incompatible.push_back(s); } } } // process m_compatible & m_incompatible if (m_incompatible.empty()) { if (m_todo.empty()) { // nothing else to process // new_expr is already in the substitution tree SASSERT(r->m_leaf && r->m_expr == new_expr); reset_registers(0); return; } else { mark_used_regs(r->m_subst); node * best_child = find_best_child(r); if (best_child == 0) { // there is no compatible child node * n = mk_node_for(new_expr); n->m_next_sibling = r->m_first_child; r->m_first_child = n; reset_registers(0); m_size++; return; } else { // continue with best_child r = best_child; } } } else { SASSERT(!m_compatible.empty()); SASSERT(!m_incompatible.empty()); mark_used_regs(m_compatible); r->m_subst.swap(m_compatible); node * n = mk_node_for(new_expr); node * incomp = alloc(node, r->m_leaf); incomp->m_subst.swap(m_incompatible); if (r->m_leaf) { incomp->m_expr = r->m_expr; r->m_leaf = false; } else incomp->m_first_child = r->m_first_child; incomp->m_next_sibling = n; SASSERT(!r->m_leaf); r->m_first_child = incomp; reset_registers(0); m_size++; return; } } } /** \brief Return true if sv is fully compatible with the expressions in the registers in m_todo. */ bool substitution_tree::is_fully_compatible(svector const & sv) { unsigned old_size = m_todo.size(); svector::const_iterator it = sv.begin(); svector::const_iterator end = sv.end(); for (; it != end; ++it) { subst const & s = *it; unsigned ireg = s.first->get_idx(); expr * out = s.second; expr * in = get_reg_value(ireg); if (is_var(out)) { if (out != in) { reset_registers(old_size); return false; } } else { if (!in || !is_app(in) || to_app(in)->get_decl() != to_app(out)->get_decl()) { reset_registers(old_size); return false; } process_args(to_app(in), to_app(out)); } } reset_registers(old_size); return true; } /** \brief Return a child of r that is fully compatible with the expressions in the registers in m_todo. */ bool substitution_tree::find_fully_compatible_child(node * r, node * & prev, node * & child) { SASSERT(!r->m_leaf); prev = 0; child = r->m_first_child; while (child) { if (is_fully_compatible(child->m_subst)) return true; prev = child; child = child->m_next_sibling; } return false; } inline bool substitution_tree::at_least_3_children(node * r) { return !r->m_leaf && r->m_first_child->m_next_sibling && r->m_first_child->m_next_sibling->m_next_sibling; } /** \brief Remove expression from the substitution tree. Do nothing, if n is not in the tree. */ void substitution_tree::erase(expr * e) { if (is_app(e)) erase(to_app(e)); else { SASSERT(is_var(e)); sort * s = to_var(e)->get_sort(); unsigned id = s->get_decl_id(); if (id >= m_vars.size() || m_vars[id] == 0) return; var_ref_vector * v = m_vars[id]; v->erase(to_var(e)); } } /** \brief Remove application from the substitution tree. Do nothing, if n is not in the tree. */ void substitution_tree::erase(app * e) { func_decl * d = e->get_decl(); unsigned id = d->get_decl_id(); if (id >= m_roots.size() || !m_roots[id]) return; reset_compiler(); set_reg_value(0, e); m_todo.push_back(0); node * r = m_roots[id]; node * parent = 0; node * prev = 0; while (true) { svector & sv = r->m_subst; svector::iterator it = sv.begin(); svector::iterator end = sv.end(); for (; it != end; ++it) { subst & s = *it; unsigned ireg = s.first->get_idx(); expr * out = s.second; expr * in = get_reg_value(ireg); SASSERT(is_var(out) || is_app(out)); if (is_var(out)) { if (out != in) { reset_registers(0); return; // node is not in the substitution tree } erase_reg_from_todo(ireg); } else { if (!in || !is_app(in) || to_app(out)->get_decl() != to_app(in)->get_decl()) { reset_registers(0); return; // node is not in the substitution tree } erase_reg_from_todo(ireg); process_args(to_app(in), to_app(out)); } } if (m_todo.empty()) { reset_registers(0); SASSERT(r->m_expr == e); if (parent == 0) { delete_node(r); m_roots[id] = 0; } else if (at_least_3_children(parent)) { if (prev == 0) parent->m_first_child = r->m_next_sibling; else prev->m_next_sibling = r->m_next_sibling; delete_node(r); } else { SASSERT(parent->m_first_child && parent->m_first_child->m_next_sibling && !parent->m_first_child->m_next_sibling->m_next_sibling); node * other_child = prev ? prev : r->m_next_sibling; SASSERT(other_child); parent->m_subst.append(other_child->m_subst); parent->m_leaf = other_child->m_leaf; if (other_child->m_leaf) parent->m_expr = other_child->m_expr; else parent->m_first_child = other_child->m_first_child; delete_node(r); dealloc(other_child); // Remark: I didn't use delete_node since its resources were sent to parent. } m_size --; return; } else { parent = r; if (!find_fully_compatible_child(r, prev, r)) { // node is not in the substitution tree reset_registers(0); return; } // continue with fully compatible child } } } void substitution_tree::delete_node(node * n) { ptr_buffer todo; SASSERT(todo.empty()); todo.push_back(n); while (!todo.empty()) { node * n = todo.back(); todo.pop_back(); svector::iterator it2 = n->m_subst.begin(); svector::iterator end2 = n->m_subst.end(); for (; it2 != end2; ++it2) { m_manager.dec_ref(it2->first); m_manager.dec_ref(it2->second); } if (n->m_leaf) m_manager.dec_ref(n->m_expr); else { node * c = n->m_first_child; while (c) { todo.push_back(c); c = c->m_next_sibling; } } dealloc(n); } } void substitution_tree::reset() { ptr_vector::iterator it = m_roots.begin(); ptr_vector::iterator end = m_roots.end(); for (; it != end; ++it) { if (*it) delete_node(*it); } m_roots.reset(); std::for_each(m_vars.begin(), m_vars.end(), delete_proc()); m_vars.reset(); m_size = 0; } void substitution_tree::display(std::ostream & out, subst const & s) const { out << "r!" << s.first->get_idx() << " -> "; if (is_app(s.second)) { unsigned num = to_app(s.second)->get_num_args(); if (num == 0) out << to_app(s.second)->get_decl()->get_name(); else { out << "(" << to_app(s.second)->get_decl()->get_name(); for (unsigned i = 0; i < num; i++) out << " r!" << to_var(to_app(s.second)->get_arg(i))->get_idx(); out << ")"; } } else { out << mk_pp(s.second, m_manager); } } void substitution_tree::display(std::ostream & out, svector const & sv) const { svector::const_iterator it = sv.begin(); svector::const_iterator end = sv.end(); for (bool first = true; it != end; ++it, first = false) { subst const & s = *it; if (!first) out << "; "; display(out, s); } } void substitution_tree::display(std::ostream & out, node * n, unsigned delta) const { for (unsigned i = 0; i < delta; i++) out << " "; display(out, n->m_subst); if (n->m_leaf) { params_ref p; p.set_bool("single_line", true); out << " ==> "; out << mk_pp(n->m_expr, m_manager, p); out << "\n"; } else { out << "\n"; node * c = n->m_first_child; while (c) { display(out, c, delta+1); c = c->m_next_sibling; } } } bool substitution_tree::backtrack() { while (!m_bstack.empty()) { TRACE("st", tout << "backtracking...\n";); m_subst->pop_scope(); node * n = m_bstack.back(); if (n->m_next_sibling) { m_bstack.back() = n->m_next_sibling; return true; } m_bstack.pop_back(); } return false; } inline expr_offset substitution_tree::find(expr_offset p) { TRACE("substitution_tree_bug", tout << "find...\n";); while (is_var(p.get_expr())) { TRACE("substitution_tree_bug", tout << mk_pp(p.get_expr(), m_manager) << " " << p.get_offset() << "\n";); if (!m_subst->find(to_var(p.get_expr()), p.get_offset(), p)) return p; } return p; } template bool substitution_tree::bind_var(var * v, unsigned offset, expr_offset const & p) { TRACE("st", tout << "bind_var: " << mk_pp(v, m_manager) << " " << offset << "\n" << mk_pp(p.get_expr(), m_manager) << " " << p.get_offset() << "\n";); if (Mode == STV_INST && offset == m_st_offset) { SASSERT(!is_var(p.get_expr()) || p.get_offset() != m_reg_offset); if (is_var(p.get_expr()) && p.get_offset() == m_in_offset) { m_subst->insert(p, expr_offset(v, offset)); return true; } return false; } if (Mode == STV_GEN && offset == m_in_offset) { SASSERT(!is_var(p.get_expr()) || p.get_offset() != m_reg_offset); if (is_var(p.get_expr()) && p.get_offset() == m_st_offset) { m_subst->insert(p, expr_offset(v, offset)); return true; } return false; } m_subst->insert(v, offset, p); TRACE("st_bug", tout << "substitution updated\n"; m_subst->display(tout);); return true; } template bool substitution_tree::unify_match(expr_offset p1, expr_offset p2) { svector & todo = m_visit_todo; todo.reset(); todo.push_back(entry(p1, p2)); while (!todo.empty()) { entry const & e = todo.back(); p1 = find(e.first); p2 = find(e.second); todo.pop_back(); if (p1 != p2) { expr * n1 = p1.get_expr(); expr * n2 = p2.get_expr(); SASSERT(!is_quantifier(n1)); SASSERT(!is_quantifier(n2)); bool v1 = is_var(n1); bool v2 = is_var(n2); TRACE("st", tout << "n1: " << mk_pp(n1, m_manager) << " " << p1.get_offset() << "\n"; tout << "n2: " << mk_pp(n2, m_manager) << " " << p2.get_offset() << "\n";); if (v1 && v2) { if (p2.get_offset() == m_reg_offset) std::swap(p1, p2); if (!bind_var(to_var(p1.get_expr()), p1.get_offset(), p2)) return false; } else if (v1) { if (!bind_var(to_var(n1), p1.get_offset(), p2)) return false; } else if (v2) { if (!bind_var(to_var(n2), p2.get_offset(), p1)) return false; } else { app * a1 = to_app(n1); app * a2 = to_app(n2); unsigned off1 = p1.get_offset(); unsigned off2 = p2.get_offset(); if (a1->get_decl() != a2->get_decl() || a1->get_num_args() != a2->get_num_args()) return false; unsigned j = a1->get_num_args(); while (j > 0) { --j; entry new_e(expr_offset(a1->get_arg(j), off1), expr_offset(a2->get_arg(j), off2)); todo.push_back(new_e); } } } } return true; } template bool substitution_tree::visit_vars(expr * e, st_visitor & st) { if (m_vars.empty()) return true; // continue sort * s = m_manager.get_sort(e); unsigned s_id = s->get_decl_id(); if (s_id < m_vars.size()) { var_ref_vector * v = m_vars[s_id]; if (v && !v->empty()) { unsigned sz = v->size(); for (unsigned i = 0; i < sz; i++) { var * curr = v->get(i); m_subst->push_scope(); if (unify_match(expr_offset(curr, m_st_offset), expr_offset(e, m_in_offset))) { if (Mode != STV_UNIF || m_subst->acyclic()) { if (!st(curr)) { m_subst->pop_scope(); return false; // stop } } } m_subst->pop_scope(); } } } return true; // continue } template bool substitution_tree::visit(svector const & sv) { svector::const_iterator it = sv.begin(); svector::const_iterator end = sv.end(); for (; it != end; ++it) { subst const & s = *it; TRACE("st", tout << "processing subst:\n"; display(tout, s); tout << "\n";); var * rin = s.first; expr * out = s.second; expr_offset p1(rin, m_reg_offset); expr_offset p2(out, is_var(out) ? m_st_offset : m_reg_offset); if (!unify_match(p1, p2)) return false; } return true; } template bool substitution_tree::visit(expr * e, st_visitor & st, node * r) { m_bstack.reset(); m_bstack.push_back(r); m_subst->push_scope(); m_subst->insert(static_cast(0), m_reg_offset, expr_offset(e, m_in_offset)); while (true) { node * n = m_bstack.back(); TRACE("st", tout << "push scope...\n";); m_subst->push_scope(); TRACE("st", tout << "processing node:\n"; display(tout, n->m_subst); tout << "\n";); if (visit(n->m_subst)) { if (n->m_leaf) { // if searching for unifiers and the substitution is cyclic, then backtrack. if (Mode == STV_UNIF && !m_subst->acyclic()) { if (!backtrack()) break; } else { TRACE("st_bug", tout << "found match:\n"; m_subst->display(tout); tout << "m_subst: " << m_subst << "\n";); if (!st(n->m_expr)) { clear_stack(); return false; } if (!backtrack()) break; } } else { m_bstack.push_back(n->m_first_child); } } else if (!backtrack()) break; } clear_stack(); return true; } void substitution_tree::clear_stack() { while (!m_bstack.empty()) { m_subst->pop_scope(); m_bstack.pop_back(); } m_subst->pop_scope(); } template void substitution_tree::visit(expr * e, st_visitor & st, unsigned in_offset, unsigned st_offset, unsigned reg_offset) { m_in_offset = in_offset; m_st_offset = st_offset; m_reg_offset = reg_offset; m_subst = &(st.get_substitution()); m_subst->reserve_vars(get_approx_num_regs()); if (visit_vars(e, st)) { if (is_app(e)) { func_decl * d = to_app(e)->get_decl(); unsigned id = d->get_decl_id(); node * r = m_roots.get(id, 0); if (r) visit(e, st, r); } else { SASSERT(is_var(e)); ptr_vector::iterator it = m_roots.begin(); ptr_vector::iterator end = m_roots.end(); for (; it != end; ++it) { node * r = *it; if (r != 0) { var * v = r->m_subst[0].first; if (v->get_sort() == to_var(e)->get_sort()) if (!visit(e, st, r)) break; } } } } } void substitution_tree::unify(expr * e, st_visitor & v, unsigned in_offset, unsigned st_offset, unsigned reg_offset) { visit(e, v, in_offset, st_offset, reg_offset); } void substitution_tree::inst(expr * e, st_visitor & v, unsigned in_offset, unsigned st_offset, unsigned reg_offset) { visit(e, v, in_offset, st_offset, reg_offset); } void substitution_tree::gen(expr * e, st_visitor & v, unsigned in_offset, unsigned st_offset, unsigned reg_offset) { visit(e, v, in_offset, st_offset, reg_offset); } void substitution_tree::display(std::ostream & out) const { out << "substitution tree:\n"; ptr_vector::const_iterator it = m_roots.begin(); ptr_vector::const_iterator end = m_roots.end(); for (; it != end; ++it) if (*it) display(out, *it, 0); bool found_var = false; ptr_vector::const_iterator it2 = m_vars.begin(); ptr_vector::const_iterator end2 = m_vars.end(); for (; it2 != end2; ++it2) { var_ref_vector * v = *it2; if (v == 0) continue; // m_vars may contain null pointers. See substitution_tree::insert. unsigned num = v->size(); for (unsigned i = 0; i < num; i++) { if (!found_var) { found_var = true; out << "vars: "; } out << mk_pp(v->get(i), m_manager) << " "; } } if (found_var) out << "\n"; } substitution_tree::substitution_tree(ast_manager & m): m_manager(m), m_max_reg(0), m_size(0) { } substitution_tree::~substitution_tree() { reset(); } z3-4.4.0/src/ast/substitution/substitution_tree.h0000644000175000017500000001034012540347414021741 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: substitution_tree.h Abstract: Substitution Trees Author: Leonardo de Moura (leonardo) 2008-02-03. Revision History: --*/ #ifndef _SUBSTITUTION_TREE_H_ #define _SUBSTITUTION_TREE_H_ #include"ast.h" #include"substitution.h" /** \brief Substitution tree visitor. */ class st_visitor { protected: substitution & m_subst; public: st_visitor(substitution & s):m_subst(s) {} virtual ~st_visitor() {} substitution & get_substitution() { return m_subst; } virtual bool operator()(expr * e) { return true; } }; /** \brief Substitution tree term index. */ class substitution_tree { typedef std::pair subst; struct node { bool m_leaf; svector m_subst; node * m_next_sibling; union { node * m_first_child; expr * m_expr; }; node(bool leaf):m_leaf(leaf), m_next_sibling(0), m_first_child(0) {} }; ast_manager & m_manager; ptr_vector m_roots; unsigned m_max_reg; ptr_vector m_registers; unsigned m_size; ptr_vector m_vars; // mapping from decl_id to var_ref_vector // Compilation time fields unsigned m_next_reg; bit_vector m_used_regs; unsigned_vector m_todo; svector m_compatible; svector m_incompatible; // Execution time fields substitution * m_subst; ptr_vector m_bstack; unsigned m_in_offset; unsigned m_st_offset; unsigned m_reg_offset; typedef std::pair entry; svector m_visit_todo; unsigned next_reg(); void push(svector & sv, subst const & s); expr * get_reg_value(unsigned ridx); void set_reg_value(unsigned ridx, expr * e); void erase_reg_from_todo(unsigned ridx); void linearize(svector & result); void process_args(app * in, app * out); void reset_registers(unsigned old_size); unsigned get_compatibility_measure(svector const & sv); node * find_best_child(node * r); void reset_compiler(); node * mk_node_for(expr * new_expr); void mark_used_reg(unsigned ridx); void mark_used_regs(svector const & sv); bool is_fully_compatible(svector const & sv); bool find_fully_compatible_child(node * r, node * & prev, node * & child); static bool at_least_3_children(node * r); void delete_node(node * n); void display(std::ostream & out, subst const & s) const; void display(std::ostream & out, svector const & sv) const; void display(std::ostream & out, node * n, unsigned delta) const; enum st_visit_mode { STV_UNIF, STV_INST, STV_GEN }; expr_offset find(expr_offset p); bool backtrack(); template bool bind_var(var * v, unsigned offset, expr_offset const & p); template bool unify_match(expr_offset p1, expr_offset p2); template bool visit_vars(expr * e, st_visitor & st); template bool visit(svector const & s); template bool visit(expr * e, st_visitor & st, node * r); template void visit(expr * e, st_visitor & st, unsigned in_offset, unsigned st_offset, unsigned reg_offset); void clear_stack(); public: substitution_tree(ast_manager & m); ~substitution_tree(); void insert(app * n); void insert(expr * n); void erase(app * n); void erase(expr * n); void reset(); bool empty() const { return m_size == 0; } void unify(expr * e, st_visitor & v, unsigned in_offset = 0, unsigned st_offset = 1, unsigned reg_offset = 2); void inst(expr * e, st_visitor & v, unsigned in_offset = 0, unsigned st_offset = 1, unsigned reg_offset = 2); void gen(expr * e, st_visitor & v, unsigned in_offset = 0, unsigned st_offset = 1, unsigned reg_offset = 2); unsigned get_approx_num_regs() const { return m_max_reg + 1; } void display(std::ostream & out) const; }; #endif /* _SUBSTITUTION_TREE_H_ */ z3-4.4.0/src/ast/substitution/expr_offset_map.h0000644000175000017500000000471512540347414021340 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: expr_offset_map.h Abstract: A generic mapping from (expression, offset) to a value T. Author: Leonardo de Moura (leonardo) 2008-02-01. Revision History: --*/ #ifndef _EXPR_OFFSET_MAP_H_ #define _EXPR_OFFSET_MAP_H_ #include"expr_offset.h" #include"vector.h" /** \brief A mapping from expr_offset to some value of type T. */ template class expr_offset_map { struct data { T m_data; unsigned m_timestamp; data():m_timestamp(0) {} }; vector > m_map; unsigned m_timestamp; public: expr_offset_map(): m_timestamp(1) {} bool contains(expr_offset const & n) const { unsigned off = n.get_offset(); if (off < m_map.size()) { svector const & v = m_map[off]; unsigned id = n.get_expr()->get_id(); if (id < v.size()) return v[id].m_timestamp == m_timestamp; } return false; } bool find(expr_offset const & n, T & r) const { unsigned off = n.get_offset(); if (off < m_map.size()) { svector const & v = m_map[off]; unsigned id = n.get_expr()->get_id(); if (id < v.size() && v[id].m_timestamp == m_timestamp) { r = v[id].m_data; return true; } } return false; } void insert(expr_offset const & n, T const & r) { unsigned off = n.get_offset(); if (off >= m_map.size()) m_map.resize(off+1, svector()); svector & v = m_map[off]; unsigned id = n.get_expr()->get_id(); if (id >= v.size()) v.resize(id+1); v[id].m_data = r; v[id].m_timestamp = m_timestamp; } void reset() { m_timestamp++; if (m_timestamp == UINT_MAX) { typename vector >::iterator it = m_map.begin(); typename vector >::iterator end = m_map.end(); for (; it != end; ++it) { svector & v = *it; typename svector::iterator it2 = v.begin(); typename svector::iterator end2 = v.end(); for (; it2 != end2; ++it2) it2->m_timestamp = 0; } m_timestamp = 1; } } }; #endif /* _EXPR_OFFSET_MAP_H_ */ z3-4.4.0/src/ast/substitution/matcher.h0000644000175000017500000000247712540347414017605 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: matcher.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-02. Revision History: --*/ #ifndef _MATCHER_H_ #define _MATCHER_H_ #include"substitution.h" #include"hashtable.h" /** \brief Functor for matching expressions. */ class matcher { typedef std::pair expr_pair; typedef pair_hash, obj_ptr_hash > expr_pair_hash; typedef hashtable > cache; substitution * m_subst; // cache m_cache; svector m_todo; void reset(); public: matcher() {} /** \brief Return true if e2 is an instance of e1. In case of success (result is true), it will store the substitution that makes e1 equals to e2 into s. For example: 1) e1 = f(g(x), x), e2 = f(g(h(a)), h(a)) The result is true, and s will contain x -> h(a) 2) e1 = f(a, x) e2 = f(x, a) The result is false. 3) e1 = f(x, x) e2 = f(y, a) The result is false 4) e1 = f(x, y) e2 = f(h(z), a) The result is true, and s contains x->h(z) and y->a */ bool operator()(expr * e1, expr * e2, substitution & s); }; #endif /* _MATCHER_H_ */ z3-4.4.0/src/ast/substitution/substitution.cpp0000644000175000017500000002636612540347414021274 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: substitution.cpp Abstract: A substitution, that is, a mapping from (variable, offset) to (expr, offset). We use offsets in order to avoid creating variants of terms. Author: Leonardo de Moura (leonardo) 2008-02-01. Revision History: --*/ #include"substitution.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"rewriter.h" substitution::substitution(ast_manager & m): m_manager(m), m_refs(m), m_new_exprs(m), m_state(CLEAN) { } void substitution::reset() { m_subst.reset(); m_vars.reset(); m_refs.reset(); m_scopes.reset(); reset_cache(); } void substitution::reset_cache() { TRACE("subst_bug", tout << "substitution::reset_cache\n"; for (unsigned i = 0; i < m_new_exprs.size(); i++) { tout << mk_pp(m_new_exprs.get(i), m_manager) << "\nref_count: " << m_new_exprs.get(i)->get_ref_count() << "\n"; }); m_apply_cache.reset(); m_new_exprs.reset(); m_state = CLEAN; } void substitution::pop_scope(unsigned num_scopes) { unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; unsigned old_sz = m_scopes[new_lvl]; unsigned curr_sz = m_vars.size(); SASSERT(old_sz <= curr_sz); for (unsigned i = old_sz; i < curr_sz; i++) { var_offset & curr = m_vars[i]; m_subst.erase(curr.first, curr.second); } m_vars.shrink(old_sz); m_refs.shrink(old_sz); m_scopes.shrink(new_lvl); reset_cache(); } inline void substitution::apply_visit(expr_offset const & n, bool & visited) { if (!m_apply_cache.contains(n)) { m_todo.push_back(n); visited = false; } } void substitution::apply(unsigned num_actual_offsets, unsigned const * deltas, expr_offset const & n, expr_offset const & s, expr_offset const & t, expr_ref & result) { TRACE("subst_bug", tout << "BEGIN substitution::apply\n";); // It is incorrect to cache results between different calls if we are applying a substitution // modulo a substitution s -> t. if (m_state == INSERT || s != expr_offset(0,0)) reset_cache(); m_state = APPLY; unsigned j; expr * e; unsigned off; expr_offset n1; bool visited; unsigned num_args; ptr_buffer new_args; m_todo.push_back(n); while (!m_todo.empty()) { expr_offset n = m_todo.back(); TRACE("subst_bug", tout << "n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset() << "\n";); if (m_apply_cache.contains(n)) { m_todo.pop_back(); continue; } expr_offset n_prime = n == s ? t : n; TRACE("subst_bug", tout << "n_prime: " << mk_pp(n_prime.get_expr(), m_manager) << " : " << n_prime.get_offset() << "\n";); visited = true; e = n_prime.get_expr(); off = n_prime.get_offset(); switch (e->get_kind()) { case AST_VAR: if (find(to_var(e)->get_idx(), off, n1)) { apply_visit(n1, visited); TRACE("subst_bug", tout << "visited: " << visited << ", n1: " << mk_pp(n1.get_expr(), m_manager) << " : " << n1.get_offset() << "\n";); if (visited) { m_todo.pop_back(); expr * new_expr; m_apply_cache.find(n1, new_expr); m_apply_cache.insert(n, new_expr); TRACE("subst_bug", tout << "1. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset() << " --> " << mk_pp(new_expr, m_manager) << "\n";); } } else { m_todo.pop_back(); SASSERT(off < num_actual_offsets); unsigned delta = deltas[off]; expr * new_expr = e; if (delta > 0) { new_expr = m_manager.mk_var(to_var(e)->get_idx() + delta, to_var(e)->get_sort()); m_new_exprs.push_back(new_expr); } m_apply_cache.insert(n, new_expr); TRACE("subst_bug", tout << "2. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset() << " --> " << mk_pp(new_expr, m_manager) << "\n";); } break; case AST_APP: num_args = to_app(e)->get_num_args(); j = num_args; while (j > 0) { --j; apply_visit(expr_offset(to_app(e)->get_arg(j), off), visited); } if (visited) { m_todo.pop_back(); new_args.reset(); bool has_new_args = false; for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(e)->get_arg(i); expr * new_arg; VERIFY(m_apply_cache.find(expr_offset(arg, off), new_arg)); new_args.push_back(new_arg); if (arg != new_arg) has_new_args = true; } if (!has_new_args) { m_apply_cache.insert(n, e); TRACE("subst_bug", tout << "3. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset() << " --> " << mk_pp(e, m_manager) << "\n";); } else { expr * new_expr = m_manager.mk_app(to_app(e)->get_decl(), new_args.size(), new_args.c_ptr()); m_new_exprs.push_back(new_expr); m_apply_cache.insert(n, new_expr); TRACE("subst_bug", tout << "3. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset() << " --> " << mk_pp(new_expr, m_manager) << "\n";); } } break; case AST_QUANTIFIER: { quantifier* q = to_quantifier(e); unsigned num_vars = q->get_num_decls(); substitution subst(m_manager); expr_ref er(m_manager); subst.reserve(m_subst.offsets_capacity(), m_subst.vars_capacity() + num_vars); var_shifter var_sh(m_manager); expr_offset r; for (unsigned i = 0; i < m_subst.offsets_capacity(); i++) { for (unsigned j = 0; j < m_subst.vars_capacity(); j++) { if (find(j, i, r)) { var_sh(r.get_expr(), num_vars, er); subst.insert(j + num_vars, i, expr_offset(er, r.get_offset())); } } } expr_offset body(q->get_expr(), off); expr_ref s1_ref(m_manager), t1_ref(m_manager); if (s.get_expr() != 0) { var_sh(s.get_expr(), num_vars, s1_ref); } if (t.get_expr() != 0) { var_sh(t.get_expr(), num_vars, t1_ref); } expr_offset s1(s1_ref, s.get_offset()); expr_offset t1(t1_ref, t.get_offset()); expr_ref_vector pats(m_manager), no_pats(m_manager); for (unsigned i = 0; i < q->get_num_patterns(); ++i) { subst.apply(num_actual_offsets, deltas, expr_offset(q->get_pattern(i), off), s1, t1, er); pats.push_back(er); } for (unsigned i = 0; i < q->get_num_no_patterns(); ++i) { subst.apply(num_actual_offsets, deltas, expr_offset(q->get_no_pattern(i), off), s1, t1, er); no_pats.push_back(er); } subst.apply(num_actual_offsets, deltas, body, s1, t1, er); er = m_manager.update_quantifier(q, pats.size(), pats.c_ptr(), no_pats.size(), no_pats.c_ptr(), er); m_todo.pop_back(); m_new_exprs.push_back(er); m_apply_cache.insert(n, er); break; } default: UNREACHABLE(); } } SASSERT(m_apply_cache.contains(n)); m_apply_cache.find(n, e); m_new_exprs.push_back(e); result = e; if (s != expr_offset(0,0)) reset_cache(); TRACE("subst_bug", tout << "END substitution::apply\nresult:\n" << mk_pp(e, m_manager) << "\nref_count: " << e->get_ref_count() << "\n";); } inline substitution::color substitution::get_color(expr_offset const & p) const { color c; if (m_color.find(p, c)) return c; return White; } inline void substitution::set_color(expr_offset const & p, color c) { m_color.insert(p, c); } inline void substitution::visit(expr_offset const & p, bool & visited) { if (get_color(p) != Black) { m_todo.push_back(p); visited = false; } } bool substitution::visit_children(expr_offset const & p) { bool visited = true; expr * n = p.get_expr(); unsigned off; expr_offset p1; unsigned j; switch (n->get_kind()) { case AST_VAR: if (find(p, p1) && p != p1) visit(p1, visited); break; case AST_APP: off = p.get_offset(); j = to_app(n)->get_num_args(); while (j > 0) { --j; visit(expr_offset(to_app(n)->get_arg(j), off), visited); } break; default: UNREACHABLE(); } return visited; } bool substitution::acyclic(expr_offset p) { if (get_color(p) == Black) return true; m_todo.reset(); m_todo.push_back(p); while (!m_todo.empty()) { expr_offset p = m_todo.back(); switch (get_color(p)) { case Black: m_todo.pop_back(); break; case White: set_color(p, Grey); if (visit_children(p)) { set_color(p, Black); SASSERT(m_todo.back() == p); m_todo.pop_back(); } break; case Grey: if (!visit_children(p)) return false; set_color(p, Black); SASSERT(m_todo.back() == p); m_todo.pop_back(); break; } } return true; } bool substitution::acyclic() { m_color.reset(); expr_offset r; svector::iterator it = m_vars.begin(); svector::iterator end = m_vars.end(); for (; it != end; ++it) { m_subst.find(it->first, it->second, r); if (!acyclic(r)) return false; } return true; } void substitution::display(std::ostream & out, unsigned num_actual_offsets, unsigned const * deltas) { reset_cache(); for (unsigned i = 0; i < num_actual_offsets; i++) for (unsigned j = 0; j < m_subst.vars_capacity(); j++) { expr_offset r; if (find(j, i, r)) { expr_ref tmp(m_manager); apply(num_actual_offsets, deltas, r, tmp); out << "VAR " << j << ":" << i << " -->\n" << mk_pp(tmp, m_manager) << "\n"; } } } void substitution::display(std::ostream & out) { for (unsigned i = 0; i < m_subst.offsets_capacity(); i++) for (unsigned j = 0; j < m_subst.vars_capacity(); j++) { expr_offset r; if (find(j, i, r)) out << "VAR " << j << ":" << i << " --> " << r.get_offset() << "\n" << mk_pp(r.get_expr(), m_manager) << "\n"; } } z3-4.4.0/src/ast/substitution/expr_offset.h0000644000175000017500000000257412540347414020504 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: expr_offset.h Abstract: Expressions + Offsets. In order to avoid creating variants of terms, we use a pair (expression, offset), where offset is just a small integer. Non ground terms with different offsets are always considered disequal. For example, (f x):1 is different from (f x):2, and (f x):1 is unifiable with x:2. Author: Leonardo de Moura (leonardo) 2008-01-28. Revision History: --*/ #ifndef _EXPR_OFFSET_H_ #define _EXPR_OFFSET_H_ #include"ast.h" class expr_offset { expr * m_expr; unsigned m_offset; public: expr_offset():m_expr(0), m_offset(0) {} expr_offset(expr * e, unsigned o):m_expr(e), m_offset(o) {} expr * get_expr() const { return m_expr; } unsigned get_offset() const { return m_offset; } bool operator==(expr_offset const & other) const { return m_expr == other.m_expr && m_offset == other.m_offset; } bool operator!=(expr_offset const & other) const { return !operator==(other); } unsigned hash() const { unsigned a = m_expr->get_id(); unsigned b = m_offset; unsigned c = 17; mix(a, b, c); return c; } }; typedef std::pair expr_offset_pair; typedef pair_hash, obj_hash > expr_offset_pair_hash; #endif /* _EXPR_OFFSET_H_ */ z3-4.4.0/src/ast/substitution/unifier.cpp0000644000175000017500000001174112540347414020150 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: unifier.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-28. Revision History: --*/ #include"unifier.h" #include"ast_pp.h" void unifier::reset(unsigned num_offsets) { m_todo.reset(); m_find.reset(); m_size.reset(); } /** \brief Find with path compression. */ expr_offset unifier::find(expr_offset p) { buffer path; expr_offset next; while (m_find.find(p, next)) { path.push_back(p); p = next; } buffer::iterator it = path.begin(); buffer::iterator end = path.end(); for (; it != end; ++it) { expr_offset & prev = *it; m_find.insert(prev, p); } return p; } void unifier::save_var(expr_offset const & p, expr_offset const & t) { expr * n = p.get_expr(); if (is_var(n)) { unsigned off = p.get_offset(); m_subst->insert(to_var(n)->get_idx(), off, t); } } /** \brief Merge the equivalence classes of n1 and n2. n2 will be the root of the resultant equivalence class. */ void unifier::union1(expr_offset const & n1, expr_offset const & n2) { DEBUG_CODE({ expr_offset f; SASSERT(!m_find.find(n1, f)); SASSERT(!m_find.find(n2, f)); }); unsigned sz1 = 1; unsigned sz2 = 1; m_size.find(n1, sz1); m_size.find(n2, sz2); m_find.insert(n1, n2); m_size.insert(n2, sz1 + sz2); save_var(n1, n2); } /** \brief Merge the equivalence classes of n1 and n2. The root of the resultant equivalence class is the one with more elements. */ void unifier::union2(expr_offset n1, expr_offset n2) { DEBUG_CODE({ expr_offset f; SASSERT(!m_find.find(n1, f)); SASSERT(!m_find.find(n2, f)); }); unsigned sz1 = 1; unsigned sz2 = 1; m_size.find(n1, sz1); m_size.find(n2, sz2); if (sz1 > sz2) std::swap(n1, n2); m_find.insert(n1, n2); m_size.insert(n2, sz1 + sz2); save_var(n1, n2); } bool unifier::unify_core(expr_offset p1, expr_offset p2) { entry e(p1, p2); m_todo.push_back(e); while (!m_todo.empty()) { entry const & e = m_todo.back(); p1 = find(e.first); p2 = find(e.second); m_todo.pop_back(); if (p1 != p2) { expr * n1 = p1.get_expr(); expr * n2 = p2.get_expr(); SASSERT(!is_quantifier(n1)); SASSERT(!is_quantifier(n2)); bool v1 = is_var(n1); bool v2 = is_var(n2); if (v1 && v2) { union2(p1, p2); } else if (v1) { union1(p1, p2); } else if (v2) { union1(p2, p1); } else { app * a1 = to_app(n1); app * a2 = to_app(n2); unsigned off1 = p1.get_offset(); unsigned off2 = p2.get_offset(); if (a1->get_decl() != a2->get_decl() || a1->get_num_args() != a2->get_num_args()) return false; union2(p1, p2); unsigned j = a1->get_num_args(); while (j > 0) { --j; entry new_e(expr_offset(a1->get_arg(j), off1), expr_offset(a2->get_arg(j), off2)); m_todo.push_back(new_e); } } } } return true; } bool unifier::operator()(unsigned num_exprs, expr ** es, substitution & s, bool use_offsets) { SASSERT(num_exprs > 0); unsigned num_offsets = use_offsets ? num_exprs : 1; reset(num_offsets); m_subst = &s; #if 1 TRACE("unifier", for (unsigned i = 0; i < num_exprs; ++i) tout << mk_pp(es[i], m_manager) << "\n";); for (unsigned i = s.get_num_bindings(); i > 0; ) { --i; std::pair bound; expr_offset root, child; s.get_binding(i, bound, root); TRACE("unifier", tout << bound.first << " |-> " << mk_pp(root.get_expr(), m_manager) << "\n";); if (is_var(root.get_expr())) { var* v = m_manager.mk_var(bound.first,to_var(root.get_expr())->get_sort()); child = expr_offset(v, bound.second); unsigned sz1 = 1; unsigned sz2 = 1; m_size.find(child, sz1); m_size.find(root, sz2); m_find.insert(child, root); m_size.insert(root, sz1 + sz2); } } #endif for (unsigned i = 0; i < num_exprs - 1; i++) { if (!unify_core(expr_offset(es[i], use_offsets ? i : 0), expr_offset(es[i+1], use_offsets ? i + 1 : 0))) { m_last_call_succeeded = false; return m_last_call_succeeded; } } m_last_call_succeeded = m_subst->acyclic(); return m_last_call_succeeded; } bool unifier::operator()(expr * e1, expr * e2, substitution & s, bool use_offsets) { expr * es[2] = { e1, e2 }; return operator()(2, es, s, use_offsets); } z3-4.4.0/src/ast/substitution/matcher.cpp0000644000175000017500000000327012540347414020130 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: matcher.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-02. Revision History: --*/ #include"matcher.h" bool matcher::operator()(expr * e1, expr * e2, substitution & s) { reset(); m_subst = &s; m_todo.push_back(expr_pair(e1, e2)); while (!m_todo.empty()) { expr_pair const & p = m_todo.back(); // if (m_cache.contains(p)) { // m_todo.pop_back(); // continue; // } if (is_var(p.first)) { expr_offset r; if (m_subst->find(to_var(p.first), 0, r)) { if (r.get_expr() != p.second) return false; } else { m_subst->insert(to_var(p.first), 0, expr_offset(p.second, 1)); } m_todo.pop_back(); continue; } if (is_var(p.second)) return false; if (!is_app(p.first)) return false; if (!is_app(p.second)) return false; app * n1 = to_app(p.first); app * n2 = to_app(p.second); if (n1->get_decl() != n2->get_decl()) return false; unsigned num_args1 = n1->get_num_args(); if (num_args1 != n2->get_num_args()) return false; m_todo.pop_back(); if (num_args1 == 0) continue; // m_cache.insert(p); unsigned j = num_args1; while (j > 0) { --j; m_todo.push_back(expr_pair(n1->get_arg(j), n2->get_arg(j))); } } return true; } void matcher::reset() { // m_cache.reset(); m_todo.reset(); } z3-4.4.0/src/ast/substitution/unifier.h0000644000175000017500000000346612540347414017622 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: unifier.h Abstract: Quasi-linear unification. Author: Leonardo de Moura (leonardo) 2008-01-28. Revision History: --*/ #ifndef _UNIFIER_H_ #define _UNIFIER_H_ #include"ast.h" #include"substitution.h" /** \brief Functor for unifying expressions. It implements a quasi-linear unification algorithm. It has support for two different variable banks: left and right. That is, variable i in left bank is considered different from variable i in the right bank. This feature allows us to avoid unnecessary variable renaming. */ class unifier { typedef std::pair entry; ast_manager & m_manager; substitution * m_subst; svector m_todo; expr_offset_map m_find; expr_offset_map m_size; bool m_last_call_succeeded; expr_offset find(expr_offset n); void save_var(expr_offset const & p, expr_offset const & t); void union1(expr_offset const & n1, expr_offset const & n2); void union2(expr_offset n1, expr_offset n2); void reset(unsigned num_offsets); bool unify_core(expr_offset p1, expr_offset p2); public: unifier(ast_manager & m):m_manager(m), m_last_call_succeeded(false) {} /** \brief Unify the given expressions. Return true if succeeded, and store the result in the given substitution. If use_offsets is true, then the variables in the given expressions are assumed to be in different banks. */ bool operator()(unsigned num_exprs, expr ** es, substitution & s, bool use_offsets = true); bool operator()(expr * e1, expr * e2, substitution & s, bool use_offsets = true); }; #endif /* _UNIFIER_H_ */ z3-4.4.0/src/ast/shared_occs.cpp0000644000175000017500000000671412540347414016214 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: shared_occs.cpp Abstract: Functor for computing the shared subterms in a given term. Author: Leonardo de Moura (leonardo) 2011-04-01. Revision History: --*/ #include"shared_occs.h" #include"ast_smt2_pp.h" #include"ref_util.h" inline void shared_occs::insert(expr * t) { obj_hashtable::entry * dummy; if (m_shared.insert_if_not_there_core(t, dummy)) m.inc_ref(t); } void shared_occs::reset() { dec_ref_collection_values(m, m_shared); m_shared.reset(); } void shared_occs::cleanup() { reset(); m_shared.finalize(); m_stack.finalize(); } shared_occs::~shared_occs() { reset(); } inline bool shared_occs::process(expr * t, shared_occs_mark & visited) { switch (t->get_kind()) { case AST_APP: { unsigned num_args = to_app(t)->get_num_args(); if (t->get_ref_count() > 1 && (m_track_atomic || num_args > 0)) { if (visited.is_marked(t)) { insert(t); return true; } visited.mark(t); } if (num_args == 0) return true; // done with t m_stack.push_back(frame(t, 0)); // need to create frame if num_args > 0 return false; } case AST_VAR: if (m_track_atomic && t->get_ref_count() > 1) { if (visited.is_marked(t)) insert(t); else visited.mark(t); } return true; // done with t case AST_QUANTIFIER: if (t->get_ref_count() > 1) { if (visited.is_marked(t)) { insert(t); return true; // done with t } visited.mark(t); } if (!m_visit_quantifiers) return true; m_stack.push_back(frame(t, 0)); return false; default: UNREACHABLE(); return true; } } void shared_occs::operator()(expr * t, shared_occs_mark & visited) { SASSERT(m_stack.empty()); if (process(t, visited)) { return; } SASSERT(!m_stack.empty()); while (!m_stack.empty()) { start: frame & fr = m_stack.back(); expr * curr = fr.first; switch (curr->get_kind()) { case AST_APP: { unsigned num_args = to_app(curr)->get_num_args(); while (fr.second < num_args) { expr * arg = to_app(curr)->get_arg(fr.second); fr.second++; if (!process(arg, visited)) goto start; } break; } case AST_QUANTIFIER: { SASSERT(m_visit_quantifiers); unsigned num_children = m_visit_patterns ? to_quantifier(curr)->get_num_children() : 1; while (fr.second < num_children) { expr * child = to_quantifier(curr)->get_child(fr.second); fr.second++; if (!process(child, visited)) goto start; } break; } default: UNREACHABLE(); break; } m_stack.pop_back(); } } void shared_occs::operator()(expr * t) { SASSERT(m_stack.empty()); shared_occs_mark visited; reset(); operator()(t, visited); } void shared_occs::display(std::ostream & out, ast_manager & m) const { iterator it = begin_shared(); iterator end = end_shared(); for (; it != end; ++it) { out << mk_ismt2_pp(*it, m) << "\n"; } } z3-4.4.0/src/ast/for_each_expr.cpp0000644000175000017500000000274612540347414016544 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: for_each_expr.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-12-28. Revision History: --*/ #include"for_each_expr.h" struct expr_counter_proc { unsigned m_num; expr_counter_proc():m_num(0) {} void operator()(var * n) { m_num++; } void operator()(app * n) { m_num++; if (n->get_decl()->is_associative()) m_num += n->get_num_args() - 2; } void operator()(quantifier * n) { m_num++; } }; unsigned get_num_exprs(expr * n, expr_mark & visited) { expr_counter_proc counter; for_each_expr(counter, visited, n); return counter.m_num; } unsigned get_num_exprs(expr * n, expr_fast_mark1 & visited) { expr_counter_proc counter; for_each_expr_core(counter, visited, n); return counter.m_num; } unsigned get_num_exprs(expr * n) { expr_fast_mark1 visited; return get_num_exprs(n, visited); } namespace has_skolem_functions_ns { struct found {}; struct proc { void operator()(var * n) const {} void operator()(app const * n) const { if (n->get_decl()->is_skolem() && n->get_num_args() > 0) throw found(); } void operator()(quantifier * n) const {} }; }; bool has_skolem_functions(expr * n) { has_skolem_functions_ns::proc p; try { for_each_expr(p, n); } catch (has_skolem_functions_ns::found) { return true; } return false; } z3-4.4.0/src/ast/ast_translation.h0000644000175000017500000000510612540347414016603 0ustar michaelmichael/*++ Copyright (c) 2008 Microsoft Corporation Module Name: ast_translation.h Abstract: AST translation functions Author: Christoph Wintersteiger (t-cwinte) 2008-11-20 Revision History: 2011-05-26: New local translation class. --*/ #ifndef _AST_TRANSLATION_H_ #define _AST_TRANSLATION_H_ #include"ast.h" class ast_translation { struct frame { ast * m_n; unsigned m_idx; unsigned m_cpos; unsigned m_rpos; frame(ast * n, unsigned idx, unsigned cpos, unsigned rpos):m_n(n), m_idx(idx), m_cpos(cpos), m_rpos(rpos) {} }; ast_manager & m_from_manager; ast_manager & m_to_manager; svector m_frame_stack; ptr_vector m_extra_children_stack; // for sort and func_decl, since they have nested AST in their parameters ptr_vector m_result_stack; obj_map m_cache; void cache(ast * s, ast * t); void collect_decl_extra_children(decl * d); void push_frame(ast * n); bool visit(ast * n); void copy_params(decl * d, unsigned rpos, buffer & ps); void mk_sort(sort * s, frame & fr); void mk_func_decl(func_decl * f, frame & fr); ast * process(ast const * n); public: ast_translation(ast_manager & from, ast_manager & to, bool copy_plugins = true) : m_from_manager(from), m_to_manager(to) { if (copy_plugins) m_to_manager.copy_families_plugins(m_from_manager); } ~ast_translation(); template T * operator()(T const * n) { SASSERT(from().contains(const_cast(n))); ast * r = process(n); SASSERT(to().contains(const_cast(r))); return static_cast(r); } ast_manager & from() const { return m_from_manager; } ast_manager & to() const { return m_to_manager; } void reset_cache(); void cleanup(); }; // Translation with non-persistent cache. inline ast * translate(ast const * a, ast_manager & from, ast_manager & to) { return ast_translation(from, to)(a); } inline expr * translate(expr const * e, ast_manager & from, ast_manager & to) { return ast_translation(from, to)(e); } class expr_dependency_translation { ast_translation & m_translation; ptr_vector m_buffer; public: expr_dependency_translation(ast_translation & t):m_translation(t) {} expr_dependency * operator()(expr_dependency * d); }; inline expr_dependency * translate(expr_dependency * d, ast_manager & from, ast_manager & to) { ast_translation t(from, to); expr_dependency_translation td(t); return td(d); } #endif z3-4.4.0/src/ast/pp_params.pyg0000644000175000017500000000372512540347414015735 0ustar michaelmichaeldef_module_params('pp', export=True, description='pretty printer', params=(('max_indent', UINT, UINT_MAX, 'max. indentation in pretty printer'), ('max_num_lines', UINT, UINT_MAX, 'max. number of lines to be displayed in pretty printer'), ('max_width', UINT, 80, 'max. width in pretty printer'), ('max_ribbon', UINT, 80, 'max. ribbon (width - indentation) in pretty printer'), ('max_depth', UINT, 5, 'max. term depth (when pretty printing SMT2 terms/formulas)'), ('min_alias_size', UINT, 10, 'min. size for creating an alias for a shared term (when pretty printing SMT2 terms/formulas)'), ('decimal', BOOL, False, 'pretty print real numbers using decimal notation (the output may be truncated). Z3 adds a ? if the value is not precise'), ('decimal_precision', UINT, 10, 'maximum number of decimal places to be used when pp.decimal=true'), ('bv_literals', BOOL, True, 'use Bit-Vector literals (e.g, #x0F and #b0101) during pretty printing'), ('fp_real_literals', BOOL, False, 'use real-numbered floating point literals (e.g, +1.0p-1) during pretty printing'), ('bv_neg', BOOL, False, 'use bvneg when displaying Bit-Vector literals where the most significant bit is 1'), ('flat_assoc', BOOL, True, 'flat associative operators (when pretty printing SMT2 terms/formulas)'), ('fixed_indent', BOOL, False, 'use a fixed indentation for applications'), ('single_line', BOOL, False, 'ignore line breaks when true'), ('bounded', BOOL, False, 'ignore characters exceeding max widht'), ('simplify_implies', BOOL, True, 'simplify nested implications for pretty printing'))) z3-4.4.0/src/ast/scoped_proof.h0000644000175000017500000000202412540347414016054 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: scoped_proof.h Abstract: Scoped proof environments. Toggles enabling proofs. Author: Nikolaj Bjorner (nbjorner) 2013-08-28 Revision History: --*/ #ifndef _SCOPED_PROOF__H_ #define _SCOPED_PROOF__H_ #include "ast.h" class scoped_proof_mode { ast_manager& m; proof_gen_mode m_mode; public: scoped_proof_mode(ast_manager& m, proof_gen_mode mode): m(m) { m_mode = m.proof_mode(); m.toggle_proof_mode(mode); } ~scoped_proof_mode() { m.toggle_proof_mode(m_mode); } }; class scoped_proof : public scoped_proof_mode { public: scoped_proof(ast_manager& m): scoped_proof_mode(m, PGM_FINE) {} }; class scoped_no_proof : public scoped_proof_mode { public: scoped_no_proof(ast_manager& m): scoped_proof_mode(m, PGM_DISABLED) {} }; class scoped_restore_proof : public scoped_proof_mode { public: scoped_restore_proof(ast_manager& m): scoped_proof_mode(m, m.proof_mode()) {} }; #endif z3-4.4.0/src/ast/func_decl_dependencies.cpp0000644000175000017500000001353012540347414020361 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: func_decl_dependencies.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-12-15. Revision History: --*/ #include"func_decl_dependencies.h" #include"for_each_expr.h" #include"ast_util.h" struct collect_dependencies_proc { ast_manager & m_manager; func_decl_set & m_set; bool m_ng_only; // collect only declarations in non ground expressions collect_dependencies_proc(ast_manager & m, func_decl_set & s, bool ng_only): m_manager(m), m_set(s), m_ng_only(ng_only) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { // We do not need to track dependencies on constants ... if (n->get_num_args()==0) return; if (m_ng_only && is_ground(n)) return; // ... and interpreted function symbols func_decl * d = n->get_decl(); if (d->get_family_id() == null_family_id) { m_set.insert(d); } } }; void collect_func_decls(ast_manager & m, expr * n, func_decl_set & r, bool ng_only) { collect_dependencies_proc proc(m, r, ng_only); for_each_expr(proc, n); } void func_decl_dependencies::reset() { dependency_graph::iterator it = m_deps.begin(); dependency_graph::iterator end = m_deps.end(); for (; it != end; ++it) { func_decl * f = (*it).m_key; func_decl_set * s = (*it).m_value; m_manager.dec_ref(f); dec_ref(m_manager, *s); dealloc(s); } m_deps.reset(); } void func_decl_dependencies::collect_func_decls(expr * n, func_decl_set * s) { ::collect_func_decls(m_manager, n, *s, false); } void func_decl_dependencies::collect_ng_func_decls(expr * n, func_decl_set * s) { ::collect_func_decls(m_manager, n, *s, true); } /** \brief Functor for finding cycles in macro definitions */ class func_decl_dependencies::top_sort { enum color { OPEN, IN_PROGRESS, CLOSED }; dependency_graph & m_deps; typedef obj_map color_map; color_map m_colors; ptr_vector m_todo; func_decl_set * definition(func_decl * f) const { func_decl_set * r = 0; m_deps.find(f, r); return r; } color get_color(func_decl * f) const { if (!f) return CLOSED; color_map::iterator it = m_colors.find_iterator(f); if (it != m_colors.end()) return it->m_value; return OPEN; } void set_color(func_decl * f, color c) { m_colors.insert(f, c); } void visit(func_decl * f, bool & visited) { if (get_color(f) != CLOSED) { m_todo.push_back(f); visited = false; } } bool visit_children(func_decl * f) { func_decl_set * def = definition(f); if (!def) return true; bool visited = true; func_decl_set::iterator it = def->begin(); func_decl_set::iterator end = def->end(); for (; it != end; ++it) { visit(*it, visited); } return visited; } bool all_children_closed(func_decl * f) const { func_decl_set * def = definition(f); if (!def) return true; func_decl_set::iterator it = def->begin(); func_decl_set::iterator end = def->end(); for (; it != end; ++it) { if (get_color(*it) != CLOSED) return false; } return true; } /** \brief Return \c true if a cycle is detected. */ bool main_loop(func_decl * f) { if (get_color(f) == CLOSED) return false; m_todo.push_back(f); while (!m_todo.empty()) { func_decl * cf = m_todo.back(); switch (get_color(cf)) { case CLOSED: m_todo.pop_back(); break; case OPEN: set_color(cf, IN_PROGRESS); if (visit_children(cf)) { SASSERT(m_todo.back() == cf); m_todo.pop_back(); set_color(cf, CLOSED); } break; case IN_PROGRESS: if (all_children_closed(cf)) { SASSERT(m_todo.back() == cf); set_color(cf, CLOSED); } else { m_todo.reset(); return true; } break; default: UNREACHABLE(); } } return false; } public: top_sort(dependency_graph & deps) : m_deps(deps) {} bool operator()(func_decl * new_decl) { // [Leo]: It is not trivial to reuse m_colors between different calls since we are update the graph. // To implement this optimization, we need an incremental topological sort algorithm. // The trick of saving the dependencies will save a lot of time. So, I don't think we really // need a incremental top-sort algo. m_colors.reset(); return main_loop(new_decl); } }; bool func_decl_dependencies::insert(func_decl * f, func_decl_set * s) { if (m_deps.contains(f)) { dealloc(s); return false; } m_deps.insert(f, s); top_sort cycle_detector(m_deps); if (cycle_detector(f)) { m_deps.erase(f); dealloc(s); return false; } m_manager.inc_ref(f); inc_ref(m_manager, *s); return true; } void func_decl_dependencies::erase(func_decl * f) { func_decl_set * s = 0; if (m_deps.find(f, s)) { m_manager.dec_ref(f); dec_ref(m_manager, *s); m_deps.erase(f); dealloc(s); } } void func_decl_dependencies::display(std::ostream & out) { // TODO } z3-4.4.0/src/ast/shared_occs.h0000644000175000017500000000433012540347414015651 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: shared_occs.h Abstract: Functor for computing the shared subterms in a given term. Author: Leonardo de Moura (leonardo) 2011-04-01. Revision History: --*/ #ifndef _SHARED_OCCS_H_ #define _SHARED_OCCS_H_ #include"ast.h" #include"obj_hashtable.h" class shared_occs_mark { ptr_buffer m_to_unmark; public: shared_occs_mark() {} ~shared_occs_mark() { reset(); } bool is_marked(ast * n) { return n->is_marked_so(); } void reset_mark(ast * n) { n->reset_mark_so(); } void mark(ast * n) { if (is_marked(n)) return; n->mark_so(true); m_to_unmark.push_back(n); } void reset() { ptr_buffer::iterator it = m_to_unmark.begin(); ptr_buffer::iterator end = m_to_unmark.end(); for (; it != end; ++it) { reset_mark(*it); } m_to_unmark.reset(); } void mark(ast * n, bool flag) { if (flag) mark(n); else reset_mark(n); } }; /** \brief Functor for computing the shared subterms in a given term. */ class shared_occs { ast_manager & m; bool m_track_atomic; bool m_visit_quantifiers; bool m_visit_patterns; obj_hashtable m_shared; typedef std::pair frame; svector m_stack; bool process(expr * t, shared_occs_mark & visited); void insert(expr * t); public: typedef obj_hashtable::iterator iterator; shared_occs(ast_manager & _m, bool track_atomic = false, bool visit_quantifiers = true, bool visit_patterns = false): m(_m), m_track_atomic(track_atomic), m_visit_quantifiers(visit_quantifiers), m_visit_patterns(visit_patterns) { } ~shared_occs(); void operator()(expr * t); void operator()(expr * t, shared_occs_mark & visited); bool is_shared(expr * t) const { return m_shared.contains(t); } unsigned num_shared() const { return m_shared.size(); } iterator begin_shared() const { return m_shared.begin(); } iterator end_shared() const { return m_shared.end(); } void reset(); void cleanup(); void display(std::ostream & out, ast_manager & mgr) const; }; #endif z3-4.4.0/src/ast/expr_substitution.cpp0000644000175000017500000001040712540347414017543 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: expr_substitution.cpp Abstract: expr -> expr substitution Author: Leonardo (leonardo) 2011-04-29 Notes: --*/ #include"expr_substitution.h" #include"ref_util.h" typedef obj_map expr2proof; typedef obj_map expr2expr_dependency; void expr_substitution::init() { if (proofs_enabled()) m_subst_pr = alloc(expr2proof); if (unsat_core_enabled()) m_subst_dep = alloc(expr2expr_dependency); } expr_substitution::expr_substitution(ast_manager & m): m_manager(m), m_cores_enabled(false), m_proofs_enabled(m.proofs_enabled()) { init(); } expr_substitution::expr_substitution(ast_manager & m, bool core_enabled): m_manager(m), m_cores_enabled(core_enabled), m_proofs_enabled(m.proofs_enabled()) { init(); } expr_substitution::expr_substitution(ast_manager & m, bool core_enabled, bool proofs_enabled): m_manager(m), m_cores_enabled(core_enabled), m_proofs_enabled(proofs_enabled) { SASSERT(!proofs_enabled || m.proofs_enabled()); init(); } expr_substitution::~expr_substitution() { reset(); } void expr_substitution::insert(expr * c, expr * def, proof * def_pr, expr_dependency * def_dep) { obj_map::obj_map_entry * entry = m_subst.insert_if_not_there2(c, 0); if (entry->get_data().m_value == 0) { // new entry m_manager.inc_ref(c); m_manager.inc_ref(def); entry->get_data().m_value = def; if (proofs_enabled()) { SASSERT(!m_subst_pr->contains(c)); m_subst_pr->insert(c, def_pr); m_manager.inc_ref(def_pr); } if (unsat_core_enabled()) { SASSERT(!m_subst_dep->contains(c)); m_subst_dep->insert(c, def_dep); m_manager.inc_ref(def_dep); } } else { // replacing entry m_manager.inc_ref(def); m_manager.dec_ref(entry->get_data().m_value); entry->get_data().m_value = def; if (proofs_enabled()) { obj_map::obj_map_entry * entry_pr = m_subst_pr->find_core(c); SASSERT(entry_pr != 0); m_manager.inc_ref(def_pr); m_manager.dec_ref(entry_pr->get_data().m_value); entry_pr->get_data().m_value = def_pr; } if (unsat_core_enabled()) { obj_map::obj_map_entry * entry_dep = m_subst_dep->find_core(c); SASSERT(entry_dep != 0); m_manager.inc_ref(def_dep); m_manager.dec_ref(entry_dep->get_data().m_value); entry_dep->get_data().m_value = def_dep; } } } void expr_substitution::erase(expr * c) { if (proofs_enabled()) { proof * pr = 0; if (m_subst_pr->find(c, pr)) { m_manager.dec_ref(pr); m_subst_pr->erase(c); } } if (unsat_core_enabled()) { expr_dependency * dep = 0; if (m_subst_dep->find(c, dep)) { m_manager.dec_ref(dep); m_subst_dep->erase(c); } } expr * def = 0; if (m_subst.find(c, def)) { m_manager.dec_ref(c); m_manager.dec_ref(def); m_subst.erase(c); } } bool expr_substitution::find(expr * c, expr * & def, proof * & def_pr) { if (m_subst.find(c, def)) { if (proofs_enabled()) m_subst_pr->find(c, def_pr); return true; } return false; } bool expr_substitution::find(expr * c, expr * & def, proof * & def_pr, expr_dependency * & def_dep) { if (m_subst.find(c, def)) { if (proofs_enabled()) m_subst_pr->find(c, def_pr); if (unsat_core_enabled()) m_subst_dep->find(c, def_dep); return true; } return false; } bool expr_substitution::contains(expr * s) { return m_subst.contains(s); } void expr_substitution::reset() { dec_ref_map_key_values(m_manager, m_subst); if (proofs_enabled()) dec_ref_map_values(m_manager, *m_subst_pr); if (unsat_core_enabled()) dec_ref_map_values(m_manager, *m_subst_dep); } void expr_substitution::cleanup() { reset(); m_subst.finalize(); if (proofs_enabled()) m_subst_pr->finalize(); if (unsat_core_enabled()) m_subst_dep->finalize(); } z3-4.4.0/src/ast/expr_map.cpp0000644000175000017500000000415512540347414015547 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: expr_map.cpp Abstract: Mapping from expressions to expressions + proofs. This mapping is used to cache simplification results. For every entry [e1->(e2, p)] we have that p is a proof that (= e1 e2). Author: Leonardo (leonardo) 2008-01-03 Notes: --*/ #include"expr_map.h" #include"dec_ref_util.h" expr_map::expr_map(ast_manager & m): m_manager(m), m_store_proofs(m.proofs_enabled()) { } expr_map::expr_map(ast_manager & m, bool store_proofs): m_manager(m), m_store_proofs(store_proofs) { } expr_map::~expr_map() { reset(); } void expr_map::insert(expr * k, expr * d, proof * p) { m_manager.inc_ref(d); obj_map::obj_map_entry * entry = m_expr2expr.find_core(k); if (entry != 0) { m_manager.dec_ref(entry->get_data().m_value); entry->get_data().m_value = d; if (m_store_proofs) { m_manager.inc_ref(p); obj_map::obj_map_entry * entry_pr = m_expr2pr.find_core(k); SASSERT(entry_pr != 0); m_manager.dec_ref(entry_pr->get_data().m_value); entry_pr->get_data().m_value = p; } } else { m_manager.inc_ref(k); m_expr2expr.insert(k, d); if (m_store_proofs) { m_manager.inc_ref(p); m_expr2pr.insert(k, p); } } } void expr_map::get(expr * k, expr * & d, proof * & p) const { if (m_expr2expr.find(k, d)) { p = 0; if (m_store_proofs) m_expr2pr.find(k, p); } } void expr_map::erase(expr * k) { expr * v; if (m_expr2expr.find(k, v)) { m_expr2expr.erase(k); m_manager.dec_ref(v); if (m_store_proofs) { proof * pr = 0; m_expr2pr.find(k, pr); m_expr2pr.erase(k); m_manager.dec_ref(pr); } m_manager.dec_ref(k); } } void expr_map::reset() { dec_ref_values(m_manager, m_expr2pr); dec_ref_key_values(m_manager, m_expr2expr); } void expr_map::flush() { reset(); m_expr2expr.finalize(); m_expr2pr.finalize(); } z3-4.4.0/src/ast/ast_translation.cpp0000644000175000017500000003123712540347414017142 0ustar michaelmichael/*++ Copyright (c) 2008 Microsoft Corporation Module Name: ast_translation.cpp Abstract: AST translation functions Author: Christoph Wintersteiger (t-cwinte) 2008-11-20 Revision History: --*/ #include "arith_decl_plugin.h" #include "bv_decl_plugin.h" #include "datatype_decl_plugin.h" #include "array_decl_plugin.h" #include "format.h" #include "ast_translation.h" #include "ast_ll_pp.h" ast_translation::~ast_translation() { reset_cache(); } void ast_translation::cleanup() { reset_cache(); m_cache.finalize(); m_result_stack.finalize(); m_frame_stack.finalize(); m_extra_children_stack.finalize(); } void ast_translation::reset_cache() { obj_map::iterator it = m_cache.begin(); obj_map::iterator end = m_cache.end(); for (; it != end; ++it) { m_from_manager.dec_ref(it->m_key); m_to_manager.dec_ref(it->m_value); } m_cache.reset(); } void ast_translation::cache(ast * s, ast * t) { SASSERT(!m_cache.contains(s)); if (s->get_ref_count() > 1) { m_cache.insert(s, t); m_from_manager.inc_ref(s); m_to_manager.inc_ref(t); } } void ast_translation::collect_decl_extra_children(decl * d) { unsigned num_params = d->get_num_parameters(); for (unsigned i = 0; i < num_params; i++) { parameter const & p = d->get_parameter(i); if (p.is_ast()) m_extra_children_stack.push_back(p.get_ast()); } } void ast_translation::push_frame(ast * n) { m_frame_stack.push_back(frame(n, 0, m_extra_children_stack.size(), m_result_stack.size())); switch (n->get_kind()) { case AST_SORT: case AST_FUNC_DECL: collect_decl_extra_children(to_decl(n)); break; default: break; } } bool ast_translation::visit(ast * n) { ast * r; if (n->get_ref_count() > 1 && m_cache.find(n, r)) { m_result_stack.push_back(r); return true; } push_frame(n); return false; } void ast_translation::copy_params(decl * d, unsigned rpos, buffer & ps) { unsigned num = d->get_num_parameters(); unsigned j = rpos; for (unsigned i = 0; i < num; i++) { parameter const & p = d->get_parameter(i); if (p.is_ast()) { ps.push_back(parameter(m_result_stack[j])); j++; } else if (p.is_external()) { SASSERT(d->get_family_id() != null_family_id); decl_plugin & from_plugin = *(m_from_manager.get_plugin(d->get_family_id())); decl_plugin & to_plugin = *(m_to_manager.get_plugin(d->get_family_id())); ps.push_back(from_plugin.translate(p, to_plugin)); } else { ps.push_back(p); } } } void ast_translation::mk_sort(sort * s, frame & fr) { sort_info * si = s->get_info(); sort * new_s; if (si == 0) { // TODO: investigate: this branch is probably unreachable. // It became unreachable after we started using mk_uninterpreted_sort for creating uninterpreted sorts, // and mk_uninterpreted_sort actually creates a user_sort. new_s = m_to_manager.mk_uninterpreted_sort(s->get_name()); SASSERT(m_result_stack.size() == fr.m_rpos); } else { buffer ps; copy_params(s, fr.m_rpos, ps); new_s = m_to_manager.mk_sort(s->get_name(), sort_info(si->get_family_id(), si->get_decl_kind(), si->get_num_elements(), si->get_num_parameters(), ps.c_ptr(), s->private_parameters())); } m_result_stack.shrink(fr.m_rpos); m_result_stack.push_back(new_s); m_extra_children_stack.shrink(fr.m_cpos); cache(s, new_s); m_frame_stack.pop_back(); } void ast_translation::mk_func_decl(func_decl * f, frame & fr) { func_decl_info * fi = f->get_info(); SASSERT(fr.m_cpos <= m_extra_children_stack.size()); unsigned num_extra = m_extra_children_stack.size() - fr.m_cpos; sort ** new_domain = reinterpret_cast(m_result_stack.c_ptr() + fr.m_rpos + num_extra); sort * new_range = static_cast(m_result_stack.back()); func_decl * new_f; if (fi == 0) { new_f = m_to_manager.mk_func_decl(f->get_name(), f->get_arity(), new_domain, new_range); } else { buffer ps; copy_params(f, fr.m_rpos, ps); func_decl_info new_fi(fi->get_family_id(), fi->get_decl_kind(), fi->get_num_parameters(), ps.c_ptr()); new_fi.set_left_associative(fi->is_left_associative()); new_fi.set_right_associative(fi->is_right_associative()); new_fi.set_flat_associative(fi->is_flat_associative()); new_fi.set_commutative(fi->is_commutative()); new_fi.set_chainable(fi->is_chainable()); new_fi.set_pairwise(fi->is_pairwise()); new_fi.set_injective(fi->is_injective()); new_fi.set_skolem(fi->is_skolem()); new_fi.set_idempotent(fi->is_idempotent()); new_f = m_to_manager.mk_func_decl(f->get_name(), f->get_arity(), new_domain, new_range, new_fi); } TRACE("ast_translation", tout << f->get_name() << " "; if (fi) tout << *fi; tout << "\n"; tout << "---->\n"; tout << new_f->get_name() << " "; if (new_f->get_info()) tout << *(new_f->get_info()); tout << "\n";); m_result_stack.shrink(fr.m_rpos); m_result_stack.push_back(new_f); m_extra_children_stack.shrink(fr.m_cpos); cache(f, new_f); m_frame_stack.pop_back(); } ast * ast_translation::process(ast const * _n) { SASSERT(m_result_stack.empty()); SASSERT(m_frame_stack.empty()); SASSERT(m_extra_children_stack.empty()); if (!visit(const_cast(_n))) { while (!m_frame_stack.empty()) { loop: frame & fr = m_frame_stack.back(); ast * n = fr.m_n; ast * r; TRACE("ast_translation", tout << mk_ll_pp(n, m_from_manager, false) << "\n";); if (fr.m_idx == 0 && n->get_ref_count() > 1 && m_cache.find(n, r)) { SASSERT(m_result_stack.size() == fr.m_rpos); m_result_stack.push_back(r); m_extra_children_stack.shrink(fr.m_cpos); m_frame_stack.pop_back(); TRACE("ast_translation", tout << "hit\n";); continue; } switch (n->get_kind()) { case AST_VAR: { if (fr.m_idx == 0) { fr.m_idx = 1; if (!visit(to_var(n)->get_sort())) goto loop; } sort * new_s = to_sort(m_result_stack.back()); var * new_var = m_to_manager.mk_var(to_var(n)->get_idx(), new_s); m_result_stack.pop_back(); m_result_stack.push_back(new_var); cache(n, new_var); m_frame_stack.pop_back(); break; } case AST_APP: { if (fr.m_idx == 0) { fr.m_idx = 1; if (!visit(to_app(n)->get_decl())) goto loop; } unsigned num = to_app(n)->get_num_args(); while (fr.m_idx <= num) { expr * arg = to_app(n)->get_arg(fr.m_idx - 1); fr.m_idx++; if (!visit(arg)) goto loop; } func_decl * new_f = to_func_decl(m_result_stack[fr.m_rpos]); expr ** new_args = reinterpret_cast(m_result_stack.c_ptr() + fr.m_rpos + 1); expr * new_app = m_to_manager.mk_app(new_f, num, new_args); m_result_stack.shrink(fr.m_rpos); m_result_stack.push_back(new_app); cache(n, new_app); m_frame_stack.pop_back(); break; } case AST_QUANTIFIER: { unsigned num_decls = to_quantifier(n)->get_num_decls(); unsigned num = num_decls + to_quantifier(n)->get_num_children(); while (fr.m_idx < num) { ast * child; if (fr.m_idx < num_decls) child = to_quantifier(n)->get_decl_sort(fr.m_idx); else child = to_quantifier(n)->get_child(fr.m_idx - num_decls); fr.m_idx++; if (!visit(child)) goto loop; } symbol const * dnames = to_quantifier(n)->get_decl_names(); sort ** dsorts = reinterpret_cast(m_result_stack.c_ptr() + fr.m_rpos); expr * body = static_cast(m_result_stack[fr.m_rpos + num_decls]); unsigned num_pats = to_quantifier(n)->get_num_patterns(); expr ** pats = reinterpret_cast(m_result_stack.c_ptr() + fr.m_rpos + num_decls + 1); unsigned num_no_pats = to_quantifier(n)->get_num_no_patterns(); expr ** no_pats = pats + num_pats; quantifier * new_q = m_to_manager.mk_quantifier(to_quantifier(n)->is_forall(), num_decls, dsorts, dnames, body, to_quantifier(n)->get_weight(), to_quantifier(n)->get_qid(), to_quantifier(n)->get_skid(), num_pats, pats, num_no_pats, no_pats); m_result_stack.shrink(fr.m_rpos); m_result_stack.push_back(new_q); cache(n, new_q); m_frame_stack.pop_back(); break; } case AST_SORT: { SASSERT(fr.m_cpos <= m_extra_children_stack.size()); unsigned num = m_extra_children_stack.size() - fr.m_cpos; while (fr.m_idx < num) { ast * c = m_extra_children_stack[fr.m_cpos + fr.m_idx]; fr.m_idx++; if (!visit(c)) goto loop; } mk_sort(to_sort(n), fr); break; } case AST_FUNC_DECL: { SASSERT(fr.m_cpos <= m_extra_children_stack.size()); unsigned num_extra = m_extra_children_stack.size() - fr.m_cpos; unsigned arity = to_func_decl(n)->get_arity(); unsigned num = num_extra + arity + 1; while (fr.m_idx < num) { ast * c; if (fr.m_idx < num_extra) c = m_extra_children_stack[fr.m_cpos + fr.m_idx]; else if (fr.m_idx < num_extra + arity) c = to_func_decl(n)->get_domain(fr.m_idx - num_extra); else c = to_func_decl(n)->get_range(); fr.m_idx++; if (!visit(c)) goto loop; } mk_func_decl(to_func_decl(n), fr); break; } default: UNREACHABLE(); break; } } } SASSERT(m_result_stack.size() == 1); ast * r = m_result_stack.back(); m_result_stack.reset(); return r; } expr_dependency * expr_dependency_translation::operator()(expr_dependency * d) { if (d == 0) return d; m_buffer.reset(); m_translation.from().linearize(d, m_buffer); unsigned sz = m_buffer.size(); SASSERT(sz >= 1); for (unsigned i = 0; i < sz; i++) { m_buffer[i] = m_translation(m_buffer[i]); } return m_translation.to().mk_join(sz, m_buffer.c_ptr()); } z3-4.4.0/src/ast/datatype_decl_plugin.cpp0000644000175000017500000011361512540347414020116 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: datatype_decl_plugin.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-10. Revision History: --*/ #include"datatype_decl_plugin.h" #include"warning.h" #include"ast_smt2_pp.h" /** \brief Auxiliary class used to declare inductive datatypes. */ class accessor_decl { symbol m_name; type_ref m_type; public: accessor_decl(const symbol & n, type_ref r):m_name(n), m_type(r) {} symbol const & get_name() const { return m_name; } type_ref const & get_type() const { return m_type; } }; accessor_decl * mk_accessor_decl(symbol const & n, type_ref const & t) { return alloc(accessor_decl, n, t); } void del_accessor_decl(accessor_decl * d) { dealloc(d); } void del_accessor_decls(unsigned num, accessor_decl * const * as) { for (unsigned i = 0; i < num; i++) del_accessor_decl(as[i]); } /** \brief Auxiliary class used to declare inductive datatypes. */ class constructor_decl { symbol m_name; symbol m_recogniser_name; ptr_vector m_accessors; public: constructor_decl(const symbol & n, const symbol & r, unsigned num_accessors, accessor_decl * const * accessors): m_name(n), m_recogniser_name(r), m_accessors(num_accessors, accessors) {} ~constructor_decl() { std::for_each(m_accessors.begin(), m_accessors.end(), delete_proc()); } symbol const & get_name() const { return m_name; } symbol const & get_recognizer_name() const { return m_recogniser_name; } ptr_vector const & get_accessors() const { return m_accessors; } }; constructor_decl * mk_constructor_decl(symbol const & n, symbol const & r, unsigned num_accessors, accessor_decl * const * accessors) { return alloc(constructor_decl, n, r, num_accessors, accessors); } void del_constructor_decl(constructor_decl * d) { dealloc(d); } void del_constructor_decls(unsigned num, constructor_decl * const * cs) { for (unsigned i = 0; i < num; i++) del_constructor_decl(cs[i]); } /** \brief Auxiliary class used to declare inductive datatypes. */ class datatype_decl { symbol m_name; ptr_vector m_constructors; public: datatype_decl(const symbol & n, unsigned num_constructors, constructor_decl * const * constructors): m_name(n), m_constructors(num_constructors, constructors) { } ~datatype_decl() { std::for_each(m_constructors.begin(), m_constructors.end(), delete_proc()); } symbol const & get_name() const { return m_name; } ptr_vector const & get_constructors() const { return m_constructors; } }; datatype_decl * mk_datatype_decl(symbol const & n, unsigned num_constructors, constructor_decl * const * cs) { return alloc(datatype_decl, n, num_constructors, cs); } void del_datatype_decl(datatype_decl * d) { dealloc(d); } void del_datatype_decls(unsigned num, datatype_decl * const * ds) { for (unsigned i = 0; i < num; i++) del_datatype_decl(ds[i]); } typedef buffer bool_buffer; struct invalid_datatype {}; static parameter const & read(unsigned num_parameters, parameter const * parameters, unsigned idx, bool_buffer & read_pos) { if (idx >= num_parameters) { throw invalid_datatype(); } if (idx >= read_pos.size()) { read_pos.resize(idx+1, false); } read_pos[idx] = true; return parameters[idx]; } static int read_int(unsigned num_parameters, parameter const * parameters, unsigned idx, bool_buffer & read_pos) { const parameter & r = read(num_parameters, parameters, idx, read_pos); if (!r.is_int()) { throw invalid_datatype(); } return r.get_int(); } static symbol read_symbol(unsigned num_parameters, parameter const * parameters, unsigned idx, bool_buffer & read_pos) { parameter const & r = read(num_parameters, parameters, idx, read_pos); if (!r.is_symbol()) { throw invalid_datatype(); } return r.get_symbol(); } enum status { WHITE, GRAY, BLACK }; /** \brief Return true if the inductive datatype is recursive. Pre-condition: The given argument constains the parameters of an inductive datatype. */ static bool is_recursive_datatype(parameter const * parameters) { unsigned num_types = parameters[0].get_int(); unsigned top_tid = parameters[1].get_int(); buffer already_found(num_types, WHITE); buffer todo; todo.push_back(top_tid); while (!todo.empty()) { unsigned tid = todo.back(); if (already_found[tid] == BLACK) { todo.pop_back(); continue; } already_found[tid] = GRAY; unsigned o = parameters[2 + 2*tid + 1].get_int(); // constructor offset unsigned num_constructors = parameters[o].get_int(); bool can_process = true; for (unsigned s = 1; s <= num_constructors; s++) { unsigned k_i = parameters[o + s].get_int(); unsigned num_accessors = parameters[k_i + 2].get_int(); for (unsigned r = 0; r < num_accessors; r++) { parameter const & a_type = parameters[k_i + 4 + 2*r]; if (a_type.is_int()) { unsigned tid_prime = a_type.get_int(); switch (already_found[tid_prime]) { case WHITE: todo.push_back(tid_prime); can_process = false; break; case GRAY: // type is recursive return true; case BLACK: break; } } } } if (can_process) { already_found[tid] = BLACK; todo.pop_back(); } } return false; } /** \brief Return the size of the inductive datatype. Pre-condition: The given argument constains the parameters of an inductive datatype. */ static sort_size get_datatype_size(parameter const * parameters) { unsigned num_types = parameters[0].get_int(); unsigned top_tid = parameters[1].get_int(); buffer szs(num_types, sort_size()); buffer already_found(num_types, WHITE); buffer todo; todo.push_back(top_tid); while (!todo.empty()) { unsigned tid = todo.back(); if (already_found[tid] == BLACK) { todo.pop_back(); continue; } already_found[tid] = GRAY; unsigned o = parameters[2 + 2*tid + 1].get_int(); // constructor offset unsigned num_constructors = parameters[o].get_int(); bool is_very_big = false; bool can_process = true; for (unsigned s = 1; s <= num_constructors; s++) { unsigned k_i = parameters[o+s].get_int(); unsigned num_accessors = parameters[k_i+2].get_int(); for (unsigned r = 0; r < num_accessors; r++) { parameter const & a_type = parameters[k_i+4 + 2*r]; if (a_type.is_int()) { int tid_prime = a_type.get_int(); switch (already_found[tid_prime]) { case WHITE: todo.push_back(tid_prime); can_process = false; break; case GRAY: // type is recursive return sort_size(); case BLACK: break; } } else { SASSERT(a_type.is_ast()); sort * ty = to_sort(a_type.get_ast()); if (ty->is_infinite()) { // type is infinite return sort_size(); } else if (ty->is_very_big()) { is_very_big = true; } } } } if (can_process) { todo.pop_back(); already_found[tid] = BLACK; if (is_very_big) { szs[tid] = sort_size::mk_very_big(); } else { // the type is not infinite nor the number of elements is infinite... // computing the number of elements rational num; for (unsigned s = 1; s <= num_constructors; s++) { unsigned k_i = parameters[o+s].get_int(); unsigned num_accessors = parameters[k_i+2].get_int(); rational c_num(1); for (unsigned r = 0; r < num_accessors; r++) { parameter const & a_type = parameters[k_i+4 + 2*r]; if (a_type.is_int()) { int tid_prime = a_type.get_int(); SASSERT(!szs[tid_prime].is_infinite() && !szs[tid_prime].is_very_big()); c_num *= rational(szs[tid_prime].size(),rational::ui64()); } else { SASSERT(a_type.is_ast()); sort * ty = to_sort(a_type.get_ast()); SASSERT(!ty->is_infinite() && !ty->is_very_big()); c_num *= rational(ty->get_num_elements().size(), rational::ui64()); } } num += c_num; } szs[tid] = sort_size(num); } } } return szs[top_tid]; } /** \brief Return true if the inductive datatype is well-founded. Pre-condition: The given argument constains the parameters of an inductive datatype. */ static bool is_well_founded(parameter const * parameters) { unsigned num_types = parameters[0].get_int(); buffer well_founded(num_types, false); unsigned num_well_founded = 0; bool changed; do { changed = false; for (unsigned tid = 0; tid < num_types; tid++) { if (!well_founded[tid]) { unsigned o = parameters[2 + 2*tid + 1].get_int(); // constructor offset unsigned num_constructors = parameters[o].get_int(); for (unsigned s = 1; s <= num_constructors; s++) { unsigned k_i = parameters[o + s].get_int(); unsigned num_accessors = parameters[k_i + 2].get_int(); unsigned r = 0; for (; r < num_accessors; r++) { parameter const & a_type = parameters[k_i + 4 + 2*r]; if (a_type.is_int() && !well_founded[a_type.get_int()]) { break; } } if (r == num_accessors) { changed = true; well_founded[tid] = true; num_well_founded++; break; } } } } } while(changed && num_well_founded < num_types); unsigned tid = parameters[1].get_int(); return well_founded[tid]; } datatype_decl_plugin::~datatype_decl_plugin() { SASSERT(m_util.get() == 0); } void datatype_decl_plugin::finalize() { m_util = 0; // force deletion } datatype_util & datatype_decl_plugin::get_util() const { SASSERT(m_manager); if (m_util.get() == 0) { m_util = alloc(datatype_util, *m_manager); } return *(m_util.get()); } sort * datatype_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { try { if (k != DATATYPE_SORT) { throw invalid_datatype(); } buffer found; unsigned num_types = read_int(num_parameters, parameters, 0, found); if (num_types == 0) { throw invalid_datatype(); } unsigned tid = read_int(num_parameters, parameters, 1, found); for (unsigned j = 0; j < num_types; j++) { read_symbol(num_parameters, parameters, 2 + 2*j, found); // type name unsigned o = read_int(num_parameters, parameters, 2 + 2*j + 1, found); unsigned num_constructors = read_int(num_parameters, parameters, o, found); if (num_constructors == 0) { throw invalid_datatype(); } for (unsigned s = 1; s <= num_constructors; s++) { unsigned k_i = read_int(num_parameters, parameters, o + s, found); read_symbol(num_parameters, parameters, k_i, found); // constructor name read_symbol(num_parameters, parameters, k_i + 1, found); // recognizer name unsigned num_accessors = read_int(num_parameters, parameters, k_i + 2, found); unsigned first_accessor = k_i+3; for (unsigned r = 0; r < num_accessors; r++) { read_symbol(num_parameters, parameters, first_accessor + 2*r, found); // accessor name parameter const & a_type = read(num_parameters, parameters, first_accessor + 2*r + 1, found); // accessort type if (!a_type.is_int() && !a_type.is_ast()) { throw invalid_datatype(); if (a_type.is_ast() && !is_sort(a_type.get_ast())) { throw invalid_datatype(); } } } } } // check if there is no garbage if (found.size() != num_parameters || std::find(found.begin(), found.end(), false) != found.end()) { throw invalid_datatype(); } if (!is_well_founded(parameters)) { m_manager->raise_exception("datatype is not well-founded"); return 0; } // compute datatype size sort_size ts = get_datatype_size(parameters); symbol const & tname = parameters[2+2*tid].get_symbol(); return m_manager->mk_sort(tname, sort_info(m_family_id, k, ts, num_parameters, parameters, true)); } catch (invalid_datatype) { m_manager->raise_exception("invalid datatype"); return 0; } } static sort * get_other_datatype(ast_manager & m, family_id datatype_fid, sort * source_datatype, unsigned tid) { SASSERT(source_datatype->get_family_id() == datatype_fid); SASSERT(source_datatype->get_decl_kind() == DATATYPE_SORT); if (tid == static_cast(source_datatype->get_parameter(1).get_int())) { return source_datatype; } buffer p; unsigned n = source_datatype->get_num_parameters(); for (unsigned i = 0; i < n; i++) { p.push_back(source_datatype->get_parameter(i)); } p[1] = parameter(tid); return m.mk_sort(datatype_fid, DATATYPE_SORT, n, p.c_ptr()); } static sort * get_type(ast_manager & m, family_id datatype_fid, sort * source_datatype, parameter const & p) { SASSERT(p.is_ast() || p.is_int()); if (p.is_ast()) { return to_sort(p.get_ast()); } else { return get_other_datatype(m, datatype_fid, source_datatype, p.get_int()); } } func_decl * datatype_decl_plugin::mk_update_field( unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { decl_kind k = OP_DT_UPDATE_FIELD; ast_manager& m = *m_manager; if (num_parameters != 1 || !parameters[0].is_ast()) { m.raise_exception("invalid parameters for datatype field update"); return 0; } if (arity != 2) { m.raise_exception("invalid number of arguments for datatype field update"); return 0; } func_decl* acc = 0; if (is_func_decl(parameters[0].get_ast())) { acc = to_func_decl(parameters[0].get_ast()); } if (acc && !get_util().is_accessor(acc)) { acc = 0; } if (!acc) { m.raise_exception("datatype field update requires a datatype accessor as the second argument"); return 0; } sort* dom = acc->get_domain(0); sort* rng = acc->get_range(); if (dom != domain[0]) { m.raise_exception("first argument to field update should be a data-type"); return 0; } if (rng != domain[1]) { std::ostringstream buffer; buffer << "second argument to field update should be " << mk_ismt2_pp(rng, m) << " instead of " << mk_ismt2_pp(domain[1], m); m.raise_exception(buffer.str().c_str()); return 0; } range = domain[0]; func_decl_info info(m_family_id, k, num_parameters, parameters); return m.mk_func_decl(symbol("update_field"), arity, domain, range, info); } func_decl * datatype_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (k == OP_DT_UPDATE_FIELD) { return mk_update_field(num_parameters, parameters, arity, domain, range); } if (num_parameters < 2 || !parameters[0].is_ast() || !is_sort(parameters[0].get_ast())) { m_manager->raise_exception("invalid parameters for datatype operator"); return 0; } sort * datatype = to_sort(parameters[0].get_ast()); if (datatype->get_family_id() != m_family_id || datatype->get_decl_kind() != DATATYPE_SORT) { m_manager->raise_exception("invalid parameters for datatype operator"); return 0; } for (unsigned i = 1; i < num_parameters; i++) { if (!parameters[i].is_int()) { m_manager->raise_exception("invalid parameters for datatype operator"); return 0; } } unsigned c_idx = parameters[1].get_int(); unsigned tid = datatype->get_parameter(1).get_int(); unsigned o = datatype->get_parameter(2 + 2 * tid + 1).get_int(); unsigned num_constructors = datatype->get_parameter(o).get_int(); if (c_idx >= num_constructors) { m_manager->raise_exception("invalid parameters for datatype operator"); return 0; } unsigned k_i = datatype->get_parameter(o + 1 + c_idx).get_int(); switch (k) { case OP_DT_CONSTRUCTOR: if (num_parameters != 2) { m_manager->raise_exception("invalid parameters for datatype constructor"); return 0; } else { symbol c_name = datatype->get_parameter(k_i).get_symbol(); unsigned num_accessors = datatype->get_parameter(k_i + 2).get_int(); if (num_accessors != arity) { m_manager->raise_exception("invalid domain size for datatype constructor"); return 0; } // // the reference count to domain could be 0. // we need to ensure that creating a temporary // copy of the same type causes a free. // sort_ref_vector domain_check(*m_manager); for (unsigned r = 0; r < num_accessors; r++) { sort_ref ty(*m_manager); ty = get_type(*m_manager, m_family_id, datatype, datatype->get_parameter(k_i + 4 + 2*r)); domain_check.push_back(ty); if (ty != domain[r]) { m_manager->raise_exception("invalid domain for datatype constructor"); return 0; } } func_decl_info info(m_family_id, k, num_parameters, parameters); info.m_private_parameters = true; SASSERT(info.private_parameters()); return m_manager->mk_func_decl(c_name, arity, domain, datatype, info); } case OP_DT_RECOGNISER: if (num_parameters != 2 || arity != 1 || domain[0] != datatype) { m_manager->raise_exception("invalid parameters for datatype recogniser"); return 0; } else { symbol r_name = datatype->get_parameter(k_i + 1).get_symbol(); sort * b = m_manager->mk_bool_sort(); func_decl_info info(m_family_id, k, num_parameters, parameters); info.m_private_parameters = true; SASSERT(info.private_parameters()); return m_manager->mk_func_decl(r_name, arity, domain, b, info); } case OP_DT_ACCESSOR: if (num_parameters != 3 || arity != 1 || domain[0] != datatype) { m_manager->raise_exception("invalid parameters for datatype accessor"); return 0; } else { unsigned a_idx = parameters[2].get_int(); unsigned num_accessors = datatype->get_parameter(k_i + 2).get_int(); if (a_idx >= num_accessors) { m_manager->raise_exception("invalid datatype accessor"); return 0; } symbol a_name = datatype->get_parameter(k_i + 3 + 2*a_idx).get_symbol(); sort * a_type = get_type(*m_manager, m_family_id, datatype, datatype->get_parameter(k_i + 4 + 2*a_idx)); func_decl_info info(m_family_id, k, num_parameters, parameters); info.m_private_parameters = true; SASSERT(info.private_parameters()); return m_manager->mk_func_decl(a_name, arity, domain, a_type, info); } break; case OP_DT_UPDATE_FIELD: UNREACHABLE(); return 0; default: m_manager->raise_exception("invalid datatype operator kind"); return 0; } } bool datatype_decl_plugin::mk_datatypes(unsigned num_datatypes, datatype_decl * const * datatypes, sort_ref_vector & new_types) { buffer p; p.push_back(parameter(num_datatypes)); p.push_back(parameter(-1)); for (unsigned i = 0; i < num_datatypes; i++) { p.push_back(parameter(datatypes[i]->get_name())); p.push_back(parameter(-1)); // offset is unknown at this point } for (unsigned i = 0; i < num_datatypes; i++) { p[3+2*i] = parameter(p.size()); // save offset to constructor table ptr_vector const & constructors = datatypes[i]->get_constructors(); unsigned num_constructors = constructors.size(); p.push_back(parameter(num_constructors)); for (unsigned j = 0; j < num_constructors; j++) { p.push_back(parameter(-1)); // offset is unknown at this point } } for (unsigned i = 0; i < num_datatypes; i++) { unsigned o = p[3+2*i].get_int(); ptr_vector const & constructors = datatypes[i]->get_constructors(); unsigned num_constructors = constructors.size(); for (unsigned j = 0; j < num_constructors; j++) { p[o+1+j] = parameter(p.size()); // save offset to constructor definition constructor_decl * c = constructors[j]; p.push_back(parameter(c->get_name())); p.push_back(parameter(c->get_recognizer_name())); ptr_vector const & accessors = c->get_accessors(); unsigned num_accessors = accessors.size(); p.push_back(parameter(num_accessors)); for (unsigned k = 0; k < num_accessors; k++) { accessor_decl * a = accessors[k]; p.push_back(parameter(a->get_name())); type_ref const & ty = a->get_type(); if (ty.is_idx()) { if (static_cast(ty.get_idx()) >= num_datatypes) { TRACE("datatype", tout << "Index out of bounds: " << ty.get_idx() << "\n";); return false; } p.push_back(parameter(ty.get_idx())); } else { p.push_back(parameter(ty.get_sort())); } } } } for (unsigned i = 0; i < num_datatypes; i++) { p[1] = parameter(i); TRACE("datatype", tout << "new datatype parameters:\n"; for (unsigned j = 0; j < p.size(); j++) { tout << "p[" << j << "] -> " << p[j] << "\n"; }); sort * ty = mk_sort(DATATYPE_SORT, p.size(), p.c_ptr()); if (ty == 0) { TRACE("datatype", tout << "Failed to create datatype sort from parameters\n";); return false; } new_types.push_back(ty); } return true; } expr * datatype_decl_plugin::get_some_value(sort * s) { SASSERT(s->is_sort_of(m_family_id, DATATYPE_SORT)); datatype_util & util = get_util(); func_decl * c = util.get_non_rec_constructor(s); ptr_buffer args; for (unsigned i = 0; i < c->get_arity(); i++) { args.push_back(m_manager->get_some_value(c->get_domain(i))); } return m_manager->mk_app(c, args.size(), args.c_ptr()); } bool datatype_decl_plugin::is_fully_interp(sort const * s) const { SASSERT(s->is_sort_of(m_family_id, DATATYPE_SORT)); parameter const * parameters = s->get_parameters(); unsigned num_types = parameters[0].get_int(); for (unsigned tid = 0; tid < num_types; tid++) { unsigned o = parameters[2 + 2*tid + 1].get_int(); // constructor offset unsigned num_constructors = parameters[o].get_int(); for (unsigned si = 1; si <= num_constructors; si++) { unsigned k_i = parameters[o + si].get_int(); unsigned num_accessors = parameters[k_i + 2].get_int(); unsigned r = 0; for (; r < num_accessors; r++) { parameter const & a_type = parameters[k_i + 4 + 2*r]; if (a_type.is_int()) continue; SASSERT(a_type.is_ast()); sort * arg_s = to_sort(a_type.get_ast()); if (!m_manager->is_fully_interp(arg_s)) return false; } } } return true; } bool datatype_decl_plugin::is_value_visit(expr * arg, ptr_buffer & todo) const { if (!is_app(arg)) return false; family_id fid = to_app(arg)->get_family_id(); if (fid == m_family_id) { if (!get_util().is_constructor(to_app(arg))) return false; if (to_app(arg)->get_num_args() == 0) return true; todo.push_back(to_app(arg)); return true; } else { return m_manager->is_value(arg); } } bool datatype_decl_plugin::is_value(app * e) const { TRACE("dt_is_value", tout << "checking\n" << mk_ismt2_pp(e, *m_manager) << "\n";); if (!get_util().is_constructor(e)) return false; if (e->get_num_args() == 0) return true; // REMARK: if the following check is too expensive, we should // cache the values in the datatype_decl_plugin. ptr_buffer todo; // potentially expensive check for common sub-expressions. for (unsigned i = 0; i < e->get_num_args(); i++) { if (!is_value_visit(e->get_arg(i), todo)) { TRACE("dt_is_value", tout << "not-value:\n" << mk_ismt2_pp(e->get_arg(i), *m_manager) << "\n";); return false; } } while (!todo.empty()) { app * curr = todo.back(); SASSERT(get_util().is_constructor(curr)); todo.pop_back(); for (unsigned i = 0; i < curr->get_num_args(); i++) { if (!is_value_visit(curr->get_arg(i), todo)) { TRACE("dt_is_value", tout << "not-value:\n" << mk_ismt2_pp(curr->get_arg(i), *m_manager) << "\n";); return false; } } } return true; } void datatype_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { if (logic == symbol::null) { op_names.push_back(builtin_name("update-field", OP_DT_UPDATE_FIELD)); } } datatype_util::datatype_util(ast_manager & m): m_manager(m), m_family_id(m.mk_family_id("datatype")), m_asts(m) { } datatype_util::~datatype_util() { std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc >()); } func_decl * datatype_util::get_constructor(sort * ty, unsigned c_id) { unsigned tid = ty->get_parameter(1).get_int(); unsigned o = ty->get_parameter(3 + 2*tid).get_int(); unsigned k_i = ty->get_parameter(o + c_id + 1).get_int(); unsigned num_accessors = ty->get_parameter(k_i + 2).get_int(); parameter p[2] = { parameter(ty), parameter(c_id) }; ptr_buffer domain; for (unsigned r = 0; r < num_accessors; r++) { domain.push_back(get_type(m_manager, m_family_id, ty, ty->get_parameter(k_i + 4 + 2*r))); } func_decl * d = m_manager.mk_func_decl(m_family_id, OP_DT_CONSTRUCTOR, 2, p, domain.size(), domain.c_ptr()); SASSERT(d); return d; } ptr_vector const * datatype_util::get_datatype_constructors(sort * ty) { SASSERT(is_datatype(ty)); ptr_vector * r = 0; if (m_datatype2constructors.find(ty, r)) return r; r = alloc(ptr_vector); m_asts.push_back(ty); m_vectors.push_back(r); m_datatype2constructors.insert(ty, r); unsigned tid = ty->get_parameter(1).get_int(); unsigned o = ty->get_parameter(3 + 2*tid).get_int(); unsigned num_constructors = ty->get_parameter(o).get_int(); for (unsigned c_id = 0; c_id < num_constructors; c_id++) { func_decl * c = get_constructor(ty, c_id); m_asts.push_back(c); r->push_back(c); } return r; } /** \brief Return a constructor mk(T_1, ... T_n) where each T_i is not a datatype or it is a datatype that contains a constructor that will not contain directly or indirectly an element of the given sort. */ func_decl * datatype_util::get_non_rec_constructor(sort * ty) { SASSERT(is_datatype(ty)); func_decl * r = 0; if (m_datatype2nonrec_constructor.find(ty, r)) return r; r = 0; ptr_vector forbidden_set; forbidden_set.push_back(ty); r = get_non_rec_constructor_core(ty, forbidden_set); SASSERT(forbidden_set.back() == ty); SASSERT(r); m_asts.push_back(ty); m_asts.push_back(r); m_datatype2nonrec_constructor.insert(ty, r); return r; } /** \brief Return a constructor mk(T_1, ..., T_n) where each T_i is not a datatype or it is a datatype t not in forbidden_set, and get_non_rec_constructor_core(T_i, forbidden_set union { T_i }) */ func_decl * datatype_util::get_non_rec_constructor_core(sort * ty, ptr_vector & forbidden_set) { // We must select a constructor c(T_1, ..., T_n):T such that // 1) T_i's are not recursive // If there is no such constructor, then we select one that // 2) each type T_i is not recursive or contains a constructor that does not depend on T ptr_vector const * constructors = get_datatype_constructors(ty); ptr_vector::const_iterator it = constructors->begin(); ptr_vector::const_iterator end = constructors->end(); // step 1) for (; it != end; ++it) { func_decl * c = *it; unsigned num_args = c->get_arity(); unsigned i = 0; for (; i < num_args; i++) { sort * T_i = c->get_domain(i); if (is_datatype(T_i)) break; } if (i == num_args) return c; } // step 2) it = constructors->begin(); for (; it != end; ++it) { func_decl * c = *it; TRACE("datatype_util_bug", tout << "non_rec_constructor c: " << c->get_name() << "\n";); unsigned num_args = c->get_arity(); unsigned i = 0; for (; i < num_args; i++) { sort * T_i = c->get_domain(i); TRACE("datatype_util_bug", tout << "c: " << c->get_name() << " i: " << i << " T_i: " << T_i->get_name() << "\n";); if (!is_datatype(T_i)) { TRACE("datatype_util_bug", tout << "T_i is not a datatype\n";); continue; } if (std::find(forbidden_set.begin(), forbidden_set.end(), T_i) != forbidden_set.end()) { TRACE("datatype_util_bug", tout << "T_i is in forbidden_set\n";); break; } forbidden_set.push_back(T_i); func_decl * nested_c = get_non_rec_constructor_core(T_i, forbidden_set); SASSERT(forbidden_set.back() == T_i); forbidden_set.pop_back(); TRACE("datatype_util_bug", tout << "nested_c: " << nested_c->get_name() << "\n";); if (nested_c == 0) break; } if (i == num_args) return c; } return 0; } func_decl * datatype_util::get_constructor_recognizer(func_decl * constructor) { SASSERT(is_constructor(constructor)); func_decl * d = 0; if (m_constructor2recognizer.find(constructor, d)) return d; sort * datatype = constructor->get_range(); d = m_manager.mk_func_decl(m_family_id, OP_DT_RECOGNISER, 2, constructor->get_parameters(), 1, &datatype); SASSERT(d); m_asts.push_back(constructor); m_asts.push_back(d); m_constructor2recognizer.insert(constructor, d); return d; } ptr_vector const * datatype_util::get_constructor_accessors(func_decl * constructor) { SASSERT(is_constructor(constructor)); ptr_vector * res = 0; if (m_constructor2accessors.find(constructor, res)) return res; res = alloc(ptr_vector); m_asts.push_back(constructor); m_vectors.push_back(res); m_constructor2accessors.insert(constructor, res); unsigned c_id = constructor->get_parameter(1).get_int(); sort * datatype = constructor->get_range(); unsigned tid = datatype->get_parameter(1).get_int(); unsigned o = datatype->get_parameter(3 + 2*tid).get_int(); unsigned k_i = datatype->get_parameter(o + c_id + 1).get_int(); unsigned num_accessors = datatype->get_parameter(k_i+2).get_int(); parameter p[3] = { parameter(datatype), parameter(c_id), parameter(-1) }; for (unsigned r = 0; r < num_accessors; r++) { p[2] = parameter(r); func_decl * d = m_manager.mk_func_decl(m_family_id, OP_DT_ACCESSOR, 3, p, 1, &datatype); SASSERT(d); m_asts.push_back(d); res->push_back(d); } return res; } func_decl * datatype_util::get_accessor_constructor(func_decl * accessor) { SASSERT(is_accessor(accessor)); func_decl * r = 0; if (m_accessor2constructor.find(accessor, r)) return r; sort * datatype = to_sort(accessor->get_parameter(0).get_ast()); unsigned c_id = accessor->get_parameter(1).get_int(); r = get_constructor(datatype, c_id); m_accessor2constructor.insert(accessor, r); m_asts.push_back(accessor); m_asts.push_back(r); return r; } func_decl * datatype_util::get_recognizer_constructor(func_decl * recognizer) { SASSERT(is_recognizer(recognizer)); func_decl * r = 0; if (m_recognizer2constructor.find(recognizer, r)) return r; sort * datatype = to_sort(recognizer->get_parameter(0).get_ast()); unsigned c_id = recognizer->get_parameter(1).get_int(); r = get_constructor(datatype, c_id); m_recognizer2constructor.insert(recognizer, r); m_asts.push_back(recognizer); m_asts.push_back(r); return r; } bool datatype_util::is_recursive(sort * ty) { SASSERT(is_datatype(ty)); bool r = false; if (m_is_recursive.find(ty, r)) return r; r = is_recursive_datatype(ty->get_parameters()); m_is_recursive.insert(ty, r); m_asts.push_back(ty); return r; } void datatype_util::reset() { m_datatype2constructors.reset(); m_datatype2nonrec_constructor.reset(); m_constructor2accessors.reset(); m_constructor2recognizer.reset(); m_recognizer2constructor.reset(); m_accessor2constructor.reset(); m_is_recursive.reset(); std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc >()); m_vectors.reset(); m_asts.reset(); } /** \brief Two datatype sorts s1 and s2 are siblings if they were defined together in the same mutually recursive definition. */ bool datatype_util::are_siblings(sort * s1, sort * s2) { SASSERT(is_datatype(s1)); SASSERT(is_datatype(s2)); if (s1 == s2) return true; if (s1->get_num_parameters() != s2->get_num_parameters()) return false; unsigned num_params = s1->get_num_parameters(); if (s1->get_parameter(0) != s2->get_parameter(0)) return false; // position 1 contains the IDX of the datatype in a mutually recursive definition. for (unsigned i = 2; i < num_params; i++) { if (s1->get_parameter(i) != s2->get_parameter(i)) return false; } return true; } void datatype_util::display_datatype(sort *s0, std::ostream& strm) { ast_mark mark; ptr_buffer todo; SASSERT(is_datatype(s0)); strm << s0->get_name() << " where\n"; todo.push_back(s0); mark.mark(s0, true); while (!todo.empty()) { sort* s = todo.back(); todo.pop_back(); strm << s->get_name() << " =\n"; ptr_vector const * cnstrs = get_datatype_constructors(s); for (unsigned i = 0; i < cnstrs->size(); ++i) { func_decl* cns = (*cnstrs)[i]; func_decl* rec = get_constructor_recognizer(cns); strm << " " << cns->get_name() << " :: " << rec->get_name() << " :: "; ptr_vector const * accs = get_constructor_accessors(cns); for (unsigned j = 0; j < accs->size(); ++j) { func_decl* acc = (*accs)[j]; sort* s1 = acc->get_range(); strm << "(" << acc->get_name() << ": " << s1->get_name() << ") "; if (is_datatype(s1) && are_siblings(s1, s0) && !mark.is_marked(s1)) { mark.mark(s1, true); todo.push_back(s1); } } strm << "\n"; } } } z3-4.4.0/src/ast/act_cache.cpp0000644000175000017500000001344612540347414015631 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: act_cache.cpp Abstract: expr -> expr activity cache It maintains at most N unused entries Author: Leonardo (leonardo) 2011-04-12 Notes: --*/ #include"act_cache.h" #define MIN_MAX_UNUSED 1024 #define INITIAL_CAPACITY 128 /* This cache is a mapping from expr -> tagged expressions A tagged expression is essentially a pair (expr, flag) Thus, an entry t -> (s, 0) maps the key t to value s, and says that key t was never accessed. That is, client code never executed find(t) Similarly, an entry t -> (s, 1) also maps the key t to value s, but signs that key t was already accessed by client code. When a new key/value pair is inserted the flag is 0. The flag is set to 1 after the key is accessed. The number of unused entries (m_unused) is equal to the number of entries of the form t -> (s, 0) That is, it is the number of keys that were never accessed by cliend code. The cache maintains at most m_max_unused entries. When the maximum number of unused entries exceeds m_max_unused, then the cache will delete the oldest unused entry. */ /** m_queue stores the recently added keys. The queue is implemented as pair: m_queue (vector), m_qhead (unsigned). The "active" part of m_queue is the range [m_qhead, m_queue.size()) The "inactive" part [0, m_qhead) contains keys that were already used by client code. This procedure, deletes the inactive part, and makes m_qhead == 0. */ void act_cache::compress_queue() { SASSERT(m_qhead > 0); unsigned sz = m_queue.size(); unsigned j = 0; for (unsigned i = m_qhead; i < sz; i++, j++) { m_queue[j] = m_queue[i]; } m_queue.shrink(j); m_qhead = 0; } void act_cache::init() { if (m_max_unused < MIN_MAX_UNUSED) m_max_unused = MIN_MAX_UNUSED; m_unused = 0; m_qhead = 0; } void act_cache::dec_refs() { map::iterator it = m_table.begin(); map::iterator end = m_table.end(); for (; it != end; ++it) { m_manager.dec_ref((*it).m_key); m_manager.dec_ref(UNTAG(expr*, (*it).m_value)); } } act_cache::act_cache(ast_manager & m): m_manager(m), m_max_unused(m.get_num_asts()) { init(); } act_cache::act_cache(ast_manager & m, unsigned max_unused): m_manager(m), m_max_unused(max_unused) { init(); } act_cache::~act_cache() { dec_refs(); } /** \brief Search m_queue from [m_qhead, m_queue.size()) until it finds an unused key. That is a key associated with an entry key -> (value, 0) */ void act_cache::del_unused() { unsigned sz = m_queue.size(); while (m_qhead < sz) { expr * k = m_queue[m_qhead]; m_qhead++; SASSERT(m_table.contains(k)); map::key_value * entry = m_table.find_core(k); SASSERT(entry); if (GET_TAG(entry->m_value) == 0) { // Key k was never accessed by client code. // That is, find(k) was never executed by client code. m_unused--; expr * v = entry->m_value; m_table.erase(k); m_manager.dec_ref(k); m_manager.dec_ref(v); break; } } if (m_qhead == sz) { // The "active" part of the queue is empty. // So, we perform a "cheap" compress. m_queue.reset(); m_qhead = 0; } else if (m_qhead > m_max_unused) { compress_queue(); } } /** \brief Insert a new entry k -> v into the cache. */ void act_cache::insert(expr * k, expr * v) { SASSERT(k); if (m_unused >= m_max_unused) del_unused(); expr * dummy = reinterpret_cast(1); map::key_value & entry = m_table.insert_if_not_there(k, dummy); #if 0 unsigned static counter = 0; counter++; if (counter % 100000 == 0) verbose_stream() << "[act-cache] counter: " << counter << " used_slots: " << m_table.used_slots() << " capacity: " << m_table.capacity() << " size: " << m_table.size() << " collisions: " << m_table.collisions() << "\n"; #endif #ifdef Z3DEBUG unsigned expected_tag; #endif if (entry.m_value == dummy) { // new entry; m_manager.inc_ref(k); m_manager.inc_ref(v); entry.m_value = v; m_queue.push_back(k); m_unused++; DEBUG_CODE(expected_tag = 0;); // new entry } else if (UNTAG(expr*, entry.m_value) == v) { // already there DEBUG_CODE(expected_tag = GET_TAG(entry.m_value);); } else { // replacing old entry m_manager.inc_ref(v); m_manager.dec_ref(UNTAG(expr*, entry.m_value)); entry.m_value = v; SASSERT(GET_TAG(entry.m_value) == 0); // replaced old entry, and reset the tag. DEBUG_CODE(expected_tag = 0;); } DEBUG_CODE({ expr * v2; SASSERT(m_table.find(k, v2)); SASSERT(v == UNTAG(expr*, v2)); SASSERT(expected_tag == GET_TAG(v2)); }); } /** \brief Search for key k in the cache. If entry k -> (v, tag) is found, we set tag to 1. */ expr * act_cache::find(expr * k) { map::key_value * entry = m_table.find_core(k); if (entry == 0) return 0; if (GET_TAG(entry->m_value) == 0) { entry->m_value = TAG(expr*, entry->m_value, 1); SASSERT(GET_TAG(entry->m_value) == 1); SASSERT(m_unused > 0); m_unused--; DEBUG_CODE({ expr * v; SASSERT(m_table.find(k, v)); SASSERT(GET_TAG(v) == 1); }); } return UNTAG(expr*, entry->m_value); } void act_cache::reset() { dec_refs(); m_table.reset(); m_queue.reset(); m_unused = 0; m_qhead = 0; } void act_cache::cleanup() { dec_refs(); m_table.finalize(); m_queue.finalize(); m_unused = 0; m_qhead = 0; } bool act_cache::check_invariant() const { return true; } z3-4.4.0/src/ast/expr_functors.h0000644000175000017500000000432412540347414016300 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: expr_functors.h Abstract: Functors on expressions. Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: Hoisted from quant_elim. --*/ #ifndef __EXPR_FUNCTORS_H__ #define __EXPR_FUNCTORS_H__ #include "ast.h" #include "expr_map.h" class i_expr_pred { public: virtual bool operator()(expr* e) = 0; virtual ~i_expr_pred() {} }; /** \brief Memoizing predicate functor on sub-expressions. The class is initialized with a predicate 'p' on expressions. The class is memoizing. */ class check_pred { i_expr_pred& m_pred; ast_mark m_pred_holds; ast_mark m_visited; expr_ref_vector m_refs; public: check_pred(i_expr_pred& p, ast_manager& m) : m_pred(p), m_refs(m) {} bool operator()(expr* e); void reset() { m_pred_holds.reset(); m_visited.reset(); m_refs.reset(); } private: void visit(expr* e); }; /** \brief Determine if expression 'e' or vector of expressions 'v' contains the app x */ class contains_app { class pred : public i_expr_pred { app* m_x; public: pred(app* x) : m_x(x) {} virtual bool operator()(expr* e) { return m_x == e; } }; app_ref m_x; pred m_pred; check_pred m_check; public: contains_app(ast_manager& m, app* x) : m_x(x, m), m_pred(x), m_check(m_pred, m) {} bool operator()(expr* e) { return m_check(e); } bool operator()(expr_ref_vector const& v) { return (*this)(v.size(), v.c_ptr()); } bool operator()(unsigned size, expr* const* es); app* x() const { return m_x; } }; /** \brief Base class of functor that applies map to expressions. */ class map_proc { protected: ast_manager& m; expr_map m_map; ptr_vector m_args; public: map_proc(ast_manager& m): m(m), m_map(m) {} void reset() { m_map.reset(); } void visit(var* e) { m_map.insert(e, e, 0); } void visit(quantifier* e); void reconstruct(app* a); expr* get_expr(expr* e); }; #endif z3-4.4.0/src/ast/array_decl_plugin.h0000644000175000017500000001321012540347414017054 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: array_decl_plugin.h Abstract: Author: Leonardo de Moura (leonardo) 2008-01-09. Revision History: --*/ #ifndef _ARRAY_DECL_PLUGIN_H_ #define _ARRAY_DECL_PLUGIN_H_ #include"ast.h" inline sort* get_array_range(sort const * s) { return to_sort(s->get_parameter(s->get_num_parameters() - 1).get_ast()); } inline unsigned get_array_arity(sort const * s) { return s->get_num_parameters() -1; } inline sort* get_array_domain(sort const * s, unsigned idx) { return to_sort(s->get_parameter(idx).get_ast()); } enum array_sort_kind { ARRAY_SORT }; enum array_op_kind { OP_STORE, OP_SELECT, OP_CONST_ARRAY, OP_ARRAY_EXT_SKOLEM, OP_ARRAY_DEFAULT, OP_ARRAY_MAP, OP_SET_UNION, OP_SET_INTERSECT, OP_SET_DIFFERENCE, OP_SET_COMPLEMENT, OP_SET_SUBSET, OP_AS_ARRAY, // used for model construction LAST_ARRAY_OP }; class array_decl_plugin : public decl_plugin { symbol m_store_sym; symbol m_select_sym; symbol m_const_sym; symbol m_default_sym; symbol m_map_sym; symbol m_set_union_sym; symbol m_set_intersect_sym; symbol m_set_difference_sym; symbol m_set_complement_sym; symbol m_set_subset_sym; symbol m_array_ext_sym; symbol m_as_array_sym; bool check_set_arguments(unsigned arity, sort * const * domain); func_decl * mk_const(sort* ty, unsigned arity, sort * const * domain); func_decl * mk_map(func_decl* f, unsigned arity, sort* const* domain); func_decl * mk_default(unsigned arity, sort* const* domain); func_decl * mk_select(unsigned arity, sort * const * domain); func_decl * mk_store(unsigned arity, sort * const * domain); func_decl * mk_array_ext_skolem(unsigned arity, sort * const * domain, unsigned i); func_decl * mk_set_union(unsigned arity, sort * const * domain); func_decl * mk_set_intersect(unsigned arity, sort * const * domain); func_decl * mk_set_difference(unsigned arity, sort * const * domain); func_decl * mk_set_complement(unsigned arity, sort * const * domain); func_decl * mk_set_subset(unsigned arity, sort * const * domain); func_decl * mk_as_array(func_decl * f); bool is_array_sort(sort* s) const; public: array_decl_plugin(); virtual ~array_decl_plugin() {} virtual decl_plugin * mk_fresh() { return alloc(array_decl_plugin); } // // Contract for sort: // parameters[0] - 1st dimension // ... // parameters[n-1] - nth dimension // parameters[n] - range // virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); // // Contract for func_decl: // parameters[0] - array sort // Contract for others: // no parameters virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); virtual void get_op_names(svector & op_names, symbol const & logic); virtual void get_sort_names(svector & sort_names, symbol const & logic); virtual expr * get_some_value(sort * s); virtual bool is_fully_interp(sort const * s) const; }; class array_recognizers { protected: family_id m_fid; public: array_recognizers(family_id fid):m_fid(fid) {} family_id get_family_id() const { return m_fid; } bool is_array(sort* s) const { return is_sort_of(s, m_fid, ARRAY_SORT);} bool is_array(expr* n) const { return is_array(get_sort(n)); } bool is_select(expr* n) const { return is_app_of(n, m_fid, OP_SELECT); } bool is_store(expr* n) const { return is_app_of(n, m_fid, OP_STORE); } bool is_const(expr* n) const { return is_app_of(n, m_fid, OP_CONST_ARRAY); } bool is_map(expr* n) const { return is_app_of(n, m_fid, OP_ARRAY_MAP); } bool is_as_array(expr * n) const { return is_app_of(n, m_fid, OP_AS_ARRAY); } bool is_select(func_decl* f) const { return is_decl_of(f, m_fid, OP_SELECT); } bool is_store(func_decl* f) const { return is_decl_of(f, m_fid, OP_STORE); } bool is_const(func_decl* f) const { return is_decl_of(f, m_fid, OP_CONST_ARRAY); } bool is_map(func_decl* f) const { return is_decl_of(f, m_fid, OP_ARRAY_MAP); } bool is_as_array(func_decl* f) const { return is_decl_of(f, m_fid, OP_AS_ARRAY); } func_decl * get_as_array_func_decl(app * n) const; }; class array_util : public array_recognizers { ast_manager & m_manager; public: array_util(ast_manager& m); ast_manager & get_manager() const { return m_manager; } bool is_as_array_tree(expr * n); app * mk_store(unsigned num_args, expr * const * args) { return m_manager.mk_app(m_fid, OP_STORE, 0, 0, num_args, args); } app * mk_select(unsigned num_args, expr * const * args) { return m_manager.mk_app(m_fid, OP_SELECT, 0, 0, num_args, args); } app * mk_map(func_decl * f, unsigned num_args, expr * const * args) { parameter p(f); return m_manager.mk_app(m_fid, OP_ARRAY_MAP, 1, &p, num_args, args); } app * mk_const_array(sort * s, expr * v) { parameter param(s); return m_manager.mk_app(m_fid, OP_CONST_ARRAY, 1, ¶m, 1, &v); } app * mk_empty_set(sort * s) { return mk_const_array(s, m_manager.mk_false()); } app * mk_full_set(sort * s) { return mk_const_array(s, m_manager.mk_true()); } sort * mk_array_sort(sort* dom, sort* range) { return mk_array_sort(1, &dom, range); } sort * mk_array_sort(unsigned arity, sort* const* domain, sort* range); }; #endif /* _ARRAY_DECL_PLUGIN_H_ */ z3-4.4.0/src/ast/arith_decl_plugin.cpp0000644000175000017500000005651012540347414017412 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: arith_decl_plugin.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-09 Revision History: --*/ #include"arith_decl_plugin.h" #include"warning.h" #include"algebraic_numbers.h" #include"id_gen.h" #include"ast_smt2_pp.h" struct arith_decl_plugin::algebraic_numbers_wrapper { unsynch_mpq_manager m_qmanager; algebraic_numbers::manager m_amanager; id_gen m_id_gen; scoped_anum_vector m_nums; algebraic_numbers_wrapper(): m_amanager(m_qmanager), m_nums(m_amanager) { } ~algebraic_numbers_wrapper() { } unsigned mk_id(algebraic_numbers::anum const & val) { SASSERT(!m_amanager.is_rational(val)); unsigned new_id = m_id_gen.mk(); m_nums.reserve(new_id+1); m_amanager.set(m_nums[new_id], val); TRACE("algebraic2expr", tout << "mk_id -> " << new_id << "\n"; m_amanager.display(tout, val); tout << "\n";); return new_id; } void recycle_id(unsigned idx) { SASSERT(idx < m_nums.size()); SASSERT(!m_amanager.is_zero(m_nums[idx])); TRACE("algebraic2expr", tout << "recycling: " << idx << "\n";); m_id_gen.recycle(idx); m_amanager.del(m_nums[idx]); } algebraic_numbers::anum const & idx2anum(unsigned idx) { return m_nums[idx]; } algebraic_numbers::anum const & to_anum(func_decl * f) { SASSERT(f->get_decl_kind() == OP_IRRATIONAL_ALGEBRAIC_NUM); return idx2anum(f->get_parameter(0).get_ext_id()); } }; arith_decl_plugin::algebraic_numbers_wrapper & arith_decl_plugin::aw() const { if (m_aw == 0) const_cast(this)->m_aw = alloc(algebraic_numbers_wrapper); return *m_aw; } algebraic_numbers::manager & arith_decl_plugin::am() const { return aw().m_amanager; } app * arith_decl_plugin::mk_numeral(algebraic_numbers::anum const & val, bool is_int) { if (am().is_rational(val)) { rational rval; am().to_rational(val, rval); return mk_numeral(rval, is_int); } else { if (is_int) m_manager->raise_exception("invalid irrational value passed as an integer"); unsigned idx = aw().mk_id(val); parameter p(idx, true); SASSERT(p.is_external()); func_decl * decl = m_manager->mk_const_decl(m_rootv_sym, m_real_decl, func_decl_info(m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM, 1, &p)); return m_manager->mk_const(decl); } } app * arith_decl_plugin::mk_numeral(sexpr const * p, unsigned i) { scoped_anum r(am()); am().mk_root(p, i, r); return mk_numeral(r, false); } void arith_decl_plugin::del(parameter const & p) { SASSERT(p.is_external()); if (m_aw != 0) { aw().recycle_id(p.get_ext_id()); } } parameter arith_decl_plugin::translate(parameter const & p, decl_plugin & target) { SASSERT(p.is_external()); arith_decl_plugin & _target = static_cast(target); return parameter(_target.aw().mk_id(aw().idx2anum(p.get_ext_id())), true); } void arith_decl_plugin::set_cancel(bool f) { if (m_aw) m_aw->m_amanager.set_cancel(f); } void arith_decl_plugin::set_manager(ast_manager * m, family_id id) { decl_plugin::set_manager(m, id); m_real_decl = m->mk_sort(symbol("Real"), sort_info(id, REAL_SORT)); m->inc_ref(m_real_decl); sort * r = m_real_decl; m_int_decl = m->mk_sort(symbol("Int"), sort_info(id, INT_SORT)); m->inc_ref(m_int_decl); sort * i = m_int_decl; sort * b = m->mk_bool_sort(); #define MK_PRED(FIELD, NAME, KIND, SORT) { \ func_decl_info info(id, KIND); \ info.set_chainable(true); \ FIELD = m->mk_func_decl(symbol(NAME), SORT, SORT, b, info); \ m->inc_ref(FIELD); \ } MK_PRED(m_r_le_decl, "<=", OP_LE, r); MK_PRED(m_r_ge_decl, ">=", OP_GE, r); MK_PRED(m_r_lt_decl, "<", OP_LT, r); MK_PRED(m_r_gt_decl, ">", OP_GT, r); MK_PRED(m_i_le_decl, "<=", OP_LE, i); MK_PRED(m_i_ge_decl, ">=", OP_GE, i); MK_PRED(m_i_lt_decl, "<", OP_LT, i); MK_PRED(m_i_gt_decl, ">", OP_GT, i); #define MK_AC_OP(FIELD, NAME, KIND, SORT) { \ func_decl_info info(id, KIND); \ info.set_associative(); \ info.set_flat_associative(); \ info.set_commutative(); \ FIELD = m->mk_func_decl(symbol(NAME), SORT, SORT, SORT, info); \ m->inc_ref(FIELD); \ } #define MK_LEFT_ASSOC_OP(FIELD, NAME, KIND, SORT) { \ func_decl_info info(id, KIND); \ info.set_left_associative(); \ FIELD = m->mk_func_decl(symbol(NAME), SORT, SORT, SORT, info); \ m->inc_ref(FIELD); \ } #define MK_OP(FIELD, NAME, KIND, SORT) \ FIELD = m->mk_func_decl(symbol(NAME), SORT, SORT, SORT, func_decl_info(id, KIND)); \ m->inc_ref(FIELD) #define MK_UNARY(FIELD, NAME, KIND, SORT) \ FIELD = m->mk_func_decl(symbol(NAME), SORT, SORT, func_decl_info(id, KIND)); \ m->inc_ref(FIELD) MK_AC_OP(m_r_add_decl, "+", OP_ADD, r); MK_LEFT_ASSOC_OP(m_r_sub_decl, "-", OP_SUB, r); MK_AC_OP(m_r_mul_decl, "*", OP_MUL, r); MK_LEFT_ASSOC_OP(m_r_div_decl, "/", OP_DIV, r); MK_UNARY(m_r_uminus_decl, "-", OP_UMINUS, r); MK_AC_OP(m_i_add_decl, "+", OP_ADD, i); MK_LEFT_ASSOC_OP(m_i_sub_decl, "-", OP_SUB, i); MK_AC_OP(m_i_mul_decl, "*", OP_MUL, i); MK_LEFT_ASSOC_OP(m_i_div_decl, "div", OP_IDIV, i); MK_OP(m_i_rem_decl, "rem", OP_REM, i); MK_OP(m_i_mod_decl, "mod", OP_MOD, i); MK_UNARY(m_i_uminus_decl, "-", OP_UMINUS, i); m_to_real_decl = m->mk_func_decl(symbol("to_real"), i, r, func_decl_info(id, OP_TO_REAL)); m->inc_ref(m_to_real_decl); m_to_int_decl = m->mk_func_decl(symbol("to_int"), r, i, func_decl_info(id, OP_TO_INT)); m->inc_ref(m_to_int_decl); m_is_int_decl = m->mk_func_decl(symbol("is_int"), r, m->mk_bool_sort(), func_decl_info(id, OP_IS_INT)); m->inc_ref(m_is_int_decl); MK_OP(m_r_power_decl, "^", OP_POWER, r); MK_OP(m_i_power_decl, "^", OP_POWER, i); MK_UNARY(m_i_abs_decl, "abs", OP_ABS, i); MK_UNARY(m_r_abs_decl, "abs", OP_ABS, r); MK_UNARY(m_sin_decl, "sin", OP_SIN, r); MK_UNARY(m_cos_decl, "cos", OP_COS, r); MK_UNARY(m_tan_decl, "tan", OP_TAN, r); MK_UNARY(m_asin_decl, "asin", OP_ASIN, r); MK_UNARY(m_acos_decl, "acos", OP_ACOS, r); MK_UNARY(m_atan_decl, "atan", OP_ATAN, r); MK_UNARY(m_sinh_decl, "sinh", OP_SINH, r); MK_UNARY(m_cosh_decl, "cosh", OP_COSH, r); MK_UNARY(m_tanh_decl, "tanh", OP_TANH, r); MK_UNARY(m_asinh_decl, "asinh", OP_ASINH, r); MK_UNARY(m_acosh_decl, "acosh", OP_ACOSH, r); MK_UNARY(m_atanh_decl, "atanh", OP_ATANH, r); func_decl * pi_decl = m->mk_const_decl(symbol("pi"), r, func_decl_info(id, OP_PI)); m_pi = m->mk_const(pi_decl); m->inc_ref(m_pi); func_decl * e_decl = m->mk_const_decl(symbol("euler"), r, func_decl_info(id, OP_E)); m_e = m->mk_const(e_decl); m->inc_ref(m_e); func_decl * z_pw_z_int = m->mk_const_decl(symbol("0^0-int"), i, func_decl_info(id, OP_0_PW_0_INT)); m_0_pw_0_int = m->mk_const(z_pw_z_int); m->inc_ref(m_0_pw_0_int); func_decl * z_pw_z_real = m->mk_const_decl(symbol("0^0-real"), r, func_decl_info(id, OP_0_PW_0_REAL)); m_0_pw_0_real = m->mk_const(z_pw_z_real); m->inc_ref(m_0_pw_0_real); MK_OP(m_neg_root_decl, "neg-root", OP_NEG_ROOT, r); MK_UNARY(m_div_0_decl, "/0", OP_DIV_0, r); MK_UNARY(m_idiv_0_decl, "div0", OP_IDIV_0, i); MK_UNARY(m_mod_0_decl, "mod0", OP_MOD_0, i); MK_UNARY(m_u_asin_decl, "asin-u", OP_U_ASIN, r); MK_UNARY(m_u_acos_decl, "acos-u", OP_U_ACOS, r); } arith_decl_plugin::arith_decl_plugin(): m_aw(0), m_intv_sym("Int"), m_realv_sym("Real"), m_rootv_sym("RootObject"), m_real_decl(0), m_int_decl(0), m_r_le_decl(0), m_r_ge_decl(0), m_r_lt_decl(0), m_r_gt_decl(0), m_r_add_decl(0), m_r_sub_decl(0), m_r_uminus_decl(0), m_r_mul_decl(0), m_r_div_decl(0), m_i_le_decl(0), m_i_ge_decl(0), m_i_lt_decl(0), m_i_gt_decl(0), m_i_add_decl(0), m_i_sub_decl(0), m_i_uminus_decl(0), m_i_mul_decl(0), m_i_div_decl(0), m_i_mod_decl(0), m_i_rem_decl(0), m_to_real_decl(0), m_to_int_decl(0), m_is_int_decl(0), m_r_power_decl(0), m_i_power_decl(0), m_r_abs_decl(0), m_i_abs_decl(0), m_sin_decl(0), m_cos_decl(0), m_tan_decl(0), m_asin_decl(0), m_acos_decl(0), m_atan_decl(0), m_sinh_decl(0), m_cosh_decl(0), m_tanh_decl(0), m_asinh_decl(0), m_acosh_decl(0), m_atanh_decl(0), m_pi(0), m_e(0), m_0_pw_0_int(0), m_0_pw_0_real(0), m_neg_root_decl(0), m_div_0_decl(0), m_idiv_0_decl(0), m_mod_0_decl(0), m_u_asin_decl(0), m_u_acos_decl(0) { } arith_decl_plugin::~arith_decl_plugin() { dealloc(m_aw); } void arith_decl_plugin::finalize() { #define DEC_REF(decl) if (decl) { m_manager->dec_ref(decl); } ((void) 0) DEC_REF(m_real_decl); DEC_REF(m_int_decl); DEC_REF(m_r_le_decl); DEC_REF(m_r_ge_decl); DEC_REF(m_r_lt_decl); DEC_REF(m_r_gt_decl); DEC_REF(m_r_add_decl); DEC_REF(m_r_sub_decl); DEC_REF(m_r_uminus_decl); DEC_REF(m_r_mul_decl); DEC_REF(m_r_div_decl); DEC_REF(m_i_le_decl); DEC_REF(m_i_ge_decl); DEC_REF(m_i_lt_decl); DEC_REF(m_i_gt_decl); DEC_REF(m_i_add_decl); DEC_REF(m_i_sub_decl); DEC_REF(m_i_uminus_decl); DEC_REF(m_i_mul_decl); DEC_REF(m_i_div_decl); DEC_REF(m_i_mod_decl); DEC_REF(m_i_rem_decl); DEC_REF(m_to_real_decl); DEC_REF(m_to_int_decl); DEC_REF(m_is_int_decl); DEC_REF(m_i_power_decl); DEC_REF(m_r_power_decl); DEC_REF(m_i_abs_decl); DEC_REF(m_r_abs_decl); DEC_REF(m_sin_decl); DEC_REF(m_cos_decl); DEC_REF(m_tan_decl); DEC_REF(m_asin_decl); DEC_REF(m_acos_decl); DEC_REF(m_atan_decl); DEC_REF(m_sinh_decl); DEC_REF(m_cosh_decl); DEC_REF(m_tanh_decl); DEC_REF(m_asinh_decl); DEC_REF(m_acosh_decl); DEC_REF(m_atanh_decl); DEC_REF(m_pi); DEC_REF(m_e); DEC_REF(m_0_pw_0_int); DEC_REF(m_0_pw_0_real); DEC_REF(m_neg_root_decl); DEC_REF(m_div_0_decl); DEC_REF(m_idiv_0_decl); DEC_REF(m_mod_0_decl); DEC_REF(m_u_asin_decl); DEC_REF(m_u_acos_decl); m_manager->dec_array_ref(m_small_ints.size(), m_small_ints.c_ptr()); m_manager->dec_array_ref(m_small_reals.size(), m_small_reals.c_ptr()); } sort * arith_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { switch (k) { case REAL_SORT: return m_real_decl; case INT_SORT: return m_int_decl; default: return 0; } } inline func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, bool is_real) { switch (k) { case OP_LE: return is_real ? m_r_le_decl : m_i_le_decl; case OP_GE: return is_real ? m_r_ge_decl : m_i_ge_decl; case OP_LT: return is_real ? m_r_lt_decl : m_i_lt_decl; case OP_GT: return is_real ? m_r_gt_decl : m_i_gt_decl; case OP_ADD: return is_real ? m_r_add_decl : m_i_add_decl; case OP_SUB: return is_real ? m_r_sub_decl : m_i_sub_decl; case OP_UMINUS: return is_real ? m_r_uminus_decl : m_i_uminus_decl; case OP_MUL: return is_real ? m_r_mul_decl : m_i_mul_decl; case OP_DIV: return m_r_div_decl; case OP_IDIV: return m_i_div_decl; case OP_REM: return m_i_rem_decl; case OP_MOD: return m_i_mod_decl; case OP_TO_REAL: return m_to_real_decl; case OP_TO_INT: return m_to_int_decl; case OP_IS_INT: return m_is_int_decl; case OP_POWER: return is_real ? m_r_power_decl : m_i_power_decl; case OP_ABS: return is_real ? m_r_abs_decl : m_i_abs_decl; case OP_SIN: return m_sin_decl; case OP_COS: return m_cos_decl; case OP_TAN: return m_tan_decl; case OP_ASIN: return m_asin_decl; case OP_ACOS: return m_acos_decl; case OP_ATAN: return m_atan_decl; case OP_SINH: return m_sinh_decl; case OP_COSH: return m_cosh_decl; case OP_TANH: return m_tanh_decl; case OP_ASINH: return m_asinh_decl; case OP_ACOSH: return m_acosh_decl; case OP_ATANH: return m_atanh_decl; case OP_PI: return m_pi->get_decl(); case OP_E: return m_e->get_decl(); case OP_0_PW_0_INT: return m_0_pw_0_int->get_decl(); case OP_0_PW_0_REAL: return m_0_pw_0_real->get_decl(); case OP_NEG_ROOT: return m_neg_root_decl; case OP_DIV_0: return m_div_0_decl; case OP_IDIV_0: return m_idiv_0_decl; case OP_MOD_0: return m_mod_0_decl; case OP_U_ASIN: return m_u_asin_decl; case OP_U_ACOS: return m_u_acos_decl; default: return 0; } } inline decl_kind arith_decl_plugin::fix_kind(decl_kind k, unsigned arity) { if (k == OP_SUB && arity == 1) { return OP_UMINUS; } return k; } #define MAX_SMALL_NUM_TO_CACHE 16 app * arith_decl_plugin::mk_numeral(rational const & val, bool is_int) { if (is_int && !val.is_int()) { m_manager->raise_exception("invalid rational value passed as an integer"); } if (val.is_unsigned()) { unsigned u_val = val.get_unsigned(); if (u_val < MAX_SMALL_NUM_TO_CACHE) { if (is_int) { app * r = m_small_ints.get(u_val, 0); if (r == 0) { parameter p[2] = { parameter(val), parameter(1) }; r = m_manager->mk_const(m_manager->mk_const_decl(m_intv_sym, m_int_decl, func_decl_info(m_family_id, OP_NUM, 2, p))); m_manager->inc_ref(r); m_small_ints.setx(u_val, r, 0); } return r; } else { app * r = m_small_reals.get(u_val, 0); if (r == 0) { parameter p[2] = { parameter(val), parameter(0) }; r = m_manager->mk_const(m_manager->mk_const_decl(m_realv_sym, m_real_decl, func_decl_info(m_family_id, OP_NUM, 2, p))); m_manager->inc_ref(r); m_small_reals.setx(u_val, r, 0); } return r; } } } parameter p[2] = { parameter(val), parameter(static_cast(is_int)) }; func_decl * decl; if (is_int) decl = m_manager->mk_const_decl(m_intv_sym, m_int_decl, func_decl_info(m_family_id, OP_NUM, 2, p)); else decl = m_manager->mk_const_decl(m_realv_sym, m_real_decl, func_decl_info(m_family_id, OP_NUM, 2, p)); return m_manager->mk_const(decl); } func_decl * arith_decl_plugin::mk_num_decl(unsigned num_parameters, parameter const * parameters, unsigned arity) { if (!(num_parameters == 2 && arity == 0 && parameters[0].is_rational() && parameters[1].is_int())) { m_manager->raise_exception("invalid numeral declaration"); return 0; } if (parameters[1].get_int() != 0) return m_manager->mk_const_decl(m_intv_sym, m_int_decl, func_decl_info(m_family_id, OP_NUM, num_parameters, parameters)); else return m_manager->mk_const_decl(m_realv_sym, m_real_decl, func_decl_info(m_family_id, OP_NUM, num_parameters, parameters)); } static bool use_coercion(decl_kind k) { return k == OP_ADD || k == OP_SUB || k == OP_MUL || k == OP_POWER || k == OP_LE || k == OP_GE || k == OP_LT || k == OP_GT || k == OP_UMINUS; } static bool has_real_arg(unsigned arity, sort * const * domain, sort * real_sort) { for (unsigned i = 0; i < arity; i++) if (domain[i] == real_sort) return true; return false; } static bool has_real_arg(ast_manager * m, unsigned num_args, expr * const * args, sort * real_sort) { for (unsigned i = 0; i < num_args; i++) if (m->get_sort(args[i]) == real_sort) return true; return false; } static bool is_const_op(decl_kind k) { return k == OP_PI || k == OP_E || k == OP_0_PW_0_INT || k == OP_0_PW_0_REAL; } func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (k == OP_NUM) return mk_num_decl(num_parameters, parameters, arity); if (arity == 0 && !is_const_op(k)) { m_manager->raise_exception("no arguments supplied to arithmetical operator"); return 0; } if (m_manager->int_real_coercions() && use_coercion(k)) { return mk_func_decl(fix_kind(k, arity), has_real_arg(arity, domain, m_real_decl)); } else { bool is_real = arity > 0 && domain[0] == m_real_decl; return mk_func_decl(fix_kind(k, arity), is_real); } } func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { if (k == OP_NUM) return mk_num_decl(num_parameters, parameters, num_args); if (num_args == 0 && !is_const_op(k)) { m_manager->raise_exception("no arguments supplied to arithmetical operator"); return 0; } if (m_manager->int_real_coercions() && use_coercion(k)) { return mk_func_decl(fix_kind(k, num_args), has_real_arg(m_manager, num_args, args, m_real_decl)); } else { bool is_real = num_args > 0 && m_manager->get_sort(args[0]) == m_real_decl; return mk_func_decl(fix_kind(k, num_args), is_real); } } void arith_decl_plugin::get_sort_names(svector& sort_names, symbol const & logic) { // TODO: only define Int and Real in the right logics sort_names.push_back(builtin_name("Int", INT_SORT)); sort_names.push_back(builtin_name("Real", REAL_SORT)); } void arith_decl_plugin::get_op_names(svector& op_names, symbol const & logic) { op_names.push_back(builtin_name("<=",OP_LE)); op_names.push_back(builtin_name(">=",OP_GE)); op_names.push_back(builtin_name("<",OP_LT)); op_names.push_back(builtin_name(">",OP_GT)); op_names.push_back(builtin_name("+",OP_ADD)); op_names.push_back(builtin_name("-",OP_SUB)); op_names.push_back(builtin_name("~",OP_UMINUS)); op_names.push_back(builtin_name("*",OP_MUL)); op_names.push_back(builtin_name("/",OP_DIV)); op_names.push_back(builtin_name("div",OP_IDIV)); op_names.push_back(builtin_name("rem",OP_REM)); op_names.push_back(builtin_name("mod",OP_MOD)); op_names.push_back(builtin_name("to_real",OP_TO_REAL)); op_names.push_back(builtin_name("to_int",OP_TO_INT)); op_names.push_back(builtin_name("is_int",OP_IS_INT)); op_names.push_back(builtin_name("abs", OP_ABS)); if (logic == symbol::null) { op_names.push_back(builtin_name("^", OP_POWER)); op_names.push_back(builtin_name("sin", OP_SIN)); op_names.push_back(builtin_name("cos", OP_COS)); op_names.push_back(builtin_name("tan", OP_TAN)); op_names.push_back(builtin_name("asin", OP_ASIN)); op_names.push_back(builtin_name("acos", OP_ACOS)); op_names.push_back(builtin_name("atan", OP_ATAN)); op_names.push_back(builtin_name("sinh", OP_SINH)); op_names.push_back(builtin_name("cosh", OP_COSH)); op_names.push_back(builtin_name("tanh", OP_TANH)); op_names.push_back(builtin_name("asinh", OP_ASINH)); op_names.push_back(builtin_name("acosh", OP_ACOSH)); op_names.push_back(builtin_name("atanh", OP_ATANH)); op_names.push_back(builtin_name("pi", OP_PI)); op_names.push_back(builtin_name("euler", OP_E)); } } bool arith_decl_plugin::is_value(app * e) const { return is_app_of(e, m_family_id, OP_NUM) || is_app_of(e, m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM) || is_app_of(e, m_family_id, OP_PI) || is_app_of(e, m_family_id, OP_E); } bool arith_decl_plugin::is_unique_value(app * e) const { return is_app_of(e, m_family_id, OP_NUM) || is_app_of(e, m_family_id, OP_PI) || is_app_of(e, m_family_id, OP_E); } bool arith_decl_plugin::are_equal(app * a, app * b) const { if (decl_plugin::are_equal(a, b)) { return true; } if (is_app_of(a, m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM) && is_app_of(b, m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM)) { return am().eq(aw().to_anum(a->get_decl()), aw().to_anum(b->get_decl())); } return false; } bool arith_decl_plugin::are_distinct(app * a, app * b) const { TRACE("are_distinct_bug", tout << mk_ismt2_pp(a, *m_manager) << "\n" << mk_ismt2_pp(b, *m_manager) << "\n";); if (decl_plugin::are_distinct(a,b)) { return true; } if (is_app_of(a, m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM) && is_app_of(b, m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM)) { return am().neq(aw().to_anum(a->get_decl()), aw().to_anum(b->get_decl())); } #define is_non_zero(e) is_app_of(e,m_family_id, OP_NUM) && !to_app(e)->get_decl()->get_parameter(0).get_rational().is_zero() if (is_app_of(a, m_family_id, OP_ADD) && a->get_num_args() == 2 && to_app(a)->get_arg(0) == b && is_non_zero(to_app(a)->get_arg(1))) { return true; } if (is_app_of(a, m_family_id, OP_ADD) && a->get_num_args() == 2 && to_app(a)->get_arg(1) == b && is_non_zero(to_app(a)->get_arg(0))) { return true; } if (is_app_of(b, m_family_id, OP_ADD) && b->get_num_args() == 2 && to_app(b)->get_arg(1) == a && is_non_zero(to_app(b)->get_arg(0))) { return true; } if (is_app_of(b, m_family_id, OP_ADD) && b->get_num_args() == 2 && to_app(b)->get_arg(0) == a && is_non_zero(to_app(b)->get_arg(1))) { return true; } return false; } expr * arith_decl_plugin::get_some_value(sort * s) { SASSERT(s == m_int_decl || s == m_real_decl); return mk_numeral(rational(0), s == m_int_decl); } bool arith_recognizers::is_numeral(expr const * n, rational & val, bool & is_int) const { if (!is_app_of(n, m_afid, OP_NUM)) return false; func_decl * decl = to_app(n)->get_decl(); val = decl->get_parameter(0).get_rational(); is_int = decl->get_parameter(1).get_int() != 0; return true; } arith_util::arith_util(ast_manager & m): arith_recognizers(m.mk_family_id("arith")), m_manager(m), m_plugin(0) { } void arith_util::init_plugin() { SASSERT(m_plugin == 0); m_plugin = static_cast(m_manager.get_plugin(m_afid)); } bool arith_util::is_irrational_algebraic_numeral(expr const * n, algebraic_numbers::anum & val) { if (!is_app_of(n, m_afid, OP_IRRATIONAL_ALGEBRAIC_NUM)) return false; am().set(val, to_irrational_algebraic_numeral(n)); return true; } algebraic_numbers::anum const & arith_util::to_irrational_algebraic_numeral(expr const * n) { SASSERT(is_irrational_algebraic_numeral(n)); return plugin().aw().to_anum(to_app(n)->get_decl()); } z3-4.4.0/src/ast/seq_decl_plugin.h0000644000175000017500000000502012540347414016526 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: seq_decl_plugin.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2011-14-11 Revision History: --*/ #ifndef _SEQ_DECL_PLUGIN_H_ #define _SEQ_DECL_PLUGIN_H_ #include "ast.h" enum seq_sort_kind { SEQ_SORT, RE_SORT }; enum seq_op_kind { OP_SEQ_UNIT, OP_SEQ_EMPTY, OP_SEQ_CONCAT, OP_SEQ_CONS, OP_SEQ_REV_CONS, OP_SEQ_HEAD, OP_SEQ_TAIL, OP_SEQ_LAST, OP_SEQ_FIRST, OP_SEQ_PREFIX_OF, OP_SEQ_SUFFIX_OF, OP_SEQ_SUBSEQ_OF, OP_SEQ_EXTRACT, OP_SEQ_NTH, OP_SEQ_LENGTH, OP_RE_PLUS, OP_RE_STAR, OP_RE_OPTION, OP_RE_RANGE, OP_RE_CONCAT, OP_RE_UNION, OP_RE_INTERSECT, OP_RE_COMPLEMENT, OP_RE_DIFFERENCE, OP_RE_LOOP, OP_RE_EMPTY_SET, OP_RE_FULL_SET, OP_RE_EMPTY_SEQ, OP_RE_OF_SEQ, OP_RE_OF_PRED, OP_RE_MEMBER, LAST_SEQ_OP }; class seq_decl_plugin : public decl_plugin { struct psig { symbol m_name; unsigned m_num_params; sort_ref_vector m_dom; sort_ref m_range; psig(ast_manager& m, char const* name, unsigned n, unsigned dsz, sort* const* dom, sort* rng): m_name(name), m_num_params(n), m_dom(m), m_range(rng, m) { m_dom.append(dsz, dom); } }; ptr_vector m_sigs; bool m_init; void match(psig& sig, unsigned dsz, sort* const* dom, sort* range, sort_ref& rng); bool match(ptr_vector& binding, sort* s, sort* sP); sort* apply_binding(ptr_vector const& binding, sort* s); bool is_sort_param(sort* s, unsigned& idx); void init(); public: seq_decl_plugin(); virtual ~seq_decl_plugin() {} virtual void finalize(); virtual decl_plugin * mk_fresh() { return alloc(seq_decl_plugin); } virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); virtual void get_op_names(svector & op_names, symbol const & logic); virtual void get_sort_names(svector & sort_names, symbol const & logic); virtual bool is_value(app * e) const; virtual bool is_unique_value(app * e) const { return is_value(e); } }; #endif /* _SEQ_DECL_PLUGIN_H_ */ z3-4.4.0/src/ast/format.h0000644000175000017500000001544412540347414014674 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: format.h Abstract: Author: Leonardo de Moura (leonardo) 2008-01-20. Revision History: --*/ #ifndef _FORMAT_H_ #define _FORMAT_H_ #include"ast.h" namespace format_ns { typedef app format; typedef app_ref format_ref; enum format_sort_kind { FORMAT_SORT }; enum format_op_kind { OP_NIL, OP_STRING, OP_INDENT, OP_COMPOSE, OP_CHOICE, OP_LINE_BREAK, OP_LINE_BREAK_EXT }; struct f2f { format * operator()(format * f) { return f; } }; /** \brief Return the "format manager" associated with the given ast_manager. */ inline ast_manager & fm(ast_manager & m) { return m.get_format_manager(); } family_id get_format_family_id(ast_manager & m); format * mk_string(ast_manager & m, char const * str); format * mk_int(ast_manager & m, int i); format * mk_unsigned(ast_manager & m, unsigned u); format * mk_indent(ast_manager & m, unsigned i, format * f); format * mk_line_break(ast_manager & m); format * mk_group(ast_manager & m, format * f); format * mk_compose(ast_manager & m, unsigned num_children, format * const * children); format * mk_compose(ast_manager & m, format * f1, format * f2); format * mk_compose(ast_manager & m, format * f1, format * f2, format * f3); format * mk_compose(ast_manager & m, format * f1, format * f2, format * f3, format * f4); format * mk_nil(ast_manager & m); format * mk_choice(ast_manager & m, format * f1, format * f2); template format * mk_seq(ast_manager & m, It const & begin, It const & end, ToDoc proc) { app_ref_buffer children(fm(m)); for (It it = begin; it != end; ++it) { format * curr = proc(*it); if (curr->get_decl_kind() != OP_NIL) { children.push_back(mk_line_break(m)); children.push_back(curr); } } return mk_compose(m, children.size(), children.c_ptr()); } /** (header elem_1 elem_2 ... elem_n) */ template format * mk_seq1(ast_manager & m, It const & begin, It const & end, ToDoc proc, char const * header, char const * lp = "(", char const * rp = ")") { if (begin == end) return mk_compose(m, mk_string(m, lp), mk_string(m, header), mk_string(m, rp)); unsigned indent = static_cast(strlen(lp) + strlen(header) + 1); It it = begin; format * first = proc(*it); ++it; return mk_group(m, mk_compose(m, mk_string(m, lp), mk_string(m, header), mk_indent(m, indent, mk_compose(m, mk_string(m, " "), first, mk_seq(m, it, end, proc), mk_string(m, rp))))); } #define FORMAT_DEFAULT_INDENT 2 /** (header elem_1 ... elem_n) */ template format * mk_seq2(ast_manager & m, It const & begin, It const & end, ToDoc proc, char const * header, unsigned indent = FORMAT_DEFAULT_INDENT, char const * lp = "(", char const * rp = ")") { if (begin == end) return mk_compose(m, mk_string(m, lp), mk_string(m, header), mk_string(m, rp)); return mk_group(m, mk_compose(m, mk_indent(m, static_cast(strlen(lp)), mk_compose(m, mk_string(m, lp), mk_string(m, header))), mk_indent(m, indent, mk_compose(m, mk_seq(m, begin, end, proc), mk_string(m, rp))))); } /** (header elem_1 ... elem_i elem_{i+1} ... elem_n) */ template format * mk_seq3(ast_manager & m, It const & begin, It const & end, ToDoc proc, char const * header, unsigned i = 1, unsigned indent = FORMAT_DEFAULT_INDENT, char const * lp = "(", char const * rp = ")") { SASSERT(i >= 1); if (begin == end) return mk_compose(m, mk_string(m, lp), mk_string(m, header), mk_string(m, rp)); unsigned idx = 0; It end1 = begin; for (;end1 != end && idx < i; ++end1, ++idx) ; It it = begin; format * first = proc(*it); ++it; return mk_group(m, mk_compose(m, mk_compose(m, mk_string(m, lp), mk_string(m, header)), mk_group(m, mk_indent(m, static_cast(strlen(header) + strlen(lp) + 1), mk_compose(m, mk_string(m, " "), first, mk_seq(m, it, end1, proc)))), mk_indent(m, indent, mk_seq(m, end1, end, proc)), mk_string(m, rp))); } /** (elem_1 elem_2 ... elem_n) */ template format * mk_seq4(ast_manager & m, It const & begin, It const & end, ToDoc proc, unsigned indent = FORMAT_DEFAULT_INDENT, char const * lp = "(", char const * rp = ")") { if (begin == end) return mk_compose(m, mk_string(m, lp), mk_string(m, rp)); unsigned indent1 = static_cast(strlen(lp)); It it = begin; format * first = proc(*it); ++it; return mk_group(m, mk_compose(m, mk_indent(m, indent1, mk_compose(m, mk_string(m, lp), first)), mk_indent(m, indent, mk_compose(m, mk_seq(m, it, end, proc), mk_string(m, rp))))); } /** (elem_1 elem_2 ... elem_n) */ template format * mk_seq5(ast_manager & m, It const & begin, It const & end, ToDoc proc, char const * lp = "(", char const * rp = ")") { return mk_seq4(m, begin, end, proc, static_cast(strlen(lp)), lp, rp); } }; #endif /* _FORMAT_H_ */ z3-4.4.0/src/ast/ast_pp.h0000644000175000017500000000132612540347414014664 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ast_pp.h Abstract: Pretty printer Author: Leonardo de Moura 2008-01-20. Revision History: 2012-11-17 - ast_smt2_pp is the official pretty printer in Z3 --*/ #ifndef _AST_PP_H_ #define _AST_PP_H_ #include"ast_smt2_pp.h" struct mk_pp : public mk_ismt2_pp { mk_pp(ast * t, ast_manager & m, params_ref const & p, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = 0): mk_ismt2_pp(t, m, p, indent, num_vars, var_prefix) { } mk_pp(ast * t, ast_manager & m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = 0): mk_ismt2_pp(t, m, indent, num_vars, var_prefix) { } }; #endif z3-4.4.0/src/ast/recurse_expr_def.h0000644000175000017500000000662712540347414016733 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: recurse_expr_def.h Abstract: Traverse an expression applying a visitor. Author: Leonardo de Moura (leonardo) 2008-01-11. Revision History: --*/ #ifndef _RECURSE_EXPR_DEF_H_ #define _RECURSE_EXPR_DEF_H_ #include"recurse_expr.h" template inline void recurse_expr::visit(expr * n, bool & visited) { if (!is_cached(n)) { m_todo.push_back(n); visited = false; } } template bool recurse_expr::visit_children(expr * n) { bool visited = true; unsigned num; switch (n->get_kind()) { case AST_APP: num = to_app(n)->get_num_args(); for (unsigned j = 0; j < num; j++) visit(to_app(n)->get_arg(j), visited); break; case AST_QUANTIFIER: if (!IgnorePatterns) { num = to_quantifier(n)->get_num_patterns(); for (unsigned j = 0; j < num; j++) visit(to_quantifier(n)->get_pattern(j), visited); num = to_quantifier(n)->get_num_no_patterns(); for (unsigned j = 0; j < num; j++) visit(to_quantifier(n)->get_no_pattern(j), visited); } visit(to_quantifier(n)->get_expr(), visited); break; default: break; } return visited; } template void recurse_expr::process(expr * n) { unsigned num; switch (n->get_kind()) { case AST_APP: m_results1.reset(); num = to_app(n)->get_num_args(); for (unsigned j = 0; j < num; j++) m_results1.push_back(get_cached(to_app(n)->get_arg(j))); cache_result(n, this->Visitor::visit(to_app(n), m_results1.c_ptr())); break; case AST_VAR: cache_result(n, this->Visitor::visit(to_var(n))); break; case AST_QUANTIFIER: if (IgnorePatterns) { cache_result(n, this->Visitor::visit(to_quantifier(n), get_cached(to_quantifier(n)->get_expr()), 0, 0)); } else { m_results1.reset(); m_results2.reset(); num = to_quantifier(n)->get_num_patterns(); for (unsigned j = 0; j < num; j++) m_results1.push_back(get_cached(to_quantifier(n)->get_pattern(j))); num = to_quantifier(n)->get_num_no_patterns(); for (unsigned j = 0; j < num; j++) m_results2.push_back(get_cached(to_quantifier(n)->get_no_pattern(j))); cache_result(n, this->Visitor::visit(to_quantifier(n), get_cached(to_quantifier(n)->get_expr()), m_results1.c_ptr(), m_results2.c_ptr())); } break; default: UNREACHABLE(); } } template T recurse_expr::operator()(expr * r) { m_todo.push_back(r); while (!m_todo.empty()) { expr * n = m_todo.back(); if (is_cached(n)) m_todo.pop_back(); else if (visit_children(n)) { m_todo.pop_back(); process(n); } } return get_cached(r); } #endif /* _RECURSE_EXPR_DEF_H_ */ z3-4.4.0/src/ast/expr_delta_pair.h0000644000175000017500000000140212540347414016533 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: expr_delta_pair.h Abstract: Author: Leonardo de Moura (leonardo) 2008-01-14. Revision History: --*/ #ifndef _EXPR_DELTA_PAIR_H_ #define _EXPR_DELTA_PAIR_H_ /** \brief Auxiliary structure used to cache the intermediate results of the variable substitution procedure. */ struct expr_delta_pair { expr * m_node; unsigned m_delta; expr_delta_pair():m_node(0), m_delta(0) {} expr_delta_pair(expr * n, unsigned d):m_node(n), m_delta(d) {} unsigned hash() const { return hash_u_u(m_node->hash(), m_delta); } bool operator==(const expr_delta_pair & e) const { return m_node == e.m_node && m_delta == e.m_delta; } }; #endif /* _EXPR_DELTA_PAIR_H_ */ z3-4.4.0/src/ast/func_decl_dependencies.h0000644000175000017500000000545512540347414020035 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: func_decl_dependencies.h Abstract: Author: Leonardo de Moura (leonardo) 2010-12-15. Revision History: --*/ #ifndef _FUNC_DECL_DEPENDENCIES_H_ #define _FUNC_DECL_DEPENDENCIES_H_ #include"ast.h" #include"obj_hashtable.h" // Set of dependencies typedef obj_hashtable func_decl_set; /** \brief Collect uninterpreted function declarations (with arity > 0) occurring in \c n. */ void collect_func_decls(ast_manager & m, expr * n, func_decl_set & r, bool ng_only = false); /** \brief Auxiliary data-structure used for tracking dependencies between function declarations. The following pattern of use is expected: func_decl_dependencies & dm; func_decl_set * S = dm.mk_func_decl_set(); dm.collect_func_decls(t_1, S); ... dm.collect_func_decls(t_n, S); dm.insert(f, S); */ class func_decl_dependencies { typedef obj_map dependency_graph; ast_manager & m_manager; dependency_graph m_deps; class top_sort; public: func_decl_dependencies(ast_manager & m):m_manager(m) {} ~func_decl_dependencies() { reset(); } void reset(); /** \brief Create a dependecy set. This set should be populated using #collect_func_decls. After populating the set, it must be used as an argument for the #insert method. \remark The manager owns the set. \warning Failure to call #insert will produce a memory leak. */ func_decl_set * mk_func_decl_set() { return alloc(func_decl_set); } /** \brief Store the uninterpreted function declarations used in \c n into \c s. */ void collect_func_decls(expr * n, func_decl_set * s); /** \brief Store the uninterpreted function declarations (in non ground terms) used in \c n into \c s. */ void collect_ng_func_decls(expr * n, func_decl_set * s); /** \brief Insert \c f in the manager with the given set of dependencies. The insertion succeeds iff 1- no cycle is created between the new entry and the already existing dependencies. 2- \c f was not already inserted into the manager. Return false in case of failure. \remark The manager is the owner of the dependency sets. */ bool insert(func_decl * f, func_decl_set * s); /** \brief Return true if \c f is registered in this manager. */ bool contains(func_decl * f) const { return m_deps.contains(f); } func_decl_set * get_dependencies(func_decl * f) const { func_decl_set * r = 0; m_deps.find(f, r); return r; } /** \brief Erase \c f (and its dependencies) from the manager. */ void erase(func_decl * f); void display(std::ostream & out); }; #endif z3-4.4.0/src/ast/pp.h0000644000175000017500000000056712540347414014023 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pp.h Abstract: Author: Leonardo de Moura (leonardo) 2008-01-20. Revision History: --*/ #ifndef _PP_H_ #define _PP_H_ #include"format.h" #include"params.h" void pp(std::ostream & out, format_ns::format * f, ast_manager & m, params_ref const & p = params_ref()); #endif /* _PP_H_ */ z3-4.4.0/src/ast/pb_decl_plugin.cpp0000644000175000017500000001750612540347414016706 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: pb_decl_plugin.cpp Abstract: Cardinality Constraints plugin Author: Nikolaj Bjorner (nbjorner) 2013-05-11 Revision History: --*/ #include "pb_decl_plugin.h" pb_decl_plugin::pb_decl_plugin(): m_at_most_sym("at-most"), m_at_least_sym("at-least"), m_pble_sym("pble"), m_pbge_sym("pbge"), m_pbeq_sym("pbeq") {} func_decl * pb_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { SASSERT(m_manager); ast_manager& m = *m_manager; for (unsigned i = 0; i < arity; ++i) { if (!m.is_bool(domain[i])) { m.raise_exception("invalid non-Boolean sort applied to 'at-most'"); } } symbol sym; switch(k) { case OP_AT_LEAST_K: sym = m_at_least_sym; break; case OP_AT_MOST_K: sym = m_at_most_sym; break; case OP_PB_LE: sym = m_pble_sym; break; case OP_PB_GE: sym = m_pbge_sym; break; case OP_PB_EQ: sym = m_pbeq_sym; break; default: break; } switch(k) { case OP_AT_LEAST_K: case OP_AT_MOST_K: { if (num_parameters != 1 || !parameters[0].is_int() || parameters[0].get_int() < 0) { m.raise_exception("function expects one non-negative integer parameter"); } func_decl_info info(m_family_id, k, 1, parameters); return m.mk_func_decl(sym, arity, domain, m.mk_bool_sort(), info); } case OP_PB_GE: case OP_PB_LE: case OP_PB_EQ: { if (num_parameters != 1 + arity) { m.raise_exception("function expects arity+1 rational parameters"); } vector params; for (unsigned i = 0; i < num_parameters; ++i) { parameter const& p = parameters[i]; if (p.is_int()) { params.push_back(p); } else if (p.is_rational()) { // HACK: ast pretty printer does not work with rationals. rational r = p.get_rational(); if (r.is_int32()) { params.push_back(parameter(r.get_int32())); } else { params.push_back(p); } } else { m.raise_exception("functions 'pble/pbge/pbeq' expect arity+1 integer parameters"); } } func_decl_info info(m_family_id, k, num_parameters, params.c_ptr()); return m.mk_func_decl(sym, arity, domain, m.mk_bool_sort(), info); } default: UNREACHABLE(); return 0; } } void pb_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { if (logic == symbol::null) { op_names.push_back(builtin_name(m_at_most_sym.bare_str(), OP_AT_MOST_K)); op_names.push_back(builtin_name(m_at_least_sym.bare_str(), OP_AT_LEAST_K)); op_names.push_back(builtin_name(m_pble_sym.bare_str(), OP_PB_LE)); op_names.push_back(builtin_name(m_pbge_sym.bare_str(), OP_PB_GE)); op_names.push_back(builtin_name(m_pbeq_sym.bare_str(), OP_PB_EQ)); } } app * pb_util::mk_le(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k) { vector params; params.push_back(parameter(k)); for (unsigned i = 0; i < num_args; ++i) { params.push_back(parameter(coeffs[i])); } return m.mk_app(m_fid, OP_PB_LE, params.size(), params.c_ptr(), num_args, args, m.mk_bool_sort()); } app * pb_util::mk_ge(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k) { vector params; params.push_back(parameter(k)); for (unsigned i = 0; i < num_args; ++i) { params.push_back(parameter(coeffs[i])); } return m.mk_app(m_fid, OP_PB_GE, params.size(), params.c_ptr(), num_args, args, m.mk_bool_sort()); } app * pb_util::mk_eq(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k) { vector params; params.push_back(parameter(k)); for (unsigned i = 0; i < num_args; ++i) { params.push_back(parameter(coeffs[i])); } return m.mk_app(m_fid, OP_PB_EQ, params.size(), params.c_ptr(), num_args, args, m.mk_bool_sort()); } // ax + by < k // <=> // -ax - by >= -k + 1 // <=> // a(1-x) + b(1-y) >= -k + a + b + 1 app * pb_util::mk_lt(unsigned num_args, rational const * _coeffs, expr * const * _args, rational const& _k) { vector coeffs; rational k(_k); expr_ref_vector args(m); expr* f; rational d(denominator(k)); for (unsigned i = 0; i < num_args; ++i) { coeffs.push_back(_coeffs[i]); d = lcm(d, denominator(coeffs[i])); if (m.is_not(_args[i], f)) { args.push_back(f); } else { args.push_back(m.mk_not(_args[i])); } } if (!d.is_one()) { k *= d; for (unsigned i = 0; i < num_args; ++i) { coeffs[i] *= d; } } k.neg(); k += rational::one(); for (unsigned i = 0; i < num_args; ++i) { k += coeffs[i]; } return mk_ge(num_args, coeffs.c_ptr(), args.c_ptr(), k); } app * pb_util::mk_at_most_k(unsigned num_args, expr * const * args, unsigned k) { parameter param(k); return m.mk_app(m_fid, OP_AT_MOST_K, 1, ¶m, num_args, args, m.mk_bool_sort()); } bool pb_util::is_at_most_k(func_decl *a) const { return is_decl_of(a, m_fid, OP_AT_MOST_K); } bool pb_util::is_at_most_k(expr *a, rational& k) const { if (is_at_most_k(a)) { k = get_k(a); return true; } else { return false; } } app * pb_util::mk_at_least_k(unsigned num_args, expr * const * args, unsigned k) { parameter param(k); return m.mk_app(m_fid, OP_AT_LEAST_K, 1, ¶m, num_args, args, m.mk_bool_sort()); } bool pb_util::is_at_least_k(func_decl *a) const { return is_decl_of(a, m_fid, OP_AT_LEAST_K); } bool pb_util::is_at_least_k(expr *a, rational& k) const { if (is_at_least_k(a)) { k = get_k(a); return true; } else { return false; } } rational pb_util::get_k(func_decl *a) const { parameter const& p = a->get_parameter(0); if (is_at_most_k(a) || is_at_least_k(a)) { return to_rational(p); } else { SASSERT(is_le(a) || is_ge(a) || is_eq(a)); return to_rational(p); } } bool pb_util::is_le(func_decl *a) const { return is_decl_of(a, m_fid, OP_PB_LE); } bool pb_util::is_le(expr* a, rational& k) const { if (is_le(a)) { k = get_k(a); return true; } else { return false; } } bool pb_util::is_ge(func_decl *a) const { return is_decl_of(a, m_fid, OP_PB_GE); } bool pb_util::is_ge(expr* a, rational& k) const { if (is_ge(a)) { k = get_k(a); return true; } else { return false; } } bool pb_util::is_eq(func_decl *a) const { return is_decl_of(a, m_fid, OP_PB_EQ); } bool pb_util::is_eq(expr* a, rational& k) const { if (is_eq(a)) { k = get_k(a); return true; } else { return false; } } rational pb_util::get_coeff(func_decl* a, unsigned index) const { if (is_at_most_k(a) || is_at_least_k(a)) { return rational::one(); } SASSERT(is_le(a) || is_ge(a) || is_eq(a)); SASSERT(1 + index < a->get_num_parameters()); return to_rational(a->get_parameter(index + 1)); } rational pb_util::to_rational(parameter const& p) const { if (p.is_int()) { return rational(p.get_int()); } SASSERT(p.is_rational()); return p.get_rational(); } bool pb_util::has_unit_coefficients(func_decl* f) const { if (is_at_most_k(f) || is_at_least_k(f)) return true; unsigned sz = f->get_arity(); for (unsigned i = 0; i < sz; ++i) { if (!get_coeff(f, i).is_one()) return false; } return true; } z3-4.4.0/src/ast/ast_ll_pp.h0000644000175000017500000000305512540347414015354 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ast_ll_pp.h Abstract: AST low level pretty printer. Author: Leonardo de Moura (leonardo) 2006-10-19. Revision History: --*/ #ifndef _AST_LL_PP_H_ #define _AST_LL_PP_H_ #include"ast.h" #include void ast_ll_pp(std::ostream & out, ast_manager & m, ast * n, bool only_exprs=true, bool compact=true); void ast_ll_pp(std::ostream & out, ast_manager & m, ast * n, ast_mark & visited, bool only_exprs=true, bool compact=true); void ast_def_ll_pp(std::ostream & out, ast_manager & m, ast * n, ast_mark & visited, bool only_exprs=true, bool compact=true); void ast_ll_bounded_pp(std::ostream & out, ast_manager & m, ast * n, unsigned depth); struct mk_ll_pp { ast * m_ast; ast_manager & m_manager; bool m_only_exprs; bool m_compact; mk_ll_pp(ast * a, ast_manager & m, bool only_exprs=true, bool compact=true): m_ast(a), m_manager(m), m_only_exprs(only_exprs), m_compact(compact) {} }; inline std::ostream & operator<<(std::ostream & out, mk_ll_pp const & p) { ast_ll_pp(out, p.m_manager, p.m_ast, p.m_only_exprs, p.m_compact); return out; } struct mk_bounded_pp { ast * m_ast; ast_manager & m_manager; unsigned m_depth; mk_bounded_pp(ast * a, ast_manager & m, unsigned depth=3): m_ast(a), m_manager(m), m_depth(depth) {} }; inline std::ostream & operator<<(std::ostream & out, mk_bounded_pp const & p) { ast_ll_bounded_pp(out, p.m_manager, p.m_ast, p.m_depth); return out; } #endif /* _AST_LL_PP_H_ */ z3-4.4.0/src/ast/for_each_expr.h0000644000175000017500000001112212540347414016175 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: for_each_expr.h Abstract: Author: Leonardo de Moura (leonardo) 2007-12-28. Revision History: --*/ #ifndef _FOR_EACH_EXPR_H_ #define _FOR_EACH_EXPR_H_ #include"ast.h" #include"trace.h" template void for_each_expr_core(ForEachProc & proc, ExprMark & visited, expr * n) { typedef std::pair frame; if (MarkAll || n->get_ref_count() > 1) { if (visited.is_marked(n)) return; visited.mark(n); } sbuffer stack; stack.push_back(frame(n, 0)); while (!stack.empty()) { start: frame & fr = stack.back(); expr * curr = fr.first; switch (curr->get_kind()) { case AST_VAR: proc(to_var(curr)); stack.pop_back(); break; case AST_APP: { unsigned num_args = to_app(curr)->get_num_args(); while (fr.second < num_args) { expr * arg = to_app(curr)->get_arg(fr.second); fr.second++; if (MarkAll || arg->get_ref_count() > 1) { if (visited.is_marked(arg)) continue; visited.mark(arg); } switch (arg->get_kind()) { case AST_VAR: proc(to_var(arg)); break; case AST_QUANTIFIER: stack.push_back(frame(arg, 0)); goto start; case AST_APP: if (to_app(arg)->get_num_args() == 0) { proc(to_app(arg)); } else { stack.push_back(frame(arg, 0)); goto start; } break; default: UNREACHABLE(); break; } } stack.pop_back(); proc(to_app(curr)); break; } case AST_QUANTIFIER: { quantifier * q = to_quantifier(curr); unsigned num_children = IgnorePatterns ? 1 : q->get_num_children(); while (fr.second < num_children) { expr * child = q->get_child(fr.second); fr.second++; if (MarkAll || child->get_ref_count() > 1) { if (visited.is_marked(child)) continue; visited.mark(child); } stack.push_back(frame(child, 0)); goto start; } stack.pop_back(); proc(to_quantifier(curr)); break; } default: UNREACHABLE(); break; } } } template bool for_each_expr_args(ptr_vector & stack, expr_mark & visited, unsigned num_args, T * const * args) { bool result = true; for (unsigned i = 0; i < num_args; i++) { T * arg = args[i]; if (!visited.is_marked(arg)) { stack.push_back(arg); result = false; } } return result; } template void for_each_expr(ForEachProc & proc, expr_mark & visited, expr * n) { for_each_expr_core(proc, visited, n); } template void for_each_expr(ForEachProc & proc, expr * n) { expr_mark visited; for_each_expr_core(proc, visited, n); } template void quick_for_each_expr(ForEachProc & proc, expr_fast_mark1 & visited, expr * n) { for_each_expr_core(proc, visited, n); } template void quick_for_each_expr(ForEachProc & proc, expr * n) { expr_fast_mark1 visited; for_each_expr_core(proc, visited, n); } template struct for_each_expr_proc : public EscapeProc { void operator()(expr * n) { EscapeProc::operator()(n); } void operator()(var * n) { operator()(static_cast(n)); } void operator()(app * n) { operator()(static_cast(n)); } void operator()(quantifier * n) { operator()(static_cast(n)); } }; unsigned get_num_exprs(expr * n); unsigned get_num_exprs(expr * n, expr_mark & visited); unsigned get_num_exprs(expr * n, expr_fast_mark1 & visited); bool has_skolem_functions(expr * n); #endif /* _FOR_EACH_EXPR_H_ */ z3-4.4.0/src/ast/well_sorted.cpp0000644000175000017500000000516012540347414016254 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: well_sorted.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-12-29. Revision History: --*/ #include #include"for_each_expr.h" #include"well_sorted.h" #include"ast_ll_pp.h" #include"ast_pp.h" #include"warning.h" #include"ast_smt2_pp.h" struct well_sorted_proc { ast_manager & m_manager; bool m_error; well_sorted_proc(ast_manager & m):m_manager(m), m_error(false) {} void operator()(var * v) {} void operator()(quantifier * n) { expr const * e = n->get_expr(); if (!m_manager.is_bool(e)) { warning_msg("quantifier's body must be a boolean."); m_error = true; } } void operator()(app * n) { unsigned num_args = n->get_num_args(); func_decl * decl = n->get_decl(); if (num_args != decl->get_arity() && !decl->is_associative()) { TRACE("ws", tout << "unexpected number of arguments.\n" << mk_ismt2_pp(n, m_manager);); warning_msg("unexpected number of arguments."); m_error = true; return; } for (unsigned i = 0; i < num_args; i++) { sort * actual_sort = m_manager.get_sort(n->get_arg(i)); sort * expected_sort = decl->is_associative() ? decl->get_domain(0) : decl->get_domain(i); if (expected_sort != actual_sort) { TRACE("tc", tout << "sort mismatch on argument #" << i << ".\n" << mk_ismt2_pp(n, m_manager); tout << "Sort mismatch for argument " << i+1 << " of " << mk_ismt2_pp(n, m_manager, false) << "\n"; tout << "Expected sort: " << mk_pp(expected_sort, m_manager) << "\n"; tout << "Actual sort: " << mk_pp(actual_sort, m_manager) << "\n"; tout << "Function sort: " << mk_pp(decl, m_manager) << "."; ); std::ostringstream strm; strm << "Sort mismatch for argument " << i+1 << " of " << mk_ll_pp(n, m_manager, false) << "\n"; strm << "Expected sort: " << mk_pp(expected_sort, m_manager) << "\n"; strm << "Actual sort: " << mk_pp(actual_sort, m_manager) << "\n"; strm << "Function sort: " << mk_pp(decl, m_manager) << "."; warning_msg(strm.str().c_str()); m_error = true; return; } } } }; bool is_well_sorted(ast_manager const & m, expr * n) { well_sorted_proc p(const_cast(m)); for_each_expr(p, n); return !p.m_error; } z3-4.4.0/src/ast/decl_collector.h0000644000175000017500000000246712540347414016362 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: decl_collector.h Abstract: Collect uninterpreted func_delcs and sorts. This class was originally in ast_smt_pp.h Author: Leonardo (leonardo) 2011-10-04 Revision History: --*/ #ifndef _SMT_DECL_COLLECTOR_H_ #define _SMT_DECL_COLLECTOR_H_ #include"ast.h" class decl_collector { ast_manager & m_manager; bool m_sep_preds; ptr_vector m_sorts; ptr_vector m_decls; ptr_vector m_preds; ast_mark m_visited; family_id m_basic_fid; family_id m_dt_fid; void visit_sort(sort* n); bool is_bool(sort* s); void visit_func(func_decl* n); public: // if preds == true, then predicates are stored in a separate collection. decl_collector(ast_manager & m, bool preds=true); ast_manager & m() { return m_manager; } void visit(ast * n); unsigned get_num_sorts() const { return m_sorts.size(); } unsigned get_num_decls() const { return m_decls.size(); } unsigned get_num_preds() const { return m_preds.size(); } sort * const * get_sorts() const { return m_sorts.c_ptr(); } func_decl * const * get_func_decls() const { return m_decls.c_ptr(); } func_decl * const * get_pred_decls() const { return m_preds.c_ptr(); } }; #endif z3-4.4.0/src/ast/expr2polynomial.cpp0000644000175000017500000003724012540347414017101 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: expr2polynomial.cpp Abstract: Translator from Z3 expressions into multivariate polynomials (and back). Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #include"expr2polynomial.h" #include"expr2var.h" #include"arith_decl_plugin.h" #include"ast_smt2_pp.h" #include"z3_exception.h" #include"cooperate.h" struct expr2polynomial::imp { struct frame { app * m_curr; unsigned m_idx; frame():m_curr(0), m_idx(0) {} frame(app * t):m_curr(t), m_idx(0) {} }; expr2polynomial & m_wrapper; ast_manager & m_am; arith_util m_autil; polynomial::manager & m_pm; expr2var * m_expr2var; bool m_expr2var_owner; expr_ref_vector m_var2expr; obj_map m_cache; expr_ref_vector m_cached_domain; polynomial::polynomial_ref_vector m_cached_polynomials; polynomial::scoped_numeral_vector m_cached_denominators; svector m_frame_stack; polynomial::polynomial_ref_vector m_presult_stack; polynomial::scoped_numeral_vector m_dresult_stack; bool m_use_var_idxs; volatile bool m_cancel; imp(expr2polynomial & w, ast_manager & am, polynomial::manager & pm, expr2var * e2v, bool use_var_idxs): m_wrapper(w), m_am(am), m_autil(am), m_pm(pm), m_expr2var(e2v == 0 && !use_var_idxs ? alloc(expr2var, am) : e2v), m_expr2var_owner(e2v == 0 && !use_var_idxs), m_var2expr(am), m_cached_domain(am), m_cached_polynomials(pm), m_cached_denominators(pm.m()), m_presult_stack(pm), m_dresult_stack(pm.m()), m_use_var_idxs(use_var_idxs), m_cancel(false) { } ~imp() { if (m_expr2var_owner) dealloc(m_expr2var); } ast_manager & m() { return m_am; } polynomial::manager & pm() { return m_pm; } polynomial::numeral_manager & nm() { return pm().m(); } void reset() { m_frame_stack.reset(); m_presult_stack.reset(); m_dresult_stack.reset(); } void reset_cache() { m_cache.reset(); m_cached_domain.reset(); m_cached_polynomials.reset(); m_cached_denominators.reset(); } void checkpoint() { if (m_cancel) throw default_exception("canceled"); cooperate("expr2polynomial"); } void throw_not_polynomial() { throw default_exception("the given expression is not a polynomial"); } void throw_no_int_var() { throw default_exception("integer variables are not allowed in the given polynomial"); } void push_frame(app * t) { m_frame_stack.push_back(frame(t)); } void cache_result(expr * t) { SASSERT(!m_cache.contains(t)); SASSERT(m_cached_denominators.size() == m_cached_polynomials.size()); SASSERT(m_cached_denominators.size() == m_cached_domain.size()); if (t->get_ref_count() <= 1) return; unsigned idx = m_cached_polynomials.size(); m_cache.insert(t, idx); m_cached_domain.push_back(t); m_cached_polynomials.push_back(m_presult_stack.back()); m_cached_denominators.push_back(m_dresult_stack.back()); } bool is_cached(expr * t) { return t->get_ref_count() > 1 && m_cache.contains(t); } bool is_int_real(expr * t) { return m_autil.is_int_real(t); } void store_result(expr * t, polynomial::polynomial * p, polynomial::numeral & d) { m_presult_stack.push_back(p); m_dresult_stack.push_back(d); cache_result(t); } void store_var_poly(expr * t) { polynomial::var x; if (m_use_var_idxs) { SASSERT(::is_var(t)); if (m_autil.is_int(t)) throw_no_int_var(); unsigned idx = to_var(t)->get_idx(); while (idx >= m_pm.num_vars()) m_pm.mk_var(); x = static_cast(idx); } else { x = m_expr2var->to_var(t); if (x == UINT_MAX) { bool is_int = m_autil.is_int(t); x = m_wrapper.mk_var(is_int); m_expr2var->insert(t, x); if (x >= m_var2expr.size()) m_var2expr.resize(x+1, 0); m_var2expr.set(x, t); } } polynomial::numeral one(1); store_result(t, pm().mk_polynomial(x), one); } void store_const_poly(app * n) { rational val; VERIFY(m_autil.is_numeral(n, val)); polynomial::scoped_numeral d(nm()); d = val.to_mpq().denominator(); store_result(n, pm().mk_const(numerator(val)), d); } bool visit_arith_app(app * t) { switch (t->get_decl_kind()) { case OP_NUM: store_const_poly(t); return true; case OP_ADD: case OP_SUB: case OP_MUL: case OP_UMINUS: case OP_TO_REAL: push_frame(t); return false; case OP_POWER: { rational k; SASSERT(t->get_num_args() == 2); if (!m_autil.is_numeral(t->get_arg(1), k) || !k.is_int() || !k.is_unsigned()) { if (m_use_var_idxs) throw_not_polynomial(); else store_var_poly(t); return true; } push_frame(t); return false; } default: // can't handle operator if (m_use_var_idxs) throw_not_polynomial(); store_var_poly(t); return true; } } bool visit(expr * t) { SASSERT(is_int_real(t)); if (is_cached(t)) { unsigned idx = m_cache.find(t); m_presult_stack.push_back(m_cached_polynomials.get(idx)); m_dresult_stack.push_back(m_cached_denominators.get(idx)); return true; } SASSERT(!is_quantifier(t)); if (::is_var(t)) { store_var_poly(t); return true; } SASSERT(is_app(t)); if (!m_autil.is_arith_expr(t)) { if (m_use_var_idxs) throw_not_polynomial(); store_var_poly(t); return true; } return visit_arith_app(to_app(t)); } void pop(unsigned num_args) { SASSERT(m_presult_stack.size() == m_dresult_stack.size()); SASSERT(m_presult_stack.size() >= num_args); m_presult_stack.shrink(m_presult_stack.size() - num_args); m_dresult_stack.shrink(m_dresult_stack.size() - num_args); } polynomial::polynomial * const * polynomial_args(unsigned num_args) { SASSERT(m_presult_stack.size() >= num_args); return m_presult_stack.c_ptr() + m_presult_stack.size() - num_args; } polynomial::numeral const * denominator_args(unsigned num_args) { SASSERT(m_dresult_stack.size() >= num_args); return m_dresult_stack.c_ptr() + m_dresult_stack.size() - num_args; } template void process_add_sub(app * t) { SASSERT(t->get_num_args() <= m_presult_stack.size()); unsigned num_args = t->get_num_args(); polynomial::polynomial * const * p_args = polynomial_args(num_args); polynomial::numeral const * d_args = denominator_args(num_args); polynomial::polynomial_ref p(pm()); polynomial::polynomial_ref p_aux(pm()); polynomial::scoped_numeral d(nm()); polynomial::scoped_numeral d_aux(nm()); d = 1; for (unsigned i = 0; i < num_args; i++) { nm().lcm(d, d_args[i], d); } p = pm().mk_zero(); for (unsigned i = 0; i < num_args; i++) { checkpoint(); nm().div(d, d_args[i], d_aux); p_aux = pm().mul(d_aux, p_args[i]); if (i == 0) p = p_aux; else if (is_add) p = pm().add(p, p_aux); else p = pm().sub(p, p_aux); } pop(num_args); store_result(t, p.get(), d.get()); } void process_add(app * t) { process_add_sub(t); } void process_sub(app * t) { process_add_sub(t); } void process_mul(app * t) { SASSERT(t->get_num_args() <= m_presult_stack.size()); unsigned num_args = t->get_num_args(); polynomial::polynomial * const * p_args = polynomial_args(num_args); polynomial::numeral const * d_args = denominator_args(num_args); polynomial::polynomial_ref p(pm()); polynomial::scoped_numeral d(nm()); p = pm().mk_const(rational(1)); d = 1; for (unsigned i = 0; i < num_args; i++) { checkpoint(); p = pm().mul(p, p_args[i]); d = d * d_args[i]; } pop(num_args); store_result(t, p.get(), d.get()); } void process_uminus(app * t) { SASSERT(t->get_num_args() <= m_presult_stack.size()); polynomial::polynomial_ref neg_p(pm()); neg_p = pm().neg(m_presult_stack.back()); m_presult_stack.pop_back(); m_presult_stack.push_back(neg_p); cache_result(t); } void process_power(app * t) { SASSERT(t->get_num_args() <= m_presult_stack.size()); rational _k; VERIFY(m_autil.is_numeral(t->get_arg(1), _k)); SASSERT(_k.is_int() && _k.is_unsigned()); unsigned k = _k.get_unsigned(); polynomial::polynomial_ref p(pm()); polynomial::scoped_numeral d(nm()); unsigned num_args = t->get_num_args(); polynomial::polynomial * const * p_args = polynomial_args(num_args); polynomial::numeral const * d_args = denominator_args(num_args); pm().pw(p_args[0], k, p); nm().power(d_args[0], k, d); pop(num_args); store_result(t, p.get(), d.get()); } void process_to_real(app * t) { // do nothing cache_result(t); } void process_app(app * t) { SASSERT(m_presult_stack.size() == m_dresult_stack.size()); switch (t->get_decl_kind()) { case OP_ADD: process_add(t); return; case OP_SUB: process_sub(t); return; case OP_MUL: process_mul(t); return; case OP_POWER: process_power(t); return; case OP_UMINUS: process_uminus(t); return; case OP_TO_REAL: process_to_real(t); return; default: UNREACHABLE(); } } bool to_polynomial(expr * t, polynomial::polynomial_ref & p, polynomial::scoped_numeral & d) { if (!is_int_real(t)) return false; reset(); if (!visit(t)) { while (!m_frame_stack.empty()) { begin_loop: checkpoint(); frame & fr = m_frame_stack.back(); app * a = fr.m_curr; TRACE("expr2polynomial", tout << "processing: " << fr.m_idx << "\n" << mk_ismt2_pp(a, m()) << "\n";); unsigned num_args = a->get_num_args(); while (fr.m_idx < num_args) { expr * arg = a->get_arg(fr.m_idx); fr.m_idx++; if (!visit(arg)) goto begin_loop; } process_app(a); m_frame_stack.pop_back(); } } p = m_presult_stack.back(); d = m_dresult_stack.back(); reset(); return true; } bool is_int_poly(polynomial::polynomial_ref const & p) { unsigned sz = size(p); for (unsigned i = 0; i < sz; i++) { polynomial::monomial * m = pm().get_monomial(p, i); unsigned msz = pm().size(m); for (unsigned j = 0; j < msz; j++) { polynomial::var x = pm().get_var(m, j); if (!m_wrapper.is_int(x)) return false; } } return true; } void to_expr(polynomial::polynomial_ref const & p, bool use_power, expr_ref & r) { expr_ref_buffer args(m()); expr_ref_buffer margs(m()); unsigned sz = size(p); bool is_int = is_int_poly(p); for (unsigned i = 0; i < sz; i++) { margs.reset(); polynomial::monomial * _m = pm().get_monomial(p, i); polynomial::numeral const & a = pm().coeff(p, i); if (!nm().is_one(a)) { margs.push_back(m_autil.mk_numeral(rational(a), is_int)); } unsigned msz = pm().size(_m); for (unsigned j = 0; j < msz; j++) { polynomial::var x = pm().get_var(_m, j); expr * t; if (m_use_var_idxs) { t = m().mk_var(x, m_autil.mk_real()); } else { t = m_var2expr.get(x); if (m_wrapper.is_int(x) && !is_int) { t = m_autil.mk_to_real(t); } } unsigned d = pm().degree(_m, j); if (use_power && d > 1) { margs.push_back(m_autil.mk_power(t, m_autil.mk_numeral(rational(d), is_int))); } else { for (unsigned k = 0; k < d; k++) margs.push_back(t); } } if (margs.size() == 0) { args.push_back(m_autil.mk_numeral(rational(1), is_int)); } else if (margs.size() == 1) { args.push_back(margs[0]); } else { args.push_back(m_autil.mk_mul(margs.size(), margs.c_ptr())); } } if (args.size() == 0) { r = m_autil.mk_numeral(rational(0), is_int); } else if (args.size() == 1) { r = args[0]; } else { r = m_autil.mk_add(args.size(), args.c_ptr()); } } void set_cancel(bool f) { m_cancel = f; } }; expr2polynomial::expr2polynomial(ast_manager & am, polynomial::manager & pm, expr2var * e2v, bool use_var_idxs) { m_imp = alloc(imp, *this, am, pm, e2v, use_var_idxs); } expr2polynomial::~expr2polynomial() { dealloc(m_imp); } ast_manager & expr2polynomial::m() const { return m_imp->m_am; } polynomial::manager & expr2polynomial::pm() const { return m_imp->m_pm; } bool expr2polynomial::to_polynomial(expr * t, polynomial::polynomial_ref & p, polynomial::scoped_numeral & d) { return m_imp->to_polynomial(t, p, d); } void expr2polynomial::to_expr(polynomial::polynomial_ref const & p, bool use_power, expr_ref & r) { m_imp->to_expr(p, use_power, r); } bool expr2polynomial::is_var(expr * t) const { SASSERT(!m_imp->m_use_var_idxs); return m_imp->m_expr2var->is_var(t); } expr2var const & expr2polynomial::get_mapping() const { SASSERT(!m_imp->m_use_var_idxs); return *(m_imp->m_expr2var); } void expr2polynomial::set_cancel(bool f) { m_imp->set_cancel(f); } default_expr2polynomial::default_expr2polynomial(ast_manager & am, polynomial::manager & pm): expr2polynomial(am, pm, 0) { } default_expr2polynomial::~default_expr2polynomial() { } bool default_expr2polynomial::is_int(polynomial::var x) const { return m_is_int[x]; } polynomial::var default_expr2polynomial::mk_var(bool is_int) { polynomial::var x = pm().mk_var(); m_is_int.reserve(x+1, false); m_is_int[x] = is_int; return x; } z3-4.4.0/src/ast/array_decl_plugin.cpp0000644000175000017500000005411112540347414017414 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: array_decl_plugin.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-09 Revision History: --*/ #include #include"array_decl_plugin.h" #include"warning.h" #include"ast_pp.h" #include"ast_ll_pp.h" array_decl_plugin::array_decl_plugin(): m_store_sym("store"), m_select_sym("select"), m_const_sym("const"), m_default_sym("default"), m_map_sym("map"), m_set_union_sym("union"), m_set_intersect_sym("intersect"), m_set_difference_sym("difference"), m_set_complement_sym("complement"), m_set_subset_sym("subset"), m_array_ext_sym("array-ext"), m_as_array_sym("as-array") { } #define ARRAY_SORT_STR "Array" sort * array_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { SASSERT(k == ARRAY_SORT); if (num_parameters < 2) { m_manager->raise_exception("invalid array sort definition, invalid number of parameters"); return 0; } for (unsigned i = 0; i < num_parameters; i++) { if (!parameters[i].is_ast() || !is_sort(parameters[i].get_ast())) { m_manager->raise_exception("invalid array sort definition, parameter is not a sort"); return 0; } } sort * range = to_sort(parameters[num_parameters - 1].get_ast()); TRACE("array_decl_plugin_bug", tout << mk_pp(range, *m_manager) << "\n";); if (!range->is_infinite() && !range->is_very_big() && (1 == range->get_num_elements().size())) { return m_manager->mk_sort(symbol(ARRAY_SORT_STR), sort_info(m_family_id, ARRAY_SORT, 1, num_parameters, parameters)); } bool is_infinite = false; bool is_very_big = false; for (unsigned i = 0; i < num_parameters; i++) { sort * s = to_sort(parameters[i].get_ast()); if (s->is_infinite()) { is_infinite = true; } if (s->is_very_big()) { is_very_big = true; } } if (is_infinite) { return m_manager->mk_sort(symbol(ARRAY_SORT_STR), sort_info(m_family_id, ARRAY_SORT, num_parameters, parameters)); } else if (is_very_big) { return m_manager->mk_sort(symbol(ARRAY_SORT_STR), sort_info(m_family_id, ARRAY_SORT, sort_size::mk_very_big(), num_parameters, parameters)); } else { rational domain_sz(1); rational num_elements; for (unsigned i = 0; i < num_parameters - 1; i++) { domain_sz *= rational(to_sort(parameters[i].get_ast())->get_num_elements().size(),rational::ui64()); } if (domain_sz <= rational(128)) { num_elements = rational(range->get_num_elements().size(),rational::ui64()); num_elements = power(num_elements, static_cast(domain_sz.get_int64())); } if (domain_sz > rational(128) || !num_elements.is_uint64()) { // too many elements... return m_manager->mk_sort(symbol(ARRAY_SORT_STR), sort_info(m_family_id, ARRAY_SORT, sort_size::mk_very_big(), num_parameters, parameters)); } else { return m_manager->mk_sort(symbol(ARRAY_SORT_STR), sort_info(m_family_id, ARRAY_SORT, num_elements.get_uint64(), num_parameters, parameters)); } } } bool array_decl_plugin::is_array_sort(sort* s) const { return m_family_id == s->get_family_id() && s->get_decl_kind() == ARRAY_SORT; } func_decl * array_decl_plugin::mk_const(sort * s, unsigned arity, sort * const * domain) { if (arity != 1) { m_manager->raise_exception("invalid const array definition, invalid domain size"); return 0; } if (!is_array_sort(s)) { m_manager->raise_exception("invalid const array definition, parameter is not an array sort"); return 0; } if (!m_manager->compatible_sorts(get_array_range(s), domain[0])) { m_manager->raise_exception("invalid const array definition, sort mismatch between array range and argument"); return 0; } parameter param(s); func_decl_info info(m_family_id, OP_CONST_ARRAY, 1, ¶m); info.m_private_parameters = true; return m_manager->mk_func_decl(m_const_sym, arity, domain, s, info); } func_decl * array_decl_plugin::mk_map(func_decl* f, unsigned arity, sort* const* domain) { if (arity != f->get_arity()) { std::ostringstream buffer; buffer << "map expects to take as many arguments as the function being mapped, " << "it was given " << arity << " but expects " << f->get_arity(); m_manager->raise_exception(buffer.str().c_str()); return 0; } if (arity == 0) { m_manager->raise_exception("don't use map on constants"); return 0; } // // check that each domain[i] is an array sort // with the same domains and same ranges. // and that the ranges coincide with the domain of f. // unsigned dom_arity = get_array_arity(domain[0]); for (unsigned i = 0; i < arity; ++i) { if (!is_array_sort(domain[i])) { std::ostringstream buffer; buffer << "map expects an array sort as argument at position " << i; m_manager->raise_exception(buffer.str().c_str()); return 0; } if (get_array_arity(domain[i]) != dom_arity) { std::ostringstream buffer; buffer << "map expects all arguments to have the same array domain, " << "this is not the case for argument " << i; m_manager->raise_exception(buffer.str().c_str()); return 0; } for (unsigned j = 0; j < dom_arity; ++j) { if (get_array_domain(domain[i],j) != get_array_domain(domain[0],j)) { std::ostringstream buffer; buffer << "map expects all arguments to have the same array domain, " << "this is not the case for argument " << i; m_manager->raise_exception(buffer.str().c_str()); return 0; } } if (get_array_range(domain[i]) != f->get_domain(i)) { std::ostringstream buffer; buffer << "map expects the argument at position " << i << " to have the array range the same as the function"; m_manager->raise_exception(buffer.str().c_str()); return 0; } } vector parameters; for (unsigned i = 0; i < dom_arity; ++i) { parameters.push_back(domain[0]->get_parameter(i)); } parameters.push_back(parameter(f->get_range())); sort* range = mk_sort(ARRAY_SORT, parameters.size(), parameters.c_ptr()); parameter param(f); func_decl_info info(m_family_id, OP_ARRAY_MAP, 1, ¶m); // // left_associative, right_associative, commutative are inherited. // m_injective is inherited, since: // forall x . g(f(x)) = x implies forall X . map(g)(map(f)(X)) = X // since map(g)(map(f)(X))[i] = g(f(X[i])) = X[i] // info.set_right_associative(f->is_right_associative()); info.set_left_associative(f->is_left_associative()); info.set_commutative(f->is_commutative()); info.set_injective(f->is_injective()); return m_manager->mk_func_decl(m_map_sym, arity, domain, range, info); } func_decl * array_decl_plugin::mk_default(unsigned domain_size, sort * const * domain) { if (domain_size != 1) { m_manager->raise_exception("invalid default array definition, invalid domain size"); return 0; } // check that domain[0] is an array sort. unsigned num_parameters = domain[0]->get_num_parameters(); if (num_parameters <= 1) { m_manager->raise_exception("parameter mismatch. There should be more than one parameter to defaults"); return 0; } parameter param(domain[0]->get_parameter(num_parameters-1)); if (!param.is_ast() || !is_sort(param.get_ast())) { m_manager->raise_exception("last parameter should be a sort"); return 0; } sort * s = to_sort(param.get_ast()); return m_manager->mk_func_decl(m_default_sym, domain_size, domain, s, func_decl_info(m_family_id, OP_ARRAY_DEFAULT)); } func_decl* array_decl_plugin::mk_select(unsigned arity, sort * const * domain) { if (arity <= 1) { m_manager->raise_exception("select takes at least two arguments"); return 0; } sort * s = domain[0]; unsigned num_parameters = s->get_num_parameters(); parameter const* parameters = s->get_parameters(); if (num_parameters != arity) { m_manager->raise_exception("select requires as many arguments as the size of the domain"); return 0; } ptr_buffer new_domain; // we need this because of coercions. new_domain.push_back(s); for (unsigned i = 0; i + 1 < num_parameters; ++i) { if (!parameters[i].is_ast() || !is_sort(parameters[i].get_ast()) || !m_manager->compatible_sorts(domain[i+1], to_sort(parameters[i].get_ast()))) { m_manager->raise_exception("domain sort and parameter do not match"); UNREACHABLE(); return 0; } new_domain.push_back(to_sort(parameters[i].get_ast())); } SASSERT(new_domain.size() == arity); return m_manager->mk_func_decl(m_select_sym, arity, new_domain.c_ptr(), get_array_range(domain[0]), func_decl_info(m_family_id, OP_SELECT)); } func_decl * array_decl_plugin::mk_store(unsigned arity, sort * const * domain) { if (arity < 3) { m_manager->raise_exception("store takes at least 3 arguments"); return 0; } sort * s = domain[0]; unsigned num_parameters = s->get_num_parameters(); parameter const * parameters = s->get_parameters(); if (!is_array_sort(s)) { m_manager->raise_exception("store expects the first argument sort to be an array"); UNREACHABLE(); return 0; } if (arity != num_parameters+1) { std::ostringstream buffer; buffer << "store expects the first argument to be an array taking " << num_parameters+1 << ", instead it was passed " << (arity - 1) << "arguments"; m_manager->raise_exception(buffer.str().c_str()); UNREACHABLE(); return 0; } ptr_buffer new_domain; // we need this because of coercions. new_domain.push_back(s); for (unsigned i = 0; i < num_parameters; ++i) { if (!parameters[i].is_ast() || !is_sort(parameters[i].get_ast())) { m_manager->raise_exception("expecting sort parameter"); return 0; } if (!m_manager->compatible_sorts(to_sort(parameters[i].get_ast()), domain[i+1])) { m_manager->raise_exception("domain sort and parameter do not match"); UNREACHABLE(); return 0; } new_domain.push_back(to_sort(parameters[i].get_ast())); } SASSERT(new_domain.size() == arity); return m_manager->mk_func_decl(m_store_sym, arity, new_domain.c_ptr(), domain[0], func_decl_info(m_family_id, OP_STORE)); } func_decl * array_decl_plugin::mk_array_ext_skolem(unsigned arity, sort * const * domain, unsigned i) { if (arity != 2 || domain[0] != domain[1]) { UNREACHABLE(); return 0; } sort * s = domain[0]; unsigned num_parameters = s->get_num_parameters(); if (num_parameters == 0 || i >= num_parameters - 1) { UNREACHABLE(); return 0; } sort * r = to_sort(s->get_parameter(i).get_ast()); parameter param(s); return m_manager->mk_func_decl(m_array_ext_sym, arity, domain, r, func_decl_info(m_family_id, OP_ARRAY_EXT_SKOLEM, 1, ¶m)); } bool array_decl_plugin::check_set_arguments(unsigned arity, sort * const * domain) { for (unsigned i = 0; i < arity; ++i) { if (domain[i] != domain[0]) { std::ostringstream buffer; buffer << "arguments " << 1 << " and " << (i+1) << " have different sorts"; m_manager->raise_exception(buffer.str().c_str()); return false; } if (domain[i]->get_family_id() != m_family_id) { std::ostringstream buffer; buffer << "argument " << (i+1) << " is not of array sort"; m_manager->raise_exception(buffer.str().c_str()); return false; } } if (arity > 0) { unsigned num_params = domain[0]->get_num_parameters(); parameter const* params = domain[0]->get_parameters(); if (1 >= num_params) { m_manager->raise_exception("expecting 2 or more parameters"); UNREACHABLE(); return false; } if (!params[num_params-1].is_ast()) { m_manager->raise_exception("expecting term parameters"); UNREACHABLE(); return false; } if (!is_sort(params[num_params-1].get_ast()) || !m_manager->is_bool(to_sort(params[num_params-1].get_ast()))) { m_manager->raise_exception("expecting boolean range"); UNREACHABLE(); return false; } } return true; } func_decl * array_decl_plugin::mk_set_union(unsigned arity, sort * const * domain) { if (arity == 0) { m_manager->raise_exception("union takes at least one argument"); return 0; } sort * s = domain[0]; if (!check_set_arguments(arity, domain)) { return 0; } parameter param(s); func_decl_info info(m_family_id, OP_SET_UNION, 1, ¶m); info.set_associative(); info.set_commutative(); info.set_idempotent(); sort* domain2[2] = { domain[0], domain[0] }; return m_manager->mk_func_decl(m_set_union_sym, 2, domain2, domain[0], info); } func_decl * array_decl_plugin::mk_set_intersect(unsigned arity, sort * const * domain) { if (arity == 0) { m_manager->raise_exception("intersection takes at least one argument"); return 0; } if (!check_set_arguments(arity, domain)) { return 0; } func_decl_info info(m_family_id, OP_SET_INTERSECT); info.set_associative(); info.set_commutative(); info.set_idempotent(); sort* domain2[2] = { domain[0], domain[0] }; return m_manager->mk_func_decl(m_set_intersect_sym, 2, domain2, domain[0], info); } func_decl * array_decl_plugin::mk_set_difference(unsigned arity, sort * const * domain) { if (arity != 2) { m_manager->raise_exception("set difference takes precisely two arguments"); return 0; } if (!check_set_arguments(arity, domain)) { return 0; } return m_manager->mk_func_decl(m_set_difference_sym, arity, domain, domain[0], func_decl_info(m_family_id, OP_SET_DIFFERENCE)); } func_decl * array_decl_plugin::mk_set_complement(unsigned arity, sort * const * domain) { if (arity != 1) { m_manager->raise_exception("set complement takes one argument"); return 0; } if (!check_set_arguments(arity, domain)) { return 0; } return m_manager->mk_func_decl(m_set_complement_sym, arity, domain, domain[0], func_decl_info(m_family_id, OP_SET_COMPLEMENT)); } func_decl * array_decl_plugin::mk_set_subset(unsigned arity, sort * const * domain) { if (arity != 2) { m_manager->raise_exception("subset takes two arguments"); return 0; } if (!check_set_arguments(arity, domain)) { return 0; } sort * bool_sort = m_manager->mk_bool_sort(); return m_manager->mk_func_decl(m_set_subset_sym, arity, domain, bool_sort, func_decl_info(m_family_id, OP_SET_SUBSET)); } func_decl * array_decl_plugin::mk_as_array(func_decl * f) { vector parameters; for (unsigned i = 0; i < f->get_arity(); i++) { parameters.push_back(parameter(f->get_domain(i))); } parameters.push_back(parameter(f->get_range())); sort * s = mk_sort(ARRAY_SORT, parameters.size(), parameters.c_ptr()); parameter param(f); func_decl_info info(m_family_id, OP_AS_ARRAY, 1, ¶m); return m_manager->mk_const_decl(m_as_array_sym, s, info); } func_decl * array_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { switch (k) { case OP_SELECT: return mk_select(arity, domain); case OP_STORE: return mk_store(arity, domain); case OP_CONST_ARRAY: { if (num_parameters == 1 && parameters[0].is_ast() && is_sort(parameters[0].get_ast())) { sort * s = to_sort(parameters[0].get_ast()); return mk_const(s, arity, domain); } else if (range != 0) { return mk_const(range, arity, domain); } else { m_manager->raise_exception("array operation requires one sort parameter (the array sort)"); UNREACHABLE(); return 0; } } case OP_ARRAY_MAP: { if (num_parameters != 1 || !parameters[0].is_ast() || !is_func_decl(parameters[0].get_ast())) { m_manager->raise_exception("array operation requires one function declaration parameter (the function to be mapped)"); UNREACHABLE(); return 0; } func_decl * f = to_func_decl(parameters[0].get_ast()); return mk_map(f, arity, domain); } case OP_ARRAY_EXT_SKOLEM: if (num_parameters != 1 || !parameters[0].is_int()) { UNREACHABLE(); return 0; } return mk_array_ext_skolem(arity, domain, parameters[0].get_int()); case OP_ARRAY_DEFAULT: return mk_default(arity, domain); case OP_SET_UNION: return mk_set_union(arity, domain); case OP_SET_INTERSECT: return mk_set_intersect(arity, domain); case OP_SET_DIFFERENCE: return mk_set_difference(arity, domain); case OP_SET_COMPLEMENT: return mk_set_complement(arity, domain); case OP_SET_SUBSET: return mk_set_subset(arity, domain); case OP_AS_ARRAY: { if (num_parameters != 1 || !parameters[0].is_ast() || !is_func_decl(parameters[0].get_ast()) || to_func_decl(parameters[0].get_ast())->get_arity() == 0) { TRACE("array_bug", tout << "num_parameters: " << num_parameters << std::endl; tout << "parameter.kind: " << parameters[0].is_int() << " " << parameters[0].is_ast() << " " << parameters[0].is_symbol() << "\n"; tout << "as-array-bug: " << to_func_decl(parameters[0].get_ast())->get_name() << " " << to_func_decl(parameters[0].get_ast())->get_arity() << std::endl;); m_manager->raise_exception("as-array takes one parameter, a function declaration with arity greater than zero"); UNREACHABLE(); return 0; } func_decl * f = to_func_decl(parameters[0].get_ast()); return mk_as_array(f); } default: return 0; } } void array_decl_plugin::get_sort_names(svector& sort_names, symbol const & logic) { sort_names.push_back(builtin_name(ARRAY_SORT_STR, ARRAY_SORT)); } void array_decl_plugin::get_op_names(svector& op_names, symbol const & logic) { op_names.push_back(builtin_name("store",OP_STORE)); op_names.push_back(builtin_name("select",OP_SELECT)); if (logic == symbol::null) { // none of the SMT2 logics support these extensions op_names.push_back(builtin_name("const",OP_CONST_ARRAY)); op_names.push_back(builtin_name("map",OP_ARRAY_MAP)); op_names.push_back(builtin_name("default",OP_ARRAY_DEFAULT)); op_names.push_back(builtin_name("union",OP_SET_UNION)); op_names.push_back(builtin_name("intersect",OP_SET_INTERSECT)); op_names.push_back(builtin_name("difference",OP_SET_DIFFERENCE)); op_names.push_back(builtin_name("complement",OP_SET_COMPLEMENT)); op_names.push_back(builtin_name("subset",OP_SET_SUBSET)); op_names.push_back(builtin_name("as-array", OP_AS_ARRAY)); } } expr * array_decl_plugin::get_some_value(sort * s) { SASSERT(s->is_sort_of(m_family_id, ARRAY_SORT)); sort * r = to_sort(s->get_parameter(s->get_num_parameters() - 1).get_ast()); expr * v = m_manager->get_some_value(r); parameter p(s); return m_manager->mk_app(m_family_id, OP_CONST_ARRAY, 1, &p, 1, &v); } bool array_decl_plugin::is_fully_interp(sort const * s) const { SASSERT(s->is_sort_of(m_family_id, ARRAY_SORT)); unsigned sz = get_array_arity(s); for (unsigned i = 0; i < sz; i++) { if (!m_manager->is_fully_interp(get_array_domain(s, i))) return false; } return m_manager->is_fully_interp(get_array_range(s)); } func_decl * array_recognizers::get_as_array_func_decl(app * n) const { SASSERT(is_as_array(n)); return to_func_decl(n->get_decl()->get_parameter(0).get_ast()); } array_util::array_util(ast_manager& m): array_recognizers(m.mk_family_id("array")), m_manager(m) { } bool array_util::is_as_array_tree(expr * n) { ptr_buffer todo; todo.push_back(n); while (!todo.empty()) { expr * curr = todo.back(); todo.pop_back(); if (is_as_array(curr)) continue; if (m_manager.is_ite(curr)) { todo.push_back(to_app(curr)->get_arg(1)); todo.push_back(to_app(curr)->get_arg(2)); continue; } return false; } return true; } sort * array_util::mk_array_sort(unsigned arity, sort* const* domain, sort* range) { vector params; for (unsigned i = 0; i < arity; ++i) { params.push_back(parameter(domain[i])); } params.push_back(parameter(range)); return m_manager.mk_sort(m_fid, ARRAY_SORT, params.size(), params.c_ptr()); } z3-4.4.0/src/ast/expr_stat.cpp0000644000175000017500000000407512540347414015746 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: expr_stat.cpp Abstract: Expression statistics (symbol count, var count, depth, ...) All functions in these module assume expressions do not contain nested quantifiers. Author: Leonardo de Moura (leonardo) 2008-02-05. Revision History: --*/ #include"for_each_expr.h" #include"expr_stat.h" void get_expr_stat(expr * n, expr_stat & r) { typedef std::pair pair; buffer todo; todo.push_back(pair(n, 0)); while (!todo.empty()) { pair & p = todo.back(); n = p.first; unsigned depth = p.second; unsigned j; todo.pop_back(); r.m_sym_count++; if (depth > r.m_depth) r.m_depth = depth; switch (n->get_kind()) { case AST_APP: j = to_app(n)->get_num_args(); if (j == 0) r.m_const_count++; while (j > 0) { --j; todo.push_back(pair(to_app(n)->get_arg(j), depth + 1)); } break; case AST_VAR: if (to_var(n)->get_idx() > r.m_max_var_idx) r.m_max_var_idx = to_var(n)->get_idx(); r.m_ground = false; break; case AST_QUANTIFIER: todo.push_back(pair(to_quantifier(n)->get_expr(), depth+1)); break; default: UNREACHABLE(); } } } unsigned get_symbol_count(expr * n) { unsigned r = 0; ptr_buffer todo; todo.push_back(n); while (!todo.empty()) { n = todo.back(); unsigned j; todo.pop_back(); r++; switch (n->get_kind()) { case AST_APP: j = to_app(n)->get_num_args(); while (j > 0) { --j; todo.push_back(to_app(n)->get_arg(j)); } break; case AST_QUANTIFIER: todo.push_back(to_quantifier(n)->get_expr()); break; default: break; } } return r; } z3-4.4.0/src/ast/proof_checker/0000755000175000017500000000000012540347414016034 5ustar michaelmichaelz3-4.4.0/src/ast/proof_checker/proof_checker.cpp0000644000175000017500000013272612540347414021364 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "proof_checker.h" #include "ast_ll_pp.h" #include "ast_pp.h" // include "spc_decl_plugin.h" #include "ast_smt_pp.h" #include "arith_decl_plugin.h" #include "th_rewriter.h" #include "var_subst.h" #define IS_EQUIV(_e_) (m.is_eq(_e_) || m.is_iff(_e_)) #define SAME_OP(_d1_, _d2_) ((_d1_ == _d2_) || (IS_EQUIV(_d1_) && IS_EQUIV(_d2_))) proof_checker::hyp_decl_plugin::hyp_decl_plugin() : m_cons(0), m_atom(0), m_nil(0), m_cell(0) { } void proof_checker::hyp_decl_plugin::finalize() { m_manager->dec_ref(m_cons); m_manager->dec_ref(m_atom); m_manager->dec_ref(m_nil); m_manager->dec_ref(m_cell); } void proof_checker::hyp_decl_plugin::set_manager(ast_manager* m, family_id id) { decl_plugin::set_manager(m,id); m_cell = m->mk_sort(symbol("cell"), sort_info(id, CELL_SORT)); m_cons = m->mk_func_decl(symbol("cons"), m_cell, m_cell, m_cell, func_decl_info(id, OP_CONS)); m_atom = m->mk_func_decl(symbol("atom"), m->mk_bool_sort(), m_cell, func_decl_info(id, OP_ATOM)); m_nil = m->mk_const_decl(symbol("nil"), m_cell, func_decl_info(id, OP_NIL)); m->inc_ref(m_cell); m->inc_ref(m_cons); m->inc_ref(m_atom); m->inc_ref(m_nil); } sort * proof_checker::hyp_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters) { SASSERT(k == CELL_SORT); return m_cell; } func_decl * proof_checker::hyp_decl_plugin::mk_func_decl(decl_kind k) { switch(k) { case OP_CONS: return m_cons; case OP_ATOM: return m_atom; case OP_NIL: return m_nil; default: UNREACHABLE(); return 0; } } func_decl * proof_checker::hyp_decl_plugin::mk_func_decl( decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { return mk_func_decl(k); } func_decl * proof_checker::hyp_decl_plugin::mk_func_decl( decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { return mk_func_decl(k); } void proof_checker::hyp_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { if (logic == symbol::null) { op_names.push_back(builtin_name("cons", OP_CONS)); op_names.push_back(builtin_name("atom", OP_ATOM)); op_names.push_back(builtin_name("nil", OP_NIL)); } } void proof_checker::hyp_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { if (logic == symbol::null) { sort_names.push_back(builtin_name("cell", CELL_SORT)); } } proof_checker::proof_checker(ast_manager& m) : m(m), m_todo(m), m_marked(), m_pinned(m), m_nil(m), m_dump_lemmas(false), m_logic("AUFLIA"), m_proof_lemma_id(0) { symbol fam_name("proof_hypothesis"); if (!m.has_plugin(fam_name)) { m.register_plugin(fam_name, alloc(hyp_decl_plugin)); } m_hyp_fid = m.mk_family_id(fam_name); // m_spc_fid = m.get_family_id("spc"); m_nil = m.mk_const(m_hyp_fid, OP_NIL); } bool proof_checker::check(proof* p, expr_ref_vector& side_conditions) { proof_ref curr(m); m_todo.push_back(p); bool result = true; while (result && !m_todo.empty()) { curr = m_todo.back(); m_todo.pop_back(); result = check1(curr.get(), side_conditions); if (!result) { IF_VERBOSE(0, ast_ll_pp(verbose_stream() << "Proof check failed\n", m, curr.get());); UNREACHABLE(); } } m_hypotheses.reset(); m_pinned.reset(); m_todo.reset(); m_marked.reset(); return result; } bool proof_checker::check1(proof* p, expr_ref_vector& side_conditions) { if (p->get_family_id() == m.get_basic_family_id()) { return check1_basic(p, side_conditions); } #if 0 if (p->get_family_id() == m_spc_fid) { return check1_spc(p, side_conditions); } #endif return false; } bool proof_checker::check1_spc(proof* p, expr_ref_vector& side_conditions) { #if 0 decl_kind k = p->get_decl_kind(); bool is_univ = false; expr_ref fact(m), fml(m); expr_ref body(m), fml1(m), fml2(m); sort_ref_vector sorts(m); proof_ref p1(m), p2(m); proof_ref_vector proofs(m); if (match_proof(p, proofs)) { for (unsigned i = 0; i < proofs.size(); ++i) { add_premise(proofs[i].get()); } } switch(k) { case PR_DEMODULATION: { if (match_proof(p, p1) && match_fact(p, fact) && match_fact(p1.get(), fml) && match_quantifier(fml.get(), is_univ, sorts, body) && is_univ) { // TBD: check that fml is an instance of body. return true; } return false; } case PR_SPC_REWRITE: case PR_SUPERPOSITION: case PR_EQUALITY_RESOLUTION: case PR_SPC_RESOLUTION: case PR_FACTORING: case PR_SPC_DER: { if (match_fact(p, fact)) { expr_ref_vector rewrite_eq(m); rewrite_eq.push_back(fact.get()); for (unsigned i = 0; i < proofs.size(); ++i) { if (match_fact(proofs[i].get(), fml)) { rewrite_eq.push_back(m.mk_not(fml.get())); } } expr_ref rewrite_cond(m); rewrite_cond = m.mk_or(rewrite_eq.size(), rewrite_eq.c_ptr()); side_conditions.push_back(rewrite_cond.get()); return true; } return false; } default: UNREACHABLE(); } return false; #else return true; #endif } bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { decl_kind k = p->get_decl_kind(); expr_ref fml0(m), fml1(m), fml2(m), fml(m); expr_ref t1(m), t2(m); expr_ref s1(m), s2(m); expr_ref u1(m), u2(m); expr_ref fact(m), body1(m), body2(m); expr_ref l1(m), l2(m), r1(m), r2(m); func_decl_ref d1(m), d2(m), d3(m); proof_ref p0(m), p1(m), p2(m); proof_ref_vector proofs(m); func_decl_ref f1(m), f2(m); expr_ref_vector terms1(m), terms2(m), terms(m); sort_ref_vector decls1(m), decls2(m); if (match_proof(p, proofs)) { for (unsigned i = 0; i < proofs.size(); ++i) { add_premise(proofs.get(i)); } } switch(k) { case PR_UNDEF: return true; case PR_ASSERTED: return true; case PR_GOAL: return true; case PR_MODUS_PONENS: { if (match_fact(p, fact) && match_proof(p, p0, p1) && match_fact(p0.get(), fml0) && match_fact(p1.get(), fml1) && (match_implies(fml1.get(), t1, t2) || match_iff(fml1.get(), t1, t2)) && (fml0.get() == t1.get()) && (fact.get() == t2.get())) { return true; } UNREACHABLE(); return false; } case PR_REFLEXIVITY: { if (match_fact(p, fact) && match_proof(p) && (match_equiv(fact, t1, t2) || match_oeq(fact, t1, t2)) && (t1.get() == t2.get())) { return true; } UNREACHABLE(); return false; } case PR_SYMMETRY: { if (match_fact(p, fact) && match_proof(p, p1) && match_fact(p1.get(), fml) && match_binary(fact.get(), d1, l1, r1) && match_binary(fml.get(), d2, l2, r2) && SAME_OP(d1.get(), d2.get()) && l1.get() == r2.get() && r1.get() == l2.get()) { // TBD d1, d2 is a symmetric predicate return true; } UNREACHABLE(); return false; } case PR_TRANSITIVITY: { if (match_fact(p, fact) && match_proof(p, p1, p2) && match_fact(p1.get(), fml1) && match_fact(p2.get(), fml2) && match_binary(fact.get(), d1, t1, t2) && match_binary(fml1.get(), d2, s1, s2) && match_binary(fml2.get(), d3, u1, u2) && d1.get() == d2.get() && d2.get() == d3.get() && t1.get() == s1.get() && s2.get() == u1.get() && u2.get() == t2.get()) { // TBD d1 is some transitive predicate. return true; } UNREACHABLE(); return false; } case PR_TRANSITIVITY_STAR: { if (match_fact(p, fact) && match_binary(fact.get(), d1, t1, t2)) { u_map vertices; // TBD check that d1 is transitive, symmetric. for (unsigned i = 0; i < proofs.size(); ++i) { if (match_fact(proofs[i].get(), fml) && match_binary(fml.get(), d2, s1, s2) && d1.get() == d2.get()) { unsigned id1 = s1->get_id(); unsigned id2 = s2->get_id(); #define INSERT(_id) if (vertices.contains(_id)) vertices.remove(_id); else vertices.insert(_id, true); INSERT(id1); INSERT(id2); } else { UNREACHABLE(); return false; } } return vertices.size() == 2 && vertices.contains(t1->get_id()) && vertices.contains(t2->get_id()); } UNREACHABLE(); return false; } case PR_MONOTONICITY: { TRACE("proof_checker", tout << mk_bounded_pp(p, m, 3) << "\n";); if (match_fact(p, fact) && match_binary(fact.get(), d1, t1, t2) && match_app(t1.get(), f1, terms1) && match_app(t2.get(), f2, terms2) && f1.get() == f2.get() && terms1.size() == terms2.size()) { // TBD: d1 is monotone. for (unsigned i = 0; i < terms1.size(); ++i) { expr* term1 = terms1[i].get(); expr* term2 = terms2[i].get(); if (term1 != term2) { bool found = false; for(unsigned j = 0; j < proofs.size() && !found; ++j) { found = match_fact(proofs[j].get(), fml) && match_binary(fml.get(), d2, s1, s2) && SAME_OP(d1.get(), d2.get()) && s1.get() == term1 && s2.get() == term2; } if (!found) { UNREACHABLE(); return false; } } } return true; } UNREACHABLE(); return false; } case PR_QUANT_INTRO: { if (match_proof(p, p1) && match_fact(p, fact) && match_fact(p1.get(), fml) && (match_iff(fact.get(), t1, t2) || match_oeq(fact.get(), t1, t2)) && (match_iff(fml.get(), s1, s2) || match_oeq(fml.get(), s1, s2)) && m.is_oeq(fact.get()) == m.is_oeq(fml.get()) && is_quantifier(t1.get()) && is_quantifier(t2.get()) && to_quantifier(t1.get())->get_expr() == s1.get() && to_quantifier(t2.get())->get_expr() == s2.get() && to_quantifier(t1.get())->get_num_decls() == to_quantifier(t2.get())->get_num_decls() && to_quantifier(t1.get())->is_forall() == to_quantifier(t2.get())->is_forall()) { quantifier* q1 = to_quantifier(t1.get()); quantifier* q2 = to_quantifier(t2.get()); for (unsigned i = 0; i < q1->get_num_decls(); ++i) { if (q1->get_decl_sort(i) != q2->get_decl_sort(i)) { // term is not well-typed. UNREACHABLE(); return false; } } return true; } UNREACHABLE(); return false; } case PR_DISTRIBUTIVITY: { if (match_fact(p, fact) && match_proof(p) && match_equiv(fact.get(), t1, t2)) { side_conditions.push_back(fact.get()); return true; } UNREACHABLE(); return false; } case PR_AND_ELIM: { expr_ref_vector terms(m); if (match_proof(p, p1) && match_fact(p, fact) && match_fact(p1.get(), fml) && match_and(fml.get(), terms)) { for (unsigned i = 0; i < terms.size(); ++i) { if (terms[i].get() == fact.get()) { return true; } } } UNREACHABLE(); return false; } case PR_NOT_OR_ELIM: { expr_ref_vector terms(m); if (match_proof(p, p1) && match_fact(p, fact) && match_fact(p1.get(), fml) && match_not(fml.get(), fml1) && match_or(fml1.get(), terms)) { for (unsigned i = 0; i < terms.size(); ++i) { if (match_negated(terms[i].get(), fact.get())) { return true; } } } UNREACHABLE(); return false; } case PR_REWRITE: { if (match_fact(p, fact) && match_proof(p) && match_equiv(fact.get(), t1, t2)) { side_conditions.push_back(fact.get()); return true; } IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m);); return false; } case PR_REWRITE_STAR: { if (match_fact(p, fact) && match_equiv(fact.get(), t1, t2)) { expr_ref_vector rewrite_eq(m); rewrite_eq.push_back(fact.get()); for (unsigned i = 0; i < proofs.size(); ++i) { if (match_fact(proofs[i].get(), fml)) { rewrite_eq.push_back(m.mk_not(fml.get())); } } expr_ref rewrite_cond(m); rewrite_cond = m.mk_or(rewrite_eq.size(), rewrite_eq.c_ptr()); side_conditions.push_back(rewrite_cond.get()); return true; } IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m);); return false; } case PR_PULL_QUANT: { if (match_proof(p) && match_fact(p, fact) && match_iff(fact.get(), t1, t2) && is_quantifier(t2.get())) { // TBD: check the enchilada. return true; } IF_VERBOSE(0, verbose_stream() << "Expected proof of equivalence with a quantifier:\n" << mk_bounded_pp(p, m);); return false; } case PR_PULL_QUANT_STAR: { if (match_proof(p) && match_fact(p, fact) && match_iff(fact.get(), t1, t2)) { // TBD: check the enchilada. return true; } IF_VERBOSE(0, verbose_stream() << "Expected proof of equivalence:\n" << mk_bounded_pp(p, m);); return false; } case PR_PUSH_QUANT: { if (match_proof(p) && match_fact(p, fact) && match_iff(fact.get(), t1, t2) && is_quantifier(t1.get()) && match_and(to_quantifier(t1.get())->get_expr(), terms1) && match_and(t2.get(), terms2) && terms1.size() == terms2.size()) { quantifier * q1 = to_quantifier(t1.get()); for (unsigned i = 0; i < terms1.size(); ++i) { if (is_quantifier(terms2[i].get()) && to_quantifier(terms2[i].get())->get_expr() == terms1[i].get() && to_quantifier(terms2[i].get())->get_num_decls() == q1->get_num_decls()) { // ok. } else { return false; } } } UNREACHABLE(); return false; } case PR_ELIM_UNUSED_VARS: { if (match_proof(p) && match_fact(p, fact) && match_iff(fact.get(), t1, t2)) { // TBD: // match_quantifier(t1.get(), is_forall1, decls1, body1) // filter out decls1 that occur in body1. // if list is empty, then t2 could be just body1. // otherwise t2 is also a quantifier. return true; } IF_VERBOSE(0, verbose_stream() << "does not match last rule: " << mk_pp(p, m) << "\n";); return false; } case PR_DER: { bool is_forall = false; if (match_proof(p) && match_fact(p, fact) && match_iff(fact.get(), t1, t2) && match_quantifier(t1, is_forall, decls1, body1) && is_forall) { // TBD: check that terms are set of equalities. // t2 is an instance of a predicate in terms1 return true; } IF_VERBOSE(0, verbose_stream() << "does not match last rule: " << mk_pp(p, m) << "\n";); return false; } case PR_HYPOTHESIS: { // TBD all branches with hyptheses must be closed by a later lemma. if (match_proof(p) && match_fact(p, fml)) { return true; } return false; } case PR_LEMMA: { if (match_proof(p, p1) && match_fact(p, fact) && match_fact(p1.get(), fml) && m.is_false(fml.get())) { expr_ref_vector hypotheses(m); expr_ref_vector ors(m); get_hypotheses(p1.get(), hypotheses); if (hypotheses.size() == 1 && match_negated(hypotheses.get(0), fact)) { // Suppose fact is (or a b c) and hypothesis is (not (or a b c)) // That is, (or a b c) should be viewed as a 'quoted expression' and a unary clause, // instead of a clause with three literals. return true; } get_ors(fact.get(), ors); for (unsigned i = 0; i < hypotheses.size(); ++i) { bool found = false; unsigned j; for (j = 0; !found && j < ors.size(); ++j) { found = match_negated(ors[j].get(), hypotheses[i].get()); } if (!found) { TRACE("pr_lemma_bug", tout << "i: " << i << "\n"; tout << "ORs:\n"; for (unsigned i = 0; i < ors.size(); i++) { tout << mk_pp(ors.get(i), m) << "\n"; } tout << "HYPOTHESIS:\n"; for (unsigned i = 0; i < hypotheses.size(); i++) { tout << mk_pp(hypotheses.get(i), m) << "\n"; }); UNREACHABLE(); return false; } TRACE("proof_checker", tout << "Matched:\n"; ast_ll_pp(tout, m, hypotheses[i].get()); ast_ll_pp(tout, m, ors[j-1].get());); } return true; } UNREACHABLE(); return false; } case PR_UNIT_RESOLUTION: { if (match_fact(p, fact) && proofs.size() == 2 && match_fact(proofs[0].get(), fml1) && match_fact(proofs[1].get(), fml2) && match_negated(fml1.get(), fml2.get()) && m.is_false(fact.get())) { return true; } if (match_fact(p, fact) && proofs.size() > 1 && match_fact(proofs.get(0), fml) && match_or(fml.get(), terms1)) { for (unsigned i = 1; i < proofs.size(); ++i) { if (!match_fact(proofs.get(i), fml2)) { return false; } bool found = false; for (unsigned j = 0; !found && j < terms1.size(); ++j) { if (match_negated(terms1.get(j), fml2)) { found = true; if (j + 1 < terms1.size()) { terms1[j] = terms1.get(terms1.size()-1); } terms1.resize(terms1.size()-1); } } if (!found) { TRACE("pr_unit_bug", tout << "Parents:\n"; for (unsigned i = 0; i < proofs.size(); i++) { expr_ref p(m); match_fact(proofs.get(i), p); tout << mk_pp(p, m) << "\n"; } tout << "Fact:\n"; tout << mk_pp(fact, m) << "\n"; tout << "Clause:\n"; tout << mk_pp(fml, m) << "\n"; tout << "Could not find premise " << mk_pp(fml2, m) << "\n"; ); UNREACHABLE(); return false; } } switch(terms1.size()) { case 0: return m.is_false(fact.get()); case 1: return fact.get() == terms1[0].get(); default: { if (match_or(fact.get(), terms2)) { for (unsigned i = 0; i < terms1.size(); ++i) { bool found = false; expr* term1 = terms1[i].get(); for (unsigned j = 0; !found && j < terms2.size(); ++j) { found = term1 == terms2[j].get(); } if (!found) { IF_VERBOSE(0, verbose_stream() << "Premise not found:" << mk_pp(term1, m) << "\n";); return false; } } return true; } IF_VERBOSE(0, verbose_stream() << "Conclusion is not a disjunction:\n"; verbose_stream() << mk_pp(fml.get(), m) << "\n"; verbose_stream() << mk_pp(fact.get(), m) << "\n";); return false; } } } UNREACHABLE(); return false; } case PR_IFF_TRUE: { // iff_true(?rule(?p1, ?fml), (iff ?fml true)) if (match_proof(p, p1) && match_fact(p, fact) && match_fact(p1.get(), fml1) && match_iff(fact.get(), l1, r1) && fml1.get() == l1.get() && r1.get() == m.mk_true()) { return true; } UNREACHABLE(); return false; } case PR_IFF_FALSE: { // iff_false(?rule(?p1, (not ?fml)), (iff ?fml false)) if (match_proof(p, p1) && match_fact(p, fact) && match_fact(p1.get(), fml1) && match_iff(fact.get(), l1, r1) && match_not(fml1.get(), t1) && t1.get() == l1.get() && r1.get() == m.mk_false()) { return true; } UNREACHABLE(); return false; } case PR_COMMUTATIVITY: { // commutativity(= (?c ?t1 ?t2) (?c ?t2 ?t1)) if (match_fact(p, fact) && match_proof(p) && match_equiv(fact.get(), t1, t2) && match_binary(t1.get(), d1, s1, s2) && match_binary(t2.get(), d2, u1, u2) && s1.get() == u2.get() && s2.get() == u1.get() && d1.get() == d2.get() && d1->is_commutative()) { return true; } UNREACHABLE(); return false; } case PR_DEF_AXIOM: { // axiom(?fml) if (match_fact(p, fact) && match_proof(p) && m.is_bool(fact.get())) { return true; } UNREACHABLE(); return false; } case PR_DEF_INTRO: { // def_intro(?fml) // // ?fml: forall x . ~p(x) or e(x) and forall x . ~e(x) or p(x) // : forall x . ~cond(x) or f(x) = then(x) and forall x . cond(x) or f(x) = else(x) // : forall x . f(x) = e(x) // if (match_fact(p, fact) && match_proof(p) && m.is_bool(fact.get())) { return true; } UNREACHABLE(); return false; } case PR_APPLY_DEF: { if (match_fact(p, fact) && match_oeq(fact.get(), t1, t2)) { // TBD: must definitions be in proofs? return true; } UNREACHABLE(); return false; } case PR_IFF_OEQ: { // axiom(?rule(?p1,(iff ?t1 ?t2)), (~ ?t1 ?t2)) if (match_fact(p, fact) && match_proof(p, p1) && match_oeq(fact.get(), t1, t2) && match_fact(p1.get(), fml) && match_iff(fml.get(), s1, s2) && s1.get() == t1.get() && s2.get() == t2.get()) { return true; } UNREACHABLE(); return false; } case PR_NNF_POS: { // TBD: return true; } case PR_NNF_NEG: { // TBD: return true; } case PR_NNF_STAR: { // TBD: return true; } case PR_SKOLEMIZE: { // (exists ?x (p ?x y)) -> (p (sk y) y) // (not (forall ?x (p ?x y))) -> (not (p (sk y) y)) if (match_fact(p, fact) && match_oeq(fact.get(), t1, t2)) { quantifier* q = 0; expr* e = t1.get(); bool is_forall = false; if (match_not(t1.get(), s1)) { e = s1.get(); is_forall = true; } if (is_quantifier(e)) { q = to_quantifier(e); // TBD check that quantifier is properly instantiated return is_forall == q->is_forall(); } } UNREACHABLE(); return false; } case PR_CNF_STAR: { for (unsigned i = 0; i < proofs.size(); ++i) { if (match_op(proofs[i].get(), PR_DEF_INTRO, terms)) { // ok } else { UNREACHABLE(); return false; } } // coarse grain CNF conversion. return true; } case PR_MODUS_PONENS_OEQ: { if (match_fact(p, fact) && match_proof(p, p0, p1) && match_fact(p0.get(), fml0) && match_fact(p1.get(), fml1) && match_oeq(fml1.get(), t1, t2) && fml0.get() == t1.get() && fact.get() == t2.get()) { return true; } UNREACHABLE(); return false; } case PR_TH_LEMMA: { SASSERT(p->get_decl()->get_num_parameters() > 0); SASSERT(p->get_decl()->get_parameter(0).is_symbol()); if (symbol("arith") == p->get_decl()->get_parameter(0).get_symbol()) { return check_arith_proof(p); } dump_proof(p); return true; } case PR_QUANT_INST: { // TODO return true; } case PR_HYPER_RESOLVE: { proof_ref_vector premises(m); expr_ref_vector fmls(m); expr_ref conclusion(m), premise(m), premise0(m), premise1(m); svector > positions; vector substs; VERIFY(m.is_hyper_resolve(p, premises, conclusion, positions, substs)); var_subst vs(m, false); for (unsigned i = 0; i < premises.size(); ++i) { expr_ref_vector const& sub = substs[i]; premise = m.get_fact(premises[i].get()); if (!sub.empty()) { if (is_forall(premise)) { // SASSERT(to_quantifier(premise)->get_num_decls() == sub.size()); premise = to_quantifier(premise)->get_expr(); } vs(premise, sub.size(), sub.c_ptr(), premise); } fmls.push_back(premise.get()); TRACE("proof_checker", tout << mk_pp(premise.get(), m) << "\n"; for (unsigned j = 0; j < sub.size(); ++j) { tout << mk_pp(sub[j], m) << " "; } tout << "\n";); } premise0 = fmls[0].get(); for (unsigned i = 1; i < fmls.size(); ++i) { expr_ref lit1(m), lit2(m); expr* lit3 = 0; std::pair pos = positions[i-1]; premise1 = fmls[i].get(); set_false(premise0, pos.first, lit1); set_false(premise1, pos.second, lit2); if (m.is_not(lit1, lit3) && lit3 == lit2) { // ok } else if (m.is_not(lit2, lit3) && lit3 == lit1) { // ok } else { IF_VERBOSE(0, verbose_stream() << "Could not establish complementarity for:\n" << mk_pp(lit1, m) << "\n" << mk_pp(lit2, m) << "\n" << mk_pp(p, m) << "\n";); } fmls[i] = premise1; } fmls[0] = premise0; premise0 = m.mk_or(fmls.size(), fmls.c_ptr()); if (is_forall(conclusion)) { quantifier* q = to_quantifier(conclusion); premise0 = m.mk_iff(premise0, q->get_expr()); premise0 = m.mk_forall(q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), premise0); } else { premise0 = m.mk_iff(premise0, conclusion); } side_conditions.push_back(premise0); return true; } default: UNREACHABLE(); return false; } } /** \brief Premises of the rules are of the form (or l0 l1 l2 .. ln) or (=> (and ln+1 ln+2 .. ln+m) l0) or in the most general (ground) form: (=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln-1)) In other words we use the following (Prolog style) convention for Horn implications: The head of a Horn implication is position 0, the first conjunct in the body of an implication is position 1 the second conjunct in the body of an implication is position 2 Set the position provided in the argument to 'false'. */ void proof_checker::set_false(expr_ref& e, unsigned position, expr_ref& lit) { app* a = to_app(e); expr* head, *body; expr_ref_vector args(m); if (m.is_or(e)) { SASSERT(position < a->get_num_args()); args.append(a->get_num_args(), a->get_args()); lit = args[position].get(); args[position] = m.mk_false(); e = m.mk_or(args.size(), args.c_ptr()); } else if (m.is_implies(e, body, head)) { expr* const* heads = &head; unsigned num_heads = 1; if (m.is_or(head)) { num_heads = to_app(head)->get_num_args(); heads = to_app(head)->get_args(); } expr*const* bodies = &body; unsigned num_bodies = 1; if (m.is_and(body)) { num_bodies = to_app(body)->get_num_args(); bodies = to_app(body)->get_args(); } if (position < num_heads) { args.append(num_heads, heads); lit = args[position].get(); args[position] = m.mk_false(); e = m.mk_implies(body, m.mk_or(args.size(), args.c_ptr())); } else { position -= num_heads; args.append(num_bodies, bodies); lit = m.mk_not(args[position].get()); args[position] = m.mk_true(); e = m.mk_implies(m.mk_and(args.size(), args.c_ptr()), head); } } else if (position == 0) { lit = e; e = m.mk_false(); } else { IF_VERBOSE(0, verbose_stream() << position << "\n" << mk_pp(e, m) << "\n";); UNREACHABLE(); } } bool proof_checker::match_fact(proof* p, expr_ref& fact) { if (m.is_proof(p) && m.has_fact(p)) { fact = m.get_fact(p); return true; } return false; } void proof_checker::add_premise(proof* p) { if (!m_marked.is_marked(p)) { m_marked.mark(p, true); m_todo.push_back(p); } } bool proof_checker::match_proof(proof* p) { return m.is_proof(p) && m.get_num_parents(p) == 0; } bool proof_checker::match_proof(proof* p, proof_ref& p0) { if (m.is_proof(p) && m.get_num_parents(p) == 1) { p0 = m.get_parent(p, 0); return true; } return false; } bool proof_checker::match_proof(proof* p, proof_ref& p0, proof_ref& p1) { if (m.is_proof(p) && m.get_num_parents(p) == 2) { p0 = m.get_parent(p, 0); p1 = m.get_parent(p, 1); return true; } return false; } bool proof_checker::match_proof(proof* p, proof_ref_vector& parents) { if (m.is_proof(p)) { for (unsigned i = 0; i < m.get_num_parents(p); ++i) { parents.push_back(m.get_parent(p, i)); } return true; } return false; } bool proof_checker::match_binary(expr* e, func_decl_ref& d, expr_ref& t1, expr_ref& t2) { if (e->get_kind() == AST_APP && to_app(e)->get_num_args() == 2) { d = to_app(e)->get_decl(); t1 = to_app(e)->get_arg(0); t2 = to_app(e)->get_arg(1); return true; } return false; } bool proof_checker::match_app(expr* e, func_decl_ref& d, expr_ref_vector& terms) { if (e->get_kind() == AST_APP) { d = to_app(e)->get_decl(); for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { terms.push_back(to_app(e)->get_arg(i)); } return true; } return false; } bool proof_checker::match_quantifier(expr* e, bool& is_univ, sort_ref_vector& sorts, expr_ref& body) { if (is_quantifier(e)) { quantifier* q = to_quantifier(e); is_univ = q->is_forall(); body = q->get_expr(); for (unsigned i = 0; i < q->get_num_decls(); ++i) { sorts.push_back(q->get_decl_sort(i)); } return true; } return false; } bool proof_checker::match_op(expr* e, decl_kind k, expr_ref& t1, expr_ref& t2) { if (e->get_kind() == AST_APP && to_app(e)->get_family_id() == m.get_basic_family_id() && to_app(e)->get_decl_kind() == k && to_app(e)->get_num_args() == 2) { t1 = to_app(e)->get_arg(0); t2 = to_app(e)->get_arg(1); return true; } return false; } bool proof_checker::match_op(expr* e, decl_kind k, expr_ref_vector& terms) { if (e->get_kind() == AST_APP && to_app(e)->get_family_id() == m.get_basic_family_id() && to_app(e)->get_decl_kind() == k) { for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { terms.push_back(to_app(e)->get_arg(i)); } return true; } return false; } bool proof_checker::match_op(expr* e, decl_kind k, expr_ref& t) { if (e->get_kind() == AST_APP && to_app(e)->get_family_id() == m.get_basic_family_id() && to_app(e)->get_decl_kind() == k && to_app(e)->get_num_args() == 1) { t = to_app(e)->get_arg(0); return true; } return false; } bool proof_checker::match_not(expr* e, expr_ref& t) { return match_op(e, OP_NOT, t); } bool proof_checker::match_or(expr* e, expr_ref_vector& terms) { return match_op(e, OP_OR, terms); } bool proof_checker::match_and(expr* e, expr_ref_vector& terms) { return match_op(e, OP_AND, terms); } bool proof_checker::match_iff(expr* e, expr_ref& t1, expr_ref& t2) { return match_op(e, OP_IFF, t1, t2); } bool proof_checker::match_equiv(expr* e, expr_ref& t1, expr_ref& t2) { return match_oeq(e, t1, t2) || match_eq(e, t1, t2); } bool proof_checker::match_implies(expr* e, expr_ref& t1, expr_ref& t2) { return match_op(e, OP_IMPLIES, t1, t2); } bool proof_checker::match_eq(expr* e, expr_ref& t1, expr_ref& t2) { return match_op(e, OP_EQ, t1, t2) || match_iff(e, t1, t2); } bool proof_checker::match_oeq(expr* e, expr_ref& t1, expr_ref& t2) { return match_op(e, OP_OEQ, t1, t2); } bool proof_checker::match_negated(expr* a, expr* b) { expr_ref t(m); return (match_not(a, t) && t.get() == b) || (match_not(b, t) && t.get() == a); } void proof_checker::get_ors(expr* e, expr_ref_vector& ors) { ptr_buffer buffer; if (m.is_or(e)) { app* a = to_app(e); ors.append(a->get_num_args(), a->get_args()); } else { ors.push_back(e); } } void proof_checker::get_hypotheses(proof* p, expr_ref_vector& ante) { ptr_vector stack; expr* h = 0; expr_ref hyp(m); stack.push_back(p); while (!stack.empty()) { p = stack.back(); SASSERT(m.is_proof(p)); if (m_hypotheses.contains(p)) { stack.pop_back(); continue; } if (is_hypothesis(p) && match_fact(p, hyp)) { hyp = mk_atom(hyp.get()); m_pinned.push_back(hyp.get()); m_hypotheses.insert(p, hyp.get()); stack.pop_back(); continue; } // in this system all hypotheses get bound by lemmas. if (m.is_lemma(p)) { m_hypotheses.insert(p, mk_nil()); stack.pop_back(); continue; } bool all_found = true; ptr_vector hyps; for (unsigned i = 0; i < m.get_num_parents(p); ++i) { proof* p_i = m.get_parent(p, i); if (m_hypotheses.find(p_i, h)) { hyps.push_back(h); } else { stack.push_back(p_i); all_found = false; } } if (all_found) { h = mk_hyp(hyps.size(), hyps.c_ptr()); m_pinned.push_back(h); m_hypotheses.insert(p, h); stack.pop_back(); } } // // dis-assemble the set of obtained hypotheses. // if (!m_hypotheses.find(p, h)) { UNREACHABLE(); } ptr_buffer hyps; ptr_buffer todo; expr_mark mark; todo.push_back(h); expr_ref a(m), b(m); while (!todo.empty()) { h = todo.back(); todo.pop_back(); if (mark.is_marked(h)) { continue; } mark.mark(h, true); if (match_cons(h, a, b)) { todo.push_back(a.get()); todo.push_back(b.get()); } else if (match_atom(h, a)) { ante.push_back(a.get()); } else { SASSERT(match_nil(h)); } } TRACE("proof_checker", { ast_ll_pp(tout << "Getting hypotheses from: ", m, p); tout << "Found hypotheses:\n"; for (unsigned i = 0; i < ante.size(); ++i) { ast_ll_pp(tout, m, ante[i].get()); } }); } bool proof_checker::match_nil(expr* e) const { return is_app(e) && to_app(e)->get_family_id() == m_hyp_fid && to_app(e)->get_decl_kind() == OP_NIL; } bool proof_checker::match_cons(expr* e, expr_ref& a, expr_ref& b) const { if (is_app(e) && to_app(e)->get_family_id() == m_hyp_fid && to_app(e)->get_decl_kind() == OP_CONS) { a = to_app(e)->get_arg(0); b = to_app(e)->get_arg(1); return true; } return false; } bool proof_checker::match_atom(expr* e, expr_ref& a) const { if (is_app(e) && to_app(e)->get_family_id() == m_hyp_fid && to_app(e)->get_decl_kind() == OP_ATOM) { a = to_app(e)->get_arg(0); return true; } return false; } expr* proof_checker::mk_atom(expr* e) { return m.mk_app(m_hyp_fid, OP_ATOM, e); } expr* proof_checker::mk_cons(expr* a, expr* b) { return m.mk_app(m_hyp_fid, OP_CONS, a, b); } expr* proof_checker::mk_nil() { return m_nil.get(); } bool proof_checker::is_hypothesis(proof* p) const { return m.is_proof(p) && p->get_decl_kind() == PR_HYPOTHESIS; } expr* proof_checker::mk_hyp(unsigned num_hyps, expr * const * hyps) { expr* result = 0; for (unsigned i = 0; i < num_hyps; ++i) { if (!match_nil(hyps[i])) { if (result) { result = mk_cons(result, hyps[i]); } else { result = hyps[i]; } } } if (result == 0) { return mk_nil(); } else { return result; } } void proof_checker::dump_proof(proof * pr) { if (!m_dump_lemmas) return; SASSERT(m.has_fact(pr)); expr * consequent = m.get_fact(pr); unsigned num = m.get_num_parents(pr); ptr_buffer antecedents; for (unsigned i = 0; i < num; i++) { proof * a = m.get_parent(pr, i); SASSERT(m.has_fact(a)); antecedents.push_back(m.get_fact(a)); } dump_proof(antecedents.size(), antecedents.c_ptr(), consequent); } void proof_checker::dump_proof(unsigned num_antecedents, expr * const * antecedents, expr * consequent) { char buffer[128]; #ifdef _WINDOWS sprintf_s(buffer, ARRAYSIZE(buffer), "proof_lemma_%d.smt", m_proof_lemma_id); #else sprintf(buffer, "proof_lemma_%d.smt", m_proof_lemma_id); #endif std::ofstream out(buffer); ast_smt_pp pp(m); pp.set_benchmark_name("lemma"); pp.set_status("unsat"); pp.set_logic(m_logic.c_str()); for (unsigned i = 0; i < num_antecedents; i++) pp.add_assumption(antecedents[i]); expr_ref n(m); n = m.mk_not(consequent); pp.display(out, n); out.close(); m_proof_lemma_id++; } bool proof_checker::check_arith_literal(bool is_pos, app* lit0, rational const& coeff, expr_ref& sum, bool& is_strict) { arith_util a(m); app* lit = lit0; if (m.is_not(lit)) { lit = to_app(lit->get_arg(0)); is_pos = !is_pos; } if (!a.is_le(lit) && !a.is_lt(lit) && !a.is_ge(lit) && !a.is_gt(lit) && !m.is_eq(lit)) { IF_VERBOSE(0, verbose_stream() << mk_pp(lit, m) << "\n";); return false; } SASSERT(lit->get_num_args() == 2); sort* s = m.get_sort(lit->get_arg(0)); bool is_int = a.is_int(s); if (!is_int && is_pos && (a.is_gt(lit) || a.is_lt(lit))) { is_strict = true; } if (!is_int && !is_pos && (a.is_ge(lit) || a.is_le(lit))) { is_strict = true; } SASSERT(a.is_int(s) || a.is_real(s)); expr_ref sign1(m), sign2(m), term(m); sign1 = a.mk_numeral(m.is_eq(lit)?coeff:abs(coeff), s); sign2 = a.mk_numeral(m.is_eq(lit)?-coeff:-abs(coeff), s); if (!sum.get()) { sum = a.mk_numeral(rational(0), s); } expr* a0 = lit->get_arg(0); expr* a1 = lit->get_arg(1); if (is_pos && (a.is_ge(lit) || a.is_gt(lit))) { std::swap(a0, a1); } if (!is_pos && (a.is_le(lit) || a.is_lt(lit))) { std::swap(a0, a1); } // // Multiplying by coefficients over strict // and non-strict inequalities: // // (a <= b) * 2 // (a - b <= 0) * 2 // (2a - 2b <= 0) // (a < b) * 2 <=> // (a +1 <= b) * 2 <=> // 2a + 2 <= 2b <=> // 2a+2-2b <= 0 bool strict_ineq = is_pos?(a.is_gt(lit) || a.is_lt(lit)):(a.is_ge(lit) || a.is_le(lit)); if (is_int && strict_ineq) { sum = a.mk_add(sum, sign1); } term = a.mk_mul(sign1, a0); sum = a.mk_add(sum, term); term = a.mk_mul(sign2, a1); sum = a.mk_add(sum, term); #if 1 { th_rewriter rw(m); rw(sum); } IF_VERBOSE(0, verbose_stream() << coeff << "\n" << mk_pp(lit0, m) << "\n" << mk_pp(sum, m) << "\n";); #endif return true; } bool proof_checker::check_arith_proof(proof* p) { func_decl* d = p->get_decl(); SASSERT(PR_TH_LEMMA == p->get_decl_kind()); SASSERT(d->get_parameter(0).get_symbol() == "arith"); unsigned num_params = d->get_num_parameters(); arith_util autil(m); SASSERT(num_params > 0); if (num_params == 1) { dump_proof(p); return true; } expr_ref fact(m); proof_ref_vector proofs(m); if (!match_fact(p, fact)) { UNREACHABLE(); return false; } if (d->get_parameter(1).get_symbol() != symbol("farkas")) { dump_proof(p); return true; } expr_ref sum(m); bool is_strict = false; unsigned offset = 0; vector coeffs; rational lc(1); for (unsigned i = 2; i < d->get_num_parameters(); ++i) { parameter const& p = d->get_parameter(i); if (!p.is_rational()) { UNREACHABLE(); return false; } coeffs.push_back(p.get_rational()); lc = lcm(lc, denominator(coeffs.back())); } if (!lc.is_one()) { for (unsigned i = 0; i < coeffs.size(); ++i) { coeffs[i] = lc*coeffs[i]; } } unsigned num_parents = m.get_num_parents(p); for (unsigned i = 0; i < num_parents; i++) { proof * a = m.get_parent(p, i); SASSERT(m.has_fact(a)); if (!check_arith_literal(true, to_app(m.get_fact(a)), coeffs[offset++], sum, is_strict)) { return false; } } if (m.is_or(fact)) { app* disj = to_app(fact); unsigned num_args = disj->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { app* lit = to_app(disj->get_arg(i)); if (!check_arith_literal(false, lit, coeffs[offset++], sum, is_strict)) { return false; } } } else if (!m.is_false(fact)) { if (!check_arith_literal(false, to_app(fact), coeffs[offset++], sum, is_strict)) { return false; } } if (!sum.get()) { return false; } sort* s = m.get_sort(sum); if (is_strict) { sum = autil.mk_lt(sum, autil.mk_numeral(rational(0), s)); } else { sum = autil.mk_le(sum, autil.mk_numeral(rational(0), s)); } th_rewriter rw(m); rw(sum); if (!m.is_false(sum)) { IF_VERBOSE(0, verbose_stream() << "Arithmetic proof check failed: " << mk_pp(sum, m) << "\n";); m_dump_lemmas = true; dump_proof(p); return false; } return true; } z3-4.4.0/src/ast/proof_checker/proof_checker.h0000644000175000017500000001036112540347414021017 0ustar michaelmichael/*++ Copyright (c) 2008 Microsoft Corporation Module Name: proof_checker.h Abstract: Proof checker. Author: Nikolaj Bjorner (nbjorner) 2008-03-07. Revision History: --*/ #ifndef _PROOF_CHECKER_H_ #define _PROOF_CHECKER_H_ #include "ast.h" #include "map.h" class proof_checker { ast_manager& m; proof_ref_vector m_todo; expr_mark m_marked; expr_ref_vector m_pinned; obj_map m_hypotheses; family_id m_hyp_fid; // family_id m_spc_fid; app_ref m_nil; bool m_dump_lemmas; std::string m_logic; unsigned m_proof_lemma_id; enum hyp_decl_kind { OP_CONS, OP_ATOM, OP_NIL }; enum hyp_sort_kind { CELL_SORT }; class hyp_decl_plugin : public decl_plugin { protected: func_decl* m_cons; func_decl* m_atom; func_decl* m_nil; sort* m_cell; virtual void set_manager(ast_manager * m, family_id id); func_decl * mk_func_decl(decl_kind k); public: hyp_decl_plugin(); virtual ~hyp_decl_plugin() {} virtual void finalize(); virtual decl_plugin * mk_fresh() { return alloc(hyp_decl_plugin); } virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters); virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range); virtual void get_op_names(svector & op_names, symbol const & logic); virtual void get_sort_names(svector & sort_names, symbol const & logic); }; public: proof_checker(ast_manager& m); void set_dump_lemmas(char const * logic = "AUFLIA") { m_dump_lemmas = true; m_logic = logic; } bool check(proof* p, expr_ref_vector& side_conditions); private: bool check1(proof* p, expr_ref_vector& side_conditions); bool check1_basic(proof* p, expr_ref_vector& side_conditions); bool check1_spc(proof* p, expr_ref_vector& side_conditions); bool check_arith_proof(proof* p); bool check_arith_literal(bool is_pos, app* lit, rational const& coeff, expr_ref& sum, bool& is_strict); bool match_fact(proof* p, expr_ref& fact); void add_premise(proof* p); bool match_proof(proof* p); bool match_proof(proof* p, proof_ref& p0); bool match_proof(proof* p, proof_ref& p0, proof_ref& p1); bool match_proof(proof* p, proof_ref_vector& parents); bool match_binary(expr* e, func_decl_ref& d, expr_ref& t1, expr_ref& t2); bool match_op(expr* e, decl_kind k, expr_ref& t1, expr_ref& t2); bool match_op(expr* e, decl_kind k, expr_ref& t); bool match_op(expr* e, decl_kind k, expr_ref_vector& terms); bool match_iff(expr* e, expr_ref& t1, expr_ref& t2); bool match_implies(expr* e, expr_ref& t1, expr_ref& t2); bool match_eq(expr* e, expr_ref& t1, expr_ref& t2); bool match_oeq(expr* e, expr_ref& t1, expr_ref& t2); bool match_not(expr* e, expr_ref& t); bool match_or(expr* e, expr_ref_vector& terms); bool match_and(expr* e, expr_ref_vector& terms); bool match_app(expr* e, func_decl_ref& d, expr_ref_vector& terms); bool match_quantifier(expr*, bool& is_univ, sort_ref_vector&, expr_ref& body); bool match_negated(expr* a, expr* b); bool match_equiv(expr* a, expr_ref& t1, expr_ref& t2); void get_ors(expr* e, expr_ref_vector& ors); void get_hypotheses(proof* p, expr_ref_vector& ante); bool match_nil(expr* e) const; bool match_cons(expr* e, expr_ref& a, expr_ref& b) const; bool match_atom(expr* e, expr_ref& a) const; expr* mk_nil(); expr* mk_cons(expr* a, expr* b); expr* mk_atom(expr* e); bool is_hypothesis(proof* p) const; expr* mk_hyp(unsigned num_hyps, expr * const * hyps); void dump_proof(proof * pr); void dump_proof(unsigned num_antecedents, expr * const * antecedents, expr * consequent); void set_false(expr_ref& e, unsigned idx, expr_ref& lit); }; #endif z3-4.4.0/src/ast/ast.h0000644000175000017500000030001212540347414014157 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ast.h Abstract: Expression DAG Author: Leonardo de Moura (leonardo) 2006-09-18. Revision History: --*/ #ifndef _AST_H_ #define _AST_H_ #include"vector.h" #include"hashtable.h" #include"buffer.h" #include"symbol.h" #include"rational.h" #include"hash.h" #include"optional.h" #include"trace.h" #include"bit_vector.h" #include"symbol_table.h" #include"tptr.h" #include"memory_manager.h" #include"small_object_allocator.h" #include"obj_ref.h" #include"ref_vector.h" #include"ref_buffer.h" #include"obj_mark.h" #include"obj_hashtable.h" #include"id_gen.h" #include"map.h" #include"parray.h" #include"dictionary.h" #include"chashtable.h" #include"z3_exception.h" #include"dependency.h" #define RECYCLE_FREE_AST_INDICES #ifdef _MSC_VER #pragma warning(disable : 4200) #pragma warning(disable : 4355) #endif class ast; class ast_manager; /** \brief Generic exception for AST related errors. We used to use fatal_error_msg to report errors inside plugins. */ class ast_exception : public default_exception { public: ast_exception(char const * msg):default_exception(msg) {} }; typedef int family_id; const family_id null_family_id = -1; // ----------------------------------- // // parameter // // ----------------------------------- /** \brief Interpreted function declarations and sorts may have parameters that are used to encode extra information associated with them. */ class parameter { public: enum kind_t { PARAM_INT, PARAM_AST, PARAM_SYMBOL, PARAM_RATIONAL, PARAM_DOUBLE, // PARAM_EXTERNAL is used for handling decl_plugin specific parameters. // For example, it is used for handling mpf numbers in float_decl_plugin, // and irrational algebraic numbers in arith_decl_plugin. // PARAM_EXTERNAL is not supported by z3 low level input format. This format is legacy, so // this is not a big problem. // Remark: PARAM_EXTERNAL can only be used to decorate theory decls. PARAM_EXTERNAL }; private: kind_t m_kind; // It is not possible to use tag pointers, since symbols are already tagged. union { int m_int; // for PARAM_INT ast* m_ast; // for PARAM_AST char m_symbol[sizeof(symbol)]; // for PARAM_SYMBOL char m_rational[sizeof(rational)]; // for PARAM_RATIONAL double m_dval; // for PARAM_DOUBLE (remark: this is not used in float_decl_plugin) unsigned m_ext_id; // for PARAM_EXTERNAL }; public: parameter(): m_kind(PARAM_INT), m_int(0) {} explicit parameter(int val): m_kind(PARAM_INT), m_int(val) {} explicit parameter(unsigned val): m_kind(PARAM_INT), m_int(val) {} explicit parameter(ast * p): m_kind(PARAM_AST), m_ast(p) {} explicit parameter(symbol const & s): m_kind(PARAM_SYMBOL) { new (m_symbol) symbol(s); } explicit parameter(rational const & r): m_kind(PARAM_RATIONAL) { new (m_rational) rational(r); } explicit parameter(double d):m_kind(PARAM_DOUBLE), m_dval(d) {} explicit parameter(unsigned ext_id, bool):m_kind(PARAM_EXTERNAL), m_ext_id(ext_id) {} parameter(parameter const&); ~parameter(); parameter& operator=(parameter const& other); kind_t get_kind() const { return m_kind; } bool is_int() const { return m_kind == PARAM_INT; } bool is_ast() const { return m_kind == PARAM_AST; } bool is_symbol() const { return m_kind == PARAM_SYMBOL; } bool is_rational() const { return m_kind == PARAM_RATIONAL; } bool is_double() const { return m_kind == PARAM_DOUBLE; } bool is_external() const { return m_kind == PARAM_EXTERNAL; } bool is_int(int & i) const { return is_int() && (i = get_int(), true); } bool is_ast(ast * & a) const { return is_ast() && (a = get_ast(), true); } bool is_symbol(symbol & s) const { return is_symbol() && (s = get_symbol(), true); } bool is_rational(rational & r) const { return is_rational() && (r = get_rational(), true); } bool is_double(double & d) const { return is_double() && (d = get_double(), true); } bool is_external(unsigned & id) const { return is_external() && (id = get_ext_id(), true); } /** \brief This method is invoked when the parameter is attached to a function declaration or sort. */ void init_eh(ast_manager & m); /** \brief This method is invoked before the function declaration or sort associated with the parameter is deleted. */ void del_eh(ast_manager & m, family_id fid); int get_int() const { SASSERT(is_int()); return m_int; } ast * get_ast() const { SASSERT(is_ast()); return m_ast; } symbol const & get_symbol() const { SASSERT(is_symbol()); return *(reinterpret_cast(m_symbol)); } rational const & get_rational() const { SASSERT(is_rational()); return *(reinterpret_cast(m_rational)); } double get_double() const { SASSERT(is_double()); return m_dval; } unsigned get_ext_id() const { SASSERT(is_external()); return m_ext_id; } bool operator==(parameter const & p) const; bool operator!=(parameter const & p) const { return !operator==(p); } unsigned hash() const; std::ostream& display(std::ostream& out) const; }; inline std::ostream& operator<<(std::ostream& out, parameter const & p) { return p.display(out); } void display_parameters(std::ostream & out, unsigned n, parameter const * p); // ----------------------------------- // // family_manager // // ----------------------------------- /** \brief Interpreted functions and sorts are grouped in families. Each family has an unique ID. This class models the mapping between symbols (family names) and the unique IDs. */ class family_manager { family_id m_next_id; symbol_table m_families; svector m_names; public: family_manager():m_next_id(0) {} /** \brief Return the family_id for s, a new id is created if !has_family(s) If has_family(s), then this method is equivalent to get_family_id(s) */ family_id mk_family_id(symbol const & s); /** \brief Return the family_id for s, return null_family_id if s was not registered in the manager. */ family_id get_family_id(symbol const & s) const; bool has_family(symbol const & s) const; void get_dom(svector& dom) const { m_families.get_dom(dom); } void get_range(svector & range) const { m_families.get_range(range); } symbol const & get_name(family_id fid) const { return fid >= 0 && fid < static_cast(m_names.size()) ? m_names[fid] : symbol::null; } bool has_family(family_id fid) const { return fid >= 0 && fid < static_cast(m_names.size()); } }; // ----------------------------------- // // decl_info // // ----------------------------------- /** \brief Each interpreted function declaration or sort has a kind. Kinds are used to identify interpreted functions and sorts in a family. */ typedef int decl_kind; const decl_kind null_decl_kind = -1; /** \brief Interpreted function declarations and sorts are associated with a family id, kind, and parameters. */ class decl_info { family_id m_family_id; decl_kind m_kind; vector m_parameters; public: bool m_private_parameters; decl_info(family_id family_id = null_family_id, decl_kind k = null_decl_kind, unsigned num_parameters = 0, parameter const * parameters = 0, bool private_params = false); decl_info(decl_info const& other); ~decl_info() {} void init_eh(ast_manager & m); void del_eh(ast_manager & m); family_id get_family_id() const { return m_family_id; } decl_kind get_decl_kind() const { return m_kind; } unsigned get_num_parameters() const { return m_parameters.size(); } parameter const & get_parameter(unsigned idx) const { return m_parameters[idx]; } parameter const * get_parameters() const { return m_parameters.begin(); } bool private_parameters() const { return m_private_parameters; } unsigned hash() const; bool operator==(decl_info const & info) const; }; std::ostream & operator<<(std::ostream & out, decl_info const & info); // ----------------------------------- // // sort_size // // ----------------------------------- /** \brief Models the number of elements of a sort. */ class sort_size { enum kind_t { SS_FINITE, // For some sorts it may be too expensive to compute the // number of elements precisely (e.g., arrays). In this // cases, we mark the sort as too big. That is, the number // of elements is at least bigger than 2^64. SS_FINITE_VERY_BIG, SS_INFINITE } m_kind; uint64 m_size; // It is only meaningful if m_kind == SS_FINITE sort_size(kind_t k, uint64 r):m_kind(k), m_size(r) {} public: sort_size():m_kind(SS_INFINITE) {} sort_size(uint64 const & sz):m_kind(SS_FINITE), m_size(sz) {} sort_size(sort_size const& other): m_kind(other.m_kind), m_size(other.m_size) {} explicit sort_size(rational const& r) { if (r.is_uint64()) { m_kind = SS_FINITE; m_size = r.get_uint64(); } else { m_kind = SS_FINITE_VERY_BIG; m_size = 0; } } static sort_size mk_infinite() { return sort_size(SS_INFINITE, 0); } static sort_size mk_very_big() { return sort_size(SS_FINITE_VERY_BIG, 0); } static sort_size mk_finite(uint64 r) { return sort_size(SS_FINITE, r); } bool is_infinite() const { return m_kind == SS_INFINITE; } bool is_very_big() const { return m_kind == SS_FINITE_VERY_BIG; } bool is_finite() const { return m_kind == SS_FINITE; } static bool is_very_big_base2(unsigned power) { return power >= 64; } uint64 size() const { SASSERT(is_finite()); return m_size; } }; std::ostream& operator<<(std::ostream& out, sort_size const & ss); // ----------------------------------- // // sort_info // // ----------------------------------- /** \brief Extra information that may be attached to intepreted sorts. */ class sort_info : public decl_info { sort_size m_num_elements; public: sort_info(family_id family_id = null_family_id, decl_kind k = null_decl_kind, unsigned num_parameters = 0, parameter const * parameters = 0, bool private_parameters = false): decl_info(family_id, k, num_parameters, parameters, private_parameters) { } sort_info(family_id family_id, decl_kind k, uint64 num_elements, unsigned num_parameters = 0, parameter const * parameters = 0, bool private_parameters = false): decl_info(family_id, k, num_parameters, parameters, private_parameters), m_num_elements(num_elements) { } sort_info(family_id family_id, decl_kind k, sort_size const& num_elements, unsigned num_parameters = 0, parameter const * parameters = 0, bool private_parameters = false): decl_info(family_id, k, num_parameters, parameters, private_parameters), m_num_elements(num_elements) { } sort_info(sort_info const& other) : decl_info(other), m_num_elements(other.m_num_elements) { } ~sort_info() {} bool is_infinite() const { return m_num_elements.is_infinite(); } bool is_very_big() const { return m_num_elements.is_very_big(); } sort_size const & get_num_elements() const { return m_num_elements; } }; std::ostream & operator<<(std::ostream & out, sort_info const & info); // ----------------------------------- // // func_decl_info // // ----------------------------------- /** \brief Extra information that may be attached to interpreted function decls. */ struct func_decl_info : public decl_info { bool m_left_assoc:1; bool m_right_assoc:1; bool m_flat_associative:1; bool m_commutative:1; bool m_chainable:1; bool m_pairwise:1; bool m_injective:1; bool m_idempotent:1; bool m_skolem:1; func_decl_info(family_id family_id = null_family_id, decl_kind k = null_decl_kind, unsigned num_parameters = 0, parameter const * parameters = 0); ~func_decl_info() {} bool is_associative() const { return m_left_assoc && m_right_assoc; } bool is_left_associative() const { return m_left_assoc; } bool is_right_associative() const { return m_right_assoc; } bool is_flat_associative() const { return m_flat_associative; } bool is_commutative() const { return m_commutative; } bool is_chainable() const { return m_chainable; } bool is_pairwise() const { return m_pairwise; } bool is_injective() const { return m_injective; } bool is_idempotent() const { return m_idempotent; } bool is_skolem() const { return m_skolem; } void set_associative(bool flag = true) { m_left_assoc = flag; m_right_assoc = flag; } void set_left_associative(bool flag = true) { m_left_assoc = flag; } void set_right_associative(bool flag = true) { m_right_assoc = flag; } void set_flat_associative(bool flag = true) { m_flat_associative = flag; } void set_commutative(bool flag = true) { m_commutative = flag; } void set_chainable(bool flag = true) { m_chainable = flag; } void set_pairwise(bool flag = true) { m_pairwise = flag; } void set_injective(bool flag = true) { m_injective = flag; } void set_idempotent(bool flag = true) { m_idempotent = flag; } void set_skolem(bool flag = true) { m_skolem = flag; } bool operator==(func_decl_info const & info) const; // Return true if the func_decl_info is equivalent to the null one (i.e., it does not contain any useful info). bool is_null() const { return get_family_id() == null_family_id && !is_left_associative() && !is_right_associative() && !is_commutative() && !is_chainable() && !is_pairwise() && !is_injective() && !is_idempotent() && !is_skolem(); } }; std::ostream & operator<<(std::ostream & out, func_decl_info const & info); // ----------------------------------- // // ast // // ----------------------------------- typedef enum { AST_APP, AST_VAR, AST_QUANTIFIER, AST_SORT, AST_FUNC_DECL } ast_kind; char const * get_ast_kind_name(ast_kind k); class shared_occs_mark; class ast { protected: friend class ast_manager; unsigned m_id; unsigned m_kind:16; // Warning: the marks should be used carefully, since they are shared. unsigned m_mark1:1; unsigned m_mark2:1; // Private mark used by shared_occs functor // Motivation for this field: // - A mark cannot be used by more than one owner. // So, it is only safe to use mark by "self-contained" code. // They should be viewed as temporary information. // - The functor shared_occs is used by some AST pretty printers. // - So, a code that uses marks could not use the pretty printer if // shared_occs used one of the public marks. // - This was a constant source of assertion violations. unsigned m_mark_shared_occs:1; friend class shared_occs_mark; void mark_so(bool flag) { m_mark_shared_occs = flag; } void reset_mark_so() { m_mark_shared_occs = false; } bool is_marked_so() const { return m_mark_shared_occs; } unsigned m_ref_count; unsigned m_hash; #ifdef Z3DEBUG // In debug mode, we store who is the owner of the mark. void * m_mark1_owner; void * m_mark2_owner; #endif void inc_ref() { SASSERT(m_ref_count < UINT_MAX); m_ref_count ++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count --; } ast(ast_kind k):m_id(UINT_MAX), m_kind(k), m_mark1(false), m_mark2(false), m_mark_shared_occs(false), m_ref_count(0) { DEBUG_CODE({ m_mark1_owner = 0; m_mark2_owner = 0; }); } public: unsigned get_id() const { return m_id; } unsigned get_ref_count() const { return m_ref_count; } ast_kind get_kind() const { return static_cast(m_kind); } unsigned hash() const { return m_hash; } #ifdef Z3DEBUG void mark1(bool flag, void * owner) { SASSERT(m_mark1_owner == 0 || m_mark1_owner == owner); m_mark1 = flag; m_mark1_owner = owner; } void mark2(bool flag, void * owner) { SASSERT(m_mark2_owner == 0 || m_mark2_owner == owner); m_mark2 = flag; m_mark2_owner = owner; } void reset_mark1(void * owner) { SASSERT(m_mark1_owner == 0 || m_mark1_owner == owner); m_mark1 = false; m_mark1_owner = 0; } void reset_mark2(void * owner) { SASSERT(m_mark2_owner == 0 || m_mark2_owner == owner); m_mark2 = false; m_mark2_owner = 0; } bool is_marked1(void * owner) const { SASSERT(m_mark1_owner == 0 || m_mark1_owner == owner); return m_mark1; } bool is_marked2(void * owner) const { SASSERT(m_mark2_owner == 0 || m_mark2_owner == owner); return m_mark2; } #define AST_MARK1(A,F,O) A->mark1(F, O) #define AST_MARK2(A,F,O) A->mark2(F, O) #define AST_RESET_MARK1(A,O) A->reset_mark1(O) #define AST_RESET_MARK2(A,O) A->reset_mark2(O) #define AST_IS_MARKED1(A,O) A->is_marked1(O) #define AST_IS_MARKED2(A,O) A->is_marked2(O) #else void mark1(bool flag) { m_mark1 = flag; } void mark2(bool flag) { m_mark2 = flag; } void reset_mark1() { m_mark1 = false; } void reset_mark2() { m_mark2 = false; } bool is_marked1() const { return m_mark1; } bool is_marked2() const { return m_mark2; } #define AST_MARK1(A,F,O) A->mark1(F) #define AST_MARK2(A,F,O) A->mark2(F) #define AST_RESET_MARK1(A,O) A->reset_mark1() #define AST_RESET_MARK2(A,O) A->reset_mark2() #define AST_IS_MARKED1(A,O) A->is_marked1() #define AST_IS_MARKED2(A,O) A->is_marked2() #endif }; #define MATCH_TERNARY(_MATCHER_) \ bool _MATCHER_(expr const* n, expr*& a1, expr*& a2, expr *& a3) const { \ if (_MATCHER_(n) && to_app(n)->get_num_args() == 3) { \ a1 = to_app(n)->get_arg(0); a2 = to_app(n)->get_arg(1); a3 = to_app(n)->get_arg(2); return true; } \ return false; \ } #define MATCH_BINARY(_MATCHER_) \ bool _MATCHER_(expr const* n, expr*& s, expr*& t) const { \ if (_MATCHER_(n) && to_app(n)->get_num_args() == 2) { s = to_app(n)->get_arg(0); t = to_app(n)->get_arg(1); return true; } \ return false; \ } #define MATCH_UNARY(_MATCHER_) \ bool _MATCHER_(expr const* n, expr*& s) const { \ if (_MATCHER_(n) && to_app(n)->get_num_args() == 1) { s = to_app(n)->get_arg(0); return true; } \ return false; \ } // ----------------------------------- // // decl // // ----------------------------------- /** The ids of expressions and declarations are in different ranges. */ const unsigned c_first_decl_id = (1 << 31); /** \brief Superclass for function declarations and sorts. */ class decl : public ast { protected: friend class ast_manager; symbol m_name; decl_info * m_info; decl(ast_kind k, symbol const & name, decl_info * info):ast(k), m_name(name), m_info(info) {} public: unsigned get_decl_id() const { SASSERT(get_id() >= c_first_decl_id); return get_id() - c_first_decl_id; } symbol const & get_name() const { return m_name; } decl_info * get_info() const { return m_info; } family_id get_family_id() const { return m_info == 0 ? null_family_id : m_info->get_family_id(); } decl_kind get_decl_kind() const { return m_info == 0 ? null_decl_kind : m_info->get_decl_kind(); } unsigned get_num_parameters() const { return m_info == 0 ? 0 : m_info->get_num_parameters(); } parameter const & get_parameter(unsigned idx) const { return m_info->get_parameter(idx); } parameter const * get_parameters() const { return m_info == 0 ? 0 : m_info->get_parameters(); } bool private_parameters() const { return m_info != 0 && m_info->private_parameters(); } }; // ----------------------------------- // // sort // // ----------------------------------- class sort : public decl { friend class ast_manager; static unsigned get_obj_size() { return sizeof(sort); } sort(symbol const & name, sort_info * info):decl(AST_SORT, name, info) {} public: sort_info * get_info() const { return static_cast(m_info); } bool is_infinite() const { return get_info() == 0 || get_info()->is_infinite(); } bool is_very_big() const { return get_info() == 0 || get_info()->is_very_big(); } bool is_sort_of(family_id fid, decl_kind k) const { return get_family_id() == fid && get_decl_kind() == k; } sort_size const & get_num_elements() const { return get_info()->get_num_elements(); } unsigned get_size() const { return get_obj_size(); } }; // ----------------------------------- // // func_decl // // ----------------------------------- class func_decl : public decl { friend class ast_manager; unsigned m_arity; sort * m_range; sort * m_domain[0]; static unsigned get_obj_size(unsigned arity) { return sizeof(func_decl) + arity * sizeof(sort *); } func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info * info); public: func_decl_info * get_info() const { return static_cast(m_info); } bool is_associative() const { return get_info() != 0 && get_info()->is_associative(); } bool is_left_associative() const { return get_info() != 0 && get_info()->is_left_associative(); } bool is_right_associative() const { return get_info() != 0 && get_info()->is_right_associative(); } bool is_flat_associative() const { return get_info() != 0 && get_info()->is_flat_associative(); } bool is_commutative() const { return get_info() != 0 && get_info()->is_commutative(); } bool is_chainable() const { return get_info() != 0 && get_info()->is_chainable(); } bool is_pairwise() const { return get_info() != 0 && get_info()->is_pairwise(); } bool is_injective() const { return get_info() != 0 && get_info()->is_injective(); } bool is_skolem() const { return get_info() != 0 && get_info()->is_skolem(); } bool is_idempotent() const { return get_info() != 0 && get_info()->is_idempotent(); } unsigned get_arity() const { return m_arity; } sort * get_domain(unsigned idx) const { SASSERT(idx < get_arity()); return m_domain[idx]; } sort * const * get_domain() const { return m_domain; } sort * get_range() const { return m_range; } unsigned get_size() const { return get_obj_size(m_arity); } }; // ----------------------------------- // // expression // // ----------------------------------- /** \brief Superclass for applications, variables and quantifiers. */ class expr : public ast { protected: friend class ast_manager; expr(ast_kind k):ast(k) {} public: }; // ----------------------------------- // // application // // ----------------------------------- #define APP_DEPTH_NUM_BITS 16 const unsigned c_max_depth = ((1 << APP_DEPTH_NUM_BITS) - 1); struct app_flags { unsigned m_depth:APP_DEPTH_NUM_BITS; // if app is to deep, it doesn't matter. unsigned m_ground:1; // application does not have free variables or nested quantifiers. unsigned m_has_quantifiers:1; // application has nested quantifiers. unsigned m_has_labels:1; // application has nested labels. static app_flags mk_const_flags(); static app_flags mk_default_app_flags(); static app_flags mk_default_quantifier_flags(); }; class app : public expr { friend class ast_manager; func_decl * m_decl; unsigned m_num_args; expr * m_args[0]; static app_flags g_constant_flags; // remark: store term depth in the end of the app. the depth is only stored if the num_args > 0 static unsigned get_obj_size(unsigned num_args) { return num_args == 0 ? sizeof(app) : sizeof(app) + num_args * sizeof(expr *) + sizeof(app_flags); } friend class tmp_app; app_flags * flags() const { return m_num_args == 0 ? &g_constant_flags : reinterpret_cast(const_cast(m_args + m_num_args)); } app(func_decl * decl, unsigned num_args, expr * const * args); public: func_decl * get_decl() const { return m_decl; } family_id get_family_id() const { return get_decl()->get_family_id(); } decl_kind get_decl_kind() const { return get_decl()->get_decl_kind(); } bool is_app_of(family_id fid, decl_kind k) const { return get_family_id() == fid && get_decl_kind() == k; } unsigned get_num_args() const { return m_num_args; } expr * get_arg(unsigned idx) const { SASSERT(idx < m_num_args); return m_args[idx]; } expr * const * get_args() const { return m_args; } unsigned get_size() const { return get_obj_size(get_num_args()); } unsigned get_depth() const { return flags()->m_depth; } bool is_ground() const { return flags()->m_ground; } bool has_quantifiers() const { return flags()->m_has_quantifiers; } bool has_labels() const { return flags()->m_has_labels; } }; // ----------------------------------- // // temporary application: little hack to avoid // the creation of temporary expressions to just // check the presence of the expression in // some container/index. // // ----------------------------------- class tmp_app { unsigned m_num_args; char * m_data; public: tmp_app(unsigned num_args): m_num_args(num_args) { unsigned sz = app::get_obj_size(num_args); m_data = alloc_svect(char, sz); memset(m_data, 0, sz); get_app()->m_num_args = m_num_args; } ~tmp_app() { dealloc_svect(m_data); } app * get_app() { return reinterpret_cast(m_data); } expr ** get_args() { return get_app()->m_args; } void set_decl(func_decl * d) { get_app()->m_decl = d; } void set_num_args(unsigned num_args) { get_app()->m_num_args = num_args; } void set_arg(unsigned idx, expr * arg) { get_args()[idx] = arg; SASSERT(get_app()->get_arg(idx) == arg); } void copy(app * source) { SASSERT(source->get_num_args() <= m_num_args); new (m_data) app(source->get_decl(), source->get_num_args(), source->get_args()); SASSERT(get_app()->get_decl() == source->get_decl()); SASSERT(get_app()->get_arg(0) == source->get_arg(0)); SASSERT(get_app()->get_arg(1) == source->get_arg(1)); } void copy_swapping_args(app * source) { SASSERT(source->get_num_args() == 2 && m_num_args >= 2); expr * args[2] = { source->get_arg(1), source->get_arg(0) }; new (m_data) app(source->get_decl(), 2, args); SASSERT(get_app()->get_decl() == source->get_decl()); SASSERT(get_app()->get_arg(0) == source->get_arg(1)); SASSERT(get_app()->get_arg(1) == source->get_arg(0)); } }; // ----------------------------------- // // variables // // ----------------------------------- class var : public expr { friend class ast_manager; unsigned m_idx; sort * m_sort; static unsigned get_obj_size() { return sizeof(var); } var(unsigned idx, sort * s):expr(AST_VAR), m_idx(idx), m_sort(s) {} public: unsigned get_idx() const { return m_idx; } sort * get_sort() const { return m_sort; } unsigned get_size() const { return get_obj_size(); } }; // ----------------------------------- // // quantifier // // ----------------------------------- class quantifier : public expr { friend class ast_manager; bool m_forall; unsigned m_num_decls; expr * m_expr; unsigned m_depth; // extra fields int m_weight; bool m_has_unused_vars; bool m_has_labels; symbol m_qid; symbol m_skid; unsigned m_num_patterns; unsigned m_num_no_patterns; char m_patterns_decls[0]; static unsigned get_obj_size(unsigned num_decls, unsigned num_patterns, unsigned num_no_patterns) { return sizeof(quantifier) + num_decls * (sizeof(sort *) + sizeof(symbol)) + (num_patterns + num_no_patterns) * sizeof(expr*); } quantifier(bool forall, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, int weight, symbol const & qid, symbol const & skid, unsigned num_patterns, expr * const * patterns, unsigned num_no_patterns, expr * const * no_patterns); public: bool is_forall() const { return m_forall; } bool is_exists() const { return !m_forall; } unsigned get_num_decls() const { return m_num_decls; } sort * const * get_decl_sorts() const { return reinterpret_cast(m_patterns_decls); } symbol const * get_decl_names() const { return reinterpret_cast(get_decl_sorts() + m_num_decls); } sort * get_decl_sort(unsigned idx) const { return get_decl_sorts()[idx]; } symbol const & get_decl_name(unsigned idx) const { return get_decl_names()[idx]; } expr * get_expr() const { return m_expr; } unsigned get_depth() const { return m_depth; } int get_weight() const { return m_weight; } symbol const & get_qid() const { return m_qid; } symbol const & get_skid() const { return m_skid; } unsigned get_num_patterns() const { return m_num_patterns; } expr * const * get_patterns() const { return reinterpret_cast(get_decl_names() + m_num_decls); } expr * get_pattern(unsigned idx) const { return get_patterns()[idx]; } unsigned get_num_no_patterns() const { return m_num_no_patterns; } expr * const * get_no_patterns() const { return reinterpret_cast(get_decl_names() + m_num_decls); } expr * get_no_pattern(unsigned idx) const { return get_no_patterns()[idx]; } bool has_patterns() const { return m_num_patterns > 0 || m_num_no_patterns > 0; } unsigned get_size() const { return get_obj_size(m_num_decls, m_num_patterns, m_num_no_patterns); } bool may_have_unused_vars() const { return m_has_unused_vars; } void set_no_unused_vars() { m_has_unused_vars = false; } bool has_labels() const { return m_has_labels; } unsigned get_num_children() const { return 1 + get_num_patterns() + get_num_no_patterns(); } expr * get_child(unsigned idx) const { SASSERT(idx < get_num_children()); if (idx == 0) return get_expr(); else if (idx <= get_num_patterns()) return get_pattern(idx - 1); else return get_no_pattern(idx - get_num_patterns() - 1); } }; // ----------------------------------- // // AST recognisers // // ----------------------------------- inline bool is_decl(ast const * n) { ast_kind k = n->get_kind(); return k == AST_FUNC_DECL || k == AST_SORT; } inline bool is_sort(ast const * n) { return n->get_kind() == AST_SORT; } inline bool is_func_decl(ast const * n) { return n->get_kind() == AST_FUNC_DECL; } inline bool is_expr(ast const * n) { return !is_decl(n); } inline bool is_app(ast const * n) { return n->get_kind() == AST_APP; } inline bool is_var(ast const * n) { return n->get_kind() == AST_VAR; } inline bool is_quantifier(ast const * n) { return n->get_kind() == AST_QUANTIFIER; } inline bool is_forall(ast const * n) { return is_quantifier(n) && static_cast(n)->is_forall(); } inline bool is_exists(ast const * n) { return is_quantifier(n) && static_cast(n)->is_exists(); } // ----------------------------------- // // AST coercions // // ----------------------------------- inline decl * to_decl(ast * n) { SASSERT(is_decl(n)); return static_cast(n); } inline sort * to_sort(ast * n) { SASSERT(is_sort(n)); return static_cast(n); } inline func_decl * to_func_decl(ast * n) { SASSERT(is_func_decl(n)); return static_cast(n); } inline expr * to_expr(ast * n) { SASSERT(is_expr(n)); return static_cast(n); } inline app * to_app(ast * n) { SASSERT(is_app(n)); return static_cast(n); } inline var * to_var(ast * n) { SASSERT(is_var(n)); return static_cast(n); } inline quantifier * to_quantifier(ast * n) { SASSERT(is_quantifier(n)); return static_cast(n); } inline decl const * to_decl(ast const * n) { SASSERT(is_decl(n)); return static_cast(n); } inline sort const * to_sort(ast const * n) { SASSERT(is_sort(n)); return static_cast(n); } inline func_decl const * to_func_decl(ast const * n) { SASSERT(is_func_decl(n)); return static_cast(n); } inline expr const * to_expr(ast const * n) { SASSERT(is_expr(n)); return static_cast(n); } inline app const * to_app(ast const * n) { SASSERT(is_app(n)); return static_cast(n); } inline var const * to_var(ast const * n) { SASSERT(is_var(n)); return static_cast(n); } inline quantifier const * to_quantifier(ast const * n) { SASSERT(is_quantifier(n)); return static_cast(n); } // ----------------------------------- // // AST hash-consing // // ----------------------------------- unsigned get_node_hash(ast const * n); bool compare_nodes(ast const * n1, ast const * n2); unsigned get_node_size(ast const * n); unsigned get_asts_hash(unsigned sz, ast * const* ns, unsigned init); unsigned get_apps_hash(unsigned sz, app * const* ns, unsigned init); unsigned get_exprs_hash(unsigned sz, expr * const* ns, unsigned init); unsigned get_sorts_hash(unsigned sz, sort * const* ns, unsigned init); unsigned get_decl_hash(unsigned sz, func_decl* const* ns, unsigned init); // This is the internal comparison functor for hash-consing AST nodes. struct ast_eq_proc { bool operator()(ast const * n1, ast const * n2) const { return n1->hash() == n2->hash() && compare_nodes(n1, n2); } }; class ast_table : public chashtable, ast_eq_proc> { public: void erase(ast * n); }; // ----------------------------------- // // decl_plugin // // ----------------------------------- /** \brief Auxiliary data-structure used to initialize the parser symbol tables. */ struct builtin_name { decl_kind m_kind; symbol m_name; builtin_name(char const * name, decl_kind k) : m_kind(k), m_name(name) {} }; /** \brief Each family of intepreted function declarations and sorts must provide a plugin to build sorts and decls of the family. */ class decl_plugin { protected: ast_manager * m_manager; family_id m_family_id; virtual void set_manager(ast_manager * m, family_id id) { SASSERT(m_manager == 0); m_manager = m; m_family_id = id; } friend class ast_manager; public: decl_plugin():m_manager(0), m_family_id(null_family_id) {} virtual ~decl_plugin() {} virtual void finalize() {} virtual void set_cancel(bool f) {} virtual decl_plugin * mk_fresh() = 0; family_id get_family_id() const { return m_family_id; } virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) = 0; virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) = 0; virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const* parameters, unsigned num_args, expr * const * args, sort * range); /** \brief Return true if the plugin can decide whether two interpreted constants are equal or not. For all a, b: If is_value(a) and is_value(b) Then, are_equal(a, b) != are_distinct(a, b) The may be much more expensive than checking a pointer. We need this because some plugin values are too expensive too canonize. */ virtual bool is_value(app * a) const { return false; } /** \brief Return true if \c a is a unique plugin value. The following property should hold for unique theory values: For all a, b: If is_unique_value(a) and is_unique_value(b) Then, a == b (pointer equality) IFF the interpretations of these theory terms are equal. \remark This is a stronger version of is_value. */ virtual bool is_unique_value(app * a) const { return false; } virtual bool are_equal(app * a, app * b) const { return a == b && is_unique_value(a) && is_unique_value(b); } virtual bool are_distinct(app * a, app * b) const { return a != b && is_unique_value(a) && is_unique_value(b); } virtual void get_op_names(svector & op_names, symbol const & logic = symbol()) {} virtual void get_sort_names(svector & sort_names, symbol const & logic = symbol()) {} virtual expr * get_some_value(sort * s) { return 0; } // Return true if the interpreted sort s does not depend on uninterpreted sorts. // This may be the case, for example, for array and datatype sorts. virtual bool is_fully_interp(sort const * s) const { return true; } // Event handlers for deleting/translating PARAM_EXTERNAL virtual void del(parameter const & p) {} virtual parameter translate(parameter const & p, decl_plugin & target) { UNREACHABLE(); return p; } }; // ----------------------------------- // // basic_decl_plugin (i.e., builtin plugin) // // ----------------------------------- enum basic_sort_kind { BOOL_SORT, PROOF_SORT }; enum basic_op_kind { OP_TRUE, OP_FALSE, OP_EQ, OP_DISTINCT, OP_ITE, OP_AND, OP_OR, OP_IFF, OP_XOR, OP_NOT, OP_IMPLIES, OP_OEQ, OP_INTERP, LAST_BASIC_OP, PR_UNDEF, PR_TRUE, PR_ASSERTED, PR_GOAL, PR_MODUS_PONENS, PR_REFLEXIVITY, PR_SYMMETRY, PR_TRANSITIVITY, PR_TRANSITIVITY_STAR, PR_MONOTONICITY, PR_QUANT_INTRO, PR_DISTRIBUTIVITY, PR_AND_ELIM, PR_NOT_OR_ELIM, PR_REWRITE, PR_REWRITE_STAR, PR_PULL_QUANT, PR_PULL_QUANT_STAR, PR_PUSH_QUANT, PR_ELIM_UNUSED_VARS, PR_DER, PR_QUANT_INST, PR_HYPOTHESIS, PR_LEMMA, PR_UNIT_RESOLUTION, PR_IFF_TRUE, PR_IFF_FALSE, PR_COMMUTATIVITY, PR_DEF_AXIOM, PR_DEF_INTRO, PR_APPLY_DEF, PR_IFF_OEQ, PR_NNF_POS, PR_NNF_NEG, PR_NNF_STAR, PR_SKOLEMIZE, PR_CNF_STAR, PR_MODUS_PONENS_OEQ, PR_TH_LEMMA, PR_HYPER_RESOLVE, LAST_BASIC_PR }; class basic_decl_plugin : public decl_plugin { protected: sort * m_bool_sort; func_decl * m_true_decl; func_decl * m_false_decl; func_decl * m_and_decl; func_decl * m_or_decl; func_decl * m_iff_decl; func_decl * m_xor_decl; func_decl * m_not_decl; func_decl * m_interp_decl; func_decl * m_implies_decl; ptr_vector m_eq_decls; // cached eqs ptr_vector m_ite_decls; // cached ites ptr_vector m_oeq_decls; // cached obsevational eqs sort * m_proof_sort; func_decl * m_undef_decl; func_decl * m_true_pr_decl; func_decl * m_asserted_decl; func_decl * m_goal_decl; func_decl * m_modus_ponens_decl; func_decl * m_reflexivity_decl; func_decl * m_symmetry_decl; func_decl * m_transitivity_decl; func_decl * m_quant_intro_decl; func_decl * m_and_elim_decl; func_decl * m_not_or_elim_decl; func_decl * m_rewrite_decl; func_decl * m_pull_quant_decl; func_decl * m_pull_quant_star_decl; func_decl * m_push_quant_decl; func_decl * m_elim_unused_vars_decl; func_decl * m_der_decl; func_decl * m_quant_inst_decl; ptr_vector m_monotonicity_decls; ptr_vector m_transitivity_star_decls; ptr_vector m_distributivity_decls; ptr_vector m_assoc_flat_decls; ptr_vector m_rewrite_star_decls; func_decl * m_hypothesis_decl; func_decl * m_iff_true_decl; func_decl * m_iff_false_decl; func_decl * m_commutativity_decl; func_decl * m_def_axiom_decl; func_decl * m_lemma_decl; ptr_vector m_unit_resolution_decls; func_decl * m_def_intro_decl; func_decl * m_iff_oeq_decl; func_decl * m_skolemize_decl; func_decl * m_mp_oeq_decl; ptr_vector m_apply_def_decls; ptr_vector m_nnf_pos_decls; ptr_vector m_nnf_neg_decls; ptr_vector m_nnf_star_decls; ptr_vector m_cnf_star_decls; ptr_vector m_th_lemma_decls; func_decl * m_hyper_res_decl0; static bool is_proof(decl_kind k) { return k > LAST_BASIC_OP; } bool check_proof_sorts(basic_op_kind k, unsigned arity, sort * const * domain) const; bool check_proof_args(basic_op_kind k, unsigned num_args, expr * const * args) const; func_decl * mk_bool_op_decl(char const * name, basic_op_kind k, unsigned num_args = 0, bool asooc = false, bool comm = false, bool idempotent = false, bool flat_associative = false, bool chainable = false); func_decl * mk_implies_decl(); func_decl * mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents); func_decl * mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents, func_decl*& fn); func_decl * mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents, ptr_vector & cache); func_decl * mk_compressed_proof_decl(char const * name, basic_op_kind k, unsigned num_parents); func_decl * mk_proof_decl(basic_op_kind k, unsigned num_parents); func_decl * mk_proof_decl(basic_op_kind k, unsigned num_parameters, parameter const* params, unsigned num_parents); func_decl * mk_proof_decl( char const * name, basic_op_kind k, unsigned num_parameters, parameter const* params, unsigned num_parents); virtual void set_manager(ast_manager * m, family_id id); func_decl * mk_eq_decl_core(char const * name, decl_kind k, sort * s, ptr_vector & cache); func_decl * mk_ite_decl(sort * s); sort* join(sort* s1, sort* s2); public: basic_decl_plugin(); virtual ~basic_decl_plugin() {} virtual void finalize(); virtual decl_plugin * mk_fresh() { return alloc(basic_decl_plugin); } virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters); virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range); virtual void get_op_names(svector & op_names, symbol const & logic); virtual void get_sort_names(svector & sort_names, symbol const & logic); virtual bool is_value(app* a) const; virtual bool is_unique_value(app* a) const; sort * mk_bool_sort() const { return m_bool_sort; } sort * mk_proof_sort() const { return m_proof_sort; } virtual expr * get_some_value(sort * s); }; typedef app proof; /* a proof is just an applicaton */ // ----------------------------------- // // label_decl_plugin // // ----------------------------------- enum label_op_kind { OP_LABEL, OP_LABEL_LIT, }; /** \brief Labels are identity functions used to mark sub-expressions. */ class label_decl_plugin : public decl_plugin { symbol m_lblpos; symbol m_lblneg; symbol m_lbllit; virtual void set_manager(ast_manager * m, family_id id); public: label_decl_plugin(); virtual ~label_decl_plugin(); virtual decl_plugin * mk_fresh() { return alloc(label_decl_plugin); } virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); /** contract: when label parameter[0] (int): 0 - if the label is negative, 1 - if positive. parameter[1] (symbol): label's tag. ... parameter[n-1] (symbol): label's tag. contract: when label literal (they are always positive) parameter[0] (symbol): label's tag ... parameter[n-1] (symbol): label's tag. */ virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); }; // ----------------------------------- // // pattern_decl_plugin // // ----------------------------------- enum pattern_op_kind { OP_PATTERN }; /** \brief Patterns are used to group expressions. These expressions are using during E-matching for heurisitic quantifier instantiation. */ class pattern_decl_plugin : public decl_plugin { public: virtual decl_plugin * mk_fresh() { return alloc(pattern_decl_plugin); } virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); }; // ----------------------------------- // // model_value_plugin // // ----------------------------------- enum model_value_op_kind { OP_MODEL_VALUE }; /** \brief Values are used during model construction. All values are assumed to be different. Users should not use them, since they may introduce unsoundess if the sort of a value is finite. Moreover, values should never be internalized in a logical context. However, values can be used during evaluation (i.e., simplification). \remark Model values can be viewed as the partion ids in Z3 1.x. */ class model_value_decl_plugin : public decl_plugin { public: model_value_decl_plugin() {} virtual decl_plugin * mk_fresh() { return alloc(model_value_decl_plugin); } virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); /** contract: parameter[0]: (integer) value idx parameter[1]: (ast) sort of the value. */ virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); virtual bool is_value(app* n) const; virtual bool is_unique_value(app* a) const; }; // ----------------------------------- // // user_sort_plugin for supporting user declared sorts in SMT2 // // ----------------------------------- class user_sort_plugin : public decl_plugin { svector m_sort_names; dictionary m_name2decl_kind; public: user_sort_plugin() {} virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); decl_kind register_name(symbol s); virtual decl_plugin * mk_fresh(); }; // ----------------------------------- // // Auxiliary functions // // ----------------------------------- // Return true if n is an application of d. inline bool is_app_of(expr const * n, func_decl const * d) { return n->get_kind() == AST_APP && to_app(n)->get_decl() == d; } inline bool is_app_of(expr const * n, family_id fid, decl_kind k) { return n->get_kind() == AST_APP && to_app(n)->is_app_of(fid, k); } inline bool is_sort_of(sort const * s, family_id fid, decl_kind k) { return s->is_sort_of(fid, k); } inline bool is_uninterp_const(expr const * n) { return n->get_kind() == AST_APP && to_app(n)->get_num_args() == 0 && to_app(n)->get_family_id() == null_family_id; } inline bool is_uninterp(expr const * n) { return n->get_kind() == AST_APP && to_app(n)->get_family_id() == null_family_id; } inline bool is_decl_of(func_decl const * d, family_id fid, decl_kind k) { return d->get_family_id() == fid && d->get_decl_kind() == k; } inline bool is_ground(expr const * n) { return is_app(n) && to_app(n)->is_ground(); } inline bool is_non_ground(expr const * n) { return ( ! is_ground(n)); } inline unsigned get_depth(expr const * n) { if (is_app(n)) return to_app(n)->get_depth(); else if (is_quantifier(n)) return to_quantifier(n)->get_depth(); else return 1; } inline bool has_quantifiers(expr const * n) { return is_app(n) ? to_app(n)->has_quantifiers() : is_quantifier(n); } inline bool has_labels(expr const * n) { if (is_app(n)) return to_app(n)->has_labels(); else if (is_quantifier(n)) return to_quantifier(n)->has_labels(); else return false; } sort * get_sort(expr const * n); class basic_recognizers { family_id m_fid; public: basic_recognizers(family_id fid):m_fid(fid) {} bool is_bool(sort const * s) const { return is_sort_of(s, m_fid, BOOL_SORT); } bool is_bool(expr const * n) const { return is_bool(get_sort(n)); } bool is_or(expr const * n) const { return is_app_of(n, m_fid, OP_OR); } bool is_implies(expr const * n) const { return is_app_of(n, m_fid, OP_IMPLIES); } bool is_and(expr const * n) const { return is_app_of(n, m_fid, OP_AND); } bool is_not(expr const * n) const { return is_app_of(n, m_fid, OP_NOT); } bool is_eq(expr const * n) const { return is_app_of(n, m_fid, OP_EQ); } bool is_oeq(expr const * n) const { return is_app_of(n, m_fid, OP_OEQ); } bool is_distinct(expr const * n) const { return is_app_of(n, m_fid, OP_DISTINCT); } bool is_iff(expr const * n) const { return is_app_of(n, m_fid, OP_IFF); } bool is_xor(expr const * n) const { return is_app_of(n, m_fid, OP_XOR); } bool is_ite(expr const * n) const { return is_app_of(n, m_fid, OP_ITE); } bool is_term_ite(expr const * n) const { return is_ite(n) && !is_bool(n); } bool is_true(expr const * n) const { return is_app_of(n, m_fid, OP_TRUE); } bool is_false(expr const * n) const { return is_app_of(n, m_fid, OP_FALSE); } bool is_complement_core(expr const * n1, expr const * n2) const { return (is_true(n1) && is_false(n2)) || (is_not(n1) && to_app(n1)->get_arg(0) == n2); } bool is_complement(expr const * n1, expr const * n2) const { return is_complement_core(n1, n2) || is_complement_core(n2, n1); } bool is_or(func_decl const * d) const { return is_decl_of(d, m_fid, OP_OR); } bool is_implies(func_decl const * d) const { return is_decl_of(d, m_fid, OP_IMPLIES); } bool is_and(func_decl const * d) const { return is_decl_of(d, m_fid, OP_AND); } bool is_not(func_decl const * d) const { return is_decl_of(d, m_fid, OP_NOT); } bool is_eq(func_decl const * d) const { return is_decl_of(d, m_fid, OP_EQ); } bool is_iff(func_decl const * d) const { return is_decl_of(d, m_fid, OP_IFF); } bool is_xor(func_decl const * d) const { return is_decl_of(d, m_fid, OP_XOR); } bool is_ite(func_decl const * d) const { return is_decl_of(d, m_fid, OP_ITE); } bool is_term_ite(func_decl const * d) const { return is_ite(d) && !is_bool(d->get_range()); } bool is_distinct(func_decl const * d) const { return is_decl_of(d, m_fid, OP_DISTINCT); } MATCH_UNARY(is_not); MATCH_BINARY(is_eq); MATCH_BINARY(is_iff); MATCH_BINARY(is_implies); MATCH_BINARY(is_and); MATCH_BINARY(is_or); MATCH_BINARY(is_xor); MATCH_TERNARY(is_and); MATCH_TERNARY(is_or); bool is_ite(expr const * n, expr * & t1, expr * & t2, expr * & t3) const; }; // ----------------------------------- // // Get Some Value functor // // Functor for returning some value // of the given sort. // // ----------------------------------- class some_value_proc { public: virtual expr * operator()(sort * s) = 0; }; // ----------------------------------- // // Proof generation mode // // ----------------------------------- enum proof_gen_mode { PGM_DISABLED, PGM_COARSE, PGM_FINE }; // ----------------------------------- // // ast_manager // // ----------------------------------- class ast_manager { friend class basic_decl_plugin; protected: struct config { typedef ast_manager value_manager; typedef small_object_allocator allocator; static const bool ref_count = true; }; struct array_config : public config { static const bool preserve_roots = true; static const unsigned max_trail_sz = 16; static const unsigned factor = 2; }; struct expr_array_config : public array_config { typedef expr * value; }; typedef parray_manager expr_array_manager; struct expr_dependency_config : public config { typedef expr * value; }; typedef dependency_manager expr_dependency_manager; public: typedef expr_array_manager::ref expr_array; typedef expr_dependency_manager::dependency expr_dependency; protected: struct expr_dependency_array_config : public array_config { typedef expr_dependency * value; }; typedef parray_manager expr_dependency_array_manager; public: typedef expr_dependency_array_manager::ref expr_dependency_array; void show_id_gen(); protected: small_object_allocator m_alloc; family_manager m_family_manager; expr_array_manager m_expr_array_manager; expr_dependency_manager m_expr_dependency_manager; expr_dependency_array_manager m_expr_dependency_array_manager; ptr_vector m_plugins; proof_gen_mode m_proof_mode; bool m_int_real_coercions; // If true, use hack that automatically introduces to_int/to_real when needed. family_id m_basic_family_id; family_id m_label_family_id; family_id m_pattern_family_id; family_id m_model_value_family_id; family_id m_user_sort_family_id; family_id m_arith_family_id; ast_table m_ast_table; id_gen m_expr_id_gen; id_gen m_decl_id_gen; sort * m_bool_sort; sort * m_proof_sort; app * m_true; app * m_false; proof * m_undef_proof; unsigned m_fresh_id; bool m_debug_ref_count; u_map m_debug_free_indices; std::fstream* m_trace_stream; bool m_trace_stream_owner; #ifdef Z3DEBUG bool slow_not_contains(ast const * n); #endif ast_manager * m_format_manager; // hack for isolating format objects in a different manager. void init(); bool coercion_needed(func_decl * decl, unsigned num_args, expr * const * args); public: ast_manager(proof_gen_mode = PGM_DISABLED, char const * trace_file = 0, bool is_format_manager = false); ast_manager(proof_gen_mode, std::fstream * trace_stream, bool is_format_manager = false); ast_manager(ast_manager const & src, bool disable_proofs = false); ~ast_manager(); // propagate cancellation signal to decl_plugins void set_cancel(bool f); void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } bool has_trace_stream() const { return m_trace_stream != 0; } std::ostream & trace_stream() { SASSERT(has_trace_stream()); return *m_trace_stream; } void enable_int_real_coercions(bool f) { m_int_real_coercions = f; } bool int_real_coercions() const { return m_int_real_coercions; } // Return true if s1 and s2 are equal, or coercions are enabled, and s1 and s2 are compatible. bool compatible_sorts(sort * s1, sort * s2) const; // For debugging purposes void display_free_ids(std::ostream & out) { m_expr_id_gen.display_free_ids(out); out << "\n"; m_decl_id_gen.display_free_ids(out); } void compact_memory(); void compress_ids(); // Equivalent to throw ast_exception(msg) void raise_exception(char const * msg); bool is_format_manager() const { return m_format_manager == 0; } ast_manager & get_format_manager() { return is_format_manager() ? *this : *m_format_manager; } void copy_families_plugins(ast_manager const & from); small_object_allocator & get_allocator() { return m_alloc; } family_id mk_family_id(symbol const & s) { return m_family_manager.mk_family_id(s); } family_id mk_family_id(char const * s) { return mk_family_id(symbol(s)); } family_id get_family_id(symbol const & s) const { return m_family_manager.get_family_id(s); } family_id get_family_id(char const * s) const { return get_family_id(symbol(s)); } symbol const & get_family_name(family_id fid) const { return m_family_manager.get_name(fid); } bool is_builtin_family_id(family_id fid) const { return fid == null_family_id || fid == m_basic_family_id || fid == m_label_family_id || fid == m_pattern_family_id || fid == m_model_value_family_id || fid == m_user_sort_family_id; } void register_plugin(symbol const & s, decl_plugin * plugin); void register_plugin(family_id id, decl_plugin * plugin); decl_plugin * get_plugin(family_id fid) const; bool has_plugin(family_id fid) const { return get_plugin(fid) != 0; } bool has_plugin(symbol const & s) const { return m_family_manager.has_family(s) && has_plugin(m_family_manager.get_family_id(s)); } void get_dom(svector & dom) const { m_family_manager.get_dom(dom); } void get_range(svector & range) const { m_family_manager.get_range(range); } family_id get_basic_family_id() const { return m_basic_family_id; } basic_decl_plugin * get_basic_decl_plugin() const { return static_cast(get_plugin(m_basic_family_id)); } family_id get_user_sort_family_id() const { return m_user_sort_family_id; } user_sort_plugin * get_user_sort_plugin() const { return static_cast(get_plugin(m_user_sort_family_id)); } /** \brief Debugging support method: set the next expression identifier to be the least value id' s.t. - id' >= id - id' is not used by any AST in m_table - id' is not in the expression m_free_ids This method should be only used to create small repros that exposes bugs in Z3. */ void set_next_expr_id(unsigned id); bool is_value(expr * e) const; bool is_unique_value(expr * e) const; bool are_equal(expr * a, expr * b) const; bool are_distinct(expr * a, expr * b) const; bool contains(ast * a) const { return m_ast_table.contains(a); } unsigned get_num_asts() const { return m_ast_table.size(); } void debug_ref_count() { m_debug_ref_count = true; } void inc_ref(ast * n) { if (n) n->inc_ref(); } void dec_ref(ast * n) { if (n) { n->dec_ref(); if (n->get_ref_count() == 0) delete_node(n); } } template void inc_array_ref(unsigned sz, T * const * a) { for(unsigned i = 0; i < sz; i++) { inc_ref(a[i]); } } template void dec_array_ref(unsigned sz, T * const * a) { for(unsigned i = 0; i < sz; i++) { dec_ref(a[i]); } } static unsigned get_node_size(ast const * n); size_t get_allocation_size() const { return m_alloc.get_allocation_size(); } protected: ast * register_node_core(ast * n); template T * register_node(T * n) { return static_cast(register_node_core(n)); } void delete_node(ast * n); void * allocate_node(unsigned size) { return m_alloc.allocate(size); } void deallocate_node(ast * n, unsigned sz) { m_alloc.deallocate(sz, n); } public: sort * get_sort(expr const * n) const { return ::get_sort(n); } void check_sort(func_decl const * decl, unsigned num_args, expr * const * args) const; void check_sorts_core(ast const * n) const; bool check_sorts(ast const * n) const; bool is_bool(expr const * n) const; bool is_bool(sort const * s) const { return s == m_bool_sort; } decl_kind get_eq_op(expr const * n) const { return is_bool(n) ? OP_IFF : OP_EQ; } private: sort * mk_sort(symbol const & name, sort_info * info); public: sort * mk_uninterpreted_sort(symbol const & name, unsigned num_parameters, parameter const * parameters); sort * mk_uninterpreted_sort(symbol const & name) { return mk_uninterpreted_sort(name, 0, 0); } sort * mk_sort(symbol const & name, sort_info const & info) { if (info.get_family_id() == null_family_id) { return mk_uninterpreted_sort(name); } else { return mk_sort(name, &const_cast(info)); } } sort * mk_sort(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = 0); sort * mk_bool_sort() const { return m_bool_sort; } sort * mk_proof_sort() const { return m_proof_sort; } sort * mk_fresh_sort(char const * prefix = ""); bool is_uninterp(sort const * s) const { return s->get_family_id() == null_family_id || s->get_family_id() == m_user_sort_family_id; } /** \brief A sort is "fully" interpreted if it is interpreted, and doesn't depend on other uninterpreted sorts. */ bool is_fully_interp(sort const * s) const; func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range = 0); func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range = 0); app * mk_app(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = 0, unsigned num_args = 0, expr * const * args = 0, sort * range = 0); app * mk_app(family_id fid, decl_kind k, unsigned num_args, expr * const * args); app * mk_app(family_id fid, decl_kind k, expr * arg); app * mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2); app * mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3); app * mk_const(family_id fid, decl_kind k) { return mk_app(fid, k, 0, static_cast(0)); } private: func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info * info); app * mk_app_core(func_decl * decl, expr * arg1, expr * arg2); app * mk_app_core(func_decl * decl, unsigned num_args, expr * const * args); public: func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range) { return mk_func_decl(name, arity, domain, range, static_cast(0)); } func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info const & info) { if (info.is_null()) { return mk_func_decl(name, arity, domain, range, static_cast(0)); } else { return mk_func_decl(name, arity, domain, range, & const_cast(info)); } } func_decl * mk_func_decl(unsigned arity, sort * const * domain, func_decl_info const & info) { return mk_func_decl(info.get_family_id(), info.get_decl_kind(), info.get_num_parameters(), info.get_parameters(), arity, domain); } func_decl * mk_const_decl(symbol const & name, sort * s) { return mk_func_decl(name, static_cast(0), 0, s); } func_decl * mk_const_decl(symbol const & name, sort * s, func_decl_info const & info) { return mk_func_decl(name, static_cast(0), 0, s, info); } func_decl * mk_func_decl(symbol const & name, sort * domain, sort * range, func_decl_info const & info) { return mk_func_decl(name, 1, &domain, range, info); } func_decl * mk_func_decl(symbol const & name, sort * domain, sort * range) { return mk_func_decl(name, 1, &domain, range); } func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range, func_decl_info const & info) { sort * d[2] = { domain1, domain2 }; return mk_func_decl(name, 2, d, range, info); } func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range) { sort * d[2] = { domain1, domain2 }; return mk_func_decl(name, 2, d, range); } func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, bool assoc, bool comm = false, bool inj = false); func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range, bool assoc, bool comm = false) { sort * d[2] = { domain1, domain2 }; return mk_func_decl(name, 2, d, range, assoc, comm, false); } app * mk_app(func_decl * decl, unsigned num_args, expr * const * args); app * mk_app(func_decl * decl, expr * const * args) { return mk_app(decl, decl->get_arity(), args); } app * mk_app(func_decl * decl, expr * arg) { SASSERT(decl->get_arity() == 1); return mk_app(decl, 1, &arg); } app * mk_app(func_decl * decl, expr * arg1, expr * arg2) { SASSERT(decl->get_arity() == 2); expr * args[2] = { arg1, arg2 }; return mk_app(decl, 2, args); } app * mk_app(func_decl * decl, expr * arg1, expr * arg2, expr * arg3) { SASSERT(decl->get_arity() == 3); expr * args[3] = { arg1, arg2, arg3 }; return mk_app(decl, 3, args); } app * mk_const(func_decl * decl) { SASSERT(decl->get_arity() == 0); return mk_app(decl, static_cast(0), static_cast(0)); } app * mk_const(symbol const & name, sort * s) { return mk_const(mk_const_decl(name, s)); } func_decl * mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity, sort * const * domain, sort * range); func_decl * mk_fresh_func_decl(unsigned arity, sort * const * domain, sort * range) { return mk_fresh_func_decl(symbol::null, symbol::null, arity, domain, range); } func_decl * mk_fresh_func_decl(char const * prefix, char const * suffix, unsigned arity, sort * const * domain, sort * range) { return mk_fresh_func_decl(symbol(prefix), symbol(suffix), arity, domain, range); } func_decl * mk_fresh_func_decl(char const * prefix, unsigned arity, sort * const * domain, sort * range) { return mk_fresh_func_decl(symbol(prefix), symbol::null, arity, domain, range); } app * mk_fresh_const(char const * prefix, sort * s) { return mk_const(mk_fresh_func_decl(prefix, 0, 0, s)); } symbol mk_fresh_var_name(char const * prefix = 0); var * mk_var(unsigned idx, sort * ty); app * mk_label(bool pos, unsigned num_names, symbol const * names, expr * n); app * mk_label(bool pos, symbol const & name, expr * n); bool is_label(expr const * n, bool & pos, buffer & names) const; bool is_label(expr const * n, bool & pos, buffer & names, expr*& l) const { return is_label(n, pos, names)?(l = to_app(n)->get_arg(0), true):false; } bool is_label(expr const * n) const { return is_app_of(n, m_label_family_id, OP_LABEL); } bool is_label(expr const * n, expr*& l) const { return is_label(n)?(l = to_app(n)->get_arg(0), true):false; } bool is_label(expr const * n, bool& pos) const { if (is_app_of(n, m_label_family_id, OP_LABEL)) { pos = to_app(n)->get_decl()->get_parameter(0).get_int() != 0; return true; } else { return false; } } app * mk_label_lit(unsigned num_names, symbol const * names); app * mk_label_lit(symbol const & name); bool is_label_lit(expr const * n, buffer & names) const; bool is_label_lit(expr const * n) const { return is_app_of(n, m_label_family_id, OP_LABEL_LIT); } family_id get_label_family_id() const { return m_label_family_id; } app * mk_pattern(unsigned num_exprs, app * const * exprs); app * mk_pattern(app * expr) { return mk_pattern(1, &expr); } bool is_pattern(expr const * n) const; public: quantifier * mk_quantifier(bool forall, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null, unsigned num_patterns = 0, expr * const * patterns = 0, unsigned num_no_patterns = 0, expr * const * no_patterns = 0); quantifier * mk_forall(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null, unsigned num_patterns = 0, expr * const * patterns = 0, unsigned num_no_patterns = 0, expr * const * no_patterns = 0) { return mk_quantifier(true, num_decls, decl_sorts, decl_names, body, weight, qid, skid, num_patterns, patterns, num_no_patterns, no_patterns); } quantifier * mk_exists(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null, unsigned num_patterns = 0, expr * const * patterns = 0, unsigned num_no_patterns = 0, expr * const * no_patterns = 0) { return mk_quantifier(false, num_decls, decl_sorts, decl_names, body, weight, qid, skid, num_patterns, patterns, num_no_patterns, no_patterns); } quantifier * update_quantifier(quantifier * q, unsigned new_num_patterns, expr * const * new_patterns, expr * new_body); quantifier * update_quantifier(quantifier * q, unsigned new_num_patterns, expr * const * new_patterns, unsigned new_num_no_patterns, expr * const * new_no_patterns, expr * new_body); quantifier * update_quantifier(quantifier * q, expr * new_body); quantifier * update_quantifier_weight(quantifier * q, int new_weight); quantifier * update_quantifier(quantifier * q, bool new_is_forall, expr * new_body); quantifier * update_quantifier(quantifier * q, bool new_is_forall, unsigned new_num_patterns, expr * const * new_patterns, expr * new_body); // ----------------------------------- // // expr_array // // ----------------------------------- public: void mk(expr_array & r) { m_expr_array_manager.mk(r); } void del(expr_array & r) { m_expr_array_manager.del(r); } void copy(expr_array const & s, expr_array & r) { m_expr_array_manager.copy(s, r); } unsigned size(expr_array const & r) const { return m_expr_array_manager.size(r); } bool empty(expr_array const & r) const { return m_expr_array_manager.empty(r); } expr * get(expr_array const & r, unsigned i) const { return m_expr_array_manager.get(r, i); } void set(expr_array & r, unsigned i, expr * v) { m_expr_array_manager.set(r, i, v); } void set(expr_array const & s, unsigned i, expr * v, expr_array & r) { m_expr_array_manager.set(s, i, v, r); } void push_back(expr_array & r, expr * v) { m_expr_array_manager.push_back(r, v); } void push_back(expr_array const & s, expr * v, expr_array & r) { m_expr_array_manager.push_back(s, v, r); } void pop_back(expr_array & r) { m_expr_array_manager.pop_back(r); } void pop_back(expr_array const & s, expr_array & r) { m_expr_array_manager.pop_back(s, r); } void unshare(expr_array & r) { m_expr_array_manager.unshare(r); } void unfold(expr_array & r) { m_expr_array_manager.unfold(r); } void reroot(expr_array & r) { m_expr_array_manager.reroot(r); } // ----------------------------------- // // expr_dependency // // ----------------------------------- public: expr_dependency * mk_empty_dependencies() { return m_expr_dependency_manager.mk_empty(); } expr_dependency * mk_leaf(expr * t); expr_dependency * mk_join(unsigned n, expr * const * ts); expr_dependency * mk_join(expr_dependency * d1, expr_dependency * d2) { return m_expr_dependency_manager.mk_join(d1, d2); } void inc_ref(expr_dependency * d) { if (d) m_expr_dependency_manager.inc_ref(d); } void dec_ref(expr_dependency * d) { if (d) m_expr_dependency_manager.dec_ref(d); } void linearize(expr_dependency * d, ptr_vector & ts); bool contains(expr_dependency * d, expr * t) { return m_expr_dependency_manager.contains(d, t); } // ----------------------------------- // // expr_dependency_array // // ----------------------------------- public: void mk(expr_dependency_array & r) { m_expr_dependency_array_manager.mk(r); } void del(expr_dependency_array & r) { m_expr_dependency_array_manager.del(r); } void copy(expr_dependency_array const & s, expr_dependency_array & r) { m_expr_dependency_array_manager.copy(s, r); } unsigned size(expr_dependency_array const & r) const { return m_expr_dependency_array_manager.size(r); } bool empty(expr_dependency_array const & r) const { return m_expr_dependency_array_manager.empty(r); } expr_dependency * get(expr_dependency_array const & r, unsigned i) const { return m_expr_dependency_array_manager.get(r, i); } void set(expr_dependency_array & r, unsigned i, expr_dependency * v) { m_expr_dependency_array_manager.set(r, i, v); } void set(expr_dependency_array const & s, unsigned i, expr_dependency * v, expr_dependency_array & r) { m_expr_dependency_array_manager.set(s, i, v, r); } void push_back(expr_dependency_array & r, expr_dependency * v) { m_expr_dependency_array_manager.push_back(r, v); } void push_back(expr_dependency_array const & s, expr_dependency * v, expr_dependency_array & r) { m_expr_dependency_array_manager.push_back(s, v, r); } void pop_back(expr_dependency_array & r) { m_expr_dependency_array_manager.pop_back(r); } void pop_back(expr_dependency_array const & s, expr_dependency_array & r) { m_expr_dependency_array_manager.pop_back(s, r); } void unshare(expr_dependency_array & r) { m_expr_dependency_array_manager.unshare(r); } void unfold(expr_dependency_array & r) { m_expr_dependency_array_manager.unfold(r); } void reroot(expr_dependency_array & r) { m_expr_dependency_array_manager.reroot(r); } // ----------------------------------- // // Builtin operators // // ----------------------------------- public: bool is_or(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_OR); } bool is_implies(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_IMPLIES); } bool is_and(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_AND); } bool is_not(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_NOT); } bool is_eq(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_EQ); } bool is_oeq(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_OEQ); } bool is_distinct(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_DISTINCT); } bool is_iff(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_IFF); } bool is_xor(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_XOR); } bool is_ite(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_ITE); } bool is_term_ite(expr const * n) const { return is_ite(n) && !is_bool(n); } bool is_true(expr const * n) const { return n == m_true; } bool is_false(expr const * n) const { return n == m_false; } bool is_complement_core(expr const * n1, expr const * n2) const { return (is_true(n1) && is_false(n2)) || (is_not(n1) && to_app(n1)->get_arg(0) == n2); } bool is_complement(expr const * n1, expr const * n2) const { return is_complement_core(n1, n2) || is_complement_core(n2, n1); } bool is_or(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_OR); } bool is_implies(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_IMPLIES); } bool is_and(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_AND); } bool is_not(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_NOT); } bool is_eq(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_EQ); } bool is_iff(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_IFF); } bool is_xor(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_XOR); } bool is_ite(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_ITE); } bool is_term_ite(func_decl const * d) const { return is_ite(d) && !is_bool(d->get_range()); } bool is_distinct(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_DISTINCT); } public: MATCH_UNARY(is_not); MATCH_BINARY(is_eq); MATCH_BINARY(is_iff); MATCH_BINARY(is_implies); MATCH_BINARY(is_and); MATCH_BINARY(is_or); MATCH_BINARY(is_xor); MATCH_TERNARY(is_and); MATCH_TERNARY(is_or); bool is_ite(expr const* n, expr*& t1, expr*& t2, expr*& t3) const { if (is_ite(n)) { t1 = to_app(n)->get_arg(0); t2 = to_app(n)->get_arg(1); t3 = to_app(n)->get_arg(2); return true; } return false; } public: app * mk_eq(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, get_eq_op(lhs), lhs, rhs); } app * mk_iff(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_IFF, lhs, rhs); } app * mk_oeq(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_OEQ, lhs, rhs); } app * mk_xor(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_XOR, lhs, rhs); } app * mk_ite(expr * c, expr * t, expr * e) { return mk_app(m_basic_family_id, OP_ITE, c, t, e); } app * mk_xor(unsigned num_args, expr * const * args) { return mk_app(m_basic_family_id, OP_XOR, num_args, args); } app * mk_or(unsigned num_args, expr * const * args) { return mk_app(m_basic_family_id, OP_OR, num_args, args); } app * mk_and(unsigned num_args, expr * const * args) { return mk_app(m_basic_family_id, OP_AND, num_args, args); } app * mk_or(expr * arg1, expr * arg2) { return mk_app(m_basic_family_id, OP_OR, arg1, arg2); } app * mk_and(expr * arg1, expr * arg2) { return mk_app(m_basic_family_id, OP_AND, arg1, arg2); } app * mk_or(expr * arg1, expr * arg2, expr * arg3) { return mk_app(m_basic_family_id, OP_OR, arg1, arg2, arg3); } app * mk_and(expr * arg1, expr * arg2, expr * arg3) { return mk_app(m_basic_family_id, OP_AND, arg1, arg2, arg3); } app * mk_implies(expr * arg1, expr * arg2) { return mk_app(m_basic_family_id, OP_IMPLIES, arg1, arg2); } app * mk_not(expr * n) { return mk_app(m_basic_family_id, OP_NOT, n); } app * mk_distinct(unsigned num_args, expr * const * args); app * mk_distinct_expanded(unsigned num_args, expr * const * args); app * mk_true() { return m_true; } app * mk_false() { return m_false; } app * mk_interp(expr * arg) { return mk_app(m_basic_family_id, OP_INTERP, arg); } func_decl* mk_and_decl() { sort* domain[2] = { m_bool_sort, m_bool_sort }; return mk_func_decl(m_basic_family_id, OP_AND, 0, 0, 2, domain); } func_decl* mk_not_decl() { return mk_func_decl(m_basic_family_id, OP_NOT, 0, 0, 1, &m_bool_sort); } func_decl* mk_or_decl() { sort* domain[2] = { m_bool_sort, m_bool_sort }; return mk_func_decl(m_basic_family_id, OP_OR, 0, 0, 2, domain); } // ----------------------------------- // // Values // // ----------------------------------- protected: some_value_proc * m_some_value_proc; public: app * mk_model_value(unsigned idx, sort * s); bool is_model_value(expr const * n) const { return is_app_of(n, m_model_value_family_id, OP_MODEL_VALUE); } bool is_model_value(func_decl const * d) const { return is_decl_of(d, m_model_value_family_id, OP_MODEL_VALUE); } expr * get_some_value(sort * s, some_value_proc * p); expr * get_some_value(sort * s); // ----------------------------------- // // Proof generation // // ----------------------------------- protected: proof * mk_proof(family_id fid, decl_kind k, unsigned num_args, expr * const * args); proof * mk_proof(family_id fid, decl_kind k, expr * arg); proof * mk_proof(family_id fid, decl_kind k, expr * arg1, expr * arg2); proof * mk_proof(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3); public: bool proofs_enabled() const { return m_proof_mode != PGM_DISABLED; } bool proofs_disabled() const { return m_proof_mode == PGM_DISABLED; } bool coarse_grain_proofs() const { return m_proof_mode == PGM_COARSE; } bool fine_grain_proofs() const { return m_proof_mode == PGM_FINE; } proof_gen_mode proof_mode() const { return m_proof_mode; } void toggle_proof_mode(proof_gen_mode m) { m_proof_mode = m; } // APIs for creating proof objects return [undef] proof * mk_undef_proof() const { return m_undef_proof; } bool is_proof(expr const * n) const { return is_app(n) && to_app(n)->get_decl()->get_range() == m_proof_sort; } proof* mk_hyper_resolve(unsigned num_premises, proof* const* premises, expr* concl, svector > const& positions, vector > const& substs); bool is_undef_proof(expr const * e) const { return e == m_undef_proof; } bool is_asserted(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_ASSERTED); } bool is_goal(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_GOAL); } bool is_modus_ponens(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_MODUS_PONENS); } bool is_reflexivity(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_REFLEXIVITY); } bool is_symmetry(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_SYMMETRY); } bool is_transitivity(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_TRANSITIVITY); } bool is_monotonicity(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_MONOTONICITY); } bool is_quant_intro(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_QUANT_INTRO); } bool is_quant_inst(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_QUANT_INST); } bool is_distributivity(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_DISTRIBUTIVITY); } bool is_and_elim(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_AND_ELIM); } bool is_not_or_elim(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_NOT_OR_ELIM); } bool is_rewrite(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_REWRITE); } bool is_rewrite_star(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_REWRITE_STAR); } bool is_unit_resolution(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_UNIT_RESOLUTION); } bool is_lemma(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_LEMMA); } bool is_quant_inst(expr const* e, expr*& not_q_or_i, ptr_vector& binding) const; bool is_rewrite(expr const* e, expr*& r1, expr*& r2) const; bool is_hyper_resolve(proof* p) const { return is_app_of(p, m_basic_family_id, PR_HYPER_RESOLVE); } bool is_hyper_resolve(proof* p, ref_vector& premises, obj_ref& conclusion, svector > & positions, vector >& substs); bool is_def_intro(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_DEF_INTRO); } bool is_apply_def(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_APPLY_DEF); } bool is_skolemize(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_SKOLEMIZE); } MATCH_UNARY(is_asserted); MATCH_UNARY(is_lemma); bool has_fact(proof const * p) const { SASSERT(is_proof(p)); unsigned n = p->get_num_args(); return n > 0 && get_sort(p->get_arg(n - 1)) != m_proof_sort; } expr * get_fact(proof const * p) const { SASSERT(is_proof(p)); SASSERT(has_fact(p)); return p->get_arg(p->get_num_args() - 1); } unsigned get_num_parents(proof const * p) const { SASSERT(is_proof(p)); unsigned n = p->get_num_args(); return !has_fact(p) ? n : n - 1; } proof * get_parent(proof const * p, unsigned idx) const { SASSERT(is_proof(p)); return to_app(p->get_arg(idx)); } proof * mk_true_proof(); proof * mk_asserted(expr * f); proof * mk_goal(expr * f); proof * mk_modus_ponens(proof * p1, proof * p2); proof * mk_reflexivity(expr * e); proof * mk_oeq_reflexivity(expr * e); proof * mk_symmetry(proof * p); proof * mk_transitivity(proof * p1, proof * p2); proof * mk_transitivity(proof * p1, proof * p2, proof * p3); proof * mk_transitivity(proof * p1, proof * p2, proof * p3, proof * p4); proof * mk_transitivity(unsigned num_proofs, proof * const * proofs); proof * mk_transitivity(unsigned num_proofs, proof * const * proofs, expr * n1, expr * n2); proof * mk_monotonicity(func_decl * R, app * f1, app * f2, unsigned num_proofs, proof * const * proofs); proof * mk_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs); proof * mk_oeq_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs); proof * mk_commutativity(app * f); proof * mk_iff_true(proof * pr); proof * mk_iff_false(proof * pr); proof * mk_quant_intro(quantifier * q1, quantifier * q2, proof * p); proof * mk_oeq_quant_intro(quantifier * q1, quantifier * q2, proof * p); proof * mk_distributivity(expr * s, expr * r); proof * mk_rewrite(expr * s, expr * t); proof * mk_oeq_rewrite(expr * s, expr * t); proof * mk_rewrite_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs); proof * mk_pull_quant(expr * e, quantifier * q); proof * mk_pull_quant_star(expr * e, quantifier * q); proof * mk_push_quant(quantifier * q, expr * e); proof * mk_elim_unused_vars(quantifier * q, expr * r); proof * mk_der(quantifier * q, expr * r); proof * mk_quant_inst(expr * not_q_or_i, unsigned num_bind, expr* const* binding); proof * mk_def_axiom(expr * ax); proof * mk_unit_resolution(unsigned num_proofs, proof * const * proofs); proof * mk_unit_resolution(unsigned num_proofs, proof * const * proofs, expr * new_fact); proof * mk_hypothesis(expr * h); proof * mk_lemma(proof * p, expr * lemma); proof * mk_def_intro(expr * new_def); proof * mk_apply_defs(expr * n, expr * def, unsigned num_proofs, proof * const * proofs); proof * mk_apply_def(expr * n, expr * def, proof * p) { return mk_apply_defs(n, def, 1, &p); } proof * mk_iff_oeq(proof * parent); proof * mk_nnf_pos(expr * s, expr * t, unsigned num_proofs, proof * const * proofs); proof * mk_nnf_neg(expr * s, expr * t, unsigned num_proofs, proof * const * proofs); proof * mk_nnf_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs); proof * mk_skolemization(expr * q, expr * e); proof * mk_cnf_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs); proof * mk_and_elim(proof * p, unsigned i); proof * mk_not_or_elim(proof * p, unsigned i); proof * mk_th_lemma(family_id tid, expr * fact, unsigned num_proofs, proof * const * proofs, unsigned num_params = 0, parameter const* params = 0); protected: bool check_nnf_proof_parents(unsigned num_proofs, proof * const * proofs) const; private: void dec_ref(ptr_buffer & worklist, ast * n) { n->dec_ref(); if (n->get_ref_count() == 0) { worklist.push_back(n); } } template void dec_array_ref(ptr_buffer & worklist, unsigned sz, T * const * a) { for(unsigned i = 0; i < sz; i++) { dec_ref(worklist, a[i]); } } }; typedef ast_manager::expr_array expr_array; typedef ast_manager::expr_dependency expr_dependency; typedef ast_manager::expr_dependency_array expr_dependency_array; typedef obj_ref expr_dependency_ref; typedef ref_vector expr_dependency_ref_vector; typedef ref_buffer expr_dependency_ref_buffer; // ----------------------------------- // // More Auxiliary Functions // // ----------------------------------- inline bool is_predicate(ast_manager const & m, func_decl const * d) { return m.is_bool(d->get_range()); } struct ast_lt_proc { bool operator()(ast const * n1, ast const * n2) const { return n1->get_id() < n2->get_id(); } }; // ----------------------------------- // // ast_ref (smart pointer) // // ----------------------------------- typedef obj_ref ast_ref; typedef obj_ref expr_ref; typedef obj_ref sort_ref; typedef obj_ref func_decl_ref; typedef obj_ref quantifier_ref; typedef obj_ref app_ref; typedef obj_ref var_ref; typedef app_ref proof_ref; // ----------------------------------- // // ast_vector (smart pointer vector) // // ----------------------------------- typedef ref_vector ast_ref_vector; typedef ref_vector decl_ref_vector; typedef ref_vector sort_ref_vector; typedef ref_vector func_decl_ref_vector; typedef ref_vector expr_ref_vector; typedef ref_vector app_ref_vector; typedef ref_vector var_ref_vector; typedef ref_vector quantifier_ref_vector; typedef app_ref_vector proof_ref_vector; // ----------------------------------- // // ast_buffer // // ----------------------------------- typedef ref_buffer ast_ref_buffer; typedef ref_buffer expr_ref_buffer; typedef ref_buffer sort_ref_buffer; typedef ref_buffer app_ref_buffer; typedef app_ref_buffer proof_ref_buffer; // ----------------------------------- // // expr_mark // // ----------------------------------- typedef obj_mark expr_mark; class expr_sparse_mark { obj_hashtable m_marked; public: expr_sparse_mark() {} bool is_marked(expr * n) const { return m_marked.contains(n); } void mark(expr * n) { m_marked.insert(n); } void mark(expr * n, bool flag) { if (flag) m_marked.insert(n); else m_marked.erase(n); } void reset() { m_marked.reset(); } }; template class ast_fast_mark { ptr_buffer m_to_unmark; public: ast_fast_mark() {} ~ast_fast_mark() { reset(); } bool is_marked(ast * n) { return IDX == 1 ? AST_IS_MARKED1(n, this) : AST_IS_MARKED2(n, this); } void reset_mark(ast * n) { if (IDX == 1) { AST_RESET_MARK1(n, this); } else { AST_RESET_MARK2(n, this); } } void mark(ast * n) { if (IDX == 1) { if (AST_IS_MARKED1(n, this)) return; AST_MARK1(n, true, this); } else { if (AST_IS_MARKED2(n, this)) return; AST_MARK2(n, true, this); } m_to_unmark.push_back(n); } void reset() { ptr_buffer::iterator it = m_to_unmark.begin(); ptr_buffer::iterator end = m_to_unmark.end(); for (; it != end; ++it) { reset_mark(*it); } m_to_unmark.reset(); } void mark(ast * n, bool flag) { if (flag) mark(n); else reset_mark(n); } unsigned get_level() { return m_to_unmark.size(); } void set_level(unsigned new_size) { SASSERT(new_size <= m_to_unmark.size()); while (new_size < m_to_unmark.size()) { reset_mark(m_to_unmark.back()); m_to_unmark.pop_back(); } } }; typedef ast_fast_mark<1> ast_fast_mark1; typedef ast_fast_mark<2> ast_fast_mark2; typedef ast_fast_mark1 expr_fast_mark1; typedef ast_fast_mark2 expr_fast_mark2; /** Similar to ast_fast_mark, but increases reference counter. */ template class ast_ref_fast_mark { ast_ref_buffer m_to_unmark; public: ast_ref_fast_mark(ast_manager & m):m_to_unmark(m) {} ~ast_ref_fast_mark() { reset(); } bool is_marked(ast * n) { return IDX == 1 ? AST_IS_MARKED1(n, this) : AST_IS_MARKED2(n, this); } // It will not decrease the reference counter void reset_mark(ast * n) { if (IDX == 1) { AST_RESET_MARK1(n, this); } else { AST_RESET_MARK2(n, this); } } void mark(ast * n) { if (IDX == 1) { if (AST_IS_MARKED1(n, this)) return; AST_MARK1(n, true, this); } else { if (AST_IS_MARKED2(n, this)) return; AST_MARK2(n, true, this); } m_to_unmark.push_back(n); } void reset() { ast * const * it = m_to_unmark.c_ptr(); ast * const * end = it + m_to_unmark.size(); for (; it != end; ++it) { reset_mark(*it); } m_to_unmark.reset(); } void mark(ast * n, bool flag) { if (flag) mark(n); else reset_mark(n); } }; typedef ast_ref_fast_mark<1> ast_ref_fast_mark1; typedef ast_ref_fast_mark<2> ast_ref_fast_mark2; typedef ast_ref_fast_mark1 expr_ref_fast_mark1; typedef ast_ref_fast_mark2 expr_ref_fast_mark2; // ----------------------------------- // // ast_mark // // ----------------------------------- /** \brief A mapping from AST to Boolean \warning This map does not cleanup the entry associated with a node N, when N is deleted. */ class ast_mark { struct decl2uint { unsigned operator()(decl const & d) const { return d.get_decl_id(); } }; obj_mark m_expr_marks; obj_mark m_decl_marks; public: virtual ~ast_mark() {} bool is_marked(ast * n) const; virtual void mark(ast * n, bool flag); virtual void reset(); }; // ----------------------------------- // // scoped_mark // // ----------------------------------- /** \brief Class for scoped-based marking of asts. This class is safe with respect to life-times of asts. */ class scoped_mark : public ast_mark { ast_ref_vector m_stack; unsigned_vector m_lim; public: scoped_mark(ast_manager& m): m_stack(m) {} virtual ~scoped_mark() {} virtual void mark(ast * n, bool flag); virtual void reset(); void mark(ast * n); void push_scope(); void pop_scope(); void pop_scope(unsigned num_scopes); }; // ------------------------------------- // // inc_ref & dec_ref functors // // ------------------------------------- template class dec_ref_proc { ast_manager & m_manager; public: dec_ref_proc(ast_manager & m):m_manager(m) {} void operator()(AST * n) { m_manager.dec_ref(n); } }; template class inc_ref_proc { ast_manager & m_manager; public: inc_ref_proc(ast_manager & m):m_manager(m) {} void operator()(AST * n) { m_manager.inc_ref(n); } }; #endif /* _AST_H_ */ z3-4.4.0/src/ast/macros/0000755000175000017500000000000012540347414014507 5ustar michaelmichaelz3-4.4.0/src/ast/macros/macro_manager.cpp0000644000175000017500000002446112540347414020015 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: macro_manager.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-04-05. Revision History: Christoph Wintersteiger (t-cwinte), 2010-04-13: Added cycle detection for macro definitions Leonardo de Moura (leonardo) 2010-12-15: Moved dependency management to func_decl_dependencies.h --*/ #include"macro_manager.h" #include"for_each_expr.h" #include"var_subst.h" #include"ast_pp.h" #include"recurse_expr_def.h" macro_manager::macro_manager(ast_manager & m, simplifier & s): m_manager(m), m_simplifier(s), m_util(m, s), m_decls(m), m_macros(m), m_macro_prs(m), m_forbidden(m), m_deps(m) { m_util.set_forbidden_set(&m_forbidden_set); } macro_manager::~macro_manager() { } void macro_manager::push_scope() { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_decls_lim = m_decls.size(); s.m_forbidden_lim = m_forbidden.size(); } void macro_manager::pop_scope(unsigned num_scopes) { unsigned new_lvl = m_scopes.size() - num_scopes; scope & s = m_scopes[new_lvl]; restore_decls(s.m_decls_lim); restore_forbidden(s.m_forbidden_lim); m_scopes.shrink(new_lvl); } void macro_manager::restore_decls(unsigned old_sz) { unsigned sz = m_decls.size(); for (unsigned i = old_sz; i < sz; i++) { m_decl2macro.erase(m_decls.get(i)); m_deps.erase(m_decls.get(i)); if (m_manager.proofs_enabled()) m_decl2macro_pr.erase(m_decls.get(i)); } m_decls.shrink(old_sz); m_macros.shrink(old_sz); if (m_manager.proofs_enabled()) m_macro_prs.shrink(old_sz); } void macro_manager::restore_forbidden(unsigned old_sz) { unsigned sz = m_forbidden.size(); for (unsigned i = old_sz; i < sz; i++) m_forbidden_set.erase(m_forbidden.get(i)); m_forbidden.shrink(old_sz); } void macro_manager::reset() { m_decl2macro.reset(); m_decl2macro_pr.reset(); m_decls.reset(); m_macros.reset(); m_macro_prs.reset(); m_scopes.reset(); m_forbidden_set.reset(); m_forbidden.reset(); m_deps.reset(); } bool macro_manager::insert(func_decl * f, quantifier * m, proof * pr) { TRACE("macro_insert", tout << "trying to create macro: " << f->get_name() << "\n" << mk_pp(m, m_manager) << "\n";); // if we already have a macro for f then return false; if (m_decls.contains(f)) { TRACE("macro_insert", tout << "we already have a macro for: " << f->get_name() << "\n";); return false; } app * head; expr * definition; get_head_def(m, f, head, definition); func_decl_set * s = m_deps.mk_func_decl_set(); m_deps.collect_func_decls(definition, s); if (!m_deps.insert(f, s)) { return false; } // add macro m_decl2macro.insert(f, m); m_decls.push_back(f); m_macros.push_back(m); if (m_manager.proofs_enabled()) { m_macro_prs.push_back(pr); m_decl2macro_pr.insert(f, pr); } TRACE("macro_insert", tout << "A macro was successfully created for: " << f->get_name() << "\n";); // Nothing's forbidden anymore; if something's bad, we detected it earlier. // mark_forbidden(m->get_expr()); return true; } namespace macro_manager_ns { struct proc { obj_hashtable & m_forbidden_set; func_decl_ref_vector & m_forbidden; proc(obj_hashtable & s, func_decl_ref_vector & v):m_forbidden_set(s), m_forbidden(v) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { func_decl * d = n->get_decl(); if (n->get_num_args() > 0 && n->get_family_id() == null_family_id && !m_forbidden_set.contains(d)) { m_forbidden_set.insert(d); m_forbidden.push_back(d); } } }; }; /** \brief Mark all func_decls used in exprs as forbidden. */ void macro_manager::mark_forbidden(unsigned n, expr * const * exprs) { expr_mark visited; macro_manager_ns::proc p(m_forbidden_set, m_forbidden); for (unsigned i = 0; i < n; i++) for_each_expr(p, visited, exprs[i]); } void macro_manager::get_head_def(quantifier * q, func_decl * d, app * & head, expr * & def) const { app * body = to_app(q->get_expr()); SASSERT(m_manager.is_eq(body) || m_manager.is_iff(body)); expr * lhs = to_app(body)->get_arg(0); expr * rhs = to_app(body)->get_arg(1); SASSERT(is_app_of(lhs, d) || is_app_of(rhs, d)); SASSERT(!is_app_of(lhs, d) || !is_app_of(rhs, d)); if (is_app_of(lhs, d)) { head = to_app(lhs); def = rhs; } else { head = to_app(rhs); def = lhs; } } void macro_manager::display(std::ostream & out) { unsigned sz = m_decls.size(); for (unsigned i = 0; i < sz; i++) { func_decl * f = m_decls.get(i); quantifier * q = 0; m_decl2macro.find(f, q); app * head; expr * def; get_head_def(q, f, head, def); SASSERT(q); out << mk_pp(head, m_manager) << " ->\n" << mk_pp(def, m_manager) << "\n"; } } func_decl * macro_manager::get_macro_interpretation(unsigned i, expr_ref & interp) const { func_decl * f = m_decls.get(i); quantifier * q = m_macros.get(i); app * head; expr * def; get_head_def(q, f, head, def); TRACE("macro_bug", tout << f->get_name() << "\n" << mk_pp(head, m_manager) << "\n" << mk_pp(q, m_manager) << "\n";); m_util.mk_macro_interpretation(head, def, interp); return f; } macro_manager::macro_expander::macro_expander(ast_manager & m, macro_manager & mm, simplifier & s): simplifier(m), m_macro_manager(mm) { // REMARK: theory simplifier should not be used by macro_expander... // is_arith_macro rewrites a quantifer such as: // forall (x Int) (= (+ x (+ (f x) 1)) 2) // into // forall (x Int) (= (f x) (+ 1 (* -1 x))) // The goal is to make simple macro detection detect the arith macro. // The arith simplifier will undo this transformation. // borrow_plugins(s); enable_ac_support(false); } macro_manager::macro_expander::~macro_expander() { // release_plugins(); } void macro_manager::macro_expander::reduce1_quantifier(quantifier * q) { simplifier::reduce1_quantifier(q); // If a macro was expanded in a pattern, we must erase it since it may not be a valid pattern anymore. // The MAM assumes valid patterns, and it crashes if invalid patterns are provided. // For example, it will crash if the pattern does not contain all variables. // // Alternative solution: use pattern_validation to check if the pattern is still valid. // I'm not sure if this is a good solution, since the pattern may be meaningless after the macro expansion. // So, I'm just erasing them. expr * new_q_expr = 0; proof * new_q_pr = 0; get_cached(q, new_q_expr, new_q_pr); if (!is_quantifier(new_q_expr)) return; quantifier * new_q = to_quantifier(new_q_expr); bool erase_patterns = false; if (q->get_num_patterns() != new_q->get_num_patterns() || q->get_num_no_patterns() != new_q->get_num_no_patterns()) { erase_patterns = true; } else { for (unsigned i = 0; !erase_patterns && i < q->get_num_patterns(); i++) { if (q->get_pattern(i) != new_q->get_pattern(i)) erase_patterns = true; } for (unsigned i = 0; !erase_patterns && i < q->get_num_no_patterns(); i++) { if (q->get_no_pattern(i) != new_q->get_no_pattern(i)) erase_patterns = true; } } if (erase_patterns) { ast_manager & m = get_manager(); expr * new_new_q = m.update_quantifier(new_q, 0, 0, 0, 0, new_q->get_expr()); // we can use the same proof since new_new_q and new_q are identical modulo patterns/annotations cache_result(q, new_new_q, new_q_pr); } } bool macro_manager::macro_expander::get_subst(expr * _n, expr_ref & r, proof_ref & p) { if (!is_app(_n)) return false; app * n = to_app(_n); quantifier * q = 0; func_decl * d = n->get_decl(); TRACE("macro_manager_bug", tout << "trying to expand:\n" << mk_pp(n, m) << "\nd:\n" << d->get_name() << "\n";); if (m_macro_manager.m_decl2macro.find(d, q)) { TRACE("macro_manager", tout << "expanding: " << mk_pp(n, m) << "\n";); app * head = 0; expr * def = 0; m_macro_manager.get_head_def(q, d, head, def); unsigned num = n->get_num_args(); SASSERT(head && def); ptr_buffer subst_args; subst_args.resize(num, 0); for (unsigned i = 0; i < num; i++) { var * v = to_var(head->get_arg(i)); SASSERT(v->get_idx() < num); unsigned nidx = num - v->get_idx() - 1; SASSERT(subst_args[nidx] == 0); subst_args[nidx] = n->get_arg(i); } var_subst s(m); s(def, num, subst_args.c_ptr(), r); if (m.proofs_enabled()) { expr_ref instance(m); s(q->get_expr(), num, subst_args.c_ptr(), instance); proof * qi_pr = m.mk_quant_inst(m.mk_or(m.mk_not(q), instance), num, subst_args.c_ptr()); proof * q_pr = 0; m_macro_manager.m_decl2macro_pr.find(d, q_pr); SASSERT(q_pr != 0); proof * prs[2] = { qi_pr, q_pr }; p = m.mk_unit_resolution(2, prs); } else { p = 0; } return true; } return false; } void macro_manager::expand_macros(expr * n, proof * pr, expr_ref & r, proof_ref & new_pr) { if (has_macros()) { // Expand macros with "real" proof production support (NO rewrite*) expr_ref old_n(m_manager); proof_ref old_pr(m_manager); old_n = n; old_pr = pr; for (;;) { macro_expander proc(m_manager, *this, m_simplifier); proof_ref n_eq_r_pr(m_manager); TRACE("macro_manager_bug", tout << "expand_macros:\n" << mk_pp(n, m_manager) << "\n";); proc(old_n, r, n_eq_r_pr); new_pr = m_manager.mk_modus_ponens(old_pr, n_eq_r_pr); if (r.get() == old_n.get()) return; old_n = r; old_pr = new_pr; } } else { r = n; new_pr = pr; } } z3-4.4.0/src/ast/macros/quasi_macros.cpp0000644000175000017500000002564012540347414017710 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: quasi_macros.cpp Abstract: Author: Christoph Wintersteiger (t-cwinte) 2010-04-23 Revision History: --*/ #include"quasi_macros.h" #include"for_each_expr.h" #include"ast_pp.h" #include"uint_set.h" #include"var_subst.h" quasi_macros::quasi_macros(ast_manager & m, macro_manager & mm, simplifier & s) : m_manager(m), m_macro_manager(mm), m_simplifier(s), m_new_vars(m), m_new_eqs(m), m_new_qsorts(m) { } quasi_macros::~quasi_macros() { } void quasi_macros::find_occurrences(expr * e) { unsigned j; m_todo.reset(); m_todo.push_back(e); // we remember whether we have seen an expr once, or more than once; // when we see it the second time, we don't have to visit it another time, // as we are only interested in finding unique function applications. m_visited_once.reset(); m_visited_more.reset(); while (!m_todo.empty()) { expr * cur = m_todo.back(); m_todo.pop_back(); if (m_visited_more.is_marked(cur)) continue; if (m_visited_once.is_marked(cur)) m_visited_more.mark(cur, true); m_visited_once.mark(cur, true); switch (cur->get_kind()) { case AST_VAR: break; case AST_QUANTIFIER: m_todo.push_back(to_quantifier(cur)->get_expr()); break; case AST_APP: if (is_non_ground_uninterp(cur)) { func_decl * f = to_app(cur)->get_decl(); m_occurrences.insert_if_not_there(f, 0); occurrences_map::iterator it = m_occurrences.find_iterator(f); it->m_value++; } j = to_app(cur)->get_num_args(); while (j) m_todo.push_back(to_app(cur)->get_arg(--j)); break; default: UNREACHABLE(); } } }; bool quasi_macros::is_non_ground_uninterp(expr const * e) const { return is_non_ground(e) && is_uninterp(e); } bool quasi_macros::is_unique(func_decl * f) const { return m_occurrences.find(f) == 1; } struct var_dep_proc { bit_vector m_bitset; public: var_dep_proc(quantifier * q) { m_bitset.resize(q->get_num_decls(), false); } void operator()(var * n) { m_bitset.set(n->get_idx(), true); } void operator()(quantifier * n) {} void operator()(app * n) {} bool all_used(void) { for (unsigned i = 0; i < m_bitset.size() ; i++) if (!m_bitset.get(i)) return false; return true; } }; bool quasi_macros::fully_depends_on(app * a, quantifier * q) const { // CMW: This checks whether all variables in q are used _somewhere_ deep down in the children of a /* var_dep_proc proc(q); for_each_expr(proc, a); return proc.all_used(); */ // CMW: This code instead checks that all variables appear at least once as a // direct argument of a, i.e., a->get_arg(i) == v for some i bit_vector bitset; bitset.resize(q->get_num_decls(), false); for (unsigned i = 0 ; i < a->get_num_args() ; i++) { if (is_var(a->get_arg(i))) bitset.set(to_var(a->get_arg(i))->get_idx(), true); } for (unsigned i = 0; i < bitset.size() ; i++) { if (!bitset.get(i)) return false; } return true; } bool quasi_macros::depends_on(expr * e, func_decl * f) const { ptr_vector todo; expr_mark visited; todo.push_back(e); while(!todo.empty()) { expr * cur = todo.back(); todo.pop_back(); if (visited.is_marked(cur)) continue; if (is_app(cur)) { app * a = to_app(cur); if (a->get_decl() == f) return true; unsigned j = a->get_num_args(); while (j>0) todo.push_back(a->get_arg(--j)); } visited.mark(cur, true); } return false; } bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const { // Our definition of a quasi-macro: // Forall X. f[X] = T[X], where f[X] is a term starting with symbol f, f is uninterpreted, // f[X] contains all universally quantified variables, and f does not occur in T[X]. TRACE("quasi_macros", tout << "Checking for quasi macro: " << mk_pp(e, m_manager) << std::endl;); if (is_quantifier(e) && to_quantifier(e)->is_forall()) { quantifier * q = to_quantifier(e); expr * qe = q->get_expr(); if ((m_manager.is_eq(qe) || m_manager.is_iff(qe))) { expr * lhs = to_app(qe)->get_arg(0); expr * rhs = to_app(qe)->get_arg(1); if (is_non_ground_uninterp(lhs) && is_unique(to_app(lhs)->get_decl()) && !depends_on(rhs, to_app(lhs)->get_decl()) && fully_depends_on(to_app(lhs), q)) { a = to_app(lhs); t = rhs; return true; } else if (is_non_ground_uninterp(rhs) && is_unique(to_app(rhs)->get_decl()) && !depends_on(lhs, to_app(rhs)->get_decl()) && fully_depends_on(to_app(rhs), q)) { a = to_app(rhs); t = lhs; return true; } } else if (m_manager.is_not(qe) && is_non_ground_uninterp(to_app(qe)->get_arg(0)) && is_unique(to_app(to_app(qe)->get_arg(0))->get_decl())) { // this is like f(...) = false a = to_app(to_app(qe)->get_arg(0)); t = m_manager.mk_false(); return true; } else if (is_non_ground_uninterp(qe) && is_unique(to_app(qe)->get_decl())) { // this is like f(...) = true a = to_app(qe); t = m_manager.mk_true(); return true; } } return false; } void quasi_macros::quasi_macro_to_macro(quantifier * q, app * a, expr * t, quantifier_ref & macro) { m_new_var_names.reset(); m_new_vars.reset(); m_new_qsorts.reset(); m_new_eqs.reset(); func_decl * f = a->get_decl(); // CMW: we rely on the fact that all variables in q appear at least once as // a direct argument of `a'. bit_vector v_seen; v_seen.resize(q->get_num_decls(), false); for (unsigned i = 0 ; i < a->get_num_args() ; i++) { if (!is_var(a->get_arg(i)) || v_seen.get(to_var(a->get_arg(i))->get_idx())) { unsigned inx = m_new_var_names.size(); m_new_name.str(""); m_new_name << "X" << inx; m_new_var_names.push_back(symbol(m_new_name.str().c_str())); m_new_qsorts.push_back(f->get_domain()[i]); m_new_vars.push_back(m_manager.mk_var(inx + q->get_num_decls(), f->get_domain()[i])); m_new_eqs.push_back(m_manager.mk_eq(m_new_vars.back(), a->get_arg(i))); } else { var * v = to_var(a->get_arg(i)); m_new_vars.push_back(v); v_seen.set(v->get_idx(), true); } } // Reverse the new variable names and sorts. [CMW: There is a smarter way to do this.] vector new_var_names_rev; sort_ref_vector new_qsorts_rev(m_manager); unsigned i = m_new_var_names.size(); while (i > 0) { i--; new_var_names_rev.push_back(m_new_var_names.get(i)); new_qsorts_rev.push_back(m_new_qsorts.get(i)); } // We want to keep all the old variables [already reversed] for (unsigned i = 0 ; i < q->get_num_decls() ; i++) { new_var_names_rev.push_back(q->get_decl_name(i)); new_qsorts_rev.push_back(q->get_decl_sort(i)); } // Macro := Forall m_new_vars . appl = ITE( m_new_eqs, t, f_else) app_ref appl(m_manager); expr_ref eq(m_manager); appl = m_manager.mk_app(f, m_new_vars.size(), m_new_vars.c_ptr()); func_decl * fd = m_manager.mk_fresh_func_decl(f->get_name(), symbol("else"), f->get_arity(), f->get_domain(), f->get_range()); expr * f_else = m_manager.mk_app(fd, m_new_vars.size(), m_new_vars.c_ptr()); expr_ref ite(m_manager); ite = m_manager.mk_ite(m_manager.mk_and(m_new_eqs.size(), m_new_eqs.c_ptr()), t, f_else); eq = m_manager.mk_eq(appl, ite); macro = m_manager.mk_quantifier(true, new_var_names_rev.size(), new_qsorts_rev.c_ptr(), new_var_names_rev.c_ptr(), eq); } bool quasi_macros::find_macros(unsigned n, expr * const * exprs) { TRACE("quasi_macros", tout << "Finding quasi-macros in: " << std::endl; for (unsigned i = 0 ; i < n ; i++) tout << i << ": " << mk_pp(exprs[i], m_manager) << std::endl; ); bool res = false; m_occurrences.reset(); // Find out how many non-ground appearences for each uninterpreted function there are for ( unsigned i = 0 ; i < n ; i++ ) find_occurrences(exprs[i]); TRACE("quasi_macros", tout << "Occurrences: " << std::endl; for (occurrences_map::iterator it = m_occurrences.begin(); it != m_occurrences.end(); it++) tout << it->m_key->get_name() << ": " << it->m_value << std::endl; ); // Find all macros for ( unsigned i = 0 ; i < n ; i++ ) { app_ref a(m_manager); expr_ref t(m_manager); if (is_quasi_macro(exprs[i], a, t)) { quantifier_ref macro(m_manager); quasi_macro_to_macro(to_quantifier(exprs[i]), a, t, macro); TRACE("quasi_macros", tout << "Found quasi macro: " << mk_pp(exprs[i], m_manager) << std::endl; tout << "Macro: " << mk_pp(macro, m_manager) << std::endl; ); proof * pr = 0; if (m_manager.proofs_enabled()) pr = m_manager.mk_def_axiom(macro); if (m_macro_manager.insert(a->get_decl(), macro, pr)) res = true; } } return res; } void quasi_macros::apply_macros(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) { for ( unsigned i = 0 ; i < n ; i++ ) { expr_ref r(m_manager), rs(m_manager); proof_ref pr(m_manager), ps(m_manager); proof * p = m_manager.proofs_enabled() ? prs[i] : 0; m_macro_manager.expand_macros(exprs[i], p, r, pr); m_simplifier(r, rs, ps); new_exprs.push_back(rs); new_prs.push_back(ps); } } bool quasi_macros::operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) { if (find_macros(n, exprs)) { apply_macros(n, exprs, prs, new_exprs, new_prs); return true; } else { // just copy them over for ( unsigned i = 0 ; i < n ; i++ ) { new_exprs.push_back(exprs[i]); if (m_manager.proofs_enabled()) new_prs.push_back(prs[i]); } return false; } } z3-4.4.0/src/ast/macros/macro_util.cpp0000644000175000017500000007514712540347414017367 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: macro_util.cpp Abstract: Macro finding goodies. They are used during preprocessing (MACRO_FINDER=true), and model building. Author: Leonardo de Moura (leonardo) 2010-12-15. Revision History: --*/ #include"macro_util.h" #include"occurs.h" #include"arith_simplifier_plugin.h" #include"basic_simplifier_plugin.h" #include"bv_simplifier_plugin.h" #include"var_subst.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"ast_util.h" #include"for_each_expr.h" #include"well_sorted.h" macro_util::macro_util(ast_manager & m, simplifier & s): m_manager(m), m_simplifier(s), m_arith_simp(0), m_bv_simp(0), m_basic_simp(0), m_forbidden_set(0), m_curr_clause(0) { } arith_simplifier_plugin * macro_util::get_arith_simp() const { if (m_arith_simp == 0) { const_cast(this)->m_arith_simp = static_cast(m_simplifier.get_plugin(m_manager.mk_family_id("arith"))); } SASSERT(m_arith_simp != 0); return m_arith_simp; } bv_simplifier_plugin * macro_util::get_bv_simp() const { if (m_bv_simp == 0) { const_cast(this)->m_bv_simp = static_cast(m_simplifier.get_plugin(m_manager.mk_family_id("bv"))); } SASSERT(m_bv_simp != 0); return m_bv_simp; } basic_simplifier_plugin * macro_util::get_basic_simp() const { if (m_basic_simp == 0) { const_cast(this)->m_basic_simp = static_cast(m_simplifier.get_plugin(m_manager.get_basic_family_id())); } SASSERT(m_basic_simp != 0); return m_basic_simp; } bool macro_util::is_bv(expr * n) const { return get_bv_simp()->is_bv(n); } bool macro_util::is_bv_sort(sort * s) const { return get_bv_simp()->is_bv_sort(s); } bool macro_util::is_add(expr * n) const { return get_arith_simp()->is_add(n) || get_bv_simp()->is_add(n); } bool macro_util::is_times_minus_one(expr * n, expr * & arg) const { return get_arith_simp()->is_times_minus_one(n, arg) || get_bv_simp()->is_times_minus_one(n, arg); } bool macro_util::is_le(expr * n) const { return get_arith_simp()->is_le(n) || get_bv_simp()->is_le(n); } bool macro_util::is_le_ge(expr * n) const { return get_arith_simp()->is_le_ge(n) || get_bv_simp()->is_le_ge(n); } poly_simplifier_plugin * macro_util::get_poly_simp_for(sort * s) const { if (is_bv_sort(s)) return get_bv_simp(); else return get_arith_simp(); } app * macro_util::mk_zero(sort * s) const { poly_simplifier_plugin * ps = get_poly_simp_for(s); ps->set_curr_sort(s); return ps->mk_zero(); } void macro_util::mk_sub(expr * t1, expr * t2, expr_ref & r) const { if (is_bv(t1)) { get_bv_simp()->mk_sub(t1, t2, r); } else { get_arith_simp()->mk_sub(t1, t2, r); } } void macro_util::mk_add(expr * t1, expr * t2, expr_ref & r) const { if (is_bv(t1)) { get_bv_simp()->mk_add(t1, t2, r); } else { get_arith_simp()->mk_add(t1, t2, r); } } void macro_util::mk_add(unsigned num_args, expr * const * args, sort * s, expr_ref & r) const { if (num_args == 0) { r = mk_zero(s); return; } poly_simplifier_plugin * ps = get_poly_simp_for(s); ps->set_curr_sort(s); ps->mk_add(num_args, args, r); } /** \brief Return true if \c n is an application of the form (f x_{k_1}, ..., x_{k_n}) where f is uninterpreted n == num_decls x_{k_i}'s are variables and {k_1, ..., k_n } is equals to the set {0, ..., num_decls-1} */ bool macro_util::is_macro_head(expr * n, unsigned num_decls) const { if (is_app(n) && !to_app(n)->get_decl()->is_associative() && to_app(n)->get_family_id() == null_family_id && to_app(n)->get_num_args() == num_decls) { sbuffer var2pos; var2pos.resize(num_decls, -1); for (unsigned i = 0; i < num_decls; i++) { expr * c = to_app(n)->get_arg(i); if (!is_var(c)) return false; unsigned idx = to_var(c)->get_idx(); if (idx >= num_decls || var2pos[idx] != -1) return false; var2pos[idx] = i; } return true; } return false; } /** \brief Return true if n is of the form (= (f x_{k_1}, ..., x_{k_n}) t) OR (iff (f x_{k_1}, ..., x_{k_n}) t) where is_macro_head((f x_{k_1}, ..., x_{k_n})) returns true AND t does not contain f AND f is not in forbidden_set In case of success head will contain (f x_{k_1}, ..., x_{k_n}) AND def will contain t */ bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const { if (m_manager.is_eq(n) || m_manager.is_iff(n)) { expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); if (is_macro_head(lhs, num_decls) && !is_forbidden(to_app(lhs)->get_decl()) && !occurs(to_app(lhs)->get_decl(), rhs)) { head = to_app(lhs); def = rhs; return true; } } return false; } /** \brief Return true if n is of the form (= t (f x_{k_1}, ..., x_{k_n})) OR (iff t (f x_{k_1}, ..., x_{k_n})) where is_macro_head((f x_{k_1}, ..., x_{k_n})) returns true AND t does not contain f AND f is not in forbidden_set In case of success head will contain (f x_{k_1}, ..., x_{k_n}) AND def will contain t */ bool macro_util::is_right_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const { if (m_manager.is_eq(n) || m_manager.is_iff(n)) { expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); if (is_macro_head(rhs, num_decls) && !is_forbidden(to_app(rhs)->get_decl()) && !occurs(to_app(rhs)->get_decl(), lhs)) { head = to_app(rhs); def = lhs; return true; } } return false; } /** \brief Return true if n contains f. The method ignores the sub-expression \c exception. \remark n is a "polynomial". */ bool macro_util::poly_contains_head(expr * n, func_decl * f, expr * exception) const { unsigned num_args; expr * const * args; if (is_add(n)) { num_args = to_app(n)->get_num_args(); args = to_app(n)->get_args(); } else { num_args = 1; args = &n; } for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (arg != exception && occurs(f, arg)) return true; } return false; } bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def, bool & inv) const { // TODO: obsolete... we should move to collect_arith_macro_candidates arith_simplifier_plugin * as = get_arith_simp(); if (!m_manager.is_eq(n) && !as->is_le(n) && !as->is_ge(n)) return false; expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); if (!as->is_numeral(rhs)) return false; inv = false; ptr_buffer args; expr * h = 0; unsigned lhs_num_args; expr * const * lhs_args; if (is_add(lhs)) { lhs_num_args = to_app(lhs)->get_num_args(); lhs_args = to_app(lhs)->get_args(); } else { lhs_num_args = 1; lhs_args = &lhs; } for (unsigned i = 0; i < lhs_num_args; i++) { expr * arg = lhs_args[i]; expr * neg_arg; if (h == 0 && is_macro_head(arg, num_decls) && !is_forbidden(to_app(arg)->get_decl()) && !poly_contains_head(lhs, to_app(arg)->get_decl(), arg)) { h = arg; } else if (h == 0 && as->is_times_minus_one(arg, neg_arg) && is_macro_head(neg_arg, num_decls) && !is_forbidden(to_app(neg_arg)->get_decl()) && !poly_contains_head(lhs, to_app(neg_arg)->get_decl(), arg)) { h = neg_arg; inv = true; } else { args.push_back(arg); } } if (h == 0) return false; head = to_app(h); expr_ref tmp(m_manager); as->mk_add(args.size(), args.c_ptr(), tmp); if (inv) as->mk_sub(tmp, rhs, def); else as->mk_sub(rhs, tmp, def); return true; } /** \brief Auxiliary function for is_pseudo_predicate_macro. It detects the pattern (= (f X) t) */ bool macro_util::is_pseudo_head(expr * n, unsigned num_decls, app * & head, app * & t) { if (!m_manager.is_eq(n)) return false; expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); if (!is_ground(lhs) && !is_ground(rhs)) return false; sort * s = m_manager.get_sort(lhs); if (m_manager.is_uninterp(s)) return false; sort_size sz = s->get_num_elements(); if (sz.is_finite() && sz.size() == 1) return false; if (is_macro_head(lhs, num_decls)) { head = to_app(lhs); t = to_app(rhs); return true; } if (is_macro_head(rhs, num_decls)) { head = to_app(rhs); t = to_app(lhs); return true; } return false; } /** \brief Returns true if n if of the form (forall (X) (iff (= (f X) t) def[X])) where t is a ground term, (f X) is the head. */ bool macro_util::is_pseudo_predicate_macro(expr * n, app * & head, app * & t, expr * & def) { if (!is_quantifier(n) || !to_quantifier(n)->is_forall()) return false; TRACE("macro_util", tout << "processing: " << mk_pp(n, m_manager) << "\n";); expr * body = to_quantifier(n)->get_expr(); unsigned num_decls = to_quantifier(n)->get_num_decls(); if (!m_manager.is_iff(body)) return false; expr * lhs = to_app(body)->get_arg(0); expr * rhs = to_app(body)->get_arg(1); if (is_pseudo_head(lhs, num_decls, head, t) && !is_forbidden(head->get_decl()) && !occurs(head->get_decl(), rhs)) { def = rhs; return true; } if (is_pseudo_head(rhs, num_decls, head, t) && !is_forbidden(head->get_decl()) && !occurs(head->get_decl(), lhs)) { def = lhs; return true; } return false; } /** \brief A quasi-macro head is of the form f[X_1, ..., X_n], where n == num_decls, f[X_1, ..., X_n] is a term starting with symbol f, f is uninterpreted, contains all universally quantified variables as arguments. Note that, some arguments of f[X_1, ..., X_n] may not be variables. Examples of quasi-macros: f(x_1, x_1 + x_2, x_2) for num_decls == 2 g(x_1, x_1) for num_decls == 1 Return true if \c n is a quasi-macro. Store the macro head in \c head, and the conditions to apply the macro in \c cond. */ bool macro_util::is_quasi_macro_head(expr * n, unsigned num_decls) const { if (is_app(n) && to_app(n)->get_family_id() == null_family_id && to_app(n)->get_num_args() >= num_decls) { unsigned num_args = to_app(n)->get_num_args(); sbuffer found_vars; found_vars.resize(num_decls, false); unsigned num_found_vars = 0; for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(n)->get_arg(i); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (idx >= num_decls) return false; if (found_vars[idx] == false) { found_vars[idx] = true; num_found_vars++; } } else { if (occurs(to_app(n)->get_decl(), arg)) return false; } } return num_found_vars == num_decls; } return false; } /** \brief Convert a quasi-macro head into a macro head, and store the conditions under which it is valid in cond. */ void macro_util::quasi_macro_head_to_macro_head(app * qhead, unsigned num_decls, app_ref & head, expr_ref & cond) const { unsigned num_args = qhead->get_num_args(); sbuffer found_vars; found_vars.resize(num_decls, false); ptr_buffer new_args; ptr_buffer new_conds; unsigned next_var_idx = num_decls; for (unsigned i = 0; i < num_args; i++) { expr * arg = qhead->get_arg(i); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); SASSERT(idx < num_decls); if (found_vars[idx] == false) { found_vars[idx] = true; new_args.push_back(arg); continue; } } var * new_var = m_manager.mk_var(next_var_idx, m_manager.get_sort(arg)); next_var_idx++; expr * new_cond = m_manager.mk_eq(new_var, arg); new_args.push_back(new_var); new_conds.push_back(new_cond); } get_basic_simp()->mk_and(new_conds.size(), new_conds.c_ptr(), cond); head = m_manager.mk_app(qhead->get_decl(), new_args.size(), new_args.c_ptr()); } /** \brief Given a macro defined by head and def, stores an interpretation for head->get_decl() in interp. This method assumes is_macro_head(head, head->get_num_args()) returns true, and def does not contain head->get_decl(). See normalize_expr */ void macro_util::mk_macro_interpretation(app * head, expr * def, expr_ref & interp) const { SASSERT(is_macro_head(head, head->get_num_args())); SASSERT(!occurs(head->get_decl(), def)); normalize_expr(head, def, interp); } /** \brief The variables in head may be in the wrong order. Example: f(x_1, x_0) instead of f(x_0, x_1) This method is essentially renaming the variables in t. Suppose t is g(x_1, x_0 + x_1) This method will store g(x_0, x_1 + x_0) in norm_t. f(x_1, x_2) --> f(x_0, x_1) f(x_3, x_2) --> f(x_0, x_1) */ void macro_util::normalize_expr(app * head, expr * t, expr_ref & norm_t) const { expr_ref_buffer var_mapping(m_manager); bool changed = false; unsigned num_args = head->get_num_args(); unsigned max = num_args; for (unsigned i = 0; i < num_args; i++) { var * v = to_var(head->get_arg(i)); if (v->get_idx() >= max) max = v->get_idx() + 1; } TRACE("normalize_expr_bug", tout << "head: " << mk_pp(head, m_manager) << "\n"; tout << "applying substitution to:\n" << mk_bounded_pp(t, m_manager) << "\n";); for (unsigned i = 0; i < num_args; i++) { var * v = to_var(head->get_arg(i)); if (v->get_idx() != i) { changed = true; var * new_var = m_manager.mk_var(i, v->get_sort()); CTRACE("normalize_expr_bug", v->get_idx() >= num_args, tout << mk_pp(v, m_manager) << ", num_args: " << num_args << "\n";); SASSERT(v->get_idx() < max); var_mapping.setx(max - v->get_idx() - 1, new_var); } else { var_mapping.setx(max - i - 1, v); } } if (changed) { // REMARK: t may have nested quantifiers... So, I must use the std order for variable substitution. var_subst subst(m_manager); TRACE("macro_util_bug", tout << "head: " << mk_pp(head, m_manager) << "\n"; tout << "applying substitution to:\n" << mk_ll_pp(t, m_manager) << "\nsubstitituion:\n"; for (unsigned i = 0; i < var_mapping.size(); i++) { tout << "#" << i << " -> " << mk_pp(var_mapping[i], m_manager) << "\n"; }); subst(t, var_mapping.size(), var_mapping.c_ptr(), norm_t); } else { norm_t = t; } } // ----------------------------- // // "Hint" support // See comment at is_hint_atom // for a definition of what a hint is. // // ----------------------------- bool is_hint_head(expr * n, ptr_buffer & vars) { if (!is_app(n)) return false; if (to_app(n)->get_decl()->is_associative() || to_app(n)->get_family_id() != null_family_id) return false; unsigned num_args = to_app(n)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(n)->get_arg(i); if (is_var(arg)) vars.push_back(to_var(arg)); } return !vars.empty(); } /** \brief Returns true if the variables in n is a subset of \c vars. */ bool vars_of_is_subset(expr * n, ptr_buffer const & vars) { if (is_ground(n)) return true; obj_hashtable visited; ptr_buffer todo; todo.push_back(n); while (!todo.empty()) { expr * curr = todo.back(); todo.pop_back(); if (is_var(curr)) { if (std::find(vars.begin(), vars.end(), to_var(curr)) == vars.end()) return false; } else if (is_app(curr)) { unsigned num_args = to_app(curr)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(curr)->get_arg(i); if (is_ground(arg)) continue; if (visited.contains(arg)) continue; visited.insert(arg); todo.push_back(arg); } } else { SASSERT(is_quantifier(curr)); return false; // do no support nested quantifier... being conservative. } } return true; } /** \brief (= lhs rhs) is a hint atom if lhs is of the form (f t_1 ... t_n) and all variables occurring in rhs are direct arguments of lhs. */ bool is_hint_atom(expr * lhs, expr * rhs) { ptr_buffer vars; if (!is_hint_head(lhs, vars)) return false; return !occurs(to_app(lhs)->get_decl(), rhs) && vars_of_is_subset(rhs, vars); } void hint_to_macro_head(ast_manager & m, app * head, unsigned num_decls, app_ref & new_head) { unsigned num_args = head->get_num_args(); ptr_buffer new_args; sbuffer found_vars; found_vars.resize(num_decls, false); unsigned next_var_idx = num_decls; for (unsigned i = 0; i < num_args; i++) { expr * arg = head->get_arg(i); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); SASSERT(idx < num_decls); if (found_vars[idx] == false) { found_vars[idx] = true; new_args.push_back(arg); continue; } } var * new_var = m.mk_var(next_var_idx, m.get_sort(arg)); next_var_idx++; new_args.push_back(new_var); } new_head = m.mk_app(head->get_decl(), new_args.size(), new_args.c_ptr()); } /** \brief Return true if n can be viewed as a polynomial "hint" based on head. That is, n (but the monomial exception) only uses the variables in head, and does not use head->get_decl(). is_hint_head(head, vars) must also return true */ bool macro_util::is_poly_hint(expr * n, app * head, expr * exception) { TRACE("macro_util_hint", tout << "is_poly_hint n:\n" << mk_pp(n, m_manager) << "\nhead:\n" << mk_pp(head, m_manager) << "\nexception:\n"; if (exception) tout << mk_pp(exception, m_manager); else tout << ""; tout << "\n";); ptr_buffer vars; if (!is_hint_head(head, vars)) { TRACE("macro_util_hint", tout << "failed because head is not hint head\n";); return false; } func_decl * f = head->get_decl(); unsigned num_args; expr * const * args; if (is_add(n)) { num_args = to_app(n)->get_num_args(); args = to_app(n)->get_args(); } else { num_args = 1; args = &n; } for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (arg != exception && (occurs(f, arg) || !vars_of_is_subset(arg, vars))) { TRACE("macro_util_hint", tout << "failed because of:\n" << mk_pp(arg, m_manager) << "\n";); return false; } } TRACE("macro_util_hint", tout << "succeeded\n";); return true; } // ----------------------------- // // Macro candidates // // ----------------------------- macro_util::macro_candidates::macro_candidates(ast_manager & m): m_defs(m), m_conds(m) { } void macro_util::macro_candidates::reset() { m_fs.reset(); m_defs.reset(); m_conds.reset(); m_ineq.reset(); m_satisfy.reset(); m_hint.reset(); } void macro_util::macro_candidates::insert(func_decl * f, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint) { m_fs.push_back(f); m_defs.push_back(def); m_conds.push_back(cond); m_ineq.push_back(ineq); m_satisfy.push_back(satisfy_atom); m_hint.push_back(hint); } // ----------------------------- // // Macro util // // ----------------------------- void macro_util::insert_macro(app * head, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, macro_candidates & r) { expr_ref norm_def(m_manager); expr_ref norm_cond(m_manager); normalize_expr(head, def, norm_def); if (cond != 0) normalize_expr(head, cond, norm_cond); else if (!hint) norm_cond = m_manager.mk_true(); SASSERT(!hint || norm_cond.get() == 0); r.insert(head->get_decl(), norm_def.get(), norm_cond.get(), ineq, satisfy_atom, hint); } void macro_util::insert_quasi_macro(app * head, unsigned num_decls, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, macro_candidates & r) { if (!is_macro_head(head, head->get_num_args())) { app_ref new_head(m_manager); expr_ref extra_cond(m_manager); expr_ref new_cond(m_manager); if (!hint) { quasi_macro_head_to_macro_head(head, num_decls, new_head, extra_cond); if (cond == 0) new_cond = extra_cond; else get_basic_simp()->mk_and(cond, extra_cond, new_cond); } else { hint_to_macro_head(m_manager, head, num_decls, new_head); } insert_macro(new_head, def, new_cond, ineq, satisfy_atom, hint, r); } else { insert_macro(head, def, cond, ineq, satisfy_atom, hint, r); } } bool macro_util::rest_contains_decl(func_decl * f, expr * except_lit) { if (m_curr_clause == 0) return false; SASSERT(is_clause(m_manager, m_curr_clause)); unsigned num_lits = get_clause_num_literals(m_manager, m_curr_clause); for (unsigned i = 0; i < num_lits; i++) { expr * l = get_clause_literal(m_manager, m_curr_clause, i); if (l != except_lit && occurs(f, l)) return true; } return false; } void macro_util::get_rest_clause_as_cond(expr * except_lit, expr_ref & extra_cond) { if (m_curr_clause == 0) return; SASSERT(is_clause(m_manager, m_curr_clause)); basic_simplifier_plugin * bs = get_basic_simp(); expr_ref_buffer neg_other_lits(m_manager); unsigned num_lits = get_clause_num_literals(m_manager, m_curr_clause); for (unsigned i = 0; i < num_lits; i++) { expr * l = get_clause_literal(m_manager, m_curr_clause, i); if (l != except_lit) { expr_ref neg_l(m_manager); bs->mk_not(l, neg_l); neg_other_lits.push_back(neg_l); } } if (neg_other_lits.empty()) return; get_basic_simp()->mk_and(neg_other_lits.size(), neg_other_lits.c_ptr(), extra_cond); } void macro_util::collect_poly_args(expr * n, expr * exception, ptr_buffer & args) { args.reset(); unsigned num_args; expr * const * _args; if (is_add(n)) { num_args = to_app(n)->get_num_args(); _args = to_app(n)->get_args(); } else { num_args = 1; _args = &n; } for (unsigned i = 0; i < num_args; i++) { expr * arg = _args[i]; if (arg != exception) args.push_back(arg); } } void macro_util::add_arith_macro_candidate(app * head, unsigned num_decls, expr * def, expr * atom, bool ineq, bool hint, macro_candidates & r) { expr_ref cond(m_manager); if (!hint) get_rest_clause_as_cond(atom, cond); insert_quasi_macro(head, num_decls, def, cond, ineq, true, hint, r); } void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * atom, unsigned num_decls, bool is_ineq, macro_candidates & r) { if (!is_add(lhs) && m_manager.is_eq(atom)) // this case is a simple macro. return; ptr_buffer args; unsigned lhs_num_args; expr * const * lhs_args; if (is_add(lhs)) { lhs_num_args = to_app(lhs)->get_num_args(); lhs_args = to_app(lhs)->get_args(); } else { lhs_num_args = 1; lhs_args = &lhs; } for (unsigned i = 0; i < lhs_num_args; i++) { expr * arg = lhs_args[i]; expr * neg_arg; if (!is_app(arg)) continue; func_decl * f = to_app(arg)->get_decl(); bool _is_arith_macro = is_quasi_macro_head(arg, num_decls) && !is_forbidden(f) && !poly_contains_head(lhs, f, arg) && !occurs(f, rhs) && !rest_contains_decl(f, atom); bool _is_poly_hint = !_is_arith_macro && is_poly_hint(lhs, to_app(arg), arg); if (_is_arith_macro || _is_poly_hint) { collect_poly_args(lhs, arg, args); expr_ref rest(m_manager); mk_add(args.size(), args.c_ptr(), m_manager.get_sort(arg), rest); expr_ref def(m_manager); mk_sub(rhs, rest, def); // If is_poly_hint, rhs may contain variables that do not occur in to_app(arg). // So, we should re-check. if (!_is_poly_hint || is_poly_hint(def, to_app(arg), 0)) add_arith_macro_candidate(to_app(arg), num_decls, def, atom, is_ineq, _is_poly_hint, r); } else if (is_times_minus_one(arg, neg_arg) && is_app(neg_arg)) { f = to_app(neg_arg)->get_decl(); bool _is_arith_macro = is_quasi_macro_head(neg_arg, num_decls) && !is_forbidden(f) && !poly_contains_head(lhs, f, arg) && !occurs(f, rhs) && !rest_contains_decl(f, atom); bool _is_poly_hint = !_is_arith_macro && is_poly_hint(lhs, to_app(neg_arg), arg); if (_is_arith_macro || _is_poly_hint) { collect_poly_args(lhs, arg, args); expr_ref rest(m_manager); mk_add(args.size(), args.c_ptr(), m_manager.get_sort(arg), rest); expr_ref def(m_manager); mk_sub(rest, rhs, def); // If is_poly_hint, rhs may contain variables that do not occur in to_app(neg_arg). // So, we should re-check. if (!_is_poly_hint || is_poly_hint(def, to_app(neg_arg), 0)) add_arith_macro_candidate(to_app(neg_arg), num_decls, def, atom, is_ineq, _is_poly_hint, r); } } } } void macro_util::collect_arith_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r) { TRACE("macro_util_hint", tout << "collect_arith_macro_candidates:\n" << mk_pp(atom, m_manager) << "\n";); if (!m_manager.is_eq(atom) && !is_le_ge(atom)) return; expr * lhs = to_app(atom)->get_arg(0); expr * rhs = to_app(atom)->get_arg(1); bool is_ineq = !m_manager.is_eq(atom); collect_arith_macro_candidates(lhs, rhs, atom, num_decls, is_ineq, r); collect_arith_macro_candidates(rhs, lhs, atom, num_decls, is_ineq, r); } /** \brief Collect macro candidates for atom \c atom. The candidates are stored in \c r. The following post-condition holds: for each i in [0, r.size() - 1] we have a conditional macro of the form r.get_cond(i) IMPLIES f(x_1, ..., x_n) = r.get_def(i) where f == r.get_fs(i) .., x_n), f is uninterpreted and x_1, ..., x_n are variables. r.get_cond(i) and r.get_defs(i) do not contain f or variables not in {x_1, ..., x_n} The idea is to use r.get_defs(i) as the interpretation for f in a model M whenever r.get_cond(i) Given a model M and values { v_1, ..., v_n } Let M' be M{x_1 -> v_1, ..., v_n -> v_n} Note that M'(f(x_1, ..., x_n)) = M(f)(v_1, ..., v_n) Then, IF we have that M(f)(v_1, ..., v_n) = M'(r.get_def(i)) AND M'(r.get_cond(i)) = true THEN M'(atom) = true That is, if the conditional macro is used then the atom is satisfied when M'(r.get_cond(i)) = true IF r.is_ineq(i) = false, then M(f)(v_1, ..., v_n) ***MUST BE*** M'(r.get_def(i)) whenever M'(r.get_cond(i)) = true IF r.satisfy_atom(i) = true, then we have the stronger property: Then, IF we have that (M'(r.get_cond(i)) = true IMPLIES M(f)(v_1, ..., v_n) = M'(r.get_def(i))) THEN M'(atom) = true */ void macro_util::collect_macro_candidates_core(expr * atom, unsigned num_decls, macro_candidates & r) { if (m_manager.is_eq(atom) || m_manager.is_iff(atom)) { expr * lhs = to_app(atom)->get_arg(0); expr * rhs = to_app(atom)->get_arg(1); if (is_quasi_macro_head(lhs, num_decls) && !is_forbidden(to_app(lhs)->get_decl()) && !occurs(to_app(lhs)->get_decl(), rhs) && !rest_contains_decl(to_app(lhs)->get_decl(), atom)) { expr_ref cond(m_manager); get_rest_clause_as_cond(atom, cond); insert_quasi_macro(to_app(lhs), num_decls, rhs, cond, false, true, false, r); } else if (is_hint_atom(lhs, rhs)) { insert_quasi_macro(to_app(lhs), num_decls, rhs, 0, false, true, true, r); } if (is_quasi_macro_head(rhs, num_decls) && !is_forbidden(to_app(rhs)->get_decl()) && !occurs(to_app(rhs)->get_decl(), lhs) && !rest_contains_decl(to_app(rhs)->get_decl(), atom)) { expr_ref cond(m_manager); get_rest_clause_as_cond(atom, cond); insert_quasi_macro(to_app(rhs), num_decls, lhs, cond, false, true, false, r); } else if (is_hint_atom(rhs, lhs)) { insert_quasi_macro(to_app(rhs), num_decls, lhs, 0, false, true, true, r); } } collect_arith_macro_candidates(atom, num_decls, r); } void macro_util::collect_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r) { m_curr_clause = 0; r.reset(); collect_macro_candidates_core(atom, num_decls, r); } void macro_util::collect_macro_candidates(quantifier * q, macro_candidates & r) { r.reset(); expr * n = q->get_expr(); if (has_quantifiers(n)) return; unsigned num_decls = q->get_num_decls(); SASSERT(m_curr_clause == 0); if (is_clause(m_manager, n)) { m_curr_clause = n; unsigned num_lits = get_clause_num_literals(m_manager, n); for (unsigned i = 0; i < num_lits; i++) collect_macro_candidates_core(get_clause_literal(m_manager, n, i), num_decls, r); m_curr_clause = 0; } else { collect_macro_candidates_core(n, num_decls, r); } } z3-4.4.0/src/ast/macros/macro_finder.cpp0000644000175000017500000002041312540347414017643 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: macro_finder.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-04-05. Revision History: --*/ #include"macro_finder.h" #include"occurs.h" #include"ast_pp.h" #include"ast_ll_pp.h" bool macro_finder::is_macro(expr * n, app * & head, expr * & def) { if (!is_quantifier(n) || !to_quantifier(n)->is_forall()) return false; TRACE("macro_finder", tout << "processing: " << mk_pp(n, m_manager) << "\n";); expr * body = to_quantifier(n)->get_expr(); unsigned num_decls = to_quantifier(n)->get_num_decls(); return m_util.is_simple_macro(body, num_decls, head, def); } /** \brief Detect macros of the form 1- (forall (X) (= (+ (f X) (R X)) c)) 2- (forall (X) (<= (+ (f X) (R X)) c)) 3- (forall (X) (>= (+ (f X) (R X)) c)) The second and third cases are first converted into (forall (X) (= (f X) (+ c (* -1 (R x)) (k X)))) and (forall (X) (<= (k X) 0)) when case 2 (forall (X) (>= (k X) 0)) when case 3 For case 2 & 3, the new quantifiers are stored in new_exprs and new_prs. */ bool macro_finder::is_arith_macro(expr * n, proof * pr, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) { if (!is_quantifier(n) || !to_quantifier(n)->is_forall()) return false; arith_simplifier_plugin * as = get_arith_simp(); arith_util & autil = as->get_arith_util(); expr * body = to_quantifier(n)->get_expr(); unsigned num_decls = to_quantifier(n)->get_num_decls(); if (!autil.is_le(body) && !autil.is_ge(body) && !m_manager.is_eq(body)) return false; if (!as->is_add(to_app(body)->get_arg(0))) return false; app_ref head(m_manager); expr_ref def(m_manager); bool inv = false; if (!m_util.is_arith_macro(body, num_decls, head, def, inv)) return false; app_ref new_body(m_manager); if (!inv || m_manager.is_eq(body)) new_body = m_manager.mk_app(to_app(body)->get_decl(), head, def); else if (as->is_le(body)) new_body = autil.mk_ge(head, def); else new_body = autil.mk_le(head, def); quantifier_ref new_q(m_manager); new_q = m_manager.update_quantifier(to_quantifier(n), new_body); proof * new_pr = 0; if (m_manager.proofs_enabled()) { proof * rw = m_manager.mk_rewrite(n, new_q); new_pr = m_manager.mk_modus_ponens(pr, rw); } if (m_manager.is_eq(body)) { return m_macro_manager.insert(head->get_decl(), new_q, new_pr); } // is ge or le // TRACE("macro_finder", tout << "is_arith_macro: is_ge or is_le\n";); func_decl * f = head->get_decl(); func_decl * k = m_manager.mk_fresh_func_decl(f->get_name(), symbol::null, f->get_arity(), f->get_domain(), f->get_range()); app * k_app = m_manager.mk_app(k, head->get_num_args(), head->get_args()); expr_ref_buffer new_rhs_args(m_manager); expr_ref new_rhs2(m_manager); as->mk_add(def, k_app, new_rhs2); expr * body1 = m_manager.mk_eq(head, new_rhs2); expr * body2 = m_manager.mk_app(new_body->get_decl(), k_app, as->mk_numeral(rational(0))); quantifier * q1 = m_manager.update_quantifier(new_q, body1); expr * patterns[1] = { m_manager.mk_pattern(k_app) }; quantifier * q2 = m_manager.update_quantifier(new_q, 1, patterns, body2); new_exprs.push_back(q1); new_exprs.push_back(q2); if (m_manager.proofs_enabled()) { // new_pr : new_q // rw : [rewrite] new_q ~ q1 & q2 // mp : [modus_pones new_pr rw] q1 & q2 // pr1 : [and-elim mp] q1 // pr2 : [and-elim mp] q2 app * q1q2 = m_manager.mk_and(q1,q2); proof * rw = m_manager.mk_oeq_rewrite(new_q, q1q2); proof * mp = m_manager.mk_modus_ponens(new_pr, rw); proof * pr1 = m_manager.mk_and_elim(mp, 0); proof * pr2 = m_manager.mk_and_elim(mp, 1); new_prs.push_back(pr1); new_prs.push_back(pr2); } return true; } /** n is of the form: (forall (X) (iff (= (f X) t) def[X])) Convert it into: (forall (X) (= (f X) (ite def[X] t (k X)))) (forall (X) (not (= (k X) t))) where k is a fresh symbol. The new quantifiers and proofs are stored in new_exprs and new_prs */ static void pseudo_predicate_macro2macro(ast_manager & m, app * head, app * t, expr * def, quantifier * q, proof * pr, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) { func_decl * f = head->get_decl(); func_decl * k = m.mk_fresh_func_decl(f->get_name(), symbol::null, f->get_arity(), f->get_domain(), f->get_range()); app * k_app = m.mk_app(k, head->get_num_args(), head->get_args()); app * ite = m.mk_ite(def, t, k_app); app * body_1 = m.mk_eq(head, ite); app * body_2 = m.mk_not(m.mk_eq(k_app, t)); quantifier * q1 = m.update_quantifier(q, body_1); expr * pats[1] = { m.mk_pattern(k_app) }; quantifier * q2 = m.update_quantifier(q, 1, pats, body_2); // erase patterns new_exprs.push_back(q1); new_exprs.push_back(q2); if (m.proofs_enabled()) { // r : [rewrite] q ~ q1 & q2 // pr : q // mp : [modus_pones pr pr1] q1 & q2 // pr1 : [and-elim mp] q1 // pr2 : [and-elim mp] q2 app * q1q2 = m.mk_and(q1,q2); proof * r = m.mk_oeq_rewrite(q, q1q2); proof * mp = m.mk_modus_ponens(pr, r); proof * pr1 = m.mk_and_elim(mp, 0); proof * pr2 = m.mk_and_elim(mp, 1); new_prs.push_back(pr1); new_prs.push_back(pr2); } } macro_finder::macro_finder(ast_manager & m, macro_manager & mm): m_manager(m), m_macro_manager(mm), m_util(mm.get_util()) { } macro_finder::~macro_finder() { } bool macro_finder::expand_macros(unsigned num, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) { TRACE("macro_finder", tout << "starting expand_macros:\n"; m_macro_manager.display(tout);); bool found_new_macro = false; for (unsigned i = 0; i < num; i++) { expr * n = exprs[i]; proof * pr = m_manager.proofs_enabled() ? prs[i] : 0; expr_ref new_n(m_manager); proof_ref new_pr(m_manager); m_macro_manager.expand_macros(n, pr, new_n, new_pr); app * head = 0; expr * def = 0; app * t = 0; if (is_macro(new_n, head, def) && m_macro_manager.insert(head->get_decl(), to_quantifier(new_n.get()), new_pr)) { TRACE("macro_finder_found", tout << "found new macro: " << head->get_decl()->get_name() << "\n" << mk_pp(new_n, m_manager) << "\n";); found_new_macro = true; } else if (is_arith_macro(new_n, new_pr, new_exprs, new_prs)) { TRACE("macro_finder_found", tout << "found new arith macro:\n" << mk_pp(new_n, m_manager) << "\n";); found_new_macro = true; } else if (m_util.is_pseudo_predicate_macro(new_n, head, t, def)) { TRACE("macro_finder_found", tout << "found new pseudo macro:\n" << mk_pp(head, m_manager) << "\n" << mk_pp(t, m_manager) << "\n" << mk_pp(def, m_manager) << "\n";); pseudo_predicate_macro2macro(m_manager, head, t, def, to_quantifier(new_n), new_pr, new_exprs, new_prs); found_new_macro = true; } else { new_exprs.push_back(new_n); if (m_manager.proofs_enabled()) new_prs.push_back(new_pr); } } return found_new_macro; } void macro_finder::operator()(unsigned num, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) { TRACE("macro_finder", tout << "processing macros...\n";); expr_ref_vector _new_exprs(m_manager); proof_ref_vector _new_prs(m_manager); if (expand_macros(num, exprs, prs, _new_exprs, _new_prs)) { while (true) { expr_ref_vector old_exprs(m_manager); proof_ref_vector old_prs(m_manager); _new_exprs.swap(old_exprs); _new_prs.swap(old_prs); SASSERT(_new_exprs.empty()); SASSERT(_new_prs.empty()); if (!expand_macros(old_exprs.size(), old_exprs.c_ptr(), old_prs.c_ptr(), _new_exprs, _new_prs)) break; } } new_exprs.append(_new_exprs); new_prs.append(_new_prs); } z3-4.4.0/src/ast/macros/macro_manager.h0000644000175000017500000000646712540347414017470 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: macro_manager.h Abstract: Author: Leonardo de Moura (leonardo) 2010-04-05. Revision History: --*/ #ifndef _MACRO_MANAGER_H_ #define _MACRO_MANAGER_H_ #include"ast_util.h" #include"obj_hashtable.h" #include"simplifier.h" #include"recurse_expr.h" #include"func_decl_dependencies.h" #include"macro_util.h" /** \brief Macros are universally quantified formulas of the form: (forall X (= (f X) T[X])) (forall X (iff (f X) T[X])) where T[X] does not contain X. This class is responsible for storing macros and expanding them. It has support for backtracking and tagging declarations in an expression as forbidded for being macros. */ class macro_manager { ast_manager & m_manager; simplifier & m_simplifier; macro_util m_util; obj_map m_decl2macro; // func-decl -> quantifier obj_map m_decl2macro_pr; // func-decl -> quantifier_proof func_decl_ref_vector m_decls; quantifier_ref_vector m_macros; proof_ref_vector m_macro_prs; obj_hashtable m_forbidden_set; func_decl_ref_vector m_forbidden; struct scope { unsigned m_decls_lim; unsigned m_forbidden_lim; }; svector m_scopes; func_decl_dependencies m_deps; void restore_decls(unsigned old_sz); void restore_forbidden(unsigned old_sz); class macro_expander : public simplifier { protected: macro_manager & m_macro_manager; virtual bool get_subst(expr * n, expr_ref & r, proof_ref & p); virtual void reduce1_quantifier(quantifier * q); public: macro_expander(ast_manager & m, macro_manager & mm, simplifier & s); ~macro_expander(); }; friend class macro_expander; public: macro_manager(ast_manager & m, simplifier & s); ~macro_manager(); ast_manager & get_manager() const { return m_manager; } macro_util & get_util() { return m_util; } bool insert(func_decl * f, quantifier * m, proof * pr); bool has_macros() const { return !m_macros.empty(); } void push_scope(); void pop_scope(unsigned num_scopes); void reset(); void mark_forbidden(unsigned n, expr * const * exprs); void mark_forbidden(expr * e) { mark_forbidden(1, &e); } bool is_forbidden(func_decl * d) const { return m_forbidden_set.contains(d); } obj_hashtable const & get_forbidden_set() const { return m_forbidden_set; } void display(std::ostream & out); unsigned get_num_macros() const { return m_decls.size(); } unsigned get_first_macro_last_level() const { return m_scopes.empty() ? 0 : m_scopes.back().m_decls_lim; } func_decl * get_macro_func_decl(unsigned i) const { return m_decls.get(i); } func_decl * get_macro_interpretation(unsigned i, expr_ref & interp) const; quantifier * get_macro_quantifier(func_decl * f) const { quantifier * q = 0; m_decl2macro.find(f, q); return q; } void get_head_def(quantifier * q, func_decl * d, app * & head, expr * & def) const; void expand_macros(expr * n, proof * pr, expr_ref & r, proof_ref & new_pr); }; #endif /* _MACRO_MANAGER_H_ */ z3-4.4.0/src/ast/macros/quasi_macros.h0000644000175000017500000000363612540347414017356 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: quasi_macros.cpp Abstract: Author: Christoph Wintersteiger (t-cwinte) 2010-04-23 Revision History: --*/ #ifndef _QUASI_MACROS_H_ #define _QUASI_MACROS_H_ #include #include"macro_manager.h" #include"basic_simplifier_plugin.h" #include"simplifier.h" /** \brief Finds quasi macros and applies them. */ class quasi_macros { typedef obj_map occurrences_map; ast_manager & m_manager; macro_manager & m_macro_manager; simplifier & m_simplifier; occurrences_map m_occurrences; ptr_vector m_todo; vector m_new_var_names; expr_ref_vector m_new_vars; expr_ref_vector m_new_eqs; sort_ref_vector m_new_qsorts; std::stringstream m_new_name; expr_mark m_visited_once; expr_mark m_visited_more; bool is_unique(func_decl * f) const; bool is_non_ground_uninterp(expr const * e) const; bool fully_depends_on(app * a, quantifier * q) const; bool depends_on(expr * e, func_decl * f) const; bool is_quasi_macro(expr * e, app_ref & a, expr_ref &v) const; void quasi_macro_to_macro(quantifier * q, app * a, expr * t, quantifier_ref & macro); void find_occurrences(expr * e); bool find_macros(unsigned n, expr * const * exprs); void apply_macros(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); public: quasi_macros(ast_manager & m, macro_manager & mm, simplifier & s); ~quasi_macros(); /** \brief Find pure function macros and apply them. */ bool operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); }; #endif z3-4.4.0/src/ast/macros/macro_finder.h0000644000175000017500000000331712540347414017314 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: macro_finder.h Abstract: Author: Leonardo de Moura (leonardo) 2010-04-05. Revision History: --*/ #ifndef _MACRO_FINDER_H_ #define _MACRO_FINDER_H_ #include"macro_manager.h" #include"arith_simplifier_plugin.h" bool is_macro_head(expr * n, unsigned num_decls); bool is_simple_macro(ast_manager & m, expr * n, unsigned num_decls, obj_hashtable const * forbidden_set, app * & head, expr * & def); inline bool is_simple_macro(ast_manager & m, expr * n, unsigned num_decls, app * & head, expr * & def) { return is_simple_macro(m, n, num_decls, 0, head, def); } /** \brief Macro finder is responsible for finding universally quantified sub-formulas that can be used as macros. */ class macro_finder { ast_manager & m_manager; macro_manager & m_macro_manager; macro_util & m_util; arith_simplifier_plugin * get_arith_simp() { return m_util.get_arith_simp(); } bool expand_macros(unsigned num, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); bool is_arith_macro(expr * n, proof * pr, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); bool is_macro(expr * n, app * & head, expr * & def); bool is_pseudo_head(expr * n, unsigned num_decls, app * & head, app * & t); bool is_pseudo_predicate_macro(expr * n, app * & head, app * & t, expr * & def); public: macro_finder(ast_manager & m, macro_manager & mm); ~macro_finder(); void operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); }; #endif /* _MACRO_FINDER_H_ */ z3-4.4.0/src/ast/macros/macro_util.h0000644000175000017500000001400312540347414017014 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: macro_util.h Abstract: Macro finding goodies. They are used during preprocessing (MACRO_FINDER=true), and model building. Author: Leonardo de Moura (leonardo) 2010-12-15. Revision History: --*/ #ifndef _MACRO_UTIL_H_ #define _MACRO_UTIL_H_ #include"ast.h" #include"obj_hashtable.h" #include"simplifier.h" class poly_simplifier_plugin; class arith_simplifier_plugin; class bv_simplifier_plugin; class basic_simplifier_plugin; class macro_util { public: /** \brief See collect_macro_candidates. */ class macro_candidates { ptr_vector m_fs; expr_ref_vector m_defs; expr_ref_vector m_conds; svector m_ineq; // true if the macro is based on an inequality instead of equality. svector m_satisfy; svector m_hint; // macro did not contain all universal variables in the quantifier. friend class macro_util; ast_manager & get_manager() { return m_conds.get_manager(); } public: macro_candidates(ast_manager & m); ~macro_candidates() { reset(); } void reset(); void insert(func_decl * f, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint); bool empty() const { return m_fs.empty(); } unsigned size() const { return m_fs.size(); } func_decl * get_f(unsigned i) const { return m_fs[i]; } expr * get_def(unsigned i) const { return m_defs.get(i); } expr * get_cond(unsigned i) const { return m_conds.get(i); } bool ineq(unsigned i) const { return m_ineq[i]; } bool satisfy_atom(unsigned i) const { return m_satisfy[i]; } bool hint(unsigned i) const { return m_hint[i]; } }; private: ast_manager & m_manager; simplifier & m_simplifier; arith_simplifier_plugin * m_arith_simp; bv_simplifier_plugin * m_bv_simp; basic_simplifier_plugin * m_basic_simp; obj_hashtable * m_forbidden_set; bool is_forbidden(func_decl * f) const { return m_forbidden_set != 0 && m_forbidden_set->contains(f); } bool poly_contains_head(expr * n, func_decl * f, expr * exception) const; void collect_arith_macros(expr * n, unsigned num_decls, unsigned max_macros, bool allow_cond_macros, macro_candidates & r); void normalize_expr(app * head, expr * t, expr_ref & norm_t) const; void insert_macro(app * head, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, macro_candidates & r); void insert_quasi_macro(app * head, unsigned num_decls, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, macro_candidates & r); expr * m_curr_clause; // auxiliary var used in collect_macro_candidates. // Return true if m_curr_clause contains f in a literal different from except_lit bool rest_contains_decl(func_decl * f, expr * except_lit); // Store in extra_cond (and (not l_1) ... (not l_n)) where l_i's are the literals of m_curr_clause that are different from except_lit. void get_rest_clause_as_cond(expr * except_lit, expr_ref & extra_cond); void collect_poly_args(expr * n, expr * exception, ptr_buffer & args); void add_arith_macro_candidate(app * head, unsigned num_decls, expr * def, expr * atom, bool ineq, bool hint, macro_candidates & r); void collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * atom, unsigned num_decls, bool ineq, macro_candidates & r); void collect_arith_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r); void collect_macro_candidates_core(expr * atom, unsigned num_decls, macro_candidates & r); bool is_poly_hint(expr * n, app * head, expr * exception); public: macro_util(ast_manager & m, simplifier & s); void set_forbidden_set(obj_hashtable * s) { m_forbidden_set = s; } arith_simplifier_plugin * get_arith_simp() const; bv_simplifier_plugin * get_bv_simp() const; basic_simplifier_plugin * get_basic_simp() const; bool is_macro_head(expr * n, unsigned num_decls) const; bool is_left_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const; bool is_right_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const; bool is_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const { return is_left_simple_macro(n, num_decls, head, def) || is_right_simple_macro(n, num_decls, head, def); } bool is_arith_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def, bool & inv) const; bool is_arith_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const { bool inv; return is_arith_macro(n, num_decls, head, def, inv); } bool is_pseudo_head(expr * n, unsigned num_decls, app * & head, app * & t); bool is_pseudo_predicate_macro(expr * n, app * & head, app * & t, expr * & def); bool is_quasi_macro_head(expr * n, unsigned num_decls) const; void quasi_macro_head_to_macro_head(app * qhead, unsigned num_decls, app_ref & head, expr_ref & cond) const; void mk_macro_interpretation(app * head, expr * def, expr_ref & interp) const; void collect_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r); void collect_macro_candidates(quantifier * q, macro_candidates & r); // // Auxiliary goodness that allows us to manipulate BV and Arith polynomials. // bool is_bv(expr * n) const; bool is_bv_sort(sort * s) const; app * mk_zero(sort * s) const; bool is_add(expr * n) const; bool is_times_minus_one(expr * n, expr * & arg) const; bool is_le(expr * n) const; bool is_le_ge(expr * n) const; void mk_sub(expr * t1, expr * t2, expr_ref & r) const; void mk_add(expr * t1, expr * t2, expr_ref & r) const; void mk_add(unsigned num_args, expr * const * args, sort * s, expr_ref & r) const; poly_simplifier_plugin * get_poly_simp_for(sort * s) const; }; #endif z3-4.4.0/src/ast/expr_abstract.h0000644000175000017500000000134212540347414016235 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: expr_abstract.h Abstract: Abstract occurrences of constants to bound variables. Author: Nikolaj Bjorner (nbjorner) 2008-03-08 Notes: --*/ #ifndef _EXPR_ABSTRACT_H_ #define _EXPR_ABSTRACT_H_ #include"ast.h" class expr_abstractor { ast_manager& m; expr_ref_vector m_pinned; ptr_vector m_stack, m_args; obj_map m_map; public: expr_abstractor(ast_manager& m): m(m), m_pinned(m) {} void operator()(unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result); }; void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result); #endif z3-4.4.0/src/ast/has_free_vars.cpp0000644000175000017500000000456212540347414016545 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: has_free_vars.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-23. Revision History: --*/ #include"ast.h" #include"expr_delta_pair.h" #include"hashtable.h" class contains_vars { typedef hashtable, default_eq > cache; cache m_cache; svector m_todo; bool m_contains; unsigned m_window; void visit(expr * n, unsigned delta, bool & visited) { expr_delta_pair e(n, delta); if (!m_cache.contains(e)) { m_todo.push_back(e); visited = false; } } bool visit_children(expr * n, unsigned delta) { bool visited = true; unsigned dw; unsigned j; switch (n->get_kind()) { case AST_VAR: dw = m_window <= UINT_MAX - delta ? m_window + delta : UINT_MAX; if (to_var(n)->get_idx() >= delta && to_var(n)->get_idx() <= dw) m_contains = true; break; case AST_APP: j = to_app(n)->get_num_args(); while (j > 0) { --j; visit(to_app(n)->get_arg(j), delta, visited); } break; case AST_QUANTIFIER: if (delta <= UINT_MAX - to_quantifier(n)->get_num_decls()) { visit(to_quantifier(n)->get_expr(), delta + to_quantifier(n)->get_num_decls(), visited); } break; default: break; } return visited; } public: // return true if n contains a variable in the range [begin, end] bool operator()(expr * n, unsigned begin = 0, unsigned end = UINT_MAX) { m_contains = false; m_window = end - begin; m_todo.reset(); m_cache.reset(); m_todo.push_back(expr_delta_pair(n, begin)); while (!m_todo.empty()) { expr_delta_pair e = m_todo.back(); if (visit_children(e.m_node, e.m_delta)) { m_cache.insert(e); m_todo.pop_back(); } if (m_contains) { return true; } } SASSERT(!m_contains); return false; } }; bool has_free_vars(expr * n) { contains_vars p; return p(n); } z3-4.4.0/src/ast/occurs.cpp0000644000175000017500000000261212540347414015226 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: occurs.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-06-07. Revision History: --*/ #include"occurs.h" #include"for_each_expr.h" // ----------------------------------- // // Occurs check // // ----------------------------------- namespace occurs_namespace { struct found {}; struct proc { expr * m_n; #define CHECK() { if (n == m_n) throw found(); } proc(expr * n):m_n(n) {} void operator()(var const * n) { CHECK(); } void operator()(app const * n) { CHECK(); } void operator()(quantifier const * n) { CHECK(); } }; struct decl_proc { func_decl * m_d; decl_proc(func_decl * d):m_d(d) {} void operator()(var const * n) { } void operator()(app const * n) { if (n->get_decl() == m_d) throw found(); } void operator()(quantifier const * n) { } }; }; // Return true if n1 occurs in n2 bool occurs(expr * n1, expr * n2) { occurs_namespace::proc p(n1); try { quick_for_each_expr(p, n2); } catch (occurs_namespace::found) { return true; } return false; } bool occurs(func_decl * d, expr * n) { occurs_namespace::decl_proc p(d); try { quick_for_each_expr(p, n); } catch (occurs_namespace::found) { return true; } return false; } z3-4.4.0/src/ast/seq_decl_plugin.cpp0000644000175000017500000002435212540347414017072 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dl_decl_plugin.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2011-14-11 Revision History: --*/ #include "seq_decl_plugin.h" #include "arith_decl_plugin.h" #include "array_decl_plugin.h" #include seq_decl_plugin::seq_decl_plugin(): m_init(false) {} void seq_decl_plugin::finalize() { for (unsigned i = 0; i < m_sigs.size(); ++i) dealloc(m_sigs[i]); } bool seq_decl_plugin::is_sort_param(sort* s, unsigned& idx) { return s->get_name().is_numerical() && (idx = s->get_name().get_num(), true); } bool seq_decl_plugin::match(ptr_vector& binding, sort* s, sort* sP) { if (s == sP) return true; unsigned i; if (is_sort_param(sP, i)) { if (binding.size() <= i) binding.resize(i+1); if (binding[i] && (binding[i] != s)) return false; binding[i] = s; return true; } if (s->get_family_id() == sP->get_family_id() && s->get_decl_kind() == sP->get_decl_kind() && s->get_name() == sP->get_name()) { SASSERT(s->get_num_parameters() == sP->get_num_parameters()); for (unsigned i = 0; i < s->get_num_parameters(); ++i) { parameter const& p = s->get_parameter(i); if (p.is_ast() && is_sort(p.get_ast())) { parameter const& p2 = sP->get_parameter(i); if (!match(binding, to_sort(p.get_ast()), to_sort(p2.get_ast()))) return false; } } return true; } else { return false; } } void seq_decl_plugin::match(psig& sig, unsigned dsz, sort *const* dom, sort* range, sort_ref& range_out) { ptr_vector binding; ast_manager& m = *m_manager; if (sig.m_dom.size() != dsz) { std::ostringstream strm; strm << "Unexpected number of arguments to '" << sig.m_name << "' "; strm << sig.m_dom.size() << " arguments expected " << dsz << " given"; m.raise_exception(strm.str().c_str()); } bool is_match = true; for (unsigned i = 0; is_match && i < dsz; ++i) { is_match = match(binding, dom[i], sig.m_dom[i].get()); } if (range && is_match) { is_match = match(binding, range, sig.m_range); } if (!is_match) { std::ostringstream strm; strm << "Sort of polymorphic function '" << sig.m_name << "' "; strm << "does not match the declared type"; m.raise_exception(strm.str().c_str()); } if (!range && dsz == 0) { std::ostringstream strm; strm << "Sort of polymorphic function '" << sig.m_name << "' "; strm << "is ambiguous. Function takes no arguments and sort of range has not been constrained"; m.raise_exception(strm.str().c_str()); } range_out = apply_binding(binding, sig.m_range); } sort* seq_decl_plugin::apply_binding(ptr_vector const& binding, sort* s) { unsigned i; if (is_sort_param(s, i)) { if (binding.size() <= i || !binding[i]) { m_manager->raise_exception("Expecting type parameter to be bound"); } return binding[i]; } if (is_sort_of(s, m_family_id, SEQ_SORT) || is_sort_of(s, m_family_id, RE_SORT)) { SASSERT(s->get_num_parameters() == 1); SASSERT(s->get_parameter(0).is_ast()); SASSERT(is_sort(s->get_parameter(0).get_ast())); sort* p = apply_binding(binding, to_sort(s->get_parameter(0).get_ast())); parameter param(p); return mk_sort(s->get_decl_kind(), 1, ¶m); } return s; } void seq_decl_plugin::init() { if(m_init) return; ast_manager& m = *m_manager; m_init = true; sort* A = m.mk_uninterpreted_sort(symbol((unsigned)0)); sort* B = m.mk_uninterpreted_sort(symbol((unsigned)1)); parameter paramA(A); sort* seqA = m.mk_sort(m_family_id, SEQ_SORT, 1, ¶mA); sort* reA = m.mk_sort(m_family_id, RE_SORT, 1, ¶mA); sort* seqAseqA[2] = { seqA, seqA }; sort* seqAA[2] = { seqA, A }; sort* seqAB[2] = { seqA, B }; sort* seqAreA[2] = { seqA, reA }; sort* AseqA[2] = { A, seqA }; sort* reAreA[2] = { reA, reA }; sort* AA[2] = { A, A }; sort* seqABB[3] = { seqA, B, B }; sort* boolT = m.mk_bool_sort(); sort* intT = arith_util(m).mk_int(); sort* predA = array_util(m).mk_array_sort(A, boolT); m_sigs.resize(LAST_SEQ_OP); // TBD: have (par ..) construct and load parameterized signature from premable. m_sigs[OP_SEQ_UNIT] = alloc(psig, m, "seq-unit", 1, 1, &A, seqA); m_sigs[OP_SEQ_EMPTY] = alloc(psig, m, "seq-empty", 1, 0, 0, seqA); m_sigs[OP_SEQ_CONCAT] = alloc(psig, m, "seq-concat", 1, 2, seqAseqA, seqA); m_sigs[OP_SEQ_CONS] = alloc(psig, m, "seq-cons", 1, 2, AseqA, seqA); m_sigs[OP_SEQ_REV_CONS] = alloc(psig, m, "seq-rev-cons", 1, 2, seqAA, seqA); m_sigs[OP_SEQ_HEAD] = alloc(psig, m, "seq-head", 1, 1, &seqA, A); m_sigs[OP_SEQ_TAIL] = alloc(psig, m, "seq-tail", 1, 1, &seqA, seqA); m_sigs[OP_SEQ_LAST] = alloc(psig, m, "seq-last", 1, 1, &seqA, A); m_sigs[OP_SEQ_FIRST] = alloc(psig, m, "seq-first", 1, 1, &seqA, seqA); m_sigs[OP_SEQ_PREFIX_OF] = alloc(psig, m, "seq-prefix-of", 1, 2, seqAseqA, boolT); m_sigs[OP_SEQ_SUFFIX_OF] = alloc(psig, m, "seq-suffix-of", 1, 2, seqAseqA, boolT); m_sigs[OP_SEQ_SUBSEQ_OF] = alloc(psig, m, "seq-subseq-of", 1, 2, seqAseqA, boolT); m_sigs[OP_SEQ_EXTRACT] = alloc(psig, m, "seq-extract", 2, 3, seqABB, seqA); m_sigs[OP_SEQ_NTH] = alloc(psig, m, "seq-nth", 2, 2, seqAB, A); m_sigs[OP_SEQ_LENGTH] = alloc(psig, m, "seq-length", 1, 1, &seqA, intT); m_sigs[OP_RE_PLUS] = alloc(psig, m, "re-plus", 1, 1, &reA, reA); m_sigs[OP_RE_STAR] = alloc(psig, m, "re-star", 1, 1, &reA, reA); m_sigs[OP_RE_OPTION] = alloc(psig, m, "re-option", 1, 1, &reA, reA); m_sigs[OP_RE_RANGE] = alloc(psig, m, "re-range", 1, 2, AA, reA); m_sigs[OP_RE_CONCAT] = alloc(psig, m, "re-concat", 1, 2, reAreA, reA); m_sigs[OP_RE_UNION] = alloc(psig, m, "re-union", 1, 2, reAreA, reA); m_sigs[OP_RE_INTERSECT] = alloc(psig, m, "re-intersect", 1, 2, reAreA, reA); m_sigs[OP_RE_DIFFERENCE] = alloc(psig, m, "re-difference", 1, 2, reAreA, reA); m_sigs[OP_RE_COMPLEMENT] = alloc(psig, m, "re-complement", 1, 1, &reA, reA); m_sigs[OP_RE_LOOP] = alloc(psig, m, "re-loop", 1, 1, &reA, reA); m_sigs[OP_RE_EMPTY_SEQ] = alloc(psig, m, "re-empty-seq", 1, 0, 0, reA); m_sigs[OP_RE_EMPTY_SET] = alloc(psig, m, "re-empty-set", 1, 0, 0, reA); m_sigs[OP_RE_FULL_SET] = alloc(psig, m, "re-full-set", 1, 0, 0, reA); m_sigs[OP_RE_OF_SEQ] = alloc(psig, m, "re-of-seq", 1, 1, &seqA, reA); m_sigs[OP_RE_OF_PRED] = alloc(psig, m, "re-of-pred", 1, 1, &predA, reA); m_sigs[OP_RE_MEMBER] = alloc(psig, m, "re-member", 1, 2, seqAreA, boolT); } sort * seq_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { init(); ast_manager& m = *m_manager; switch (k) { case SEQ_SORT: if (num_parameters != 1) { m.raise_exception("Invalid sequence sort, expecting one parameter"); } if (!parameters[0].is_ast() || !is_sort(parameters[0].get_ast())) { m.raise_exception("invalid sequence sort, parameter is not a sort"); } return m.mk_sort(symbol("Seq"), sort_info(m_family_id, SEQ_SORT, num_parameters, parameters)); case RE_SORT: if (num_parameters != 1) { m.raise_exception("Invalid regex sort, expecting one parameter"); } if (!parameters[0].is_ast() || !is_sort(parameters[0].get_ast())) { m.raise_exception("invalid regex sort, parameter is not a sort"); } return m.mk_sort(symbol("RegEx"), sort_info(m_family_id, RE_SORT, num_parameters, parameters)); default: UNREACHABLE(); return 0; } } func_decl * seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { init(); ast_manager& m = *m_manager; sort_ref rng(m); switch(k) { case OP_SEQ_UNIT: case OP_SEQ_EMPTY: case OP_SEQ_CONCAT: case OP_SEQ_CONS: case OP_SEQ_REV_CONS: case OP_SEQ_HEAD: case OP_SEQ_TAIL: case OP_SEQ_LAST: case OP_SEQ_FIRST: case OP_SEQ_PREFIX_OF: case OP_SEQ_SUFFIX_OF: case OP_SEQ_SUBSEQ_OF: case OP_SEQ_LENGTH: case OP_RE_PLUS: case OP_RE_STAR: case OP_RE_OPTION: case OP_RE_RANGE: case OP_RE_CONCAT: case OP_RE_UNION: case OP_RE_INTERSECT: case OP_RE_DIFFERENCE: case OP_RE_COMPLEMENT: case OP_RE_EMPTY_SEQ: case OP_RE_EMPTY_SET: case OP_RE_OF_SEQ: case OP_RE_OF_PRED: case OP_RE_MEMBER: match(*m_sigs[k], arity, domain, range, rng); return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k)); case OP_SEQ_EXTRACT: case OP_SEQ_NTH: // TBD check numeric arguments for being BVs or integers. match(*m_sigs[k], arity, domain, range, rng); return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k)); case OP_RE_LOOP: match(*m_sigs[k], arity, domain, range, rng); if (num_parameters != 2 || !parameters[0].is_int() || !parameters[1].is_int()) { m.raise_exception("Expecting two numeral parameters to function re-loop"); } return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k, num_parameters, parameters)); default: UNREACHABLE(); return 0; } } void seq_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { init(); for (unsigned i = 0; i < m_sigs.size(); ++i) { op_names.push_back(builtin_name(m_sigs[i]->m_name.str().c_str(), i)); } } void seq_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { init(); sort_names.push_back(builtin_name("Seq", SEQ_SORT)); sort_names.push_back(builtin_name("RegEx", RE_SORT)); } bool seq_decl_plugin::is_value(app* e) const { // TBD: empty sequence is a value. return false; } z3-4.4.0/src/ast/ast_ll_pp.cpp0000644000175000017500000002164112540347414015710 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ast_ll_pp.cpp Abstract: AST low level pretty printer. Author: Leonardo de Moura (leonardo) 2006-10-19. Revision History: --*/ #include #include"for_each_ast.h" #include"arith_decl_plugin.h" // #define AST_LL_PP_SHOW_FAMILY_NAME class ll_printer { std::ostream & m_out; ast_manager & m_manager; ast * m_root; bool m_only_exprs; bool m_compact; arith_util m_autil; void display_def_header(ast * n) { if (n != m_root) { m_out << "#" << n->get_id() << " := "; } } void display_child_ref(ast * n) { m_out << "#" << n->get_id(); } void display_name(func_decl * decl) { symbol n = decl->get_name(); if (decl->is_skolem() && n.is_numerical()) m_out << "z3.sk." << n.get_num(); else m_out << n; } bool process_numeral(expr * n) { rational val; bool is_int; if (m_autil.is_numeral(n, val, is_int)) { m_out << val << "::" << (is_int ? "Int" : "Real"); return true; } return false; } void display_sort(sort * s) { m_out << s->get_name(); display_params(s); } void display_child(ast * n) { switch (n->get_kind()) { case AST_SORT: display_sort(to_sort(n)); break; case AST_APP: if (process_numeral(to_expr(n))) { // skip } else if (to_app(n)->get_num_args() == 0) { display_name(to_app(n)->get_decl()); display_params(to_app(n)->get_decl()); } else { display_child_ref(n); } break; default: display_child_ref(n); } } template void display_children(unsigned num_children, T * const * children) { for (unsigned i = 0; i < num_children; i++) { if (i > 0) { m_out << " "; } display_child(children[i]); } } void display_params(decl * d) { unsigned n = d->get_num_parameters(); parameter const* p = d->get_parameters(); if (n > 0 && !d->private_parameters()) { m_out << "["; for (unsigned i = 0; i < n; i ++) { if (p[i].is_ast()) { display_child(p[i].get_ast()); } else { m_out << p[i]; } m_out << (i < n-1 ? ":" : ""); } m_out << "]"; } } public: ll_printer(std::ostream & out, ast_manager & m, ast * n, bool only_exprs, bool compact): m_out(out), m_manager(m), m_root(n), m_only_exprs(only_exprs), m_compact(compact), m_autil(m) { } void pp(ast* n) { ast_mark visited; pp(n, visited); } void pp(ast* n, ast_mark& visited) { if (is_sort(n)) { display_sort(to_sort(n)); } else { for_each_ast(*this, visited, n, true); } } void operator()(sort* n) { } void operator()(func_decl * n) { if (m_only_exprs) { return; } if (n->get_family_id() != null_family_id) { return; } m_out << "decl "; display_name(n); m_out << " :: "; if (n->get_arity() == 0) { display_child(n->get_range()); } else { m_out << "(-> "; display_children(n->get_arity(), n->get_domain()); m_out << " "; display_child(n->get_range()); m_out << ")"; display_params(n); if (n->is_associative()) { m_out << " :assoc"; } if (n->is_commutative()) { m_out << " :comm"; } if (n->is_injective()) { m_out << " :inj"; } } m_out << "\n"; } void operator()(var * n) { display_def_header(n); m_out << "(:var " << to_var(n)->get_idx() << " "; display_sort(n->get_sort()); m_out << ")\n"; } void operator()(app * n) { if (m_autil.is_numeral(n)) { if (!m_compact) display_def_header(n); if (n == m_root || !m_compact) { process_numeral(n); m_out << "\n"; } } else if (m_manager.is_proof(n)) { display_def_header(n); m_out << "[" << n->get_decl()->get_name(); unsigned num_params = n->get_decl()->get_num_parameters(); for (unsigned i = 0; i < num_params; ++i) { m_out << " "; m_out << n->get_decl()->get_parameter(i); } unsigned num_parents = m_manager.get_num_parents(n); for (unsigned i = 0; i < num_parents; i++) { m_out << " "; display_child(m_manager.get_parent(n, i)); } m_out << "]: "; if (m_manager.has_fact(n)) { // display(m_manager.get_fact(n), 6); display_child(m_manager.get_fact(n)); } else m_out << "*"; m_out << "\n"; } else if (m_compact && n->get_num_args() == 0) { if (n == m_root) { display_child(n); m_out << "\n"; } } else { display_def_header(n); if (n->get_num_args() > 0) m_out << "("; display_name(n->get_decl()); display_params(n->get_decl()); if (n->get_num_args() > 0) { m_out << " "; display_children(n->get_num_args(), n->get_args()); m_out << ")"; } #ifdef AST_LL_PP_SHOW_FAMILY_NAME if (to_app(n)->get_family_id() != null_family_id) { m_out << " family: " << m_manager.get_family_name(to_app(n)->get_family_id()); } #endif m_out << "\n"; } } void operator()(quantifier * n) { display_def_header(n); m_out << "(" << (n->is_forall() ? "forall" : "exists") << " "; unsigned num_decls = n->get_num_decls(); m_out << "(vars "; for (unsigned i = 0; i < num_decls; i++) { if (i > 0) { m_out << " "; } m_out << "(" << n->get_decl_name(i) << " "; display_sort(n->get_decl_sort(i)); m_out << ")"; } m_out << ") "; if (n->get_num_patterns() > 0) { m_out << "(:pat "; display_children(n->get_num_patterns(), n->get_patterns()); m_out << ") "; } if (n->get_num_no_patterns() > 0) { m_out << "(:nopat "; display_children(n->get_num_no_patterns(), n->get_no_patterns()); m_out << ") "; } display_child(n->get_expr()); m_out << ")\n"; } void display(expr * n, unsigned depth) { if (is_var(n)) { m_out << "(:var " << to_var(n)->get_idx() << ")"; return; } if (!is_app(n) || depth == 0 || to_app(n)->get_num_args() == 0) { display_child(n); return; } if (to_app(n)->get_num_args() > depth && to_app(n)->get_num_args() > 16) { display_child(n); return; } unsigned num_args = to_app(n)->get_num_args(); if (num_args > 0) m_out << "("; display_name(to_app(n)->get_decl()); display_params(to_app(n)->get_decl()); for (unsigned i = 0; i < num_args; i++) { m_out << " "; display(to_app(n)->get_arg(i), depth-1); } if (num_args > 0) m_out << ")"; } void display_bounded(ast * n, unsigned depth) { if (is_app(n)) { display(to_expr(n), depth); } else if (is_var(n)) { m_out << "(:var " << to_var(n)->get_idx() << ")"; } else { m_out << "#" << n->get_id(); } } }; void ast_ll_pp(std::ostream & out, ast_manager & m, ast * n, bool only_exprs, bool compact) { ll_printer p(out, m, n, only_exprs, compact); p.pp(n); } void ast_ll_pp(std::ostream & out, ast_manager & m, ast * n, ast_mark & visited, bool only_exprs, bool compact) { ll_printer p(out, m, n, only_exprs, compact); p.pp(n, visited); } void ast_def_ll_pp(std::ostream & out, ast_manager & m, ast * n, ast_mark & visited, bool only_exprs, bool compact) { ll_printer p(out, m, 0, only_exprs, compact); p.pp(n, visited); } void ast_ll_bounded_pp(std::ostream & out, ast_manager & m, ast * n, unsigned depth) { ll_printer p(out, m, 0, false, true); p.display_bounded(n, depth); } z3-4.4.0/src/ast/ast_util.h0000644000175000017500000000663312540347414015230 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ast_util.h Abstract: Helper functions Author: Leonardo de Moura (leonardo) 2007-06-08. Revision History: --*/ #ifndef _AST_UTIL_H_ #define _AST_UTIL_H_ #include"ast.h" #include"obj_hashtable.h" template void remove_duplicates(C & v) { expr_fast_mark1 visited; if (!v.empty()) { unsigned sz = v.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { typename C::data curr = v.get(i); if (!visited.is_marked(curr)) { visited.mark(curr); if (i != j) v.set(j, curr); j++; } } v.shrink(j); } } app * mk_list_assoc_app(ast_manager & m, func_decl * f, unsigned num_args, expr * const * args); app * mk_list_assoc_app(ast_manager & m, family_id fid, decl_kind k, unsigned num_args, expr * const * args); bool is_well_formed_vars(ptr_vector& bound, expr* n); inline bool args_are_vars(app const * n) { unsigned sz = n->get_num_args(); for (unsigned i = 0; i < sz; i++) { if (!is_var(n->get_arg(i))) return false; } return true; } inline bool depth_leq_one(app * n) { unsigned sz = n->get_num_args(); for (unsigned i = 0; i < sz; i++) { expr * arg = n->get_arg(i); if (is_app(arg) && to_app(arg)->get_num_args() > 0) return false; } return true; } template void dec_ref(ast_manager & m, obj_hashtable & s) { typename obj_hashtable::iterator it = s.begin(); typename obj_hashtable::iterator end = s.end(); for (;it != end; ++it) { m.dec_ref(*it); } } template void inc_ref(ast_manager & m, obj_hashtable & s) { typename obj_hashtable::iterator it = s.begin(); typename obj_hashtable::iterator end = s.end(); for (;it != end; ++it) { m.inc_ref(*it); } } // ----------------------------------- // // Clauses (as ASTs) support // // ----------------------------------- bool is_atom(ast_manager & m, expr * n); bool is_literal(ast_manager & m, expr * n); void get_literal_atom_sign(ast_manager & m, expr * n, expr * & atom, bool & sign); bool is_clause(ast_manager & m, expr * n); unsigned get_clause_num_literals(ast_manager & m, expr * cls); expr * get_clause_literal(ast_manager & m, expr * cls, unsigned idx); // ----------------------------------- // // Goodies for creating Boolean expressions // // ----------------------------------- /** Return (and args[0] ... args[num_args-1]) if num_args >= 2 Return args[0] if num_args == 1 Return true if num_args == 0 */ expr * mk_and(ast_manager & m, unsigned num_args, expr * const * args); /** Return (or args[0] ... args[num_args-1]) if num_args >= 2 Return args[0] if num_args == 1 Return false if num_args == 0 */ expr * mk_or(ast_manager & m, unsigned num_args, expr * const * args); /** Return a if arg = (not a) Retur (not arg) otherwise */ expr * mk_not(ast_manager & m, expr * arg); /** Return the expression (and (not (= args[0] args[1])) (not (= args[0] args[2])) ... (not (= args[num_args-2] args[num_args-1]))) */ expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args); #endif /* _AST_UTIL_H_ */ z3-4.4.0/src/ast/expr_functors.cpp0000644000175000017500000000620412540347414016632 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: expr_functors.cpp Abstract: Functors on expressions. Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: Hoisted from quant_elim. --*/ #include "expr_functors.h" // ---------- // check_pred bool check_pred::operator()(expr* e) { if (!m_visited.is_marked(e)) { m_refs.push_back(e); visit(e); } SASSERT(m_visited.is_marked(e)); return m_pred_holds.is_marked(e); } void check_pred::visit(expr* e) { ptr_vector todo; todo.push_back(e); while (!todo.empty()) { e = todo.back(); if (m_pred(e)) { m_pred_holds.mark(e, true); } if (m_visited.is_marked(e)) { todo.pop_back(); continue; } switch(e->get_kind()) { case AST_APP: { app* a = to_app(e); bool all_visited = true; unsigned num_args = a->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { expr* arg = a->get_arg(i); if (!m_visited.is_marked(arg)) { todo.push_back(arg); all_visited = false; } else if (m_pred_holds.is_marked(arg)) { m_pred_holds.mark(e, true); } } if (all_visited) { m_visited.mark(e, true); todo.pop_back(); } break; } case AST_QUANTIFIER: { quantifier* q = to_quantifier(e); expr* arg = q->get_expr(); if (m_visited.is_marked(arg)) { todo.pop_back(); if (m_pred_holds.is_marked(arg)) { m_pred_holds.mark(e, true); } m_visited.mark(e, true); } else { todo.push_back(arg); } break; } case AST_VAR: todo.pop_back(); m_visited.mark(e, true); break; default: UNREACHABLE(); break; } } } // ------------ // contains_app bool contains_app::operator()(unsigned size, expr* const* es) { for (unsigned i = 0; i < size; ++i) { if ((*this)(es[i])) { return true; } } return false; } // ----------- // map_proc void map_proc::reconstruct(app* a) { m_args.reset(); bool is_new = false; for (unsigned i = 0; i < a->get_num_args(); ++i) { expr* e1 = a->get_arg(i); expr* e2 = get_expr(e1); m_args.push_back(e2); if (e1 != e2) { is_new = true; } } if (is_new) { expr* b = m.mk_app(a->get_decl(), m_args.size(), m_args.c_ptr()); m_map.insert(a, b, 0); } else { m_map.insert(a, a, 0); } } void map_proc::visit(quantifier* e) { expr_ref q(m); q = m.update_quantifier(e, get_expr(e->get_expr())); m_map.insert(e, q, 0); } expr* map_proc::get_expr(expr* e) { expr* result = 0; proof* p = 0; m_map.get(e, result, p); return result; } z3-4.4.0/src/ast/ast.cpp0000644000175000017500000034240112540347414014522 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ast.cpp Abstract: Expression DAG Author: Leonardo de Moura (leonardo) 2006-09-28. Revision History: --*/ #include #include"ast.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"buffer.h" #include"warning.h" #include"string_buffer.h" #include"ast_util.h" #include"ast_smt2_pp.h" // ----------------------------------- // // parameter // // ----------------------------------- parameter::~parameter() { if (m_kind == PARAM_RATIONAL) { reinterpret_cast(m_rational)->~rational(); } } parameter::parameter(parameter const& other) { m_kind = PARAM_INT; m_int = 0; *this = other; } parameter& parameter::operator=(parameter const& other) { if (this == &other) { return *this; } if (m_kind == PARAM_RATIONAL) { reinterpret_cast(m_rational)->~rational(); } m_kind = other.m_kind; switch(other.m_kind) { case PARAM_INT: m_int = other.get_int(); break; case PARAM_AST: m_ast = other.get_ast(); break; case PARAM_SYMBOL: new (m_symbol) symbol(other.get_symbol()); break; case PARAM_RATIONAL: new (m_rational) rational(other.get_rational()); break; case PARAM_DOUBLE: m_dval = other.m_dval; break; case PARAM_EXTERNAL: m_ext_id = other.m_ext_id; break; default: UNREACHABLE(); break; } return *this; } void parameter::init_eh(ast_manager & m) { if (is_ast()) { m.inc_ref(get_ast()); } } void parameter::del_eh(ast_manager & m, family_id fid) { if (is_ast()) { m.dec_ref(get_ast()); } else if (is_external()) { SASSERT(fid != null_family_id); m.get_plugin(fid)->del(*this); } } bool parameter::operator==(parameter const & p) const { if (m_kind != p.m_kind) return false; switch(m_kind) { case PARAM_INT: return m_int == p.m_int; case PARAM_AST: return m_ast == p.m_ast; case PARAM_SYMBOL: return get_symbol() == p.get_symbol(); case PARAM_RATIONAL: return get_rational() == p.get_rational(); case PARAM_DOUBLE: return m_dval == p.m_dval; case PARAM_EXTERNAL: return m_ext_id == p.m_ext_id; default: UNREACHABLE(); return false; } } unsigned parameter::hash() const { unsigned b = 0; switch(m_kind) { case PARAM_INT: b = m_int; break; case PARAM_AST: b = m_ast->hash(); break; case PARAM_SYMBOL: b = get_symbol().hash(); break; case PARAM_RATIONAL: b = get_rational().hash(); break; case PARAM_DOUBLE: b = static_cast(m_dval); break; case PARAM_EXTERNAL: b = m_ext_id; break; } return (b << 2) | m_kind; } std::ostream& parameter::display(std::ostream& out) const { switch(m_kind) { case PARAM_INT: return out << get_int(); case PARAM_SYMBOL: return out << get_symbol(); case PARAM_RATIONAL: return out << get_rational(); case PARAM_AST: return out << "#" << get_ast()->get_id(); case PARAM_DOUBLE: return out << m_dval; case PARAM_EXTERNAL: return out << "@" << m_ext_id; default: UNREACHABLE(); return out << "[invalid parameter]"; } } void display_parameters(std::ostream & out, unsigned n, parameter const * p) { if (n > 0) { out << "["; for (unsigned i = 0; i < n; i ++) out << p[i] << (i < n-1 ? ":" : ""); out << "]"; } } // ----------------------------------- // // family_manager // // ----------------------------------- family_id family_manager::mk_family_id(symbol const & s) { family_id r; if (m_families.find(s, r)) { return r; } r = m_next_id; m_next_id++; m_families.insert(s, r); m_names.push_back(s); return r; } family_id family_manager::get_family_id(symbol const & s) const { family_id r; if (m_families.find(s, r)) return r; else return null_family_id; } bool family_manager::has_family(symbol const & s) const { return m_families.contains(s); } // ----------------------------------- // // decl_info // // ----------------------------------- decl_info::decl_info(family_id family_id, decl_kind k, unsigned num_parameters, parameter const * parameters, bool private_params): m_family_id(family_id), m_kind(k), m_parameters(num_parameters, const_cast(parameters)), m_private_parameters(private_params) { } decl_info::decl_info(decl_info const& other) : m_family_id(other.m_family_id), m_kind(other.m_kind), m_parameters(other.m_parameters.size(), other.m_parameters.c_ptr()), m_private_parameters(other.m_private_parameters) { } void decl_info::init_eh(ast_manager & m) { vector::iterator it = m_parameters.begin(); vector::iterator end = m_parameters.end(); for (; it != end; ++it) { it->init_eh(m); } } void decl_info::del_eh(ast_manager & m) { vector::iterator it = m_parameters.begin(); vector::iterator end = m_parameters.end(); for (; it != end; ++it) { it->del_eh(m, m_family_id); } } struct decl_info_child_hash_proc { unsigned operator()(decl_info const * info, unsigned idx) const { return info->get_parameter(idx).hash(); } }; unsigned decl_info::hash() const { unsigned a = m_family_id; unsigned b = m_kind; unsigned c = get_num_parameters() == 0 ? 0 : get_composite_hash, decl_info_child_hash_proc>(this, get_num_parameters()); mix(a, b, c); return c; } bool decl_info::operator==(decl_info const & info) const { return m_family_id == info.m_family_id && m_kind == info.m_kind && compare_arrays(m_parameters.begin(), info.m_parameters.begin(), m_parameters.size()); } std::ostream & operator<<(std::ostream & out, decl_info const & info) { out << ":fid " << info.get_family_id() << " :decl-kind " << info.get_decl_kind() << " :parameters ("; for (unsigned i = 0; i < info.get_num_parameters(); i++) { if (i > 0) out << " "; out << info.get_parameter(i); } out << ")"; return out; } // ----------------------------------- // // sort_size // // ----------------------------------- std::ostream& operator<<(std::ostream& out, sort_size const & ss) { if (ss.is_infinite()) { return out << "infinite"; } else if (ss.is_very_big()) { return out << "very-big"; } else { SASSERT(ss.is_finite()); return out << ss.size(); } } // ----------------------------------- // // sort_info // // ----------------------------------- std::ostream & operator<<(std::ostream & out, sort_info const & info) { operator<<(out, static_cast(info)); out << " :size " << info.get_num_elements(); return out; } // ----------------------------------- // // func_decl_info // // ----------------------------------- func_decl_info::func_decl_info(family_id family_id, decl_kind k, unsigned num_parameters, parameter const * parameters): decl_info(family_id, k, num_parameters, parameters), m_left_assoc(false), m_right_assoc(false), m_flat_associative(false), m_commutative(false), m_chainable(false), m_pairwise(false), m_injective(false), m_idempotent(false), m_skolem(false) { } bool func_decl_info::operator==(func_decl_info const & info) const { return decl_info::operator==(info) && m_left_assoc == info.m_left_assoc && m_right_assoc == info.m_right_assoc && m_flat_associative == info.m_flat_associative && m_commutative == info.m_commutative && m_chainable == info.m_chainable && m_pairwise == info.m_pairwise && m_injective == info.m_injective && m_skolem == info.m_skolem; } std::ostream & operator<<(std::ostream & out, func_decl_info const & info) { operator<<(out, static_cast(info)); out << " :left-assoc " << info.is_left_associative(); out << " :right-assoc " << info.is_right_associative(); out << " :flat-associative " << info.is_flat_associative(); out << " :commutative " << info.is_commutative(); out << " :chainable " << info.is_chainable(); out << " :pairwise " << info.is_pairwise(); out << " :injective " << info.is_injective(); out << " :idempotent " << info.is_idempotent(); out << " :skolem " << info.is_skolem(); return out; } // ----------------------------------- // // ast // // ----------------------------------- static char const * g_ast_kind_names[] = {"application", "variable", "quantifier", "sort", "function declaration" }; char const * get_ast_kind_name(ast_kind k) { return g_ast_kind_names[k]; } // ----------------------------------- // // func_decl // // ----------------------------------- func_decl::func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info * info): decl(AST_FUNC_DECL, name, info), m_arity(arity), m_range(range) { if (arity != 0) memcpy(const_cast(get_domain()), domain, sizeof(sort *) * arity); } // ----------------------------------- // // application // // ----------------------------------- static app_flags mk_const_flags() { app_flags r; r.m_depth = 1; r.m_ground = true; r.m_has_quantifiers = false; r.m_has_labels = false; return r; } static app_flags mk_default_app_flags() { app_flags r; r.m_depth = 1; r.m_ground = true; r.m_has_quantifiers = false; r.m_has_labels = false; return r; } app_flags app::g_constant_flags = mk_const_flags(); app::app(func_decl * decl, unsigned num_args, expr * const * args): expr(AST_APP), m_decl(decl), m_num_args(num_args) { for (unsigned i = 0; i < num_args; i++) m_args[i] = args[i]; } // ----------------------------------- // // quantifier // // ----------------------------------- quantifier::quantifier(bool forall, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, int weight, symbol const & qid, symbol const & skid, unsigned num_patterns, expr * const * patterns, unsigned num_no_patterns, expr * const * no_patterns): expr(AST_QUANTIFIER), m_forall(forall), m_num_decls(num_decls), m_expr(body), m_depth(::get_depth(body) + 1), m_weight(weight), m_has_unused_vars(true), m_has_labels(::has_labels(body)), m_qid(qid), m_skid(skid), m_num_patterns(num_patterns), m_num_no_patterns(num_no_patterns) { SASSERT(m_num_patterns == 0 || m_num_no_patterns == 0); memcpy(const_cast(get_decl_sorts()), decl_sorts, sizeof(sort *) * num_decls); memcpy(const_cast(get_decl_names()), decl_names, sizeof(symbol) * num_decls); if (num_patterns != 0) memcpy(const_cast(get_patterns()), patterns, sizeof(expr *) * num_patterns); if (num_no_patterns != 0) memcpy(const_cast(get_no_patterns()), no_patterns, sizeof(expr *) * num_no_patterns); } // ----------------------------------- // // Auxiliary functions // // ----------------------------------- sort * get_sort(expr const * n) { while (true) { switch(n->get_kind()) { case AST_APP: return to_app(n)->get_decl()->get_range(); case AST_VAR: return to_var(n)->get_sort(); case AST_QUANTIFIER: // The sort of the quantifier is the sort of the nested expression. // This code assumes the given expression is well-sorted. n = to_quantifier(n)->get_expr(); break; default: UNREACHABLE(); return 0; } } } // ----------------------------------- // // AST hash-consing // // ----------------------------------- unsigned get_node_size(ast const * n) { switch(n->get_kind()) { case AST_SORT: return to_sort(n)->get_size(); case AST_FUNC_DECL: return to_func_decl(n)->get_size(); case AST_APP: return to_app(n)->get_size(); case AST_VAR: return to_var(n)->get_size(); case AST_QUANTIFIER: return to_quantifier(n)->get_size(); default: UNREACHABLE(); } return 0; } bool compare_nodes(ast const * n1, ast const * n2) { if (n1->get_kind() != n2->get_kind()) { return false; } switch (n1->get_kind()) { case AST_SORT: if ((to_sort(n1)->get_info() == 0) != (to_sort(n2)->get_info() == 0)) { return false; } if (to_sort(n1)->get_info() != 0 && !(*to_sort(n1)->get_info() == *to_sort(n2)->get_info())) { return false; } return to_sort(n1)->get_name() == to_sort(n2)->get_name(); case AST_FUNC_DECL: if ((to_func_decl(n1)->get_info() == 0) != (to_func_decl(n2)->get_info() == 0)) { return false; } if (to_func_decl(n1)->get_info() != 0 && !(*to_func_decl(n1)->get_info() == *to_func_decl(n2)->get_info())) { return false; } return to_func_decl(n1)->get_name() == to_func_decl(n2)->get_name() && to_func_decl(n1)->get_arity() == to_func_decl(n2)->get_arity() && to_func_decl(n1)->get_range() == to_func_decl(n2)->get_range() && compare_arrays(to_func_decl(n1)->get_domain(), to_func_decl(n2)->get_domain(), to_func_decl(n1)->get_arity()); case AST_APP: return to_app(n1)->get_decl() == to_app(n2)->get_decl() && to_app(n1)->get_num_args() == to_app(n2)->get_num_args() && compare_arrays(to_app(n1)->get_args(), to_app(n2)->get_args(), to_app(n1)->get_num_args()); case AST_VAR: return to_var(n1)->get_idx() == to_var(n2)->get_idx() && to_var(n1)->get_sort() == to_var(n2)->get_sort(); case AST_QUANTIFIER: return to_quantifier(n1)->is_forall() == to_quantifier(n2)->is_forall() && to_quantifier(n1)->get_num_decls() == to_quantifier(n2)->get_num_decls() && compare_arrays(to_quantifier(n1)->get_decl_sorts(), to_quantifier(n2)->get_decl_sorts(), to_quantifier(n1)->get_num_decls()) && to_quantifier(n1)->get_expr() == to_quantifier(n2)->get_expr() && to_quantifier(n1)->get_weight() == to_quantifier(n2)->get_weight() && to_quantifier(n1)->get_num_patterns() == to_quantifier(n2)->get_num_patterns() && compare_arrays(to_quantifier(n1)->get_patterns(), to_quantifier(n2)->get_patterns(), to_quantifier(n1)->get_num_patterns()) && to_quantifier(n1)->get_num_no_patterns() == to_quantifier(n2)->get_num_no_patterns() && compare_arrays(to_quantifier(n1)->get_no_patterns(), to_quantifier(n2)->get_no_patterns(), to_quantifier(n1)->get_num_no_patterns()); default: UNREACHABLE(); } return false; } template inline unsigned ast_array_hash(T * const * array, unsigned size, unsigned init_value) { if (size == 0) return init_value; switch (size) { case 1: return combine_hash(array[0]->hash(), init_value); case 2: return combine_hash(combine_hash(array[0]->hash(), array[1]->hash()), init_value); case 3: return combine_hash(combine_hash(array[0]->hash(), array[1]->hash()), combine_hash(array[2]->hash(), init_value)); default: { unsigned a, b, c; a = b = 0x9e3779b9; c = init_value; while (size >= 3) { size--; a += array[size]->hash(); size--; b += array[size]->hash(); size--; c += array[size]->hash(); mix(a, b, c); } switch (size) { case 2: b += array[1]->hash(); __fallthrough; case 1: c += array[0]->hash(); } mix(a, b, c); return c; } } } unsigned get_asts_hash(unsigned sz, ast * const* ns, unsigned init) { return ast_array_hash(ns, sz, init); } unsigned get_apps_hash(unsigned sz, app * const* ns, unsigned init) { return ast_array_hash(ns, sz, init); } unsigned get_exprs_hash(unsigned sz, expr * const* ns, unsigned init) { return ast_array_hash(ns, sz, init); } unsigned get_sorts_hash(unsigned sz, sort * const* ns, unsigned init) { return ast_array_hash(ns, sz, init); } unsigned get_decl_hash(unsigned sz, func_decl* const* ns, unsigned init) { return ast_array_hash(ns, sz, init); } unsigned get_node_hash(ast const * n) { unsigned a, b, c; switch (n->get_kind()) { case AST_SORT: if (to_sort(n)->get_info() == 0) return to_sort(n)->get_name().hash(); else return combine_hash(to_sort(n)->get_name().hash(), to_sort(n)->get_info()->hash()); case AST_FUNC_DECL: return ast_array_hash(to_func_decl(n)->get_domain(), to_func_decl(n)->get_arity(), to_func_decl(n)->get_info() == 0 ? to_func_decl(n)->get_name().hash() : combine_hash(to_func_decl(n)->get_name().hash(), to_func_decl(n)->get_info()->hash())); case AST_APP: return ast_array_hash(to_app(n)->get_args(), to_app(n)->get_num_args(), to_app(n)->get_decl()->hash()); case AST_VAR: return combine_hash(to_var(n)->get_idx(), to_var(n)->get_sort()->hash()); case AST_QUANTIFIER: a = ast_array_hash(to_quantifier(n)->get_decl_sorts(), to_quantifier(n)->get_num_decls(), to_quantifier(n)->is_forall() ? 31 : 19); b = to_quantifier(n)->get_num_patterns(); c = to_quantifier(n)->get_expr()->hash(); mix(a,b,c); return c; default: UNREACHABLE(); } return 0; } void ast_table::erase(ast * n) { // Customized erase method // It uses two important properties: // 1. n is known to be in the table. // 2. operator== can be used instead of compare_nodes (big savings) unsigned mask = m_slots - 1; unsigned h = n->hash(); unsigned idx = h & mask; cell * c = m_table + idx; SASSERT(!c->is_free()); cell * prev = 0; while (true) { if (c->m_data == n) { m_size--; if (prev == 0) { cell * next = c->m_next; if (next == 0) { m_used_slots--; c->mark_free(); SASSERT(c->is_free()); } else { *c = *next; recycle_cell(next); } } else { prev->m_next = c->m_next; recycle_cell(c); } return; } CHS_CODE(m_collisions++;); prev = c; c = c->m_next; SASSERT(c); } } // ----------------------------------- // // decl_plugin // // ----------------------------------- func_decl * decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { ptr_buffer sorts; for (unsigned i = 0; i < num_args; i++) { sorts.push_back(m_manager->get_sort(args[i])); } return mk_func_decl(k, num_parameters, parameters, num_args, sorts.c_ptr(), range); } // ----------------------------------- // // basic_decl_plugin (i.e., builtin plugin) // // ----------------------------------- basic_decl_plugin::basic_decl_plugin(): m_bool_sort(0), m_true_decl(0), m_false_decl(0), m_and_decl(0), m_or_decl(0), m_iff_decl(0), m_xor_decl(0), m_not_decl(0), m_interp_decl(0), m_implies_decl(0), m_proof_sort(0), m_undef_decl(0), m_true_pr_decl(0), m_asserted_decl(0), m_goal_decl(0), m_modus_ponens_decl(0), m_reflexivity_decl(0), m_symmetry_decl(0), m_transitivity_decl(0), m_quant_intro_decl(0), m_and_elim_decl(0), m_not_or_elim_decl(0), m_rewrite_decl(0), m_pull_quant_decl(0), m_pull_quant_star_decl(0), m_push_quant_decl(0), m_elim_unused_vars_decl(0), m_der_decl(0), m_quant_inst_decl(0), m_hypothesis_decl(0), m_iff_true_decl(0), m_iff_false_decl(0), m_commutativity_decl(0), m_def_axiom_decl(0), m_lemma_decl(0), m_def_intro_decl(0), m_iff_oeq_decl(0), m_skolemize_decl(0), m_mp_oeq_decl(0), m_hyper_res_decl0(0) { } bool basic_decl_plugin::check_proof_sorts(basic_op_kind k, unsigned arity, sort * const * domain) const { if (k == PR_UNDEF) return arity == 0; if (arity == 0) return false; else { for (unsigned i = 0; i < arity - 1; i++) if (domain[i] != m_proof_sort) return false; return domain[arity-1] == m_bool_sort || domain[arity-1] == m_proof_sort; } } bool basic_decl_plugin::check_proof_args(basic_op_kind k, unsigned num_args, expr * const * args) const { if (k == PR_UNDEF) return num_args == 0; if (num_args == 0) return false; else { for (unsigned i = 0; i < num_args - 1; i++) if (m_manager->get_sort(args[i]) != m_proof_sort) return false; return m_manager->get_sort(args[num_args - 1]) == m_bool_sort || m_manager->get_sort(args[num_args - 1]) == m_proof_sort; } } func_decl * basic_decl_plugin::mk_bool_op_decl(char const * name, basic_op_kind k, unsigned num_args, bool assoc, bool comm, bool idempotent, bool flat_associative, bool chainable) { ptr_buffer domain; for (unsigned i = 0; i < num_args; i++) domain.push_back(m_bool_sort); func_decl_info info(m_family_id, k); info.set_associative(assoc); info.set_flat_associative(flat_associative); info.set_commutative(comm); info.set_idempotent(idempotent); info.set_chainable(chainable); func_decl * d = m_manager->mk_func_decl(symbol(name), num_args, domain.c_ptr(), m_bool_sort, info); m_manager->inc_ref(d); return d; } func_decl * basic_decl_plugin::mk_implies_decl() { sort * domain[2] = { m_bool_sort, m_bool_sort }; func_decl_info info(m_family_id, OP_IMPLIES); info.set_right_associative(); func_decl * d = m_manager->mk_func_decl(symbol("=>"), 2, domain, m_bool_sort, info); m_manager->inc_ref(d); return d; } func_decl * basic_decl_plugin::mk_proof_decl( char const * name, basic_op_kind k, unsigned num_parameters, parameter const* params, unsigned num_parents) { ptr_buffer domain; for (unsigned i = 0; i < num_parents; i++) domain.push_back(m_proof_sort); domain.push_back(m_bool_sort); func_decl_info info(m_family_id, k, num_parameters, params); return m_manager->mk_func_decl(symbol(name), num_parents+1, domain.c_ptr(), m_proof_sort, info); } func_decl * basic_decl_plugin::mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents) { ptr_buffer domain; for (unsigned i = 0; i < num_parents; i++) domain.push_back(m_proof_sort); domain.push_back(m_bool_sort); func_decl * d = m_manager->mk_func_decl(symbol(name), num_parents+1, domain.c_ptr(), m_proof_sort, func_decl_info(m_family_id, k)); m_manager->inc_ref(d); return d; } func_decl * basic_decl_plugin::mk_compressed_proof_decl(char const * name, basic_op_kind k, unsigned num_parents) { ptr_buffer domain; for (unsigned i = 0; i < num_parents; i++) domain.push_back(m_proof_sort); func_decl * d = m_manager->mk_func_decl(symbol(name), num_parents, domain.c_ptr(), m_proof_sort, func_decl_info(m_family_id, k)); m_manager->inc_ref(d); return d; } func_decl * basic_decl_plugin::mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents, ptr_vector & cache) { if (num_parents >= cache.size()) { cache.resize(num_parents+1, 0); } if (cache[num_parents] == 0) { cache[num_parents] = mk_proof_decl(name, k, num_parents); } return cache[num_parents]; } func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_parameters, parameter const* params, unsigned num_parents) { switch(k) { case PR_TH_LEMMA: { return mk_proof_decl("th-lemma", k, num_parameters, params, num_parents); } case PR_QUANT_INST: { SASSERT(num_parents == 0); return mk_proof_decl("quant-inst", k, num_parameters, params, num_parents); } case PR_HYPER_RESOLVE: { return mk_proof_decl("hyper-res", k, num_parameters, params, num_parents); } default: UNREACHABLE(); return 0; } } #define MK_DECL(_decl_,_mk_decl_) if (!_decl_) _decl_ = _mk_decl_; return _decl_; func_decl * basic_decl_plugin::mk_proof_decl(char const* name, basic_op_kind k, unsigned num_parents, func_decl*& fn) { if (!fn) { fn = mk_proof_decl(name, k, num_parents); } return fn; } func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_parents) { SASSERT(k == PR_UNDEF || m_manager->proofs_enabled()); switch (static_cast(k)) { // // A description of the semantics of the proof // declarations is provided in z3_api.h // case PR_UNDEF: return m_undef_decl; case PR_TRUE: return mk_proof_decl("true-axiom", k, 0, m_true_pr_decl); case PR_ASSERTED: return mk_proof_decl("asserted", k, 0, m_asserted_decl); case PR_GOAL: return mk_proof_decl("goal", k, 2, m_goal_decl); case PR_MODUS_PONENS: return mk_proof_decl("mp", k, 2, m_modus_ponens_decl); case PR_REFLEXIVITY: return mk_proof_decl("refl", k, 0, m_reflexivity_decl); case PR_SYMMETRY: return mk_proof_decl("symm", k, 1, m_symmetry_decl); case PR_TRANSITIVITY: return mk_proof_decl("trans", k, 2, m_transitivity_decl); case PR_TRANSITIVITY_STAR: return mk_proof_decl("trans*", k, num_parents, m_transitivity_star_decls); case PR_MONOTONICITY: return mk_proof_decl("monotonicity", k, num_parents, m_monotonicity_decls); case PR_QUANT_INTRO: return mk_proof_decl("quant-intro", k, 1, m_quant_intro_decl); case PR_DISTRIBUTIVITY: return mk_proof_decl("distributivity", k, num_parents, m_distributivity_decls); case PR_AND_ELIM: return mk_proof_decl("and-elim", k, 1, m_and_elim_decl); case PR_NOT_OR_ELIM: return mk_proof_decl("not-or-elim", k, 1, m_not_or_elim_decl); case PR_REWRITE: return mk_proof_decl("rewrite", k, 0, m_rewrite_decl); case PR_REWRITE_STAR: return mk_proof_decl("rewrite*", k, num_parents, m_rewrite_star_decls); case PR_PULL_QUANT: return mk_proof_decl("pull-quant", k, 0, m_pull_quant_decl); case PR_PULL_QUANT_STAR: return mk_proof_decl("pull-quant*", k, 0, m_pull_quant_star_decl); case PR_PUSH_QUANT: return mk_proof_decl("push-quant", k, 0, m_push_quant_decl); case PR_ELIM_UNUSED_VARS: return mk_proof_decl("elim-unused", k, 0, m_elim_unused_vars_decl); case PR_DER: return mk_proof_decl("der", k, 0, m_der_decl); case PR_QUANT_INST: return mk_proof_decl("quant-inst", k, 0, m_quant_inst_decl); case PR_HYPOTHESIS: return mk_proof_decl("hypothesis", k, 0, m_hypothesis_decl); case PR_LEMMA: return mk_proof_decl("lemma", k, 1, m_lemma_decl); case PR_UNIT_RESOLUTION: return mk_proof_decl("unit-resolution", k, num_parents, m_unit_resolution_decls); case PR_IFF_TRUE: return mk_proof_decl("iff-true", k, 1, m_iff_true_decl); case PR_IFF_FALSE: return mk_proof_decl("iff-false", k, 1, m_iff_false_decl); case PR_COMMUTATIVITY: return mk_proof_decl("commutativity", k, 0, m_commutativity_decl); case PR_DEF_AXIOM: return mk_proof_decl("def-axiom", k, 0, m_def_axiom_decl); case PR_DEF_INTRO: return mk_proof_decl("intro-def", k, 0, m_def_intro_decl); case PR_APPLY_DEF: return mk_proof_decl("apply-def", k, num_parents, m_apply_def_decls); case PR_IFF_OEQ: return mk_proof_decl("iff~", k, 1, m_iff_oeq_decl); case PR_NNF_POS: return mk_proof_decl("nnf-pos", k, num_parents, m_nnf_pos_decls); case PR_NNF_NEG: return mk_proof_decl("nnf-neg", k, num_parents, m_nnf_neg_decls); case PR_NNF_STAR: return mk_proof_decl("nnf*", k, num_parents, m_nnf_star_decls); case PR_CNF_STAR: return mk_proof_decl("cnf*", k, num_parents, m_cnf_star_decls); case PR_SKOLEMIZE: return mk_proof_decl("sk", k, 0, m_skolemize_decl); case PR_MODUS_PONENS_OEQ: return mk_proof_decl("mp~", k, 2, m_mp_oeq_decl); case PR_TH_LEMMA: return mk_proof_decl("th-lemma", k, num_parents, m_th_lemma_decls); case PR_HYPER_RESOLVE: return mk_proof_decl("hyper-res", k, num_parents, m_hyper_res_decl0); default: UNREACHABLE(); return 0; } } void basic_decl_plugin::set_manager(ast_manager * m, family_id id) { decl_plugin::set_manager(m, id); m_bool_sort = m->mk_sort(symbol("Bool"), sort_info(id, BOOL_SORT, sort_size(2))); m->inc_ref(m_bool_sort); m_true_decl = mk_bool_op_decl("true", OP_TRUE); m_false_decl = mk_bool_op_decl("false", OP_FALSE); m_and_decl = mk_bool_op_decl("and", OP_AND, 2, true, true, true, true); m_or_decl = mk_bool_op_decl("or", OP_OR, 2, true, true, true, true); m_iff_decl = mk_bool_op_decl("iff", OP_IFF, 2, false, true, false, false, true); m_xor_decl = mk_bool_op_decl("xor", OP_XOR, 2, true, true); m_not_decl = mk_bool_op_decl("not", OP_NOT, 1); m_interp_decl = mk_bool_op_decl("interp", OP_INTERP, 1); m_implies_decl = mk_implies_decl(); m_proof_sort = m->mk_sort(symbol("Proof"), sort_info(id, PROOF_SORT)); m->inc_ref(m_proof_sort); m_undef_decl = mk_compressed_proof_decl("undef", PR_UNDEF, 0); } void basic_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { if (logic == symbol::null) sort_names.push_back(builtin_name("bool", BOOL_SORT)); sort_names.push_back(builtin_name("Bool", BOOL_SORT)); } void basic_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { op_names.push_back(builtin_name("true", OP_TRUE)); op_names.push_back(builtin_name("false", OP_FALSE)); op_names.push_back(builtin_name("=", OP_EQ)); op_names.push_back(builtin_name("distinct", OP_DISTINCT)); op_names.push_back(builtin_name("ite", OP_ITE)); op_names.push_back(builtin_name("and", OP_AND)); op_names.push_back(builtin_name("or", OP_OR)); op_names.push_back(builtin_name("xor", OP_XOR)); op_names.push_back(builtin_name("not", OP_NOT)); op_names.push_back(builtin_name("interp", OP_INTERP)); op_names.push_back(builtin_name("=>", OP_IMPLIES)); if (logic == symbol::null) { // user friendly aliases op_names.push_back(builtin_name("implies", OP_IMPLIES)); op_names.push_back(builtin_name("iff", OP_IFF)); op_names.push_back(builtin_name("if_then_else", OP_ITE)); op_names.push_back(builtin_name("if", OP_ITE)); op_names.push_back(builtin_name("&&", OP_AND)); op_names.push_back(builtin_name("||", OP_OR)); op_names.push_back(builtin_name("equals", OP_EQ)); op_names.push_back(builtin_name("equiv", OP_IFF)); op_names.push_back(builtin_name("@@", OP_INTERP)); } } bool basic_decl_plugin::is_value(app* a) const { return a->get_decl() == m_true_decl || a->get_decl() == m_false_decl; } bool basic_decl_plugin::is_unique_value(app* a) const { return is_value(a); } void basic_decl_plugin::finalize() { #define DEC_REF(FIELD) if (FIELD) { m_manager->dec_ref(FIELD); } #define DEC_ARRAY_REF(FIELD) m_manager->dec_array_ref(FIELD.size(), FIELD.begin()) DEC_REF(m_bool_sort); DEC_REF(m_true_decl); DEC_REF(m_false_decl); DEC_REF(m_and_decl); DEC_REF(m_or_decl); DEC_REF(m_not_decl); DEC_REF(m_interp_decl); DEC_REF(m_iff_decl); DEC_REF(m_xor_decl); DEC_REF(m_implies_decl); DEC_ARRAY_REF(m_eq_decls); DEC_ARRAY_REF(m_ite_decls); DEC_ARRAY_REF(m_oeq_decls); DEC_REF(m_proof_sort); DEC_REF(m_undef_decl); DEC_REF(m_true_pr_decl); DEC_REF(m_asserted_decl); DEC_REF(m_goal_decl); DEC_REF(m_modus_ponens_decl); DEC_REF(m_reflexivity_decl); DEC_REF(m_symmetry_decl); DEC_REF(m_transitivity_decl); DEC_REF(m_quant_intro_decl); DEC_REF(m_and_elim_decl); DEC_REF(m_not_or_elim_decl); DEC_REF(m_rewrite_decl); DEC_REF(m_pull_quant_decl); DEC_REF(m_pull_quant_star_decl); DEC_REF(m_push_quant_decl); DEC_REF(m_elim_unused_vars_decl); DEC_REF(m_der_decl); DEC_REF(m_quant_inst_decl); DEC_ARRAY_REF(m_monotonicity_decls); DEC_ARRAY_REF(m_transitivity_star_decls); DEC_ARRAY_REF(m_distributivity_decls); DEC_ARRAY_REF(m_assoc_flat_decls); DEC_ARRAY_REF(m_rewrite_star_decls); DEC_REF(m_hypothesis_decl); DEC_REF(m_iff_true_decl); DEC_REF(m_iff_false_decl); DEC_REF(m_commutativity_decl); DEC_REF(m_def_axiom_decl); DEC_REF(m_lemma_decl); DEC_ARRAY_REF(m_unit_resolution_decls); DEC_REF(m_def_intro_decl); DEC_REF(m_iff_oeq_decl); DEC_REF(m_skolemize_decl); DEC_REF(m_mp_oeq_decl); DEC_ARRAY_REF(m_apply_def_decls); DEC_ARRAY_REF(m_nnf_pos_decls); DEC_ARRAY_REF(m_nnf_neg_decls); DEC_ARRAY_REF(m_nnf_star_decls); DEC_ARRAY_REF(m_cnf_star_decls); DEC_ARRAY_REF(m_th_lemma_decls); DEC_REF(m_hyper_res_decl0); } sort * basic_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { if (k == BOOL_SORT) return m_bool_sort; SASSERT(k == PROOF_SORT); return m_proof_sort; } func_decl * basic_decl_plugin::mk_eq_decl_core(char const * name, decl_kind k, sort * s, ptr_vector & cache) { unsigned id = s->get_decl_id(); force_ptr_array_size(cache, id + 1); if (cache[id] == 0) { sort * domain[2] = { s, s}; func_decl_info info(m_family_id, k); info.set_commutative(); info.set_chainable(); func_decl * decl = m_manager->mk_func_decl(symbol(name), 2, domain, m_bool_sort, info); SASSERT(decl->is_chainable()); cache[id] = decl; m_manager->inc_ref(decl); } return cache[id]; } func_decl * basic_decl_plugin::mk_ite_decl(sort * s) { unsigned id = s->get_decl_id(); force_ptr_array_size(m_ite_decls, id + 1); if (m_ite_decls[id] == 0) { sort * domain[3] = { m_bool_sort, s, s}; func_decl * decl = m_manager->mk_func_decl(symbol("if"), 3, domain, s, func_decl_info(m_family_id, OP_ITE)); m_ite_decls[id] = decl; m_manager->inc_ref(decl); } return m_ite_decls[id]; } sort* basic_decl_plugin::join(sort* s1, sort* s2) { if (s1 == s2) return s1; if (s1->get_family_id() == m_manager->m_arith_family_id && s2->get_family_id() == m_manager->m_arith_family_id) { if (s1->get_decl_kind() == REAL_SORT) { return s1; } } return s2; } func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { switch (static_cast(k)) { case OP_TRUE: return m_true_decl; case OP_FALSE: return m_false_decl; case OP_AND: return m_and_decl; case OP_OR: return m_or_decl; case OP_NOT: return m_not_decl; case OP_INTERP: return m_interp_decl; case OP_IFF: return m_iff_decl; case OP_IMPLIES: return m_implies_decl; case OP_XOR: return m_xor_decl; case OP_ITE: return arity == 3 ? mk_ite_decl(join(domain[1], domain[2])) : 0; // eq and oeq must have at least two arguments, they can have more since they are chainable case OP_EQ: return arity >= 2 ? mk_eq_decl_core("=", OP_EQ, join(domain[0],domain[1]), m_eq_decls) : 0; case OP_OEQ: return arity >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(domain[0],domain[1]), m_oeq_decls) : 0; case OP_DISTINCT: { func_decl_info info(m_family_id, OP_DISTINCT); info.set_pairwise(); for (unsigned i = 1; i < arity; i++) { if (domain[i] != domain[0]) { std::ostringstream buffer; buffer << "Sort mismatch between first argument and argument " << (i+1); throw ast_exception(buffer.str().c_str()); } } return m_manager->mk_func_decl(symbol("distinct"), arity, domain, m_bool_sort, info); } default: break; } SASSERT(is_proof(k)); if (!check_proof_sorts(static_cast(k), arity, domain)) m_manager->raise_exception("Invalid proof object."); if (num_parameters == 0) { return mk_proof_decl(static_cast(k), arity - 1); } return mk_proof_decl(static_cast(k), num_parameters, parameters, arity - 1); } func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { switch (static_cast(k)) { case OP_TRUE: return m_true_decl; case OP_FALSE: return m_false_decl; case OP_AND: return m_and_decl; case OP_OR: return m_or_decl; case OP_NOT: return m_not_decl; case OP_INTERP: return m_interp_decl; case OP_IFF: return m_iff_decl; case OP_IMPLIES: return m_implies_decl; case OP_XOR: return m_xor_decl; case OP_ITE: return num_args == 3 ? mk_ite_decl(join(m_manager->get_sort(args[1]), m_manager->get_sort(args[2]))): 0; // eq and oeq must have at least two arguments, they can have more since they are chainable case OP_EQ: return num_args >= 2 ? mk_eq_decl_core("=", OP_EQ, join(m_manager->get_sort(args[0]), m_manager->get_sort(args[1])), m_eq_decls) : 0; case OP_OEQ: return num_args >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(m_manager->get_sort(args[0]), m_manager->get_sort(args[1])), m_oeq_decls) : 0; case OP_DISTINCT: return decl_plugin::mk_func_decl(k, num_parameters, parameters, num_args, args, range); default: break; } SASSERT(is_proof(k)); if (!check_proof_args(static_cast(k), num_args, args)) m_manager->raise_exception("Invalid proof object."); if (num_parameters == 0) { return mk_proof_decl(static_cast(k), num_args - 1); } return mk_proof_decl(static_cast(k), num_parameters, parameters, num_args - 1); } expr * basic_decl_plugin::get_some_value(sort * s) { if (s == m_bool_sort) return m_manager->mk_false(); return 0; } bool basic_recognizers::is_ite(expr const * n, expr * & t1, expr * & t2, expr * & t3) const { if (is_ite(n)) { t1 = to_app(n)->get_arg(0); t2 = to_app(n)->get_arg(1); t3 = to_app(n)->get_arg(2); return true; } return false; } // ----------------------------------- // // label_decl_plugin // // ----------------------------------- label_decl_plugin::label_decl_plugin(): m_lblpos("lblpos"), m_lblneg("lblneg"), m_lbllit("lbl-lit") { } label_decl_plugin::~label_decl_plugin() { } void label_decl_plugin::set_manager(ast_manager * m, family_id id) { decl_plugin::set_manager(m, id); } sort * label_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { UNREACHABLE(); return 0; } func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (k == OP_LABEL) { if (arity != 1 || num_parameters < 2 || !parameters[0].is_int() || !parameters[1].is_symbol() || !m_manager->is_bool(domain[0])) { m_manager->raise_exception("invalid label declaration"); return 0; } for (unsigned i = 2; i < num_parameters; i++) { if (!parameters[i].is_symbol()) { m_manager->raise_exception("invalid label declaration"); return 0; } } return m_manager->mk_func_decl(parameters[0].get_int() ? m_lblpos : m_lblneg, arity, domain, domain[0], func_decl_info(m_family_id, OP_LABEL, num_parameters, parameters)); } else { SASSERT(k == OP_LABEL_LIT); if (arity != 0) { m_manager->raise_exception("invalid label literal declaration"); return 0; } for (unsigned i = 0; i < num_parameters; i++) { if (!parameters[i].is_symbol()) { m_manager->raise_exception("invalid label literal declaration"); return 0; } } return m_manager->mk_func_decl(m_lbllit, 0, static_cast(0), m_manager->mk_bool_sort(), func_decl_info(m_family_id, OP_LABEL_LIT, num_parameters, parameters)); } } // ----------------------------------- // // pattern_decl_plugin // // ----------------------------------- sort * pattern_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { UNREACHABLE(); return 0; } func_decl * pattern_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { return m_manager->mk_func_decl(symbol("pattern"), arity, domain, m_manager->mk_bool_sort(), // the range can be an arbitrary sort. func_decl_info(m_family_id, OP_PATTERN)); } // ----------------------------------- // // model_value_decl_plugin // // ----------------------------------- sort * model_value_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { UNREACHABLE(); return 0; } func_decl * model_value_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { SASSERT(k == OP_MODEL_VALUE); if (arity != 0 || num_parameters != 2 || !parameters[0].is_int() || !parameters[1].is_ast() || !is_sort(parameters[1].get_ast())) { UNREACHABLE(); m_manager->raise_exception("invalid model value"); return 0; } int idx = parameters[0].get_int(); sort * s = to_sort(parameters[1].get_ast()); string_buffer<64> buffer; buffer << s->get_name().bare_str() << "!val!" << idx; func_decl_info info(m_family_id, k, num_parameters, parameters); info.m_private_parameters = true; return m_manager->mk_func_decl(symbol(buffer.c_str()), 0, static_cast(0), s, info); } bool model_value_decl_plugin::is_value(app* n) const { return is_app_of(n, m_family_id, OP_MODEL_VALUE); } bool model_value_decl_plugin::is_unique_value(app* n) const { return is_value(n); } // ----------------------------------- // // user_sort_plugin // // ----------------------------------- sort * user_sort_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { SASSERT(m_family_id != null_family_id); SASSERT(k < static_cast(m_sort_names.size())); sort_info si(m_family_id, k, num_parameters, parameters); return m_manager->mk_sort(m_sort_names[k], si); } func_decl * user_sort_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { UNREACHABLE(); return 0; } decl_kind user_sort_plugin::register_name(symbol s) { decl_kind k; if (m_name2decl_kind.find(s, k)) return k; k = m_sort_names.size(); m_sort_names.push_back(s); m_name2decl_kind.insert(s, k); return k; } decl_plugin * user_sort_plugin::mk_fresh() { user_sort_plugin * p = alloc(user_sort_plugin); svector::iterator it = m_sort_names.begin(); svector::iterator end = m_sort_names.end(); for (; it != end; ++it) p->register_name(*it); return p; } // ----------------------------------- // // ast_manager // // ----------------------------------- ast_manager::ast_manager(proof_gen_mode m, char const * trace_file, bool is_format_manager): m_alloc("ast_manager"), m_expr_array_manager(*this, m_alloc), m_expr_dependency_manager(*this, m_alloc), m_expr_dependency_array_manager(*this, m_alloc), m_proof_mode(m), m_trace_stream(0), m_trace_stream_owner(false) { if (trace_file) { m_trace_stream = alloc(std::fstream, trace_file, std::ios_base::out); m_trace_stream_owner = true; } if (!is_format_manager) m_format_manager = alloc(ast_manager, PGM_DISABLED, m_trace_stream, true); else m_format_manager = 0; init(); } ast_manager::ast_manager(proof_gen_mode m, std::fstream * trace_stream, bool is_format_manager): m_alloc("ast_manager"), m_expr_array_manager(*this, m_alloc), m_expr_dependency_manager(*this, m_alloc), m_expr_dependency_array_manager(*this, m_alloc), m_proof_mode(m), m_trace_stream(trace_stream), m_trace_stream_owner(false) { if (!is_format_manager) m_format_manager = alloc(ast_manager, PGM_DISABLED, trace_stream, true); else m_format_manager = 0; init(); } ast_manager::ast_manager(ast_manager const & src, bool disable_proofs): m_alloc("ast_manager"), m_expr_array_manager(*this, m_alloc), m_expr_dependency_manager(*this, m_alloc), m_expr_dependency_array_manager(*this, m_alloc), m_proof_mode(disable_proofs ? PGM_DISABLED : src.m_proof_mode), m_trace_stream(src.m_trace_stream), m_trace_stream_owner(false) { SASSERT(!src.is_format_manager()); m_format_manager = alloc(ast_manager, PGM_DISABLED, m_trace_stream, true); init(); copy_families_plugins(src); } void ast_manager::init() { m_int_real_coercions = true; m_debug_ref_count = false; m_fresh_id = 0; m_expr_id_gen.reset(0); m_decl_id_gen.reset(c_first_decl_id); m_some_value_proc = 0; m_basic_family_id = mk_family_id("basic"); m_label_family_id = mk_family_id("label"); m_pattern_family_id = mk_family_id("pattern"); m_model_value_family_id = mk_family_id("model-value"); m_user_sort_family_id = mk_family_id("user-sort"); m_arith_family_id = mk_family_id("arith"); basic_decl_plugin * plugin = alloc(basic_decl_plugin); register_plugin(m_basic_family_id, plugin); m_bool_sort = plugin->mk_bool_sort(); inc_ref(m_bool_sort); m_proof_sort = plugin->mk_proof_sort(); inc_ref(m_proof_sort); m_undef_proof = mk_const(m_basic_family_id, PR_UNDEF); inc_ref(m_undef_proof); register_plugin(m_label_family_id, alloc(label_decl_plugin)); register_plugin(m_pattern_family_id, alloc(pattern_decl_plugin)); register_plugin(m_model_value_family_id, alloc(model_value_decl_plugin)); register_plugin(m_user_sort_family_id, alloc(user_sort_plugin)); m_true = mk_const(m_basic_family_id, OP_TRUE); inc_ref(m_true); m_false = mk_const(m_basic_family_id, OP_FALSE); inc_ref(m_false); } ast_manager::~ast_manager() { SASSERT(is_format_manager() || !m_family_manager.has_family(symbol("format"))); dec_ref(m_bool_sort); dec_ref(m_proof_sort); dec_ref(m_true); dec_ref(m_false); dec_ref(m_undef_proof); ptr_vector::iterator it = m_plugins.begin(); ptr_vector::iterator end = m_plugins.end(); for (; it != end; ++it) { if (*it) (*it)->finalize(); } it = m_plugins.begin(); for (; it != end; ++it) { if (*it) dealloc(*it); } DEBUG_CODE({ if (!m_ast_table.empty()) std::cout << "ast_manager LEAKED: " << m_ast_table.size() << std::endl; }); #if 1 DEBUG_CODE({ ast_table::iterator it_a = m_ast_table.begin(); ast_table::iterator end_a = m_ast_table.end(); for (; it_a != end_a; ++it_a) { ast* a = (*it_a); std::cout << "Leaked: "; if (is_sort(a)) { std::cout << to_sort(a)->get_name() << "\n"; } else { std::cout << mk_ll_pp(a, *this, false); } } }); #endif if (m_format_manager != 0) dealloc(m_format_manager); if (m_trace_stream_owner) { std::fstream & tmp = * m_trace_stream; tmp << "[eof]\n"; tmp.close(); dealloc(m_trace_stream); m_trace_stream = 0; } } void ast_manager::set_cancel(bool f) { for (unsigned i = 0; i < m_plugins.size(); i++) { m_plugins[i]->set_cancel(f); } } void ast_manager::compact_memory() { m_alloc.consolidate(); unsigned capacity = m_ast_table.capacity(); if (capacity > 4*m_ast_table.size()) { ast_table new_ast_table; ast_table::iterator it = m_ast_table.begin(); ast_table::iterator end = m_ast_table.end(); for (; it != end; ++it) { new_ast_table.insert(*it); } m_ast_table.swap(new_ast_table); IF_VERBOSE(10, verbose_stream() << "(ast-table :prev-capacity " << capacity << " :capacity " << m_ast_table.capacity() << " :size " << m_ast_table.size() << ")\n";); } else { IF_VERBOSE(10, verbose_stream() << "(ast-table :capacity " << m_ast_table.capacity() << " :size " << m_ast_table.size() << ")\n";); } } void ast_manager::compress_ids() { ptr_vector asts; m_expr_id_gen.cleanup(); m_decl_id_gen.cleanup(c_first_decl_id); ast_table::iterator it = m_ast_table.begin(); ast_table::iterator end = m_ast_table.end(); for (; it != end; ++it) { ast * n = *it; if (is_decl(n)) n->m_id = m_decl_id_gen.mk(); else n->m_id = m_expr_id_gen.mk(); asts.push_back(n); } m_ast_table.finalize(); ptr_vector::iterator it2 = asts.begin(); ptr_vector::iterator end2 = asts.end(); for (; it2 != end2; ++it2) m_ast_table.insert(*it2); } void ast_manager::raise_exception(char const * msg) { throw ast_exception(msg); } void ast_manager::copy_families_plugins(ast_manager const & from) { TRACE("copy_families_plugins", tout << "target:\n"; for (family_id fid = 0; m_family_manager.has_family(fid); fid++) { tout << "fid: " << fid << " fidname: " << get_family_name(fid) << "\n"; }); for (family_id fid = 0; from.m_family_manager.has_family(fid); fid++) { SASSERT(from.is_builtin_family_id(fid) == is_builtin_family_id(fid)); SASSERT(!from.is_builtin_family_id(fid) || m_family_manager.has_family(fid)); symbol fid_name = from.get_family_name(fid); TRACE("copy_families_plugins", tout << "copying: " << fid_name << ", src fid: " << fid << ", target has_family: " << m_family_manager.has_family(fid) << "\n"; if (m_family_manager.has_family(fid)) tout << get_family_id(fid_name) << "\n";); if (!m_family_manager.has_family(fid)) { family_id new_fid = mk_family_id(fid_name); TRACE("copy_families_plugins", tout << "new target fid created: " << new_fid << " fid_name: " << fid_name << "\n";); } TRACE("copy_families_plugins", tout << "target fid: " << get_family_id(fid_name) << "\n";); SASSERT(fid == get_family_id(fid_name)); if (from.has_plugin(fid) && !has_plugin(fid)) { decl_plugin * new_p = from.get_plugin(fid)->mk_fresh(); register_plugin(fid, new_p); SASSERT(new_p->get_family_id() == fid); SASSERT(has_plugin(fid)); } SASSERT(from.m_family_manager.has_family(fid) == m_family_manager.has_family(fid)); SASSERT(from.get_family_id(fid_name) == get_family_id(fid_name)); SASSERT(!from.has_plugin(fid) || has_plugin(fid)); } } void ast_manager::set_next_expr_id(unsigned id) { while (true) { id = m_expr_id_gen.set_next_id(id); ast_table::iterator it = m_ast_table.begin(); ast_table::iterator end = m_ast_table.end(); for (; it != end; ++it) { ast * curr = *it; if (curr->get_id() == id) break; } if (it == end) return; // id is in use, move to the next one. id++; } } unsigned ast_manager::get_node_size(ast const * n) { return ::get_node_size(n); } void ast_manager::register_plugin(symbol const & s, decl_plugin * plugin) { family_id id = m_family_manager.mk_family_id(s); SASSERT(is_format_manager() || s != symbol("format")); register_plugin(id, plugin); } decl_plugin * ast_manager::get_plugin(family_id fid) const { return m_plugins.get(fid, 0); } bool ast_manager::is_value(expr* e) const { decl_plugin const * p = 0; if (is_app(e)) { p = get_plugin(to_app(e)->get_family_id()); return p && p->is_value(to_app(e)); } return false; } bool ast_manager::is_unique_value(expr* e) const { decl_plugin const * p = 0; if (is_app(e)) { p = get_plugin(to_app(e)->get_family_id()); return p && p->is_unique_value(to_app(e)); } return false; } bool ast_manager::are_equal(expr * a, expr * b) const { if (is_app(a) && is_app(b)) { app* ap = to_app(a), *bp = to_app(b); decl_plugin const * p = get_plugin(ap->get_family_id()); if (!p) { p = get_plugin(bp->get_family_id()); } return p && p->are_equal(ap, bp); } return false; } bool ast_manager::are_distinct(expr* a, expr* b) const { if (is_app(a) && is_app(b)) { app* ap = to_app(a), *bp = to_app(b); decl_plugin const * p = get_plugin(ap->get_family_id()); if (!p) { p = get_plugin(bp->get_family_id()); } return p && p->are_distinct(ap, bp); } return false; } void ast_manager::register_plugin(family_id id, decl_plugin * plugin) { SASSERT(m_plugins.get(id, 0) == 0); m_plugins.setx(id, plugin, 0); plugin->set_manager(this, id); } bool ast_manager::is_bool(expr const * n) const { return get_sort(n) == m_bool_sort; } #ifdef Z3DEBUG bool ast_manager::slow_not_contains(ast const * n) { ast_table::iterator it = m_ast_table.begin(); ast_table::iterator end = m_ast_table.end(); unsigned num = 0; for (; it != end; ++it) { ast * curr = *it; if (compare_nodes(curr, n)) { TRACE("nondet_bug", tout << "id1: " << curr->get_id() << ", id2: " << n->get_id() << "\n"; tout << "hash1: " << get_node_hash(curr) << ", hash2: " << get_node_hash(n) << "\n";); return false; } SASSERT(!(is_app(n) && is_app(curr) && to_app(n)->get_decl() == to_app(curr)->get_decl() && to_app(n)->get_num_args() == 0 && to_app(curr)->get_num_args() == 0)); num++; } SASSERT(num == m_ast_table.size()); return true; } #endif ast * ast_manager::register_node_core(ast * n) { unsigned h = get_node_hash(n); n->m_hash = h; #ifdef Z3DEBUG bool contains = m_ast_table.contains(n); CASSERT("nondet_bug", contains || slow_not_contains(n)); #endif #if 0 static unsigned counter = 0; counter++; if (counter % 100000 == 0) verbose_stream() << "[ast-table] counter: " << counter << " collisions: " << m_ast_table.collisions() << " capacity: " << m_ast_table.capacity() << " size: " << m_ast_table.size() << "\n"; #endif ast * r = m_ast_table.insert_if_not_there(n); SASSERT(r->m_hash == h); if (r != n) { #if 0 static unsigned reused = 0; reused++; if (reused % 100000 == 0) verbose_stream() << "[ast-table] reused: " << reused << "\n"; #endif SASSERT(contains); SASSERT(m_ast_table.contains(n)); if (is_func_decl(r) && to_func_decl(r)->get_range() != to_func_decl(n)->get_range()) { std::ostringstream buffer; buffer << "Recycling of declaration for the same name '" << to_func_decl(r)->get_name().str().c_str() << "'" << " and domain, but different range type is not permitted"; throw ast_exception(buffer.str().c_str()); } deallocate_node(n, ::get_node_size(n)); return r; } else { SASSERT(!contains); SASSERT(m_ast_table.contains(n)); } n->m_id = is_decl(n) ? m_decl_id_gen.mk() : m_expr_id_gen.mk(); TRACE("ast", tout << "Object " << n->m_id << " was created.\n";); TRACE("mk_var_bug", tout << "mk_ast: " << n->m_id << "\n";); // increment reference counters switch (n->get_kind()) { case AST_SORT: if (to_sort(n)->m_info != 0) { to_sort(n)->m_info = alloc(sort_info, *(to_sort(n)->get_info())); to_sort(n)->m_info->init_eh(*this); } break; case AST_FUNC_DECL: if (to_func_decl(n)->m_info != 0) { to_func_decl(n)->m_info = alloc(func_decl_info, *(to_func_decl(n)->get_info())); to_func_decl(n)->m_info->init_eh(*this); } inc_array_ref(to_func_decl(n)->get_arity(), to_func_decl(n)->get_domain()); inc_ref(to_func_decl(n)->get_range()); break; case AST_APP: { app * t = to_app(n); inc_ref(t->get_decl()); unsigned num_args = t->get_num_args(); if (num_args > 0) { app_flags * f = t->flags(); *f = mk_default_app_flags(); SASSERT(t->is_ground()); SASSERT(!t->has_quantifiers()); SASSERT(!t->has_labels()); if (is_label(t)) f->m_has_labels = true; unsigned depth = 0; for (unsigned i = 0; i < num_args; i++) { expr * arg = t->get_arg(i); inc_ref(arg); unsigned arg_depth = 0; switch (arg->get_kind()) { case AST_APP: { app_flags * arg_flags = to_app(arg)->flags(); arg_depth = arg_flags->m_depth; if (arg_flags->m_has_quantifiers) f->m_has_quantifiers = true; if (arg_flags->m_has_labels) f->m_has_labels = true; if (!arg_flags->m_ground) f->m_ground = false; break; } case AST_QUANTIFIER: f->m_has_quantifiers = true; f->m_ground = false; arg_depth = to_quantifier(arg)->get_depth(); break; case AST_VAR: f->m_ground = false; arg_depth = 1; break; default: UNREACHABLE(); } if (arg_depth > depth) depth = arg_depth; } depth++; if (depth > c_max_depth) depth = c_max_depth; f->m_depth = depth; SASSERT(t->get_depth() == depth); } break; } case AST_VAR: inc_ref(to_var(n)->get_sort()); break; case AST_QUANTIFIER: inc_array_ref(to_quantifier(n)->get_num_decls(), to_quantifier(n)->get_decl_sorts()); inc_ref(to_quantifier(n)->get_expr()); inc_array_ref(to_quantifier(n)->get_num_patterns(), to_quantifier(n)->get_patterns()); inc_array_ref(to_quantifier(n)->get_num_no_patterns(), to_quantifier(n)->get_no_patterns()); break; default: break; } return n; } void ast_manager::delete_node(ast * n) { TRACE("delete_node_bug", tout << mk_ll_pp(n, *this) << "\n";); ptr_buffer worklist; worklist.push_back(n); while (!worklist.empty()) { n = worklist.back(); worklist.pop_back(); TRACE("ast", tout << "Deleting object " << n->m_id << " " << n << "\n";); CTRACE("del_quantifier", is_quantifier(n), tout << "deleting quantifier " << n->m_id << " " << n << "\n";); TRACE("mk_var_bug", tout << "del_ast: " << n->m_id << "\n";); TRACE("ast_delete_node", tout << mk_bounded_pp(n, *this) << "\n";); SASSERT(m_ast_table.contains(n)); m_ast_table.erase(n); SASSERT(!m_ast_table.contains(n)); SASSERT(!m_debug_ref_count || !m_debug_free_indices.contains(n->m_id)); #ifdef RECYCLE_FREE_AST_INDICES if (!m_debug_ref_count) { if (is_decl(n)) m_decl_id_gen.recycle(n->m_id); else m_expr_id_gen.recycle(n->m_id); } #endif switch (n->get_kind()) { case AST_SORT: if (to_sort(n)->m_info != 0 && !m_debug_ref_count) { sort_info * info = to_sort(n)->get_info(); info->del_eh(*this); dealloc(info); } break; case AST_FUNC_DECL: if (to_func_decl(n)->m_info != 0 && !m_debug_ref_count) { func_decl_info * info = to_func_decl(n)->get_info(); info->del_eh(*this); dealloc(info); } dec_array_ref(worklist, to_func_decl(n)->get_arity(), to_func_decl(n)->get_domain()); dec_ref(worklist, to_func_decl(n)->get_range()); break; case AST_APP: dec_ref(worklist, to_app(n)->get_decl()); dec_array_ref(worklist, to_app(n)->get_num_args(), to_app(n)->get_args()); break; case AST_VAR: dec_ref(worklist, to_var(n)->get_sort()); break; case AST_QUANTIFIER: dec_array_ref(worklist, to_quantifier(n)->get_num_decls(), to_quantifier(n)->get_decl_sorts()); dec_ref(worklist, to_quantifier(n)->get_expr()); dec_array_ref(worklist, to_quantifier(n)->get_num_patterns(), to_quantifier(n)->get_patterns()); dec_array_ref(worklist, to_quantifier(n)->get_num_no_patterns(), to_quantifier(n)->get_no_patterns()); break; default: break; } if (m_debug_ref_count) { m_debug_free_indices.insert(n->m_id,0); } deallocate_node(n, ::get_node_size(n)); } } sort * ast_manager::mk_sort(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters) { decl_plugin * p = get_plugin(fid); if (p) return p->mk_sort(k, num_parameters, parameters); return 0; } func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { decl_plugin * p = get_plugin(fid); if (p) return p->mk_func_decl(k, num_parameters, parameters, arity, domain, range); return 0; } func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { decl_plugin * p = get_plugin(fid); if (p) return p->mk_func_decl(k, num_parameters, parameters, num_args, args, range); return 0; } app * ast_manager::mk_app(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { func_decl * decl = mk_func_decl(fid, k, num_parameters, parameters, num_args, args, range); if (decl != 0) return mk_app(decl, num_args, args); return 0; } app * ast_manager::mk_app(family_id fid, decl_kind k, unsigned num_args, expr * const * args) { return mk_app(fid, k, 0, 0, num_args, args); } app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg) { return mk_app(fid, k, 0, 0, 1, &arg); } app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_app(fid, k, 0, 0, 2, args); } app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3) { expr * args[3] = { arg1, arg2, arg3 }; return mk_app(fid, k, 0, 0, 3, args); } sort * ast_manager::mk_sort(symbol const & name, sort_info * info) { unsigned sz = sort::get_obj_size(); void * mem = allocate_node(sz); sort * new_node = new (mem) sort(name, info); return register_node(new_node); } sort * ast_manager::mk_uninterpreted_sort(symbol const & name, unsigned num_parameters, parameter const * parameters) { user_sort_plugin * plugin = get_user_sort_plugin(); decl_kind kind = plugin->register_name(name); return plugin->mk_sort(kind, num_parameters, parameters); } func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, bool assoc, bool comm, bool inj) { func_decl_info info(null_family_id, null_decl_kind); info.set_associative(assoc); info.set_commutative(comm); info.set_injective(inj); return mk_func_decl(name, arity, domain, range, info); } func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info * info) { SASSERT(arity == 1 || info == 0 || !info->is_injective()); SASSERT(arity == 2 || info == 0 || !info->is_associative()); SASSERT(arity == 2 || info == 0 || !info->is_commutative()); unsigned sz = func_decl::get_obj_size(arity); void * mem = allocate_node(sz); func_decl * new_node = new (mem) func_decl(name, arity, domain, range, info); return register_node(new_node); } void ast_manager::check_sort(func_decl const * decl, unsigned num_args, expr * const * args) const { ast_manager& m = const_cast(*this); if (decl->is_associative()) { sort * expected = decl->get_domain(0); for (unsigned i = 0; i < num_args; i++) { sort * given = get_sort(args[i]); if (!compatible_sorts(expected, given)) { std::ostringstream buff; buff << "invalid function application for " << decl->get_name() << ", "; buff << "sort mismatch on argument at position " << (i+1) << ", "; buff << "expected " << mk_pp(expected, m) << " but given " << mk_pp(given, m); throw ast_exception(buff.str().c_str()); } } } else { if (decl->get_arity() != num_args) { throw ast_exception("invalid function application, wrong number of arguments"); } for (unsigned i = 0; i < num_args; i++) { sort * expected = decl->get_domain(i); sort * given = get_sort(args[i]); if (!compatible_sorts(expected, given)) { std::ostringstream buff; buff << "invalid function application for " << decl->get_name() << ", "; buff << "sort mismatch on argument at position " << (i+1) << ", "; buff << "expected " << mk_pp(expected, m) << " but given " << mk_pp(given, m); throw ast_exception(buff.str().c_str()); } } } } /** \brief Shallow sort checker. Return true if success. If n == 0, then fail. If n is an application, checks whether the arguments of n match the expected types. */ void ast_manager::check_sorts_core(ast const * n) const { if (!n) { throw ast_exception("expression is null"); } if (n->get_kind() != AST_APP) return; // nothing else to check... app const * a = to_app(n); func_decl* d = a->get_decl(); check_sort(d, a->get_num_args(), a->get_args()); if (a->get_num_args() == 2 && !d->is_flat_associative() && d->is_right_associative()) { check_sorts_core(a->get_arg(1)); } if (a->get_num_args() == 2 && !d->is_flat_associative() && d->is_left_associative()) { check_sorts_core(a->get_arg(0)); } } bool ast_manager::check_sorts(ast const * n) const { try { check_sorts_core(n); return true; } catch (ast_exception & ex) { warning_msg(ex.msg()); return false; } } bool ast_manager::compatible_sorts(sort * s1, sort * s2) const { if (s1 == s2) return true; if (m_int_real_coercions) return s1->get_family_id() == m_arith_family_id && s2->get_family_id() == m_arith_family_id; return false; } bool ast_manager::coercion_needed(func_decl * decl, unsigned num_args, expr * const * args) { SASSERT(m_int_real_coercions); if (decl->is_associative()) { sort * d = decl->get_domain(0); if (d->get_family_id() == m_arith_family_id) { for (unsigned i = 0; i < num_args; i++) { if (d != get_sort(args[i])) return true; } } } else { if (decl->get_arity() != num_args) { // Invalid input: unexpected number of arguments for non-associative operator. // So, there is no point in coercing the input arguments. return false; } for (unsigned i = 0; i < num_args; i++) { sort * d = decl->get_domain(i); if (d->get_family_id() == m_arith_family_id && d != get_sort(args[i])) return true; } } return false; } app * ast_manager::mk_app_core(func_decl * decl, unsigned num_args, expr * const * args) { unsigned sz = app::get_obj_size(num_args); void * mem = allocate_node(sz); app * new_node; app * r; if (m_int_real_coercions && coercion_needed(decl, num_args, args)) { expr_ref_buffer new_args(*this); if (decl->is_associative()) { sort * d = decl->get_domain(0); for (unsigned i = 0; i < num_args; i++) { sort * s = get_sort(args[i]); if (d != s && d->get_family_id() == m_arith_family_id && s->get_family_id() == m_arith_family_id) { if (d->get_decl_kind() == REAL_SORT) new_args.push_back(mk_app(m_arith_family_id, OP_TO_REAL, args[i])); else new_args.push_back(mk_app(m_arith_family_id, OP_TO_INT, args[i])); } else { new_args.push_back(args[i]); } } } else { for (unsigned i = 0; i < num_args; i++) { sort * d = decl->get_domain(i); sort * s = get_sort(args[i]); if (d != s && d->get_family_id() == m_arith_family_id && s->get_family_id() == m_arith_family_id) { if (d->get_decl_kind() == REAL_SORT) new_args.push_back(mk_app(m_arith_family_id, OP_TO_REAL, args[i])); else new_args.push_back(mk_app(m_arith_family_id, OP_TO_INT, args[i])); } else { new_args.push_back(args[i]); } } } SASSERT(new_args.size() == num_args); new_node = new (mem) app(decl, num_args, new_args.c_ptr()); r = register_node(new_node); } else { new_node = new (mem) app(decl, num_args, args); r = register_node(new_node); } if (m_trace_stream && r == new_node) { *m_trace_stream << "[mk-app] #" << r->get_id() << " "; if (r->get_num_args() == 0 && r->get_decl()->get_name() == "int") { ast_ll_pp(*m_trace_stream, *this, r); } else if (is_label_lit(r)) { ast_ll_pp(*m_trace_stream, *this, r); } else { *m_trace_stream << r->get_decl()->get_name(); for (unsigned i = 0; i < r->get_num_args(); i++) *m_trace_stream << " #" << r->get_arg(i)->get_id(); *m_trace_stream << "\n"; } } return r; } inline app * ast_manager::mk_app_core(func_decl * decl, expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_app_core(decl, 2, args); } app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * args) { if (decl->get_arity() != num_args && !decl->is_right_associative() && !decl->is_left_associative() && !decl->is_chainable()) { std::ostringstream buffer; buffer << "Wrong number of arguments (" << num_args << ") passed to function " << mk_pp(decl, *this); throw ast_exception(buffer.str().c_str()); } app * r = 0; if (num_args > 2 && !decl->is_flat_associative()) { if (decl->is_right_associative()) { unsigned j = num_args - 1; r = mk_app_core(decl, args[j-1], args[j]); -- j; while (j > 0) { --j; r = mk_app_core(decl, args[j], r); } } else if (decl->is_left_associative()) { r = mk_app_core(decl, args[0], args[1]); for (unsigned i = 2; i < num_args; i++) { r = mk_app_core(decl, r, args[i]); } } else if (decl->is_chainable()) { TRACE("chainable", tout << "chainable...\n";); ptr_buffer new_args; for (unsigned i = 1; i < num_args; i++) { new_args.push_back(mk_app_core(decl, args[i-1], args[i])); } r = mk_and(new_args.size(), new_args.c_ptr()); } } if (r == 0) { r = mk_app_core(decl, num_args, args); } SASSERT(r != 0); TRACE("app_ground", tout << "ground: " << r->is_ground() << "\n" << mk_ll_pp(r, *this) << "\n";); return r; } func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity, sort * const * domain, sort * range) { func_decl_info info(null_family_id, null_decl_kind); info.m_skolem = true; SASSERT(info.is_skolem()); func_decl * d; if (prefix == symbol::null && suffix == symbol::null) { d = mk_func_decl(symbol(m_fresh_id), arity, domain, range, &info); } else { string_buffer<64> buffer; buffer << prefix; if (prefix == symbol::null) buffer << "sk"; buffer << "!"; if (suffix != symbol::null) buffer << suffix << "!"; buffer << m_fresh_id; d = mk_func_decl(symbol(buffer.c_str()), arity, domain, range, &info); } m_fresh_id++; SASSERT(d->get_info()); SASSERT(d->is_skolem()); return d; } sort * ast_manager::mk_fresh_sort(char const * prefix) { string_buffer<32> buffer; buffer << prefix << "!" << m_fresh_id; m_fresh_id++; return mk_uninterpreted_sort(symbol(buffer.c_str())); } symbol ast_manager::mk_fresh_var_name(char const * prefix) { string_buffer<32> buffer; buffer << (prefix ? prefix : "var") << "!" << m_fresh_id; m_fresh_id++; return symbol(buffer.c_str()); } var * ast_manager::mk_var(unsigned idx, sort * s) { unsigned sz = var::get_obj_size(); void * mem = allocate_node(sz); var * new_node = new (mem) var(idx, s); return register_node(new_node); } app * ast_manager::mk_label(bool pos, unsigned num_names, symbol const * names, expr * n) { SASSERT(num_names > 0); SASSERT(get_sort(n) == m_bool_sort); buffer p; p.push_back(parameter(static_cast(pos))); for (unsigned i = 0; i < num_names; i++) p.push_back(parameter(names[i])); return mk_app(m_label_family_id, OP_LABEL, p.size(), p.c_ptr(), 1, &n); } app * ast_manager::mk_label(bool pos, symbol const & name, expr * n) { return mk_label(pos, 1, &name, n); } bool ast_manager::is_label(expr const * n, bool & pos, buffer & names) const { if (!is_app_of(n, m_label_family_id, OP_LABEL)) { return false; } func_decl const * decl = to_app(n)->get_decl(); pos = decl->get_parameter(0).get_int() != 0; for (unsigned i = 1; i < decl->get_num_parameters(); i++) names.push_back(decl->get_parameter(i).get_symbol()); return true; } app * ast_manager::mk_label_lit(unsigned num_names, symbol const * names) { SASSERT(num_names > 0); buffer p; for (unsigned i = 0; i < num_names; i++) p.push_back(parameter(names[i])); return mk_app(m_label_family_id, OP_LABEL_LIT, p.size(), p.c_ptr(), 0, 0); } app * ast_manager::mk_label_lit(symbol const & name) { return mk_label_lit(1, &name); } bool ast_manager::is_label_lit(expr const * n, buffer & names) const { if (!is_app_of(n, m_label_family_id, OP_LABEL_LIT)) { return false; } func_decl const * decl = to_app(n)->get_decl(); for (unsigned i = 0; i < decl->get_num_parameters(); i++) names.push_back(decl->get_parameter(i).get_symbol()); return true; } app * ast_manager::mk_pattern(unsigned num_exprs, app * const * exprs) { DEBUG_CODE({ for (unsigned i = 0; i < num_exprs; ++i) { SASSERT(is_app(exprs[i])); }}); return mk_app(m_pattern_family_id, OP_PATTERN, 0, 0, num_exprs, (expr*const*)exprs); } bool ast_manager::is_pattern(expr const * n) const { if (!is_app_of(n, m_pattern_family_id, OP_PATTERN)) { return false; } for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) { if (!is_app(to_app(n)->get_arg(i))) { return false; } } return true; } quantifier * ast_manager::mk_quantifier(bool forall, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, int weight , symbol const & qid, symbol const & skid, unsigned num_patterns, expr * const * patterns, unsigned num_no_patterns, expr * const * no_patterns) { SASSERT(body); SASSERT(num_patterns == 0 || num_no_patterns == 0); SASSERT(num_decls > 0); DEBUG_CODE({ for (unsigned i = 0; i < num_patterns; ++i) { SASSERT(is_pattern(patterns[i])); }}); unsigned sz = quantifier::get_obj_size(num_decls, num_patterns, num_no_patterns); void * mem = allocate_node(sz); quantifier * new_node = new (mem) quantifier(forall, num_decls, decl_sorts, decl_names, body, weight, qid, skid, num_patterns, patterns, num_no_patterns, no_patterns); quantifier * r = register_node(new_node); if (m_trace_stream && r == new_node) { *m_trace_stream << "[mk-quant] #" << r->get_id() << " " << qid; for (unsigned i = 0; i < num_patterns; ++i) { *m_trace_stream << " #" << patterns[i]->get_id(); } *m_trace_stream << " #" << body->get_id() << "\n"; } return r; } // Return true if the patterns of q are the given ones. static bool same_patterns(quantifier * q, unsigned num_patterns, expr * const * patterns) { if (num_patterns != q->get_num_patterns()) return false; for (unsigned i = 0; i < num_patterns; i++) if (q->get_pattern(i) != patterns[i]) return false; return true; } // Return true if the no patterns of q are the given ones. static bool same_no_patterns(quantifier * q, unsigned num_no_patterns, expr * const * no_patterns) { if (num_no_patterns != q->get_num_no_patterns()) return false; for (unsigned i = 0; i < num_no_patterns; i++) if (q->get_no_pattern(i) != no_patterns[i]) return false; return true; } quantifier * ast_manager::update_quantifier(quantifier * q, unsigned num_patterns, expr * const * patterns, expr * body) { if (q->get_expr() == body && same_patterns(q, num_patterns, patterns)) return q; return mk_quantifier(q->is_forall(), q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), body, q->get_weight(), q->get_qid(), q->get_skid(), num_patterns, patterns, num_patterns == 0 ? q->get_num_no_patterns() : 0, num_patterns == 0 ? q->get_no_patterns() : 0); } quantifier * ast_manager::update_quantifier(quantifier * q, unsigned num_patterns, expr * const * patterns, unsigned num_no_patterns, expr * const * no_patterns, expr * body) { if (q->get_expr() == body && same_patterns(q, num_patterns, patterns) && same_no_patterns(q, num_no_patterns, no_patterns)) return q; return mk_quantifier(q->is_forall(), q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), body, q->get_weight(), q->get_qid(), q->get_skid(), num_patterns, patterns, num_no_patterns, no_patterns); } quantifier * ast_manager::update_quantifier(quantifier * q, expr * body) { if (q->get_expr() == body) return q; return mk_quantifier(q->is_forall(), q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), body, q->get_weight(), q->get_qid(), q->get_skid(), q->get_num_patterns(), q->get_patterns(), q->get_num_no_patterns(), q->get_no_patterns()); } quantifier * ast_manager::update_quantifier_weight(quantifier * q, int w) { if (q->get_weight() == w) return q; TRACE("update_quantifier_weight", tout << "#" << q->get_id() << " " << q->get_weight() << " -> " << w << "\n";); return mk_quantifier(q->is_forall(), q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), q->get_expr(), w, q->get_qid(), q->get_skid(), q->get_num_patterns(), q->get_patterns(), q->get_num_no_patterns(), q->get_no_patterns()); } quantifier * ast_manager::update_quantifier(quantifier * q, bool is_forall, expr * body) { if (q->get_expr() == body && q->is_forall() == is_forall) return q; return mk_quantifier(is_forall, q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), body, q->get_weight(), q->get_qid(), q->get_skid(), q->get_num_patterns(), q->get_patterns(), q->get_num_no_patterns(), q->get_no_patterns()); } quantifier * ast_manager::update_quantifier(quantifier * q, bool is_forall, unsigned num_patterns, expr * const * patterns, expr * body) { if (q->get_expr() == body && q->is_forall() == is_forall && same_patterns(q, num_patterns, patterns)) return q; return mk_quantifier(is_forall, q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), body, q->get_weight(), q->get_qid(), q->get_skid(), num_patterns, patterns, num_patterns == 0 ? q->get_num_no_patterns() : 0, num_patterns == 0 ? q->get_no_patterns() : 0); } app * ast_manager::mk_distinct(unsigned num_args, expr * const * args) { return mk_app(m_basic_family_id, OP_DISTINCT, num_args, args); } app * ast_manager::mk_distinct_expanded(unsigned num_args, expr * const * args) { if (num_args < 2) return mk_true(); if (num_args == 2) return mk_not(mk_eq(args[0], args[1])); ptr_buffer new_args; for (unsigned i = 0; i < num_args - 1; i++) { expr * a1 = args[i]; for (unsigned j = i + 1; j < num_args; j++) { expr * a2 = args[j]; new_args.push_back(mk_not(mk_eq(a1, a2))); } } app * r = mk_and(new_args.size(), new_args.c_ptr()); TRACE("distinct", tout << "expanded distinct:\n" << mk_pp(r, *this) << "\n";); return r; } // ----------------------------------- // // expr_dependency // // ----------------------------------- expr_dependency * ast_manager::mk_leaf(expr * t) { if (t == 0) return 0; else return m_expr_dependency_manager.mk_leaf(t); } expr_dependency * ast_manager::mk_join(unsigned n, expr * const * ts) { expr_dependency * d = 0; for (unsigned i = 0; i < n; i++) d = mk_join(d, mk_leaf(ts[i])); return d; } void ast_manager::linearize(expr_dependency * d, ptr_vector & ts) { m_expr_dependency_manager.linearize(d, ts); remove_duplicates(ts); } // ----------------------------------- // // Values // // ----------------------------------- app * ast_manager::mk_model_value(unsigned idx, sort * s) { parameter p[2] = { parameter(idx), parameter(s) }; return mk_app(m_model_value_family_id, OP_MODEL_VALUE, 2, p, 0, static_cast(0)); } expr * ast_manager::get_some_value(sort * s, some_value_proc * p) { flet l(m_some_value_proc, p); return get_some_value(s); } expr * ast_manager::get_some_value(sort * s) { expr * v = 0; if (m_some_value_proc) v = (*m_some_value_proc)(s); if (v != 0) return v; family_id fid = s->get_family_id(); if (fid != null_family_id) { decl_plugin * p = get_plugin(fid); if (p != 0) { v = p->get_some_value(s); if (v != 0) return v; } } return mk_model_value(0, s); } bool ast_manager::is_fully_interp(sort const * s) const { if (is_uninterp(s)) return false; family_id fid = s->get_family_id(); SASSERT(fid != null_family_id); decl_plugin * p = get_plugin(fid); if (p != 0) return p->is_fully_interp(s); return false; } // ----------------------------------- // // Proof generation // // ----------------------------------- proof * ast_manager::mk_proof(family_id fid, decl_kind k, unsigned num_args, expr * const * args) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(fid, k, num_args, args); } proof * ast_manager::mk_proof(family_id fid, decl_kind k, expr * arg) { return mk_proof(fid, k, 1, &arg); } proof * ast_manager::mk_proof(family_id fid, decl_kind k, expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_proof(fid, k, 2, args); } proof * ast_manager::mk_proof(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3) { expr * args[3] = { arg1, arg2, arg3 }; return mk_proof(fid, k, 2, args); } proof * ast_manager::mk_true_proof() { expr * f = mk_true(); return mk_proof(m_basic_family_id, PR_TRUE, f); } proof * ast_manager::mk_asserted(expr * f) { CTRACE("mk_asserted_bug", !is_bool(f), tout << mk_ismt2_pp(f, *this) << "\nsort: " << mk_ismt2_pp(get_sort(f), *this) << "\n";); SASSERT(is_bool(f)); return mk_proof(m_basic_family_id, PR_ASSERTED, f); } proof * ast_manager::mk_goal(expr * f) { SASSERT(is_bool(f)); return mk_proof(m_basic_family_id, PR_GOAL, f); } proof * ast_manager::mk_modus_ponens(proof * p1, proof * p2) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(p1)); SASSERT(has_fact(p2)); CTRACE("mk_modus_ponens", !(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2))), tout << mk_ll_pp(p1, *this) << "\n"; tout << mk_ll_pp(p2, *this) << "\n";); SASSERT(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2))); CTRACE("mk_modus_ponens", to_app(get_fact(p2))->get_arg(0) != get_fact(p1), tout << mk_pp(get_fact(p1), *this) << "\n" << mk_pp(get_fact(p2), *this) << "\n";); SASSERT(to_app(get_fact(p2))->get_arg(0) == get_fact(p1)); if (is_reflexivity(p2)) return p1; expr * f = to_app(get_fact(p2))->get_arg(1); if (is_oeq(get_fact(p2))) return mk_app(m_basic_family_id, PR_MODUS_PONENS_OEQ, p1, p2, f); return mk_app(m_basic_family_id, PR_MODUS_PONENS, p1, p2, f); } proof * ast_manager::mk_reflexivity(expr * e) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_REFLEXIVITY, mk_eq(e, e)); } proof * ast_manager::mk_oeq_reflexivity(expr * e) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_REFLEXIVITY, mk_oeq(e, e)); } proof * ast_manager::mk_commutativity(app * f) { SASSERT(f->get_num_args() == 2); app * f_prime = mk_app(f->get_decl(), f->get_arg(1), f->get_arg(0)); return mk_app(m_basic_family_id, PR_COMMUTATIVITY, mk_eq(f, f_prime)); } /** \brief Given a proof of p, return a proof of (p <=> true) */ proof * ast_manager::mk_iff_true(proof * pr) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(pr)); SASSERT(is_bool(get_fact(pr))); return mk_app(m_basic_family_id, PR_IFF_TRUE, pr, mk_iff(get_fact(pr), mk_true())); } /** \brief Given a proof of (not p), return a proof of (p <=> false) */ proof * ast_manager::mk_iff_false(proof * pr) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(pr)); SASSERT(is_not(get_fact(pr))); expr * p = to_app(get_fact(pr))->get_arg(0); return mk_app(m_basic_family_id, PR_IFF_FALSE, pr, mk_iff(p, mk_false())); } proof * ast_manager::mk_symmetry(proof * p) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; if (!p) return p; if (is_reflexivity(p)) return p; if (is_symmetry(p)) return get_parent(p, 0); SASSERT(has_fact(p)); SASSERT(is_app(get_fact(p))); SASSERT(to_app(get_fact(p))->get_num_args() == 2); return mk_app(m_basic_family_id, PR_SYMMETRY, p, mk_app(to_app(get_fact(p))->get_decl(), to_app(get_fact(p))->get_arg(1), to_app(get_fact(p))->get_arg(0))); } proof * ast_manager::mk_transitivity(proof * p1, proof * p2) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; if (!p1) return p2; if (!p2) return p1; SASSERT(has_fact(p1)); SASSERT(has_fact(p2)); SASSERT(is_app(get_fact(p1))); SASSERT(is_app(get_fact(p2))); SASSERT(to_app(get_fact(p1))->get_num_args() == 2); SASSERT(to_app(get_fact(p2))->get_num_args() == 2); CTRACE("mk_transitivity", to_app(get_fact(p1))->get_decl() != to_app(get_fact(p2))->get_decl(), tout << mk_pp(get_fact(p1), *this) << "\n\n" << mk_pp(get_fact(p2), *this) << "\n"; tout << mk_pp(to_app(get_fact(p1))->get_decl(), *this) << "\n"; tout << mk_pp(to_app(get_fact(p2))->get_decl(), *this) << "\n";); SASSERT(to_app(get_fact(p1))->get_decl() == to_app(get_fact(p2))->get_decl() || ( (is_eq(get_fact(p1)) || is_oeq(get_fact(p1))) && (is_eq(get_fact(p2)) || is_oeq(get_fact(p2))))); CTRACE("mk_transitivity", to_app(get_fact(p1))->get_arg(1) != to_app(get_fact(p2))->get_arg(0), tout << mk_pp(get_fact(p1), *this) << "\n\n" << mk_pp(get_fact(p2), *this) << "\n"; tout << mk_bounded_pp(p1, *this, 5) << "\n\n"; tout << mk_bounded_pp(p2, *this, 5) << "\n\n"; ); SASSERT(to_app(get_fact(p1))->get_arg(1) == to_app(get_fact(p2))->get_arg(0)); if (is_reflexivity(p1)) return p2; if (is_reflexivity(p2)) return p1; // OEQ is compatible with EQ for transitivity. func_decl* f = to_app(get_fact(p1))->get_decl(); if (is_oeq(get_fact(p2))) f = to_app(get_fact(p2))->get_decl(); return mk_app(m_basic_family_id, PR_TRANSITIVITY, p1, p2, mk_app(f, to_app(get_fact(p1))->get_arg(0), to_app(get_fact(p2))->get_arg(1))); } proof * ast_manager::mk_transitivity(proof * p1, proof * p2, proof * p3) { return mk_transitivity(mk_transitivity(p1,p2), p3); } proof * ast_manager::mk_transitivity(proof * p1, proof * p2, proof * p3, proof * p4) { return mk_transitivity(mk_transitivity(mk_transitivity(p1,p2), p3), p4); } proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(num_proofs > 0); proof * r = proofs[0]; for (unsigned i = 1; i < num_proofs; i++) r = mk_transitivity(r, proofs[i]); return r; } proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs, expr * n1, expr * n2) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; if (fine_grain_proofs()) return mk_transitivity(num_proofs, proofs); SASSERT(num_proofs > 0); if (num_proofs == 1) return proofs[0]; DEBUG_CODE({ for (unsigned i = 0; i < num_proofs; i++) { SASSERT(proofs[i]); SASSERT(!is_reflexivity(proofs[i])); } }); ptr_buffer args; args.append(num_proofs, (expr**) proofs); args.push_back(mk_eq(n1,n2)); return mk_app(m_basic_family_id, PR_TRANSITIVITY_STAR, args.size(), args.c_ptr()); } proof * ast_manager::mk_monotonicity(func_decl * R, app * f1, app * f2, unsigned num_proofs, proof * const * proofs) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(f1->get_num_args() == f2->get_num_args()); SASSERT(f1->get_decl() == f2->get_decl()); ptr_buffer args; args.append(num_proofs, (expr**) proofs); args.push_back(mk_app(R, f1, f2)); return mk_app(m_basic_family_id, PR_MONOTONICITY, args.size(), args.c_ptr()); } proof * ast_manager::mk_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(get_sort(f1) == get_sort(f2)); sort * s = get_sort(f1); sort * d[2] = { s, s }; return mk_monotonicity(mk_func_decl(m_basic_family_id, get_eq_op(f1), 0, 0, 2, d), f1, f2, num_proofs, proofs); } proof * ast_manager::mk_oeq_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(get_sort(f1) == get_sort(f2)); sort * s = get_sort(f1); sort * d[2] = { s, s }; return mk_monotonicity(mk_func_decl(m_basic_family_id, OP_OEQ, 0, 0, 2, d), f1, f2, num_proofs, proofs); } proof * ast_manager::mk_quant_intro(quantifier * q1, quantifier * q2, proof * p) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; if (!p) { return 0; } SASSERT(q1->get_num_decls() == q2->get_num_decls()); SASSERT(has_fact(p)); SASSERT(is_iff(get_fact(p))); return mk_app(m_basic_family_id, PR_QUANT_INTRO, p, mk_iff(q1, q2)); } proof * ast_manager::mk_oeq_quant_intro(quantifier * q1, quantifier * q2, proof * p) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(q1->get_num_decls() == q2->get_num_decls()); SASSERT(has_fact(p)); SASSERT(is_oeq(get_fact(p))); return mk_app(m_basic_family_id, PR_QUANT_INTRO, p, mk_oeq(q1, q2)); } proof * ast_manager::mk_distributivity(expr * s, expr * r) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_DISTRIBUTIVITY, mk_eq(s, r)); } proof * ast_manager::mk_rewrite(expr * s, expr * t) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_REWRITE, mk_eq(s, t)); } proof * ast_manager::mk_oeq_rewrite(expr * s, expr * t) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_REWRITE, mk_oeq(s, t)); } proof * ast_manager::mk_rewrite_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; ptr_buffer args; args.append(num_proofs, (expr**) proofs); args.push_back(mk_eq(s, t)); return mk_app(m_basic_family_id, PR_REWRITE_STAR, args.size(), args.c_ptr()); } proof * ast_manager::mk_pull_quant(expr * e, quantifier * q) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_PULL_QUANT, mk_iff(e, q)); } proof * ast_manager::mk_pull_quant_star(expr * e, quantifier * q) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_PULL_QUANT_STAR, mk_iff(e, q)); } proof * ast_manager::mk_push_quant(quantifier * q, expr * e) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_PUSH_QUANT, mk_iff(q, e)); } proof * ast_manager::mk_elim_unused_vars(quantifier * q, expr * e) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_ELIM_UNUSED_VARS, mk_iff(q, e)); } proof * ast_manager::mk_der(quantifier * q, expr * e) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_DER, mk_iff(q, e)); } proof * ast_manager::mk_quant_inst(expr * not_q_or_i, unsigned num_bind, expr* const* binding) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; vector params; for (unsigned i = 0; i < num_bind; ++i) { params.push_back(parameter(binding[i])); SASSERT(params.back().is_ast()); } return mk_app(m_basic_family_id, PR_QUANT_INST, num_bind, params.c_ptr(), 1, & not_q_or_i); } bool ast_manager::is_quant_inst(expr const* e, expr*& not_q_or_i, ptr_vector& binding) const { if (is_quant_inst(e)) { not_q_or_i = to_app(e)->get_arg(0); func_decl* d = to_app(e)->get_decl(); SASSERT(binding.empty()); for (unsigned i = 0; i < d->get_num_parameters(); ++i) { binding.push_back(to_expr(d->get_parameter(i).get_ast())); } return true; } return false; } bool ast_manager::is_rewrite(expr const* e, expr*& r1, expr*& r2) const { if (is_rewrite(e)) { VERIFY (is_eq(to_app(e)->get_arg(0), r1, r2) || is_iff(to_app(e)->get_arg(0), r1, r2)); return true; } else { return false; } } proof * ast_manager::mk_def_axiom(expr * ax) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_DEF_AXIOM, ax); } proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * proofs) { SASSERT(num_proofs >= 2); for (unsigned i = 0; i < num_proofs; i++) { SASSERT(has_fact(proofs[i])); } ptr_buffer args; args.append(num_proofs, (expr**) proofs); expr * fact; expr * f1 = get_fact(proofs[0]); expr * f2 = get_fact(proofs[1]); if (num_proofs == 2 && is_complement(f1, f2)) { fact = mk_false(); } else { CTRACE("mk_unit_resolution_bug", !is_or(f1), tout << mk_pp(f1, *this) << " " << mk_pp(f2, *this) << "\n";); SASSERT(is_or(f1)); ptr_buffer new_lits; app const * cls = to_app(f1); unsigned num_args = cls->get_num_args(); #ifdef Z3DEBUG svector found; #endif for (unsigned i = 0; i < num_args; i++) { expr * lit = cls->get_arg(i); unsigned j = 1; for (; j < num_proofs; j++) { expr const * _fact = get_fact(proofs[j]); if (is_complement(lit, _fact)) { DEBUG_CODE(found.setx(j, true, false);); break; } } if (j == num_proofs) new_lits.push_back(lit); } DEBUG_CODE({ for (unsigned i = 1; m_proof_mode == PGM_FINE && i < num_proofs; i++) { CTRACE("mk_unit_resolution_bug", !found.get(i, false), for (unsigned j = 0; j < num_proofs; j++) { tout << mk_ll_pp(get_fact(proofs[j]), *this); }); SASSERT(found.get(i, false)); } }); switch (new_lits.size()) { case 0: fact = mk_false(); break; case 1: fact = new_lits[0]; break; default: fact = mk_or(new_lits.size(), new_lits.c_ptr()); break; } } args.push_back(fact); proof * pr = mk_app(m_basic_family_id, PR_UNIT_RESOLUTION, args.size(), args.c_ptr()); TRACE("unit_resolution", tout << "unit_resolution generating fact\n" << mk_ll_pp(pr, *this);); return pr; } proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * proofs, expr * new_fact) { TRACE("unit_bug", for (unsigned i = 0; i < num_proofs; i++) tout << mk_pp(get_fact(proofs[i]), *this) << "\n"; tout << "===>\n"; tout << mk_pp(new_fact, *this) << "\n";); ptr_buffer args; args.append(num_proofs, (expr**) proofs); args.push_back(new_fact); #ifdef Z3DEBUG expr * f1 = get_fact(proofs[0]); expr const * f2 = get_fact(proofs[1]); if (num_proofs == 2 && is_complement(f1, f2)) { SASSERT(is_false(new_fact)); } else { SASSERT(is_or(f1)); app * cls = to_app(f1); unsigned cls_sz = cls->get_num_args(); CTRACE("cunit_bug", !(num_proofs == cls_sz || (num_proofs == cls_sz + 1 && is_false(new_fact))), for (unsigned i = 0; i < num_proofs; i++) tout << mk_pp(get_fact(proofs[i]), *this) << "\n"; tout << "===>\n"; tout << mk_pp(new_fact, *this) << "\n";); SASSERT(num_proofs == cls_sz || (num_proofs == cls_sz + 1 && is_false(new_fact))); unsigned num_matches = 0; for (unsigned i = 0; i < cls_sz; i++) { expr * lit = cls->get_arg(i); unsigned j = 1; for (; j < num_proofs; j++) { if (is_complement(lit, get_fact(proofs[j]))) { num_matches++; break; } } if (j == num_proofs) { CTRACE("unit_bug1", new_fact != lit, tout << mk_ll_pp(new_fact, *this) << "\n" << mk_ll_pp(lit, *this) << "\n";); SASSERT(new_fact == lit); } } SASSERT(num_matches == cls_sz || num_matches == cls_sz - 1); SASSERT(num_matches != cls_sz || is_false(new_fact)); } #endif proof * pr = mk_app(m_basic_family_id, PR_UNIT_RESOLUTION, args.size(), args.c_ptr()); TRACE("unit_resolution", tout << "unit_resolution using fact\n" << mk_ll_pp(pr, *this);); return pr; } proof * ast_manager::mk_hypothesis(expr * h) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_HYPOTHESIS, h); } proof * ast_manager::mk_lemma(proof * p, expr * lemma) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(p)); CTRACE("mk_lemma", !is_false(get_fact(p)), tout << mk_ll_pp(p, *this) << "\n";); SASSERT(is_false(get_fact(p))); return mk_app(m_basic_family_id, PR_LEMMA, p, lemma); } proof * ast_manager::mk_def_intro(expr * new_def) { SASSERT(is_bool(new_def)); return mk_proof(m_basic_family_id, PR_DEF_INTRO, new_def); } proof * ast_manager::mk_apply_defs(expr * n, expr * def, unsigned num_proofs, proof * const * proofs) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; ptr_buffer args; args.append(num_proofs, (expr**) proofs); args.push_back(mk_oeq(n, def)); return mk_app(m_basic_family_id, PR_APPLY_DEF, args.size(), args.c_ptr()); } proof * ast_manager::mk_iff_oeq(proof * p) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; if (!p) return p; SASSERT(has_fact(p)); SASSERT(is_iff(get_fact(p)) || is_oeq(get_fact(p))); if (is_oeq(get_fact(p))) return p; app * iff = to_app(get_fact(p)); expr * lhs = iff->get_arg(0); expr * rhs = iff->get_arg(1); return mk_app(m_basic_family_id, PR_IFF_OEQ, p, mk_oeq(lhs, rhs)); } bool ast_manager::check_nnf_proof_parents(unsigned num_proofs, proof * const * proofs) const { for (unsigned i = 0; i < num_proofs; i++) { if (!has_fact(proofs[i])) return false; if (!is_oeq(get_fact(proofs[i]))) return false; } return true; } proof * ast_manager::mk_nnf_pos(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; check_nnf_proof_parents(num_proofs, proofs); ptr_buffer args; args.append(num_proofs, (expr**) proofs); args.push_back(mk_oeq(s, t)); return mk_app(m_basic_family_id, PR_NNF_POS, args.size(), args.c_ptr()); } proof * ast_manager::mk_nnf_neg(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; check_nnf_proof_parents(num_proofs, proofs); ptr_buffer args; args.append(num_proofs, (expr**) proofs); args.push_back(mk_oeq(mk_not(s), t)); return mk_app(m_basic_family_id, PR_NNF_NEG, args.size(), args.c_ptr()); } proof * ast_manager::mk_nnf_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; ptr_buffer args; args.append(num_proofs, (expr**) proofs); args.push_back(mk_oeq(s, t)); return mk_app(m_basic_family_id, PR_NNF_STAR, args.size(), args.c_ptr()); } proof * ast_manager::mk_skolemization(expr * q, expr * e) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(is_bool(q)); SASSERT(is_bool(e)); return mk_app(m_basic_family_id, PR_SKOLEMIZE, mk_oeq(q, e)); } proof * ast_manager::mk_cnf_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; ptr_buffer args; args.append(num_proofs, (expr**) proofs); args.push_back(mk_oeq(s, t)); return mk_app(m_basic_family_id, PR_CNF_STAR, args.size(), args.c_ptr()); } proof * ast_manager::mk_and_elim(proof * p, unsigned i) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(p)); SASSERT(is_and(get_fact(p))); CTRACE("mk_and_elim", i >= to_app(get_fact(p))->get_num_args(), tout << "i: " << i << "\n" << mk_pp(get_fact(p), *this) << "\n";); SASSERT(i < to_app(get_fact(p))->get_num_args()); expr * f = to_app(get_fact(p))->get_arg(i); return mk_app(m_basic_family_id, PR_AND_ELIM, p, f); } proof * ast_manager::mk_not_or_elim(proof * p, unsigned i) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(p)); SASSERT(is_not(get_fact(p))); SASSERT(is_or(to_app(get_fact(p))->get_arg(0))); app * or_app = to_app(to_app(get_fact(p))->get_arg(0)); SASSERT(i < or_app->get_num_args()); expr * c = or_app->get_arg(i); expr * f; if (is_not(c)) f = to_app(c)->get_arg(0); else f = mk_not(c); return mk_app(m_basic_family_id, PR_NOT_OR_ELIM, p, f); } proof * ast_manager::mk_th_lemma( family_id tid, expr * fact, unsigned num_proofs, proof * const * proofs, unsigned num_params, parameter const* params ) { if (m_proof_mode == PGM_DISABLED) return m_undef_proof; ptr_buffer args; vector parameters; parameters.push_back(parameter(get_family_name(tid))); for (unsigned i = 0; i < num_params; ++i) { parameters.push_back(params[i]); } args.append(num_proofs, (expr**) proofs); args.push_back(fact); return mk_app(m_basic_family_id, PR_TH_LEMMA, num_params+1, parameters.c_ptr(), args.size(), args.c_ptr()); } proof* ast_manager::mk_hyper_resolve(unsigned num_premises, proof* const* premises, expr* concl, svector > const& positions, vector const& substs) { ptr_vector fmls; SASSERT(positions.size() + 1 == substs.size()); for (unsigned i = 0; i < num_premises; ++i) { TRACE("hyper_res", tout << mk_pp(premises[i], *this) << "\n";); fmls.push_back(get_fact(premises[i])); } SASSERT(is_bool(concl)); vector params; for (unsigned i = 0; i < substs.size(); ++i) { expr_ref_vector const& vec = substs[i]; for (unsigned j = 0; j < vec.size(); ++j) { params.push_back(parameter(vec[j])); } if (i + 1 < substs.size()) { params.push_back(parameter(positions[i].first)); params.push_back(parameter(positions[i].second)); } } TRACE("hyper_res", for (unsigned i = 0; i < params.size(); ++i) { params[i].display(tout); tout << "\n"; }); ptr_vector sorts; ptr_vector args; for (unsigned i = 0; i < num_premises; ++i) { sorts.push_back(mk_proof_sort()); args.push_back(premises[i]); } sorts.push_back(mk_bool_sort()); args.push_back(concl); app* result = mk_app(m_basic_family_id, PR_HYPER_RESOLVE, params.size(), params.c_ptr(), args.size(), args.c_ptr()); SASSERT(result->get_family_id() == m_basic_family_id); SASSERT(result->get_decl_kind() == PR_HYPER_RESOLVE); return result; } bool ast_manager::is_hyper_resolve( proof* p, proof_ref_vector& premises, expr_ref& conclusion, svector > & positions, vector & substs) { if (!is_hyper_resolve(p)) { return false; } unsigned sz = p->get_num_args(); SASSERT(sz > 0); for (unsigned i = 0; i + 1 < sz; ++i) { premises.push_back(to_app(p->get_arg(i))); } conclusion = p->get_arg(sz-1); func_decl* d = p->get_decl(); unsigned num_p = d->get_num_parameters(); parameter const* params = d->get_parameters(); substs.push_back(expr_ref_vector(*this)); for (unsigned i = 0; i < num_p; ++i) { if (params[i].is_int()) { SASSERT(i + 1 < num_p); SASSERT(params[i+1].is_int()); unsigned x = static_cast(params[i].get_int()); unsigned y = static_cast(params[i+1].get_int()); positions.push_back(std::make_pair(x, y)); substs.push_back(expr_ref_vector(*this)); ++i; } else { SASSERT(params[i].is_ast()); ast* a = params[i].get_ast(); SASSERT(is_expr(a)); substs.back().push_back(to_expr(a)); } } return true; } // ----------------------------------- // // ast_mark // // ----------------------------------- bool ast_mark::is_marked(ast * n) const { if (is_decl(n)) return m_decl_marks.is_marked(to_decl(n)); else return m_expr_marks.is_marked(to_expr(n)); } void ast_mark::mark(ast * n, bool flag) { if (is_decl(n)) return m_decl_marks.mark(to_decl(n), flag); else return m_expr_marks.mark(to_expr(n), flag); } void ast_mark::reset() { m_decl_marks.reset(); m_expr_marks.reset(); } // ----------------------------------- // // scoped_mark // // ----------------------------------- void scoped_mark::mark(ast * n, bool flag) { SASSERT(flag); mark(n); } void scoped_mark::mark(ast * n) { if (!ast_mark::is_marked(n)) { m_stack.push_back(n); ast_mark::mark(n, true); } } void scoped_mark::reset() { ast_mark::reset(); m_stack.reset(); m_lim.reset(); } void scoped_mark::push_scope() { m_lim.push_back(m_stack.size()); } void scoped_mark::pop_scope() { unsigned new_size = m_stack.size(); unsigned old_size = m_lim.back(); for (unsigned i = old_size; i < new_size; ++i) { ast_mark::mark(m_stack[i].get(), false); } m_lim.pop_back(); m_stack.resize(old_size); } void scoped_mark::pop_scope(unsigned num_scopes) { for (unsigned i = 0; i < num_scopes; ++i) { pop_scope(); } } // Added by KLM for use in GDB // show an expr_ref on stdout void prexpr(expr_ref &e){ std::cout << mk_pp(e.get(), e.get_manager()) << std::endl; } void ast_manager::show_id_gen(){ std::cout << "id_gen: " << m_expr_id_gen.show_hash() << " " << m_decl_id_gen.show_hash() << "\n"; } z3-4.4.0/src/ast/for_each_ast.cpp0000644000175000017500000000157512540347414016354 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: for_each_ast.cpp Abstract: For each ast visitor Author: Leonardo de Moura (leonardo) 2006-10-18. Revision History: --*/ #include"for_each_ast.h" struct ast_counter_proc { unsigned m_num; ast_counter_proc():m_num(0) {} void operator()(ast *) { m_num++; } }; unsigned get_num_nodes(ast * n) { for_each_ast_proc counter; for_each_ast(counter, n); return counter.m_num; } bool for_each_parameter(ptr_vector & stack, ast_mark & visited, unsigned num_args, parameter const * params) { bool result = true; for (unsigned i = 0; i < num_args; i++) { parameter const& p = params[i]; if (p.is_ast() && !visited.is_marked(p.get_ast())) { stack.push_back(p.get_ast()); result = false; } } return result; } z3-4.4.0/src/ast/fpa_decl_plugin.cpp0000644000175000017500000013130212540347414017042 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa_decl_plugin.cpp Abstract: Floating point decl plugin Author: Leonardo de Moura (leonardo) 2012-01-15. Revision History: --*/ #include"fpa_decl_plugin.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" fpa_decl_plugin::fpa_decl_plugin(): m_values(m_fm), m_value_table(mpf_hash_proc(m_values), mpf_eq_proc(m_values)) { m_real_sort = 0; m_int_sort = 0; m_bv_plugin = 0; } void fpa_decl_plugin::set_manager(ast_manager * m, family_id id) { decl_plugin::set_manager(m, id); m_arith_fid = m_manager->mk_family_id("arith"); m_real_sort = m_manager->mk_sort(m_arith_fid, REAL_SORT); SASSERT(m_real_sort != 0); // arith_decl_plugin must be installed before fpa_decl_plugin. m_manager->inc_ref(m_real_sort); m_int_sort = m_manager->mk_sort(m_arith_fid, INT_SORT); SASSERT(m_int_sort != 0); // arith_decl_plugin must be installed before fpa_decl_plugin. m_manager->inc_ref(m_int_sort); // BV is not optional anymore. SASSERT(m_manager->has_plugin(symbol("bv"))); m_bv_fid = m_manager->mk_family_id("bv"); m_bv_plugin = static_cast(m_manager->get_plugin(m_bv_fid)); } fpa_decl_plugin::~fpa_decl_plugin() { } unsigned fpa_decl_plugin::mk_id(mpf const & v) { unsigned new_id = m_id_gen.mk(); m_values.reserve(new_id+1); m_fm.set(m_values[new_id], v); unsigned old_id = m_value_table.insert_if_not_there(new_id); if (old_id != new_id) { m_id_gen.recycle(new_id); m_fm.del(m_values[new_id]); } return old_id; } void fpa_decl_plugin::recycled_id(unsigned id) { SASSERT(m_value_table.contains(id)); m_value_table.erase(id); m_id_gen.recycle(id); m_fm.del(m_values[id]); } func_decl * fpa_decl_plugin::mk_numeral_decl(mpf const & v) { parameter p(mk_id(v), true); SASSERT(p.is_external()); sort * s = mk_float_sort(v.get_ebits(), v.get_sbits()); return m_manager->mk_const_decl(symbol("fp.numeral"), s, func_decl_info(m_family_id, OP_FPA_NUM, 1, &p)); } app * fpa_decl_plugin::mk_numeral(mpf const & v) { sort * s = mk_float_sort(v.get_ebits(), v.get_sbits()); func_decl * d; if (m_fm.is_nan(v)) d = m_manager->mk_const_decl(symbol("NaN"), s, func_decl_info(m_family_id, OP_FPA_NAN)); else if (m_fm.is_pinf(v)) d = m_manager->mk_const_decl(symbol("+oo"), s, func_decl_info(m_family_id, OP_FPA_PLUS_INF)); else if (m_fm.is_ninf(v)) d = m_manager->mk_const_decl(symbol("-oo"), s, func_decl_info(m_family_id, OP_FPA_MINUS_INF)); else if (m_fm.is_pzero(v)) d = m_manager->mk_const_decl(symbol("+zero"), s, func_decl_info(m_family_id, OP_FPA_PLUS_ZERO)); else if (m_fm.is_nzero(v)) d = m_manager->mk_const_decl(symbol("-zero"), s, func_decl_info(m_family_id, OP_FPA_MINUS_ZERO)); else d = mk_numeral_decl(v); return m_manager->mk_const(d); } bool fpa_decl_plugin::is_numeral(expr * n, mpf & val) { if (is_app_of(n, m_family_id, OP_FPA_NUM)) { m_fm.set(val, m_values[to_app(n)->get_decl()->get_parameter(0).get_ext_id()]); return true; } else if (is_app_of(n, m_family_id, OP_FPA_MINUS_INF)) { unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int(); unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int(); m_fm.mk_ninf(ebits, sbits, val); return true; } else if (is_app_of(n, m_family_id, OP_FPA_PLUS_INF)) { unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int(); unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int(); m_fm.mk_pinf(ebits, sbits, val); return true; } else if (is_app_of(n, m_family_id, OP_FPA_NAN)) { unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int(); unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int(); m_fm.mk_nan(ebits, sbits, val); return true; } else if (is_app_of(n, m_family_id, OP_FPA_PLUS_ZERO)) { unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int(); unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int(); m_fm.mk_pzero(ebits, sbits, val); return true; } else if (is_app_of(n, m_family_id, OP_FPA_MINUS_ZERO)) { unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int(); unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int(); m_fm.mk_nzero(ebits, sbits, val); return true; } return false; } bool fpa_decl_plugin::is_numeral(expr * n) { scoped_mpf v(m_fm); return is_numeral(n, v); } bool fpa_decl_plugin::is_rm_numeral(expr * n, mpf_rounding_mode & val) { if (is_app_of(n, m_family_id, OP_FPA_RM_NEAREST_TIES_TO_AWAY)) { val = MPF_ROUND_NEAREST_TAWAY; return true; } else if (is_app_of(n, m_family_id, OP_FPA_RM_NEAREST_TIES_TO_EVEN)) { val = MPF_ROUND_NEAREST_TEVEN; return true; } else if (is_app_of(n, m_family_id, OP_FPA_RM_TOWARD_NEGATIVE)) { val = MPF_ROUND_TOWARD_NEGATIVE; return true; } else if (is_app_of(n, m_family_id, OP_FPA_RM_TOWARD_POSITIVE)) { val = MPF_ROUND_TOWARD_POSITIVE; return true; } else if (is_app_of(n, m_family_id, OP_FPA_RM_TOWARD_ZERO)) { val = MPF_ROUND_TOWARD_ZERO; return true; } return 0; } bool fpa_decl_plugin::is_rm_numeral(expr * n) { mpf_rounding_mode t; return is_rm_numeral(n, t); } void fpa_decl_plugin::del(parameter const & p) { SASSERT(p.is_external()); recycled_id(p.get_ext_id()); } parameter fpa_decl_plugin::translate(parameter const & p, decl_plugin & target) { SASSERT(p.is_external()); fpa_decl_plugin & _target = static_cast(target); return parameter(_target.mk_id(m_values[p.get_ext_id()]), true); } void fpa_decl_plugin::finalize() { if (m_real_sort) { m_manager->dec_ref(m_real_sort); } if (m_int_sort) { m_manager->dec_ref(m_int_sort); } } decl_plugin * fpa_decl_plugin::mk_fresh() { return alloc(fpa_decl_plugin); } sort * fpa_decl_plugin::mk_float_sort(unsigned ebits, unsigned sbits) { if (sbits < 2) m_manager->raise_exception("minimum number of significand bits is 1"); if (ebits < 2) m_manager->raise_exception("minimum number of exponent bits is 2"); parameter p1(ebits), p2(sbits); parameter ps[2] = { p1, p2 }; sort_size sz; sz = sort_size::mk_very_big(); // TODO: refine return m_manager->mk_sort(symbol("FloatingPoint"), sort_info(m_family_id, FLOATING_POINT_SORT, sz, 2, ps)); } sort * fpa_decl_plugin::mk_rm_sort() { return m_manager->mk_sort(symbol("RoundingMode"), sort_info(m_family_id, ROUNDING_MODE_SORT)); } sort * fpa_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { switch (k) { case FLOATING_POINT_SORT: if (!(num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int())) m_manager->raise_exception("expecting two integer parameters to floating point sort (ebits, sbits)"); return mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); case ROUNDING_MODE_SORT: return mk_rm_sort(); case FLOAT16_SORT: return mk_float_sort(5, 11); case FLOAT32_SORT: return mk_float_sort(8, 24); case FLOAT64_SORT: return mk_float_sort(11, 53); case FLOAT128_SORT: return mk_float_sort(15, 113); default: m_manager->raise_exception("unknown floating point theory sort"); return 0; } } func_decl * fpa_decl_plugin::mk_rm_const_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (num_parameters != 0) m_manager->raise_exception("rounding mode constant does not have parameters"); if (arity != 0) m_manager->raise_exception("rounding mode is a constant"); sort * s = mk_rm_sort(); func_decl_info finfo(m_family_id, k); switch (k) { case OP_FPA_RM_NEAREST_TIES_TO_EVEN: return m_manager->mk_const_decl(symbol("roundNearestTiesToEven"), s, finfo); case OP_FPA_RM_NEAREST_TIES_TO_AWAY: return m_manager->mk_const_decl(symbol("roundNearestTiesToAway"), s, finfo); case OP_FPA_RM_TOWARD_POSITIVE: return m_manager->mk_const_decl(symbol("roundTowardPositive"), s, finfo); case OP_FPA_RM_TOWARD_NEGATIVE: return m_manager->mk_const_decl(symbol("roundTowardNegative"), s, finfo); case OP_FPA_RM_TOWARD_ZERO: return m_manager->mk_const_decl(symbol("roundTowardZero"), s, finfo); default: UNREACHABLE(); return 0; } } func_decl * fpa_decl_plugin::mk_float_const_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { sort * s = 0; if (num_parameters == 1 && parameters[0].is_ast() && is_sort(parameters[0].get_ast()) && is_float_sort(to_sort(parameters[0].get_ast()))) { s = to_sort(parameters[0].get_ast()); } else if (num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int()) { s = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); } else if (range != 0 && is_float_sort(range)) { s = range; } else { m_manager->raise_exception("sort of floating point constant was not specified"); UNREACHABLE(); } SASSERT(is_sort_of(s, m_family_id, FLOATING_POINT_SORT)); unsigned ebits = s->get_parameter(0).get_int(); unsigned sbits = s->get_parameter(1).get_int(); scoped_mpf val(m_fm); switch (k) { case OP_FPA_NAN: m_fm.mk_nan(ebits, sbits, val); SASSERT(m_fm.is_nan(val)); break; case OP_FPA_MINUS_INF: m_fm.mk_ninf(ebits, sbits, val); break; case OP_FPA_PLUS_INF: m_fm.mk_pinf(ebits, sbits, val); break; case OP_FPA_MINUS_ZERO: m_fm.mk_nzero(ebits, sbits, val); break; case OP_FPA_PLUS_ZERO: m_fm.mk_pzero(ebits, sbits, val); break; } return mk_numeral_decl(val); } func_decl * fpa_decl_plugin::mk_bin_rel_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity < 2) m_manager->raise_exception("invalid number of arguments to floating point relation"); if (domain[0] != domain[1] || !is_float_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected equal FloatingPoint sorts as arguments"); symbol name; switch (k) { case OP_FPA_EQ: name = "fp.eq"; break; case OP_FPA_LT: name = "fp.lt"; break; case OP_FPA_GT: name = "fp.gt"; break; case OP_FPA_LE: name = "fp.leq"; break; case OP_FPA_GE: name = "fp.geq"; break; default: UNREACHABLE(); break; } func_decl_info finfo(m_family_id, k); finfo.set_chainable(true); return m_manager->mk_func_decl(name, domain[0], domain[1], m_manager->mk_bool_sort(), finfo); } func_decl * fpa_decl_plugin::mk_unary_rel_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 1) m_manager->raise_exception("invalid number of arguments to floating point relation"); if (!is_float_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected argument of FloatingPoint sort"); symbol name; switch (k) { case OP_FPA_IS_ZERO: name = "fp.isZero"; break; case OP_FPA_IS_NEGATIVE: name = "fp.isNegative"; break; case OP_FPA_IS_POSITIVE: name = "fp.isPositive"; break; case OP_FPA_IS_NAN: name = "fp.isNaN"; break; case OP_FPA_IS_INF: name = "fp.isInfinite"; break; case OP_FPA_IS_NORMAL: name = "fp.isNormal"; break; case OP_FPA_IS_SUBNORMAL: name = "fp.isSubnormal"; break; default: UNREACHABLE(); break; } return m_manager->mk_func_decl(name, arity, domain, m_manager->mk_bool_sort(), func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_unary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 1) m_manager->raise_exception("invalid number of arguments to floating point operator"); if (!is_float_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected argument of FloatingPoint sort"); symbol name; switch (k) { case OP_FPA_ABS: name = "fp.abs"; break; case OP_FPA_NEG: name = "fp.neg"; break; default: UNREACHABLE(); break; } return m_manager->mk_func_decl(name, arity, domain, domain[0], func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_binary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 2) m_manager->raise_exception("invalid number of arguments to floating point operator"); if (domain[0] != domain[1] || !is_float_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected arguments of equal FloatingPoint sorts"); symbol name; switch (k) { case OP_FPA_REM: name = "fp.rem"; break; case OP_FPA_MIN: name = "fp.min"; break; case OP_FPA_MAX: name = "fp.max"; break; default: UNREACHABLE(); break; } return m_manager->mk_func_decl(name, arity, domain, domain[0], func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_rm_binary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 3) m_manager->raise_exception("invalid number of arguments to floating point operator"); if (!is_rm_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected first argument of RoundingMode sort"); if (domain[1] != domain[2] || !is_float_sort(domain[1])) m_manager->raise_exception("sort mismatch, expected arguments 1 and 2 of equal FloatingPoint sorts"); symbol name; switch (k) { case OP_FPA_ADD: name = "fp.add"; break; case OP_FPA_SUB: name = "fp.sub"; break; case OP_FPA_MUL: name = "fp.mul"; break; case OP_FPA_DIV: name = "fp.div"; break; default: UNREACHABLE(); break; } return m_manager->mk_func_decl(name, arity, domain, domain[1], func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_rm_unary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 2) m_manager->raise_exception("invalid number of arguments to floating point operator"); if (!is_rm_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected RoundingMode as first argument"); if (!is_float_sort(domain[1])) m_manager->raise_exception("sort mismatch, expected FloatingPoint as second argument"); symbol name; switch (k) { case OP_FPA_SQRT: name = "fp.sqrt"; break; case OP_FPA_ROUND_TO_INTEGRAL: name = "fp.roundToIntegral"; break; default: UNREACHABLE(); break; } return m_manager->mk_func_decl(name, arity, domain, domain[1], func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_fma(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 4) m_manager->raise_exception("invalid number of arguments to fused_ma operator"); if (!is_rm_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected RoundingMode as first argument"); if (domain[1] != domain[2] || domain[1] != domain[3] || !is_float_sort(domain[1])) m_manager->raise_exception("sort mismatch, expected arguments 1,2,3 of equal FloatingPoint sort"); symbol name("fp.fma"); return m_manager->mk_func_decl(name, arity, domain, domain[1], func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_to_fp(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (m_bv_plugin && arity == 3 && is_sort_of(domain[0], m_bv_fid, BV_SORT) && is_sort_of(domain[1], m_bv_fid, BV_SORT) && is_sort_of(domain[2], m_bv_fid, BV_SORT)) { // 3 BVs -> 1 FP unsigned ebits = domain[1]->get_parameter(0).get_int(); unsigned sbits = domain[2]->get_parameter(0).get_int() + 1; parameter ps[] = { parameter(ebits), parameter(sbits) }; sort * fp = mk_float_sort(ebits, sbits); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, 2, ps)); } else if (m_bv_plugin && arity == 1 && is_sort_of(domain[0], m_bv_fid, BV_SORT)) { // 1 BV -> 1 FP if (num_parameters != 2) m_manager->raise_exception("invalid number of parameters to to_fp"); if (!parameters[0].is_int() || !parameters[1].is_int()) m_manager->raise_exception("invalid parameter type to to_fp"); int ebits = parameters[0].get_int(); int sbits = parameters[1].get_int(); if (domain[0]->get_parameter(0).get_int() != (ebits + sbits)) m_manager->raise_exception("sort mismatch; invalid bit-vector size, expected bitvector of size (ebits+sbits)"); sort * fp = mk_float_sort(ebits, sbits); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else if (m_bv_plugin && arity == 2 && is_sort_of(domain[0], m_family_id, ROUNDING_MODE_SORT) && is_sort_of(domain[1], m_bv_fid, BV_SORT)) { // RoundingMode + 1 BV -> 1 FP if (num_parameters != 2) m_manager->raise_exception("invalid number of parameters to to_fp"); if (!parameters[0].is_int() || !parameters[1].is_int()) m_manager->raise_exception("invalid parameter type to to_fp"); int ebits = parameters[0].get_int(); int sbits = parameters[1].get_int(); sort * fp = mk_float_sort(ebits, sbits); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else if (arity == 2 && is_sort_of(domain[0], m_family_id, ROUNDING_MODE_SORT) && is_sort_of(domain[1], m_family_id, FLOATING_POINT_SORT)) { // Rounding + 1 FP -> 1 FP if (num_parameters != 2) m_manager->raise_exception("invalid number of parameters to to_fp"); if (!parameters[0].is_int() || !parameters[1].is_int()) m_manager->raise_exception("invalid parameter type to to_fp"); int ebits = parameters[0].get_int(); int sbits = parameters[1].get_int(); if (!is_rm_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected first argument of RoundingMode sort"); if (!is_sort_of(domain[1], m_family_id, FLOATING_POINT_SORT)) m_manager->raise_exception("sort mismatch, expected second argument of FloatingPoint sort"); sort * fp = mk_float_sort(ebits, sbits); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else if (arity == 3 && is_sort_of(domain[0], m_family_id, ROUNDING_MODE_SORT) && is_sort_of(domain[1], m_arith_fid, REAL_SORT) && is_sort_of(domain[2], m_arith_fid, INT_SORT)) { // Rounding + 1 Real + 1 Int -> 1 FP if (!(num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int())) m_manager->raise_exception("expecting two integer parameters to to_fp"); sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else if (arity == 1 && is_sort_of(domain[0], m_arith_fid, REAL_SORT)) { // 1 Real -> 1 FP if (!(num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int())) m_manager->raise_exception("expecting two integer parameters to to_fp"); if (domain[1] != m_real_sort) m_manager->raise_exception("sort mismatch, expected one argument of Real sort"); sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else if (arity == 2 && is_sort_of(domain[0], m_family_id, ROUNDING_MODE_SORT) && is_sort_of(domain[1], m_arith_fid, REAL_SORT)) { // Rounding + 1 Real -> 1 FP if (!(num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int())) m_manager->raise_exception("expecting two integer parameters to to_fp"); sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else if (arity == 2 && is_sort_of(domain[0], m_family_id, ROUNDING_MODE_SORT) && is_sort_of(domain[1], m_arith_fid, INT_SORT)) { // Rounding + 1 Int -> 1 FP if (!(num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int())) m_manager->raise_exception("expecting two integer parameters to to_fp"); sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else { m_manager->raise_exception("Unexpected argument combination for (_ to_fp eb sb). Supported argument combinations are: " "((_ BitVec 1) (_ BitVec eb) (_ BitVec sb-1)), " "(_ BitVec (eb+sb)), " "(Real), " "(RoundingMode (_ BitVec (eb+sb))), " "(RoundingMode (_ FloatingPoint eb' sb')), " "(RoundingMode Real Int), " "(RoundingMode Int), and " "(RoundingMode Real)." ); } return 0; } func_decl * fpa_decl_plugin::mk_to_fp_unsigned(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { SASSERT(m_bv_plugin); if (arity != 2) m_manager->raise_exception("invalid number of arguments to to_fp_unsigned"); if (!is_sort_of(domain[0], m_family_id, ROUNDING_MODE_SORT)) m_manager->raise_exception("sort mismatch, expected first argument of RoundingMode sort"); if (!is_sort_of(domain[1], m_bv_fid, BV_SORT)) m_manager->raise_exception("sort mismatch, expected second argument of bit-vector sort"); // RoundingMode + 1 BV -> 1 FP if (num_parameters != 2) m_manager->raise_exception("invalid number of parameters to to_fp_unsigned"); if (!parameters[0].is_int() || !parameters[1].is_int()) m_manager->raise_exception("invalid parameter type to to_fp_unsigned"); int ebits = parameters[0].get_int(); int sbits = parameters[1].get_int(); sort * fp = mk_float_sort(ebits, sbits); symbol name("to_fp_unsigned"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } func_decl * fpa_decl_plugin::mk_fp(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 3) m_manager->raise_exception("invalid number of arguments to fp"); if (!is_sort_of(domain[0], m_bv_fid, BV_SORT) || (domain[0]->get_parameter(0).get_int() != 1) || !is_sort_of(domain[1], m_bv_fid, BV_SORT) || !is_sort_of(domain[2], m_bv_fid, BV_SORT)) m_manager->raise_exception("sort mismatch, expected three bit-vectors, the first one of size 1."); int eb = (domain[1])->get_parameter(0).get_int(); int sb = (domain[2])->get_parameter(0).get_int() + 1; symbol name("fp"); sort * fp = mk_float_sort(eb, sb); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_to_ubv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { SASSERT(m_bv_plugin); if (arity != 2) m_manager->raise_exception("invalid number of arguments to fp.to_ubv"); if (num_parameters != 1) m_manager->raise_exception("invalid number of parameters to fp.to_ubv"); if (!parameters[0].is_int()) m_manager->raise_exception("invalid parameter type; fp.to_ubv expects an int parameter"); if (!is_rm_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected first argument of RoundingMode sort"); if (!is_sort_of(domain[1], m_family_id, FLOATING_POINT_SORT)) m_manager->raise_exception("sort mismatch, expected second argument of FloatingPoint sort"); if (parameters[0].get_int() <= 0) m_manager->raise_exception("invalid parameter value; fp.to_ubv expects a parameter larger than 0"); symbol name("fp.to_ubv"); sort * bvs = m_bv_plugin->mk_sort(BV_SORT, 1, parameters); return m_manager->mk_func_decl(name, arity, domain, bvs, func_decl_info(m_family_id, k, num_parameters, parameters)); } func_decl * fpa_decl_plugin::mk_to_sbv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { SASSERT(m_bv_plugin); if (arity != 2) m_manager->raise_exception("invalid number of arguments to fp.to_sbv"); if (num_parameters != 1) m_manager->raise_exception("invalid number of parameters to fp.to_sbv"); if (!parameters[0].is_int()) m_manager->raise_exception("invalid parameter type; fp.to_sbv expects an int parameter"); if (!is_rm_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected first argument of RoundingMode sort"); if (!is_sort_of(domain[1], m_family_id, FLOATING_POINT_SORT)) m_manager->raise_exception("sort mismatch, expected second argument of FloatingPoint sort"); if (parameters[0].get_int() <= 0) m_manager->raise_exception("invalid parameter value; fp.to_sbv expects a parameter larger than 0"); symbol name("fp.to_sbv"); sort * bvs = m_bv_plugin->mk_sort(BV_SORT, 1, parameters); return m_manager->mk_func_decl(name, arity, domain, bvs, func_decl_info(m_family_id, k, num_parameters, parameters)); } func_decl * fpa_decl_plugin::mk_to_real(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 1) m_manager->raise_exception("invalid number of arguments to fp.to_real"); if (!is_float_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected argument of FloatingPoint sort"); symbol name("fp.to_real"); return m_manager->mk_func_decl(name, 1, domain, m_real_sort, func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_float_to_ieee_bv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 1) m_manager->raise_exception("invalid number of arguments to asIEEEBV"); if (!is_float_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected argument of FloatingPoint sort"); unsigned float_sz = domain[0]->get_parameter(0).get_int() + domain[0]->get_parameter(1).get_int(); parameter ps[] = { parameter(float_sz) }; sort * bv_srt = m_bv_plugin->mk_sort(m_bv_fid, 1, ps); symbol name("to_ieee_bv"); return m_manager->mk_func_decl(name, 1, domain, bv_srt, func_decl_info(m_family_id, k, num_parameters, parameters)); } func_decl * fpa_decl_plugin::mk_internal_bv_wrap(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 1) m_manager->raise_exception("invalid number of arguments to internal_bv_wrap"); if (!is_float_sort(domain[0]) && !is_rm_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected argument of FloatingPoint or RoundingMode sort"); if (is_float_sort(domain[0])) { unsigned float_sz = domain[0]->get_parameter(0).get_int() + domain[0]->get_parameter(1).get_int(); parameter ps[] = { parameter(float_sz) }; sort * bv_srt = m_bv_plugin->mk_sort(m_bv_fid, 1, ps); return m_manager->mk_func_decl(symbol("bv_wrap"), 1, domain, bv_srt, func_decl_info(m_family_id, k, num_parameters, parameters)); } else { parameter ps[] = { parameter(3) }; sort * bv_srt = m_bv_plugin->mk_sort(m_bv_fid, 1, ps); return m_manager->mk_func_decl(symbol("bv_wrap"), 1, domain, bv_srt, func_decl_info(m_family_id, k, num_parameters, parameters)); } } func_decl * fpa_decl_plugin::mk_internal_bv_unwrap(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 1) m_manager->raise_exception("invalid number of arguments to internal_bv_unwrap"); if (!is_sort_of(domain[0], m_bv_fid, BV_SORT)) m_manager->raise_exception("sort mismatch, expected argument of bitvector sort"); if (!is_float_sort(range) && !is_rm_sort(range)) m_manager->raise_exception("sort mismatch, expected range of FloatingPoint sort"); return m_manager->mk_func_decl(symbol("bv_unwrap"), 1, domain, range, func_decl_info(m_family_id, k, num_parameters, parameters)); } func_decl * fpa_decl_plugin::mk_internal_to_ubv_unspecified( decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 0) m_manager->raise_exception("invalid number of arguments to fp.to_ubv_unspecified"); if (num_parameters != 1) m_manager->raise_exception("invalid number of parameters to fp.to_ubv_unspecified; expecting 1"); if (!parameters[0].is_int()) m_manager->raise_exception("invalid parameters type provided to fp.to_ubv_unspecified; expecting an integer"); sort * bv_srt = m_bv_plugin->mk_sort(m_bv_fid, 1, parameters); return m_manager->mk_func_decl(symbol("fp.to_ubv_unspecified"), 0, domain, bv_srt, func_decl_info(m_family_id, k, num_parameters, parameters)); } func_decl * fpa_decl_plugin::mk_internal_to_sbv_unspecified( decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 0) m_manager->raise_exception("invalid number of arguments to internal_to_sbv_unspecified"); if (num_parameters != 1) m_manager->raise_exception("invalid number of parameters to fp.to_sbv_unspecified; expecting 1"); if (!parameters[0].is_int()) m_manager->raise_exception("invalid parameters type provided to fp.to_sbv_unspecified; expecting an integer"); sort * bv_srt = m_bv_plugin->mk_sort(m_bv_fid, 1, parameters); return m_manager->mk_func_decl(symbol("fp.to_sbv_unspecified"), 0, domain, bv_srt, func_decl_info(m_family_id, k, num_parameters, parameters)); } func_decl * fpa_decl_plugin::mk_internal_to_real_unspecified( decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 0) m_manager->raise_exception("invalid number of arguments to internal_to_real_unspecified"); if (!is_sort_of(range, m_arith_fid, REAL_SORT)) m_manager->raise_exception("sort mismatch, expected range of FloatingPoint sort"); return m_manager->mk_func_decl(symbol("fp.to_real_unspecified"), 0, domain, m_real_sort, func_decl_info(m_family_id, k, num_parameters, parameters)); } func_decl * fpa_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { switch (k) { case OP_FPA_MINUS_INF: case OP_FPA_PLUS_INF: case OP_FPA_NAN: case OP_FPA_MINUS_ZERO: case OP_FPA_PLUS_ZERO: return mk_float_const_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_RM_NEAREST_TIES_TO_EVEN: case OP_FPA_RM_NEAREST_TIES_TO_AWAY: case OP_FPA_RM_TOWARD_POSITIVE: case OP_FPA_RM_TOWARD_NEGATIVE: case OP_FPA_RM_TOWARD_ZERO: return mk_rm_const_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_EQ: case OP_FPA_LT: case OP_FPA_GT: case OP_FPA_LE: case OP_FPA_GE: return mk_bin_rel_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_IS_ZERO: case OP_FPA_IS_NEGATIVE: case OP_FPA_IS_POSITIVE: case OP_FPA_IS_NAN: case OP_FPA_IS_INF: case OP_FPA_IS_NORMAL: case OP_FPA_IS_SUBNORMAL: return mk_unary_rel_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_ABS: case OP_FPA_NEG: return mk_unary_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_REM: case OP_FPA_MIN: case OP_FPA_MAX: return mk_binary_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_ADD: case OP_FPA_MUL: case OP_FPA_DIV: return mk_rm_binary_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_SUB: if (arity == 1) return mk_unary_decl(OP_FPA_NEG, num_parameters, parameters, arity, domain, range); else return mk_rm_binary_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_SQRT: case OP_FPA_ROUND_TO_INTEGRAL: return mk_rm_unary_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_FMA: return mk_fma(k, num_parameters, parameters, arity, domain, range); case OP_FPA_FP: return mk_fp(k, num_parameters, parameters, arity, domain, range); case OP_FPA_TO_UBV: return mk_to_ubv(k, num_parameters, parameters, arity, domain, range); case OP_FPA_TO_SBV: return mk_to_sbv(k, num_parameters, parameters, arity, domain, range); case OP_FPA_TO_REAL: return mk_to_real(k, num_parameters, parameters, arity, domain, range); case OP_FPA_TO_FP: return mk_to_fp(k, num_parameters, parameters, arity, domain, range); case OP_FPA_TO_FP_UNSIGNED: return mk_to_fp_unsigned(k, num_parameters, parameters, arity, domain, range); case OP_FPA_TO_IEEE_BV: return mk_float_to_ieee_bv(k, num_parameters, parameters, arity, domain, range); case OP_FPA_INTERNAL_BVWRAP: return mk_internal_bv_wrap(k, num_parameters, parameters, arity, domain, range); case OP_FPA_INTERNAL_BVUNWRAP: return mk_internal_bv_unwrap(k, num_parameters, parameters, arity, domain, range); case OP_FPA_INTERNAL_TO_UBV_UNSPECIFIED: return mk_internal_to_ubv_unspecified(k, num_parameters, parameters, arity, domain, range); case OP_FPA_INTERNAL_TO_SBV_UNSPECIFIED: return mk_internal_to_sbv_unspecified(k, num_parameters, parameters, arity, domain, range); case OP_FPA_INTERNAL_TO_REAL_UNSPECIFIED: return mk_internal_to_real_unspecified(k, num_parameters, parameters, arity, domain, range); default: m_manager->raise_exception("unsupported floating point operator"); return 0; } } void fpa_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { // These are the operators from the final draft of the SMT FloatingPoint standard op_names.push_back(builtin_name("+oo", OP_FPA_PLUS_INF)); op_names.push_back(builtin_name("-oo", OP_FPA_MINUS_INF)); op_names.push_back(builtin_name("+zero", OP_FPA_PLUS_ZERO)); op_names.push_back(builtin_name("-zero", OP_FPA_MINUS_ZERO)); op_names.push_back(builtin_name("NaN", OP_FPA_NAN)); op_names.push_back(builtin_name("roundNearestTiesToEven", OP_FPA_RM_NEAREST_TIES_TO_EVEN)); op_names.push_back(builtin_name("roundNearestTiesToAway", OP_FPA_RM_NEAREST_TIES_TO_AWAY)); op_names.push_back(builtin_name("roundTowardPositive", OP_FPA_RM_TOWARD_POSITIVE)); op_names.push_back(builtin_name("roundTowardNegative", OP_FPA_RM_TOWARD_NEGATIVE)); op_names.push_back(builtin_name("roundTowardZero", OP_FPA_RM_TOWARD_ZERO)); op_names.push_back(builtin_name("RNE", OP_FPA_RM_NEAREST_TIES_TO_EVEN)); op_names.push_back(builtin_name("RNA", OP_FPA_RM_NEAREST_TIES_TO_AWAY)); op_names.push_back(builtin_name("RTP", OP_FPA_RM_TOWARD_POSITIVE)); op_names.push_back(builtin_name("RTN", OP_FPA_RM_TOWARD_NEGATIVE)); op_names.push_back(builtin_name("RTZ", OP_FPA_RM_TOWARD_ZERO)); op_names.push_back(builtin_name("fp.abs", OP_FPA_ABS)); op_names.push_back(builtin_name("fp.neg", OP_FPA_NEG)); op_names.push_back(builtin_name("fp.add", OP_FPA_ADD)); op_names.push_back(builtin_name("fp.sub", OP_FPA_SUB)); op_names.push_back(builtin_name("fp.mul", OP_FPA_MUL)); op_names.push_back(builtin_name("fp.div", OP_FPA_DIV)); op_names.push_back(builtin_name("fp.fma", OP_FPA_FMA)); op_names.push_back(builtin_name("fp.sqrt", OP_FPA_SQRT)); op_names.push_back(builtin_name("fp.rem", OP_FPA_REM)); op_names.push_back(builtin_name("fp.roundToIntegral", OP_FPA_ROUND_TO_INTEGRAL)); op_names.push_back(builtin_name("fp.min", OP_FPA_MIN)); op_names.push_back(builtin_name("fp.max", OP_FPA_MAX)); op_names.push_back(builtin_name("fp.leq", OP_FPA_LE)); op_names.push_back(builtin_name("fp.lt", OP_FPA_LT)); op_names.push_back(builtin_name("fp.geq", OP_FPA_GE)); op_names.push_back(builtin_name("fp.gt", OP_FPA_GT)); op_names.push_back(builtin_name("fp.eq", OP_FPA_EQ)); op_names.push_back(builtin_name("fp.isNormal", OP_FPA_IS_NORMAL)); op_names.push_back(builtin_name("fp.isSubnormal", OP_FPA_IS_SUBNORMAL)); op_names.push_back(builtin_name("fp.isZero", OP_FPA_IS_ZERO)); op_names.push_back(builtin_name("fp.isInfinite", OP_FPA_IS_INF)); op_names.push_back(builtin_name("fp.isNaN", OP_FPA_IS_NAN)); op_names.push_back(builtin_name("fp.isNegative", OP_FPA_IS_NEGATIVE)); op_names.push_back(builtin_name("fp.isPositive", OP_FPA_IS_POSITIVE)); op_names.push_back(builtin_name("fp", OP_FPA_FP)); op_names.push_back(builtin_name("fp.to_ubv", OP_FPA_TO_UBV)); op_names.push_back(builtin_name("fp.to_sbv", OP_FPA_TO_SBV)); op_names.push_back(builtin_name("fp.to_real", OP_FPA_TO_REAL)); op_names.push_back(builtin_name("to_fp", OP_FPA_TO_FP)); op_names.push_back(builtin_name("to_fp_unsigned", OP_FPA_TO_FP_UNSIGNED)); /* Extensions */ op_names.push_back(builtin_name("to_ieee_bv", OP_FPA_TO_IEEE_BV)); } void fpa_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { sort_names.push_back(builtin_name("FloatingPoint", FLOATING_POINT_SORT)); sort_names.push_back(builtin_name("RoundingMode", ROUNDING_MODE_SORT)); // The final theory supports three common FloatingPoint sorts sort_names.push_back(builtin_name("Float16", FLOAT16_SORT)); sort_names.push_back(builtin_name("Float32", FLOAT32_SORT)); sort_names.push_back(builtin_name("Float64", FLOAT64_SORT)); sort_names.push_back(builtin_name("Float128", FLOAT128_SORT)); } expr * fpa_decl_plugin::get_some_value(sort * s) { if (s->is_sort_of(m_family_id, FLOATING_POINT_SORT)) { mpf tmp; m_fm.mk_nan(s->get_parameter(0).get_int(), s->get_parameter(1).get_int(), tmp); expr * res = mk_numeral(tmp); m_fm.del(tmp); return res; } else if (s->is_sort_of(m_family_id, ROUNDING_MODE_SORT)) { func_decl * f = mk_rm_const_decl(OP_FPA_RM_TOWARD_ZERO, 0, 0, 0, 0, s); return m_manager->mk_const(f); } UNREACHABLE(); return 0; } bool fpa_decl_plugin::is_value(app * e) const { if (e->get_family_id() != m_family_id) return false; switch (e->get_decl_kind()) { case OP_FPA_RM_NEAREST_TIES_TO_EVEN: case OP_FPA_RM_NEAREST_TIES_TO_AWAY: case OP_FPA_RM_TOWARD_POSITIVE: case OP_FPA_RM_TOWARD_NEGATIVE: case OP_FPA_RM_TOWARD_ZERO: case OP_FPA_NUM: case OP_FPA_PLUS_INF: case OP_FPA_MINUS_INF: case OP_FPA_PLUS_ZERO: case OP_FPA_MINUS_ZERO: case OP_FPA_NAN: return true; case OP_FPA_FP: return m_manager->is_value(e->get_arg(0)) && m_manager->is_value(e->get_arg(1)) && m_manager->is_value(e->get_arg(2)); default: return false; } } bool fpa_decl_plugin::is_unique_value(app* e) const { if (e->get_family_id() != m_family_id) return false; switch (e->get_decl_kind()) { case OP_FPA_RM_NEAREST_TIES_TO_EVEN: case OP_FPA_RM_NEAREST_TIES_TO_AWAY: case OP_FPA_RM_TOWARD_POSITIVE: case OP_FPA_RM_TOWARD_NEGATIVE: case OP_FPA_RM_TOWARD_ZERO: return true; case OP_FPA_PLUS_INF: /* No; +oo == fp(#b0 #b11 #b00) */ case OP_FPA_MINUS_INF: /* No; -oo == fp #b1 #b11 #b00) */ case OP_FPA_PLUS_ZERO: /* No; +zero == fp #b0 #b00 #b000) */ case OP_FPA_MINUS_ZERO: /* No; -zero == fp #b1 #b00 #b000) */ case OP_FPA_NAN: /* No; NaN == (fp #b0 #b111111 #b0000001) */ case OP_FPA_NUM: /* see NaN */ return false; case OP_FPA_FP: return m_manager->is_unique_value(e->get_arg(0)) && m_manager->is_unique_value(e->get_arg(1)) && m_manager->is_unique_value(e->get_arg(2)); default: return false; } } fpa_util::fpa_util(ast_manager & m): m_manager(m), m_fid(m.mk_family_id("fpa")), m_a_util(m), m_bv_util(m) { m_plugin = static_cast(m.get_plugin(m_fid)); } fpa_util::~fpa_util() { } sort * fpa_util::mk_float_sort(unsigned ebits, unsigned sbits) { parameter ps[2] = { parameter(ebits), parameter(sbits) }; return m().mk_sort(m_fid, FLOATING_POINT_SORT, 2, ps); } unsigned fpa_util::get_ebits(sort * s) { SASSERT(is_float(s)); return static_cast(s->get_parameter(0).get_int()); } unsigned fpa_util::get_sbits(sort * s) { SASSERT(is_float(s)); return static_cast(s->get_parameter(1).get_int()); } app * fpa_util::mk_nan(unsigned ebits, unsigned sbits) { scoped_mpf v(fm()); fm().mk_nan(ebits, sbits, v); return mk_value(v); } app * fpa_util::mk_pinf(unsigned ebits, unsigned sbits) { scoped_mpf v(fm()); fm().mk_pinf(ebits, sbits, v); return mk_value(v); } app * fpa_util::mk_ninf(unsigned ebits, unsigned sbits) { scoped_mpf v(fm()); fm().mk_ninf(ebits, sbits, v); return mk_value(v); } app * fpa_util::mk_pzero(unsigned ebits, unsigned sbits) { scoped_mpf v(fm()); fm().mk_pzero(ebits, sbits, v); return mk_value(v); } app * fpa_util::mk_nzero(unsigned ebits, unsigned sbits) { scoped_mpf v(fm()); fm().mk_nzero(ebits, sbits, v); return mk_value(v); } app * fpa_util::mk_internal_to_ubv_unspecified(unsigned width) { parameter ps[] = { parameter(width) }; sort * range = m_bv_util.mk_sort(width); return m().mk_app(get_family_id(), OP_FPA_INTERNAL_TO_UBV_UNSPECIFIED, 1, ps, 0, 0, range); } app * fpa_util::mk_internal_to_sbv_unspecified(unsigned width) { parameter ps[] = { parameter(width) }; sort * range = m_bv_util.mk_sort(width); return m().mk_app(get_family_id(), OP_FPA_INTERNAL_TO_SBV_UNSPECIFIED, 1, ps, 0, 0, range); } app * fpa_util::mk_internal_to_real_unspecified() { sort * range = m_a_util.mk_real(); return m().mk_app(get_family_id(), OP_FPA_INTERNAL_TO_REAL_UNSPECIFIED, 0, 0, 0, 0, range); }z3-4.4.0/src/ast/pb_decl_plugin.h0000644000175000017500000001013212540347414016337 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: pb_decl_plugin.h Abstract: Pseudo-Boolean and Cardinality Constraints plugin Author: Nikolaj Bjorner (nbjorner) 2013-05-11 Notes: (at-most-k x1 .... x_n) means x1 + ... + x_n <= k hence: (not (at-most-k x1 .... x_n)) means x1 + ... + x_n >= k + 1 --*/ #ifndef _PB_DECL_PLUGIN_H_ #define _PB_DECL_PLUGIN_H_ #include"ast.h" enum pb_op_kind { OP_AT_MOST_K, // at most K Booleans are true. OP_AT_LEAST_K, // at least K Booleans are true. OP_PB_LE, // pseudo-Boolean <= (generalizes at_most_k) OP_PB_GE, // pseudo-Boolean >= OP_PB_EQ, // equality LAST_PB_OP }; class pb_decl_plugin : public decl_plugin { symbol m_at_most_sym; symbol m_at_least_sym; symbol m_pble_sym; symbol m_pbge_sym; symbol m_pbeq_sym; func_decl * mk_at_most(unsigned arity, unsigned k); func_decl * mk_at_least(unsigned arity, unsigned k); func_decl * mk_le(unsigned arity, rational const* coeffs, int k); func_decl * mk_ge(unsigned arity, rational const* coeffs, int k); func_decl * mk_eq(unsigned arity, rational const* coeffs, int k); public: pb_decl_plugin(); virtual ~pb_decl_plugin() {} virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { UNREACHABLE(); return 0; } virtual decl_plugin * mk_fresh() { return alloc(pb_decl_plugin); } // // Contract for func_decl: // parameters[0] - integer (at most k elements) // all sorts are Booleans // parameters[1] .. parameters[arity] - coefficients virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); virtual void get_op_names(svector & op_names, symbol const & logic); }; class pb_util { ast_manager & m; family_id m_fid; public: pb_util(ast_manager& m):m(m), m_fid(m.mk_family_id("pb")) {} ast_manager & get_manager() const { return m; } family_id get_family_id() const { return m_fid; } app * mk_at_most_k(unsigned num_args, expr * const * args, unsigned k); app * mk_at_least_k(unsigned num_args, expr * const * args, unsigned k); app * mk_le(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k); app * mk_ge(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k); app * mk_eq(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k); app * mk_lt(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k); bool is_at_most_k(func_decl *a) const; bool is_at_most_k(expr *a) const { return is_app(a) && is_at_most_k(to_app(a)->get_decl()); } bool is_at_most_k(expr *a, rational& k) const; bool is_at_least_k(func_decl *a) const; bool is_at_least_k(expr *a) const { return is_app(a) && is_at_least_k(to_app(a)->get_decl()); } bool is_at_least_k(expr *a, rational& k) const; rational get_k(func_decl *a) const; rational get_k(expr *a) const { return get_k(to_app(a)->get_decl()); } bool is_le(func_decl *a) const; bool is_le(expr *a) const { return is_app(a) && is_le(to_app(a)->get_decl()); } bool is_le(expr* a, rational& k) const; bool is_ge(func_decl* a) const; bool is_ge(expr* a) const { return is_app(a) && is_ge(to_app(a)->get_decl()); } bool is_ge(expr* a, rational& k) const; rational get_coeff(expr* a, unsigned index) const { return get_coeff(to_app(a)->get_decl(), index); } rational get_coeff(func_decl* a, unsigned index) const; bool has_unit_coefficients(func_decl* f) const; bool has_unit_coefficients(expr* f) const { return is_app(f) && has_unit_coefficients(to_app(f)->get_decl()); } bool is_eq(func_decl* f) const; bool is_eq(expr* e) const { return is_app(e) && is_eq(to_app(e)->get_decl()); } bool is_eq(expr* e, rational& k) const; private: rational to_rational(parameter const& p) const; }; #endif /* _PB_DECL_PLUGIN_H_ */ z3-4.4.0/src/ast/bv_decl_plugin.cpp0000644000175000017500000010134112540347414016703 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bv_decl_plugin.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-09. Revision History: --*/ #include #include"bv_decl_plugin.h" #include"arith_decl_plugin.h" #include"warning.h" #include"ast_pp.h" #include"ast_smt2_pp.h" bv_decl_plugin::bv_decl_plugin(): m_bv_sym("bv"), m_concat_sym("concat"), m_sign_extend_sym("sign_extend"), m_zero_extend_sym("zero_extend"), m_extract_sym("extract"), m_rotate_left_sym("rotate_left"), m_rotate_right_sym("rotate_right"), m_repeat_sym("repeat"), m_bit2bool_sym("bit2bool"), m_mkbv_sym("mkbv"), m_bit0(0), m_bit1(0), m_carry(0), m_xor3(0), m_int_sort(0) { } void bv_decl_plugin::set_manager(ast_manager * m, family_id id) { decl_plugin::set_manager(m, id); for (unsigned i = 1; i <= 64; i++) { mk_bv_sort(i); } m_bit0 = m->mk_const_decl(symbol("bit0"), get_bv_sort(1), func_decl_info(m_family_id, OP_BIT0)); m_bit1 = m->mk_const_decl(symbol("bit1"), get_bv_sort(1), func_decl_info(m_family_id, OP_BIT1)); m->inc_ref(m_bit0); m->inc_ref(m_bit1); sort * b = m->mk_bool_sort(); sort * d[3] = {b, b, b}; m_carry = m_manager->mk_func_decl(symbol("carry"), 3, d, b, func_decl_info(m_family_id, OP_CARRY)); m_manager->inc_ref(m_carry); m_xor3 = m_manager->mk_func_decl(symbol("xor3"), 3, d, b, func_decl_info(m_family_id, OP_XOR3)); m_manager->inc_ref(m_xor3); m_int_sort = m_manager->mk_sort(m_manager->mk_family_id("arith"), INT_SORT); SASSERT(m_int_sort != 0); // arith_decl_plugin must be installed before bv_decl_plugin. m_manager->inc_ref(m_int_sort); } void bv_decl_plugin::finalize() { #define DEC_REF(FIELD) dec_range_ref(FIELD.begin(), FIELD.end(), *m_manager) if (m_bit0) { m_manager->dec_ref(m_bit0); } if (m_bit1) { m_manager->dec_ref(m_bit1); } if (m_carry) { m_manager->dec_ref(m_carry); } if (m_xor3) { m_manager->dec_ref(m_xor3); } if (m_int_sort) { m_manager->dec_ref(m_int_sort); } DEC_REF(m_bv_sorts); DEC_REF(m_bv_neg); DEC_REF(m_bv_add); DEC_REF(m_bv_sub); DEC_REF(m_bv_mul); DEC_REF(m_bv_sdiv); DEC_REF(m_bv_udiv); DEC_REF(m_bv_srem); DEC_REF(m_bv_urem); DEC_REF(m_bv_smod); DEC_REF(m_bv_sdiv0); DEC_REF(m_bv_udiv0); DEC_REF(m_bv_srem0); DEC_REF(m_bv_urem0); DEC_REF(m_bv_smod0); DEC_REF(m_bv_sdiv_i); DEC_REF(m_bv_udiv_i); DEC_REF(m_bv_srem_i); DEC_REF(m_bv_urem_i); DEC_REF(m_bv_smod_i); DEC_REF(m_bv_uleq); DEC_REF(m_bv_sleq); DEC_REF(m_bv_ugeq); DEC_REF(m_bv_sgeq); DEC_REF(m_bv_ult); DEC_REF(m_bv_slt); DEC_REF(m_bv_ugt); DEC_REF(m_bv_sgt); DEC_REF(m_bv_and); DEC_REF(m_bv_or); DEC_REF(m_bv_not); DEC_REF(m_bv_xor); DEC_REF(m_bv_nand); DEC_REF(m_bv_nor); DEC_REF(m_bv_xnor); DEC_REF(m_bv_redor); DEC_REF(m_bv_redand); DEC_REF(m_bv_comp); DEC_REF(m_bv_mul_ovfl); DEC_REF(m_bv_smul_ovfl); DEC_REF(m_bv_smul_udfl); DEC_REF(m_bv_shl); DEC_REF(m_bv_lshr); DEC_REF(m_bv_ashr); DEC_REF(m_ext_rotate_left); DEC_REF(m_ext_rotate_right); DEC_REF(m_int2bv); DEC_REF(m_bv2int); vector >::iterator it = m_bit2bool.begin(); vector >::iterator end = m_bit2bool.end(); for (; it != end; ++it) { ptr_vector & ds = *it; DEC_REF(ds); } DEC_REF(m_mkbv); } void bv_decl_plugin::mk_bv_sort(unsigned bv_size) { force_ptr_array_size(m_bv_sorts, bv_size + 1); if (m_bv_sorts[bv_size] == 0) { parameter p(bv_size); sort_size sz; if (sort_size::is_very_big_base2(bv_size)) { sz = sort_size::mk_very_big(); } else { sz = sort_size(rational::power_of_two(bv_size)); } m_bv_sorts[bv_size] = m_manager->mk_sort(symbol("bv"), sort_info(m_family_id, BV_SORT, sz, 1, &p)); m_manager->inc_ref(m_bv_sorts[bv_size]); } } inline sort * bv_decl_plugin::get_bv_sort(unsigned bv_size) { if (bv_size < (1 << 12)) { mk_bv_sort(bv_size); return m_bv_sorts[bv_size]; } parameter p(bv_size); sort_size sz(sort_size::mk_very_big()); return m_manager->mk_sort(symbol("bv"), sort_info(m_family_id, BV_SORT, sz, 1, &p)); } sort * bv_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { if (!(num_parameters == 1 && parameters[0].is_int())) { m_manager->raise_exception("expecting one integer parameter to bit-vector sort"); } unsigned bv_size = parameters[0].get_int(); if (bv_size == 0) { m_manager->raise_exception("bit-vector size must be greater than zero"); } mk_bv_sort(bv_size); return m_bv_sorts[bv_size]; } func_decl * bv_decl_plugin::mk_binary(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size, bool ac, bool idempotent) { force_ptr_array_size(decls, bv_size + 1); if (decls[bv_size] == 0) { sort * s = get_bv_sort(bv_size); func_decl_info info(m_family_id, k); info.set_associative(ac); info.set_flat_associative(ac); info.set_commutative(ac); info.set_idempotent(idempotent); decls[bv_size] = m_manager->mk_func_decl(symbol(name), s, s, s, info); m_manager->inc_ref(decls[bv_size]); } return decls[bv_size]; } func_decl * bv_decl_plugin::mk_unary(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size) { force_ptr_array_size(decls, bv_size + 1); if (decls[bv_size] == 0) { sort * s = get_bv_sort(bv_size); decls[bv_size] = m_manager->mk_func_decl(symbol(name), s, s, func_decl_info(m_family_id, k)); m_manager->inc_ref(decls[bv_size]); } return decls[bv_size]; } func_decl * bv_decl_plugin::mk_int2bv(unsigned bv_size, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain) { if (bv_size == 0) { m_manager->raise_exception("bit-vector size must be greater than zero"); } force_ptr_array_size(m_int2bv, bv_size + 1); if (arity != 1) { m_manager->raise_exception("expecting one argument to int2bv"); return 0; } if (m_int2bv[bv_size] == 0) { sort * s = get_bv_sort(bv_size); m_int2bv[bv_size] = m_manager->mk_func_decl(symbol("int2bv"), domain[0], s, func_decl_info(m_family_id, OP_INT2BV, num_parameters, parameters)); m_manager->inc_ref(m_int2bv[bv_size]); } return m_int2bv[bv_size]; } func_decl * bv_decl_plugin::mk_bv2int(unsigned bv_size, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain) { force_ptr_array_size(m_bv2int, bv_size + 1); if (arity != 1) { m_manager->raise_exception("expecting one argument to bv2int"); return 0; } if (m_bv2int[bv_size] == 0) { m_bv2int[bv_size] = m_manager->mk_func_decl(symbol("bv2int"), domain[0], m_int_sort, func_decl_info(m_family_id, OP_BV2INT)); m_manager->inc_ref(m_bv2int[bv_size]); } return m_bv2int[bv_size]; } func_decl * bv_decl_plugin::mk_pred(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size) { force_ptr_array_size(decls, bv_size + 1); if (decls[bv_size] == 0) { sort * s = get_bv_sort(bv_size); decls[bv_size] = m_manager->mk_func_decl(symbol(name), s, s, m_manager->mk_bool_sort(), func_decl_info(m_family_id, k)); m_manager->inc_ref(decls[bv_size]); } return decls[bv_size]; } func_decl * bv_decl_plugin::mk_reduction(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size) { force_ptr_array_size(decls, bv_size + 1); if (decls[bv_size] == 0) { sort * d = get_bv_sort(bv_size); sort * r = get_bv_sort(1); decls[bv_size] = m_manager->mk_func_decl(symbol(name), d, r, func_decl_info(m_family_id, k)); m_manager->inc_ref(decls[bv_size]); } return decls[bv_size]; } func_decl * bv_decl_plugin::mk_comp(unsigned bv_size) { force_ptr_array_size(m_bv_comp, bv_size + 1); if (m_bv_comp[bv_size] == 0) { sort * d = get_bv_sort(bv_size); sort * r = get_bv_sort(1); func_decl_info info(m_family_id, OP_BCOMP); info.set_commutative(); m_bv_comp[bv_size] = m_manager->mk_func_decl(symbol("bvcomp"), d, d, r, info); m_manager->inc_ref(m_bv_comp[bv_size]); } return m_bv_comp[bv_size]; } func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned bv_size) { switch (k) { case OP_BNEG: return mk_unary(m_bv_neg, k, "bvneg", bv_size); case OP_BADD: return mk_binary(m_bv_add, k, "bvadd", bv_size, true); case OP_BSUB: return mk_binary(m_bv_sub, k, "bvsub", bv_size, false); case OP_BMUL: return mk_binary(m_bv_mul, k, "bvmul", bv_size, true); case OP_BSDIV: return mk_binary(m_bv_sdiv, k, "bvsdiv", bv_size, false); case OP_BUDIV: return mk_binary(m_bv_udiv, k, "bvudiv", bv_size, false); case OP_BSREM: return mk_binary(m_bv_srem, k, "bvsrem", bv_size, false); case OP_BUREM: return mk_binary(m_bv_urem, k, "bvurem", bv_size, false); case OP_BSMOD: return mk_binary(m_bv_smod, k, "bvsmod", bv_size, false); case OP_BSDIV0: return mk_unary(m_bv_sdiv0, k, "bvsdiv0", bv_size); case OP_BUDIV0: return mk_unary(m_bv_udiv0, k, "bvudiv0", bv_size); case OP_BSREM0: return mk_unary(m_bv_srem0, k, "bvsrem0", bv_size); case OP_BUREM0: return mk_unary(m_bv_urem0, k, "bvurem0", bv_size); case OP_BSMOD0: return mk_unary(m_bv_smod0, k, "bvsmod0", bv_size); case OP_BSDIV_I: return mk_binary(m_bv_sdiv_i, k, "bvsdiv_i", bv_size, false); case OP_BUDIV_I: return mk_binary(m_bv_udiv_i, k, "bvudiv_i", bv_size, false); case OP_BSREM_I: return mk_binary(m_bv_srem_i, k, "bvsrem_i", bv_size, false); case OP_BUREM_I: return mk_binary(m_bv_urem_i, k, "bvurem_i", bv_size, false); case OP_BSMOD_I: return mk_binary(m_bv_smod_i, k, "bvsmod_i", bv_size, false); case OP_ULEQ: return mk_pred(m_bv_uleq, k, "bvule", bv_size); case OP_SLEQ: return mk_pred(m_bv_sleq, k, "bvsle", bv_size); case OP_UGEQ: return mk_pred(m_bv_ugeq, k, "bvuge", bv_size); case OP_SGEQ: return mk_pred(m_bv_sgeq, k, "bvsge", bv_size); case OP_ULT: return mk_pred(m_bv_ult, k, "bvult", bv_size); case OP_SLT: return mk_pred(m_bv_slt, k, "bvslt", bv_size); case OP_UGT: return mk_pred(m_bv_ugt, k, "bvugt", bv_size); case OP_SGT: return mk_pred(m_bv_sgt, k, "bvsgt", bv_size); case OP_BAND: return mk_binary(m_bv_and, k, "bvand", bv_size, true, true); case OP_BOR: return mk_binary(m_bv_or, k, "bvor", bv_size, true, true); case OP_BNOT: return mk_unary(m_bv_not, k, "bvnot", bv_size); case OP_BXOR: return mk_binary(m_bv_xor, k, "bvxor", bv_size, true); case OP_BNAND: return mk_binary(m_bv_nand, k, "bvnand", bv_size, false); case OP_BNOR: return mk_binary(m_bv_nor, k, "bvnor", bv_size, false); case OP_BXNOR: return mk_binary(m_bv_xnor, k, "bvxnor", bv_size, false); case OP_BREDOR: return mk_reduction(m_bv_redor, k, "bvredor", bv_size); case OP_BREDAND: return mk_reduction(m_bv_redand, k, "bvredand", bv_size); case OP_BCOMP: return mk_comp(bv_size); case OP_BUMUL_NO_OVFL: return mk_pred(m_bv_mul_ovfl, k, "bvumul_noovfl", bv_size); case OP_BSMUL_NO_OVFL: return mk_pred(m_bv_smul_ovfl, k, "bvsmul_noovfl", bv_size); case OP_BSMUL_NO_UDFL: return mk_pred(m_bv_smul_udfl, k, "bvsmul_noudfl", bv_size); case OP_BSHL: return mk_binary(m_bv_shl, k, "bvshl", bv_size, false); case OP_BLSHR: return mk_binary(m_bv_lshr, k, "bvlshr", bv_size, false); case OP_BASHR: return mk_binary(m_bv_ashr, k, "bvashr", bv_size, false); case OP_EXT_ROTATE_LEFT: return mk_binary(m_ext_rotate_left, k, "ext_rotate_left", bv_size, false); case OP_EXT_ROTATE_RIGHT: return mk_binary(m_ext_rotate_right, k, "ext_rotate_right", bv_size, false); default: return 0; } } inline bool bv_decl_plugin::get_bv_size(sort * s, int & result) { if (s->get_family_id() == m_family_id && s->get_decl_kind() == BV_SORT) { result = s->get_parameter(0).get_int(); return true; } return false; } inline bool bv_decl_plugin::get_bv_size(expr * t, int & result) { return get_bv_size(m_manager->get_sort(t), result); } bool bv_decl_plugin::get_concat_size(unsigned arity, sort * const * domain, int & result) { result = 0; for (unsigned i = 0; i < arity; i++) { int sz; if (!get_bv_size(domain[i], sz)) { return false; } result += sz; } return true; } bool bv_decl_plugin::get_extend_size(unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, int & result) { int arg_sz; if (arity != 1 || num_parameters != 1 || !parameters[0].is_int() || !get_bv_size(domain[0], arg_sz)) { return false; } result = arg_sz + parameters[0].get_int(); return true; } bool bv_decl_plugin::get_extract_size(unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, int & result) { int arg_sz; if (arity != 1 || !get_bv_size(domain[0], arg_sz) || num_parameters != 2 || !parameters[0].is_int() || !parameters[1].is_int() || parameters[1].get_int() > parameters[0].get_int() || parameters[0].get_int() >= arg_sz) { return false; } result = parameters[0].get_int() - parameters[1].get_int() + 1; return true; } bool bv_decl_plugin::get_int2bv_size(unsigned num_parameters, parameter const * parameters, int & result) { if (num_parameters != 1) { m_manager->raise_exception("int2bv expects one parameter"); return false; } parameter p(parameters[0]); if (p.is_int()) { result = p.get_int(); return true; } if (!p.is_ast() || !is_expr(p.get_ast())) { m_manager->raise_exception("int2bv expects one integer parameter"); return false; } return get_bv_size(to_expr(p.get_ast()), result); } func_decl * bv_decl_plugin::mk_num_decl(unsigned num_parameters, parameter const * parameters, unsigned arity) { if (!(num_parameters == 2 && arity == 0 && parameters[0].is_rational() && parameters[1].is_int())) { m_manager->raise_exception("invalid bit-vector numeral declaration"); return 0; } unsigned bv_size = parameters[1].get_int(); if (bv_size == 0) { m_manager->raise_exception("bit-vector size must be greater than zero"); } // TODO: sign an error if the parameters[0] is out of range, that is, it is a value not in [0, 2^{bv_size}) // This cannot be enforced now, since some Z3 modules try to generate these invalid numerals. // After SMT-COMP, I should find all offending modules. // For now, I will just simplify the numeral here. parameter p0(mod(parameters[0].get_rational(), rational::power_of_two(bv_size))); parameter ps[2] = { p0, parameters[1] }; sort * bv = get_bv_sort(bv_size); return m_manager->mk_const_decl(m_bv_sym, bv, func_decl_info(m_family_id, OP_BV_NUM, num_parameters, ps)); } func_decl * bv_decl_plugin::mk_bit2bool(unsigned bv_size, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain) { if (!(num_parameters == 1 && parameters[0].is_int() && arity == 1 && parameters[0].get_int() < static_cast(bv_size))) { m_manager->raise_exception("invalid bit2bool declaration"); return 0; } unsigned idx = parameters[0].get_int(); m_bit2bool.reserve(bv_size+1); ptr_vector & v = m_bit2bool[bv_size]; v.reserve(bv_size, 0); if (v[idx] == 0) { v[idx] = m_manager->mk_func_decl(m_bit2bool_sym, domain[0], m_manager->mk_bool_sort(), func_decl_info(m_family_id, OP_BIT2BOOL, num_parameters, parameters)); m_manager->inc_ref(v[idx]); } return v[idx]; } func_decl * bv_decl_plugin::mk_mkbv(unsigned arity, sort * const * domain) { for (unsigned i = 0; i < arity; i++) { if (!m_manager->is_bool(domain[i])) { m_manager->raise_exception("invalid mkbv operator"); return 0; } } unsigned bv_size = arity; m_mkbv.reserve(bv_size+1); if (m_mkbv[bv_size] == 0) { m_mkbv[bv_size] = m_manager->mk_func_decl(m_mkbv_sym, arity, domain, get_bv_sort(bv_size), func_decl_info(m_family_id, OP_MKBV)); m_manager->inc_ref(m_mkbv[bv_size]); } return m_mkbv[bv_size]; } func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { int bv_size; if (k == OP_INT2BV && get_int2bv_size(num_parameters, parameters, bv_size)) { // bv_size is filled in. } else if (k == OP_BV_NUM) { return mk_num_decl(num_parameters, parameters, arity); } else if (k == OP_BIT0) { return m_bit0; } else if (k == OP_BIT1) { return m_bit1; } else if (k == OP_CARRY) { return m_carry; } else if (k == OP_XOR3) { return m_xor3; } else if (k == OP_MKBV) { return mk_mkbv(arity, domain); } else if (arity == 0) { m_manager->raise_exception("no arguments supplied to bit-vector operator"); return 0; } else if (!get_bv_size(domain[0], bv_size)) { m_manager->raise_exception("could not extract bit-vector size"); return 0; } func_decl * r = mk_func_decl(k, bv_size); if (r != 0) { if (arity != r->get_arity()) { if (r->get_info()->is_associative()) arity = r->get_arity(); else { m_manager->raise_exception("declared arity mismatches supplied arity"); return 0; } } for (unsigned i = 0; i < arity; ++i) { if (domain[i] != r->get_domain(i)) { m_manager->raise_exception("declared sorts do not match supplied sorts"); return 0; } } return r; } int r_size; switch (k) { case OP_BIT2BOOL: return mk_bit2bool(bv_size, num_parameters, parameters, arity, domain); case OP_INT2BV: return mk_int2bv(bv_size, num_parameters, parameters, arity, domain); case OP_BV2INT: return mk_bv2int(bv_size, num_parameters, parameters, arity, domain); case OP_CONCAT: if (!get_concat_size(arity, domain, r_size)) m_manager->raise_exception("invalid concat application"); return m_manager->mk_func_decl(m_concat_sym, arity, domain, get_bv_sort(r_size), func_decl_info(m_family_id, k)); case OP_SIGN_EXT: if (!get_extend_size(num_parameters, parameters, arity, domain, r_size)) m_manager->raise_exception("invalid sign_extend application"); return m_manager->mk_func_decl(m_sign_extend_sym, arity, domain, get_bv_sort(r_size), func_decl_info(m_family_id, k, num_parameters, parameters)); case OP_ZERO_EXT: if (!get_extend_size(num_parameters, parameters, arity, domain, r_size)) m_manager->raise_exception("invalid zero_extend application"); return m_manager->mk_func_decl(m_zero_extend_sym, arity, domain, get_bv_sort(r_size), func_decl_info(m_family_id, k, num_parameters, parameters)); case OP_EXTRACT: if (!get_extract_size(num_parameters, parameters, arity, domain, r_size)) m_manager->raise_exception("invalid extract application"); return m_manager->mk_func_decl(m_extract_sym, arity, domain, get_bv_sort(r_size), func_decl_info(m_family_id, k, num_parameters, parameters)); case OP_ROTATE_LEFT: if (arity != 1) m_manager->raise_exception("rotate left expects one argument"); return m_manager->mk_func_decl(m_rotate_left_sym, arity, domain, domain[0], func_decl_info(m_family_id, k, num_parameters, parameters)); case OP_ROTATE_RIGHT: if (arity != 1) m_manager->raise_exception("rotate right expects one argument"); return m_manager->mk_func_decl(m_rotate_right_sym, arity, domain, domain[0], func_decl_info(m_family_id, k, num_parameters, parameters)); case OP_REPEAT: if (arity != 1) m_manager->raise_exception("repeat expects one argument"); if (num_parameters != 1 || !parameters[0].is_int() || parameters[0].get_int() == 0) m_manager->raise_exception("repeat expects one nonzero integer parameter"); if (!get_bv_size(domain[0], bv_size)) m_manager->raise_exception("repeat expects an argument with bit-vector sort"); return m_manager->mk_func_decl(m_repeat_sym, arity, domain, get_bv_sort(bv_size * parameters[0].get_int()), func_decl_info(m_family_id, k, num_parameters, parameters)); default: return 0; } } func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { ast_manager& m = *m_manager; int bv_size; if (k == OP_INT2BV && get_int2bv_size(num_parameters, parameters, bv_size)) { // bv_size is filled in. } else if (k == OP_BV_NUM) { return mk_num_decl(num_parameters, parameters, num_args); } else if (k == OP_BIT0) { return m_bit0; } else if (k == OP_BIT1) { return m_bit1; } else if (k == OP_CARRY) { return m_carry; } else if (k == OP_XOR3) { return m_xor3; } else if (k == OP_MKBV) { return decl_plugin::mk_func_decl(k, num_parameters, parameters, num_args, args, range); } else if (num_args == 0 || !get_bv_size(args[0], bv_size)) { m.raise_exception("operator is applied to arguments of the wrong sort"); return 0; } func_decl * r = mk_func_decl(k, bv_size); if (r != 0) { if (num_args != r->get_arity()) { if (r->get_info()->is_associative()) { sort * fs = r->get_domain(0); for (unsigned i = 0; i < num_args; ++i) { if (m.get_sort(args[i]) != fs) { m_manager->raise_exception("declared sorts do not match supplied sorts"); return 0; } } return r; } else { m.raise_exception("declared arity mismatches supplied arity"); return 0; } } for (unsigned i = 0; i < num_args; ++i) { if (m.get_sort(args[i]) != r->get_domain(i)) { std::ostringstream buffer; buffer << "Argument " << mk_pp(args[i], m) << " at position " << i << " does not match declaration " << mk_pp(r, m); m.raise_exception(buffer.str().c_str()); return 0; } } return r; } return decl_plugin::mk_func_decl(k, num_parameters, parameters, num_args, args, range); } bool bv_decl_plugin::is_value(app* e) const { return is_app_of(e, m_family_id, OP_BV_NUM); } void bv_decl_plugin::get_offset_term(app * a, expr * & t, rational & offset) const { family_id fid = get_family_id(); if (a->get_num_args() == 2 && is_app_of(a, fid, OP_BADD) && is_app_of(a->get_arg(0), fid, OP_BV_NUM)) { unsigned sz; func_decl * decl = to_app(a->get_arg(0))->get_decl(); offset = decl->get_parameter(0).get_rational(); sz = decl->get_parameter(1).get_int(); t = a->get_arg(1); offset = mod(offset, rational::power_of_two(sz)); } else { t = a; offset = rational(0); } } bool bv_decl_plugin::are_distinct(app * a, app * b) const { #if 1 // Check for a + k1 != a + k2 when k1 != k2 rational a_offset; expr * a_term; rational b_offset; expr * b_term; get_offset_term(a, a_term, a_offset); get_offset_term(b, b_term, b_offset); TRACE("bv_are_distinct", tout << mk_ismt2_pp(a, *m_manager) << "\n" << mk_ismt2_pp(b, *m_manager) << "\n"; tout << "---->\n"; tout << "a: " << a_offset << " + " << mk_ismt2_pp(a_term, *m_manager) << "\n"; tout << "b: " << b_offset << " + " << mk_ismt2_pp(b_term, *m_manager) << "\n";); if (a_term == b_term && a_offset != b_offset) return true; #endif return decl_plugin::are_distinct(a, b); } void bv_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { if (logic == symbol::null) sort_names.push_back(builtin_name("bv", BV_SORT)); sort_names.push_back(builtin_name("BitVec", BV_SORT)); } void bv_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { op_names.push_back(builtin_name("bit1",OP_BIT1)); op_names.push_back(builtin_name("bit0",OP_BIT0)); op_names.push_back(builtin_name("bvneg",OP_BNEG)); op_names.push_back(builtin_name("bvadd",OP_BADD)); op_names.push_back(builtin_name("bvsub",OP_BSUB)); op_names.push_back(builtin_name("bvmul",OP_BMUL)); op_names.push_back(builtin_name("bvsdiv",OP_BSDIV)); op_names.push_back(builtin_name("bvudiv",OP_BUDIV)); op_names.push_back(builtin_name("bvsrem",OP_BSREM)); op_names.push_back(builtin_name("bvurem",OP_BUREM)); op_names.push_back(builtin_name("bvsmod",OP_BSMOD)); op_names.push_back(builtin_name("bvule",OP_ULEQ)); op_names.push_back(builtin_name("bvsle",OP_SLEQ)); op_names.push_back(builtin_name("bvuge",OP_UGEQ)); op_names.push_back(builtin_name("bvsge",OP_SGEQ)); op_names.push_back(builtin_name("bvult",OP_ULT)); op_names.push_back(builtin_name("bvslt",OP_SLT)); op_names.push_back(builtin_name("bvugt",OP_UGT)); op_names.push_back(builtin_name("bvsgt",OP_SGT)); op_names.push_back(builtin_name("bvand",OP_BAND)); op_names.push_back(builtin_name("bvor",OP_BOR)); op_names.push_back(builtin_name("bvnot",OP_BNOT)); op_names.push_back(builtin_name("bvxor",OP_BXOR)); op_names.push_back(builtin_name("bvnand",OP_BNAND)); op_names.push_back(builtin_name("bvnor",OP_BNOR)); op_names.push_back(builtin_name("bvxnor",OP_BXNOR)); op_names.push_back(builtin_name("concat",OP_CONCAT)); op_names.push_back(builtin_name("sign_extend",OP_SIGN_EXT)); op_names.push_back(builtin_name("zero_extend",OP_ZERO_EXT)); op_names.push_back(builtin_name("extract",OP_EXTRACT)); op_names.push_back(builtin_name("repeat",OP_REPEAT)); op_names.push_back(builtin_name("bvredor",OP_BREDOR)); op_names.push_back(builtin_name("bvredand",OP_BREDAND)); op_names.push_back(builtin_name("bvcomp",OP_BCOMP)); op_names.push_back(builtin_name("bvshl",OP_BSHL)); op_names.push_back(builtin_name("bvlshr",OP_BLSHR)); op_names.push_back(builtin_name("bvashr",OP_BASHR)); op_names.push_back(builtin_name("rotate_left",OP_ROTATE_LEFT)); op_names.push_back(builtin_name("rotate_right",OP_ROTATE_RIGHT)); if (logic == symbol::null) { op_names.push_back(builtin_name("bvumul_noovfl",OP_BUMUL_NO_OVFL)); op_names.push_back(builtin_name("bvsmul_noovfl",OP_BSMUL_NO_OVFL)); op_names.push_back(builtin_name("bvsmul_noudfl",OP_BSMUL_NO_UDFL)); op_names.push_back(builtin_name("bvsdiv0", OP_BSDIV0)); op_names.push_back(builtin_name("bvudiv0", OP_BUDIV0)); op_names.push_back(builtin_name("bvsrem0", OP_BSREM0)); op_names.push_back(builtin_name("bvurem0", OP_BUREM0)); op_names.push_back(builtin_name("bvsmod0", OP_BSMOD0)); op_names.push_back(builtin_name("bvsdiv_i", OP_BSDIV_I)); op_names.push_back(builtin_name("bvudiv_i", OP_BUDIV_I)); op_names.push_back(builtin_name("bvsrem_i", OP_BSREM_I)); op_names.push_back(builtin_name("bvurem_i", OP_BUREM_I)); op_names.push_back(builtin_name("bvumod_i", OP_BSMOD_I)); op_names.push_back(builtin_name("ext_rotate_left",OP_EXT_ROTATE_LEFT)); op_names.push_back(builtin_name("ext_rotate_right",OP_EXT_ROTATE_RIGHT)); op_names.push_back(builtin_name("int2bv",OP_INT2BV)); op_names.push_back(builtin_name("bv2int",OP_BV2INT)); op_names.push_back(builtin_name("mkbv",OP_MKBV)); } } expr * bv_decl_plugin::get_some_value(sort * s) { SASSERT(s->is_sort_of(m_family_id, BV_SORT)); unsigned bv_size = s->get_parameter(0).get_int(); parameter p[2] = { parameter(rational(0)), parameter(static_cast(bv_size)) }; return m_manager->mk_app(m_family_id, OP_BV_NUM, 2, p, 0, 0); } rational bv_recognizers::norm(rational const & val, unsigned bv_size, bool is_signed) const { rational r = mod(val, rational::power_of_two(bv_size)); SASSERT(!r.is_neg()); if (is_signed) { if (r >= rational::power_of_two(bv_size - 1)) { r -= rational::power_of_two(bv_size); } if (r < -rational::power_of_two(bv_size - 1)) { r += rational::power_of_two(bv_size); } } return r; } bool bv_recognizers::has_sign_bit(rational const & n, unsigned bv_size) const { SASSERT(bv_size > 0); rational m = norm(n, bv_size, false); rational p = rational::power_of_two(bv_size - 1); return m >= p; } bool bv_recognizers::is_bv_sort(sort const * s) const { return (s->get_family_id() == get_fid() && s->get_decl_kind() == BV_SORT && s->get_num_parameters() == 1); } bool bv_recognizers::is_numeral(expr const * n, rational & val, unsigned & bv_size) const { if (!is_app_of(n, get_fid(), OP_BV_NUM)) { return false; } func_decl * decl = to_app(n)->get_decl(); val = decl->get_parameter(0).get_rational(); bv_size = decl->get_parameter(1).get_int(); return true; } bool bv_recognizers::is_allone(expr const * e) const { rational r; unsigned bv_size; if (!is_numeral(e, r, bv_size)) { return false; } bool result = (r == rational::power_of_two(bv_size) - rational(1)); TRACE("is_allone", tout << r << " " << result << "\n";); return result; } bool bv_recognizers::is_zero(expr const * n) const { if (!is_app_of(n, get_fid(), OP_BV_NUM)) { return false; } func_decl * decl = to_app(n)->get_decl(); return decl->get_parameter(0).get_rational().is_zero(); } bool bv_recognizers::is_extract(expr const* e, unsigned& low, unsigned& high, expr*& b) const { if (!is_extract(e)) return false; low = get_extract_low(e); high = get_extract_high(e); b = to_app(e)->get_arg(0); return true; } bool bv_recognizers::is_bv2int(expr const* e, expr*& r) const { if (!is_bv2int(e)) return false; r = to_app(e)->get_arg(0); return true; } bool bv_recognizers::mult_inverse(rational const & n, unsigned bv_size, rational & result) { if (n.is_one()) { result = n; return true; } if (!mod(n, rational(2)).is_one()) { return false; } rational g; rational x; rational y; g = gcd(n, rational::power_of_two(bv_size), x, y); if (x.is_neg()) { x = mod(x, rational::power_of_two(bv_size)); } SASSERT(x.is_pos()); SASSERT(mod(x * n, rational::power_of_two(bv_size)).is_one()); result = x; return true; } bv_util::bv_util(ast_manager & m): bv_recognizers(m.mk_family_id(symbol("bv"))), m_manager(m) { SASSERT(m.has_plugin(symbol("bv"))); m_plugin = static_cast(m.get_plugin(m.mk_family_id("bv"))); } app * bv_util::mk_numeral(rational const & val, sort* s) { if (!is_bv_sort(s)) { return 0; } unsigned bv_size = get_bv_size(s); return mk_numeral(val, bv_size); } app * bv_util::mk_numeral(rational const & val, unsigned bv_size) { parameter p1(val); parameter p[2] = { p1, parameter(static_cast(bv_size)) }; return m_manager.mk_app(get_fid(), OP_BV_NUM, 2, p, 0, 0); } sort * bv_util::mk_sort(unsigned bv_size) { parameter p[1] = { parameter(bv_size) }; return m_manager.mk_sort(get_fid(), BV_SORT, 1, p); } app * bv_util::mk_bv2int(expr* e) { sort* s = m_manager.mk_sort(m_manager.mk_family_id("arith"), INT_SORT); parameter p(s); return m_manager.mk_app(get_fid(), OP_BV2INT, 1, &p, 1, &e); } z3-4.4.0/src/ast/occurs.h0000644000175000017500000000067612540347414014703 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: occurs.h Abstract: Author: Leonardo de Moura (leonardo) 2007-06-07. Revision History: --*/ #ifndef _OCCURS_H_ #define _OCCURS_H_ class expr; class func_decl; /** \brief Return true if n1 occurs in n2 */ bool occurs(expr * n1, expr * n2); /** \brief Return true if d is used in n */ bool occurs(func_decl * d, expr * n); #endif /* _OCCURS_H_ */ z3-4.4.0/src/ast/ast_lt.h0000644000175000017500000000126312540347414014664 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ast_lt.h Abstract: Total order on ASTs that does not depend on the internal ids. Author: Leonardo de Moura (leonardo) 2011-04-08 Revision History: --*/ #ifndef _AST_LT_H_ #define _AST_LT_H_ class ast; bool lt(ast * n1, ast * n2); bool is_sorted(unsigned num, expr * const * ns); struct ast_to_lt { bool operator()(ast * n1, ast * n2) const { return lt(n1, n2); } }; bool lex_lt(unsigned num, ast * const * n1, ast * const * n2); inline bool lex_lt(unsigned num, expr * const * n1, expr * const * n2) { return lex_lt(num, reinterpret_cast(n1), reinterpret_cast(n2)); } #endif z3-4.4.0/src/muz/0000755000175000017500000000000012540347414013247 5ustar michaelmichaelz3-4.4.0/src/muz/clp/0000755000175000017500000000000012540347414014025 5ustar michaelmichaelz3-4.4.0/src/muz/clp/clp_context.cpp0000644000175000017500000001645612540347414017067 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: clp_context.cpp Abstract: Bounded CLP (symbolic simulation using Z3) context. Author: Nikolaj Bjorner (nbjorner) 2013-04-26 Revision History: --*/ #include "clp_context.h" #include "dl_context.h" #include "unifier.h" #include "var_subst.h" #include "substitution.h" #include "smt_kernel.h" #include "dl_transforms.h" namespace datalog { class clp::imp { struct stats { stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } unsigned m_num_unfold; unsigned m_num_no_unfold; unsigned m_num_subsumed; }; context& m_ctx; ast_manager& m; rule_manager& rm; smt_params m_fparams; smt::kernel m_solver; var_subst m_var_subst; expr_ref_vector m_ground; app_ref_vector m_goals; volatile bool m_cancel; stats m_stats; public: imp(context& ctx): m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_solver(m, m_fparams), // TBD: can be replaced by efficient BV solver. m_var_subst(m, false), m_ground(m), m_goals(m), m_cancel(false) { // m_fparams.m_relevancy_lvl = 0; m_fparams.m_mbqi = false; m_fparams.m_timeout = 1000; } ~imp() {} lbool query(expr* query) { m_ctx.ensure_opened(); m_solver.reset(); m_goals.reset(); rm.mk_query(query, m_ctx.get_rules()); apply_default_transformation(m_ctx); if (m_ctx.get_rules().get_output_predicates().empty()) { return l_false; } func_decl* head_decl = m_ctx.get_rules().get_output_predicate(); rule_set& rules = m_ctx.get_rules(); rule_vector const& rv = rules.get_predicate_rules(head_decl); if (rv.empty()) { return l_false; } expr_ref head(rv[0]->get_head(), m); ground(head); m_goals.push_back(to_app(head)); return search(20, 0); } void cancel() { m_cancel = true; m_solver.cancel(); } void cleanup() { m_cancel = false; m_goals.reset(); m_solver.reset_cancel(); } void reset_statistics() { m_stats.reset(); } void collect_statistics(statistics& st) const { //st.update("tab.num_unfold", m_stats.m_num_unfold); //st.update("tab.num_unfold_fail", m_stats.m_num_no_unfold); //st.update("tab.num_subsumed", m_stats.m_num_subsumed); } void display_certificate(std::ostream& out) const { expr_ref ans = get_answer(); out << mk_pp(ans, m) << "\n"; } expr_ref get_answer() const { return expr_ref(m.mk_true(), m); } private: void reset_ground() { m_ground.reset(); } void ground(expr_ref& e) { expr_free_vars fv; fv(e); if (m_ground.size() < fv.size()) { m_ground.resize(fv.size()); } for (unsigned i = 0; i < fv.size(); ++i) { if (fv[i] && !m_ground[i].get()) { m_ground[i] = m.mk_fresh_const("c", fv[i]); } } m_var_subst(e, m_ground.size(), m_ground.c_ptr(), e); } static bool rule_sort_fn(const rule *r1, const rule *r2) { return r1->get_uninterpreted_tail_size() < r2->get_uninterpreted_tail_size(); } lbool search(unsigned depth, unsigned index) { if (index == m_goals.size()) { return l_true; } if (depth == 0) { return l_undef; } IF_VERBOSE(1, verbose_stream() << "search " << depth << " " << index << "\n";); unsigned num_goals = m_goals.size(); app* head = m_goals[index].get(); rule_vector rules(m_ctx.get_rules().get_predicate_rules(head->get_decl())); std::stable_sort(rules.begin(), rules.end(), rule_sort_fn); lbool status = l_false; for (unsigned i = 0; i < rules.size(); ++i) { rule* r = rules[i]; m_solver.push(); reset_ground(); expr_ref tmp(m); tmp = r->get_head(); IF_VERBOSE(2, verbose_stream() << index << " " << mk_pp(tmp, m) << "\n";); ground(tmp); for (unsigned j = 0; j < head->get_num_args(); ++j) { expr_ref eq(m); eq = m.mk_eq(head->get_arg(j), to_app(tmp)->get_arg(j)); m_solver.assert_expr(eq); } for (unsigned j = r->get_uninterpreted_tail_size(); j < r->get_tail_size(); ++j) { tmp = r->get_tail(j); ground(tmp); m_solver.assert_expr(tmp); } lbool is_sat = m_solver.check(); switch (is_sat) { case l_false: break; case l_true: if (depth == 1 && (index+1 > m_goals.size() || r->get_uninterpreted_tail_size() > 0)) { status = l_undef; break; } for (unsigned j = 0; j < r->get_uninterpreted_tail_size(); ++j) { tmp = r->get_tail(j); ground(tmp); m_goals.push_back(to_app(tmp)); } switch(search(depth-1, index+1)) { case l_undef: status = l_undef; // fallthrough case l_false: m_goals.resize(num_goals); break; case l_true: return l_true; } break; case l_undef: status = l_undef; throw default_exception("undef"); } m_solver.pop(1); } return status; } proof_ref get_proof() const { return proof_ref(0, m); } }; clp::clp(context& ctx): engine_base(ctx.get_manager(), "clp"), m_imp(alloc(imp, ctx)) { } clp::~clp() { dealloc(m_imp); } lbool clp::query(expr* query) { return m_imp->query(query); } void clp::cancel() { m_imp->cancel(); } void clp::cleanup() { m_imp->cleanup(); } void clp::reset_statistics() { m_imp->reset_statistics(); } void clp::collect_statistics(statistics& st) const { m_imp->collect_statistics(st); } void clp::display_certificate(std::ostream& out) const { m_imp->display_certificate(out); } expr_ref clp::get_answer() { return m_imp->get_answer(); } }; z3-4.4.0/src/muz/clp/clp_context.h0000644000175000017500000000156312540347414016525 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: clp_context.h Abstract: Bounded CLP (symbolic simulation using Z3) context. Author: Nikolaj Bjorner (nbjorner) 2013-04-26 Revision History: --*/ #ifndef _CLP_CONTEXT_H_ #define _CLP_CONTEXT_H_ #include "ast.h" #include "lbool.h" #include "statistics.h" #include "dl_engine_base.h" namespace datalog { class context; class clp : public datalog::engine_base { class imp; imp* m_imp; public: clp(context& ctx); ~clp(); virtual lbool query(expr* query); virtual void cancel(); virtual void cleanup(); virtual void reset_statistics(); virtual void collect_statistics(statistics& st) const; virtual void display_certificate(std::ostream& out) const; virtual expr_ref get_answer(); }; }; #endif z3-4.4.0/src/muz/duality/0000755000175000017500000000000012540347414014722 5ustar michaelmichaelz3-4.4.0/src/muz/duality/duality_dl_interface.cpp0000755000175000017500000005123712540347414021613 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: duality_dl_interface.cpp Abstract: SMT2 interface for Duality Author: Krystof Hoder (t-khoder) 2011-9-22. Modified by Ken McMIllan (kenmcmil) 2013-4-18. Revision History: --*/ #include "dl_context.h" #include "dl_mk_coi_filter.h" #include "dl_mk_interp_tail_simplifier.h" #include "dl_mk_subsumption_checker.h" #include "dl_mk_rule_inliner.h" #include "dl_rule.h" #include "dl_rule_transformer.h" #include "smt2parser.h" #include "duality_dl_interface.h" #include "dl_rule_set.h" #include "dl_mk_slice.h" #include "dl_mk_unfold.h" #include "dl_mk_coalesce.h" #include "expr_abstract.h" #include "model_smt2_pp.h" #include "model_v2_pp.h" #include "fixedpoint_params.hpp" #include "used_vars.h" #include "func_decl_dependencies.h" #include "dl_transforms.h" // template class symbol_table; #ifdef WIN32 #pragma warning(disable:4996) #pragma warning(disable:4800) #pragma warning(disable:4267) #pragma warning(disable:4101) #endif #include "duality.h" #include "duality_profiling.h" // using namespace Duality; namespace Duality { enum DualityStatus {StatusModel, StatusRefutation, StatusUnknown, StatusNull}; class duality_data { public: context ctx; RPFP::LogicSolver *ls; RPFP *rpfp; DualityStatus status; std::vector clauses; std::vector > clause_labels; hash_map map; // edges to clauses Solver *old_rs; Solver::Counterexample cex; duality_data(ast_manager &_m) : ctx(_m,config(params_ref())) { ls = 0; rpfp = 0; status = StatusNull; old_rs = 0; } ~duality_data(){ if(old_rs) dealloc(old_rs); if(rpfp) dealloc(rpfp); if(ls) dealloc(ls); } }; dl_interface::dl_interface(datalog::context& dl_ctx) : engine_base(dl_ctx.get_manager(), "duality"), m_ctx(dl_ctx) { _d = 0; // dl_ctx.get_manager().toggle_proof_mode(PGM_FINE); } dl_interface::~dl_interface() { if(_d) dealloc(_d); } // // Check if the new rules are weaker so that we can // re-use existing context. // #if 0 void dl_interface::check_reset() { // TODO datalog::rule_ref_vector const& new_rules = m_ctx.get_rules().get_rules(); datalog::rule_ref_vector const& old_rules = m_old_rules.get_rules(); bool is_subsumed = !old_rules.empty(); for (unsigned i = 0; is_subsumed && i < new_rules.size(); ++i) { is_subsumed = false; for (unsigned j = 0; !is_subsumed && j < old_rules.size(); ++j) { if (m_ctx.check_subsumes(*old_rules[j], *new_rules[i])) { is_subsumed = true; } } if (!is_subsumed) { TRACE("pdr", new_rules[i]->display(m_ctx, tout << "Fresh rule ");); m_context->reset(); } } m_old_rules.reset(); m_old_rules.add_rules(new_rules.size(), new_rules.c_ptr()); } #endif lbool dl_interface::query(::expr * query) { // we restore the initial state in the datalog context m_ctx.ensure_opened(); // if there is old data, get the cex and dispose (later) duality_data *old_data = _d; Solver *old_rs = 0; if(old_data){ old_rs = old_data->old_rs; old_rs->GetCounterexample().swap(old_data->cex); } scoped_proof generate_proofs_please(m_ctx.get_manager()); // make a new problem and solver _d = alloc(duality_data,m_ctx.get_manager()); _d->ctx.set("mbqi",m_ctx.get_params().duality_mbqi()); _d->ls = alloc(RPFP::iZ3LogicSolver,_d->ctx); _d->rpfp = alloc(RPFP,_d->ls); expr_ref_vector rules(m_ctx.get_manager()); svector< ::symbol> names; vector bounds; // m_ctx.get_rules_as_formulas(rules, names); // If using SAS 2013 abstractiion, we need to perform some transforms expr_ref query_ref(m_ctx.get_manager()); if(m_ctx.quantify_arrays()){ datalog::rule_manager& rm = m_ctx.get_rule_manager(); rm.mk_query(query, m_ctx.get_rules()); apply_default_transformation(m_ctx); datalog::rule_set &rs = m_ctx.get_rules(); if(m_ctx.get_rules().get_output_predicates().empty()) query_ref = m_ctx.get_manager().mk_false(); else { func_decl_ref query_pred(m_ctx.get_manager()); query_pred = m_ctx.get_rules().get_output_predicate(); ptr_vector sorts; unsigned nargs = query_pred.get()->get_arity(); expr_ref_vector vars(m_ctx.get_manager()); for(unsigned i = 0; i < nargs; i++){ ::sort *s = query_pred.get()->get_domain(i); vars.push_back(m_ctx.get_manager().mk_var(nargs-1-i,s)); } query_ref = m_ctx.get_manager().mk_app(query_pred.get(),nargs,vars.c_ptr()); query = query_ref.get(); } unsigned nrules = rs.get_num_rules(); for(unsigned i = 0; i < nrules; i++){ expr_ref f(m_ctx.get_manager()); rm.to_formula(*rs.get_rule(i), f); rules.push_back(f); } } else m_ctx.get_raw_rule_formulas(rules, names, bounds); // get all the rules as clauses std::vector &clauses = _d->clauses; clauses.clear(); for (unsigned i = 0; i < rules.size(); ++i) { expr e(_d->ctx,rules[i].get()); clauses.push_back(e); } std::vector b_sorts; std::vector b_names; used_vars uv; uv.process(query); unsigned nuv = uv.get_max_found_var_idx_plus_1(); for(int i = nuv-1; i >= 0; i--){ // var indices are backward ::sort * s = uv.get(i); if(!s) s = _d->ctx.m().mk_bool_sort(); // missing var, whatever b_sorts.push_back(sort(_d->ctx,s)); b_names.push_back(symbol(_d->ctx,::symbol(i))); // names? } #if 0 // turn the query into a clause expr q(_d->ctx,m_ctx.bind_variables(query,false)); std::vector b_sorts; std::vector b_names; if (q.is_quantifier() && !q.is_quantifier_forall()) { int bound = q.get_quantifier_num_bound(); for(int j = 0; j < bound; j++){ b_sorts.push_back(q.get_quantifier_bound_sort(j)); b_names.push_back(q.get_quantifier_bound_name(j)); } q = q.arg(0); } #else expr q(_d->ctx,query); #endif expr qc = implies(q,_d->ctx.bool_val(false)); qc = _d->ctx.make_quant(Forall,b_sorts,b_names,qc); clauses.push_back(qc); bounds.push_back(UINT_MAX); // get the background axioms unsigned num_asserts = m_ctx.get_num_assertions(); for (unsigned i = 0; i < num_asserts; ++i) { expr e(_d->ctx,m_ctx.get_assertion(i)); _d->rpfp->AssertAxiom(e); } // make sure each predicate is the head of at least one clause func_decl_set heads; for(unsigned i = 0; i < clauses.size(); i++){ expr cl = clauses[i]; while(true){ if(cl.is_app()){ decl_kind k = cl.decl().get_decl_kind(); if(k == Implies) cl = cl.arg(1); else { heads.insert(cl.decl()); break; } } else if(cl.is_quantifier()) cl = cl.body(); else break; } } ast_ref_vector const &pinned = m_ctx.get_pinned(); for(unsigned i = 0; i < pinned.size(); i++){ ::ast *fa = pinned[i]; if(is_func_decl(fa)){ ::func_decl *fd = to_func_decl(fa); if (m_ctx.is_predicate(fd)) { func_decl f(_d->ctx, fd); if (!heads.contains(fd)) { int arity = f.arity(); std::vector args; for (int j = 0; j < arity; j++) args.push_back(_d->ctx.fresh_func_decl("X", f.domain(j))()); expr c = implies(_d->ctx.bool_val(false), f(args)); c = _d->ctx.make_quant(Forall, args, c); clauses.push_back(c); bounds.push_back(UINT_MAX); } } } } unsigned rb = m_ctx.get_params().duality_recursion_bound(); std::vector std_bounds; for(unsigned i = 0; i < bounds.size(); i++){ unsigned b = bounds[i]; if (b == UINT_MAX) b = rb; std_bounds.push_back(b); } // creates 1-1 map between clauses and rpfp edges _d->rpfp->FromClauses(clauses,&std_bounds); // populate the edge-to-clause map for(unsigned i = 0; i < _d->rpfp->edges.size(); ++i) _d->map[_d->rpfp->edges[i]] = i; // create a solver object Solver *rs = Solver::Create("duality", _d->rpfp); if(old_rs) rs->LearnFrom(old_rs); // new solver gets hints from old solver // set its options IF_VERBOSE(1, rs->SetOption("report","1");); rs->SetOption("full_expand",m_ctx.get_params().duality_full_expand() ? "1" : "0"); rs->SetOption("no_conj",m_ctx.get_params().duality_no_conj() ? "1" : "0"); rs->SetOption("feasible_edges",m_ctx.get_params().duality_feasible_edges() ? "1" : "0"); rs->SetOption("use_underapprox",m_ctx.get_params().duality_use_underapprox() ? "1" : "0"); rs->SetOption("stratified_inlining",m_ctx.get_params().duality_stratified_inlining() ? "1" : "0"); rs->SetOption("batch_expand",m_ctx.get_params().duality_batch_expand() ? "1" : "0"); rs->SetOption("conjecture_file",m_ctx.get_params().duality_conjecture_file()); rs->SetOption("enable_restarts",m_ctx.get_params().duality_enable_restarts() ? "1" : "0"); #if 0 if(rb != UINT_MAX){ std::ostringstream os; os << rb; rs->SetOption("recursion_bound", os.str()); } #endif // Solve! bool ans; try { ans = rs->Solve(); } catch (Duality::solver::cancel_exception &exn){ throw default_exception("duality canceled"); } catch (Duality::Solver::Incompleteness &exn){ throw default_exception("incompleteness"); } // profile! if(m_ctx.get_params().duality_profile()) print_profile(std::cout); // save the result and counterexample if there is one _d->status = ans ? StatusModel : StatusRefutation; _d->cex.swap(rs->GetCounterexample()); // take ownership of cex _d->old_rs = rs; // save this for later hints if(old_data){ dealloc(old_data); // this deallocates the old solver if there is one } // dealloc(rs); this is now owned by data // true means the RPFP problem is SAT, so the query is UNSAT // but we return undef if the UNSAT result is bounded if(ans){ if(rs->IsResultRecursionBounded()){ #if 0 m_ctx.set_status(datalog::BOUNDED); return l_undef; #else return l_false; #endif } return l_false; } return l_true; } expr_ref dl_interface::get_cover_delta(int level, ::func_decl* pred_orig) { SASSERT(false); return expr_ref(m_ctx.get_manager()); } void dl_interface::add_cover(int level, ::func_decl* pred, ::expr* property) { SASSERT(false); } unsigned dl_interface::get_num_levels(::func_decl* pred) { SASSERT(false); return 0; } void dl_interface::collect_statistics(::statistics& st) const { } void dl_interface::reset_statistics() { } static hash_set *local_func_decls; static void print_proof(dl_interface *d, std::ostream& out, RPFP *tree, RPFP::Node *root) { context &ctx = d->dd()->ctx; RPFP::Node &node = *root; RPFP::Edge &edge = *node.Outgoing; // first, prove the children (that are actually used) for(unsigned i = 0; i < edge.Children.size(); i++){ if(!tree->Empty(edge.Children[i])){ print_proof(d,out,tree,edge.Children[i]); } } // print the label and the proved fact out << "(step s!" << node.number; out << " (" << node.Name.name(); for(unsigned i = 0; i < edge.F.IndParams.size(); i++) out << " " << tree->Eval(&edge,edge.F.IndParams[i]); out << ")\n"; // print the rule number out << " rule!" << node.Outgoing->map->number; // print the substitution out << " (subst\n"; RPFP::Edge *orig_edge = edge.map; int orig_clause = d->dd()->map[orig_edge]; expr &t = d->dd()->clauses[orig_clause]; if (t.is_quantifier() && t.is_quantifier_forall()) { int bound = t.get_quantifier_num_bound(); std::vector sorts; std::vector names; hash_map subst; for(int j = 0; j < bound; j++){ sort the_sort = t.get_quantifier_bound_sort(j); symbol name = t.get_quantifier_bound_name(j); expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); out << " (= " << skolem << " " << tree->Eval(&edge,skolem) << ")\n"; expr local_skolem = tree->Localize(&edge,skolem); (*local_func_decls).insert(local_skolem.decl()); } } out << " )\n"; out << " (labels"; std::vector labels; tree->GetLabels(&edge,labels); for(unsigned j = 0; j < labels.size(); j++){ out << " " << labels[j]; } out << " )\n"; // reference the proofs of all the children, in syntactic order // "true" means the child is not needed out << " (ref "; for(unsigned i = 0; i < edge.Children.size(); i++){ if(!tree->Empty(edge.Children[i])) out << " s!" << edge.Children[i]->number; else out << " true"; } out << " )"; out << ")\n"; } void dl_interface::display_certificate(std::ostream& out) const { ((dl_interface *)this)->display_certificate_non_const(out); } void dl_interface::display_certificate_non_const(std::ostream& out) { if(_d->status == StatusModel){ ast_manager &m = m_ctx.get_manager(); model_ref md = get_model(); out << "(fixedpoint \n"; model_smt2_pp(out, m, *md.get(), 0); out << ")\n"; } else if(_d->status == StatusRefutation){ out << "(derivation\n"; // negation of the query is the last clause -- prove it hash_set locals; local_func_decls = &locals; print_proof(this,out,_d->cex.get_tree(),_d->cex.get_root()); out << ")\n"; out << "(model \n\""; ::model mod(m_ctx.get_manager()); model orig_model = _d->cex.get_tree()->dualModel; for(unsigned i = 0; i < orig_model.num_consts(); i++){ func_decl cnst = orig_model.get_const_decl(i); if (locals.find(cnst) == locals.end()) { expr thing = orig_model.get_const_interp(cnst); mod.register_decl(to_func_decl(cnst.raw()), to_expr(thing.raw())); } } for(unsigned i = 0; i < orig_model.num_funcs(); i++){ func_decl cnst = orig_model.get_func_decl(i); if (locals.find(cnst) == locals.end()) { func_interp thing = orig_model.get_func_interp(cnst); ::func_interp *thing_raw = thing; mod.register_decl(to_func_decl(cnst.raw()), thing_raw->copy()); } } model_v2_pp(out,mod); out << "\")\n"; } } expr_ref dl_interface::get_answer() { SASSERT(false); return expr_ref(m_ctx.get_manager()); } void dl_interface::cancel() { #if 0 if(_d && _d->ls) _d->ls->cancel(); #else // HACK: duality can't cancel at all times, we just exit here std::cout << "(error \"duality canceled\")\nunknown\n"; abort(); #endif } void dl_interface::cleanup() { } void dl_interface::updt_params() { } model_ref dl_interface::get_model() { ast_manager &m = m_ctx.get_manager(); model_ref md(alloc(::model, m)); std::vector &nodes = _d->rpfp->nodes; expr_ref_vector conjs(m); for (unsigned i = 0; i < nodes.size(); ++i) { RPFP::Node *node = nodes[i]; func_decl &pred = node->Name; expr_ref prop(m); prop = to_expr(node->Annotation.Formula); std::vector ¶ms = node->Annotation.IndParams; expr_ref q(m); expr_ref_vector sig_vars(m); for (unsigned j = 0; j < params.size(); ++j) sig_vars.push_back(params[params.size()-j-1]); // TODO: why backwards? expr_abstract(m, 0, sig_vars.size(), sig_vars.c_ptr(), prop, q); if (params.empty()) { md->register_decl(pred, q); } else { ::func_interp* fi = alloc(::func_interp, m, params.size()); fi->set_else(q); md->register_decl(pred, fi); } } return md; } static proof_ref extract_proof(dl_interface *d, RPFP *tree, RPFP::Node *root) { context &ctx = d->dd()->ctx; ast_manager &mgr = ctx.m(); RPFP::Node &node = *root; RPFP::Edge &edge = *node.Outgoing; RPFP::Edge *orig_edge = edge.map; // first, prove the children (that are actually used) proof_ref_vector prems(mgr); ::vector substs; int orig_clause = d->dd()->map[orig_edge]; expr &t = d->dd()->clauses[orig_clause]; prems.push_back(mgr.mk_asserted(ctx.uncook(t))); substs.push_back(expr_ref_vector(mgr)); if (t.is_quantifier() && t.is_quantifier_forall()) { int bound = t.get_quantifier_num_bound(); std::vector sorts; std::vector names; hash_map subst; for(int j = 0; j < bound; j++){ sort the_sort = t.get_quantifier_bound_sort(j); symbol name = t.get_quantifier_bound_name(j); expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); expr val = tree->Eval(&edge,skolem); expr_ref thing(ctx.uncook(val),mgr); substs[0].push_back(thing); expr local_skolem = tree->Localize(&edge,skolem); (*local_func_decls).insert(local_skolem.decl()); } } svector > pos; for(unsigned i = 0; i < edge.Children.size(); i++){ if(!tree->Empty(edge.Children[i])){ pos.push_back(std::pair(i+1,0)); proof_ref prem = extract_proof(d,tree,edge.Children[i]); prems.push_back(prem); substs.push_back(expr_ref_vector(mgr)); } } func_decl f = node.Name; std::vector args; for(unsigned i = 0; i < edge.F.IndParams.size(); i++) args.push_back(tree->Eval(&edge,edge.F.IndParams[i])); expr conc = f(args); ::vector< ::proof *> pprems; for(unsigned i = 0; i < prems.size(); i++) pprems.push_back(prems[i].get()); proof_ref res(mgr.mk_hyper_resolve(pprems.size(),&pprems[0], ctx.uncook(conc), pos, substs),mgr); return res; } proof_ref dl_interface::get_proof() { if(_d->status == StatusRefutation){ hash_set locals; local_func_decls = &locals; return extract_proof(this,_d->cex.get_tree(),_d->cex.get_root()); } else return proof_ref(m_ctx.get_manager()); } } z3-4.4.0/src/muz/duality/duality_dl_interface.h0000644000175000017500000000263212540347414021250 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: duality_dl_interface.h Abstract: SMT2 interface for Duality Author: Krystof Hoder (t-khoder) 2011-9-22. Modified by Ken McMIllan (kenmcmil) 2013-4-18. Revision History: --*/ #ifndef _DUALITY_DL_INTERFACE_H_ #define _DUALITY_DL_INTERFACE_H_ #include "lbool.h" #include "dl_rule.h" #include "dl_rule_set.h" #include "dl_engine_base.h" #include "statistics.h" namespace datalog { class context; } namespace Duality { class duality_data; class dl_interface : public datalog::engine_base { duality_data *_d; datalog::context &m_ctx; public: dl_interface(datalog::context& ctx); ~dl_interface(); lbool query(expr* query); void cancel(); void cleanup(); void display_certificate(std::ostream& out) const; void collect_statistics(statistics& st) const; void reset_statistics(); expr_ref get_answer(); unsigned get_num_levels(func_decl* pred); expr_ref get_cover_delta(int level, func_decl* pred); void add_cover(int level, func_decl* pred, expr* property); void updt_params(); model_ref get_model(); proof_ref get_proof(); duality_data *dd(){return _d;} private: void display_certificate_non_const(std::ostream& out); }; } #endif z3-4.4.0/src/muz/README0000644000175000017500000000073012540347414014127 0ustar michaelmichaelmuZ: routines related to solving satisfiability of Horn clauses and solving Datalog programs. - base - contains base routines and the main context for maintaining fixedpoint solvers - transforms - common rule transformations - rel - relational algebra based Datalog engine - pdr - PDR based Horn clause solver - clp - Dart/Symbolic execution-based solver - tab - Tabulation based solver - bmc - Bounded model checking based solver - fp - main exported routinesz3-4.4.0/src/muz/fp/0000755000175000017500000000000012540347414013654 5ustar michaelmichaelz3-4.4.0/src/muz/fp/dl_register_engine.cpp0000644000175000017500000000241712540347414020214 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_register_engine.cpp Abstract: Class for creating Datalog engines. Author: Nikolaj Bjorner (nbjorner) 2013-08-28 Revision History: --*/ #include "dl_register_engine.h" #include "dl_bmc_engine.h" #include "clp_context.h" #include "tab_context.h" #include "rel_context.h" #include "pdr_dl_interface.h" #include "ddnf.h" #include "duality_dl_interface.h" namespace datalog { register_engine::register_engine(): m_ctx(0) {} engine_base* register_engine::mk_engine(DL_ENGINE engine_type) { switch(engine_type) { case PDR_ENGINE: case QPDR_ENGINE: return alloc(pdr::dl_interface, *m_ctx); case DATALOG_ENGINE: return alloc(rel_context, *m_ctx); case BMC_ENGINE: case QBMC_ENGINE: return alloc(bmc, *m_ctx); case TAB_ENGINE: return alloc(tab, *m_ctx); case CLP_ENGINE: return alloc(clp, *m_ctx); case DUALITY_ENGINE: return alloc(Duality::dl_interface, *m_ctx); case DDNF_ENGINE: return alloc(ddnf, *m_ctx); case LAST_ENGINE: UNREACHABLE(); return 0; } UNREACHABLE(); return 0; } } z3-4.4.0/src/muz/fp/datalog_parser.cpp0000644000175000017500000013370612540347414017361 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include"datalog_parser.h" #include"string_buffer.h" #include"str_hashtable.h" #include"ast_pp.h" #include"arith_decl_plugin.h" #include"region.h" #include"warning.h" #include #include #include using namespace datalog; enum dtoken { TK_LP, TK_RP, TK_STRING, TK_ID, TK_NUM, TK_PERIOD, TK_INCLUDE, TK_COMMA, TK_COLON, TK_WILD, TK_LEFT_ARROW, TK_EOS, TK_NEWLINE, TK_ERROR, TK_NEQ, TK_LT, TK_GT, TK_EQ, TK_NEG }; static char const* dtoken_strings[] = { "(", ")", "", "", "", ".", ".include", ",", ":", "_", ":-", "", "\\n", "", "!=", "<", ">", "=", "!" }; class line_reader { static const char s_delimiter = '\n'; static const unsigned s_expansion_step = 1024; FILE * m_file; svector m_data; bool m_eof; bool m_eof_behind_buffer; unsigned m_next_index; bool m_ok; //actually by one larger than the actual size of m_data, //to fit in the terminating delimiter unsigned m_data_size; void resize_data(unsigned sz) { m_data_size = sz; m_data.resize(m_data_size+1); m_data[m_data_size] = s_delimiter; } #if 0 void refill_buffer(unsigned start) { unsigned should_read = m_data_size-start; m_stm.read(m_data.begin()+start, should_read); unsigned actually_read = static_cast(m_stm.gcount()); SASSERT(should_read==actually_read || m_stm.eof()); if(m_stm.eof()) { m_eof_behind_buffer = true; resize_data(start+actually_read); } } #else void refill_buffer(unsigned start) { unsigned should_read = m_data_size-start; size_t actually_read = fread(m_data.begin()+start, 1, should_read, m_file); if(actually_read==should_read) { return; } SASSERT(actually_read < should_read); SASSERT(feof(m_file)); m_eof_behind_buffer = true; resize_data(start+static_cast(actually_read)); } #endif public: line_reader(const char * fname) :m_eof(false), m_eof_behind_buffer(false), m_next_index(0), m_ok(true), m_data_size(0) { m_data.resize(2*s_expansion_step); resize_data(0); #if _WINDOWS errno_t err = fopen_s(&m_file, fname, "rb"); m_ok = err == 0; #else m_file = fopen(fname, "rb"); #endif } ~line_reader() { fclose(m_file); } bool operator()() { return m_ok; } /** \brief Retrieve next line from the stream. This operation invalidates the line previously retrieved. This operatio can be called only if we are not at the end of file. User is free to modify the content of the returned array until the terminating NULL character. */ char * get_line() { SASSERT(!m_eof); unsigned start = m_next_index; unsigned curr = start; for(;;) { SASSERT(curr<=m_data_size); SASSERT(m_data[m_data_size]==s_delimiter); { const char * data_ptr = m_data.begin(); const char * ptr = data_ptr+curr; while(*ptr!=s_delimiter) { ptr++; } curr = static_cast(ptr-data_ptr); } SASSERT(m_data[curr]==s_delimiter); if(curr str2token; str2token m_str2token; public: reserved_symbols() { m_str2token.insert(":-", TK_LEFT_ARROW); m_str2token.insert("_", TK_WILD); m_str2token.insert(".", TK_PERIOD); m_str2token.insert("!=", TK_NEQ); m_str2token.insert("=", TK_EQ); m_str2token.insert("<", TK_LT); m_str2token.insert(">", TK_GT); m_str2token.insert(":", TK_COLON); m_str2token.insert(".include", TK_INCLUDE); m_str2token.insert("!", TK_NEG); } dtoken string2dtoken(char const * str) { str2token::entry * e = m_str2token.find_core(str); if (e) return e->get_data().m_value; else return TK_ID; } }; class dlexer { std::istream* m_input; char_reader* m_reader; char m_prev_char; char m_curr_char; int m_line; int m_pos; int m_tok_pos; string_buffer<> m_buffer; reserved_symbols m_reserved_symbols; public: //when parsing domains, we want '.' character to be allowed in IDs, but elsewhere //we don't (because of the "y." in rules like "P(x,y):-x=y.") bool m_parsing_domains; bool eos() const { return m_curr_char == EOF; } void next() { m_prev_char = m_curr_char; if (m_reader) { m_curr_char = m_reader->get(); } else { m_curr_char = m_input->get(); } m_pos++; } void save_char(char c) { m_buffer << c; } void save_and_next() { m_buffer << m_curr_char; next(); } dlexer(): m_input(0), m_reader(0), m_prev_char(0), m_curr_char(0), m_line(1), m_pos(0), m_tok_pos(0), m_parsing_domains(false) { } void set_stream(std::istream* s, char_reader* r) { m_input = s; m_reader = r; next(); } dtoken read_num() { while(isdigit(m_curr_char)) { save_and_next(); } return TK_NUM; } dtoken read_id() { while (!eos() && m_curr_char != '(' && m_curr_char != ')' && m_curr_char != '#' && m_curr_char != ',' && (m_parsing_domains || m_curr_char != '.') && m_curr_char != ':' && m_curr_char != '=' && !iswspace(m_curr_char) ) { save_and_next(); } return m_reserved_symbols.string2dtoken(m_buffer.c_str()); } // read an id of the form '|'.*'|' dtoken read_bid() { while (!eos() && m_curr_char != '|') { save_and_next(); } if (m_curr_char == '|') { next(); } return m_reserved_symbols.string2dtoken(m_buffer.c_str()); } dtoken read_string() { m_tok_pos = m_pos; next(); while (m_curr_char != '"') { if (m_input && m_input->eof()) { return TK_ERROR; } if (m_reader && m_reader->eof()) { return TK_ERROR; } if (m_curr_char == '\n') { return TK_ERROR; } save_and_next(); } next(); return TK_STRING; } void read_comment() { bool line_comment = m_prev_char=='\n' || m_prev_char == 0; while (m_curr_char != '\n' && !eos()) { next(); } if (line_comment && m_curr_char == '\n') { m_line++; next(); } } bool lookahead_newline() { while (m_curr_char == ' ') { save_and_next(); } if (m_curr_char == '\n') { next(); m_line++; m_buffer.reset(); return true; } if (m_curr_char == '#') { m_buffer.reset(); m_prev_char = 0; read_comment(); return true; } return false; } dtoken next_token() { for(;;) { if (eos()) { return TK_EOS; } m_buffer.reset(); switch (m_curr_char) { case '#': // comment read_comment(); break; case '\n': next(); m_line++; return TK_NEWLINE; case '\\': // here we ignore a newline if it is preceded by a backslash. // We need to take care, since anywhere else backshlash is used // as a regular character next(); save_char('\\'); if (lookahead_newline()) { break; } return read_id(); case '(': m_tok_pos = m_pos; next(); return TK_LP; case ')': m_tok_pos = m_pos; next(); return TK_RP; case ',': m_tok_pos = m_pos; next(); return TK_COMMA; case '=': m_tok_pos = m_pos; next(); return TK_EQ; case '!': m_tok_pos = m_pos; next(); if(m_curr_char == '=') { next(); return TK_NEQ; } return TK_NEG; case ':': m_tok_pos = m_pos; next(); if (m_curr_char == '-') { next(); return TK_LEFT_ARROW; } return TK_COLON; case '\"': return read_string(); case '|': next(); return read_bid(); default: if (iswspace(m_curr_char)) { next(); break; } else if (iswdigit(m_curr_char)) { m_tok_pos = m_pos; save_and_next(); return read_num(); } else { char old = m_curr_char; m_tok_pos = m_pos; save_and_next(); if (old == '-' && iswdigit(m_curr_char)) { return read_num(); } else { return read_id(); } } } } } const char * get_token_data() const { return m_buffer.c_str(); } unsigned get_token_pos() const { return m_tok_pos; } unsigned get_line() const { return m_line; } }; class dparser : public parser { protected: typedef map > str2var; typedef map > str2sort; context& m_context; ast_manager& m_manager; dlexer* m_lexer; region m_region; dl_decl_util & m_decl_util; arith_util m_arith; unsigned m_num_vars; str2var m_vars; unsigned m_sym_idx; std::string m_path; str2sort m_sort_dict; // true if an error occured during the current call to the parse_stream // function bool m_error; public: dparser(context& ctx, ast_manager& m): m_context(ctx), m_manager(m), m_decl_util(ctx.get_decl_util()), m_arith(m), m_num_vars(0), m_sym_idx(0) { } virtual bool parse_file(char const * filename) { reset(); if (filename != 0) { set_path(filename); char_reader reader(filename); if (!reader()) { get_err() << "ERROR: could not open file '" << filename << "'.\n"; return false; } return parse_stream(0, &reader); } else { return parse_stream(&std::cin, 0); } } virtual bool parse_string(char const * string) { reset(); std::string s(string); std::istringstream is(s); return parse_stream(&is, 0); } protected: void reset() { m_num_vars = 0; m_sym_idx = 0; m_vars.reset(); m_region.reset(); m_path.clear(); m_sort_dict.reset(); } void set_path(char const* filename) { char const* div = strrchr(filename, '/'); if (!div) { div = strrchr(filename,'\\'); } if (div) { m_path.assign(filename, div - filename + 1); } } std::ostream& get_err() { return std::cerr; } bool parse_stream(std::istream* is, char_reader* r) { bool result = false; try { m_error=false; dlexer lexer; m_lexer = &lexer; m_lexer->set_stream(is, r); dtoken tok = m_lexer->next_token(); tok = parse_domains(tok); tok = parse_decls(tok); result = tok == TK_EOS && m_error == false; } catch (z3_exception& ex) { std::cerr << ex.msg() << std::endl; result = false; } return result; } dtoken parse_domains(dtoken tok) { flet flet_parsing_domains(m_lexer->m_parsing_domains, true); while (tok != TK_EOS && tok != TK_ERROR) { switch(tok) { case TK_ID: tok = parse_domain(); break; case TK_NEWLINE: return m_lexer->next_token(); case TK_INCLUDE: tok = m_lexer->next_token(); if (tok != TK_STRING) { tok = unexpected(tok, "a string"); break; } tok = parse_include(m_lexer->get_token_data(), true); if(tok!=TK_NEWLINE) { tok = unexpected(tok, "newline expected after include statement"); } else { tok = m_lexer->next_token(); } break; default: tok = unexpected(tok, "identifier, newline or include"); break; } } return tok; } bool extract_domain_name(const char* s0, std::string & result) { std::string str(s0); size_t last_non_digit = str.find_last_not_of("0123456789"); if(last_non_digit==std::string::npos) { //the domain name consists only of digits, which should not happen result=str; return false; } str.erase(last_non_digit+1); result=str; return true; } dtoken parse_domain() { std::string domain_name; if(!extract_domain_name(m_lexer->get_token_data(), domain_name)) { return unexpected(TK_ID, "domain name"); } dtoken tok = m_lexer->next_token(); if (tok == TK_ID && strcmp(m_lexer->get_token_data(), "int")==0) { register_int_sort(symbol(domain_name.c_str())); tok = m_lexer->next_token(); if(tok != TK_NEWLINE) { return unexpected(tok, "end of line"); } return tok; } if (tok != TK_NUM) { return unexpected(tok, "numeral or 'int'"); } unsigned num = atoi(m_lexer->get_token_data()); sort * s = register_finite_sort(symbol(domain_name.c_str()), num, context::SK_SYMBOL); tok = m_lexer->next_token(); if (tok == TK_ID) { tok = parse_mapfile(tok, s, m_lexer->get_token_data()); } if (tok == TK_NEWLINE) { tok = m_lexer->next_token(); } return tok; } dtoken parse_decls(dtoken tok) { while (tok != TK_EOS && tok != TK_ERROR) { switch(tok) { case TK_ID: tok = parse_rule(tok); break; case TK_NEWLINE: tok = m_lexer->next_token(); break; case TK_INCLUDE: tok = m_lexer->next_token(); if (tok != TK_STRING) { tok = unexpected(tok, "a string"); break; } tok = parse_include(m_lexer->get_token_data(), false); break; default: tok = unexpected(tok, "identifier"); break; } } return tok; } dtoken unexpected(dtoken tok, char const* msg) { #if 1 throw default_exception("%s at line %u '%s' found '%s'\n", msg, m_lexer->get_line(), m_lexer->get_token_data(), dtoken_strings[tok]); SASSERT(false); return tok; #else m_error = true; get_err() << msg << " expected at line " << m_lexer->get_line() << "\n"; get_err() << "'" << m_lexer->get_token_data() << "' found\n"; get_err() << "'" << dtoken_strings[tok] << "'\n"; if (tok == TK_ERROR || tok == TK_EOS) { return tok; } return m_lexer->next_token(); #endif } dtoken parse_rule(dtoken tok) { m_num_vars = 0; m_vars.reset(); switch(tok) { case TK_EOS: return tok; case TK_ID: { app_ref pred(m_manager); symbol s(m_lexer->get_token_data()); tok = m_lexer->next_token(); bool is_predicate_declaration; tok = parse_pred(tok, s, pred, is_predicate_declaration); switch (tok) { case TK_PERIOD: if(is_predicate_declaration) { return unexpected(tok, "predicate declaration should not end with '.'"); } add_rule(pred, 0, 0, 0); return m_lexer->next_token(); case TK_LEFT_ARROW: return parse_body(pred); case TK_NEWLINE: case TK_EOS: if(!is_predicate_declaration) { return unexpected(tok, "'.' expected at the end of rule"); } return tok; default: return unexpected(tok, "unexpected token"); } } default: return unexpected(tok, "rule expected"); } } dtoken parse_body(app* head) { app_ref_vector body(m_manager); svector polarity_vect; dtoken tok = m_lexer->next_token(); while (tok != TK_ERROR && tok != TK_EOS) { if (tok == TK_PERIOD) { SASSERT(body.size()==polarity_vect.size()); add_rule(head, body.size(), body.c_ptr(), polarity_vect.c_ptr()); return m_lexer->next_token(); } char const* td = m_lexer->get_token_data(); app_ref pred(m_manager); bool is_neg = false; if (tok == TK_NEG) { tok = m_lexer->next_token(); is_neg = true; } if (tok == TK_STRING || tok == TK_NUM || (tok == TK_ID && m_vars.contains(td))) { tok = parse_infix(tok, td, pred); } else if (tok == TK_ID) { symbol s(td); tok = m_lexer->next_token(); bool is_declaration; tok = parse_pred(tok, s, pred, is_declaration); SASSERT(!is_declaration); } else { tok = unexpected(tok, "expected predicate or relation"); return tok; } body.push_back(pred); polarity_vect.push_back(is_neg); if (tok == TK_COMMA) { tok = m_lexer->next_token(); } else if (tok == TK_PERIOD) { continue; } else { tok = unexpected(tok, "expected comma or period"); return tok; } } return tok; } // // infix: // Sym REL Sym // Sym ::= String | NUM | Var // dtoken parse_infix(dtoken tok1, char const* td, app_ref& pred) { symbol td1(td); expr_ref v1(m_manager), v2(m_manager); sort* s = 0; dtoken tok2 = m_lexer->next_token(); if (tok2 != TK_NEQ && tok2 != TK_GT && tok2 != TK_LT && tok2 != TK_EQ) { return unexpected(tok2, "built-in infix operator"); } dtoken tok3 = m_lexer->next_token(); td = m_lexer->get_token_data(); if (tok3 != TK_STRING && tok3 != TK_NUM && !(tok3 == TK_ID && m_vars.contains(td))) { return unexpected(tok3, "identifier"); } symbol td2(td); if (tok1 == TK_ID) { expr* _v1 = 0; m_vars.find(td1.bare_str(), _v1); v1 = _v1; } if (tok3 == TK_ID) { expr* _v2 = 0; m_vars.find(td2.bare_str(), _v2); v2 = _v2; } if (!v1 && !v2) { return unexpected(tok3, "at least one argument should be a variable"); } if (v1) { s = m_manager.get_sort(v1); } else { s = m_manager.get_sort(v2); } if (!v1) { v1 = mk_const(td1, s); } if (!v2) { v2 = mk_const(td2, s); } switch(tok2) { case TK_EQ: pred = m_manager.mk_eq(v1,v2); break; case TK_NEQ: pred = m_manager.mk_not(m_manager.mk_eq(v1,v2)); break; case TK_LT: pred = m_decl_util.mk_lt(v1, v2); break; case TK_GT: pred = m_decl_util.mk_lt(v2, v1); break; default: UNREACHABLE(); } return m_lexer->next_token(); } dtoken parse_pred(dtoken tok, symbol const& s, app_ref& pred, bool & is_predicate_declaration) { expr_ref_vector args(m_manager); svector arg_names; func_decl* f = m_context.try_get_predicate_decl(s); tok = parse_args(tok, f, args, arg_names); is_predicate_declaration = f==0; if (f==0) { //we're in a declaration unsigned arity = args.size(); ptr_vector domain; for (unsigned i = 0; i < arity; ++i) { domain.push_back(m_manager.get_sort(args[i].get())); } f = m_manager.mk_func_decl(s, domain.size(), domain.c_ptr(), m_manager.mk_bool_sort()); m_context.register_predicate(f, true); while (tok == TK_ID) { char const* pred_pragma = m_lexer->get_token_data(); if(strcmp(pred_pragma, "printtuples")==0 || strcmp(pred_pragma, "outputtuples")==0) { m_context.set_output_predicate(f); } tok = m_lexer->next_token(); } m_context.set_argument_names(f, arg_names); } if(args.size() < f->get_arity()) { return unexpected(tok, "too few arguments passed to predicate"); } SASSERT(args.size()==f->get_arity()); //TODO: we do not need to do the mk_app if we're in a declaration pred = m_manager.mk_app(f, args.size(), args.c_ptr()); return tok; } /** \brief Parse predicate arguments. If \c f==0, they are arguments of a predicate declaration. If parsing a declaration, argumens names are pushed to the \c arg_names vector. */ dtoken parse_args(dtoken tok, func_decl* f, expr_ref_vector& args, svector & arg_names) { if (tok != TK_LP) { return tok; } unsigned arg_idx = 0; tok = m_lexer->next_token(); while (tok != TK_EOS && tok != TK_ERROR) { symbol alias; sort* s = 0; if(!f) { //we're in a predicate declaration if(tok != TK_ID) { tok = unexpected(tok, "Expecting variable in declaration"); return tok; } symbol var_symbol(m_lexer->get_token_data()); tok = m_lexer->next_token(); if (tok != TK_COLON) { tok = unexpected(tok, "Expecting colon in declaration (first occurence of a predicate must be a declaration)"); return tok; } tok = m_lexer->next_token(); if (tok != TK_ID) { tok = unexpected(tok, "Expecting sort after colon in declaration"); return tok; } std::string sort_name; if(!extract_domain_name(m_lexer->get_token_data(), sort_name)) { return unexpected(TK_ID, "sort name"); } sort* s = get_sort(sort_name.c_str()); args.push_back(m_manager.mk_var(m_num_vars, s)); arg_names.push_back(var_symbol); tok = m_lexer->next_token(); } else { if(arg_idx>=f->get_arity()) { return unexpected(tok, "too many arguments passed to predicate"); } s = f->get_domain(arg_idx); symbol var_symbol; tok = parse_arg(tok, s, args); } ++arg_idx; if (tok == TK_RP) { return m_lexer->next_token(); } if (tok == TK_COMMA) { tok = m_lexer->next_token(); } } return tok; } /** \remark \c var_symbol argument is assigned name of the variable. If the argument is not a variable, is remains unchanged. */ dtoken parse_arg(dtoken tok, sort* s, expr_ref_vector& args) { switch(tok) { case TK_WILD: { args.push_back(m_manager.mk_var(m_num_vars++, s)); break; } case TK_ID: { symbol data (m_lexer->get_token_data()); if (is_var(data.bare_str())) { unsigned idx = 0; expr* v = 0; if (!m_vars.find(data.bare_str(), v)) { idx = m_num_vars++; v = m_manager.mk_var(idx, s); m_vars.insert(data.bare_str(), v); } else if (s != m_manager.get_sort(v)) { throw default_exception("sort: %s expected, but got: %s\n", s->get_name().bare_str(), m_manager.get_sort(v)->get_name().bare_str()); } args.push_back(v); } else { args.push_back(mk_const(data, s)); } break; } case TK_STRING: { char const* data = m_lexer->get_token_data(); args.push_back(mk_const(symbol(data), s)); break; } case TK_NUM: { char const* data = m_lexer->get_token_data(); rational num(data); if(!num.is_uint64()) { return unexpected(tok, "integer expected"); } uint64 int_num = num.get_uint64(); app * numeral = mk_symbol_const(int_num, s); args.push_back(numeral); break; } default: break; } return m_lexer->next_token(); } // all IDs are variables. bool is_var(char const* data) { return true; } dtoken parse_decl(dtoken tok) { return tok; } dtoken parse_include(char const* filename, bool parsing_domain) { IF_VERBOSE(2, verbose_stream() << "include: " << filename << "\n";); std::string path(m_path); path += filename; char_reader stream(path.c_str()); if (!stream()) { get_err() << "ERROR: could not open file '" << path << "'.\n"; return TK_ERROR; } dtoken tok; dlexer lexer; { flet lexer_let(m_lexer, &lexer); m_lexer->set_stream(0, &stream); tok = m_lexer->next_token(); if(parsing_domain) { tok = parse_domains(tok); } tok = parse_decls(tok); } if (tok == TK_EOS) { tok = m_lexer->next_token(); } return tok; } dtoken parse_mapfile(dtoken tok, sort * s, char const* filename) { std::string path(m_path); path += filename; line_reader reader(path.c_str()); if (!reader()) { get_err() << "Warning: could not open file '" << path << "'.\n"; return m_lexer->next_token(); } std::string line; while(!reader.eof()) { symbol sym=symbol(reader.get_line()); m_context.get_constant_number(s, sym); } return m_lexer->next_token(); } bool read_line(std::istream& strm, std::string& line) { line.clear(); char ch = strm.get(); while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') { ch = strm.get(); } while (ch != '\n' && ch != '\r' && ch != EOF) { line.push_back(ch); ch = strm.get(); } return line.size() > 0; } void add_rule(app* head, unsigned sz, app* const* body, const bool * is_neg) { rule_manager& m = m_context.get_rule_manager(); if(sz==0 && m.is_fact(head)) { m_context.add_fact(head); } else { rule * r = m.mk(head, sz, body, is_neg); rule_ref rule(r, m); m_context.add_rule(rule); } } sort * register_finite_sort(symbol name, uint64 domain_size, context::sort_kind k) { if(m_sort_dict.contains(name.bare_str())) { throw default_exception("sort %s already declared", name.bare_str()); } sort * s = m_decl_util.mk_sort(name, domain_size); m_context.register_finite_sort(s, k); m_sort_dict.insert(name.bare_str(), s); return s; } sort * register_int_sort(symbol name) { if(m_sort_dict.contains(name.bare_str())) { throw default_exception("sort %s already declared", name.bare_str()); } sort * s = m_arith.mk_int(); m_sort_dict.insert(name.bare_str(), s); return s; } sort * get_sort(const char* str) { sort * res; if(!m_sort_dict.find(str, res)) { throw default_exception("unknown sort \"%s\"", str); } return res; } app* mk_const(symbol const& name, sort* s) { app * res; if(m_arith.is_int(s)) { uint64 val; if(!string_to_uint64(name.bare_str(), val)) { throw default_exception("Invalid integer: \"&s\"", name.bare_str()); } res = m_arith.mk_numeral(rational(val, rational::ui64()), s); } else { unsigned idx = m_context.get_constant_number(s, name); res = m_decl_util.mk_numeral(idx, s); } return res; } /** \brief Make a constant for DK_SYMBOL sort out of an integer */ app* mk_symbol_const(uint64 el, sort* s) { app * res; if(m_arith.is_int(s)) { res = m_arith.mk_numeral(rational(el, rational::ui64()), s); } else { unsigned idx = m_context.get_constant_number(s, symbol(to_string(el).c_str())); res = m_decl_util.mk_numeral(idx, s); } return res; } app* mk_const(uint64 el, sort* s) { unsigned idx = m_context.get_constant_number(s, el); app * res = m_decl_util.mk_numeral(idx, s); return res; } table_element mk_table_const(symbol const& name, sort* s) { return m_context.get_constant_number(s, name); } table_element mk_table_const(uint64 el, sort* s) { return m_context.get_constant_number(s, el); } }; /* Program ::== Sort* (Rule | Include | Decl)* Comment ::== '#...' Rule ::== Fact | InfRule Fact ::== Identifier(Element*). InfRule ::== Identifier(Element*) :- (Identifier(Element*))+. Element ::== '_' | 'string' | integer | Identifier Sort ::== Identifier (Number [map-file]| 'int') Decl ::== Identifier(SortDecl) [Pragma] \n SortDecl ::== Identifier ':' Identifier Pragma ::== 'input' | 'printtuples' | If sort name ends with a sequence of digits, they are ignored (so V and V1234 stand for the same sort) This is how BDDBDDB behaves. */ // ----------------------------------- // // wpa_parser // // ----------------------------------- class wpa_parser_impl : public wpa_parser, dparser { typedef svector uint64_vector; typedef hashtable > uint64_set; typedef map > num2sym; typedef map sym2nums; num2sym m_number_names; sym2nums m_sort_contents; sort_ref m_bool_sort; sort_ref m_short_sort; std::string m_current_file; unsigned m_current_line; bool m_use_map_names; uint64_set& ensure_sort_content(symbol sort_name) { sym2nums::entry * e = m_sort_contents.insert_if_not_there2(sort_name, 0); if(!e->get_data().m_value) { e->get_data().m_value = alloc(uint64_set); } return *e->get_data().m_value; } public: wpa_parser_impl(context & ctx) : dparser(ctx, ctx.get_manager()), m_bool_sort(ctx.get_manager()), m_short_sort(ctx.get_manager()), m_use_map_names(ctx.use_map_names()) { } ~wpa_parser_impl() { reset_dealloc_values(m_sort_contents); } void reset() { } virtual bool parse_directory(char const * path) { bool result = false; try { result = parse_directory_core(path); } catch (z3_exception& ex) { std::cerr << ex.msg() << std::endl; return false; } return result; } private: bool parse_directory_core(char const* path) { IF_VERBOSE(10, verbose_stream() << "Start parsing directory " << path << "\n";); reset(); string_vector map_files; get_file_names(path, "map", true, map_files); string_vector::iterator mit = map_files.begin(); string_vector::iterator mend = map_files.end(); for(; mit!=mend; ++mit) { std::string map_file_name = *mit; parse_map_file(map_file_name); } finish_map_files(); string_vector rule_files; get_file_names(path, "rules", true, rule_files); string_vector::iterator rlit = rule_files.begin(); string_vector::iterator rlend = rule_files.end(); for(; rlit!=rlend; ++rlit) { parse_rules_file(*rlit); } string_vector rel_files; get_file_names(path, "rel", true, rel_files); string_vector::iterator rit = rel_files.begin(); string_vector::iterator rend = rel_files.end(); for(; rit!=rend; ++rit) { std::string rel_file_name = *rit; //skip relations which we do not support yet if(rel_file_name.find("DirectCall")!=std::string::npos || rel_file_name.find("FunctionFormals")!=std::string::npos || rel_file_name.find("IndirectCall")!=std::string::npos) { continue; } parse_rel_file(rel_file_name); } IF_VERBOSE(10, verbose_stream() << "Done parsing directory " << path << "\n";); return true; } bool inp_num_to_element(sort * s, uint64 num, table_element & res) { if(s==m_bool_sort.get() || s==m_short_sort.get()) { res = mk_table_const(num, s); return true; } if(num==0) { if(!m_use_map_names) { res = mk_table_const(0, s); } else { res = mk_table_const(symbol(""), s); } return true; } sym2nums::entry * e = m_sort_contents.find_core(s->get_name()); SASSERT(e); SASSERT(e->get_data().m_value); uint64_set & sort_content = *e->get_data().m_value; if(!sort_content.contains(num)) { warning_msg("symbol number %I64u on line %d in file %s does not belong to sort %s", num, m_current_line, m_current_file.c_str(), s->get_name().bare_str()); return false; } if(!m_use_map_names) { res = mk_table_const(num, s); return true; } else { symbol const_name; if(num==0) { const_name = symbol(""); } else if(!m_number_names.find(num, const_name)) { throw default_exception("unknown symbol number %I64u on line %d in file %s", num, m_current_line, m_current_file.c_str()); } res = mk_table_const(const_name, s); return true; } } void parse_rules_file(std::string fname) { SASSERT(file_exists(fname)); flet flet_cur_file(m_current_file, fname); std::ifstream stm(fname.c_str()); SASSERT(!stm.fail()); dlexer lexer; m_lexer = &lexer; m_lexer->set_stream(&stm, 0); dtoken tok = m_lexer->next_token(); tok = parse_decls(tok); m_lexer = 0; } bool parse_rel_line(char * full_line, uint64_vector & args) { SASSERT(args.empty()); cut_off_comment(full_line); if(full_line[0]==0) { return false; } const char * ptr = full_line; bool last = false; do { while(*ptr==' ') { ptr++; } if(*ptr==0) { break; } uint64 num; if(!read_uint64(ptr, num)) { throw default_exception("number expected on line %d in file %s", m_current_line, m_current_file.c_str()); } if(*ptr!=' ' && *ptr!=0) { throw default_exception("' ' expected to separate numbers on line %d in file %s, got '%s'", m_current_line, m_current_file.c_str(), ptr); } args.push_back(num); } while(!last); return true; } void parse_rel_file(std::string fname) { SASSERT(file_exists(fname)); IF_VERBOSE(10, verbose_stream() << "Parsing relation file " << fname << "\n";); flet flet_cur_file(m_current_file, fname); flet flet_cur_line(m_current_line, 0); std::string predicate_name_str = get_file_name_without_extension(fname); symbol predicate_name(predicate_name_str.c_str()); func_decl * pred = m_context.try_get_predicate_decl(predicate_name); if(!pred) { throw default_exception("tuple file %s for undeclared predicate %s", m_current_file.c_str(), predicate_name.bare_str()); } unsigned pred_arity = pred->get_arity(); sort * const * arg_sorts = pred->get_domain(); uint64_vector args; table_fact fact; //std::ifstream stm(fname.c_str(), std::ios_base::binary); //SASSERT(!stm.fail()); //line_reader rdr(stm); line_reader rdr(fname.c_str()); while(!rdr.eof()) { m_current_line++; char * full_line = rdr.get_line(); args.reset(); if(!parse_rel_line(full_line, args)) { continue; } if(args.size()!=pred_arity) { throw default_exception("invalid number of arguments on line %d in file %s", m_current_line, m_current_file.c_str()); } bool fact_fail = false; fact.reset(); for(unsigned i=0;im_key; uint64_set & sort_content = *sit->m_value; //the +1 is for a zero element which happens to appear in the problem files uint64 domain_size = sort_content.size()+1; // sort * s; if(!m_use_map_names) { /* s = */ register_finite_sort(sort_name, domain_size, context::SK_UINT64); } else { /* s = */ register_finite_sort(sort_name, domain_size, context::SK_SYMBOL); } /* uint64_set::iterator cit = sort_content.begin(); uint64_set::iterator cend = sort_content.end(); for(; cit!=cend; ++cit) { uint64 const_num = *cit; inp_num_to_element(s, const_num); } */ } } void cut_off_comment(char * line) { char * ptr = line; while(*ptr && *ptr!='#' && *ptr!='\n' && *ptr!='\r') { ptr++; } *ptr=0; } bool parse_map_line(char * full_line, uint64 & num, symbol & name) { cut_off_comment(full_line); if(full_line[0]==0) { return false; } const char * ptr = full_line; if(!read_uint64(ptr, num)) { throw default_exception("number expected at line %d in file %s", m_current_line, m_current_file.c_str()); } if(*ptr!=' ') { throw default_exception("' ' expected after the number at line %d in file %s", m_current_line, m_current_file.c_str()); } ptr++; if(!m_use_map_names) { static symbol no_name(""); name=no_name; } else { std::string rest_of_line(ptr); const char * cut_off_word = " SC_EXTERN "; size_t cut_off_pos = rest_of_line.find(cut_off_word); if(cut_off_pos!=std::string::npos) { rest_of_line = rest_of_line.substr(0, cut_off_pos); } cut_off_word = " _ZONE_"; cut_off_pos = rest_of_line.find(cut_off_word); if(cut_off_pos!=std::string::npos) { rest_of_line = rest_of_line.substr(0, cut_off_pos); } const char * const ignored_suffix = "Constant "; const size_t ignored_suffix_len = 9; if(rest_of_line.size()>ignored_suffix_len && rest_of_line.substr(rest_of_line.size()-ignored_suffix_len)==ignored_suffix) { rest_of_line = rest_of_line.substr(0, rest_of_line.size()-ignored_suffix_len); } if(rest_of_line[rest_of_line.size()-1]==' ') { rest_of_line = rest_of_line.substr(0, rest_of_line.size()-1); } name = symbol(rest_of_line.c_str()); } return true; } void parse_map_file(std::string fname) { SASSERT(file_exists(fname)); IF_VERBOSE(10, verbose_stream() << "Parsing map file " << fname << "\n";); flet flet_cur_file(m_current_file, fname); flet flet_cur_line(m_current_line, 0); std::string sort_name_str = get_file_name_without_extension(fname); symbol sort_name(sort_name_str.c_str()); uint64_set & sort_elements = ensure_sort_content(sort_name); //std::ifstream stm(fname.c_str(), std::ios_base::binary); //SASSERT(!stm.fail()); //line_reader rdr(stm); line_reader rdr(fname.c_str()); while(!rdr.eof()) { m_current_line++; char * full_line = rdr.get_line(); uint64 num; symbol el_name; if(!parse_map_line(full_line, num, el_name)) { continue; } sort_elements.insert(num); if(m_use_map_names) { num2sym::entry * e = m_number_names.insert_if_not_there2(num, el_name); if(e->get_data().m_value!=el_name) { warning_msg("mismatch of number names on line %d in file %s. old: \"%s\" new: \"%s\"", m_current_line, fname.c_str(), e->get_data().m_value.bare_str(), el_name.bare_str()); } } } } }; parser* parser::create(context& ctx, ast_manager& m) { return alloc(dparser, ctx, m); } wpa_parser * wpa_parser::create(context& ctx, ast_manager & ast_manager) { return alloc(wpa_parser_impl, ctx); } z3-4.4.0/src/muz/fp/dl_register_engine.h0000644000175000017500000000107612540347414017661 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_register_engine.h Abstract: Class for creating Datalog engines. Author: Nikolaj Bjorner (nbjorner) 2013-08-28 Revision History: --*/ #ifndef _DL_REGISTER_ENGINE_H_ #define _DL_REGISTER_ENGINE_H_ #include "dl_context.h" namespace datalog { class register_engine : public register_engine_base { context* m_ctx; public: register_engine(); engine_base* mk_engine(DL_ENGINE engine_type); void set_context(context* ctx) { m_ctx = ctx; } }; } #endif z3-4.4.0/src/muz/fp/horn_tactic.h0000644000175000017500000000123512540347414016323 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: horn_tactic.h Abstract: PDR as a tactic to solve Horn clauses. Author: Nikolaj Bjorner (nbjorner) 2012-11-16. Revision History: --*/ #ifndef _HORN_TACTIC_H_ #define _HORN_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_horn_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("horn", "apply tactic for horn clauses.", "mk_horn_tactic(m, p)") */ tactic * mk_horn_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("horn-simplify", "simplify horn clauses.", "mk_horn_simplify_tactic(m, p)") */ #endif z3-4.4.0/src/muz/fp/dl_cmds.h0000644000175000017500000000120112540347414015424 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dl_cmds.h Abstract: Datalog commands for SMT2 front-end. Author: Nikolaj Bjorner (nbjorner) 2012-11-17 Notes: --*/ #ifndef _DL_CMDS_H_ #define _DL_CMDS_H_ #include "ast.h" class cmd_context; struct dl_collected_cmds { expr_ref_vector m_rules; svector m_names; expr_ref_vector m_queries; func_decl_ref_vector m_rels; dl_collected_cmds(ast_manager& m) : m_rules(m), m_queries(m), m_rels(m) {} }; void install_dl_cmds(cmd_context & ctx); void install_dl_collect_cmds(dl_collected_cmds& collected_cmds, cmd_context& ctx); #endif z3-4.4.0/src/muz/fp/datalog_parser.h0000644000175000017500000000146612540347414017023 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: datalog_parser.h Abstract: Parser for Datalogish files Author: Nikolaj Bjorner (nbjorner) 2010-5-17 Revision History: --*/ #ifndef _DATALOG_PARSER_H_ #define _DATALOG_PARSER_H_ #include "ast.h" #include "dl_context.h" namespace datalog { class parser { public: static parser * create(context& ctx, ast_manager & ast_manager); virtual ~parser() {} virtual bool parse_file(char const * path) = 0; virtual bool parse_string(char const * string) = 0; }; class wpa_parser { public: static wpa_parser * create(context& ctx, ast_manager & ast_manager); virtual ~wpa_parser() {} virtual bool parse_directory(char const * path) = 0; }; }; #endif z3-4.4.0/src/muz/fp/horn_tactic.cpp0000644000175000017500000003120112540347414016652 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: horn_tactic.h Abstract: HORN as a tactic to solve Horn clauses. Author: Nikolaj Bjorner (nbjorner) 2012-11-16. Revision History: --*/ #include"tactical.h" #include"model_converter.h" #include"proof_converter.h" #include"horn_tactic.h" #include"dl_context.h" #include"dl_register_engine.h" #include"expr_replacer.h" #include"dl_rule_transformer.h" #include"dl_mk_slice.h" #include"filter_model_converter.h" #include"dl_transforms.h" #include"fixedpoint_params.hpp" class horn_tactic : public tactic { struct imp { ast_manager& m; bool m_is_simplify; datalog::register_engine m_register_engine; datalog::context m_ctx; smt_params m_fparams; imp(bool t, ast_manager & m, params_ref const & p): m(m), m_is_simplify(t), m_ctx(m, m_register_engine, m_fparams) { updt_params(p); } void updt_params(params_ref const & p) { m_ctx.updt_params(p); } void collect_param_descrs(param_descrs & r) { m_ctx.collect_params(r); } void reset_statistics() { m_ctx.reset_statistics(); } void collect_statistics(statistics & st) const { m_ctx.collect_statistics(st); } void set_cancel(bool f) { if (f) { m_ctx.cancel(); } } void normalize(expr_ref& f) { bool is_positive = true; expr* e = 0; while (true) { if (is_forall(f) && is_positive) { f = to_quantifier(f)->get_expr(); } else if (is_exists(f) && !is_positive) { f = to_quantifier(f)->get_expr(); } else if (m.is_not(f, e)) { is_positive = !is_positive; f = e; } else { break; } } if (!is_positive) { f = m.mk_not(f); } } bool is_predicate(expr* a) { SASSERT(m.is_bool(a)); return is_app(a) && to_app(a)->get_decl()->get_family_id() == null_family_id; } void register_predicate(expr* a) { SASSERT(is_predicate(a)); m_ctx.register_predicate(to_app(a)->get_decl(), false); } void check_predicate(ast_mark& mark, expr* a) { ptr_vector todo; todo.push_back(a); while (!todo.empty()) { a = todo.back(); todo.pop_back(); if (mark.is_marked(a)) { continue; } mark.mark(a, true); if (is_quantifier(a)) { a = to_quantifier(a)->get_expr(); todo.push_back(a); } else if (m.is_not(a) || m.is_and(a) || m.is_or(a) || m.is_implies(a)) { todo.append(to_app(a)->get_num_args(), to_app(a)->get_args()); } else if (m.is_ite(a)) { todo.push_back(to_app(a)->get_arg(1)); todo.push_back(to_app(a)->get_arg(2)); } else if (is_predicate(a)) { register_predicate(a); } } } enum formula_kind { IS_RULE, IS_QUERY, IS_NONE }; bool is_implication(expr* f) { expr* e1; while (is_forall(f)) { f = to_quantifier(f)->get_expr(); } while (m.is_implies(f, e1, f)) ; return is_predicate(f); } formula_kind get_formula_kind(expr_ref& f) { expr_ref tmp(f); normalize(tmp); ast_mark mark; expr_ref_vector args(m), body(m); expr_ref head(m); expr* a = 0, *a1 = 0; qe::flatten_or(tmp, args); for (unsigned i = 0; i < args.size(); ++i) { a = args[i].get(); check_predicate(mark, a); if (m.is_not(a, a1)) { body.push_back(a1); } else if (is_predicate(a)) { if (head) { return IS_NONE; } head = a; } else { body.push_back(m.mk_not(a)); } } if (head) { if (!is_implication(f)) { f = m.mk_and(body.size(), body.c_ptr()); f = m.mk_implies(f, head); } return IS_RULE; } else { f = m.mk_and(body.size(), body.c_ptr()); return IS_QUERY; } } expr_ref mk_rule(expr* body, expr* head) { return expr_ref(m.mk_implies(body, head), m); } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("horn", *g); bool produce_proofs = g->proofs_enabled(); if (produce_proofs) { if (!m_ctx.generate_proof_trace()) { params_ref params = m_ctx.get_params().p; params.set_bool("generate_proof_trace", true); updt_params(params); } } unsigned sz = g->size(); expr_ref q(m), f(m); expr_ref_vector queries(m); std::stringstream msg; m_ctx.reset(); m_ctx.ensure_opened(); for (unsigned i = 0; i < sz; i++) { f = g->form(i); formula_kind k = get_formula_kind(f); switch(k) { case IS_RULE: m_ctx.add_rule(f, symbol::null); break; case IS_QUERY: queries.push_back(f); break; default: msg << "formula is not in Horn fragment: " << mk_pp(g->form(i), m) << "\n"; TRACE("horn", tout << msg.str();); throw tactic_exception(msg.str().c_str()); } } if (queries.size() != 1 || m_is_simplify) { q = m.mk_fresh_const("query", m.mk_bool_sort()); register_predicate(q); for (unsigned i = 0; i < queries.size(); ++i) { f = mk_rule(queries[i].get(), q); m_ctx.add_rule(f, symbol::null); } queries.reset(); queries.push_back(q); filter_model_converter* mc1 = alloc(filter_model_converter, m); mc1->insert(to_app(q)->get_decl()); mc = mc1; } SASSERT(queries.size() == 1); q = queries[0].get(); if (m_is_simplify) { simplify(q, g, result, mc, pc); } else { verify(q, g, result, mc, pc); } } void verify(expr* q, goal_ref const& g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc) { lbool is_reachable = l_undef; try { is_reachable = m_ctx.query(q); } catch (default_exception& ex) { IF_VERBOSE(1, verbose_stream() << ex.msg() << "\n";); throw ex; } g->inc_depth(); bool produce_models = g->models_enabled(); bool produce_proofs = g->proofs_enabled(); result.push_back(g.get()); switch (is_reachable) { case l_true: { // goal is unsat if (produce_proofs) { proof_ref proof = m_ctx.get_proof(); pc = proof2proof_converter(m, proof); g->assert_expr(m.mk_false(), proof, 0); } else { g->assert_expr(m.mk_false()); } break; } case l_false: { // goal is sat g->reset(); if (produce_models) { model_ref md = m_ctx.get_model(); model_converter_ref mc2 = model2model_converter(&*md); if (mc) { mc = concat(mc.get(), mc2.get()); } else { mc = mc2; } } break; } case l_undef: // subgoal is unchanged. break; } TRACE("horn", g->display(tout);); SASSERT(g->is_well_sorted()); } void simplify(expr* q, goal_ref const& g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc) { expr_ref fml(m); func_decl* query_pred = to_app(q)->get_decl(); m_ctx.set_output_predicate(query_pred); m_ctx.get_rules(); // flush adding rules. apply_default_transformation(m_ctx); if (m_ctx.xform_slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); m_ctx.transform_rules(transformer); } expr_substitution sub(m); sub.insert(q, m.mk_false()); scoped_ptr rep = mk_default_expr_replacer(m); rep->set_substitution(&sub); g->inc_depth(); g->reset(); result.push_back(g.get()); datalog::rule_set const& rules = m_ctx.get_rules(); datalog::rule_set::iterator it = rules.begin(), end = rules.end(); for (; it != end; ++it) { datalog::rule* r = *it; m_ctx.get_rule_manager().to_formula(*r, fml); (*rep)(fml); g->assert_expr(fml); } } }; bool m_is_simplify; params_ref m_params; statistics m_stats; imp * m_imp; public: horn_tactic(bool t, ast_manager & m, params_ref const & p): m_is_simplify(t), m_params(p) { m_imp = alloc(imp, t, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(horn_tactic, m_is_simplify, m, m_params); } virtual ~horn_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { m_imp->collect_param_descrs(r); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } virtual void collect_statistics(statistics & st) const { m_imp->collect_statistics(st); st.copy(m_stats); } virtual void reset_statistics() { m_stats.reset(); m_imp->reset_statistics(); } virtual void cleanup() { ast_manager & m = m_imp->m; imp * d = m_imp; d->collect_statistics(m_stats); #pragma omp critical (tactic_cancel) { m_imp = 0; } dealloc(d); d = alloc(imp, m_is_simplify, m, m_params); #pragma omp critical (tactic_cancel) { m_imp = d; } } protected: virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_horn_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(horn_tactic, false, m, p)); } tactic * mk_horn_simplify_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(horn_tactic, true, m, p)); } z3-4.4.0/src/muz/fp/dl_cmds.cpp0000644000175000017500000004005312540347414015767 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dl_cmds.cpp Abstract: Datalog commands for SMT2 front-end. Author: Leonardo (leonardo) 2011-03-28 Notes: --*/ #include"cmd_context.h" #include"dl_cmds.h" #include"dl_external_relation.h" #include"dl_context.h" #include"dl_register_engine.h" #include"dl_decl_plugin.h" #include"dl_instruction.h" #include"dl_compiler.h" #include"dl_rule.h" #include"ast_pp.h" #include"parametric_cmd.h" #include"cancel_eh.h" #include"scoped_ctrl_c.h" #include"scoped_timer.h" #include"trail.h" #include"fixedpoint_params.hpp" #include struct dl_context { smt_params m_fparams; params_ref m_params_ref; fixedpoint_params m_params; cmd_context & m_cmd; datalog::register_engine m_register_engine; dl_collected_cmds* m_collected_cmds; unsigned m_ref_count; datalog::dl_decl_plugin* m_decl_plugin; scoped_ptr m_context; trail_stack m_trail; fixedpoint_params const& get_params() { init(); return m_context->get_params(); } dl_context(cmd_context & ctx, dl_collected_cmds* collected_cmds): m_params(m_params_ref), m_cmd(ctx), m_collected_cmds(collected_cmds), m_ref_count(0), m_decl_plugin(0), m_trail(*this) {} void inc_ref() { ++m_ref_count; } void dec_ref() { --m_ref_count; if (0 == m_ref_count) { dealloc(this); } } void init() { ast_manager& m = m_cmd.m(); if (!m_context) { m_context = alloc(datalog::context, m, m_register_engine, m_fparams, m_params_ref); } if (!m_decl_plugin) { symbol name("datalog_relation"); if (m.has_plugin(name)) { m_decl_plugin = static_cast(m_cmd.m().get_plugin(m.mk_family_id(name))); } else { m_decl_plugin = alloc(datalog::dl_decl_plugin); m.register_plugin(symbol("datalog_relation"), m_decl_plugin); } } } void reset() { m_context = 0; } void register_predicate(func_decl* pred, unsigned num_kinds, symbol const* kinds) { if (m_collected_cmds) { m_collected_cmds->m_rels.push_back(pred); m_trail.push(push_back_vector(m_collected_cmds->m_rels)); } dlctx().register_predicate(pred, false); dlctx().set_predicate_representation(pred, num_kinds, kinds); } void add_rule(expr * rule, symbol const& name, unsigned bound) { init(); if (m_collected_cmds) { expr_ref rl = m_context->bind_vars(rule, true); m_collected_cmds->m_rules.push_back(rl); m_collected_cmds->m_names.push_back(name); m_trail.push(push_back_vector(m_collected_cmds->m_rules)); m_trail.push(push_back_vector >(m_collected_cmds->m_names)); } else { m_context->add_rule(rule, name, bound); } } bool collect_query(expr* q) { if (m_collected_cmds) { expr_ref qr = m_context->bind_vars(q, false); m_collected_cmds->m_queries.push_back(qr); m_trail.push(push_back_vector(m_collected_cmds->m_queries)); return true; } else { return false; } } void push() { m_trail.push_scope(); dlctx().push(); } void pop() { m_trail.pop_scope(1); dlctx().pop(); } datalog::context & dlctx() { init(); return *m_context; } }; /** \brief rule command. It is also the owner of dl_context object. */ class dl_rule_cmd : public cmd { ref m_dl_ctx; mutable unsigned m_arg_idx; expr* m_t; symbol m_name; unsigned m_bound; public: dl_rule_cmd(dl_context * dl_ctx): cmd("rule"), m_dl_ctx(dl_ctx), m_arg_idx(0), m_t(0), m_bound(UINT_MAX) {} virtual char const * get_usage() const { return "(forall (q) (=> (and body) head)) :optional-name :optional-recursion-bound"; } virtual char const * get_descr(cmd_context & ctx) const { return "add a Horn rule."; } virtual unsigned get_arity() const { return VAR_ARITY; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { switch(m_arg_idx) { case 0: return CPK_EXPR; case 1: return CPK_SYMBOL; case 2: return CPK_UINT; default: return CPK_SYMBOL; } } virtual void set_next_arg(cmd_context & ctx, expr * t) { m_t = t; m_arg_idx++; } virtual void set_next_arg(cmd_context & ctx, symbol const & s) { m_name = s; m_arg_idx++; } virtual void set_next_arg(cmd_context & ctx, unsigned bound) { m_bound = bound; m_arg_idx++; } virtual void reset(cmd_context & ctx) { m_dl_ctx->reset(); prepare(ctx); } virtual void prepare(cmd_context& ctx) { m_arg_idx = 0; m_name = symbol::null; m_bound = UINT_MAX; } virtual void finalize(cmd_context & ctx) { } virtual void execute(cmd_context & ctx) { m_dl_ctx->add_rule(m_t, m_name, m_bound); } }; class dl_query_cmd : public parametric_cmd { ref m_dl_ctx; expr* m_target; public: dl_query_cmd(dl_context * dl_ctx): parametric_cmd("query"), m_dl_ctx(dl_ctx), m_target(0) { } virtual char const * get_usage() const { return "(exists (q) (and body))"; } virtual char const * get_main_descr() const { return "pose a query based on the Horn rules."; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { if (m_target == 0) return CPK_EXPR; return parametric_cmd::next_arg_kind(ctx); } virtual void set_next_arg(cmd_context & ctx, expr * t) { m_target = t; } virtual void prepare(cmd_context & ctx) { parametric_cmd::prepare(ctx); m_target = 0; } virtual void execute(cmd_context& ctx) { if (m_target == 0) { throw cmd_exception("invalid query command, argument expected"); } if (m_dl_ctx->collect_query(m_target)) { return; } datalog::context& dlctx = m_dl_ctx->dlctx(); set_background(ctx); dlctx.updt_params(m_params); unsigned timeout = m_dl_ctx->get_params().timeout(); cancel_eh eh(dlctx); bool query_exn = false; lbool status = l_undef; { IF_VERBOSE(10, verbose_stream() << "(query)\n";); scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); cmd_context::scoped_watch sw(ctx); try { status = dlctx.query(m_target); } catch (z3_error & ex) { ctx.regular_stream() << "(error \"query failed: " << ex.msg() << "\")" << std::endl; throw ex; } catch (z3_exception& ex) { ctx.regular_stream() << "(error \"query failed: " << ex.msg() << "\")" << std::endl; query_exn = true; } } switch (status) { case l_false: ctx.regular_stream() << "unsat\n"; print_certificate(ctx); break; case l_true: ctx.regular_stream() << "sat\n"; print_answer(ctx); print_certificate(ctx); break; case l_undef: if(dlctx.get_status() == datalog::BOUNDED){ ctx.regular_stream() << "bounded\n"; print_certificate(ctx); break; } ctx.regular_stream() << "unknown\n"; switch(dlctx.get_status()) { case datalog::INPUT_ERROR: ctx.regular_stream() << "input error\n"; break; case datalog::MEMOUT: ctx.regular_stream() << "memory bounds exceeded\n"; break; case datalog::TIMEOUT: ctx.regular_stream() << "timeout\n"; break; case datalog::APPROX: ctx.regular_stream() << "approximated relations\n"; break; case datalog::OK: SASSERT(query_exn); break; case datalog::CANCELED: ctx.regular_stream() << "canceled\n"; dlctx.display_profile(ctx.regular_stream()); break; default: UNREACHABLE(); break; } break; } dlctx.cleanup(); print_statistics(ctx); m_target = 0; } virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { m_dl_ctx->dlctx().collect_params(p); } private: void set_background(cmd_context& ctx) { datalog::context& dlctx = m_dl_ctx->dlctx(); ptr_vector::const_iterator it = ctx.begin_assertions(); ptr_vector::const_iterator end = ctx.end_assertions(); for (; it != end; ++it) { dlctx.assert_expr(*it); } } void print_answer(cmd_context& ctx) { if (m_dl_ctx->get_params().print_answer()) { datalog::context& dlctx = m_dl_ctx->dlctx(); ast_manager& m = ctx.m(); expr_ref query_result(dlctx.get_answer_as_formula(), m); sbuffer var_names; unsigned num_decls = 0; if (is_quantifier(m_target)) { num_decls = to_quantifier(m_target)->get_num_decls(); } ctx.display(ctx.regular_stream(), query_result, 0, num_decls, "X", var_names); ctx.regular_stream() << std::endl; } } void print_statistics(cmd_context& ctx) { if (m_dl_ctx->get_params().print_statistics()) { statistics st; datalog::context& dlctx = m_dl_ctx->dlctx(); unsigned long long max_mem = memory::get_max_used_memory(); unsigned long long mem = memory::get_allocation_size(); dlctx.collect_statistics(st); st.update("time", ctx.get_seconds()); st.update("memory", static_cast(mem)/static_cast(1024*1024)); st.update("max-memory", static_cast(max_mem)/static_cast(1024*1024)); st.display_smt2(ctx.regular_stream()); } } void print_certificate(cmd_context& ctx) { if (m_dl_ctx->get_params().print_certificate()) { datalog::context& dlctx = m_dl_ctx->dlctx(); dlctx.display_certificate(ctx.regular_stream()); ctx.regular_stream() << "\n"; } } }; class dl_declare_rel_cmd : public cmd { ref m_dl_ctx; unsigned m_arg_idx; mutable unsigned m_query_arg_idx; symbol m_rel_name; scoped_ptr m_domain; svector m_kinds; void ensure_domain(cmd_context& ctx) { if (!m_domain) m_domain = alloc(sort_ref_vector, ctx.m()); } public: dl_declare_rel_cmd(dl_context * dl_ctx): cmd("declare-rel"), m_dl_ctx(dl_ctx), m_domain(0) {} virtual char const * get_usage() const { return " ( ...) *"; } virtual char const * get_descr(cmd_context & ctx) const { return "declare new relation"; } virtual unsigned get_arity() const { return VAR_ARITY; } virtual void prepare(cmd_context & ctx) { m_arg_idx = 0; m_query_arg_idx = 0; m_domain = 0; m_kinds.reset(); } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { switch(m_query_arg_idx++) { case 0: return CPK_SYMBOL; // relation name case 1: return CPK_SORT_LIST; // arguments default: return CPK_SYMBOL; // optional representation specification } } virtual void set_next_arg(cmd_context & ctx, unsigned num, sort * const * slist) { ensure_domain(ctx); m_domain->append(num, slist); m_arg_idx++; } virtual void set_next_arg(cmd_context & ctx, symbol const & s) { if(m_arg_idx==0) { m_rel_name = s; } else { SASSERT(m_arg_idx>1); m_kinds.push_back(s); } m_arg_idx++; } virtual void execute(cmd_context & ctx) { if(m_arg_idx<2) { throw cmd_exception("at least 2 arguments expected"); } ensure_domain(ctx); ast_manager& m = ctx.m(); func_decl_ref pred( m.mk_func_decl(m_rel_name, m_domain->size(), m_domain->c_ptr(), m.mk_bool_sort()), m); ctx.insert(pred); m_dl_ctx->register_predicate(pred, m_kinds.size(), m_kinds.c_ptr()); m_domain = 0; } }; class dl_declare_var_cmd : public cmd { unsigned m_arg_idx; symbol m_var_name; sort* m_var_sort; ref m_dl_ctx; public: dl_declare_var_cmd(dl_context* dl_ctx): cmd("declare-var"), m_arg_idx(0), m_dl_ctx(dl_ctx) {} virtual char const * get_usage() const { return " "; } virtual char const * get_descr(cmd_context & ctx) const { return "declare constant as variable"; } virtual unsigned get_arity() const { return 2; } virtual void prepare(cmd_context & ctx) { m_arg_idx = 0; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { SASSERT(m_arg_idx <= 1); if (m_arg_idx == 0) { return CPK_SYMBOL; } return CPK_SORT; } virtual void set_next_arg(cmd_context & ctx, sort* s) { m_var_sort = s; ++m_arg_idx; } virtual void set_next_arg(cmd_context & ctx, symbol const & s) { m_var_name = s; ++m_arg_idx; } virtual void execute(cmd_context & ctx) { ast_manager& m = ctx.m(); func_decl_ref var(m.mk_func_decl(m_var_name, 0, static_cast(0), m_var_sort), m); ctx.insert(var); m_dl_ctx->dlctx().register_variable(var); } }; /** \brief fixedpoint-push command. */ class dl_push_cmd : public cmd { ref m_dl_ctx; public: dl_push_cmd(dl_context * dl_ctx): cmd("fixedpoint-push"), m_dl_ctx(dl_ctx) {} virtual char const * get_usage() const { return ""; } virtual char const * get_descr(cmd_context & ctx) const { return "push the fixedpoint context"; } virtual unsigned get_arity() const { return 0; } virtual void execute(cmd_context & ctx) { m_dl_ctx->push(); } }; /** \brief fixedpoint-pop command. */ class dl_pop_cmd : public cmd { ref m_dl_ctx; public: dl_pop_cmd(dl_context * dl_ctx): cmd("fixedpoint-pop"), m_dl_ctx(dl_ctx) {} virtual char const * get_usage() const { return ""; } virtual char const * get_descr(cmd_context & ctx) const { return "pop the fixedpoint context"; } virtual unsigned get_arity() const { return 0; } virtual void execute(cmd_context & ctx) { m_dl_ctx->pop(); } }; static void install_dl_cmds_aux(cmd_context& ctx, dl_collected_cmds* collected_cmds) { dl_context * dl_ctx = alloc(dl_context, ctx, collected_cmds); ctx.insert(alloc(dl_rule_cmd, dl_ctx)); ctx.insert(alloc(dl_query_cmd, dl_ctx)); ctx.insert(alloc(dl_declare_rel_cmd, dl_ctx)); ctx.insert(alloc(dl_declare_var_cmd, dl_ctx)); // #ifndef _EXTERNAL_RELEASE // TODO: we need these! #if 1 ctx.insert(alloc(dl_push_cmd, dl_ctx)); // not exposed to keep command-extensions simple. ctx.insert(alloc(dl_pop_cmd, dl_ctx)); #endif // #endif } void install_dl_cmds(cmd_context & ctx) { install_dl_cmds_aux(ctx, 0); } void install_dl_collect_cmds(dl_collected_cmds& collected_cmds, cmd_context & ctx) { install_dl_cmds_aux(ctx, &collected_cmds); } z3-4.4.0/src/muz/pdr/0000755000175000017500000000000012540347414014034 5ustar michaelmichaelz3-4.4.0/src/muz/pdr/pdr_util.cpp0000644000175000017500000004044412540347414016370 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdr_util.cpp Abstract: Utility functions for PDR. Author: Krystof Hoder (t-khoder) 2011-8-19. Revision History: Notes: --*/ #include #include "arith_simplifier_plugin.h" #include "array_decl_plugin.h" #include "ast_pp.h" #include "basic_simplifier_plugin.h" #include "bv_simplifier_plugin.h" #include "bool_rewriter.h" #include "dl_util.h" #include "for_each_expr.h" #include "smt_params.h" #include "model.h" #include "ref_vector.h" #include "rewriter.h" #include "rewriter_def.h" #include "util.h" #include "pdr_manager.h" #include "pdr_util.h" #include "arith_decl_plugin.h" #include "expr_replacer.h" #include "model_smt2_pp.h" #include "poly_rewriter.h" #include "poly_rewriter_def.h" #include "arith_rewriter.h" #include "scoped_proof.h" namespace pdr { unsigned ceil_log2(unsigned u) { if (u == 0) { return 0; } unsigned pow2 = next_power_of_two(u); return get_num_1bits(pow2-1); } std::string pp_cube(const ptr_vector& model, ast_manager& m) { return pp_cube(model.size(), model.c_ptr(), m); } std::string pp_cube(const expr_ref_vector& model, ast_manager& m) { return pp_cube(model.size(), model.c_ptr(), m); } std::string pp_cube(const app_ref_vector& model, ast_manager& m) { return pp_cube(model.size(), model.c_ptr(), m); } std::string pp_cube(const app_vector& model, ast_manager& m) { return pp_cube(model.size(), model.c_ptr(), m); } std::string pp_cube(unsigned sz, app * const * lits, ast_manager& m) { return pp_cube(sz, reinterpret_cast(lits), m); } std::string pp_cube(unsigned sz, expr * const * lits, ast_manager& m) { std::stringstream res; res << "("; expr * const * end = lits+sz; for (expr * const * it = lits; it!=end; it++) { res << mk_pp(*it, m); if (it+1!=end) { res << ", "; } } res << ")"; return res.str(); } void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) { ast_manager& m = fml.get_manager(); expr_ref_vector conjs(m); qe::flatten_and(fml, conjs); obj_map diseqs; expr* n, *lhs, *rhs; for (unsigned i = 0; i < conjs.size(); ++i) { if (m.is_not(conjs[i].get(), n) && m.is_eq(n, lhs, rhs)) { if (!m.is_value(rhs)) { std::swap(lhs, rhs); } if (!m.is_value(rhs)) { continue; } diseqs.insert_if_not_there2(lhs, 0)->get_data().m_value++; } } expr_substitution sub(m); unsigned orig_size = conjs.size(); unsigned num_deleted = 0; expr_ref val(m), tmp(m); proof_ref pr(m); pr = m.mk_asserted(m.mk_true()); obj_map::iterator it = diseqs.begin(); obj_map::iterator end = diseqs.end(); for (; it != end; ++it) { if (it->m_value >= threshold) { model.eval(it->m_key, val); sub.insert(it->m_key, val, pr); conjs.push_back(m.mk_eq(it->m_key, val)); num_deleted += it->m_value; } } if (orig_size < conjs.size()) { scoped_ptr rep = mk_expr_simp_replacer(m); rep->set_substitution(&sub); for (unsigned i = 0; i < orig_size; ++i) { tmp = conjs[i].get(); (*rep)(tmp); if (m.is_true(tmp)) { conjs[i] = conjs.back(); SASSERT(orig_size <= conjs.size()); conjs.pop_back(); SASSERT(orig_size <= 1 + conjs.size()); if (i + 1 == orig_size) { // no-op. } else if (orig_size <= conjs.size()) { // no-op } else { SASSERT(orig_size == 1 + conjs.size()); --orig_size; --i; } } else { conjs[i] = tmp; } } IF_VERBOSE(2, verbose_stream() << "Deleted " << num_deleted << " disequalities " << conjs.size() << " conjuncts\n";); } fml = m.mk_and(conjs.size(), conjs.c_ptr()); } class test_diff_logic { ast_manager& m; arith_util a; bv_util bv; bool m_is_dl; bool m_test_for_utvpi; bool is_numeric(expr* e) const { if (a.is_numeral(e)) { return true; } expr* cond, *th, *el; if (m.is_ite(e, cond, th, el)) { return is_numeric(th) && is_numeric(el); } return false; } bool is_arith_expr(expr *e) const { return is_app(e) && a.get_family_id() == to_app(e)->get_family_id(); } bool is_offset(expr* e) const { if (a.is_numeral(e)) { return true; } expr* cond, *th, *el, *e1, *e2; if (m.is_ite(e, cond, th, el)) { return is_offset(th) && is_offset(el); } // recognize offsets. if (a.is_add(e, e1, e2)) { if (is_numeric(e1)) { return is_offset(e2); } if (is_numeric(e2)) { return is_offset(e1); } return false; } if (m_test_for_utvpi) { if (a.is_mul(e, e1, e2)) { if (is_minus_one(e1)) { return is_offset(e2); } if (is_minus_one(e2)) { return is_offset(e1); } } } return !is_arith_expr(e); } bool is_minus_one(expr const * e) const { rational r; return a.is_numeral(e, r) && r.is_minus_one(); } bool test_ineq(expr* e) const { SASSERT(a.is_le(e) || a.is_ge(e) || m.is_eq(e)); SASSERT(to_app(e)->get_num_args() == 2); expr * lhs = to_app(e)->get_arg(0); expr * rhs = to_app(e)->get_arg(1); if (is_offset(lhs) && is_offset(rhs)) return true; if (!is_numeric(rhs)) std::swap(lhs, rhs); if (!is_numeric(rhs)) return false; // lhs can be 'x' or '(+ x (* -1 y))' if (is_offset(lhs)) return true; expr* arg1, *arg2; if (!a.is_add(lhs, arg1, arg2)) return false; // x if (m_test_for_utvpi) { return is_offset(arg1) && is_offset(arg2); } if (is_arith_expr(arg1)) std::swap(arg1, arg2); if (is_arith_expr(arg1)) return false; // arg2: (* -1 y) expr* m1, *m2; if (!a.is_mul(arg2, m1, m2)) return false; return is_minus_one(m1) && is_offset(m2); } bool test_eq(expr* e) const { expr* lhs, *rhs; VERIFY(m.is_eq(e, lhs, rhs)); if (!a.is_int_real(lhs)) { return true; } if (a.is_numeral(lhs) || a.is_numeral(rhs)) { return test_ineq(e); } return test_term(lhs) && test_term(rhs) && !a.is_mul(lhs) && !a.is_mul(rhs); } bool test_term(expr* e) const { if (m.is_bool(e)) { return true; } if (a.is_numeral(e)) { return true; } if (is_offset(e)) { return true; } expr* lhs, *rhs; if (a.is_add(e, lhs, rhs)) { if (!a.is_numeral(lhs)) { std::swap(lhs, rhs); } return a.is_numeral(lhs) && is_offset(rhs); } if (a.is_mul(e, lhs, rhs)) { return is_minus_one(lhs) || is_minus_one(rhs); } return false; } bool is_non_arith_or_basic(expr* e) { if (!is_app(e)) { return false; } family_id fid = to_app(e)->get_family_id(); if (fid == null_family_id && !m.is_bool(e) && to_app(e)->get_num_args() > 0) { return true; } return fid != m.get_basic_family_id() && fid != null_family_id && fid != a.get_family_id() && fid != bv.get_family_id(); } public: test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true), m_test_for_utvpi(false) {} void test_for_utvpi() { m_test_for_utvpi = true; } void operator()(expr* e) { if (!m_is_dl) { return; } if (a.is_le(e) || a.is_ge(e)) { m_is_dl = test_ineq(e); } else if (m.is_eq(e)) { m_is_dl = test_eq(e); } else if (is_non_arith_or_basic(e)) { m_is_dl = false; } else if (is_app(e)) { app* a = to_app(e); for (unsigned i = 0; m_is_dl && i < a->get_num_args(); ++i) { m_is_dl = test_term(a->get_arg(i)); } } if (!m_is_dl) { char const* msg = "non-diff: "; if (m_test_for_utvpi) { msg = "non-utvpi: "; } IF_VERBOSE(1, verbose_stream() << msg << mk_pp(e, m) << "\n";); } } bool is_dl() const { return m_is_dl; } }; bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { test_diff_logic test(m); expr_fast_mark1 mark; for (unsigned i = 0; i < num_fmls; ++i) { quick_for_each_expr(test, mark, fmls[i]); } return test.is_dl(); } bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { test_diff_logic test(m); test.test_for_utvpi(); expr_fast_mark1 mark; for (unsigned i = 0; i < num_fmls; ++i) { quick_for_each_expr(test, mark, fmls[i]); } return test.is_dl(); } class arith_normalizer : public poly_rewriter { ast_manager& m; arith_util m_util; enum op_kind { LE, GE, EQ }; public: arith_normalizer(ast_manager& m, params_ref const& p = params_ref()): poly_rewriter(m, p), m(m), m_util(m) {} br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { br_status st = BR_FAILED; if (m.is_eq(f)) { SASSERT(num_args == 2); return mk_eq_core(args[0], args[1], result); } if (f->get_family_id() != get_fid()) { return st; } switch (f->get_decl_kind()) { case OP_NUM: st = BR_FAILED; break; case OP_IRRATIONAL_ALGEBRAIC_NUM: st = BR_FAILED; break; case OP_LE: SASSERT(num_args == 2); st = mk_le_core(args[0], args[1], result); break; case OP_GE: SASSERT(num_args == 2); st = mk_ge_core(args[0], args[1], result); break; case OP_LT: SASSERT(num_args == 2); st = mk_lt_core(args[0], args[1], result); break; case OP_GT: SASSERT(num_args == 2); st = mk_gt_core(args[0], args[1], result); break; default: st = BR_FAILED; break; } return st; } private: br_status mk_eq_core(expr* arg1, expr* arg2, expr_ref& result) { return mk_le_ge_eq_core(arg1, arg2, EQ, result); } br_status mk_le_core(expr* arg1, expr* arg2, expr_ref& result) { return mk_le_ge_eq_core(arg1, arg2, LE, result); } br_status mk_ge_core(expr* arg1, expr* arg2, expr_ref& result) { return mk_le_ge_eq_core(arg1, arg2, GE, result); } br_status mk_lt_core(expr* arg1, expr* arg2, expr_ref& result) { result = m.mk_not(m_util.mk_ge(arg1, arg2)); return BR_REWRITE2; } br_status mk_gt_core(expr* arg1, expr* arg2, expr_ref& result) { result = m.mk_not(m_util.mk_le(arg1, arg2)); return BR_REWRITE2; } br_status mk_le_ge_eq_core(expr* arg1, expr* arg2, op_kind kind, expr_ref& result) { if (m_util.is_real(arg1)) { numeral g(0); get_coeffs(arg1, g); get_coeffs(arg2, g); if (!g.is_one() && !g.is_zero()) { SASSERT(g.is_pos()); expr_ref new_arg1 = rdiv_polynomial(arg1, g); expr_ref new_arg2 = rdiv_polynomial(arg2, g); switch(kind) { case LE: result = m_util.mk_le(new_arg1, new_arg2); return BR_DONE; case GE: result = m_util.mk_ge(new_arg1, new_arg2); return BR_DONE; case EQ: result = m_util.mk_eq(new_arg1, new_arg2); return BR_DONE; } } } return BR_FAILED; } void update_coeff(numeral const& r, numeral& g) { if (g.is_zero() || abs(r) < g) { g = abs(r); } } void get_coeffs(expr* e, numeral& g) { rational r; unsigned sz; expr* const* args = get_monomials(e, sz); for (unsigned i = 0; i < sz; ++i) { expr* arg = args[i]; if (!m_util.is_numeral(arg, r)) { get_power_product(arg, r); } update_coeff(r, g); } } expr_ref rdiv_polynomial(expr* e, numeral const& g) { rational r; SASSERT(g.is_pos()); SASSERT(!g.is_one()); expr_ref_vector monomes(m); unsigned sz; expr* const* args = get_monomials(e, sz); for (unsigned i = 0; i < sz; ++i) { expr* arg = args[i]; if (m_util.is_numeral(arg, r)) { monomes.push_back(m_util.mk_numeral(r/g, false)); } else { expr* p = get_power_product(arg, r); r /= g; if (r.is_one()) { monomes.push_back(p); } else { monomes.push_back(m_util.mk_mul(m_util.mk_numeral(r, false), p)); } } } expr_ref result(m); mk_add(monomes.size(), monomes.c_ptr(), result); return result; } }; struct arith_normalizer_cfg: public default_rewriter_cfg { arith_normalizer m_r; bool rewrite_patterns() const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { return m_r.mk_app_core(f, num, args, result); } arith_normalizer_cfg(ast_manager & m, params_ref const & p):m_r(m,p) {} }; class arith_normalizer_star : public rewriter_tpl { arith_normalizer_cfg m_cfg; public: arith_normalizer_star(ast_manager & m, params_ref const & p): rewriter_tpl(m, false, m_cfg), m_cfg(m, p) {} }; void normalize_arithmetic(expr_ref& t) { ast_manager& m = t.get_manager(); scoped_no_proof _sp(m); params_ref p; arith_normalizer_star rw(m, p); expr_ref tmp(m); rw(t, tmp); t = tmp; } } template class rewriter_tpl; z3-4.4.0/src/muz/pdr/pdr_sym_mux.h0000644000175000017500000001636412540347414016565 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sym_mux.h Abstract: A symbol multiplexer that helps with having multiple versions of each of a set of symbols. Author: Krystof Hoder (t-khoder) 2011-9-8. Revision History: --*/ #ifndef _SYM_MUX_H_ #define _SYM_MUX_H_ #include "ast.h" #include "map.h" #include "vector.h" class model_core; namespace pdr { class sym_mux { public: typedef ptr_vector app_vector; typedef ptr_vector decl_vector; private: typedef obj_map sym2u; typedef obj_map sym2dv; typedef obj_map sym2sym; typedef obj_map sym2pred; typedef hashtable symbols; ast_manager & m; mutable ast_ref_vector m_ref_holder; mutable expr_mark m_visited; mutable unsigned m_next_sym_suffix_idx; mutable symbols m_used_suffixes; /** Here we have default suffixes for each of the variants */ mutable vector m_suffixes; /** Primary symbol is the 0-th variant. This member maps from primary symbol to vector of all its variants (including the primary variant). */ sym2dv m_prim2all; /** For each symbol contains its variant index */ mutable sym2u m_sym2idx; /** For each symbol contains its primary variant */ mutable sym2sym m_sym2prim; /** Maps prefixes passed to the create_tuple to the primary symbol created from it. */ sym2pred m_prefix2prim; /** Maps pripary symbols to prefixes that were used to create them. */ sym2sym m_prim2prefix; decl_vector m_prim_preds; obj_hashtable m_non_model_syms; struct formula_checker; struct conv_rewriter_cfg; struct shifting_rewriter_cfg; class decl_idx_comparator; class hmg_checker; class nonmodel_sym_checker; class index_renamer_cfg; class index_collector; class variable_collector; std::string get_suffix(unsigned i) const; void ensure_tuple_size(func_decl * prim, unsigned sz) const; expr_ref isolate_o_idx(expr* e, unsigned idx) const; public: sym_mux(ast_manager & m, const vector & suffixes); ast_manager & get_manager() const { return m; } bool is_muxed(func_decl * sym) const { return m_sym2idx.contains(sym); } bool try_get_index(func_decl * sym, unsigned & idx) const { return m_sym2idx.find(sym,idx); } bool has_index(func_decl * sym, unsigned idx) const { unsigned actual_idx; return try_get_index(sym, actual_idx) && idx==actual_idx; } /** Return primary symbol. sym must be muxed. */ func_decl * get_primary(func_decl * sym) const { func_decl * prim; TRUSTME(m_sym2prim.find(sym, prim)); return prim; } /** Return primary symbol created from prefix, or 0 if the prefix was never used. */ func_decl * try_get_primary_by_prefix(func_decl* prefix) const { func_decl * res; if(!m_prefix2prim.find(prefix, res)) { return 0; } return res; } /** Return symbol created from prefix, or 0 if the prefix was never used. */ func_decl * try_get_by_prefix(func_decl* prefix, unsigned idx) const { func_decl * prim = try_get_primary_by_prefix(prefix); if(!prim) { return 0; } return conv(prim, 0, idx); } /** Marks symbol as non-model which means it will not appear in models collected by get_muxed_cube_from_model function. This is to take care of auxiliary symbols introduced by the disjunction relations to relativize lemmas coming from disjuncts. */ void mark_as_non_model(func_decl * sym) { SASSERT(is_muxed(sym)); m_non_model_syms.insert(get_primary(sym)); } func_decl * get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx, unsigned arity, sort * const * domain, sort * range); bool is_muxed_lit(expr * e, unsigned idx) const; bool is_non_model_sym(func_decl * s) const { return is_muxed(s) && m_non_model_syms.contains(get_primary(s)); } /** Create a multiplexed tuple of propositional constants. Symbols may be suplied in the tuple vector, those beyond the size of the array and those with corresponding positions assigned to zero will be created using prefix. Tuple length must be at least one. */ void create_tuple(func_decl* prefix, unsigned arity, sort * const * domain, sort * range, unsigned tuple_length, decl_vector & tuple); /** Return true if the only multiplexed symbols which e contains are of index idx. */ bool is_homogenous_formula(expr * e, unsigned idx) const; bool is_homogenous(const expr_ref_vector & vect, unsigned idx) const; /** Return true if all multiplexed symbols which e contains are of one index. */ bool is_homogenous_formula(expr * e) const; /** Return true if expression e contains a muxed symbol of index idx. */ bool contains(expr * e, unsigned idx) const; /** Collect indices used in expression. */ void collect_indices(expr* e, unsigned_vector& indices) const; /** Collect used variables of each index. */ void collect_variables(expr* e, vector >& vars) const; /** Convert symbol sym which has to be of src_idx variant into variant tgt_idx. */ func_decl * conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx) const; /** Convert src_idx symbols in formula f variant into tgt_idx. If homogenous is true, formula cannot contain symbols of other variants. */ void conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous=true) const; void conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx, expr_ref_vector & res) const; /** Shifts the muxed symbols in f by dist. Dist can be negative, but it should never shift symbol index to a negative value. */ void shift_formula(expr * f, int dist, expr_ref & res) const; /** Remove from vect literals (atoms or negations of atoms) of symbols that contain multiplexed symbols with indexes other than idx. Each of the literals can contain only symbols multiplexed with one index (this trivially holds if the literals are propositional). Order of elements in vect may be modified by this function */ void filter_idx(expr_ref_vector & vect, unsigned idx) const; /** Partition literals into o_literals and others. */ void partition_o_idx(expr_ref_vector const& lits, expr_ref_vector& o_lits, expr_ref_vector& other, unsigned idx) const; bool has_nonmodel_symbol(expr * e) const; void filter_non_model_lits(expr_ref_vector & vect) const; func_decl * const * begin_prim_preds() const { return m_prim_preds.begin(); } func_decl * const * end_prim_preds() const { return m_prim_preds.end(); } void get_muxed_cube_from_model(const model_core & model, expr_ref_vector & res) const; std::string pp_model(const model_core & mdl) const; }; } #endif z3-4.4.0/src/muz/pdr/pdr_prop_solver.h0000644000175000017500000001023512540347414017425 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: prop_solver.h Abstract: SAT solver abstraction for PDR. Author: Krystof Hoder (t-khoder) 2011-8-17. Revision History: --*/ #ifndef _PROP_SOLVER_H_ #define _PROP_SOLVER_H_ #include #include #include #include "ast.h" #include "obj_hashtable.h" #include "smt_kernel.h" #include "util.h" #include "vector.h" #include "pdr_manager.h" #include "pdr_smt_context_manager.h" namespace pdr { class prop_solver { private: smt_params& m_fparams; ast_manager& m; manager& m_pm; symbol m_name; scoped_ptr m_ctx; decl_vector m_level_preds; app_ref_vector m_pos_level_atoms; // atoms used to identify level app_ref_vector m_neg_level_atoms; // obj_hashtable m_level_atoms_set; app_ref_vector m_proxies; // predicates for assumptions expr_ref_vector* m_core; model_ref* m_model; expr_ref_vector* m_consequences; bool m_subset_based_core; bool m_assumes_level; bool m_use_farkas; func_decl_set m_aux_symbols; bool m_in_level; unsigned m_current_level; // set when m_in_level /** Add level atoms activating certain level into a vector */ void push_level_atoms(unsigned level, expr_ref_vector & tgt) const; void ensure_level(unsigned lvl); class safe_assumptions; void extract_theory_core(safe_assumptions& assumptions); void extract_subset_core(safe_assumptions& assumptions); lbool check_safe_assumptions( safe_assumptions& assumptions, expr_ref_vector const& atoms); public: prop_solver(pdr::manager& pm, symbol const& name); /** return true is s is a symbol introduced by prop_solver */ bool is_aux_symbol(func_decl * s) const { return m_aux_symbols.contains(s) || m_ctx->is_aux_predicate(s); } void set_core(expr_ref_vector* core) { m_core = core; } void set_model(model_ref* mdl) { m_model = mdl; } void set_subset_based_core(bool f) { m_subset_based_core = f; } void set_consequences(expr_ref_vector* consequences) { m_consequences = consequences; } bool assumes_level() const { return m_assumes_level; } void add_level(); unsigned level_cnt() const; class scoped_level { bool& m_lev; public: scoped_level(prop_solver& ps, unsigned lvl):m_lev(ps.m_in_level) { SASSERT(!m_lev); m_lev = true; ps.m_current_level = lvl; } ~scoped_level() { m_lev = false; } }; void set_use_farkas(bool f) { m_use_farkas = f; } bool get_use_farkas() const { return m_use_farkas; } void add_formula(expr * form); void add_level_formula(expr * form, unsigned level); /** * Return true iff conjunction of atoms is consistent with the current state of * the solver. * * If the conjunction of atoms is inconsistent with the solver state and core is non-zero, * core will contain an unsatisfiable core of atoms. * * If the conjunction of atoms is consistent with the solver state and o_model is non-zero, * o_model will contain the "o" literals true in the assignment. */ lbool check_assumptions(const expr_ref_vector & atoms); lbool check_conjunction_as_assumptions(expr * conj); /** * Like check_assumptions, except it also asserts an extra formula */ lbool check_assumptions_and_formula( const expr_ref_vector & atoms, expr * form); void collect_statistics(statistics& st) const; void reset_statistics(); }; } #endif z3-4.4.0/src/muz/pdr/pdr_dl_interface.h0000644000175000017500000000307212540347414017473 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdr_dl_interface.h Abstract: SMT2 interface for the datalog PDR Author: Krystof Hoder (t-khoder) 2011-9-22. Revision History: --*/ #ifndef _PDR_DL_INTERFACE_H_ #define _PDR_DL_INTERFACE_H_ #include "lbool.h" #include "dl_rule.h" #include "dl_rule_set.h" #include "dl_util.h" #include "dl_engine_base.h" #include "statistics.h" namespace datalog { class context; } namespace pdr { class context; class dl_interface : public datalog::engine_base { datalog::context& m_ctx; datalog::rule_set m_pdr_rules; datalog::rule_set m_old_rules; context* m_context; obj_map m_pred2slice; ast_ref_vector m_refs; void check_reset(); public: dl_interface(datalog::context& ctx); ~dl_interface(); virtual lbool query(expr* query); virtual void cancel(); virtual void cleanup(); virtual void display_certificate(std::ostream& out) const; virtual void collect_statistics(statistics& st) const; virtual void reset_statistics(); virtual expr_ref get_answer(); virtual unsigned get_num_levels(func_decl* pred); virtual expr_ref get_cover_delta(int level, func_decl* pred); virtual void add_cover(int level, func_decl* pred, expr* property); virtual void updt_params(); virtual model_ref get_model(); virtual proof_ref get_proof(); }; } #endif z3-4.4.0/src/muz/pdr/pdr_farkas_learner.h0000644000175000017500000000655212540347414020041 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdr_farkas_learner.h Abstract: SMT2 interface for the datalog PDR Author: Krystof Hoder (t-khoder) 2011-11-1. Revision History: --*/ #ifndef _PDR_FARKAS_LEARNER_H_ #define _PDR_FARKAS_LEARNER_H_ #include "arith_decl_plugin.h" #include "ast_translation.h" #include "bv_decl_plugin.h" #include "smt_kernel.h" #include "bool_rewriter.h" #include "pdr_util.h" #include "smt_params.h" #include "tactic.h" namespace pdr { class farkas_learner { class farkas_collector; class constant_replacer_cfg; class equality_expander_cfg; class constr; typedef obj_hashtable expr_set; smt_params m_proof_params; ast_manager m_pr; scoped_ptr m_ctx; constr* m_constr; // // true: produce a combined constraint by applying Farkas coefficients. // false: produce a conjunction of the negated literals from the theory lemmas. // bool m_combine_farkas_coefficients; static smt_params get_proof_params(smt_params& orig_params); // // all ast objects passed to private functions have m_proof_mgs as their ast_manager // ast_translation p2o; /** Translate expression from inner ast_manager to outer one */ ast_translation o2p; /** Translate expression from outer ast_manager to inner one */ /** All ast opbjects here are in the m_proof_mgs */ void get_lemma_guesses_internal(proof * p, expr* A, expr * B, expr_ref_vector& lemmas); bool farkas2lemma(proof * fstep, expr* A, expr * B, expr_ref& res); void combine_constraints(unsigned cnt, app * const * constrs, rational const * coeffs, expr_ref& res); bool try_ensure_lemma_in_language(expr_ref& lemma, expr* A, const func_decl_set& lang); bool is_farkas_lemma(ast_manager& m, expr* e); void get_asserted(proof* p, expr_set const& bs, ast_mark& b_closed, obj_hashtable& lemma_set, expr_ref_vector& lemmas); bool is_pure_expr(func_decl_set const& symbs, expr* e) const; static void test(); public: farkas_learner(smt_params& params, ast_manager& m); ~farkas_learner(); /** All ast objects have the ast_manager which was passed as an argument to the constructor (i.e. m_outer_mgr) B is a conjunction of literals. A && B is unsat, equivalently A => ~B is valid Find a weakened B' such that A && B' is unsat and B' uses vocabulary (and constants) in common with A. return lemmas to weaken B. */ bool get_lemma_guesses(expr * A, expr * B, expr_ref_vector& lemmas); /** Traverse a proof and retrieve lemmas using the vocabulary from bs. */ void get_lemmas(proof* root, expr_set const& bs, expr_ref_vector& lemmas); /** Traverse a proof and retrieve consequences of A that are used to establish ~B. The assumption is that: A => \/ ~consequences[i] and \/ ~consequences[i] => ~B e.g., the second implication can be rewritten as: B => /\ consequences[i] */ void get_consequences(proof* root, expr_set const& bs, expr_ref_vector& consequences); /** \brief Simplify lemmas using subsumption. */ void simplify_lemmas(expr_ref_vector& lemmas); void collect_statistics(statistics& st) const; }; } #endif z3-4.4.0/src/muz/pdr/pdr_util.h0000644000175000017500000000371312540347414016033 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdr_util.h Abstract: Utility functions for PDR. Author: Krystof Hoder (t-khoder) 2011-8-19. Revision History: --*/ #ifndef _PDR_UTIL_H_ #define _PDR_UTIL_H_ #include "ast.h" #include "ast_pp.h" #include "obj_hashtable.h" #include "ref_vector.h" #include "simplifier.h" #include "trace.h" #include "vector.h" #include "arith_decl_plugin.h" #include "array_decl_plugin.h" #include "bv_decl_plugin.h" class model; class model_core; namespace pdr { /** * Return the ceiling of base 2 logarithm of a number, * or zero if the nmber is zero. */ unsigned ceil_log2(unsigned u); typedef ptr_vector app_vector; typedef ptr_vector decl_vector; typedef obj_hashtable func_decl_set; std::string pp_cube(const ptr_vector& model, ast_manager& manager); std::string pp_cube(const expr_ref_vector& model, ast_manager& manager); std::string pp_cube(const ptr_vector& model, ast_manager& manager); std::string pp_cube(const app_ref_vector& model, ast_manager& manager); std::string pp_cube(unsigned sz, app * const * lits, ast_manager& manager); std::string pp_cube(unsigned sz, expr * const * lits, ast_manager& manager); /** \brief replace variables that are used in many disequalities by an equality using the model. Assumption: the model satisfies the conjunctions. */ void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml); /** \brief normalize coefficients in polynomials so that least coefficient is 1. */ void normalize_arithmetic(expr_ref& t); /** \brief determine if formulas belong to difference logic or UTVPI fragment. */ bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); } #endif z3-4.4.0/src/muz/pdr/pdr_farkas_learner.cpp0000644000175000017500000010333312540347414020367 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdr_farkas_learner.cpp Abstract: Proviced abstract interface and some inplementations of algorithms for strenghtning lemmas Author: Krystof Hoder (t-khoder) 2011-11-1. Revision History: --*/ #include "ast_smt2_pp.h" #include "array_decl_plugin.h" #include "bool_rewriter.h" #include "dl_decl_plugin.h" #include "for_each_expr.h" #include "dl_util.h" #include "rewriter.h" #include "rewriter_def.h" #include "pdr_util.h" #include "pdr_farkas_learner.h" #include "th_rewriter.h" #include "ast_ll_pp.h" #include "arith_bounds_tactic.h" #include "proof_utils.h" #include "reg_decl_plugins.h" namespace pdr { class farkas_learner::constr { ast_manager& m; arith_util a; app_ref_vector m_ineqs; vector m_coeffs; unsigned m_time; unsigned_vector m_roots, m_size, m_his, m_reps, m_ts; void mk_coerce(expr*& e1, expr*& e2) { if (a.is_int(e1) && a.is_real(e2)) { e1 = a.mk_to_real(e1); } else if (a.is_int(e2) && a.is_real(e1)) { e2 = a.mk_to_real(e2); } } app* mk_add(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_add(e1, e2); } app* mk_mul(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_mul(e1, e2); } app* mk_le(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_le(e1, e2); } app* mk_ge(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_ge(e1, e2); } app* mk_gt(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_gt(e1, e2); } app* mk_lt(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_lt(e1, e2); } void mul(rational const& c, expr* e, expr_ref& res) { expr_ref tmp(m); if (c.is_one()) { tmp = e; } else { tmp = mk_mul(a.mk_numeral(c, c.is_int() && a.is_int(e)), e); } res = mk_add(res, tmp); } bool is_int_sort(app* c) { SASSERT(m.is_eq(c) || a.is_le(c) || a.is_lt(c) || a.is_gt(c) || a.is_ge(c)); SASSERT(a.is_int(c->get_arg(0)) || a.is_real(c->get_arg(0))); return a.is_int(c->get_arg(0)); } bool is_int_sort() { SASSERT(!m_ineqs.empty()); return is_int_sort(m_ineqs[0].get()); } void normalize_coeffs() { rational l(1); for (unsigned i = 0; i < m_coeffs.size(); ++i) { l = lcm(l, denominator(m_coeffs[i])); } if (!l.is_one()) { for (unsigned i = 0; i < m_coeffs.size(); ++i) { m_coeffs[i] *= l; } } } app* mk_one() { return a.mk_numeral(rational(1), true); } app* fix_sign(bool is_pos, app* c) { expr* x, *y; SASSERT(m.is_eq(c) || a.is_le(c) || a.is_lt(c) || a.is_gt(c) || a.is_ge(c)); bool is_int = is_int_sort(c); if (is_int && is_pos && (a.is_lt(c, x, y) || a.is_gt(c, y, x))) { return mk_le(mk_add(x, mk_one()), y); } if (is_int && !is_pos && (a.is_le(c, x, y) || a.is_ge(c, y, x))) { // !(x <= y) <=> x > y <=> x >= y + 1 return mk_ge(x, mk_add(y, mk_one())); } if (is_pos) { return c; } if (a.is_le(c, x, y)) return mk_gt(x, y); if (a.is_lt(c, x, y)) return mk_ge(x, y); if (a.is_ge(c, x, y)) return mk_lt(x, y); if (a.is_gt(c, x, y)) return mk_le(x, y); UNREACHABLE(); return c; } public: constr(ast_manager& m) : m(m), a(m), m_ineqs(m), m_time(0) {} void reset() { m_ineqs.reset(); m_coeffs.reset(); } /** add a multiple of constraint c to the current constr */ void add(rational const & coef, app * c) { bool is_pos = true; expr* e; while (m.is_not(c, e)) { is_pos = !is_pos; c = to_app(e); } if (!coef.is_zero() && !m.is_true(c)) { m_coeffs.push_back(coef); m_ineqs.push_back(fix_sign(is_pos, c)); } } // // Extract the complement of premises multiplied by Farkas coefficients. // void get(expr_ref& res) { if (m_coeffs.empty()) { res = m.mk_false(); return; } bool is_int = is_int_sort(); if (is_int) { normalize_coeffs(); } TRACE("pdr", for (unsigned i = 0; i < m_coeffs.size(); ++i) { tout << m_coeffs[i] << ": " << mk_pp(m_ineqs[i].get(), m) << "\n"; } ); res = extract_consequence(0, m_coeffs.size()); #if 1 // partition equalities into variable disjoint sets. // take the conjunction of these instead of the // linear combination. partition_ineqs(); expr_ref_vector lits(m); unsigned lo = 0; for (unsigned i = 0; i < m_his.size(); ++i) { unsigned hi = m_his[i]; lits.push_back(extract_consequence(lo, hi)); lo = hi; } res = qe::mk_or(lits); IF_VERBOSE(2, { if (lits.size() > 1) { verbose_stream() << "combined lemma: " << mk_pp(res, m) << "\n"; } }); #endif } private: // partition inequalities into variable disjoint sets. void partition_ineqs() { m_reps.reset(); m_his.reset(); ++m_time; for (unsigned i = 0; i < m_ineqs.size(); ++i) { m_reps.push_back(process_term(m_ineqs[i].get())); } unsigned head = 0; while (head < m_ineqs.size()) { unsigned r = find(m_reps[head]); unsigned tail = head; for (unsigned i = head+1; i < m_ineqs.size(); ++i) { if (find(m_reps[i]) == r) { ++tail; if (tail != i) { SASSERT(tail < i); std::swap(m_reps[tail], m_reps[i]); app_ref tmp(m); tmp = m_ineqs[i].get(); m_ineqs[i] = m_ineqs[tail].get(); m_ineqs[tail] = tmp; std::swap(m_coeffs[tail], m_coeffs[i]); } } } head = tail + 1; m_his.push_back(head); } } unsigned find(unsigned idx) { if (m_ts.size() <= idx) { m_roots.resize(idx+1); m_size.resize(idx+1); m_ts.resize(idx+1); m_roots[idx] = idx; m_ts[idx] = m_time; m_size[idx] = 1; return idx; } if (m_ts[idx] != m_time) { m_size[idx] = 1; m_ts[idx] = m_time; m_roots[idx] = idx; return idx; } while (true) { if (m_roots[idx] == idx) { return idx; } idx = m_roots[idx]; } } void merge(unsigned i, unsigned j) { i = find(i); j = find(j); if (i == j) { return; } if (m_size[i] > m_size[j]) { std::swap(i, j); } m_roots[i] = j; m_size[j] += m_size[i]; } unsigned process_term(expr* e) { unsigned r = e->get_id(); ptr_vector todo; ast_mark mark; todo.push_back(e); while (!todo.empty()) { e = todo.back(); todo.pop_back(); if (mark.is_marked(e)) { continue; } mark.mark(e, true); if (is_uninterp(e)) { merge(r, e->get_id()); } if (is_app(e)) { app* a = to_app(e); for (unsigned i = 0; i < a->get_num_args(); ++i) { todo.push_back(a->get_arg(i)); } } } return r; } expr_ref extract_consequence(unsigned lo, unsigned hi) { bool is_int = is_int_sort(); app_ref zero(a.mk_numeral(rational::zero(), is_int), m); expr_ref res(m); res = zero; bool is_strict = false; bool is_eq = true; expr* x, *y; for (unsigned i = lo; i < hi; ++i) { app* c = m_ineqs[i].get(); if (m.is_eq(c, x, y)) { mul(m_coeffs[i], x, res); mul(-m_coeffs[i], y, res); } if (a.is_lt(c, x, y) || a.is_gt(c, y, x)) { mul(m_coeffs[i], x, res); mul(-m_coeffs[i], y, res); is_strict = true; is_eq = false; } if (a.is_le(c, x, y) || a.is_ge(c, y, x)) { mul(m_coeffs[i], x, res); mul(-m_coeffs[i], y, res); is_eq = false; } } zero = a.mk_numeral(rational::zero(), a.is_int(res)); if (is_eq) { res = m.mk_eq(res, zero); } else if (is_strict) { res = mk_lt(res, zero); } else { res = mk_le(res, zero); } res = m.mk_not(res); th_rewriter rw(m); params_ref params; params.set_bool("gcd_rounding", true); rw.updt_params(params); proof_ref pr(m); expr_ref result(m); rw(res, result, pr); fix_dl(result); return result; } // patch: swap addends to make static // features recognize difference constraint. void fix_dl(expr_ref& r) { expr* e; if (m.is_not(r, e)) { r = e; fix_dl(r); r = m.mk_not(r); return; } expr* e1, *e2, *e3, *e4; if ((m.is_eq(r, e1, e2) || a.is_lt(r, e1, e2) || a.is_gt(r, e1, e2) || a.is_le(r, e1, e2) || a.is_ge(r, e1, e2))) { if (a.is_add(e1, e3, e4) && a.is_mul(e3)) { r = m.mk_app(to_app(r)->get_decl(), a.mk_add(e4,e3), e2); } } } }; farkas_learner::farkas_learner(smt_params& params, ast_manager& outer_mgr) : m_proof_params(get_proof_params(params)), m_pr(PGM_FINE), m_constr(0), m_combine_farkas_coefficients(true), p2o(m_pr, outer_mgr), o2p(outer_mgr, m_pr) { reg_decl_plugins(m_pr); m_ctx = alloc(smt::kernel, m_pr, m_proof_params); } farkas_learner::~farkas_learner() { dealloc(m_constr); } smt_params farkas_learner::get_proof_params(smt_params& orig_params) { smt_params res(orig_params); res.m_arith_bound_prop = BP_NONE; // temp hack to fix the build // res.m_conflict_resolution_strategy = CR_ALL_DECIDED; res.m_arith_auto_config_simplex = true; res.m_arith_propagate_eqs = false; res.m_arith_eager_eq_axioms = false; res.m_arith_eq_bounds = false; return res; } class farkas_learner::equality_expander_cfg : public default_rewriter_cfg { ast_manager& m; arith_util m_ar; public: equality_expander_cfg(ast_manager& m) : m(m), m_ar(m) {} bool get_subst(expr * s, expr * & t, proof * & t_pr) { expr * x, *y; if (m.is_eq(s, x, y) && (m_ar.is_int(x) || m_ar.is_real(x))) { t = m.mk_and(m_ar.mk_ge(x, y), m_ar.mk_le(x, y)); return true; } else { return false; } } }; class collect_pure_proc { func_decl_set& m_symbs; public: collect_pure_proc(func_decl_set& s):m_symbs(s) {} void operator()(app* a) { if (a->get_family_id() == null_family_id) { m_symbs.insert(a->get_decl()); } } void operator()(var*) {} void operator()(quantifier*) {} }; bool farkas_learner::get_lemma_guesses(expr * A_ext, expr * B_ext, expr_ref_vector& lemmas) { expr_ref A(o2p(A_ext), m_pr); expr_ref B(o2p(B_ext), m_pr); proof_ref pr(m_pr); expr_ref tmp(m_pr); expr_ref_vector ilemmas(m_pr); equality_expander_cfg ee_rwr_cfg(m_pr); rewriter_tpl ee_rwr(m_pr, false, ee_rwr_cfg); lemmas.reset(); ee_rwr(A, A); ee_rwr(B, B); expr_set bs; expr_ref_vector blist(m_pr); qe::flatten_and(B, blist); for (unsigned i = 0; i < blist.size(); ++i) { bs.insert(blist[i].get()); } if (!m_ctx) { m_ctx = alloc(smt::kernel, m_pr, m_proof_params); } m_ctx->push(); m_ctx->assert_expr(A); expr_set::iterator it = bs.begin(), end = bs.end(); for (; it != end; ++it) { m_ctx->assert_expr(*it); } lbool res = m_ctx->check(); bool is_unsat = res == l_false; if (is_unsat) { pr = m_ctx->get_proof(); get_lemmas(m_ctx->get_proof(), bs, ilemmas); for (unsigned i = 0; i < ilemmas.size(); ++i) { lemmas.push_back(p2o(ilemmas[i].get())); } } m_ctx->pop(1); IF_VERBOSE(3, { for (unsigned i = 0; i < ilemmas.size(); ++i) { verbose_stream() << "B': " << mk_pp(ilemmas[i].get(), m_pr) << "\n"; } }); TRACE("farkas_learner", tout << (is_unsat?"unsat":"sat") << "\n"; tout << "A: " << mk_pp(A_ext, m_ctx->m()) << "\n"; tout << "B: " << mk_pp(B_ext, m_ctx->m()) << "\n"; for (unsigned i = 0; i < lemmas.size(); ++i) { tout << "B': " << mk_pp(ilemmas[i].get(), m_pr) << "\n"; }); DEBUG_CODE( if (is_unsat) { m_ctx->push(); m_ctx->assert_expr(A); for (unsigned i = 0; i < ilemmas.size(); ++i) { m_ctx->assert_expr(ilemmas[i].get()); } lbool res2 = m_ctx->check(); SASSERT(l_false == res2); m_ctx->pop(1); } ); return is_unsat; } // // Perform simple subsumption check of lemmas. // void farkas_learner::simplify_lemmas(expr_ref_vector& lemmas) { ast_manager& m = lemmas.get_manager(); goal_ref g(alloc(goal, m, false, false, false)); TRACE("farkas_simplify_lemmas", for (unsigned i = 0; i < lemmas.size(); ++i) { tout << mk_pp(lemmas[i].get(), m) << "\n"; }); for (unsigned i = 0; i < lemmas.size(); ++i) { g->assert_expr(lemmas[i].get()); } expr_ref tmp(m); model_converter_ref mc; proof_converter_ref pc; expr_dependency_ref core(m); goal_ref_buffer result; tactic_ref simplifier = mk_arith_bounds_tactic(m); (*simplifier)(g, result, mc, pc, core); lemmas.reset(); SASSERT(result.size() == 1); goal* r = result[0]; 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"; }); } void farkas_learner::combine_constraints(unsigned n, app * const * lits, rational const * coeffs, expr_ref& res) { ast_manager& m = res.get_manager(); if (m_combine_farkas_coefficients) { if (!m_constr) { m_constr = alloc(constr, m); } m_constr->reset(); for (unsigned i = 0; i < n; ++i) { m_constr->add(coeffs[i], lits[i]); } m_constr->get(res); } else { bool_rewriter rw(m); rw.mk_or(n, (expr*const*)(lits), res); res = m.mk_not(res); } } class farkas_learner::constant_replacer_cfg : public default_rewriter_cfg { const obj_map& m_translation; public: constant_replacer_cfg(const obj_map& translation) : m_translation(translation) { } bool get_subst(expr * s, expr * & t, proof * & t_pr) { return m_translation.find(s, t); } }; // every uninterpreted symbol is in symbs class is_pure_expr_proc { func_decl_set const& m_symbs; public: struct non_pure {}; is_pure_expr_proc(func_decl_set const& s):m_symbs(s) {} void operator()(app* a) { if (a->get_family_id() == null_family_id) { if (!m_symbs.contains(a->get_decl())) { throw non_pure(); } } } void operator()(var*) {} void operator()(quantifier*) {} }; bool farkas_learner::is_pure_expr(func_decl_set const& symbs, expr* e) const { is_pure_expr_proc proc(symbs); try { for_each_expr(proc, e); } catch (is_pure_expr_proc::non_pure) { return false; } return true; }; /** Revised version of Farkas strengthener. 1. Mark B-pure nodes as derivations that depend only on B. 2. Collect B-influenced nodes 3. (optional) Permute B-pure units over resolution steps to narrow dependencies on B. 4. Weaken B-pure units for resolution with Farkas Clauses. 5. Add B-pure units elsewhere. Rules: - hypothesis h |- h H |- false - lemma ---------- |- not H Th |- L \/ C H |- not L - th-lemma ------------------------- H |- C Note: C is false for theory axioms, C is unit literal for propagation. - rewrite |- t = s H |- t = s - monotonicity ---------------- H |- f(t) = f(s) H |- t = s H' |- s = u - trans ---------------------- H, H' |- t = u H |- C \/ L H' |- not L - unit_resolve ------------------------ H, H' |- C H |- a ~ b H' |- a - mp -------------------- H, H' |- b - def-axiom |- C - asserted |- f Mark nodes by: - Hypotheses - Dependency on bs - Dependency on A A node is unit derivable from bs if: - It has no hypotheses. - It depends on bs. - It does not depend on A. NB: currently unit derivable is not symmetric: A clause can be unit derivable, but a unit literal with hypotheses is not. This is clearly wrong, because hypotheses are just additional literals in a clausal version. NB: the routine is not interpolating, though an interpolating variant would be preferrable because then we can also use it for model propagation. We collect the unit derivable nodes from bs. These are the weakenings of bs, besides the units under Farkas. */ #define INSERT(_x_) if (!lemma_set.contains(_x_)) { lemma_set.insert(_x_); lemmas.push_back(_x_); } void farkas_learner::get_lemmas(proof* root, expr_set const& bs, expr_ref_vector& lemmas) { ast_manager& m = lemmas.get_manager(); bool_rewriter brwr(m); func_decl_set Bsymbs; collect_pure_proc collect_proc(Bsymbs); expr_set::iterator it = bs.begin(), end = bs.end(); for (; it != end; ++it) { for_each_expr(collect_proc, *it); } proof_ref pr(root, m); proof_utils::reduce_hypotheses(pr); proof_utils::permute_unit_resolution(pr); IF_VERBOSE(3, verbose_stream() << "Reduced proof:\n" << mk_ismt2_pp(pr, m) << "\n";); ptr_vector hyprefs; obj_map hypmap; obj_hashtable lemma_set; ast_mark b_depend, a_depend, visited, b_closed; expr_set* empty_set = alloc(expr_set); hyprefs.push_back(empty_set); ptr_vector todo; TRACE("pdr_verbose", tout << mk_pp(pr, m) << "\n";); todo.push_back(pr); while (!todo.empty()) { proof* p = todo.back(); SASSERT(m.is_proof(p)); if (visited.is_marked(p)) { todo.pop_back(); continue; } bool all_visit = true; for (unsigned i = 0; i < m.get_num_parents(p); ++i) { expr* arg = p->get_arg(i); SASSERT(m.is_proof(arg)); if (!visited.is_marked(arg)) { all_visit = false; todo.push_back(to_app(arg)); } } if (!all_visit) { continue; } visited.mark(p, true); todo.pop_back(); // retrieve hypotheses and dependencies on A, bs. bool b_dep = false, a_dep = false; expr_set* hyps = empty_set; for (unsigned i = 0; i < m.get_num_parents(p); ++i) { expr* arg = p->get_arg(i); a_dep = a_dep || a_depend.is_marked(arg); b_dep = b_dep || b_depend.is_marked(arg); expr_set* hyps2 = hypmap.find(arg); if (hyps != hyps2 && !hyps2->empty()) { if (hyps->empty()) { hyps = hyps2; } else { expr_set* hyps3 = alloc(expr_set); datalog::set_union(*hyps3, *hyps); datalog::set_union(*hyps3, *hyps2); hyps = hyps3; hyprefs.push_back(hyps); } } } hypmap.insert(p, hyps); a_depend.mark(p, a_dep); b_depend.mark(p, b_dep); #define IS_B_PURE(_p) (b_depend.is_marked(_p) && !a_depend.is_marked(_p) && hypmap.find(_p)->empty()) // Add lemmas that depend on bs, have no hypotheses, don't depend on A. if ((!hyps->empty() || a_depend.is_marked(p)) && b_depend.is_marked(p) && !is_farkas_lemma(m, p)) { for (unsigned i = 0; i < m.get_num_parents(p); ++i) { app* arg = to_app(p->get_arg(i)); if (IS_B_PURE(arg)) { expr* fact = m.get_fact(arg); if (is_pure_expr(Bsymbs, fact)) { TRACE("farkas_learner", tout << "Add: " << mk_pp(m.get_fact(arg), m) << "\n"; tout << mk_pp(arg, m) << "\n"; ); INSERT(fact); } else { get_asserted(p, bs, b_closed, lemma_set, lemmas); b_closed.mark(p, true); } } } } switch(p->get_decl_kind()) { case PR_ASSERTED: if (bs.contains(m.get_fact(p))) { b_depend.mark(p, true); } else { a_depend.mark(p, true); } break; case PR_HYPOTHESIS: { SASSERT(hyps == empty_set); hyps = alloc(expr_set); hyps->insert(m.get_fact(p)); hyprefs.push_back(hyps); hypmap.insert(p, hyps); break; } case PR_DEF_AXIOM: { if (!is_pure_expr(Bsymbs, m.get_fact(p))) { a_depend.mark(p, true); } break; } case PR_LEMMA: { expr_set* hyps2 = alloc(expr_set); hyprefs.push_back(hyps2); datalog::set_union(*hyps2, *hyps); hyps = hyps2; expr* fml = m.get_fact(p); hyps->remove(fml); if (m.is_or(fml)) { for (unsigned i = 0; i < to_app(fml)->get_num_args(); ++i) { expr* f = to_app(fml)->get_arg(i); expr_ref hyp(m); brwr.mk_not(f, hyp); hyps->remove(hyp); } } hypmap.insert(p, hyps); break; } case PR_TH_LEMMA: { if (!is_farkas_lemma(m, p)) break; SASSERT(m.has_fact(p)); unsigned prem_cnt = m.get_num_parents(p); func_decl * d = p->get_decl(); SASSERT(d->get_num_parameters() >= prem_cnt+2); SASSERT(d->get_parameter(0).get_symbol() == "arith"); SASSERT(d->get_parameter(1).get_symbol() == "farkas"); parameter const* params = d->get_parameters() + 2; app_ref_vector lits(m); expr_ref tmp(m); unsigned num_b_pures = 0; rational coef; vector coeffs; TRACE("farkas_learner", for (unsigned i = 0; i < prem_cnt; ++i) { VERIFY(params[i].is_rational(coef)); proof* prem = to_app(p->get_arg(i)); bool b_pure = IS_B_PURE(prem); tout << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m) << "\n"; } tout << mk_pp(m.get_fact(p), m) << "\n"; ); // NB. Taking 'abs' of coefficients is a workaround. // The Farkas coefficient extraction in arith_core must be wrong. // The coefficients would be always positive relative to the theory lemma. for(unsigned i = 0; i < prem_cnt; ++i) { expr * prem_e = p->get_arg(i); SASSERT(is_app(prem_e)); proof * prem = to_app(prem_e); if(IS_B_PURE(prem)) { ++num_b_pures; } else { VERIFY(params[i].is_rational(coef)); lits.push_back(to_app(m.get_fact(prem))); coeffs.push_back(abs(coef)); } } params += prem_cnt; if (prem_cnt + 2 < d->get_num_parameters()) { unsigned num_args = 1; expr* fact = m.get_fact(p); expr* const* args = &fact; if (m.is_or(fact)) { app* _or = to_app(fact); num_args = _or->get_num_args(); args = _or->get_args(); } SASSERT(prem_cnt + 2 + num_args == d->get_num_parameters()); for (unsigned i = 0; i < num_args; ++i) { expr* prem_e = args[i]; brwr.mk_not(prem_e, tmp); VERIFY(params[i].is_rational(coef)); SASSERT(is_app(tmp)); lits.push_back(to_app(tmp)); coeffs.push_back(abs(coef)); } } SASSERT(coeffs.size() == lits.size()); if (num_b_pures > 0) { expr_ref res(m); combine_constraints(coeffs.size(), lits.c_ptr(), coeffs.c_ptr(), res); TRACE("farkas_learner", tout << "Add: " << mk_pp(res, m) << "\n";); INSERT(res); b_closed.mark(p, true); } } default: break; } } std::for_each(hyprefs.begin(), hyprefs.end(), delete_proc()); simplify_lemmas(lemmas); } void farkas_learner::get_consequences(proof* root, expr_set const& bs, expr_ref_vector& consequences) { TRACE("farkas_learner", tout << "get consequences\n";); m_combine_farkas_coefficients = false; get_lemmas(root, bs, consequences); m_combine_farkas_coefficients = true; } void farkas_learner::get_asserted(proof* p, expr_set const& bs, ast_mark& b_closed, obj_hashtable& lemma_set, expr_ref_vector& lemmas) { ast_manager& m = lemmas.get_manager(); ast_mark visited; proof* p0 = p; ptr_vector todo; todo.push_back(p); while (!todo.empty()) { p = todo.back(); todo.pop_back(); if (visited.is_marked(p) || b_closed.is_marked(p)) { continue; } visited.mark(p, true); for (unsigned i = 0; i < m.get_num_parents(p); ++i) { expr* arg = p->get_arg(i); SASSERT(m.is_proof(arg)); todo.push_back(to_app(arg)); } if (p->get_decl_kind() == PR_ASSERTED && bs.contains(m.get_fact(p))) { expr* fact = m.get_fact(p); TRACE("farkas_learner", tout << mk_ll_pp(p0,m) << "\n"; tout << "Add: " << mk_pp(p,m) << "\n";); INSERT(fact); b_closed.mark(p, true); } } } bool farkas_learner::is_farkas_lemma(ast_manager& m, expr* e) { app * a; func_decl* d; symbol sym; return is_app(e) && (a = to_app(e), d = a->get_decl(), true) && PR_TH_LEMMA == a->get_decl_kind() && d->get_num_parameters() >= 2 && d->get_parameter(0).is_symbol(sym) && sym == "arith" && d->get_parameter(1).is_symbol(sym) && sym == "farkas" && d->get_num_parameters() >= m.get_num_parents(to_app(e)) + 2; }; void farkas_learner::test() { smt_params params; enable_trace("farkas_learner"); bool res; ast_manager m; reg_decl_plugins(m); arith_util a(m); pdr::farkas_learner fl(params, m); expr_ref_vector lemmas(m); sort_ref int_s(a.mk_int(), m); expr_ref x(m.mk_const(symbol("x"), int_s), m); expr_ref y(m.mk_const(symbol("y"), int_s), m); expr_ref z(m.mk_const(symbol("z"), int_s), m); expr_ref u(m.mk_const(symbol("u"), int_s), m); expr_ref v(m.mk_const(symbol("v"), int_s), m); // A: x > y >= z // B: x < z // Farkas: x <= z expr_ref A(m.mk_and(a.mk_gt(x,y), a.mk_ge(y,z)),m); expr_ref B(a.mk_gt(z,x),m); res = fl.get_lemma_guesses(A, B, lemmas); std::cout << "\nres: " << res << "\nlemmas: " << pp_cube(lemmas, m) << "\n"; // A: x > y >= z + 2 // B: x = 1, z = 8 // Farkas: x <= z + 2 expr_ref one(a.mk_numeral(rational(1), true), m); expr_ref two(a.mk_numeral(rational(2), true), m); expr_ref eight(a.mk_numeral(rational(8), true), m); A = m.mk_and(a.mk_gt(x,y),a.mk_ge(y,a.mk_add(z,two))); B = m.mk_and(m.mk_eq(x,one), m.mk_eq(z, eight)); res = fl.get_lemma_guesses(A, B, lemmas); std::cout << "\nres: " << res << "\nlemmas: " << pp_cube(lemmas, m) << "\n"; // A: x > y >= z \/ x >= u > z // B: z > x + 1 // Farkas: z >= x A = m.mk_or(m.mk_and(a.mk_gt(x,y),a.mk_ge(y,z)),m.mk_and(a.mk_ge(x,u),a.mk_gt(u,z))); B = a.mk_gt(z, a.mk_add(x,one)); res = fl.get_lemma_guesses(A, B, lemmas); std::cout << "\nres: " << res << "\nlemmas: " << pp_cube(lemmas, m) << "\n"; // A: (x > y >= z \/ x >= u > z \/ u > v) // B: z > x + 1 & not (u > v) // Farkas: z >= x & not (u > v) A = m.mk_or(m.mk_and(a.mk_gt(x,y),a.mk_ge(y,z)),m.mk_and(a.mk_ge(x,u),a.mk_gt(u,z)), a.mk_gt(u, v)); B = m.mk_and(a.mk_gt(z, a.mk_add(x,one)), m.mk_not(a.mk_gt(u, v))); res = fl.get_lemma_guesses(A, B, lemmas); std::cout << "\nres: " << res << "\nlemmas: " << pp_cube(lemmas, m) << "\n"; } void farkas_learner::collect_statistics(statistics& st) const { if (m_ctx) { m_ctx->collect_statistics(st); } } }; z3-4.4.0/src/muz/pdr/pdr_reachable_cache.h0000644000175000017500000000303112540347414020100 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: reachable_cache.h Abstract: Object for caching of reachable states. Author: Krystof Hoder (t-khoder) 2011-9-14. Revision History: --*/ #ifndef _REACHABLE_CACHE_H_ #define _REACHABLE_CACHE_H_ #include "ast.h" #include "ref_vector.h" #include "pdr_manager.h" #include "pdr_smt_context_manager.h" namespace pdr { class reachable_cache { struct stats { unsigned m_hits; unsigned m_miss; unsigned m_inserts; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; ast_manager & m; manager & m_pm; scoped_ptr m_ctx; ast_ref_vector m_ref_holder; app_ref m_disj_connector; obj_hashtable m_cache; stats m_stats; datalog::PDR_CACHE_MODE m_cache_mode; void add_disjuncted_formula(expr * f); public: reachable_cache(pdr::manager & pm, datalog::PDR_CACHE_MODE cm); void add_init(app * f) { add_disjuncted_formula(f); } /** add cube whose all models are reachable */ void add_reachable(expr * cube); /** return true if there is a model of cube which is reachable */ bool is_reachable(expr * cube); void collect_statistics(statistics& st) const; void reset_statistics(); }; } #endif z3-4.4.0/src/muz/pdr/pdr_closure.h0000644000175000017500000000314112540347414016525 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: pdr_closure.h Abstract: Utility functions for computing closures. Author: Nikolaj Bjorner (nbjorner) 2013-9-1. Revision History: --*/ #ifndef _PDR_CLOSURE_H_ #define _PDR_CLOSURE_H_ #include "arith_decl_plugin.h" namespace pdr { // Arithmetic scaling functor. // Variables are replaced using // m_translate. Constants are replaced by // multiplication with a variable 'k' (scale factor). class scaler { ast_manager& m; arith_util a; obj_map m_cache[2]; expr* m_k; obj_map* m_translate; public: scaler(ast_manager& m): m(m), a(m), m_translate(0) {} expr_ref operator()(expr* e, expr* k, obj_map* translate = 0); expr_ref undo_k(expr* e, expr* k); private: expr_ref scale(expr* e, bool is_mul); }; class pred_transformer; class closure { ast_manager& m; pred_transformer& m_pt; arith_util a; bool m_is_closure; expr_ref_vector m_sigma; expr_ref_vector m_trail; vector > m_vars; expr_ref relax(unsigned i, expr* fml); expr_ref close_conjunction(expr* fml); expr_ref close_fml(expr* fml); void add_variables(unsigned num_vars, expr_ref_vector& fmls); public: closure(pred_transformer& pt, bool is_closure); expr_ref operator()(expr_ref_vector const& As); }; } #endif z3-4.4.0/src/muz/pdr/pdr_smt_context_manager.cpp0000644000175000017500000001136412540347414021453 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdr_smt_context_manager.cpp Abstract: Manager of smt contexts Author: Nikolaj Bjorner (nbjorner) 2011-11-26. 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" namespace pdr { smt_context::smt_context(smt_context_manager& p, ast_manager& m, app* pred): m_pred(pred, m), m_parent(p), m_in_delay_scope(false), m_pushed(false) {} bool smt_context::is_aux_predicate(func_decl* p) { return m_parent.is_aux_predicate(p); } smt_context::scoped::scoped(smt_context& ctx): m_ctx(ctx) { SASSERT(!m_ctx.m_in_delay_scope); SASSERT(!m_ctx.m_pushed); m_ctx.m_in_delay_scope = true; } smt_context::scoped::~scoped() { SASSERT(m_ctx.m_in_delay_scope); if (m_ctx.m_pushed) { m_ctx.pop(); m_ctx.m_pushed = false; } m_ctx.m_in_delay_scope = false; } _smt_context::_smt_context(smt::kernel & ctx, smt_context_manager& p, app* pred): smt_context(p, ctx.m(), pred), m_context(ctx) {} void _smt_context::assert_expr(expr* e) { ast_manager& m = m_context.m(); if (m.is_true(e)) { return; } CTRACE("pdr", has_free_vars(e), tout << mk_pp(e, m) << "\n";); SASSERT(!has_free_vars(e)); if (m_in_delay_scope && !m_pushed) { m_context.push(); m_pushed = true; } expr_ref fml(m); fml = m_pushed?e:m.mk_implies(m_pred, e); m_context.assert_expr(fml); } lbool _smt_context::check(expr_ref_vector& assumptions) { ast_manager& m = m_pred.get_manager(); 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()); } 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++; tout << "pdr_check: " << strm.str() << "\n"; }); lbool result = m_context.check(assumptions.size(), assumptions.c_ptr()); if (!m.is_true(m_pred)) { assumptions.pop_back(); } return result; } void _smt_context::get_model(model_ref& model) { m_context.get_model(model); } proof* _smt_context::get_proof() { return m_context.get_proof(); } smt_context_manager::smt_context_manager(smt_params& fp, unsigned max_num_contexts, ast_manager& m): m_fparams(fp), m(m), m_max_num_contexts(max_num_contexts), m_num_contexts(0), m_predicate_list(m) { } smt_context_manager::~smt_context_manager() { TRACE("pdr",tout << "\n";); std::for_each(m_contexts.begin(), m_contexts.end(), delete_proc()); } smt_context* smt_context_manager::mk_fresh() { ++m_num_contexts; app_ref pred(m); smt::kernel * ctx = 0; if (m_max_num_contexts == 0) { m_contexts.push_back(alloc(smt::kernel, m, m_fparams)); pred = m.mk_true(); ctx = m_contexts[m_num_contexts-1]; } else { if (m_contexts.size() < m_max_num_contexts) { m_contexts.push_back(alloc(smt::kernel, m, m_fparams)); } std::stringstream name; name << "#context" << m_num_contexts; pred = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()); m_predicate_list.push_back(pred); m_predicate_set.insert(pred->get_decl()); ctx = m_contexts[(m_num_contexts-1)%m_max_num_contexts]; } return alloc(_smt_context, *ctx, *this, pred); } void smt_context_manager::collect_statistics(statistics& st) const { for (unsigned i = 0; i < m_contexts.size(); ++i) { m_contexts[i]->collect_statistics(st); } } void smt_context_manager::reset_statistics() { for (unsigned i = 0; i < m_contexts.size(); ++i) { m_contexts[i]->reset_statistics(); } } }; z3-4.4.0/src/muz/pdr/pdr_manager.h0000644000175000017500000002542512540347414016474 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdr_manager.h Abstract: A manager class for PDR, taking care of creating of AST objects and conversions between them. Author: Krystof Hoder (t-khoder) 2011-8-25. Revision History: --*/ #ifndef _PDR_MANAGER_H_ #define _PDR_MANAGER_H_ #include #include #include "bool_rewriter.h" #include "expr_replacer.h" #include "expr_substitution.h" #include "map.h" #include "ref_vector.h" #include "smt_kernel.h" #include "pdr_util.h" #include "pdr_sym_mux.h" #include "pdr_farkas_learner.h" #include "pdr_smt_context_manager.h" #include "dl_rule.h" namespace smt { class context; } namespace pdr { struct relation_info { func_decl_ref m_pred; func_decl_ref_vector m_vars; expr_ref m_body; relation_info(ast_manager& m, func_decl* pred, ptr_vector const& vars, expr* b): m_pred(pred, m), m_vars(m, vars.size(), vars.c_ptr()), m_body(b, m) {} relation_info(relation_info const& other): m_pred(other.m_pred), m_vars(other.m_vars), m_body(other.m_body) {} }; class unknown_exception {}; class inductive_property { ast_manager& m; model_converter_ref m_mc; vector m_relation_info; expr_ref fixup_clauses(expr* property) const; expr_ref fixup_clause(expr* clause) const; public: inductive_property(ast_manager& m, model_converter_ref& mc, vector const& relations): m(m), m_mc(mc), m_relation_info(relations) {} std::string to_string() const; expr_ref to_expr() const; void to_model(model_ref& md) const; void display(datalog::rule_manager& rm, ptr_vector const& rules, std::ostream& out) const; }; class manager { ast_manager& m; smt_params& m_fparams; mutable bool_rewriter m_brwr; sym_mux m_mux; expr_ref m_background; decl_vector m_o0_preds; pdr::smt_context_manager m_contexts; /** whenever we need an unique number, we get this one and increase */ unsigned m_next_unique_num; static vector get_state_suffixes(); unsigned n_index() const { return 0; } unsigned o_index(unsigned i) const { return i+1; } void add_new_state(func_decl * s); public: manager(smt_params& fparams, unsigned max_num_contexts, ast_manager & manager); ast_manager& get_manager() const { return m; } smt_params& get_fparams() const { return m_fparams; } bool_rewriter& get_brwr() const { return m_brwr; } expr_ref mk_and(unsigned sz, expr* const* exprs); expr_ref mk_and(expr_ref_vector const& exprs) { return mk_and(exprs.size(), exprs.c_ptr()); } expr_ref mk_and(expr* a, expr* b) { expr* args[2] = { a, b }; return mk_and(2, args); } expr_ref mk_or(unsigned sz, expr* const* exprs); expr_ref mk_or(expr_ref_vector const& exprs) { return mk_or(exprs.size(), exprs.c_ptr()); } expr_ref mk_not_and(expr_ref_vector const& exprs); void get_or(expr* e, expr_ref_vector& result); //"o" predicates stand for the old states and "n" for the new states func_decl * get_o_pred(func_decl * s, unsigned idx); func_decl * get_n_pred(func_decl * s); /** Marks symbol as non-model which means it will not appear in models collected by get_state_cube_from_model function. This is to take care of auxiliary symbols introduced by the disjunction relations to relativize lemmas coming from disjuncts. */ void mark_as_non_model(func_decl * p) { m_mux.mark_as_non_model(p); } func_decl * const * begin_o0_preds() const { return m_o0_preds.begin(); } func_decl * const * end_o0_preds() const { return m_o0_preds.end(); } bool is_state_pred(func_decl * p) const { return m_mux.is_muxed(p); } func_decl * to_o0(func_decl * p) { return m_mux.conv(m_mux.get_primary(p), 0, o_index(0)); } bool is_o(func_decl * p, unsigned idx) const { return m_mux.has_index(p, o_index(idx)); } bool is_o(expr* e, unsigned idx) const { return is_app(e) && is_o(to_app(e)->get_decl(), idx); } bool is_o(func_decl * p) const { unsigned idx; return m_mux.try_get_index(p, idx) && idx!=n_index(); } bool is_o(expr* e) const { return is_app(e) && is_o(to_app(e)->get_decl()); } bool is_n(func_decl * p) const { return m_mux.has_index(p, n_index()); } bool is_n(expr* e) const { return is_app(e) && is_n(to_app(e)->get_decl()); } /** true if p should not appead in models propagates into child relations */ bool is_non_model_sym(func_decl * p) const { return m_mux.is_non_model_sym(p); } /** true if f doesn't contain any n predicates */ bool is_o_formula(expr * f) const { return !m_mux.contains(f, n_index()); } /** true if f contains only o state preds of index o_idx */ bool is_o_formula(expr * f, unsigned o_idx) const { return m_mux.is_homogenous_formula(f, o_index(o_idx)); } /** true if f doesn't contain any o predicates */ bool is_n_formula(expr * f) const { return m_mux.is_homogenous_formula(f, n_index()); } func_decl * o2n(func_decl * p, unsigned o_idx) const { return m_mux.conv(p, o_index(o_idx), n_index()); } func_decl * o2o(func_decl * p, unsigned src_idx, unsigned tgt_idx) const { return m_mux.conv(p, o_index(src_idx), o_index(tgt_idx)); } func_decl * n2o(func_decl * p, unsigned o_idx) const { return m_mux.conv(p, n_index(), o_index(o_idx)); } void formula_o2n(expr * f, expr_ref & result, unsigned o_idx, bool homogenous=true) const { m_mux.conv_formula(f, o_index(o_idx), n_index(), result, homogenous); } void formula_n2o(expr * f, expr_ref & result, unsigned o_idx, bool homogenous=true) const { m_mux.conv_formula(f, n_index(), o_index(o_idx), result, homogenous); } void formula_n2o(unsigned o_idx, bool homogenous, expr_ref & result) const { m_mux.conv_formula(result.get(), n_index(), o_index(o_idx), result, homogenous); } void formula_o2o(expr * src, expr_ref & tgt, unsigned src_idx, unsigned tgt_idx, bool homogenous=true) const { m_mux.conv_formula(src, o_index(src_idx), o_index(tgt_idx), tgt, homogenous); } /** Return true if all state symbols which e contains are of one kind (either "n" or one of "o"). */ bool is_homogenous_formula(expr * e) const { return m_mux.is_homogenous_formula(e); } /** Collect indices used in expression. */ void collect_indices(expr* e, unsigned_vector& indices) const { m_mux.collect_indices(e, indices); } /** Collect used variables of each index. */ void collect_variables(expr* e, vector >& vars) const { m_mux.collect_variables(e, vars); } /** Return true iff both s1 and s2 are either "n" or "o" of the same index. If one (or both) of them are not state symbol, return false. */ bool have_different_state_kinds(func_decl * s1, func_decl * s2) const { unsigned i1, i2; return m_mux.try_get_index(s1, i1) && m_mux.try_get_index(s2, i2) && i1!=i2; } /** Increase indexes of state symbols in formula by dist. The 'N' index becomes 'O' index with number dist-1. */ void formula_shift(expr * src, expr_ref & tgt, unsigned dist) const { SASSERT(n_index()==0); SASSERT(o_index(0)==1); m_mux.shift_formula(src, dist, tgt); } void mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res); void mk_core_into_cube(const expr_ref_vector & core, expr_ref & res); void mk_cube_into_lemma(expr * cube, expr_ref & res); void mk_lemma_into_cube(expr * lemma, expr_ref & res); /** Remove from vec all atoms that do not have an "o" state. The order of elements in vec may change. An assumption is that atoms having "o" state of given index do not have "o" states of other indexes or "n" states. */ void filter_o_atoms(expr_ref_vector& vec, unsigned o_idx) const { m_mux.filter_idx(vec, o_index(o_idx)); } void filter_n_atoms(expr_ref_vector& vec) const { m_mux.filter_idx(vec, n_index()); } /** Partition literals into o_lits and others. */ void partition_o_atoms(expr_ref_vector const& lits, expr_ref_vector& o_lits, expr_ref_vector& other, unsigned o_idx) const { m_mux.partition_o_idx(lits, o_lits, other, o_index(o_idx)); } void filter_out_non_model_atoms(expr_ref_vector& vec) const { m_mux.filter_non_model_lits(vec); } bool try_get_state_and_value_from_atom(expr * atom, app *& state, app_ref& value); bool try_get_state_decl_from_atom(expr * atom, func_decl *& state); std::string pp_model(const model_core & mdl) const { return m_mux.pp_model(mdl); } void set_background(expr* b) { m_background = b; } expr* get_background() const { return m_background; } /** Return true if we can show that lhs => rhs. The function can have false negatives (i.e. when smt::context returns unknown), but no false positives. bg is background knowledge and can be null */ bool implication_surely_holds(expr * lhs, expr * rhs, expr * bg=0); unsigned get_unique_num() { return m_next_unique_num++; } pdr::smt_context* mk_fresh() { return m_contexts.mk_fresh(); } void collect_statistics(statistics& st) const { m_contexts.collect_statistics(st); } void reset_statistics() { m_contexts.reset_statistics(); } }; } #endif z3-4.4.0/src/muz/pdr/pdr_manager.cpp0000644000175000017500000002370212540347414017023 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdr_manager.cpp Abstract: A manager class for PDR, taking care of creating of AST objects and conversions between them. Author: Krystof Hoder (t-khoder) 2011-8-25. Revision History: --*/ #include #include "pdr_manager.h" #include "ast_smt2_pp.h" #include "for_each_expr.h" #include "has_free_vars.h" #include "expr_replacer.h" #include "expr_abstract.h" #include "model2expr.h" #include "model_smt2_pp.h" #include "model_converter.h" namespace pdr { class collect_decls_proc { func_decl_set& m_bound_decls; func_decl_set& m_aux_decls; public: collect_decls_proc(func_decl_set& bound_decls, func_decl_set& aux_decls): m_bound_decls(bound_decls), m_aux_decls(aux_decls) { } void operator()(app* a) { if (a->get_family_id() == null_family_id) { func_decl* f = a->get_decl(); if (!m_bound_decls.contains(f)) { m_aux_decls.insert(f); } } } void operator()(var* v) {} void operator()(quantifier* q) {} }; typedef hashtable symbol_set; expr_ref inductive_property::fixup_clause(expr* fml) const { expr_ref_vector disjs(m); qe::flatten_or(fml, disjs); expr_ref result(m); bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), result); return result; } expr_ref inductive_property::fixup_clauses(expr* fml) const { expr_ref_vector conjs(m); expr_ref result(m); qe::flatten_and(fml, conjs); for (unsigned i = 0; i < conjs.size(); ++i) { conjs[i] = fixup_clause(conjs[i].get()); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), result); return result; } std::string inductive_property::to_string() const { std::stringstream stm; model_ref md; expr_ref result(m); to_model(md); model_smt2_pp(stm, m, *md.get(), 0); return stm.str(); } void inductive_property::to_model(model_ref& md) const { md = alloc(model, m); vector const& rs = m_relation_info; expr_ref_vector conjs(m); for (unsigned i = 0; i < rs.size(); ++i) { relation_info ri(rs[i]); func_decl * pred = ri.m_pred; expr_ref prop = fixup_clauses(ri.m_body); func_decl_ref_vector const& sig = ri.m_vars; expr_ref q(m); expr_ref_vector sig_vars(m); for (unsigned j = 0; j < sig.size(); ++j) { sig_vars.push_back(m.mk_const(sig[sig.size()-j-1])); } expr_abstract(m, 0, sig_vars.size(), sig_vars.c_ptr(), prop, q); if (sig.empty()) { md->register_decl(pred, q); } else { func_interp* fi = alloc(func_interp, m, sig.size()); fi->set_else(q); md->register_decl(pred, fi); } } TRACE("pdr", model_smt2_pp(tout, m, *md, 0);); apply(const_cast(m_mc), md, 0); } expr_ref inductive_property::to_expr() const { model_ref md; expr_ref result(m); to_model(md); model2expr(md, result); return result; } void inductive_property::display(datalog::rule_manager& rm, ptr_vector const& rules, std::ostream& out) const { func_decl_set bound_decls, aux_decls; collect_decls_proc collect_decls(bound_decls, aux_decls); for (unsigned i = 0; i < m_relation_info.size(); ++i) { bound_decls.insert(m_relation_info[i].m_pred); func_decl_ref_vector const& sig = m_relation_info[i].m_vars; for (unsigned j = 0; j < sig.size(); ++j) { bound_decls.insert(sig[j]); } for_each_expr(collect_decls, m_relation_info[i].m_body); } for (unsigned i = 0; i < rules.size(); ++i) { bound_decls.insert(rules[i]->get_decl()); } for (unsigned i = 0; i < rules.size(); ++i) { unsigned u_sz = rules[i]->get_uninterpreted_tail_size(); unsigned t_sz = rules[i]->get_tail_size(); for (unsigned j = u_sz; j < t_sz; ++j) { for_each_expr(collect_decls, rules[i]->get_tail(j)); } } smt2_pp_environment_dbg env(m); func_decl_set::iterator it = aux_decls.begin(), end = aux_decls.end(); for (; it != end; ++it) { func_decl* f = *it; ast_smt2_pp(out, f, env); out << "\n"; } out << to_string() << "\n"; for (unsigned i = 0; i < rules.size(); ++i) { out << "(push)\n"; out << "(assert (not\n"; rm.display_smt2(*rules[i], out); out << "))\n"; out << "(check-sat)\n"; out << "(pop)\n"; } } vector manager::get_state_suffixes() { vector res; res.push_back("_n"); return res; } manager::manager(smt_params& fparams, unsigned max_num_contexts, ast_manager& manager) : m(manager), m_fparams(fparams), m_brwr(m), m_mux(m, get_state_suffixes()), m_background(m.mk_true(), m), m_contexts(fparams, max_num_contexts, m), m_next_unique_num(0) { } void manager::add_new_state(func_decl * s) { SASSERT(s->get_arity()==0); //we currently don't support non-constant states decl_vector vect; SASSERT(o_index(0)==1); //we assume this in the number of retrieved symbols m_mux.create_tuple(s, s->get_arity(), s->get_domain(), s->get_range(), 2, vect); m_o0_preds.push_back(vect[o_index(0)]); } func_decl * manager::get_o_pred(func_decl* s, unsigned idx) { func_decl * res = m_mux.try_get_by_prefix(s, o_index(idx)); if(res) { return res; } add_new_state(s); res = m_mux.try_get_by_prefix(s, o_index(idx)); SASSERT(res); return res; } func_decl * manager::get_n_pred(func_decl* s) { func_decl * res = m_mux.try_get_by_prefix(s, n_index()); if(res) { return res; } add_new_state(s); res = m_mux.try_get_by_prefix(s, n_index()); SASSERT(res); return res; } void manager::mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res) { m_brwr.mk_and(mdl.size(), mdl.c_ptr(), res); } void manager::mk_core_into_cube(const expr_ref_vector & core, expr_ref & res) { m_brwr.mk_and(core.size(), core.c_ptr(), res); } void manager::mk_cube_into_lemma(expr * cube, expr_ref & res) { m_brwr.mk_not(cube, res); } void manager::mk_lemma_into_cube(expr * lemma, expr_ref & res) { m_brwr.mk_not(lemma, res); } expr_ref manager::mk_and(unsigned sz, expr* const* exprs) { expr_ref result(m); m_brwr.mk_and(sz, exprs, result); return result; } expr_ref manager::mk_or(unsigned sz, expr* const* exprs) { expr_ref result(m); m_brwr.mk_or(sz, exprs, result); return result; } expr_ref manager::mk_not_and(expr_ref_vector const& conjs) { expr_ref result(m), e(m); expr_ref_vector es(conjs); qe::flatten_and(es); for (unsigned i = 0; i < es.size(); ++i) { m_brwr.mk_not(es[i].get(), e); es[i] = e; } m_brwr.mk_or(es.size(), es.c_ptr(), result); return result; } void manager::get_or(expr* e, expr_ref_vector& result) { result.push_back(e); for (unsigned i = 0; i < result.size(); ) { e = result[i].get(); if (m.is_or(e)) { result.append(to_app(e)->get_num_args(), to_app(e)->get_args()); result[i] = result.back(); result.pop_back(); } else { ++i; } } } bool manager::try_get_state_and_value_from_atom(expr * atom0, app *& state, app_ref& value) { if(!is_app(atom0)) { return false; } app * atom = to_app(atom0); expr * arg1; expr * arg2; app * candidate_state; app_ref candidate_value(m); if(m.is_not(atom, arg1)) { if(!is_app(arg1)) { return false; } candidate_state = to_app(arg1); candidate_value = m.mk_false(); } else if(m.is_eq(atom, arg1, arg2)) { if(!is_app(arg1) || !is_app(arg2)) { return false; } if(!m_mux.is_muxed(to_app(arg1)->get_decl())) { std::swap(arg1, arg2); } candidate_state = to_app(arg1); candidate_value = to_app(arg2); } else { candidate_state = atom; candidate_value = m.mk_true(); } if(!m_mux.is_muxed(candidate_state->get_decl())) { return false; } state = candidate_state; value = candidate_value; return true; } bool manager::try_get_state_decl_from_atom(expr * atom, func_decl *& state) { app_ref dummy_value_holder(m); app * s; if(try_get_state_and_value_from_atom(atom, s, dummy_value_holder)) { state = s->get_decl(); return true; } else { return false; } } bool manager::implication_surely_holds(expr * lhs, expr * rhs, expr * bg) { smt::kernel sctx(m, get_fparams()); if(bg) { sctx.assert_expr(bg); } sctx.assert_expr(lhs); expr_ref neg_rhs(m.mk_not(rhs),m); sctx.assert_expr(neg_rhs); lbool smt_res = sctx.check(); return smt_res==l_false; } }; z3-4.4.0/src/muz/pdr/pdr_context.h0000644000175000017500000004067312540347414016550 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdr_context.h Abstract: PDR for datalog Author: Nikolaj Bjorner (nbjorner) 2011-11-20. Revision History: --*/ #ifndef _PDR_CONTEXT_H_ #define _PDR_CONTEXT_H_ #ifdef _CYGWIN #undef min #undef max #endif #include #include "pdr_manager.h" #include "pdr_prop_solver.h" #include "pdr_reachable_cache.h" #include "fixedpoint_params.hpp" namespace datalog { class rule_set; class context; }; namespace pdr { class pred_transformer; class model_node; class context; typedef obj_map rule2inst; typedef obj_map decl2rel; // // Predicate transformer state. // A predicate transformer corresponds to the // set of rules that have the same head predicates. // class pred_transformer { struct stats { unsigned m_num_propagations; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; typedef obj_map rule2expr; typedef obj_map > rule2apps; manager& pm; // pdr-manager ast_manager& m; // manager context& ctx; func_decl_ref m_head; // predicate func_decl_ref_vector m_sig; // signature ptr_vector m_use; // places where 'this' is referenced. ptr_vector m_rules; // rules used to derive transformer prop_solver m_solver; // solver context vector m_levels; // level formulas expr_ref_vector m_invariants; // properties that are invariant. obj_map m_prop2level; // map property to level where it occurs. obj_map m_tag2rule; // map tag predicate to rule. rule2expr m_rule2tag; // map rule to predicate tag. rule2inst m_rule2inst; // map rules to instantiations of indices rule2expr m_rule2transition; // map rules to transition rule2apps m_rule2vars; // map rule to auxiliary variables expr_ref m_transition; // transition relation. expr_ref m_initial_state; // initial state. reachable_cache m_reachable; ptr_vector m_predicates; stats m_stats; void init_sig(); void ensure_level(unsigned level); bool add_property1(expr * lemma, unsigned lvl); // add property 'p' to state at level lvl. void add_child_property(pred_transformer& child, expr* lemma, unsigned lvl); void mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result); // Initialization void init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transition); void init_rule(decl2rel const& pts, datalog::rule const& rule, expr_ref& init, ptr_vector& rules, expr_ref_vector& transition); void init_atom(decl2rel const& pts, app * atom, app_ref_vector& var_reprs, expr_ref_vector& conj, unsigned tail_idx); void simplify_formulas(tactic& tac, expr_ref_vector& fmls); // Debugging bool check_filled(app_ref_vector const& v) const; void add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r); public: pred_transformer(context& ctx, manager& pm, func_decl* head); ~pred_transformer(); void add_rule(datalog::rule* r) { m_rules.push_back(r); } void add_use(pred_transformer* pt) { if (!m_use.contains(pt)) m_use.insert(pt); } void initialize(decl2rel const& pts); func_decl* head() const { return m_head; } ptr_vector const& rules() const { return m_rules; } func_decl* sig(unsigned i) { init_sig(); return m_sig[i].get(); } // signature func_decl* const* sig() { init_sig(); return m_sig.c_ptr(); } unsigned sig_size() { init_sig(); return m_sig.size(); } expr* transition() const { return m_transition; } expr* initial_state() const { return m_initial_state; } expr* rule2tag(datalog::rule const* r) { return m_rule2tag.find(r); } unsigned get_num_levels() { return m_levels.size(); } expr_ref get_cover_delta(func_decl* p_orig, int level); void add_cover(unsigned level, expr* property); context& get_context() { return ctx; } std::ostream& display(std::ostream& strm) const; void collect_statistics(statistics& st) const; void reset_statistics(); bool is_reachable(expr* state); void remove_predecessors(expr_ref_vector& literals); void find_predecessors(datalog::rule const& r, ptr_vector& predicates) const; datalog::rule const& find_rule(model_core const& model) const; expr* get_transition(datalog::rule const& r) { return m_rule2transition.find(&r); } ptr_vector& get_aux_vars(datalog::rule const& r) { return m_rule2vars.find(&r); } bool propagate_to_next_level(unsigned level); void propagate_to_infinity(unsigned level); void add_property(expr * lemma, unsigned lvl); // add property 'p' to state at level. lbool is_reachable(model_node& n, expr_ref_vector* core, bool& uses_level); bool is_invariant(unsigned level, expr* co_state, bool inductive, bool& assumes_level, expr_ref_vector* core = 0); bool check_inductive(unsigned level, expr_ref_vector& state, bool& assumes_level); expr_ref get_formulas(unsigned level, bool add_axioms); void simplify_formulas(); expr_ref get_propagation_formula(decl2rel const& pts, unsigned level); manager& get_pdr_manager() const { return pm; } ast_manager& get_manager() const { return m; } void add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r); void close(expr* e); app_ref_vector& get_inst(datalog::rule const* r) { return *m_rule2inst.find(r);} void inherit_properties(pred_transformer& other); void ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector& aux_vars); prop_solver& get_solver() { return m_solver; } prop_solver const& get_solver() const { return m_solver; } void set_use_farkas(bool f) { get_solver().set_use_farkas(f); } bool get_use_farkas() const { return get_solver().get_use_farkas(); } class scoped_farkas { bool m_old; pred_transformer& m_p; public: scoped_farkas(pred_transformer& p, bool v): m_old(p.get_use_farkas()), m_p(p) { p.set_use_farkas(v); } ~scoped_farkas() { m_p.set_use_farkas(m_old); } }; }; // structure for counter-example search. class model_node { model_node* m_parent; model_node* m_next; model_node* m_prev; pred_transformer& m_pt; expr_ref m_state; model_ref m_model; ptr_vector m_children; unsigned m_level; unsigned m_orig_level; unsigned m_depth; bool m_closed; datalog::rule const* m_rule; public: model_node(model_node* parent, expr_ref& state, pred_transformer& pt, unsigned level): m_parent(parent), m_next(0), m_prev(0), m_pt(pt), m_state(state), m_model(0), m_level(level), m_orig_level(level), m_depth(0), m_closed(false), m_rule(0) { model_node* p = m_parent; if (p) { p->m_children.push_back(this); SASSERT(p->m_level == level+1); SASSERT(p->m_level > 0); m_depth = p->m_depth+1; if (p && p->is_closed()) { p->set_open(); } } } void set_model(model_ref& m) { m_model = m; } unsigned level() const { return m_level; } unsigned orig_level() const { return m_orig_level; } unsigned depth() const { return m_depth; } void increase_level() { ++m_level; } expr_ref const& state() const { return m_state; } ptr_vector const& children() { return m_children; } pred_transformer& pt() const { return m_pt; } model_node* parent() const { return m_parent; } model* get_model_ptr() const { return m_model.get(); } model const& get_model() const { return *m_model; } unsigned index() const; bool is_closed() const { return m_closed; } bool is_open() const { return !is_closed(); } bool is_1closed() { if (is_closed()) return true; if (m_children.empty()) return false; for (unsigned i = 0; i < m_children.size(); ++i) { if (m_children[i]->is_open()) return false; } return true; } void check_pre_closed(); void set_closed(); void set_open(); void set_pre_closed() { m_closed = true; } void reset() { m_children.reset(); } void set_rule(datalog::rule const* r) { m_rule = r; } datalog::rule* get_rule(); void mk_instantiate(datalog::rule_ref& r0, datalog::rule_ref& r1, expr_ref_vector& binding); std::ostream& display(std::ostream& out, unsigned indent); void dequeue(model_node*& root); void enqueue(model_node* n); model_node* next() const { return m_next; } bool is_goal() const { return 0 != next(); } }; class model_search { typedef ptr_vector model_nodes; bool m_bfs; model_node* m_root; model_node* m_goal; vector > m_cache; obj_map& cache(model_node const& n); void erase_children(model_node& n, bool backtrack); void remove_node(model_node& n, bool backtrack); void enqueue_leaf(model_node* n); // add leaf to priority queue. void update_models(); void set_leaf(model_node& n); // Set node as leaf, remove children. bool is_repeated(model_node& n) const; unsigned num_goals() const; public: model_search(bool bfs): m_bfs(bfs), m_root(0), m_goal(0) {} ~model_search(); void reset(); model_node* next(); void add_leaf(model_node& n); // add fresh node. void set_root(model_node* n); model_node& get_root() const { return *m_root; } std::ostream& display(std::ostream& out) const; expr_ref get_trace(context const& ctx); proof_ref get_proof_trace(context const& ctx); void backtrack_level(bool uses_level, model_node& n); void remove_goal(model_node& n); void well_formed(); }; struct model_exception { }; struct inductive_exception {}; // 'state' is unsatisfiable at 'level' with 'core'. // Minimize or weaken core. class core_generalizer { protected: context& m_ctx; public: typedef vector > cores; core_generalizer(context& ctx): m_ctx(ctx) {} virtual ~core_generalizer() {} virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) = 0; virtual void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { new_cores.push_back(std::make_pair(core, uses_level)); if (!core.empty()) { (*this)(n, new_cores.back().first, new_cores.back().second); } } virtual void collect_statistics(statistics& st) const {} virtual void reset_statistics() {} }; class context { struct stats { unsigned m_num_nodes; unsigned m_max_depth; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; smt_params& m_fparams; fixedpoint_params const& m_params; ast_manager& m; datalog::context* m_context; manager m_pm; decl2rel m_rels; // Map from relation predicate to fp-operator. func_decl_ref m_query_pred; pred_transformer* m_query; mutable model_search m_search; lbool m_last_result; unsigned m_inductive_lvl; unsigned m_expanded_lvl; ptr_vector m_core_generalizers; stats m_stats; volatile bool m_cancel; model_converter_ref m_mc; proof_converter_ref m_pc; // Functions used by search. void solve_impl(); bool check_reachability(unsigned level); void propagate(unsigned max_prop_lvl); void close_node(model_node& n); void check_pre_closed(model_node& n); void expand_node(model_node& n); lbool expand_state(model_node& n, expr_ref_vector& cube, bool& uses_level); void create_children(model_node& n); expr_ref mk_sat_answer() const; expr_ref mk_unsat_answer() const; // Generate inductive property void get_level_property(unsigned lvl, expr_ref_vector& res, vector & rs) const; // Initialization class classifier_proc; void init_core_generalizers(datalog::rule_set& rules); bool check_invariant(unsigned lvl); bool check_invariant(unsigned lvl, func_decl* fn); void checkpoint(); void init_rules(datalog::rule_set& rules, decl2rel& transformers); void simplify_formulas(); void reset_core_generalizers(); void validate(); void validate_proof(); void validate_search(); void validate_model(); public: /** Initial values of predicates are stored in corresponding relations in dctx. We check whether there is some reachable state of the relation checked_relation. */ context( smt_params& fparams, fixedpoint_params const& params, ast_manager& m); ~context(); smt_params& get_fparams() const { return m_fparams; } fixedpoint_params const& get_params() const { return m_params; } ast_manager& get_manager() const { return m; } manager& get_pdr_manager() { return m_pm; } decl2rel const& get_pred_transformers() const { return m_rels; } pred_transformer& get_pred_transformer(func_decl* p) const { return *m_rels.find(p); } datalog::context& get_context() const { SASSERT(m_context); return *m_context; } expr_ref get_answer(); bool is_dl() const { return m_fparams.m_arith_mode == AS_DIFF_LOGIC; } bool is_utvpi() const { return m_fparams.m_arith_mode == AS_UTVPI; } void collect_statistics(statistics& st) const; void reset_statistics(); std::ostream& display(std::ostream& strm) const; void display_certificate(std::ostream& strm) const; lbool solve(); void cancel(); void cleanup(); void reset(); void set_query(func_decl* q) { m_query_pred = q; } void set_unsat() { m_last_result = l_false; } void set_model_converter(model_converter_ref& mc) { m_mc = mc; } model_converter_ref get_model_converter() { return m_mc; } void set_proof_converter(proof_converter_ref& pc) { m_pc = pc; } void update_rules(datalog::rule_set& rules); void set_axioms(expr* axioms) { m_pm.set_background(axioms); } unsigned get_num_levels(func_decl* p); expr_ref get_cover_delta(int level, func_decl* p_orig, func_decl* p); void add_cover(int level, func_decl* pred, expr* property); model_ref get_model(); proof_ref get_proof() const; model_node& get_root() const { return m_search.get_root(); } }; }; #endif z3-4.4.0/src/muz/pdr/pdr_dl_interface.cpp0000644000175000017500000001456212540347414020034 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdr_dl.cpp Abstract: SMT2 interface for the datalog PDR Author: Krystof Hoder (t-khoder) 2011-9-22. Revision History: --*/ #include "dl_context.h" #include "dl_mk_coi_filter.h" #include "dl_mk_interp_tail_simplifier.h" #include "dl_mk_subsumption_checker.h" #include "dl_mk_rule_inliner.h" #include "dl_rule.h" #include "dl_rule_transformer.h" #include "smt2parser.h" #include "pdr_context.h" #include "pdr_dl_interface.h" #include "dl_rule_set.h" #include "dl_mk_slice.h" #include "dl_mk_unfold.h" #include "dl_mk_coalesce.h" #include "dl_transforms.h" #include "scoped_proof.h" #include "model_smt2_pp.h" using namespace pdr; dl_interface::dl_interface(datalog::context& ctx) : engine_base(ctx.get_manager(), "pdr"), m_ctx(ctx), m_pdr_rules(ctx), m_old_rules(ctx), m_context(0), m_refs(ctx.get_manager()) { m_context = alloc(pdr::context, ctx.get_fparams(), ctx.get_params(), ctx.get_manager()); } dl_interface::~dl_interface() { dealloc(m_context); } // // Check if the new rules are weaker so that we can // re-use existing context. // void dl_interface::check_reset() { datalog::rule_set const& new_rules = m_ctx.get_rules(); datalog::rule_ref_vector const& old_rules = m_old_rules.get_rules(); bool is_subsumed = !old_rules.empty(); for (unsigned i = 0; is_subsumed && i < new_rules.get_num_rules(); ++i) { is_subsumed = false; for (unsigned j = 0; !is_subsumed && j < old_rules.size(); ++j) { if (m_ctx.check_subsumes(*old_rules[j], *new_rules.get_rule(i))) { is_subsumed = true; } } if (!is_subsumed) { TRACE("pdr", new_rules.get_rule(i)->display(m_ctx, tout << "Fresh rule ");); m_context->reset(); } } m_old_rules.replace_rules(new_rules); } lbool dl_interface::query(expr * query) { //we restore the initial state in the datalog context m_ctx.ensure_opened(); m_refs.reset(); m_pred2slice.reset(); ast_manager& m = m_ctx.get_manager(); datalog::rule_manager& rm = m_ctx.get_rule_manager(); datalog::rule_set old_rules(m_ctx.get_rules()); func_decl_ref query_pred(m); rm.mk_query(query, m_ctx.get_rules()); expr_ref bg_assertion = m_ctx.get_background_assertion(); check_reset(); TRACE("pdr", if (!m.is_true(bg_assertion)) { tout << "axioms:\n"; tout << mk_pp(bg_assertion, m) << "\n"; } tout << "query: " << mk_pp(query, m) << "\n"; tout << "rules:\n"; m_ctx.display_rules(tout); ); apply_default_transformation(m_ctx); if (m_ctx.get_params().xform_slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); m_ctx.transform_rules(transformer); // track sliced predicates. obj_map const& preds = slice->get_predicates(); obj_map::iterator it = preds.begin(); obj_map::iterator end = preds.end(); for (; it != end; ++it) { m_pred2slice.insert(it->m_key, it->m_value); m_refs.push_back(it->m_key); m_refs.push_back(it->m_value); } } if (m_ctx.get_params().xform_unfold_rules() > 0) { unsigned num_unfolds = m_ctx.get_params().xform_unfold_rules(); datalog::rule_transformer transf1(m_ctx), transf2(m_ctx); transf1.register_plugin(alloc(datalog::mk_coalesce, m_ctx)); transf2.register_plugin(alloc(datalog::mk_unfold, m_ctx)); if (m_ctx.get_params().xform_coalesce_rules()) { m_ctx.transform_rules(transf1); } while (num_unfolds > 0) { m_ctx.transform_rules(transf2); --num_unfolds; } } if (m_ctx.get_rules().get_output_predicates().empty()) { m_context->set_unsat(); return l_false; } query_pred = m_ctx.get_rules().get_output_predicate(); IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); m_pdr_rules.replace_rules(m_ctx.get_rules()); m_pdr_rules.close(); m_ctx.record_transformed_rules(); m_ctx.reopen(); m_ctx.replace_rules(old_rules); scoped_restore_proof _sc(m); // update_rules may overwrite the proof mode. m_context->set_proof_converter(m_ctx.get_proof_converter()); m_context->set_model_converter(m_ctx.get_model_converter()); m_context->set_query(query_pred); m_context->set_axioms(bg_assertion); m_context->update_rules(m_pdr_rules); if (m_pdr_rules.get_rules().empty()) { m_context->set_unsat(); IF_VERBOSE(1, model_smt2_pp(verbose_stream(), m, *m_context->get_model(),0);); return l_false; } return m_context->solve(); } expr_ref dl_interface::get_cover_delta(int level, func_decl* pred_orig) { func_decl* pred = pred_orig; m_pred2slice.find(pred_orig, pred); SASSERT(pred); return m_context->get_cover_delta(level, pred_orig, pred); } void dl_interface::add_cover(int level, func_decl* pred, expr* property) { if (m_ctx.get_params().xform_slice()) { throw default_exception("Covers are incompatible with slicing. Disable slicing before using covers"); } m_context->add_cover(level, pred, property); } unsigned dl_interface::get_num_levels(func_decl* pred) { m_pred2slice.find(pred, pred); SASSERT(pred); return m_context->get_num_levels(pred); } void dl_interface::collect_statistics(statistics& st) const { m_context->collect_statistics(st); } void dl_interface::reset_statistics() { m_context->reset_statistics(); } void dl_interface::display_certificate(std::ostream& out) const { m_context->display_certificate(out); } expr_ref dl_interface::get_answer() { return m_context->get_answer(); } void dl_interface::cancel() { m_context->cancel(); } void dl_interface::cleanup() { m_context->cleanup(); } void dl_interface::updt_params() { dealloc(m_context); m_context = alloc(pdr::context, m_ctx.get_fparams(), m_ctx.get_params(), m_ctx.get_manager()); } model_ref dl_interface::get_model() { return m_context->get_model(); } proof_ref dl_interface::get_proof() { return m_context->get_proof(); } z3-4.4.0/src/muz/pdr/pdr_prop_solver.cpp0000644000175000017500000003602312540347414017763 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: prop_solver.cpp Abstract: SMT solver abstraction for PDR. Author: Krystof Hoder (t-khoder) 2011-8-17. Revision History: --*/ #include #include "model.h" #include "pdr_util.h" #include "pdr_prop_solver.h" #include "ast_smt2_pp.h" #include "dl_util.h" #include "model_pp.h" #include "smt_params.h" #include "datatype_decl_plugin.h" #include "bv_decl_plugin.h" #include "pdr_farkas_learner.h" #include "ast_smt2_pp.h" #include "expr_replacer.h" // // Auxiliary structure to introduce propositional names for assumptions that are not // propositional. It is to work with the smt::context's restriction // that assumptions be propositional literals. // namespace pdr { class prop_solver::safe_assumptions { prop_solver& s; ast_manager& m; expr_ref_vector m_atoms; expr_ref_vector m_assumptions; obj_map m_proxies2expr; obj_map m_expr2proxies; unsigned m_num_proxies; app * mk_proxy(expr* literal) { app* res; SASSERT(!is_var(literal)); //it doesn't make sense to introduce names to variables if (m_expr2proxies.find(literal, res)) { return res; } SASSERT(s.m_proxies.size() >= m_num_proxies); if (m_num_proxies == s.m_proxies.size()) { std::stringstream name; name << "pdr_proxy_" << s.m_proxies.size(); res = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()); s.m_proxies.push_back(res); s.m_aux_symbols.insert(res->get_decl()); } else { res = s.m_proxies[m_num_proxies].get(); } ++m_num_proxies; m_expr2proxies.insert(literal, res); m_proxies2expr.insert(res, literal); expr_ref implies(m.mk_or(m.mk_not(res), literal), m); s.m_ctx->assert_expr(implies); m_assumptions.push_back(implies); TRACE("pdr_verbose", tout << "name asserted " << mk_pp(implies, m) << "\n";); return res; } void mk_safe(expr_ref_vector& conjs) { qe::flatten_and(conjs); expand_literals(conjs); for (unsigned i = 0; i < conjs.size(); ++i) { expr * lit = conjs[i].get(); expr * lit_core = lit; m.is_not(lit, lit_core); SASSERT(!m.is_true(lit)); if (!is_uninterp(lit_core) || to_app(lit_core)->get_num_args() != 0) { conjs[i] = mk_proxy(lit); } } 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); bv_util bv(m); expr* e1, *e2, *c, *val; 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)) { conjs[i] = arith.mk_le(e1,e2); if (i+1 == conjs.size()) { conjs.push_back(arith.mk_ge(e1, e2)); } else { conjs.push_back(conjs[i+1].get()); conjs[i+1] = arith.mk_ge(e1, e2); } ++i; } else if ((m.is_eq(e, c, val) && is_app(val) && dt.is_constructor(to_app(val))) || (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); ptr_vector const& acc = *dt.get_constructor_accessors(f); 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)) || (m.is_eq(e, val, c) && bv.is_numeral(val, r, bv_size))) { rational two(2); for (unsigned j = 0; j < bv_size; ++j) { parameter p(j); //expr* e = m.mk_app(bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &c); expr* e = m.mk_eq(m.mk_app(bv.get_family_id(), OP_BIT1), bv.mk_extract(j, j, c)); if ((r % two).is_zero()) { e = m.mk_not(e); } r = div(r, two); if (j == 0) { conjs[i] = e; } else { conjs.push_back(e); } } } } TRACE("pdr", tout << "end expand\n"; for (unsigned i = 0; i < conjs.size(); ++i) { tout << mk_pp(conjs[i].get(), m) << "\n"; }); } public: safe_assumptions(prop_solver& s, expr_ref_vector const& assumptions): s(s), m(s.m), m_atoms(assumptions), m_assumptions(m), m_num_proxies(0) { mk_safe(m_atoms); } ~safe_assumptions() { } expr_ref_vector const& atoms() const { return m_atoms; } unsigned assumptions_size() const { return m_assumptions.size(); } expr* assumptions(unsigned i) const { return m_assumptions[i]; } void undo_proxies(expr_ref_vector& es) { expr_ref e(m); expr* r; for (unsigned i = 0; i < es.size(); ++i) { e = es[i].get(); if (is_app(e) && m_proxies2expr.find(to_app(e), r)) { es[i] = r; } } } void elim_proxies(expr_ref_vector& es) { expr_substitution sub(m, false, m.proofs_enabled()); proof_ref pr(m); if (m.proofs_enabled()) { pr = m.mk_asserted(m.mk_true()); } obj_map::iterator it = m_proxies2expr.begin(), end = m_proxies2expr.end(); for (; it != end; ++it) { sub.insert(it->m_key, m.mk_true(), pr); } scoped_ptr rep = mk_default_expr_replacer(m); rep->set_substitution(&sub); replace_proxies(*rep, es); } private: void replace_proxies(expr_replacer& rep, expr_ref_vector& es) { expr_ref e(m); for (unsigned i = 0; i < es.size(); ++i) { e = es[i].get(); rep(e); es[i] = e; if (m.is_true(e)) { es[i] = es.back(); es.pop_back(); --i; } } } }; prop_solver::prop_solver(manager& pm, symbol const& name) : m_fparams(pm.get_fparams()), m(pm.get_manager()), m_pm(pm), m_name(name), m_ctx(pm.mk_fresh()), m_pos_level_atoms(m), m_neg_level_atoms(m), m_proxies(m), m_core(0), m_model(0), m_consequences(0), m_subset_based_core(false), m_use_farkas(false), m_in_level(false), m_current_level(0) { m_ctx->assert_expr(m_pm.get_background()); } void prop_solver::add_level() { unsigned idx = level_cnt(); std::stringstream name; name << m_name << "#level_" << idx; func_decl * lev_pred = m.mk_fresh_func_decl(name.str().c_str(), 0, 0,m.mk_bool_sort()); m_aux_symbols.insert(lev_pred); m_level_preds.push_back(lev_pred); app_ref pos_la(m.mk_const(lev_pred), m); app_ref neg_la(m.mk_not(pos_la.get()), m); m_pos_level_atoms.push_back(pos_la); m_neg_level_atoms.push_back(neg_la); m_level_atoms_set.insert(pos_la.get()); m_level_atoms_set.insert(neg_la.get()); } void prop_solver::ensure_level(unsigned lvl) { while (lvl>=level_cnt()) { add_level(); } } unsigned prop_solver::level_cnt() const { return m_level_preds.size(); } void prop_solver::push_level_atoms(unsigned level, expr_ref_vector& tgt) const { unsigned lev_cnt = level_cnt(); for (unsigned i=0; i=level; app * lev_atom = active ? m_neg_level_atoms[i] : m_pos_level_atoms[i]; tgt.push_back(lev_atom); } } void prop_solver::add_formula(expr * form) { SASSERT(!m_in_level); m_ctx->assert_expr(form); IF_VERBOSE(21, verbose_stream() << "$ asserted " << mk_pp(form, m) << "\n";); TRACE("pdr", tout << "add_formula: " << mk_pp(form, m) << "\n";); } void prop_solver::add_level_formula(expr * form, unsigned level) { ensure_level(level); app * lev_atom = m_pos_level_atoms[level].get(); app_ref lform(m.mk_or(form, lev_atom), m); add_formula(lform.get()); } lbool prop_solver::check_safe_assumptions( safe_assumptions& safe, const expr_ref_vector& atoms) { flet _model(m_fparams.m_model, m_model != 0); expr_ref_vector expr_atoms(m); expr_atoms.append(atoms.size(), atoms.c_ptr()); if (m_in_level) { push_level_atoms(m_current_level, expr_atoms); } lbool result = m_ctx->check(expr_atoms); TRACE("pdr", tout << mk_pp(m_pm.mk_and(expr_atoms), m) << "\n"; tout << result << "\n";); if (result == l_true && m_model) { m_ctx->get_model(*m_model); TRACE("pdr_verbose", model_pp(tout, **m_model); ); } if (result == l_false) { unsigned core_size = m_ctx->get_unsat_core_size(); m_assumes_level = false; for (unsigned i = 0; i < core_size; ++i) { if (m_level_atoms_set.contains(m_ctx->get_unsat_core_expr(i))) { m_assumes_level = true; break; } } } if (result == l_false && m_core && m.proofs_enabled() && m_use_farkas && !m_subset_based_core) { extract_theory_core(safe); } else if (result == l_false && m_core) { extract_subset_core(safe); SASSERT(expr_atoms.size() >= m_core->size()); } m_core = 0; m_model = 0; m_subset_based_core = false; return result; } void prop_solver::extract_subset_core(safe_assumptions& safe) { unsigned core_size = m_ctx->get_unsat_core_size(); m_core->reset(); for (unsigned i = 0; i < core_size; ++i) { expr * core_expr = m_ctx->get_unsat_core_expr(i); SASSERT(is_app(core_expr)); if (m_level_atoms_set.contains(core_expr)) { continue; } if (m_ctx->is_aux_predicate(core_expr)) { continue; } m_core->push_back(to_app(core_expr)); } safe.undo_proxies(*m_core); TRACE("pdr", tout << "core_exprs: "; for (unsigned i = 0; i < core_size; ++i) { tout << mk_pp(m_ctx->get_unsat_core_expr(i), m) << " "; } tout << "\n"; tout << "core: " << mk_pp(m_pm.mk_and(*m_core), m) << "\n"; ); } void prop_solver::extract_theory_core(safe_assumptions& safe) { proof_ref pr(m); pr = m_ctx->get_proof(); IF_VERBOSE(21, verbose_stream() << mk_ismt2_pp(pr, m) << "\n";); farkas_learner fl(m_fparams, m); expr_ref_vector lemmas(m); obj_hashtable bs; for (unsigned i = 0; i < safe.assumptions_size(); ++i) { bs.insert(safe.assumptions(i)); } fl.get_lemmas(pr, bs, lemmas); safe.elim_proxies(lemmas); fl.simplify_lemmas(lemmas); // redundant? bool outside_of_logic = (m_fparams.m_arith_mode == AS_DIFF_LOGIC && !is_difference_logic(m, lemmas.size(), lemmas.c_ptr())) || (m_fparams.m_arith_mode == AS_UTVPI && !is_utvpi_logic(m, lemmas.size(), lemmas.c_ptr())); if (outside_of_logic) { IF_VERBOSE(2, verbose_stream() << "not diff\n"; for (unsigned i = 0; i < lemmas.size(); ++i) { verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n"; }); extract_subset_core(safe); } else { IF_VERBOSE(2, verbose_stream() << "Lemmas\n"; for (unsigned i = 0; i < lemmas.size(); ++i) { verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n"; }); m_core->reset(); m_core->append(lemmas); if (m_consequences) { fl.get_consequences(pr, bs, *m_consequences); } } } lbool prop_solver::check_assumptions(const expr_ref_vector & atoms) { return check_assumptions_and_formula(atoms, m.mk_true()); } lbool prop_solver::check_conjunction_as_assumptions(expr * conj) { expr_ref_vector asmp(m); asmp.push_back(conj); return check_assumptions(asmp); } lbool prop_solver::check_assumptions_and_formula(const expr_ref_vector & atoms, expr * form) { pdr::smt_context::scoped _scoped(*m_ctx); safe_assumptions safe(*this, atoms); m_ctx->assert_expr(form); CTRACE("pdr", !m.is_true(form), tout << "check with formula: " << mk_pp(form, m) << "\n";); lbool res = check_safe_assumptions(safe, safe.atoms()); // // we don't have to undo model naming, as from the model // we extract the values for state variables directly // return res; } void prop_solver::collect_statistics(statistics& st) const { } void prop_solver::reset_statistics() { } } z3-4.4.0/src/muz/pdr/pdr_smt_context_manager.h0000644000175000017500000000541712540347414021122 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdr_smt_context_manager.h Abstract: Manager of smt contexts Author: Nikolaj Bjorner (nbjorner) 2011-11-26. Revision History: --*/ #ifndef _PDR_SMT_CONTEXT_MANAGER_H_ #define _PDR_SMT_CONTEXT_MANAGER_H_ #include "smt_kernel.h" #include "func_decl_dependencies.h" #include "dl_util.h" namespace pdr { class smt_context_manager; class smt_context { protected: app_ref m_pred; smt_context_manager& m_parent; bool m_in_delay_scope; bool m_pushed; public: smt_context(smt_context_manager& p, ast_manager& m, app* pred); virtual ~smt_context() {} virtual void assert_expr(expr* e) = 0; virtual lbool check(expr_ref_vector& assumptions) = 0; virtual void get_model(model_ref& model) = 0; virtual proof* get_proof() = 0; virtual unsigned get_unsat_core_size() = 0; virtual expr* get_unsat_core_expr(unsigned i) = 0; virtual void push() = 0; virtual void pop() = 0; bool is_aux_predicate(func_decl* p); bool is_aux_predicate(expr* p) { return is_app(p) && is_aux_predicate(to_app(p)->get_decl()); } class scoped { smt_context& m_ctx; public: scoped(smt_context& ctx); ~scoped(); }; }; class _smt_context : public smt_context { smt::kernel & m_context; public: _smt_context(smt::kernel & ctx, smt_context_manager& p, app* pred); virtual ~_smt_context() {} virtual void assert_expr(expr* e); virtual lbool check(expr_ref_vector& assumptions); virtual void get_model(model_ref& model); virtual proof* get_proof(); virtual void push() { m_context.push(); } virtual void pop() { m_context.pop(1); } virtual unsigned get_unsat_core_size() { return m_context.get_unsat_core_size(); } virtual expr* get_unsat_core_expr(unsigned i) { return m_context.get_unsat_core_expr(i); } }; class smt_context_manager { smt_params& m_fparams; ast_manager& m; unsigned m_max_num_contexts; ptr_vector m_contexts; unsigned m_num_contexts; app_ref_vector m_predicate_list; func_decl_set m_predicate_set; public: smt_context_manager(smt_params& fp, unsigned max_num_contexts, ast_manager& m); ~smt_context_manager(); smt_context* mk_fresh(); void collect_statistics(statistics& st) const; void reset_statistics(); bool is_aux_predicate(func_decl* p) const { return m_predicate_set.contains(p); } }; }; #endif z3-4.4.0/src/muz/pdr/pdr_sym_mux.cpp0000644000175000017500000003766612540347414017130 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sym_mux.cpp Abstract: A symbol multiplexer that helps with having multiple versions of each of a set of symbols. Author: Krystof Hoder (t-khoder) 2011-9-8. Revision History: --*/ #include #include "ast_pp.h" #include "for_each_expr.h" #include "model.h" #include "rewriter.h" #include "rewriter_def.h" #include "pdr_util.h" #include "pdr_sym_mux.h" using namespace pdr; sym_mux::sym_mux(ast_manager & m, const vector & suffixes) : m(m), m_ref_holder(m), m_next_sym_suffix_idx(0), m_suffixes(suffixes) { unsigned suf_sz = m_suffixes.size(); for(unsigned i = 0; i < suf_sz; ++i) { symbol suff_sym = symbol(m_suffixes[i].c_str()); m_used_suffixes.insert(suff_sym); } } std::string sym_mux::get_suffix(unsigned i) const { while(m_suffixes.size() <= i) { std::string new_suffix; symbol new_syffix_sym; do { std::stringstream stm; stm<<'_'<0); while(tuple.size()get_name().str(); for(unsigned i=0; iget_arity()==arity); SASSERT(tuple[i]->get_range()==range); //domain should match as well, but we won't bother checking an array equality } else { std::string name = pre+get_suffix(i); tuple[i] = m.mk_func_decl(symbol(name.c_str()), arity, domain, range); } m_ref_holder.push_back(tuple[i]); m_sym2idx.insert(tuple[i], i); m_sym2prim.insert(tuple[i], tuple[0]); } m_prim2all.insert(tuple[0], tuple); m_prefix2prim.insert(prefix, tuple[0]); m_prim2prefix.insert(tuple[0], prefix); m_prim_preds.push_back(tuple[0]); m_ref_holder.push_back(prefix); } void sym_mux::ensure_tuple_size(func_decl * prim, unsigned sz) const { SASSERT(m_prim2all.contains(prim)); decl_vector& tuple = m_prim2all.find_core(prim)->get_data().m_value; SASSERT(tuple[0]==prim); if(sz <= tuple.size()) { return; } func_decl * prefix; TRUSTME(m_prim2prefix.find(prim, prefix)); std::string prefix_name = prefix->get_name().bare_str(); for(unsigned i=tuple.size(); iget_arity(), prefix->get_domain(), prefix->get_range()); tuple.push_back(new_sym); m_ref_holder.push_back(new_sym); m_sym2idx.insert(new_sym, i); m_sym2prim.insert(new_sym, prim); } } func_decl * sym_mux::conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx) const { if(src_idx==tgt_idx) { return sym; } func_decl * prim = (src_idx==0) ? sym : get_primary(sym); if(tgt_idx>src_idx) { ensure_tuple_size(prim, tgt_idx+1); } decl_vector & sym_vect = m_prim2all.find_core(prim)->get_data().m_value; SASSERT(sym_vect[src_idx]==sym); return sym_vect[tgt_idx]; } func_decl * sym_mux::get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx, unsigned arity, sort * const * domain, sort * range) { func_decl * prim = try_get_primary_by_prefix(prefix); if(prim) { SASSERT(prim->get_arity()==arity); SASSERT(prim->get_range()==range); //domain should match as well, but we won't bother checking an array equality return conv(prim, 0, idx); } decl_vector syms; create_tuple(prefix, arity, domain, range, idx+1, syms); return syms[idx]; } bool sym_mux::is_muxed_lit(expr * e, unsigned idx) const { if(!is_app(e)) { return false; } app * a = to_app(e); if(m.is_not(a) && is_app(a->get_arg(0))) { a = to_app(a->get_arg(0)); } return is_muxed(a->get_decl()); } struct sym_mux::formula_checker { formula_checker(const sym_mux & parent, bool all, unsigned idx) : m_parent(parent), m_all(all), m_idx(idx), m_found_what_needed(false) { } void operator()(expr * e) { if(m_found_what_needed || !is_app(e)) { return; } func_decl * sym = to_app(e)->get_decl(); unsigned sym_idx; if(!m_parent.try_get_index(sym, sym_idx)) { return; } bool have_idx = sym_idx==m_idx; if( m_all ? (!have_idx) : have_idx ) { m_found_what_needed = true; } } bool all_have_idx() const { SASSERT(m_all); //we were looking for the queried property return !m_found_what_needed; } bool some_with_idx() const { SASSERT(!m_all); //we were looking for the queried property return m_found_what_needed; } private: const sym_mux & m_parent; bool m_all; unsigned m_idx; /** If we check whether all muxed symbols are of given index, we look for counter-examples, checking whether form contains a muxed symbol of an index, we look for symbol of index m_idx. */ bool m_found_what_needed; }; bool sym_mux::contains(expr * e, unsigned idx) const { formula_checker chck(*this, false, idx); for_each_expr(chck, m_visited, e); m_visited.reset(); return chck.some_with_idx(); } bool sym_mux::is_homogenous_formula(expr * e, unsigned idx) const { formula_checker chck(*this, true, idx); for_each_expr(chck, m_visited, e); m_visited.reset(); return chck.all_have_idx(); } bool sym_mux::is_homogenous(const expr_ref_vector & vect, unsigned idx) const { expr * const * begin = vect.c_ptr(); expr * const * end = begin + vect.size(); for(expr * const * it = begin; it!=end; it++) { if(!is_homogenous_formula(*it, idx)) { return false; } } return true; } class sym_mux::index_collector { sym_mux const& m_parent; svector m_indices; public: index_collector(sym_mux const& s): m_parent(s) {} void operator()(expr * e) { if (is_app(e)) { func_decl * sym = to_app(e)->get_decl(); unsigned idx; if (m_parent.try_get_index(sym, idx)) { SASSERT(idx > 0); --idx; if (m_indices.size() <= idx) { m_indices.resize(idx+1, false); } m_indices[idx] = true; } } } void extract(unsigned_vector& indices) { for (unsigned i = 0; i < m_indices.size(); ++i) { if (m_indices[i]) { indices.push_back(i); } } } }; void sym_mux::collect_indices(expr* e, unsigned_vector& indices) const { indices.reset(); index_collector collector(*this); for_each_expr(collector, m_visited, e); m_visited.reset(); collector.extract(indices); } class sym_mux::variable_collector { sym_mux const& m_parent; vector >& m_vars; public: variable_collector(sym_mux const& s, vector >& vars): m_parent(s), m_vars(vars) {} void operator()(expr * e) { if (is_app(e)) { func_decl * sym = to_app(e)->get_decl(); unsigned idx; if (m_parent.try_get_index(sym, idx)) { SASSERT(idx > 0); --idx; if (m_vars.size() <= idx) { m_vars.resize(idx+1, ptr_vector()); } m_vars[idx].push_back(to_app(e)); } } } }; void sym_mux::collect_variables(expr* e, vector >& vars) const { vars.reset(); variable_collector collector(*this, vars); for_each_expr(collector, m_visited, e); m_visited.reset(); } class sym_mux::hmg_checker { const sym_mux & m_parent; bool m_found_idx; unsigned m_idx; bool m_multiple_indexes; public: hmg_checker(const sym_mux & parent) : m_parent(parent), m_found_idx(false), m_multiple_indexes(false) { } void operator()(expr * e) { if(m_multiple_indexes || !is_app(e)) { return; } func_decl * sym = to_app(e)->get_decl(); unsigned sym_idx; if(!m_parent.try_get_index(sym, sym_idx)) { return; } if(!m_found_idx) { m_found_idx = true; m_idx = sym_idx; return; } if(m_idx==sym_idx) { return; } m_multiple_indexes = true; } bool has_multiple_indexes() const { return m_multiple_indexes; } }; bool sym_mux::is_homogenous_formula(expr * e) const { hmg_checker chck(*this); for_each_expr(chck, m_visited, e); m_visited.reset(); return !chck.has_multiple_indexes(); } struct sym_mux::conv_rewriter_cfg : public default_rewriter_cfg { private: ast_manager & m; const sym_mux & m_parent; unsigned m_from_idx; unsigned m_to_idx; bool m_homogenous; public: conv_rewriter_cfg(const sym_mux & parent, unsigned from_idx, unsigned to_idx, bool homogenous) : m(parent.get_manager()), m_parent(parent), m_from_idx(from_idx), m_to_idx(to_idx), m_homogenous(homogenous) {} bool get_subst(expr * s, expr * & t, proof * & t_pr) { if(!is_app(s)) { return false; } app * a = to_app(s); func_decl * sym = a->get_decl(); if(!m_parent.has_index(sym, m_from_idx)) { SASSERT(!m_homogenous || !m_parent.is_muxed(sym)); return false; } func_decl * tgt = m_parent.conv(sym, m_from_idx, m_to_idx); t = m.mk_app(tgt, a->get_args()); return true; } }; void sym_mux::conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous) const { if(src_idx==tgt_idx) { res = f; return; } conv_rewriter_cfg r_cfg(*this, src_idx, tgt_idx, homogenous); rewriter_tpl rwr(m, false, r_cfg); rwr(f, res); } struct sym_mux::shifting_rewriter_cfg : public default_rewriter_cfg { private: ast_manager & m; const sym_mux & m_parent; int m_shift; public: shifting_rewriter_cfg(const sym_mux & parent, int shift) : m(parent.get_manager()), m_parent(parent), m_shift(shift) {} bool get_subst(expr * s, expr * & t, proof * & t_pr) { if(!is_app(s)) { return false; } app * a = to_app(s); func_decl * sym = a->get_decl(); unsigned idx; if(!m_parent.try_get_index(sym, idx)) { return false; } SASSERT(static_cast(idx)+m_shift>=0); func_decl * tgt = m_parent.conv(sym, idx, idx+m_shift); t = m.mk_app(tgt, a->get_args()); return true; } }; void sym_mux::shift_formula(expr * f, int dist, expr_ref & res) const { if(dist==0) { res = f; return; } shifting_rewriter_cfg r_cfg(*this, dist); rewriter_tpl rwr(m, false, r_cfg); rwr(f, res); } void sym_mux::conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx, expr_ref_vector & res) const { res.reset(); expr * const * begin = vect.c_ptr(); expr * const * end = begin + vect.size(); for(expr * const * it = begin; it!=end; it++) { expr_ref converted(m); conv_formula(*it, src_idx, tgt_idx, converted); res.push_back(converted); } } void sym_mux::filter_idx(expr_ref_vector & vect, unsigned idx) const { unsigned i = 0; while (i < vect.size()) { expr* e = vect[i].get(); if(contains(e, idx) && is_homogenous_formula(e, idx)) { i++; } else { //we don't allow mixing states inside vector elements SASSERT(!contains(e, idx)); vect[i] = vect.back(); vect.pop_back(); } } } void sym_mux::partition_o_idx( expr_ref_vector const& lits, expr_ref_vector& o_lits, expr_ref_vector& other, unsigned idx) const { for (unsigned i = 0; i < lits.size(); ++i) { if (contains(lits[i], idx) && is_homogenous_formula(lits[i], idx)) { o_lits.push_back(lits[i]); } else { other.push_back(lits[i]); } } } class sym_mux::nonmodel_sym_checker { const sym_mux & m_parent; bool m_found; public: nonmodel_sym_checker(const sym_mux & parent) : m_parent(parent), m_found(false) { } void operator()(expr * e) { if(m_found || !is_app(e)) { return; } func_decl * sym = to_app(e)->get_decl(); if(m_parent.is_non_model_sym(sym)) { m_found = true; } } bool found() const { return m_found; } }; bool sym_mux::has_nonmodel_symbol(expr * e) const { nonmodel_sym_checker chck(*this); for_each_expr(chck, e); return chck.found(); } void sym_mux::filter_non_model_lits(expr_ref_vector & vect) const { unsigned i=0; while(iget_name(), sym2->get_name()); } }; std::string sym_mux::pp_model(const model_core & mdl) const { decl_vector consts; unsigned sz = mdl.get_num_constants(); for (unsigned i = 0; i < sz; i++) { func_decl * d = mdl.get_constant(i); consts.push_back(d); } std::sort(consts.begin(), consts.end(), decl_idx_comparator(*this)); std::stringstream res; decl_vector::iterator end = consts.end(); for(decl_vector::iterator it = consts.begin(); it!=end; it++) { func_decl * d = *it; std::string name = d->get_name().str(); const char * arrow = " -> "; res << name << arrow; unsigned indent = static_cast(name.length() + strlen(arrow)); res << mk_pp(mdl.get_const_interp(d), m, indent) << "\n"; if(it+1!=end) { unsigned idx1, idx2; if(!try_get_index(*it, idx1)) { idx1 = UINT_MAX; } if(!try_get_index(*(it+1), idx2)) { idx2 = UINT_MAX; } if(idx1!=idx2) { res << "\n"; } } } return res.str(); } #if 0 class sym_mux::index_renamer_cfg : public default_rewriter_cfg{ const sym_mux & m_parent; unsigned m_idx; public: index_renamer_cfg(const sym_mux & p, unsigned idx) : m_parent(p), m_idx(idx) {} bool get_subst(expr * s, expr * & t, proof * & t_pr) { if (!is_app(s)) return false; app * a = to_app(s); if (a->get_family_id() != null_family_id) { return false; } func_decl * sym = a->get_decl(); unsigned idx; if(!m_parent.try_get_index(sym, idx)) { return false; } if (m_idx == idx) { return false; } ast_manager& m = m_parent.get_manager(); symbol name = symbol((sym->get_name().str() + "!").c_str()); func_decl * tgt = m.mk_func_decl(name, sym->get_arity(), sym->get_domain(), sym->get_range()); t = m.mk_app(tgt, a->get_num_args(), a->get_args()); return true; } }; #endif z3-4.4.0/src/muz/pdr/pdr_generalizers.h0000644000175000017500000000764412540347414017557 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdr_generalizers.h Abstract: Generalizer plugins. Author: Nikolaj Bjorner (nbjorner) 2011-11-22. Revision History: --*/ #ifndef _PDR_GENERALIZERS_H_ #define _PDR_GENERALIZERS_H_ #include "pdr_context.h" #include "pdr_closure.h" #include "arith_decl_plugin.h" namespace pdr { class core_bool_inductive_generalizer : public core_generalizer { unsigned m_failure_limit; public: core_bool_inductive_generalizer(context& ctx, unsigned failure_limit) : core_generalizer(ctx), m_failure_limit(failure_limit) {} virtual ~core_bool_inductive_generalizer() {} virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level); }; template class r_map : public map { }; class core_arith_inductive_generalizer : public core_generalizer { typedef std::pair term_loc_t; typedef r_map > bounds_t; ast_manager& m; arith_util a; expr_ref_vector m_refs; bounds_t m_lb; bounds_t m_ub; struct eq { expr* m_term; rational m_value; unsigned m_i; unsigned m_j; eq(expr* t, rational const& r, unsigned i, unsigned j): m_term(t), m_value(r), m_i(i), m_j(j) {} }; void reset(); void insert_bound(bool is_lower, expr* x, rational const& r, unsigned i); void get_eqs(expr_ref_vector const& core, svector& eqs); bool substitute_alias(rational const&r, expr* x, expr* e, expr_ref& result); public: core_arith_inductive_generalizer(context& ctx); virtual ~core_arith_inductive_generalizer() {} virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level); }; class core_farkas_generalizer : public core_generalizer { farkas_learner m_farkas_learner; public: core_farkas_generalizer(context& ctx, ast_manager& m, smt_params& p); virtual ~core_farkas_generalizer() {} virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level); virtual void collect_statistics(statistics& st) const; }; class core_convex_hull_generalizer : public core_generalizer { ast_manager& m; obj_map m_models; bool m_is_closure; void method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); void method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); bool strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B); bool is_unsat(expr_ref_vector const& As, expr* B); public: core_convex_hull_generalizer(context& ctx, bool is_closure); virtual ~core_convex_hull_generalizer() {} virtual void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level); }; class core_multi_generalizer : public core_generalizer { core_bool_inductive_generalizer m_gen; public: core_multi_generalizer(context& ctx, unsigned max_failures): core_generalizer(ctx), m_gen(ctx, max_failures) {} virtual ~core_multi_generalizer() {} virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level); virtual void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); }; class core_induction_generalizer : public core_generalizer { class imp; public: core_induction_generalizer(context& ctx): core_generalizer(ctx) {} virtual ~core_induction_generalizer() {} virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level); }; }; #endif z3-4.4.0/src/muz/pdr/pdr_closure.cpp0000644000175000017500000001230412540347414017061 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: pdr_closure.cpp Abstract: Utility functions for computing closures. Author: Nikolaj Bjorner (nbjorner) 2013-9-1. Revision History: --*/ #include "pdr_closure.h" #include "pdr_context.h" #include "expr_safe_replace.h" namespace pdr { expr_ref scaler::operator()(expr* e, expr* k, obj_map* translate) { m_cache[0].reset(); m_cache[1].reset(); m_translate = translate; m_k = k; return scale(e, false); } expr_ref scaler::scale(expr* e, bool is_mul) { expr* r; if (m_cache[is_mul].find(e, r)) { return expr_ref(r, m); } if (!is_app(e)) { return expr_ref(e, m); } app* ap = to_app(e); if (m_translate && m_translate->find(ap->get_decl(), r)) { return expr_ref(r, m); } if (!is_mul && a.is_numeral(e)) { return expr_ref(a.mk_mul(m_k, e), m); } expr_ref_vector args(m); bool is_mul_rec = is_mul || a.is_mul(e); for (unsigned i = 0; i < ap->get_num_args(); ++i) { args.push_back(scale(ap->get_arg(i), is_mul_rec)); } expr_ref result(m); result = m.mk_app(ap->get_decl(), args.size(), args.c_ptr()); m_cache[is_mul].insert(e, result); return result; } expr_ref scaler::undo_k(expr* e, expr* k) { expr_safe_replace sub(m); th_rewriter rw(m); expr_ref result(e, m); sub.insert(k, a.mk_numeral(rational(1), false)); sub(result); rw(result); return result; } closure::closure(pred_transformer& p, bool is_closure): m(p.get_manager()), m_pt(p), a(m), m_is_closure(is_closure), m_sigma(m), m_trail(m) {} void closure::add_variables(unsigned num_vars, expr_ref_vector& fmls) { manager& pm = m_pt.get_pdr_manager(); SASSERT(num_vars > 0); while (m_vars.size() < num_vars) { m_vars.resize(m_vars.size()+1); m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); } unsigned sz = m_pt.sig_size(); for (unsigned i = 0; i < sz; ++i) { expr* var; ptr_vector vars; func_decl* fn0 = m_pt.sig(i); func_decl* fn1 = pm.o2n(fn0, 0); sort* srt = fn0->get_range(); if (a.is_int_real(srt)) { for (unsigned j = 0; j < num_vars; ++j) { if (!m_vars[j].find(fn1, var)) { var = m.mk_fresh_const(fn1->get_name().str().c_str(), srt); m_trail.push_back(var); m_vars[j].insert(fn1, var); } vars.push_back(var); } fmls.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(num_vars, vars.c_ptr()))); } } if (m_is_closure) { for (unsigned i = 0; i < num_vars; ++i) { fmls.push_back(a.mk_ge(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real()))); } } else { // is interior: for (unsigned i = 0; i < num_vars; ++i) { fmls.push_back(a.mk_gt(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real()))); } } fmls.push_back(m.mk_eq(a.mk_numeral(rational(1), a.mk_real()), a.mk_add(num_vars, m_sigma.c_ptr()))); } expr_ref closure::close_fml(expr* e) { expr* e0, *e1, *e2; expr_ref result(m); if (a.is_lt(e, e1, e2)) { result = a.mk_le(e1, e2); } else if (a.is_gt(e, e1, e2)) { result = a.mk_ge(e1, e2); } else if (m.is_not(e, e0) && a.is_ge(e0, e1, e2)) { result = a.mk_le(e1, e2); } else if (m.is_not(e, e0) && a.is_le(e0, e1, e2)) { result = a.mk_ge(e1, e2); } else if (a.is_ge(e) || a.is_le(e) || m.is_eq(e) || (m.is_not(e, e0) && (a.is_gt(e0) || a.is_lt(e0)))) { result = e; } else { IF_VERBOSE(1, verbose_stream() << "Cannot close: " << mk_pp(e, m) << "\n";); result = m.mk_true(); } return result; } expr_ref closure::close_conjunction(expr* fml) { expr_ref_vector fmls(m); qe::flatten_and(fml, fmls); for (unsigned i = 0; i < fmls.size(); ++i) { fmls[i] = close_fml(fmls[i].get()); } return qe::mk_and(fmls); } expr_ref closure::relax(unsigned i, expr* fml) { scaler sc(m); expr_ref result = sc(fml, m_sigma[i].get(), &m_vars[i]); return close_conjunction(result); } expr_ref closure::operator()(expr_ref_vector const& As) { if (As.empty()) { return expr_ref(m.mk_false(), m); } if (As.size() == 1) { return expr_ref(As[0], m); } expr_ref_vector fmls(m); expr_ref B(m); add_variables(As.size(), fmls); for (unsigned i = 0; i < As.size(); ++i) { fmls.push_back(relax(i, As[i])); } B = qe::mk_and(fmls); return B; } } z3-4.4.0/src/muz/pdr/pdr_reachable_cache.cpp0000644000175000017500000000623612540347414020445 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: reachable_cache.cpp Abstract: Object for caching of reachable states. Author: Krystof Hoder (t-khoder) 2011-9-14. Revision History: --*/ #include "pdr_reachable_cache.h" namespace pdr { reachable_cache::reachable_cache(pdr::manager & pm, datalog::PDR_CACHE_MODE cm) : m(pm.get_manager()), m_pm(pm), m_ctx(0), m_ref_holder(m), m_disj_connector(m), m_cache_mode(cm) { if (m_cache_mode == datalog::CONSTRAINT_CACHE) { m_ctx = pm.mk_fresh(); m_ctx->assert_expr(m_pm.get_background()); } } void reachable_cache::add_disjuncted_formula(expr * f) { app_ref new_connector(m.mk_fresh_const("disj_conn", m.mk_bool_sort()), m); app_ref neg_new_connector(m.mk_not(new_connector), m); app_ref extended_form(m); if(m_disj_connector) { extended_form = m.mk_or(m_disj_connector, neg_new_connector, f); } else { extended_form = m.mk_or(neg_new_connector, f); } if (m_ctx) { m_ctx->assert_expr(extended_form); } m_disj_connector = new_connector; } void reachable_cache::add_reachable(expr * cube) { switch (m_cache_mode) { case datalog::NO_CACHE: break; case datalog::HASH_CACHE: m_stats.m_inserts++; m_cache.insert(cube); m_ref_holder.push_back(cube); break; case datalog::CONSTRAINT_CACHE: m_stats.m_inserts++; TRACE("pdr", tout << mk_pp(cube, m) << "\n";); add_disjuncted_formula(cube); break; default: UNREACHABLE(); } } bool reachable_cache::is_reachable(expr * cube) { bool found = false; switch (m_cache_mode) { case datalog::NO_CACHE: return false; case datalog::HASH_CACHE: found = m_cache.contains(cube); break; case datalog::CONSTRAINT_CACHE: { if(!m_disj_connector) { found = false; break; } expr * connector = m_disj_connector.get(); expr_ref_vector assms(m); assms.push_back(connector); m_ctx->push(); m_ctx->assert_expr(cube); lbool res = m_ctx->check(assms); m_ctx->pop(); TRACE("pdr", tout << "is_reachable: " << res << " " << mk_pp(cube, m) << "\n";); found = res == l_true; break; } default: UNREACHABLE(); break; } if (found) m_stats.m_hits++; m_stats.m_miss++; return found; } void reachable_cache::collect_statistics(statistics& st) const { st.update("cache inserts", m_stats.m_inserts); st.update("cache miss", m_stats.m_miss); st.update("cache hits", m_stats.m_hits); } void reachable_cache::reset_statistics() { m_stats.reset(); } } z3-4.4.0/src/muz/pdr/pdr_context.cpp0000644000175000017500000025015512540347414017101 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdr_context.cpp Abstract: PDR predicate transformers and search context. Author: Nikolaj Bjorner (nbjorner) 2011-11-20. Revision History: Based on pdr_dl.cpp by Krystof Hoder (t-khoder) 2011-9-19. Notes: --*/ #include #include "dl_util.h" #include "rewriter.h" #include "rewriter_def.h" #include "var_subst.h" #include "util.h" #include "pdr_prop_solver.h" #include "pdr_context.h" #include "pdr_generalizers.h" #include "for_each_expr.h" #include "dl_rule_set.h" #include "unit_subsumption_tactic.h" #include "model_smt2_pp.h" #include "dl_mk_rule_inliner.h" #include "ast_smt2_pp.h" #include "qe_lite.h" #include "ast_ll_pp.h" #include "proof_checker.h" #include "smt_value_sort.h" #include "proof_utils.h" #include "dl_boogie_proof.h" #include "qe_util.h" #include "scoped_proof.h" #include "blast_term_ite_tactic.h" #include "model_implicant.h" #include "expr_safe_replace.h" namespace pdr { static const unsigned infty_level = UINT_MAX; static bool is_infty_level(unsigned lvl) { return lvl == infty_level; } static unsigned next_level(unsigned lvl) { return is_infty_level(lvl)?lvl:(lvl+1); } struct pp_level { unsigned m_level; pp_level(unsigned l): m_level(l) {} }; static std::ostream& operator<<(std::ostream& out, pp_level const& p) { if (is_infty_level(p.m_level)) { return out << "oo"; } else { return out << p.m_level; } } // ---------------- // pred_tansformer pred_transformer::pred_transformer(context& ctx, manager& pm, func_decl* head): pm(pm), m(pm.get_manager()), ctx(ctx), m_head(head, m), m_sig(m), m_solver(pm, head->get_name()), m_invariants(m), m_transition(m), m_initial_state(m), m_reachable(pm, (datalog::PDR_CACHE_MODE)ctx.get_params().pdr_cache_mode()) {} pred_transformer::~pred_transformer() { rule2inst::iterator it2 = m_rule2inst.begin(), end2 = m_rule2inst.end(); for (; it2 != end2; ++it2) { dealloc(it2->m_value); } rule2expr::iterator it3 = m_rule2transition.begin(), end3 = m_rule2transition.end(); for (; it3 != end3; ++it3) { m.dec_ref(it3->m_value); } } std::ostream& pred_transformer::display(std::ostream& out) const { if (!rules().empty()) out << "rules\n"; datalog::rule_manager& rm = ctx.get_context().get_rule_manager(); for (unsigned i = 0; i < rules().size(); ++i) { rm.display_smt2(*rules()[i], out) << "\n"; } out << "transition\n" << mk_pp(transition(), m) << "\n"; return out; } void pred_transformer::collect_statistics(statistics& st) const { m_solver.collect_statistics(st); m_reachable.collect_statistics(st); st.update("PDR num propagations", m_stats.m_num_propagations); unsigned np = m_invariants.size(); for (unsigned i = 0; i < m_levels.size(); ++i) { np += m_levels[i].size(); } st.update("PDR num properties", np); } void pred_transformer::reset_statistics() { m_solver.reset_statistics(); m_reachable.reset_statistics(); m_stats.reset(); } void pred_transformer::init_sig() { if (m_sig.empty()) { for (unsigned i = 0; i < m_head->get_arity(); ++i) { sort * arg_sort = m_head->get_domain(i); std::stringstream name_stm; name_stm << m_head->get_name() << '_' << i; func_decl_ref stm(m); stm = m.mk_func_decl(symbol(name_stm.str().c_str()), 0, (sort*const*)0, arg_sort); m_sig.push_back(pm.get_o_pred(stm, 0)); } } } void pred_transformer::ensure_level(unsigned level) { if (is_infty_level(level)) { return; } while (m_levels.size() <= level) { m_solver.add_level(); m_levels.push_back(expr_ref_vector(m)); } } bool pred_transformer::is_reachable(expr* state) { return m_reachable.is_reachable(state); } datalog::rule const& pred_transformer::find_rule(model_core const& model) const { obj_map::iterator it = m_tag2rule.begin(), end = m_tag2rule.end(); TRACE("pdr_verbose", datalog::rule_manager& rm = ctx.get_context().get_rule_manager(); for (; it != end; ++it) { expr* pred = it->m_key; tout << mk_pp(pred, m) << ":\n"; if (it->m_value) rm.display_smt2(*it->m_value, tout) << "\n"; } ); it = m_tag2rule.begin(); if (m_tag2rule.size() == 1) { return *it->m_value; } expr_ref vl(m); for (; it != end; ++it) { expr* pred = it->m_key; if (model.eval(to_app(pred)->get_decl(), vl) && m.is_true(vl)) { return *it->m_value; } } UNREACHABLE(); return *((datalog::rule*)0); } void pred_transformer::find_predecessors(datalog::rule const& r, ptr_vector& preds) const { preds.reset(); unsigned tail_sz = r.get_uninterpreted_tail_size(); for (unsigned ti = 0; ti < tail_sz; ti++) { preds.push_back(r.get_tail(ti)->get_decl()); } } void pred_transformer::remove_predecessors(expr_ref_vector& literals) { // remove tags for (unsigned i = 0; i < literals.size(); ) { expr* l = literals[i].get(); m.is_not(l, l); if (m_tag2rule.contains(l)) { literals[i] = literals.back(); literals.pop_back(); } else { ++i; } } } void pred_transformer::simplify_formulas(tactic& tac, expr_ref_vector& v) { goal_ref g(alloc(goal, m, false, false, false)); for (unsigned j = 0; j < v.size(); ++j) g->assert_expr(v[j].get()); model_converter_ref mc; proof_converter_ref pc; expr_dependency_ref core(m); goal_ref_buffer result; tac(g, result, mc, pc, core); SASSERT(result.size() == 1); goal* r = result[0]; v.reset(); for (unsigned j = 0; j < r->size(); ++j) v.push_back(r->form(j)); } void pred_transformer::simplify_formulas() { tactic_ref us = mk_unit_subsumption_tactic(m); simplify_formulas(*us, m_invariants); for (unsigned i = 0; i < m_levels.size(); ++i) { simplify_formulas(*us, m_levels[i]); } } expr_ref pred_transformer::get_formulas(unsigned level, bool add_axioms) { expr_ref_vector res(m); if (add_axioms) { res.push_back(pm.get_background()); res.push_back((level == 0)?initial_state():transition()); } res.append(m_invariants); for (unsigned i = level; i < m_levels.size(); ++i) { res.append(m_levels[i]); } return pm.mk_and(res); } expr_ref pred_transformer::get_propagation_formula(decl2rel const& pts, unsigned level) { expr_ref result(m), tmp1(m), tmp2(m); expr_ref_vector conj(m); if (level == 0) { conj.push_back(initial_state()); } else { conj.push_back(transition()); } conj.push_back(get_formulas(level, true)); obj_map::iterator it = m_tag2rule.begin(), end = m_tag2rule.end(); for (; level > 0 && it != end; ++it) { expr* tag = it->m_key; datalog::rule const* r = it->m_value; if (!r) continue; find_predecessors(*r, m_predicates); for (unsigned i = 0; i < m_predicates.size(); ++i) { func_decl* d = m_predicates[i]; pred_transformer & pt = *pts.find(d); tmp1 = pt.get_formulas(level-1, false); TRACE("pdr", tout << mk_pp(tmp1, m) << "\n";); pm.formula_n2o(tmp1, tmp2, i, false); conj.push_back(m.mk_implies(tag, tmp2)); } } return pm.mk_and(conj); } bool pred_transformer::propagate_to_next_level(unsigned src_level) { unsigned tgt_level = next_level(src_level); ensure_level(next_level(tgt_level)); expr_ref_vector& src = m_levels[src_level]; CTRACE("pdr", !src.empty(), tout << "propagating " << src_level << " to " << tgt_level; tout << " for relation " << head()->get_name() << "\n";); for (unsigned i = 0; i < src.size(); ) { expr * curr = src[i].get(); unsigned stored_lvl; VERIFY(m_prop2level.find(curr, stored_lvl)); SASSERT(stored_lvl >= src_level); bool assumes_level; if (stored_lvl > src_level) { TRACE("pdr", tout << "at level: "<< stored_lvl << " " << mk_pp(curr, m) << "\n";); src[i] = src.back(); src.pop_back(); } else if (is_invariant(tgt_level, curr, false, assumes_level)) { add_property(curr, assumes_level?tgt_level:infty_level); TRACE("pdr", tout << "is invariant: "<< pp_level(tgt_level) << " " << mk_pp(curr, m) << "\n";); src[i] = src.back(); src.pop_back(); ++m_stats.m_num_propagations; } else { TRACE("pdr", tout << "not propagated: " << mk_pp(curr, m) << "\n";); ++i; } } IF_VERBOSE(3, verbose_stream() << "propagate: " << pp_level(src_level) << "\n"; for (unsigned i = 0; i < src.size(); ++i) { verbose_stream() << mk_pp(src[i].get(), m) << "\n"; }); return src.empty(); } bool pred_transformer::add_property1(expr * lemma, unsigned lvl) { if (is_infty_level(lvl)) { if (!m_invariants.contains(lemma)) { TRACE("pdr", tout << "property1: " << head()->get_name() << " " << mk_pp(lemma, m) << "\n";); m_invariants.push_back(lemma); m_prop2level.insert(lemma, lvl); m_solver.add_formula(lemma); return true; } else { TRACE("pdr", tout << "already contained: " << head()->get_name() << " " << mk_pp(lemma, m) << "\n";); return false; } } ensure_level(lvl); unsigned old_level; if (!m_prop2level.find(lemma, old_level) || old_level < lvl) { TRACE("pdr", tout << "property1: " << pp_level(lvl) << " " << head()->get_name() << " " << mk_pp(lemma, m) << "\n";); m_levels[lvl].push_back(lemma); m_prop2level.insert(lemma, lvl); m_solver.add_level_formula(lemma, lvl); return true; } else { TRACE("pdr", tout << "old-level: " << pp_level(old_level) << " " << head()->get_name() << " " << mk_pp(lemma, m) << "\n";); return false; } } void pred_transformer::add_child_property(pred_transformer& child, expr* lemma, unsigned lvl) { ensure_level(lvl); expr_ref_vector fmls(m); mk_assumptions(child.head(), lemma, fmls); for (unsigned i = 0; i < fmls.size(); ++i) { TRACE("pdr", tout << "child property: " << mk_pp(fmls[i].get(), m) << "\n";); if (is_infty_level(lvl)) { m_solver.add_formula(fmls[i].get()); } else { m_solver.add_level_formula(fmls[i].get(), lvl); } } } void pred_transformer::add_property(expr* lemma, unsigned lvl) { expr_ref_vector lemmas(m); qe::flatten_and(lemma, lemmas); for (unsigned i = 0; i < lemmas.size(); ++i) { expr* lemma_i = lemmas[i].get(); if (add_property1(lemma_i, lvl)) { IF_VERBOSE(2, verbose_stream() << pp_level(lvl) << " " << mk_pp(lemma_i, m) << "\n";); for (unsigned j = 0; j < m_use.size(); ++j) { m_use[j]->add_child_property(*this, lemma_i, next_level(lvl)); } } } } expr_ref pred_transformer::get_cover_delta(func_decl* p_orig, int level) { expr_ref result(m.mk_true(), m), v(m), c(m); if (level == -1) { result = pm.mk_and(m_invariants); } else if ((unsigned)level < m_levels.size()) { result = pm.mk_and(m_levels[level]); } // replace local constants by bound variables. expr_substitution sub(m); for (unsigned i = 0; i < sig_size(); ++i) { c = m.mk_const(pm.o2n(sig(i), 0)); v = m.mk_var(i, sig(i)->get_range()); sub.insert(c, v); } scoped_ptr rep = mk_default_expr_replacer(m); rep->set_substitution(&sub); (*rep)(result); // adjust result according to model converter. unsigned arity = m_head->get_arity(); model_ref md = alloc(model, m); if (arity == 0) { md->register_decl(m_head, result); } else { func_interp* fi = alloc(func_interp, m, arity); fi->set_else(result); md->register_decl(m_head, fi); } model_converter_ref mc = ctx.get_model_converter(); apply(mc, md, 0); if (p_orig->get_arity() == 0) { result = md->get_const_interp(p_orig); } else { result = md->get_func_interp(p_orig)->get_interp(); } return result; } void pred_transformer::add_cover(unsigned level, expr* property) { // replace bound variables by local constants. expr_ref result(property, m), v(m), c(m); expr_substitution sub(m); for (unsigned i = 0; i < sig_size(); ++i) { c = m.mk_const(pm.o2n(sig(i), 0)); v = m.mk_var(i, sig(i)->get_range()); sub.insert(v, c); } scoped_ptr rep = mk_default_expr_replacer(m); rep->set_substitution(&sub); (*rep)(result); TRACE("pdr", tout << "cover:\n" << mk_pp(result, m) << "\n";); // add the property. add_property(result, level); } void pred_transformer::propagate_to_infinity(unsigned invariant_level) { expr_ref inv = get_formulas(invariant_level, false); add_property(inv, infty_level); // cleanup for (unsigned i = invariant_level; i < m_levels.size(); ++i) { m_levels[i].reset(); } } lbool pred_transformer::is_reachable(model_node& n, expr_ref_vector* core, bool& uses_level) { TRACE("pdr", tout << "is-reachable: " << head()->get_name() << " level: " << n.level() << "\n"; tout << mk_pp(n.state(), m) << "\n";); ensure_level(n.level()); model_ref model; prop_solver::scoped_level _sl(m_solver, n.level()); m_solver.set_core(core); m_solver.set_model(&model); lbool is_sat = m_solver.check_conjunction_as_assumptions(n.state()); if (is_sat == l_true && core) { core->reset(); TRACE("pdr", tout << "updating model\n"; model_smt2_pp(tout, m, *model, 0); tout << mk_pp(n.state(), m) << "\n";); n.set_model(model); } else if (is_sat == l_false) { uses_level = m_solver.assumes_level(); } return is_sat; } bool pred_transformer::is_invariant(unsigned level, expr* states, bool inductive, bool& assumes_level, expr_ref_vector* core) { expr_ref_vector conj(m); expr_ref tmp(m); conj.push_back(m.mk_not(states)); if (inductive) { mk_assumptions(head(), states, conj); } tmp = pm.mk_and(conj); prop_solver::scoped_level _sl(m_solver, level); m_solver.set_core(core); m_solver.set_model(0); lbool r = m_solver.check_conjunction_as_assumptions(tmp); if (r == l_false) { assumes_level = m_solver.assumes_level(); } return r == l_false; } bool pred_transformer::check_inductive(unsigned level, expr_ref_vector& lits, bool& assumes_level) { manager& pm = get_pdr_manager(); expr_ref_vector conj(m), core(m); expr_ref fml(m), states(m); states = m.mk_not(pm.mk_and(lits)); mk_assumptions(head(), states, conj); fml = pm.mk_and(conj); prop_solver::scoped_level _sl(m_solver, level); m_solver.set_core(&core); m_solver.set_subset_based_core(true); lbool res = m_solver.check_assumptions_and_formula(lits, fml); if (res == l_false) { lits.reset(); lits.append(core); assumes_level = m_solver.assumes_level(); } return res == l_false; } void pred_transformer::mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result) { expr_ref tmp1(m), tmp2(m); obj_map::iterator it = m_tag2rule.begin(), end = m_tag2rule.end(); for (; it != end; ++it) { expr* pred = it->m_key; datalog::rule const* r = it->m_value; if (!r) continue; find_predecessors(*r, m_predicates); for (unsigned i = 0; i < m_predicates.size(); i++) { func_decl* d = m_predicates[i]; if (d == head) { tmp1 = m.mk_implies(pred, fml); pm.formula_n2o(tmp1, tmp2, i); result.push_back(tmp2); } } } } void pred_transformer::initialize(decl2rel const& pts) { m_initial_state = m.mk_false(); m_transition = m.mk_true(); init_rules(pts, m_initial_state, m_transition); th_rewriter rw(m); rw(m_transition); rw(m_initial_state); m_solver.add_formula(m_transition); m_solver.add_level_formula(m_initial_state, 0); TRACE("pdr", tout << "Initial state: " << mk_pp(m_initial_state, m) << "\n"; tout << "Transition: " << mk_pp(m_transition, m) << "\n";); SASSERT(is_app(m_initial_state)); m_reachable.add_init(to_app(m_initial_state)); } void pred_transformer::init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transition) { expr_ref_vector transitions(m); ptr_vector tr_rules; datalog::rule const* rule; expr_ref_vector disj(m); app_ref pred(m); for (unsigned i = 0; i < rules().size(); ++i) { init_rule(pts, *rules()[i], init, tr_rules, transitions); } switch(transitions.size()) { case 0: transition = m.mk_false(); break; case 1: // create a dummy tag. pred = m.mk_fresh_const(head()->get_name().str().c_str(), m.mk_bool_sort()); rule = tr_rules[0]; m_tag2rule.insert(pred, rule); m_rule2tag.insert(rule, pred.get()); transitions.push_back(pred); transition = pm.mk_and(transitions); break; default: for (unsigned i = 0; i < transitions.size(); ++i) { pred = m.mk_fresh_const(head()->get_name().str().c_str(), m.mk_bool_sort()); rule = tr_rules[i]; m_tag2rule.insert(pred, rule); m_rule2tag.insert(rule, pred); disj.push_back(pred); transitions[i] = m.mk_implies(pred, transitions[i].get()); } transitions.push_back(m.mk_or(disj.size(), disj.c_ptr())); transition = pm.mk_and(transitions); break; } } void pred_transformer::init_rule( decl2rel const& pts, datalog::rule const& rule, expr_ref& init, ptr_vector& rules, expr_ref_vector& transitions) { // Predicates that are variable representatives. Other predicates at // positions the variables occur are made equivalent with these. expr_ref_vector conj(m); app_ref_vector& var_reprs = *(alloc(app_ref_vector, m)); ptr_vector aux_vars; unsigned ut_size = rule.get_uninterpreted_tail_size(); unsigned t_size = rule.get_tail_size(); SASSERT(ut_size <= t_size); init_atom(pts, rule.get_head(), var_reprs, conj, UINT_MAX); for (unsigned i = 0; i < ut_size; ++i) { if (rule.is_neg_tail(i)) { throw default_exception("PDR does not support negated predicates in rule tails"); } init_atom(pts, rule.get_tail(i), var_reprs, conj, i); } for (unsigned i = ut_size; i < t_size; ++i) { ground_free_vars(rule.get_tail(i), var_reprs, aux_vars); } SASSERT(check_filled(var_reprs)); expr_ref_vector tail(m); for (unsigned i = ut_size; i < t_size; ++i) { tail.push_back(rule.get_tail(i)); } qe::flatten_and(tail); for (unsigned i = 0; i < tail.size(); ++i) { expr_ref tmp(m); var_subst(m, false)(tail[i].get(), var_reprs.size(), (expr*const*)var_reprs.c_ptr(), tmp); conj.push_back(tmp); TRACE("pdr", tout << mk_pp(tail[i].get(), m) << "\n" << mk_pp(tmp, m) << "\n";); SASSERT(is_ground(tmp)); } expr_ref fml = pm.mk_and(conj); th_rewriter rw(m); rw(fml); if (ctx.is_dl() || ctx.is_utvpi()) { blast_term_ite(fml); } TRACE("pdr", tout << mk_pp(fml, m) << "\n";); SASSERT(is_ground(fml)); if (m.is_false(fml)) { // no-op. } else { if (ut_size == 0) { init = m.mk_or(init, fml); } transitions.push_back(fml); m.inc_ref(fml); m_rule2transition.insert(&rule, fml.get()); rules.push_back(&rule); } m_rule2inst.insert(&rule, &var_reprs); m_rule2vars.insert(&rule, aux_vars); TRACE("pdr", tout << rule.get_decl()->get_name() << "\n"; for (unsigned i = 0; i < var_reprs.size(); ++i) { tout << mk_pp(var_reprs[i].get(), m) << " "; } if (!var_reprs.empty()) tout << "\n";); } bool pred_transformer::check_filled(app_ref_vector const& v) const { for (unsigned i = 0; i < v.size(); ++i) { if (!v[i]) return false; } return true; } // create constants for free variables in tail. void pred_transformer::ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector& aux_vars) { expr_free_vars fv; fv(e); while (vars.size() < fv.size()) { vars.push_back(0); } for (unsigned i = 0; i < fv.size(); ++i) { if (fv[i] && !vars[i].get()) { vars[i] = m.mk_fresh_const("aux", fv[i]); aux_vars.push_back(vars[i].get()); } } } // create names for variables used in relations. void pred_transformer::init_atom( decl2rel const& pts, app * atom, app_ref_vector& var_reprs, expr_ref_vector& conj, unsigned tail_idx ) { unsigned arity = atom->get_num_args(); func_decl* head = atom->get_decl(); pred_transformer& pt = *pts.find(head); for (unsigned i = 0; i < arity; i++) { app_ref rep(m); if (tail_idx == UINT_MAX) { rep = m.mk_const(pm.o2n(pt.sig(i), 0)); } else { rep = m.mk_const(pm.o2o(pt.sig(i), 0, tail_idx)); } expr * arg = atom->get_arg(i); if (is_var(arg)) { var * v = to_var(arg); unsigned var_idx = v->get_idx(); if (var_idx >= var_reprs.size()) { var_reprs.resize(var_idx+1); } expr * repr = var_reprs[var_idx].get(); if (repr) { conj.push_back(m.mk_eq(rep, repr)); } else { var_reprs[var_idx] = rep; } } else { SASSERT(is_app(arg)); conj.push_back(m.mk_eq(rep, arg)); } } } void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r) { r.push_back(pm.get_background()); r.push_back((lvl == 0)?initial_state():transition()); for (unsigned i = 0; i < rules().size(); ++i) { add_premises(pts, lvl, *rules()[i], r); } } void pred_transformer::close(expr* e) { m_reachable.add_reachable(e); } void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r) { find_predecessors(rule, m_predicates); for (unsigned i = 0; i < m_predicates.size(); ++i) { expr_ref tmp(m); func_decl* head = m_predicates[i]; pred_transformer& pt = *pts.find(head); expr_ref inv = pt.get_formulas(lvl, false); if (!m.is_true(inv)) { pm.formula_n2o(inv, tmp, i, true); r.push_back(tmp); } } } void pred_transformer::inherit_properties(pred_transformer& other) { SASSERT(m_head == other.m_head); obj_map::iterator it = other.m_prop2level.begin(); obj_map::iterator end = other.m_prop2level.end(); for (; it != end; ++it) { IF_VERBOSE(2, verbose_stream() << "(pdr-inherit: " << mk_pp(it->m_key, m) << ")\n";); add_property(it->m_key, it->m_value); } } // ---------------- // model_node void model_node::set_closed() { pt().close(state()); m_closed = true; } void model_node::set_open() { SASSERT(m_closed); m_closed = false; model_node* p = parent(); while (p && p->is_closed()) { p->m_closed = false; p = p->parent(); } } void model_node::check_pre_closed() { for (unsigned i = 0; i < children().size(); ++i) { if (children()[i]->is_open()) return; } set_pre_closed(); model_node* p = parent(); while (p && p->is_1closed()) { p->set_pre_closed(); p = p->parent(); } } static bool is_ini(datalog::rule const& r) { return r.get_uninterpreted_tail_size() == 0; } datalog::rule* model_node::get_rule() { if (m_rule) { return const_cast(m_rule); } // only initial states are not set by the PDR search. SASSERT(m_model.get()); datalog::rule const& rl1 = pt().find_rule(*m_model); if (is_ini(rl1)) { set_rule(&rl1); return const_cast(m_rule); } ast_manager& m = pt().get_manager(); // otherwise, the initial state is reachable. ptr_vector const& rules = pt().rules(); ptr_vector ini_rules; expr_ref_vector tags(m); expr_ref ini_tags(m), ini_state(m); for (unsigned i = 0; i < rules.size(); ++i) { datalog::rule* rl = rules[i]; if (is_ini(*rl)) { tags.push_back(pt().rule2tag(rl)); } } SASSERT(!tags.empty()); ini_tags = m.mk_or(tags.size(), tags.c_ptr()); ini_state = m.mk_and(ini_tags, pt().initial_state(), state()); model_ref mdl; pt().get_solver().set_model(&mdl); TRACE("pdr", tout << mk_pp(ini_state, m) << "\n";); VERIFY(l_true == pt().get_solver().check_conjunction_as_assumptions(ini_state)); datalog::rule const& rl2 = pt().find_rule(*mdl); SASSERT(is_ini(rl2)); set_rule(&rl2); return const_cast(m_rule); } void model_node::mk_instantiate(datalog::rule_ref& r0, datalog::rule_ref& r1, expr_ref_vector& binding) { ast_manager& m = pt().get_manager(); expr_ref_vector conjs(m); obj_map model; qe::flatten_and(state(), conjs); for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(), *e1, *e2; if (m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) { if (m.is_value(e2)) { model.insert(e1, e2); } else if (m.is_value(e1)) { model.insert(e2, e1); } } else if (m.is_not(e, e1)) { model.insert(e1, m.mk_false()); } else { model.insert(e, m.mk_true()); } } r0 = get_rule(); app_ref_vector& inst = pt().get_inst(r0); TRACE("pdr", tout << mk_pp(state(), m) << " instance: " << inst.size() << "\n";); for (unsigned i = 0; i < inst.size(); ++i) { expr* v; if (model.find(inst[i].get(), v)) { binding.push_back(v); } else { binding.push_back(m.mk_var(i, m.get_sort(inst[i].get()))); } } r1 = r0; if (!inst.empty()) { r1.get_manager().substitute(r1, binding.size(), binding.c_ptr()); } } std::ostream& model_node::display(std::ostream& out, unsigned indent) { for (unsigned i = 0; i < indent; ++i) out << " "; out << m_level << " " << m_pt.head()->get_name() << " " << (m_closed?"closed":"open") << "\n"; for (unsigned i = 0; i < indent; ++i) out << " "; out << " " << mk_pp(m_state, m_state.get_manager(), indent) << "\n"; for (unsigned i = 0; i < children().size(); ++i) { children()[i]->display(out, indent + 1); } return out; } unsigned model_node::index() const { model_node* p = parent(); if (!p) return 0; for (unsigned i = 0; i < p->children().size(); ++i) { if (this == p->children()[i]) return i; } UNREACHABLE(); return 0; } void model_node::dequeue(model_node*& root) { TRACE("pdr", tout << this << " " << state() << "\n";); if (!m_next && !m_prev) return; SASSERT(m_next); SASSERT(m_prev); SASSERT(children().empty()); if (this == m_next) { SASSERT(root == this); root = 0; } else { m_next->m_prev = m_prev; m_prev->m_next = m_next; if (this == root) { root = m_next; } } TRACE("pdr", tout << "new root: " << root << "\n";); m_prev = 0; m_next = 0; } void model_node::enqueue(model_node* n) { TRACE("pdr", tout << n << " " << n->state() << "\n";); SASSERT(!n->m_next); SASSERT(!n->m_prev); if (this == n) { m_next = n; m_prev = n; } else { n->m_next = m_next; m_next->m_prev = n; m_next = n; n->m_prev = this; } } // ---------------- // model_search /** \brief Dequeue the next goal. */ model_node* model_search::next() { if (!m_goal) { return 0; } else { model_node* result = m_goal; result->dequeue(m_goal); return result; } } bool model_search::is_repeated(model_node& n) const { model_node* p = n.parent(); while (p) { if (p->state() == n.state()) { TRACE("pdr", tout << "repeated\n";); return true; } p = p->parent(); } return false; } void model_search::add_leaf(model_node& n) { SASSERT(n.children().empty()); model_nodes ns; model_nodes& nodes = cache(n).insert_if_not_there2(n.state(), ns)->get_data().m_value; if (nodes.contains(&n)) { return; } nodes.push_back(&n); TRACE("pdr_verbose", tout << "add: " << n.level() << ": " << &n << " " << n.state() << "\n";); if (nodes.size() == 1) { set_leaf(n); } else { n.set_pre_closed(); } } void model_search::set_leaf(model_node& n) { erase_children(n, true); SASSERT(n.is_open()); enqueue_leaf(&n); } void model_search::enqueue_leaf(model_node* n) { TRACE("pdr_verbose", tout << n << " " << n->state() << " goal: " << m_goal << "\n";); SASSERT(n->is_open()); if (!m_goal) { m_goal = n; m_goal->enqueue(n); } else if (m_bfs) { m_goal->enqueue(n); } else { m_goal->next()->enqueue(n); } } void model_search::set_root(model_node* root) { reset(); m_root = root; SASSERT(cache(*root).empty()); cache(*root).insert(root->state(), 1); set_leaf(*root); } obj_map >& model_search::cache(model_node const& n) { unsigned l = n.orig_level(); if (l >= m_cache.size()) { m_cache.resize(l + 1); } return m_cache[l]; } void model_search::erase_children(model_node& n, bool backtrack) { ptr_vector todo, nodes; todo.append(n.children()); remove_goal(n); n.reset(); while (!todo.empty()) { model_node* m = todo.back(); todo.pop_back(); nodes.push_back(m); todo.append(m->children()); remove_node(*m, backtrack); } std::for_each(nodes.begin(), nodes.end(), delete_proc()); } void model_search::remove_node(model_node& n, bool backtrack) { TRACE("pdr_verbose", tout << "remove: " << n.level() << ": " << &n << " " << n.state() << "\n";); model_nodes& nodes = cache(n).find(n.state()); nodes.erase(&n); bool is_goal = n.is_goal(); remove_goal(n); if (!nodes.empty() && is_goal && backtrack) { TRACE("pdr_verbose", for (unsigned i = 0; i < nodes.size(); ++i) n.display(tout << &n << "\n", 2);); model_node* n1 = nodes[0]; n1->set_open(); SASSERT(n1->children().empty()); enqueue_leaf(n1); } if (nodes.empty()) { cache(n).remove(n.state()); } } void model_search::remove_goal(model_node& n) { n.dequeue(m_goal); } void model_search::well_formed() { // each open leaf is in the set of m_goal. ptr_vector nodes; nodes.push_back(&get_root()); for (unsigned i = 0; i < nodes.size(); ++i) { model_node* n = nodes[i]; if (!n->children().empty()) { nodes.append(n->children()); } else if (n->is_open() && !n->is_goal() && n->parent()) { TRACE("pdr", n->display(tout << "node " << n << " not found among leaves\n", 0); display(tout);); UNREACHABLE(); return; } } if (m_goal) { model_node* n = m_goal; do { if (!n->is_open() || !n->children().empty()) { TRACE("pdr", n->display(tout << "invalid leaf\n", 0); display(tout);); UNREACHABLE(); return; } n = n->next(); } while (m_goal != n); } // each state appears in at most one goal per level. bool found = true; for (unsigned l = 0; m_goal && found; ++l) { found = false; obj_hashtable open_states; model_node* n = m_goal; do { if (n->level() == l) { found = true; if (n->is_open()) { if (open_states.contains(n->state())) { TRACE("pdr", n->display(tout << "repeated leaf\n", 0); display(tout);); UNREACHABLE(); } open_states.insert(n->state()); } } n = n->next(); } while (m_goal != n); } // a node is open if and only if it contains an // open child which is a goal. for (unsigned i = 0; i < nodes.size(); ++i) { model_node* n = nodes[i]; if (!n->children().empty() && n->parent()) { found = false; for (unsigned j = 0; !found && j < n->children().size(); ++j) { found = n->children()[j]->is_open(); } if (n->is_open() != found) { TRACE("pdr", n->display(tout << "node in inconsistent state\n", 0); display(tout);); UNREACHABLE(); } } } } unsigned model_search::num_goals() const { model_node* n = m_goal; unsigned num = 0; if (n) { do { ++num; n = n->next(); } while (n != m_goal); } return num; } std::ostream& model_search::display(std::ostream& out) const { if (m_root) { m_root->display(out, 0); } out << "goals " << num_goals() << "\n"; model_node* n = m_goal; if (n) { do { n->display(out, 1); n = n->next(); } while (n != m_goal); } return out; } /** \brief Ensure that all nodes in the tree have associated models. get_trace and get_proof_trace rely on models to extract rules. */ void model_search::update_models() { obj_map models; obj_map rules; ptr_vector todo; todo.push_back(m_root); while (!todo.empty()) { model_node* n = todo.back(); if (n->get_model_ptr()) { models.insert(n->state(), n->get_model_ptr()); rules.insert(n->state(), n->get_rule()); } todo.pop_back(); todo.append(n->children().size(), n->children().c_ptr()); } todo.push_back(m_root); while (!todo.empty()) { model_node* n = todo.back(); model* md = 0; ast_manager& m = n->pt().get_manager(); if (!n->get_model_ptr()) { if (models.find(n->state(), md)) { TRACE("pdr", tout << mk_pp(n->state(), m) << "\n";); model_ref mr(md); n->set_model(mr); datalog::rule const* rule = rules.find(n->state()); n->set_rule(rule); } else { IF_VERBOSE(1, n->display(verbose_stream() << "no model:\n", 0); verbose_stream() << mk_pp(n->state(), m) << "\n";); } } todo.pop_back(); todo.append(n->children().size(), n->children().c_ptr()); } } /** Extract trace comprising of constraints and predicates that are satisfied from facts to the query. The resulting trace */ expr_ref model_search::get_trace(context const& ctx) { pred_transformer& pt = get_root().pt(); ast_manager& m = pt.get_manager(); manager& pm = pt.get_pdr_manager(); datalog::context& dctx = ctx.get_context(); datalog::rule_manager& rm = dctx.get_rule_manager(); expr_ref_vector constraints(m), predicates(m); expr_ref tmp(m); ptr_vector children; unsigned deltas[2]; datalog::rule_ref rule(rm), r0(rm); model_node* n = m_root; datalog::rule_counter& vc = rm.get_counter(); substitution subst(m); unifier unif(m); rule = n->get_rule(); unsigned max_var = vc.get_max_rule_var(*rule); predicates.push_back(rule->get_head()); children.push_back(n); bool first = true; update_models(); while (!children.empty()) { SASSERT(children.size() == predicates.size()); expr_ref_vector binding(m); n = children.back(); children.pop_back(); TRACE("pdr", n->display(tout, 0);); n->mk_instantiate(r0, rule, binding); max_var = std::max(max_var, vc.get_max_rule_var(*rule)); subst.reset(); subst.reserve(2, max_var+1); deltas[0] = 0; deltas[1] = max_var+1; VERIFY(unif(predicates.back(), rule->get_head(), subst)); for (unsigned i = 0; i < constraints.size(); ++i) { subst.apply(2, deltas, expr_offset(constraints[i].get(), 0), tmp); dctx.get_rewriter()(tmp); constraints[i] = tmp; } for (unsigned i = 0; i < predicates.size(); ++i) { subst.apply(2, deltas, expr_offset(predicates[i].get(), 0), tmp); predicates[i] = tmp; } if (!first) { constraints.push_back(predicates.back()); } first = false; predicates.pop_back(); for (unsigned i = rule->get_uninterpreted_tail_size(); i < rule->get_tail_size(); ++i) { subst.apply(2, deltas, expr_offset(rule->get_tail(i), 1), tmp); constraints.push_back(tmp); } for (unsigned i = 0; i < constraints.size(); ++i) { max_var = std::max(vc.get_max_var(constraints[i].get()), max_var); } if (n->children().empty()) { // nodes whose states are repeated // in the search tree do not have children. continue; } SASSERT(n->children().size() == rule->get_uninterpreted_tail_size()); for (unsigned i = 0; i < rule->get_uninterpreted_tail_size(); ++i) { subst.apply(2, deltas, expr_offset(rule->get_tail(i), 1), tmp); predicates.push_back(tmp); } for (unsigned i = 0; i < predicates.size(); ++i) { max_var = std::max(vc.get_max_var(predicates[i].get()), max_var); } children.append(n->children()); } expr_safe_replace repl(m); for (unsigned i = 0; i < constraints.size(); ++i) { expr* e = constraints[i].get(), *e1, *e2; if (m.is_eq(e, e1, e2) && is_var(e1) && is_ground(e2)) { repl.insert(e1, e2); } else if (m.is_eq(e, e1, e2) && is_var(e2) && is_ground(e1)) { repl.insert(e2, e1); } } expr_ref_vector result(m); for (unsigned i = 0; i < constraints.size(); ++i) { expr_ref tmp(m); tmp = constraints[i].get(); repl(tmp); dctx.get_rewriter()(tmp); if (!m.is_true(tmp)) { result.push_back(tmp); } } return pm.mk_and(result); } proof_ref model_search::get_proof_trace(context const& ctx) { pred_transformer& pt = get_root().pt(); ast_manager& m = pt.get_manager(); datalog::context& dctx = ctx.get_context(); datalog::rule_manager& rm = dctx.get_rule_manager(); datalog::rule_unifier unif(dctx); datalog::dl_decl_util util(m); datalog::rule_ref r0(rm), r1(rm); obj_map cache; obj_map rules; ptr_vector todo; proof_ref_vector trail(m); datalog::rule_ref_vector rules_trail(rm); proof* pr = 0; unif.set_normalize(true); todo.push_back(m_root); update_models(); while (!todo.empty()) { model_node* n = todo.back(); TRACE("pdr", n->display(tout, 0);); if (cache.find(n->state(), pr)) { todo.pop_back(); continue; } ptr_vector pfs; ptr_vector rls; ptr_vector const& chs = n->children(); pfs.push_back(0); rls.push_back(0); for (unsigned i = 0; i < chs.size(); ++i) { if (cache.find(chs[i]->state(), pr)) { pfs.push_back(pr); rls.push_back(rules.find(chs[i]->state())); } else { todo.push_back(chs[i]); } } if (pfs.size() != 1 + chs.size()) { continue; } proof_ref rl(m); expr_ref_vector binding(m); n->mk_instantiate(r0, r1, binding); proof_ref p1(m), p2(m); p1 = r0->get_proof(); IF_VERBOSE(0, if (!p1) r0->display(dctx, verbose_stream());); SASSERT(p1); pfs[0] = p1; rls[0] = r1; TRACE("pdr", tout << "root: " << mk_pp(p1.get(), m) << "\n"; for (unsigned i = 0; i < binding.size(); ++i) { tout << mk_pp(binding[i].get(), m) << "\n"; } for (unsigned i = 1; i < pfs.size(); ++i) { tout << mk_pp(pfs[i], m) << "\n"; } ); datalog::rule_ref reduced_rule(rm), r3(rm); reduced_rule = rls[0]; // check if binding is identity. bool binding_is_id = true; for (unsigned i = 0; binding_is_id && i < binding.size(); ++i) { expr* v = binding[i].get(); binding_is_id = is_var(v) && to_var(v)->get_idx() == i; } if (rls.size() > 1 || !binding_is_id) { expr_ref tmp(m); vector substs; svector > positions; substs.push_back(binding); // TODO base substitution. for (unsigned i = 1; i < rls.size(); ++i) { datalog::rule& src = *rls[i]; bool unified = unif.unify_rules(*reduced_rule, 0, src); if (!unified) { IF_VERBOSE(0, verbose_stream() << "Could not unify rules: "; reduced_rule->display(dctx, verbose_stream()); src.display(dctx, verbose_stream());); } expr_ref_vector sub1 = unif.get_rule_subst(*reduced_rule, true); TRACE("pdr", for (unsigned k = 0; k < sub1.size(); ++k) { tout << mk_pp(sub1[k].get(), m) << " "; } tout << "\n"; ); for (unsigned j = 0; j < substs.size(); ++j) { for (unsigned k = 0; k < substs[j].size(); ++k) { var_subst(m, false)(substs[j][k].get(), sub1.size(), sub1.c_ptr(), tmp); substs[j][k] = tmp; } while (substs[j].size() < sub1.size()) { substs[j].push_back(sub1[substs[j].size()].get()); } } positions.push_back(std::make_pair(i,0)); substs.push_back(unif.get_rule_subst(src, false)); VERIFY(unif.apply(*reduced_rule.get(), 0, src, r3)); reduced_rule = r3; } expr_ref fml_concl(m); rm.to_formula(*reduced_rule.get(), fml_concl); p1 = m.mk_hyper_resolve(pfs.size(), pfs.c_ptr(), fml_concl, positions, substs); } cache.insert(n->state(), p1); rules.insert(n->state(), reduced_rule); trail.push_back(p1); rules_trail.push_back(reduced_rule); todo.pop_back(); } return proof_ref(cache.find(m_root->state()), m); } model_search::~model_search() { TRACE("pdr", tout << "\n";); reset(); } void model_search::reset() { if (m_root) { erase_children(*m_root, false); remove_node(*m_root, false); dealloc(m_root); m_root = 0; } m_cache.reset(); } void model_search::backtrack_level(bool uses_level, model_node& n) { SASSERT(m_root); if (uses_level && m_root->level() > n.level()) { IF_VERBOSE(2, verbose_stream() << "Increase level " << n.level() << "\n";); n.increase_level(); enqueue_leaf(&n); } else { model_node* p = n.parent(); if (p) { set_leaf(*p); } } DEBUG_CODE(well_formed();); } // ---------------- // context context::context( smt_params& fparams, fixedpoint_params const& params, ast_manager& m ) : m_fparams(fparams), m_params(params), m(m), m_context(0), m_pm(m_fparams, params.pdr_max_num_contexts(), m), m_query_pred(m), m_query(0), m_search(m_params.pdr_bfs_model_search()), m_last_result(l_undef), m_inductive_lvl(0), m_expanded_lvl(0), m_cancel(false) { } context::~context() { reset_core_generalizers(); reset(); } void context::reset() { TRACE("pdr", tout << "\n";); cleanup(); decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); for (; it != end; ++it) { dealloc(it->m_value); } m_rels.reset(); m_search.reset(); m_query = 0; m_last_result = l_undef; m_inductive_lvl = 0; } void context::init_rules(datalog::rule_set& rules, decl2rel& rels) { m_context = &rules.get_context(); // Allocate collection of predicate transformers datalog::rule_set::decl2rules::iterator dit = rules.begin_grouped_rules(), dend = rules.end_grouped_rules(); decl2rel::obj_map_entry* e; for (; dit != dend; ++dit) { func_decl* pred = dit->m_key; TRACE("pdr", tout << mk_pp(pred, m) << "\n";); SASSERT(!rels.contains(pred)); e = rels.insert_if_not_there2(pred, alloc(pred_transformer, *this, get_pdr_manager(), pred)); datalog::rule_vector const& pred_rules = *dit->m_value; for (unsigned i = 0; i < pred_rules.size(); ++i) { e->get_data().m_value->add_rule(pred_rules[i]); } } datalog::rule_set::iterator rit = rules.begin(), rend = rules.end(); for (; rit != rend; ++rit) { datalog::rule* r = *rit; pred_transformer* pt; unsigned utz = r->get_uninterpreted_tail_size(); for (unsigned i = 0; i < utz; ++i) { func_decl* pred = r->get_decl(i); if (!rels.find(pred, pt)) { pt = alloc(pred_transformer, *this, get_pdr_manager(), pred); rels.insert(pred, pt); } } } // Initialize use list dependencies decl2rel::iterator it = rels.begin(), end = rels.end(); for (; it != end; ++it) { func_decl* pred = it->m_key; pred_transformer* pt = it->m_value, *pt_user; obj_hashtable const& deps = rules.get_dependencies().get_deps(pred); obj_hashtable::iterator itf = deps.begin(), endf = deps.end(); for (; itf != endf; ++itf) { TRACE("pdr", tout << mk_pp(pred, m) << " " << mk_pp(*itf, m) << "\n";); pt_user = rels.find(*itf); pt_user->add_use(pt); } } // Initialize the predicate transformers. it = rels.begin(), end = rels.end(); for (; it != end; ++it) { pred_transformer& rel = *it->m_value; rel.initialize(rels); TRACE("pdr", rel.display(tout); ); } } void context::update_rules(datalog::rule_set& rules) { decl2rel rels; init_core_generalizers(rules); init_rules(rules, rels); decl2rel::iterator it = rels.begin(), end = rels.end(); for (; it != end; ++it) { pred_transformer* pt = 0; if (m_rels.find(it->m_key, pt)) { it->m_value->inherit_properties(*pt); } } reset(); it = rels.begin(), end = rels.end(); for (; it != end; ++it) { m_rels.insert(it->m_key, it->m_value); } } unsigned context::get_num_levels(func_decl* p) { pred_transformer* pt = 0; if (m_rels.find(p, pt)) { return pt->get_num_levels(); } else { IF_VERBOSE(10, verbose_stream() << "did not find predicate " << p->get_name() << "\n";); return 0; } } expr_ref context::get_cover_delta(int level, func_decl* p_orig, func_decl* p) { pred_transformer* pt = 0; if (m_rels.find(p, pt)) { return pt->get_cover_delta(p_orig, level); } else { IF_VERBOSE(10, verbose_stream() << "did not find predicate " << p->get_name() << "\n";); return expr_ref(m.mk_true(), m); } } void context::add_cover(int level, func_decl* p, expr* property) { pred_transformer* pt = 0; if (!m_rels.find(p, pt)) { pt = alloc(pred_transformer, *this, get_pdr_manager(), p); m_rels.insert(p, pt); IF_VERBOSE(10, verbose_stream() << "did not find predicate " << p->get_name() << "\n";); } unsigned lvl = (level == -1)?infty_level:((unsigned)level); pt->add_cover(lvl, property); } class context::classifier_proc { ast_manager& m; arith_util a; bool m_is_bool; bool m_is_bool_arith; bool m_has_arith; bool m_is_dl; bool m_is_utvpi; public: classifier_proc(ast_manager& m, datalog::rule_set& rules): m(m), a(m), m_is_bool(true), m_is_bool_arith(true), m_has_arith(false), m_is_dl(false), m_is_utvpi(false) { classify(rules); } void operator()(expr* e) { if (m_is_bool) { if (!m.is_bool(e)) { m_is_bool = false; } else if (is_var(e)) { // no-op. } else if (!is_app(e)) { m_is_bool = false; } else if (to_app(e)->get_num_args() > 0 && to_app(e)->get_family_id() != m.get_basic_family_id()) { m_is_bool = false; } } m_has_arith = m_has_arith || a.is_int_real(e); if (m_is_bool_arith) { if (!m.is_bool(e) && !a.is_int_real(e)) { m_is_bool_arith = false; } else if (is_var(e)) { // no-op } else if (!is_app(e)) { m_is_bool_arith = false; } else if (to_app(e)->get_num_args() > 0 && to_app(e)->get_family_id() != m.get_basic_family_id() && to_app(e)->get_family_id() != a.get_family_id()) { m_is_bool_arith = false; } } } bool is_bool() const { return m_is_bool; } bool is_bool_arith() const { return m_is_bool_arith; } bool is_dl() const { return m_is_dl; } bool is_utvpi() const { return m_is_utvpi; } private: void classify(datalog::rule_set& rules) { expr_fast_mark1 mark; datalog::rule_set::iterator it = rules.begin(), end = rules.end(); for (; it != end; ++it) { datalog::rule& r = *(*it); classify_pred(mark, r.get_head()); unsigned utsz = r.get_uninterpreted_tail_size(); for (unsigned i = 0; i < utsz; ++i) { classify_pred(mark, r.get_tail(i)); } for (unsigned i = utsz; i < r.get_tail_size(); ++i) { quick_for_each_expr(*this, mark, r.get_tail(i)); } } mark.reset(); m_is_dl = false; m_is_utvpi = false; if (m_has_arith) { ptr_vector forms; for (it = rules.begin(); it != end; ++it) { datalog::rule& r = *(*it); unsigned utsz = r.get_uninterpreted_tail_size(); forms.push_back(r.get_head()); for (unsigned i = utsz; i < r.get_tail_size(); ++i) { forms.push_back(r.get_tail(i)); } } m_is_dl = is_difference_logic(m, forms.size(), forms.c_ptr()); m_is_utvpi = m_is_dl || is_utvpi_logic(m, forms.size(), forms.c_ptr()); } } void classify_pred(expr_fast_mark1& mark, app* pred) { for (unsigned i = 0; i < pred->get_num_args(); ++i) { quick_for_each_expr(*this, mark, pred->get_arg(i)); } } }; void context::validate_proof() { std::stringstream msg; proof_ref pr = get_proof(); proof_checker checker(m); expr_ref_vector side_conditions(m); bool ok = checker.check(pr, side_conditions); if (!ok) { msg << "proof validation failed"; IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); throw default_exception(msg.str()); } for (unsigned i = 0; i < side_conditions.size(); ++i) { expr* cond = side_conditions[i].get(); expr_ref tmp(m); tmp = m.mk_not(cond); IF_VERBOSE(2, verbose_stream() << "checking side-condition:\n" << mk_pp(cond, m) << "\n";); smt::kernel solver(m, get_fparams()); solver.assert_expr(tmp); lbool res = solver.check(); if (res != l_false) { msg << "rule validation failed when checking: " << mk_pp(cond, m); IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); throw default_exception(msg.str()); } } } void context::validate_search() { expr_ref tr = m_search.get_trace(*this); // TBD: tr << "\n"; } void context::validate_model() { std::stringstream msg; expr_ref_vector refs(m); expr_ref tmp(m); model_ref model; vector rs; model_converter_ref mc; get_level_property(m_inductive_lvl, refs, rs); inductive_property ex(m, mc, rs); ex.to_model(model); decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); var_subst vs(m, false); expr_free_vars fv; for (; it != end; ++it) { ptr_vector const& rules = it->m_value->rules(); for (unsigned i = 0; i < rules.size(); ++i) { datalog::rule& r = *rules[i]; model->eval(r.get_head(), tmp); expr_ref_vector fmls(m); fmls.push_back(m.mk_not(tmp)); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); for (unsigned j = 0; j < utsz; ++j) { model->eval(r.get_tail(j), tmp); fmls.push_back(tmp); } for (unsigned j = utsz; j < tsz; ++j) { fmls.push_back(r.get_tail(j)); } tmp = m.mk_and(fmls.size(), fmls.c_ptr()); svector names; fv(tmp); fv.set_default_sort(m.mk_bool_sort()); for (unsigned i = 0; i < fv.size(); ++i) { names.push_back(symbol(i)); } fv.reverse(); if (!fv.empty()) { tmp = m.mk_exists(fv.size(), fv.c_ptr(), names.c_ptr(), tmp); } smt::kernel solver(m, get_fparams()); solver.assert_expr(tmp); lbool res = solver.check(); if (res != l_false) { msg << "rule validation failed when checking: " << mk_pp(tmp, m); IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); throw default_exception(msg.str()); } } } } void context::validate() { if (!m_params.pdr_validate_result()) { return; } switch(m_last_result) { case l_true: if (m_params.generate_proof_trace()) { validate_proof(); } validate_search(); break; case l_false: validate_model(); break; default: break; } } void context::reset_core_generalizers() { std::for_each(m_core_generalizers.begin(), m_core_generalizers.end(), delete_proc()); m_core_generalizers.reset(); } void context::init_core_generalizers(datalog::rule_set& rules) { reset_core_generalizers(); classifier_proc classify(m, rules); bool use_mc = m_params.pdr_use_multicore_generalizer(); if (use_mc) { m_core_generalizers.push_back(alloc(core_multi_generalizer, *this, 0)); } if (!classify.is_bool()) { m.toggle_proof_mode(PGM_FINE); m_fparams.m_arith_bound_prop = BP_NONE; m_fparams.m_arith_auto_config_simplex = true; m_fparams.m_arith_propagate_eqs = false; m_fparams.m_arith_eager_eq_axioms = false; if (m_params.pdr_utvpi() && !m_params.pdr_use_convex_closure_generalizer() && !m_params.pdr_use_convex_interior_generalizer()) { if (classify.is_dl()) { m_fparams.m_arith_mode = AS_DIFF_LOGIC; m_fparams.m_arith_expand_eqs = true; } else if (classify.is_utvpi()) { IF_VERBOSE(1, verbose_stream() << "UTVPI\n";); m_fparams.m_arith_mode = AS_UTVPI; m_fparams.m_arith_expand_eqs = true; } } } if (m_params.pdr_use_convex_closure_generalizer()) { m_core_generalizers.push_back(alloc(core_convex_hull_generalizer, *this, true)); } if (m_params.pdr_use_convex_interior_generalizer()) { m_core_generalizers.push_back(alloc(core_convex_hull_generalizer, *this, false)); } if (!use_mc && m_params.pdr_use_inductive_generalizer()) { m_core_generalizers.push_back(alloc(core_bool_inductive_generalizer, *this, 0)); } if (m_params.pdr_inductive_reachability_check()) { m_core_generalizers.push_back(alloc(core_induction_generalizer, *this)); } if (m_params.pdr_use_arith_inductive_generalizer()) { m_core_generalizers.push_back(alloc(core_arith_inductive_generalizer, *this)); } } void context::get_level_property(unsigned lvl, expr_ref_vector& res, vector& rs) const { decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); for (; it != end; ++it) { pred_transformer* r = it->m_value; if (r->head() == m_query_pred) { continue; } expr_ref conj = r->get_formulas(lvl, false); m_pm.formula_n2o(0, false, conj); res.push_back(conj); ptr_vector sig(r->head()->get_arity(), r->sig()); rs.push_back(relation_info(m, r->head(), sig, conj)); } } void context::simplify_formulas() { decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); for (; it != end; ++it) { pred_transformer* r = it->m_value; r->simplify_formulas(); } } lbool context::solve() { m_last_result = l_undef; try { solve_impl(); UNREACHABLE(); } catch (model_exception) { IF_VERBOSE(1, verbose_stream() << "\n"; m_search.display(verbose_stream());); m_last_result = l_true; validate(); IF_VERBOSE(1, if (m_params.print_boogie_certificate()) { display_certificate(verbose_stream()); }); return l_true; } catch (inductive_exception) { simplify_formulas(); m_last_result = l_false; TRACE("pdr", display_certificate(tout);); IF_VERBOSE(1, { expr_ref_vector refs(m); vector rs; get_level_property(m_inductive_lvl, refs, rs); model_converter_ref mc; inductive_property ex(m, mc, rs); verbose_stream() << ex.to_string(); }); // upgrade invariants that are known to be inductive. decl2rel::iterator it = m_rels.begin (), end = m_rels.end (); for (; m_inductive_lvl > 0 && it != end; ++it) { if (it->m_value->head() != m_query_pred) { it->m_value->propagate_to_infinity (m_inductive_lvl); } } validate(); return l_false; } catch (unknown_exception) { return l_undef; } UNREACHABLE(); return l_undef; } void context::cancel() { m_cancel = true; } void context::cleanup() { m_cancel = false; } void context::checkpoint() { if (m_cancel) { throw default_exception("pdr canceled"); } } /** \brief retrieve answer. */ expr_ref context::get_answer() { switch(m_last_result) { case l_true: return mk_sat_answer(); case l_false: return mk_unsat_answer(); default: return expr_ref(m.mk_true(), m); } } model_ref context::get_model() { SASSERT(m_last_result == l_false); expr_ref_vector refs(m); vector rs; model_ref md; get_level_property(m_inductive_lvl, refs, rs); inductive_property ex(m, m_mc, rs); ex.to_model(md); return md; } proof_ref context::get_proof() const { scoped_proof _sc(m); proof_ref proof(m); SASSERT(m_last_result == l_true); proof = m_search.get_proof_trace(*this); TRACE("pdr", tout << "PDR trace: " << mk_pp(proof, m) << "\n";); apply(m, m_pc.get(), proof); TRACE("pdr", tout << "PDR trace: " << mk_pp(proof, m) << "\n";); // proof_utils::push_instantiations_up(proof); // TRACE("pdr", tout << "PDR up: " << mk_pp(proof, m) << "\n";); return proof; } /** \brief Retrieve satisfying assignment with explanation. */ expr_ref context::mk_sat_answer() const { if (m_params.generate_proof_trace()) { proof_ref pr = get_proof(); return expr_ref(pr.get(), m); } return m_search.get_trace(*this); } expr_ref context::mk_unsat_answer() const { expr_ref_vector refs(m); vector rs; get_level_property(m_inductive_lvl, refs, rs); inductive_property ex(m, const_cast(m_mc), rs); return ex.to_expr(); } void context::solve_impl() { if (!m_rels.find(m_query_pred, m_query)) { throw inductive_exception(); } unsigned lvl = 0; bool reachable; while (true) { checkpoint(); m_expanded_lvl = lvl; reachable = check_reachability(lvl); if (reachable) { throw model_exception(); } if (lvl != 0) { propagate(lvl); } lvl++; m_stats.m_max_depth = std::max(m_stats.m_max_depth, lvl); IF_VERBOSE(1,verbose_stream() << "Entering level "<level() << "\n";); checkpoint(); expand_node(*node); } return root->is_closed(); } void context::close_node(model_node& n) { n.set_closed(); model_node* p = n.parent(); while (p && p->is_1closed()) { p->set_closed(); p = p->parent(); } } void context::expand_node(model_node& n) { SASSERT(n.is_open()); expr_ref_vector cube(m); if (n.level() < m_expanded_lvl) { m_expanded_lvl = n.level(); } pred_transformer::scoped_farkas sf (n.pt(), m_params.pdr_farkas()); if (n.pt().is_reachable(n.state())) { TRACE("pdr", tout << "reachable\n";); close_node(n); } else { bool uses_level = true; switch (expand_state(n, cube, uses_level)) { case l_true: if (n.level() == 0) { TRACE("pdr", tout << "reachable at level 0\n";); close_node(n); } else { TRACE("pdr", tout << "node: " << &n << "\n";); create_children(n); } break; 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) { (*m_core_generalizers[i])(n, cores[j].first, cores[j].second, new_cores); } cores.reset(); cores.append(new_cores); } bool found_invariant = false; for (unsigned i = 0; i < cores.size(); ++i) { expr_ref_vector const& core = cores[i].first; uses_level = cores[i].second; found_invariant = !uses_level || found_invariant; expr_ref ncore(m_pm.mk_not_and(core), m); TRACE("pdr", tout << "invariant state: " << (uses_level?"":"(inductive) ") << mk_pp(ncore, m) << "\n";); n.pt().add_property(ncore, uses_level?n.level():infty_level); } CASSERT("pdr",n.level() == 0 || check_invariant(n.level()-1)); m_search.backtrack_level(!found_invariant && m_params.pdr_flexible_trace(), n); break; } case l_undef: { TRACE("pdr", tout << "unknown state: " << mk_pp(m_pm.mk_and(cube), m) << "\n";); throw unknown_exception(); } } } } // // check if predicate transformer has a satisfiable predecessor state. // returns either a satisfiable predecessor state or // return a property that blocks state and is implied by the // predicate transformer (or some unfolding of it). // lbool context::expand_state(model_node& n, expr_ref_vector& result, bool& uses_level) { TRACE("pdr", tout << "expand_state: " << n.pt().head()->get_name(); tout << " level: " << n.level() << "\n"; tout << mk_pp(n.state(), m) << "\n";); return n.pt().is_reachable(n, &result, uses_level); } void context::propagate(unsigned max_prop_lvl) { if (m_params.pdr_simplify_formulas_pre()) { simplify_formulas(); } for (unsigned lvl = m_expanded_lvl; lvl <= max_prop_lvl; lvl++) { checkpoint(); bool all_propagated = true; decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); for (; it != end; ++it) { checkpoint(); pred_transformer& r = *it->m_value; all_propagated = r.propagate_to_next_level(lvl) && all_propagated; } CASSERT("pdr", check_invariant(lvl)); if (all_propagated && lvl == max_prop_lvl) { m_inductive_lvl = lvl; throw inductive_exception(); } } if (m_params.pdr_simplify_formulas_post()) { simplify_formulas(); } } /** \brief create children states from model cube. Introduce the shorthands: - T(x0,x1,x) for transition - phi(x) for n.state() - M(x0,x1,x) for n.model() Assumptions: M => phi & T In other words, 1. phi & T is implied by M Goal is to find phi0(x0), phi1(x1) such that: phi(x) & phi0(x0) & phi1(x1) => T(x0, x1, x) Strategy: - Extract literals from T & phi using ternary simulation with M. - resulting formula is Phi. - perform cheap existential quantifier elimination on Phi <- exists x . Phi(x0,x1,x) (e.g., destructive equality resolution) - Sub-strategy 1: rename remaining x to fresh variables. - Sub-strategy 2: replace remaining x to M(x). - For each literal L in result: - if L is x0 pure, add L to L0 - if L is x1 pure, add L to L1 - if L mixes x0, x1, add x1 = M(x1) to L1, add L(x1 |-> M(x1)) to L0 - Create sub-goals for L0 and L1. */ void context::create_children(model_node& n) { SASSERT(n.level() > 0); bool use_model_generalizer = m_params.pdr_use_model_generalizer(); scoped_no_proof _sc(m); pred_transformer& pt = n.pt(); model_ref M = n.get_model_ptr(); SASSERT(M.get()); datalog::rule const& r = pt.find_rule(*M); expr* T = pt.get_transition(r); expr* phi = n.state(); n.set_rule(&r); TRACE("pdr", tout << "Model:\n"; model_smt2_pp(tout, m, *M, 0); tout << "\n"; tout << "Transition:\n" << mk_pp(T, m) << "\n"; tout << "Phi:\n" << mk_pp(phi, m) << "\n";); model_implicant mev(m); expr_ref_vector mdl(m), forms(m), Phi(m); forms.push_back(T); forms.push_back(phi); qe::flatten_and(forms); ptr_vector forms1(forms.size(), forms.c_ptr()); if (use_model_generalizer) { Phi.append(mev.minimize_model(forms1, M)); } else { Phi.append(mev.minimize_literals(forms1, M)); } ptr_vector preds; pt.find_predecessors(r, preds); pt.remove_predecessors(Phi); app_ref_vector vars(m); unsigned sig_size = pt.head()->get_arity(); for (unsigned i = 0; i < sig_size; ++i) { vars.push_back(m.mk_const(m_pm.o2n(pt.sig(i), 0))); } ptr_vector& aux_vars = pt.get_aux_vars(r); vars.append(aux_vars.size(), aux_vars.c_ptr()); scoped_ptr rep; qe_lite qe(m); expr_ref phi1 = m_pm.mk_and(Phi); qe(vars, phi1); TRACE("pdr", tout << "Eliminated\n" << mk_pp(phi1, m) << "\n";); if (!use_model_generalizer) { reduce_disequalities(*M, 3, phi1); TRACE("pdr", tout << "Reduced-eq\n" << mk_pp(phi1, m) << "\n";); } get_context().get_rewriter()(phi1); TRACE("pdr", tout << "Vars:\n"; for (unsigned i = 0; i < vars.size(); ++i) { tout << mk_pp(vars[i].get(), m) << "\n"; } tout << "Literals\n"; tout << mk_pp(m_pm.mk_and(Phi), m) << "\n"; tout << "Reduced\n" << mk_pp(phi1, m) << "\n";); if (!vars.empty()) { // also fresh names for auxiliary variables in body? expr_substitution sub(m); expr_ref tmp(m); proof_ref pr(m); pr = m.mk_asserted(m.mk_true()); for (unsigned i = 0; i < vars.size(); ++i) { tmp = mev.eval(M, vars[i].get()); sub.insert(vars[i].get(), tmp, pr); } if (!rep) rep = mk_expr_simp_replacer(m); rep->set_substitution(&sub); (*rep)(phi1); TRACE("pdr", tout << "Projected:\n" << mk_pp(phi1, m) << "\n";); } Phi.reset(); qe::flatten_and(phi1, Phi); unsigned_vector indices; vector child_states; child_states.resize(preds.size(), expr_ref_vector(m)); for (unsigned i = 0; i < Phi.size(); ++i) { m_pm.collect_indices(Phi[i].get(), indices); if (indices.size() == 0) { IF_VERBOSE(3, verbose_stream() << "Skipping " << mk_pp(Phi[i].get(), m) << "\n";); } else if (indices.size() == 1) { child_states[indices.back()].push_back(Phi[i].get()); } else { expr_substitution sub(m); expr_ref tmp(m); proof_ref pr(m); pr = m.mk_asserted(m.mk_true()); vector > vars; m_pm.collect_variables(Phi[i].get(), vars); SASSERT(vars.size() == indices.back()+1); for (unsigned j = 1; j < indices.size(); ++j) { ptr_vector const& vs = vars[indices[j]]; for (unsigned k = 0; k < vs.size(); ++k) { tmp = mev.eval(M, vs[k]); sub.insert(vs[k], tmp, pr); child_states[indices[j]].push_back(m.mk_eq(vs[k], tmp)); } } tmp = Phi[i].get(); if (!rep) rep = mk_expr_simp_replacer(m); rep->set_substitution(&sub); (*rep)(tmp); child_states[indices[0]].push_back(tmp); } } expr_ref n_cube(m); for (unsigned i = 0; i < preds.size(); ++i) { pred_transformer& pt = *m_rels.find(preds[i]); SASSERT(pt.head() == preds[i]); expr_ref o_cube = m_pm.mk_and(child_states[i]); m_pm.formula_o2n(o_cube, n_cube, i); model_node* child = alloc(model_node, &n, n_cube, pt, n.level()-1); ++m_stats.m_num_nodes; m_search.add_leaf(*child); IF_VERBOSE(2, verbose_stream() << "Predecessor: " << mk_pp(n_cube, m) << " " << (child->is_closed()?"closed":"open") << "\n";); m_stats.m_max_depth = std::max(m_stats.m_max_depth, child->depth()); } n.check_pre_closed(); TRACE("pdr", m_search.display(tout);); } void context::collect_statistics(statistics& st) const { decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); for (it = m_rels.begin(); it != end; ++it) { it->m_value->collect_statistics(st); } st.update("PDR num unfoldings", m_stats.m_num_nodes); st.update("PDR max depth", m_stats.m_max_depth); st.update("PDR inductive level", m_inductive_lvl); m_pm.collect_statistics(st); for (unsigned i = 0; i < m_core_generalizers.size(); ++i) { m_core_generalizers[i]->collect_statistics(st); } } void context::reset_statistics() { decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); for (it = m_rels.begin(); it != end; ++it) { it->m_value->reset_statistics(); } m_stats.reset(); m_pm.reset_statistics(); for (unsigned i = 0; i < m_core_generalizers.size(); ++i) { m_core_generalizers[i]->reset_statistics(); } } std::ostream& context::display(std::ostream& out) const { decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); for (; it != end; ++it) { it->m_value->display(out); } m_search.display(out); return out; } bool context::check_invariant(unsigned lvl) { decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); for (; it != end; ++it) { checkpoint(); if (!check_invariant(lvl, it->m_key)) { return false; } } return true; } bool context::check_invariant(unsigned lvl, func_decl* fn) { smt::kernel ctx(m, m_fparams); pred_transformer& pt = *m_rels.find(fn); expr_ref_vector conj(m); expr_ref inv = pt.get_formulas(next_level(lvl), false); if (m.is_true(inv)) return true; pt.add_premises(m_rels, lvl, conj); conj.push_back(m.mk_not(inv)); expr_ref fml(m.mk_and(conj.size(), conj.c_ptr()), m); ctx.assert_expr(fml); lbool result = ctx.check(); TRACE("pdr", tout << "Check invariant level: " << lvl << " " << result << "\n" << mk_pp(fml, m) << "\n";); return result == l_false; } void context::display_certificate(std::ostream& strm) const { switch(m_last_result) { case l_false: { expr_ref_vector refs(m); vector rs; get_level_property(m_inductive_lvl, refs, rs); inductive_property ex(m, const_cast(m_mc), rs); strm << ex.to_string(); break; } case l_true: { if (m_params.print_boogie_certificate()) { datalog::boogie_proof bp(m); bp.set_proof(get_proof()); bp.set_model(0); bp.pp(strm); } else { strm << mk_pp(mk_sat_answer(), m); } break; } case l_undef: { strm << "unknown"; break; } } } } z3-4.4.0/src/muz/pdr/pdr_generalizers.cpp0000644000175000017500000007045012540347414020105 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdr_generalizers.cpp Abstract: Generalizers of satisfiable states and unsat cores. Author: Nikolaj Bjorner (nbjorner) 2011-11-20. Revision History: --*/ #include "pdr_context.h" #include "pdr_farkas_learner.h" #include "pdr_generalizers.h" #include "expr_abstract.h" #include "var_subst.h" #include "expr_safe_replace.h" #include "model_smt2_pp.h" namespace pdr { // ------------------------ // core_bool_inductive_generalizer // main propositional induction generalizer. // drop literals one by one from the core and check if the core is still inductive. // void core_bool_inductive_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { if (core.size() <= 1) { return; } ast_manager& m = core.get_manager(); TRACE("pdr", for (unsigned i = 0; i < core.size(); ++i) { tout << mk_pp(core[i].get(), m) << "\n"; }); unsigned num_failures = 0, i = 0, old_core_size = core.size(); ptr_vector processed; while (i < core.size() && 1 < core.size() && (!m_failure_limit || num_failures <= m_failure_limit)) { expr_ref lit(m); lit = core[i].get(); core[i] = m.mk_true(); if (n.pt().check_inductive(n.level(), core, uses_level)) { num_failures = 0; for (i = 0; i < core.size() && processed.contains(core[i].get()); ++i); } else { core[i] = lit; processed.push_back(lit); ++num_failures; ++i; } } IF_VERBOSE(2, verbose_stream() << "old size: " << old_core_size << " new size: " << core.size() << "\n";); TRACE("pdr", tout << "old size: " << old_core_size << " new size: " << core.size() << "\n";); } void core_multi_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { UNREACHABLE(); } /** \brief Find minimal cores. Apply a simple heuristic: find a minimal core, then find minimal cores that exclude at least one literal from each of the literals in the minimal cores. */ void core_multi_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { ast_manager& m = core.get_manager(); expr_ref_vector old_core(m), core0(core); bool uses_level1 = uses_level; m_gen(n, core0, uses_level1); new_cores.push_back(std::make_pair(core0, uses_level1)); obj_hashtable core_exprs, core1_exprs; datalog::set_union(core_exprs, core0); for (unsigned i = 0; i < old_core.size(); ++i) { expr* lit = old_core[i].get(); if (core_exprs.contains(lit)) { expr_ref_vector core1(old_core); core1[i] = core1.back(); core1.pop_back(); uses_level1 = uses_level; m_gen(n, core1, uses_level1); SASSERT(core1.size() <= old_core.size()); if (core1.size() < old_core.size()) { new_cores.push_back(std::make_pair(core1, uses_level1)); core1_exprs.reset(); datalog::set_union(core1_exprs, core1); datalog::set_intersection(core_exprs, core1_exprs); } } } } // ------------------------ // core_farkas_generalizer // // for each disjunct of core: // weaken predecessor. // core_farkas_generalizer::core_farkas_generalizer(context& ctx, ast_manager& m, smt_params& p): core_generalizer(ctx), m_farkas_learner(p, m) {} void core_farkas_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { ast_manager& m = n.pt().get_manager(); if (core.empty()) return; expr_ref A(m), B(qe::mk_and(core)), C(m); expr_ref_vector Bs(m); qe::flatten_or(B, Bs); A = n.pt().get_propagation_formula(m_ctx.get_pred_transformers(), n.level()); bool change = false; for (unsigned i = 0; i < Bs.size(); ++i) { expr_ref_vector lemmas(m); C = Bs[i].get(); if (m_farkas_learner.get_lemma_guesses(A, B, lemmas)) { TRACE("pdr", tout << "Old core:\n" << mk_pp(B, m) << "\n"; tout << "New core:\n" << mk_pp(qe::mk_and(lemmas), m) << "\n";); Bs[i] = qe::mk_and(lemmas); change = true; } } if (change) { C = qe::mk_or(Bs); TRACE("pdr", tout << "prop:\n" << mk_pp(A,m) << "\ngen:" << mk_pp(B, m) << "\nto: " << mk_pp(C, m) << "\n";); core.reset(); qe::flatten_and(C, core); uses_level = true; } } void core_farkas_generalizer::collect_statistics(statistics& st) const { m_farkas_learner.collect_statistics(st); } core_convex_hull_generalizer::core_convex_hull_generalizer(context& ctx, bool is_closure): core_generalizer(ctx), m(ctx.get_manager()), m_is_closure(is_closure) { } void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { // method3(n, core, uses_level, new_cores); method1(n, core, uses_level, new_cores); } void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { UNREACHABLE(); } // use the entire region as starting point for generalization. // // Constraints: // add_variables: y = y1 + y2 // core: Ay <= b -> conv1: A*y1 <= b*sigma1 // sigma1 > 0 // sigma2 > 0 // 1 = sigma1 + sigma2 // A'y <= b' -> conv2: A'*y2 <= b'*sigma2 // // If Constraints & Transition(y0, y) is unsat, then // update with new core. // void core_convex_hull_generalizer::method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { expr_ref_vector conv2(m), fmls(m), fml1_2(m); bool change = false; if (core.empty()) { new_cores.push_back(std::make_pair(core, uses_level)); return; } closure cl(n.pt(), m_is_closure); expr_ref fml1 = qe::mk_and(core); expr_ref fml2 = n.pt().get_formulas(n.level(), false); fml1_2.push_back(fml1); fml1_2.push_back(0); qe::flatten_and(fml2, fmls); for (unsigned i = 0; i < fmls.size(); ++i) { fml2 = m.mk_not(fmls[i].get()); fml1_2[1] = fml2; expr_ref state = cl(fml1_2); TRACE("pdr", tout << "Check states:\n" << mk_pp(state, m) << "\n"; tout << "Old states:\n" << mk_pp(fml2, m) << "\n"; ); model_node nd(0, state, n.pt(), n.level()); pred_transformer::scoped_farkas sf(n.pt(), true); bool uses_level1 = uses_level; if (l_false == n.pt().is_reachable(nd, &conv2, uses_level1)) { new_cores.push_back(std::make_pair(conv2, uses_level1)); change = true; expr_ref state1 = qe::mk_and(conv2); TRACE("pdr", tout << mk_pp(state, m) << "\n"; tout << "Generalized to:\n" << mk_pp(state1, m) << "\n";); IF_VERBOSE(0, verbose_stream() << mk_pp(state, m) << "\n"; verbose_stream() << "Generalized to:\n" << mk_pp(state1, m) << "\n";); } } if (!m_is_closure || !change) { new_cores.push_back(std::make_pair(core, uses_level)); } } /* Extract the lemmas from the transition relation that were used to establish unsatisfiability. Take convex closures of conbinations of these lemmas. */ void core_convex_hull_generalizer::method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { TRACE("dl", tout << "method: generalize consequences of F(R)\n"; for (unsigned i = 0; i < core.size(); ++i) { tout << "B:" << mk_pp(core[i], m) << "\n"; }); bool uses_level1; expr_ref_vector core1(m); core1.append(core); expr_ref_vector consequences(m); { n.pt().get_solver().set_consequences(&consequences); pred_transformer::scoped_farkas sf (n.pt(), true); VERIFY(l_false == n.pt().is_reachable(n, &core1, uses_level1)); n.pt().get_solver().set_consequences(0); } IF_VERBOSE(0, verbose_stream() << "Consequences: " << consequences.size() << "\n"; for (unsigned i = 0; i < consequences.size(); ++i) { verbose_stream() << mk_pp(consequences[i].get(), m) << "\n"; } verbose_stream() << "core: " << core1.size() << "\n"; for (unsigned i = 0; i < core1.size(); ++i) { verbose_stream() << mk_pp(core1[i].get(), m) << "\n"; }); expr_ref tmp(m); // Check that F(R) => \/ consequences { expr_ref_vector cstate(m); for (unsigned i = 0; i < consequences.size(); ++i) { cstate.push_back(m.mk_not(consequences[i].get())); } tmp = m.mk_and(cstate.size(), cstate.c_ptr()); model_node nd(0, tmp, n.pt(), n.level()); pred_transformer::scoped_farkas sf (n.pt(), false); VERIFY(l_false == n.pt().is_reachable(nd, &core1, uses_level1)); } // Create disjunction. tmp = m.mk_and(core.size(), core.c_ptr()); // Check that \/ consequences => not (core) if (!is_unsat(consequences, tmp)) { IF_VERBOSE(0, verbose_stream() << "Consequences don't contradict the core\n";); return; } IF_VERBOSE(0, verbose_stream() << "Consequences contradict core\n";); if (!strengthen_consequences(n, consequences, tmp)) { return; } IF_VERBOSE(0, verbose_stream() << "consequences strengthened\n";); // Use the resulting formula to find Farkas lemmas from core. } bool core_convex_hull_generalizer::strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B) { expr_ref A(m), tmp(m), convA(m); unsigned sz = As.size(); closure cl(n.pt(), m_is_closure); for (unsigned i = 0; i < As.size(); ++i) { expr_ref_vector Hs(m); Hs.push_back(As[i].get()); for (unsigned j = i + 1; j < As.size(); ++j) { Hs.push_back(As[j].get()); bool unsat = false; A = cl(Hs); tmp = As[i].get(); As[i] = A; unsat = is_unsat(As, B); As[i] = tmp; if (unsat) { IF_VERBOSE(0, verbose_stream() << "New convex: " << mk_pp(convA, m) << "\n";); convA = A; As[j] = As.back(); As.pop_back(); --j; } else { Hs.pop_back(); } } if (Hs.size() > 1) { As[i] = convA; } } return sz > As.size(); } bool core_convex_hull_generalizer::is_unsat(expr_ref_vector const& As, expr* B) { smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p); expr_ref disj(m); disj = m.mk_or(As.size(), As.c_ptr()); ctx.assert_expr(disj); ctx.assert_expr(B); std::cout << "Checking\n" << mk_pp(disj, m) << "\n" << mk_pp(B, m) << "\n"; return l_false == ctx.check(); } // --------------------------------- // core_arith_inductive_generalizer // NB. this is trying out some ideas for generalization in // an ad hoc specialized way. arith_inductive_generalizer should // not be used by default. It is a place-holder for a general purpose // extrapolator of a lattice basis. core_arith_inductive_generalizer::core_arith_inductive_generalizer(context& ctx): core_generalizer(ctx), m(ctx.get_manager()), a(m), m_refs(m) {} void core_arith_inductive_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { if (core.size() <= 1) { return; } reset(); expr_ref e(m), t1(m), t2(m), t3(m); rational r; TRACE("pdr", for (unsigned i = 0; i < core.size(); ++i) { tout << mk_pp(core[i].get(), m) << "\n"; }); svector eqs; get_eqs(core, eqs); if (eqs.empty()) { return; } expr_ref_vector new_core(m); new_core.append(core); for (unsigned eq = 0; eq < eqs.size(); ++eq) { rational r = eqs[eq].m_value; expr* x = eqs[eq].m_term; unsigned k = eqs[eq].m_i; unsigned l = eqs[eq].m_j; new_core[l] = m.mk_true(); new_core[k] = m.mk_true(); for (unsigned i = 0; i < new_core.size(); ++i) { if (substitute_alias(r, x, new_core[i].get(), e)) { new_core[i] = e; } } if (abs(r) >= rational(2) && a.is_int(x)) { new_core[k] = m.mk_eq(a.mk_mod(x, a.mk_numeral(rational(2), true)), a.mk_numeral(rational(0), true)); new_core[l] = a.mk_le(x, a.mk_numeral(rational(0), true)); } } bool inductive = n.pt().check_inductive(n.level(), new_core, uses_level); IF_VERBOSE(1, verbose_stream() << (inductive?"":"non") << "inductive\n"; verbose_stream() << "old\n"; for (unsigned j = 0; j < core.size(); ++j) { verbose_stream() << mk_pp(core[j].get(), m) << "\n"; } verbose_stream() << "new\n"; for (unsigned j = 0; j < new_core.size(); ++j) { verbose_stream() << mk_pp(new_core[j].get(), m) << "\n"; }); if (inductive) { core.reset(); core.append(new_core); } } void core_arith_inductive_generalizer::insert_bound(bool is_lower, expr* x, rational const& r, unsigned i) { if (r.is_neg()) { expr_ref e(m); e = a.mk_uminus(x); m_refs.push_back(e); x = e; is_lower = !is_lower; } vector bound; bound.push_back(std::make_pair(x, i)); if (is_lower) { m_lb.insert(abs(r), bound); } else { m_ub.insert(abs(r), bound); } } void core_arith_inductive_generalizer::reset() { m_refs.reset(); m_lb.reset(); m_ub.reset(); } void core_arith_inductive_generalizer::get_eqs(expr_ref_vector const& core, svector& eqs) { expr* e1, *x, *y; expr_ref e(m); rational r; for (unsigned i = 0; i < core.size(); ++i) { e = core[i]; if (m.is_not(e, e1) && a.is_le(e1, x, y) && a.is_numeral(y, r) && a.is_int(x)) { // not (<= x r) <=> x >= r + 1 insert_bound(true, x, r + rational(1), i); } else if (m.is_not(e, e1) && a.is_ge(e1, x, y) && a.is_numeral(y, r) && a.is_int(x)) { // not (>= x r) <=> x <= r - 1 insert_bound(false, x, r - rational(1), i); } else if (a.is_le(e, x, y) && a.is_numeral(y, r)) { insert_bound(false, x, r, i); } else if (a.is_ge(e, x, y) && a.is_numeral(y, r)) { insert_bound(true, x, r, i); } } bounds_t::iterator it = m_lb.begin(), end = m_lb.end(); for (; it != end; ++it) { rational r = it->m_key; vector & terms1 = it->m_value; vector terms2; if (r >= rational(2) && m_ub.find(r, terms2)) { for (unsigned i = 0; i < terms1.size(); ++i) { bool done = false; for (unsigned j = 0; !done && j < terms2.size(); ++j) { expr* t1 = terms1[i].first; expr* t2 = terms2[j].first; if (t1 == t2) { eqs.push_back(eq(t1, r, terms1[i].second, terms2[j].second)); done = true; } else { e = m.mk_eq(t1, t2); th_rewriter rw(m); rw(e); if (m.is_true(e)) { eqs.push_back(eq(t1, r, terms1[i].second, terms2[j].second)); done = true; } } } } } } } bool core_arith_inductive_generalizer::substitute_alias(rational const& r, expr* x, expr* e, expr_ref& result) { rational r2; expr* y, *z, *e1; if (m.is_not(e, e1) && substitute_alias(r, x, e1, result)) { result = m.mk_not(result); return true; } if (a.is_le(e, y, z) && a.is_numeral(z, r2)) { if (r == r2) { result = a.mk_le(y, x); return true; } if (r == r2 + rational(1)) { result = a.mk_lt(y, x); return true; } if (r == r2 - rational(1)) { result = a.mk_le(y, a.mk_sub(x, a.mk_numeral(rational(1), a.is_int(x)))); return true; } } if (a.is_ge(e, y, z) && a.is_numeral(z, r2)) { if (r == r2) { result = a.mk_ge(y, x); return true; } if (r2 == r + rational(1)) { result = a.mk_gt(y, x); return true; } if (r2 == r - rational(1)) { result = a.mk_ge(y, a.mk_sub(x, a.mk_numeral(rational(1), a.is_int(x)))); return true; } } return false; } // // < F, phi, i + 1> // | // < G, psi, i > // // where: // // p(x) <- F(x,y,p,q) // q(x) <- G(x,y) // // Hyp: // Q_k(x) => phi(x) j <= k <= i // Q_k(x) => R_k(x) j <= k <= i + 1 // Q_k(x) <=> Trans(Q_{k-1}) j < k <= i + 1 // Conclusion: // Q_{i+1}(x) => phi(x) // class core_induction_generalizer::imp { context& m_ctx; manager& pm; ast_manager& m; // // Create predicate Q_level // func_decl_ref mk_pred(unsigned level, func_decl* f) { func_decl_ref result(m); std::ostringstream name; name << f->get_name() << "_" << level; symbol sname(name.str().c_str()); result = m.mk_func_decl(sname, f->get_arity(), f->get_domain(), f->get_range()); return result; } // // Create formula exists y . z . F[Q_{level-1}, x, y, z] // expr_ref mk_transition_rule( expr_ref_vector const& reps, unsigned level, datalog::rule const& rule) { expr_ref_vector conj(m), sub(m); expr_ref result(m); svector names; unsigned ut_size = rule.get_uninterpreted_tail_size(); unsigned t_size = rule.get_tail_size(); if (0 == level && 0 < ut_size) { result = m.mk_false(); return result; } app* atom = rule.get_head(); SASSERT(atom->get_num_args() == reps.size()); for (unsigned i = 0; i < reps.size(); ++i) { expr* arg = atom->get_arg(i); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (idx >= sub.size()) sub.resize(idx+1); if (sub[idx].get()) { conj.push_back(m.mk_eq(sub[idx].get(), reps[i])); } else { sub[idx] = reps[i]; } } else { conj.push_back(m.mk_eq(arg, reps[i])); } } for (unsigned i = 0; 0 < level && i < ut_size; i++) { app* atom = rule.get_tail(i); func_decl* head = atom->get_decl(); func_decl_ref fn = mk_pred(level-1, head); conj.push_back(m.mk_app(fn, atom->get_num_args(), atom->get_args())); } for (unsigned i = ut_size; i < t_size; i++) { conj.push_back(rule.get_tail(i)); } result = qe::mk_and(conj); if (!sub.empty()) { expr_ref tmp = result; var_subst(m, false)(tmp, sub.size(), sub.c_ptr(), result); } expr_free_vars fv; fv(result); fv.set_default_sort(m.mk_bool_sort()); for (unsigned i = 0; i < fv.size(); ++i) { names.push_back(symbol(fv.size() - i - 1)); } if (!fv.empty()) { fv.reverse(); result = m.mk_exists(fv.size(), fv.c_ptr(), names.c_ptr(), result); } return result; } expr_ref bind_head(expr_ref_vector const& reps, expr* fml) { expr_ref result(m); expr_abstract(m, 0, reps.size(), reps.c_ptr(), fml, result); ptr_vector sorts; svector names; unsigned sz = reps.size(); for (unsigned i = 0; i < sz; ++i) { sorts.push_back(m.get_sort(reps[sz-i-1])); names.push_back(symbol(sz-i-1)); } if (sz > 0) { result = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), result); } return result; } expr_ref_vector mk_reps(pred_transformer& pt) { expr_ref_vector reps(m); expr_ref rep(m); for (unsigned i = 0; i < pt.head()->get_arity(); ++i) { rep = m.mk_const(pm.o2n(pt.sig(i), 0)); reps.push_back(rep); } return reps; } // // extract transition axiom: // // forall x . p_lvl(x) <=> exists y z . F[p_{lvl-1}(y), q_{lvl-1}(z), x] // expr_ref mk_transition_axiom(pred_transformer& pt, unsigned level) { expr_ref fml(m.mk_false(), m), tr(m); expr_ref_vector reps = mk_reps(pt); ptr_vector const& rules = pt.rules(); for (unsigned i = 0; i < rules.size(); ++i) { tr = mk_transition_rule(reps, level, *rules[i]); fml = (i == 0)?tr.get():m.mk_or(fml, tr); } func_decl_ref fn = mk_pred(level, pt.head()); fml = m.mk_iff(m.mk_app(fn, reps.size(), reps.c_ptr()), fml); fml = bind_head(reps, fml); return fml; } // // Create implication: // Q_level(x) => phi(x) // expr_ref mk_predicate_property(unsigned level, pred_transformer& pt, expr* phi) { expr_ref_vector reps = mk_reps(pt); func_decl_ref fn = mk_pred(level, pt.head()); expr_ref fml(m); fml = m.mk_implies(m.mk_app(fn, reps.size(), reps.c_ptr()), phi); fml = bind_head(reps, fml); return fml; } public: imp(context& ctx): m_ctx(ctx), pm(ctx.get_pdr_manager()), m(ctx.get_manager()) {} // // not exists y . F(x,y) // expr_ref mk_blocked_transition(pred_transformer& pt, unsigned level) { SASSERT(level > 0); expr_ref fml(m.mk_true(), m); expr_ref_vector reps = mk_reps(pt), fmls(m); ptr_vector const& rules = pt.rules(); for (unsigned i = 0; i < rules.size(); ++i) { fmls.push_back(m.mk_not(mk_transition_rule(reps, level, *rules[i]))); } fml = qe::mk_and(fmls); TRACE("pdr", tout << mk_pp(fml, m) << "\n";); return fml; } expr_ref mk_induction_goal(pred_transformer& pt, unsigned level, unsigned depth) { SASSERT(level >= depth); expr_ref_vector conjs(m); ptr_vector pts; unsigned_vector levels; // negated goal expr_ref phi = mk_blocked_transition(pt, level); conjs.push_back(m.mk_not(mk_predicate_property(level, pt, phi))); pts.push_back(&pt); levels.push_back(level); // Add I.H. for (unsigned lvl = level-depth; lvl < level; ++lvl) { if (lvl > 0) { expr_ref psi = mk_blocked_transition(pt, lvl); conjs.push_back(mk_predicate_property(lvl, pt, psi)); pts.push_back(&pt); levels.push_back(lvl); } } // Transitions: for (unsigned qhead = 0; qhead < pts.size(); ++qhead) { pred_transformer& qt = *pts[qhead]; unsigned lvl = levels[qhead]; // Add transition definition and properties at level. conjs.push_back(mk_transition_axiom(qt, lvl)); conjs.push_back(mk_predicate_property(lvl, qt, qt.get_formulas(lvl, true))); // Enqueue additional hypotheses ptr_vector const& rules = qt.rules(); if (lvl + depth < level || lvl == 0) { continue; } for (unsigned i = 0; i < rules.size(); ++i) { datalog::rule& r = *rules[i]; unsigned ut_size = r.get_uninterpreted_tail_size(); for (unsigned j = 0; j < ut_size; ++j) { func_decl* f = r.get_tail(j)->get_decl(); pred_transformer* rt = m_ctx.get_pred_transformers().find(f); bool found = false; for (unsigned k = 0; !found && k < levels.size(); ++k) { found = (rt == pts[k] && levels[k] + 1 == lvl); } if (!found) { levels.push_back(lvl-1); pts.push_back(rt); } } } } expr_ref result = qe::mk_and(conjs); TRACE("pdr", tout << mk_pp(result, m) << "\n";); return result; } }; // // Instantiate Peano induction schema. // void core_induction_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { model_node* p = n.parent(); if (p == 0) { return; } unsigned depth = 2; imp imp(m_ctx); ast_manager& m = core.get_manager(); expr_ref goal = imp.mk_induction_goal(p->pt(), p->level(), depth); smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p); ctx.assert_expr(goal); lbool r = ctx.check(); TRACE("pdr", tout << r << "\n"; for (unsigned i = 0; i < core.size(); ++i) { tout << mk_pp(core[i].get(), m) << "\n"; }); if (r == l_false) { core.reset(); expr_ref phi = imp.mk_blocked_transition(p->pt(), p->level()); core.push_back(m.mk_not(phi)); uses_level = true; } } }; z3-4.4.0/src/muz/transforms/0000755000175000017500000000000012540347414015445 5ustar michaelmichaelz3-4.4.0/src/muz/transforms/dl_mk_separate_negated_tails.h0000644000175000017500000000267512540347414023465 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: mk_separate_negated_tails.h Abstract: Rule transformer which creates new rules for predicates in negated tails that use free variables not used elsewhere. These free variables incur an overhead on the instructions compiled using dl_compiler. Consider the following transformations: P(x) :- Exists y, z, u . Q(x,y), !R(y,z), !T(z,u). => P(x) :- Exists y, z . Q(x,y), !R(y,z), Exists u . ! T(z,u). => P(x) :- Exists y, z . Q(x,y), !R(y,z), TN(z). TN(z) :- !T(z,u). Author: Nikolaj Bjorner (nbjorner) 2013-09-09 Revision History: --*/ #ifndef _DL_MK_SEPARAT_NEGATED_TAILS_H_ #define _DL_MK_SEPARAT_NEGATED_TAILS_H_ #include "dl_rule_transformer.h" #include "dl_context.h" namespace datalog { class mk_separate_negated_tails : public rule_transformer::plugin { ast_manager & m; rule_manager& rm; context & m_ctx; ptr_vector m_vars; expr_free_vars m_fv; bool has_private_vars(rule const& r, unsigned j); void get_private_vars(rule const& r, unsigned j); void abstract_predicate(app* p, app_ref& q, rule_set& rules); void create_rule(rule const&r, rule_set& rules); public: mk_separate_negated_tails(context& ctx, unsigned priority = 21000); rule_set * operator()(rule_set const & source); }; } #endif z3-4.4.0/src/muz/transforms/dl_mk_scale.h0000644000175000017500000000220512540347414020052 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_scale.h Abstract: Add scale factor to linear (Real) arithemetic Horn clauses. The transformation replaces occurrences of isolated constants by a scale multiplied to each constant. Author: Nikolaj Bjorner (nbjorner) 2013-08-19 Revision History: --*/ #ifndef _DL_MK_SCALE_H_ #define _DL_MK_SCALE_H_ #include"dl_rule_transformer.h" #include"arith_decl_plugin.h" namespace datalog { class mk_scale : public rule_transformer::plugin { class scale_model_converter; ast_manager& m; context& m_ctx; arith_util a; expr_ref_vector m_trail; app_ref_vector m_eqs; obj_map m_cache; scale_model_converter* m_mc; expr* linearize(unsigned num_vars, expr* e); app_ref mk_pred(unsigned num_vars, app* q); app_ref mk_constraint(unsigned num_vars, app* q); public: mk_scale(context & ctx, unsigned priority = 33039); virtual ~mk_scale(); rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_SCALE_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_magic_sets.h0000644000175000017500000001021512540347414021101 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_magic_sets.h Abstract: Author: Krystof Hoder (t-khoder) 2010-10-4. Revision History: --*/ #ifndef _DL_MK_MAGIC_SETS_H_ #define _DL_MK_MAGIC_SETS_H_ #include #include"map.h" #include"obj_pair_hashtable.h" #include"dl_context.h" #include"dl_rule_set.h" #include"dl_rule_transformer.h" namespace datalog { /** \brief Implements magic sets rule transformation. According to A. Voronkov. Foundations of Deductive Databases. The stratified negation is not in the book addressed wrt. magic sets, but it seems that, for the purpose of magic sets, the negated literals should be treated just as if they were non-negated (we are interested only in values of arguments, not in the actual content of relations, at that point). */ class mk_magic_sets : public rule_transformer::plugin { enum a_flag { AD_FREE, AD_BOUND }; struct a_flag_hash { typedef a_flag data; unsigned operator()(a_flag x) const { return x; } }; struct adornment : public svector { void populate(app * lit, const var_idx_set & bound_vars); bool operator==(const adornment & o) const { return vectors_equal(*this, o); } std::string to_string() const; }; struct adornment_desc { func_decl * m_pred; adornment m_adornment; adornment_desc() {} adornment_desc(func_decl * pred) : m_pred(pred) {} adornment_desc(func_decl * pred, const adornment & a) : m_pred(pred), m_adornment(a) {} bool operator==(const adornment_desc & o) const { //m_tail_adornment value is implied by the rule and the head adornment return m_pred==o.m_pred && m_adornment==o.m_adornment; } unsigned hash() const { return m_pred->hash()^svector_hash()(m_adornment); } }; struct adorned_rule { app * m_head; adornment m_head_adornment; ptr_vector m_tail; }; typedef hashtable, default_eq > adornment_set; typedef map, default_eq > adornment_map; typedef obj_map pred_adornment_map; typedef obj_map pred2pred; context & m_context; ast_manager & m; rule_manager& rm; ast_ref_vector m_pinned; /** \brief Predicates from the original set that appear in a head of a rule */ func_decl_set m_extentional; //adornment_set m_processed; vector m_todo; adornment_map m_adorned_preds; pred_adornment_map m_adornments; pred2pred m_magic_preds; func_decl_ref m_goal; void reset(); float get_unbound_cost(app * lit, const var_idx_set & bound_vars); int pop_bound(unsigned_vector & cont, rule * r, const var_idx_set & bound_vars); app * create_magic_literal(app * l); void create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated, rule_set& result); app * adorn_literal(app * lit, const var_idx_set & bound_vars); void transform_rule(const adornment & head_adornment, rule * r, rule_set& result); void create_transfer_rule(const adornment_desc & d, rule_set& result); public: /** \brief Create magic sets rule transformer for \c goal_rule. When applying the transformer, the \c goal_rule must be present in the \c rule_set that is being transformed. */ mk_magic_sets(context & ctx, func_decl* goal); rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_MAGIC_SETS_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_array_blast.h0000644000175000017500000000332512540347414021272 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_array_blast.h Abstract: Remove array variables from rules. Author: Nikolaj Bjorner (nbjorner) 2012-11-23 Revision History: --*/ #ifndef _DL_MK_ARRAY_BLAST_H_ #define _DL_MK_ARRAY_BLAST_H_ #include"dl_context.h" #include"dl_rule_set.h" #include"dl_rule_transformer.h" #include"dl_mk_interp_tail_simplifier.h" #include "equiv_proof_converter.h" #include "array_decl_plugin.h" #include "expr_safe_replace.h" namespace datalog { /** \brief Blast occurrences of arrays in rules */ class mk_array_blast : public rule_transformer::plugin { typedef obj_map defs_t; context& m_ctx; ast_manager& m; array_util a; rule_manager& rm; params_ref m_params; th_rewriter m_rewriter; mk_interp_tail_simplifier m_simplifier; defs_t m_defs; unsigned m_next_var; bool blast(rule& r, rule_set& new_rules); bool is_store_def(expr* e, expr*& x, expr*& y); bool ackermanize(rule const& r, expr_ref& body, expr_ref& head); expr* get_select(expr* e) const; void get_select_args(expr* e, ptr_vector& args) const; bool insert_def(rule const& r, app* e, var* v); bool is_select_eq_var(expr* e, app*& s, var*& v) const; public: /** \brief Create rule transformer that removes array stores and selects by ackermannization. */ mk_array_blast(context & ctx, unsigned priority); virtual ~mk_array_blast(); rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_ARRAY_BLAST_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_magic_symbolic.h0000644000175000017500000000140712540347414021747 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_magic_symbolic.h Abstract: Create Horn clauses for magic symbolic transformation. Author: Nikolaj Bjorner (nbjorner) 2013-06-19 Revision History: --*/ #ifndef _DL_MK_MAGIC_SYMBOLIC_H_ #define _DL_MK_MAGIC_SYMBOLIC_H_ #include"dl_rule_transformer.h" namespace datalog { class mk_magic_symbolic : public rule_transformer::plugin { ast_manager& m; context& m_ctx; app_ref mk_ans(app* q); app_ref mk_query(app* q); public: mk_magic_symbolic(context & ctx, unsigned priority = 33037); ~mk_magic_symbolic(); rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_MAGIC_SYMBOLIC_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_magic_symbolic.cpp0000644000175000017500000001035712540347414022306 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_magic_symbolic.cpp Abstract: Create Horn clauses for magic symbolic flow. Q(x) :- A(y), B(z), phi1(x,y,z). Q(x) :- C(y), phi2(x,y). A(x) :- C(y), phi3(x,y). A(x) :- A(y), phi3(x,y). B(x) :- C(y), A(z), phi4(x,y,z). C(x) :- phi5(x). Transformed clauses: Q_ans(x) :- Q_query(x), A_ans(y), B_ans(z), phi1(x,y,z). Q_ans(x) :- Q_query(x), C_ans(y), phi2(x,y). Q_query(x) :- true. A_ans(x) :- A_query(x), C_ans(y), phi2(x,y) A_ans(x) :- A_query(x), A_ans(y), phi3(x,y). A_query(y) :- Q_query(x), phi1(x,y,z). A_query(y) :- A_query(x), phi3(x,y). A_query(z) :- B_query(x), C_ans(y), phi4(x,y,z). B_ans(x) :- B_query(x), C_ans(y), A_ans(z), phi4(x,y,z). B_query(z) :- Q_query(x), A_ans(y), phi1(x,y,z). C_ans(x) :- C_query(x), phi5(x). C_query(y) :- Q_query(x), phi2(x,y). C_query(y) :- Q_query(x), phi3(x,y). C_query(y) :- B_query(x), phi4(x,y,z). General scheme: A(x) :- P1(x_1), ..., Pn(x_n), phi(x,x1,..,x_n). P(x) :- Prefix(x,y,z), A(z) ... A_ans(x) :- A_query(x), P_i_ans(x_i), phi(x,..). A_query(z) :- P_query(x), Prefix_ans(x,y,z). Author: Nikolaj Bjorner (nbjorner) 2013-06-19 Revision History: --*/ #include"dl_mk_magic_symbolic.h" #include"dl_context.h" namespace datalog { mk_magic_symbolic::mk_magic_symbolic(context & ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), m_ctx(ctx) { } mk_magic_symbolic::~mk_magic_symbolic() { } rule_set * mk_magic_symbolic::operator()(rule_set const & source) { if (!m_ctx.magic()) { return 0; } context& ctx = source.get_context(); rule_manager& rm = source.get_rule_manager(); rule_set * result = alloc(rule_set, ctx); unsigned sz = source.get_num_rules(); rule_ref new_rule(rm); app_ref_vector tail(m); app_ref head(m); svector neg; for (unsigned i = 0; i < sz; ++i) { rule & r = *source.get_rule(i); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); tail.reset(); neg.reset(); for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(r.get_tail(j)); neg.push_back(false); } tail.push_back(mk_query(r.get_head())); neg.push_back(false); for (unsigned j = 0; j < utsz; ++j) { tail.push_back(mk_ans(r.get_tail(j))); neg.push_back(false); } new_rule = rm.mk(mk_ans(r.get_head()), tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); result->add_rule(new_rule); if (source.is_output_predicate(r.get_decl())) { result->set_output_predicate(new_rule->get_decl()); new_rule = rm.mk(mk_query(r.get_head()), 0, 0, 0, r.name(), true); result->add_rule(new_rule); } for (unsigned j = 0; j < utsz; ++j) { new_rule = rm.mk(mk_query(r.get_tail(j)), tail.size()-utsz+j, tail.c_ptr(), neg.c_ptr(), r.name(), true); result->add_rule(new_rule); } } TRACE("dl", result->display(tout);); return result; } app_ref mk_magic_symbolic::mk_query(app* q) { string_buffer<64> name; func_decl* f = q->get_decl(); name << f->get_name() << "!query"; func_decl_ref g(m); g = m.mk_func_decl(symbol(name.c_str()), f->get_arity(), f->get_domain(), f->get_range()); m_ctx.register_predicate(g, false); return app_ref(m.mk_app(g, q->get_num_args(), q->get_args()), m); } app_ref mk_magic_symbolic::mk_ans(app* q) { string_buffer<64> name; func_decl* f = q->get_decl(); func_decl_ref g(m); name << f->get_name() << "!ans"; g = m.mk_func_decl(symbol(name.c_str()), f->get_arity(), f->get_domain(), f->get_range()); m_ctx.register_predicate(g, false); return app_ref(m.mk_app(g, q->get_num_args(), q->get_args()), m); } }; z3-4.4.0/src/muz/transforms/dl_mk_different.h0000644000175000017500000000133312540347414020732 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_different_symbolic.h Abstract: Create Horn clauses for different symbolic transformation. Author: Nikolaj Bjorner (nbjorner) 2013-06-19 Revision History: --*/ #ifndef _DL_MK_DIFFERENT_SYMBOLIC_H_ #define _DL_MK_DIFFERENT_SYMBOLIC_H_ #include"dl_rule_transformer.h" namespace datalog { class mk_different_symbolic : public rule_transformer::plugin { ast_manager& m; context& m_ctx; public: mk_different_symbolic(context & ctx, unsigned priority = 33037); ~mk_different_symbolic(); rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_DIFFERENT_SYMBOLIC_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_coi_filter.h0000644000175000017500000000172312540347414021106 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_coi_filter.h Abstract: Rule transformer which removes relations which are out of the cone of influence of output relations Author: Krystof Hoder (t-khoder) 2011-10-01. Revision History: --*/ #ifndef _DL_MK_COI_FILTER_H_ #define _DL_MK_COI_FILTER_H_ #include "dl_context.h" #include "dl_rule_transformer.h" namespace datalog { class mk_coi_filter : public rule_transformer::plugin { typedef obj_map decl_map; ast_manager & m; context & m_context; rule_set * bottom_up(rule_set const & source); rule_set * top_down(rule_set const & source); public: mk_coi_filter(context & ctx, unsigned priority=45000) : plugin(priority), m(ctx.get_manager()), m_context(ctx) {} rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_COI_FILTER_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_karr_invariants.cpp0000644000175000017500000002507612540347414022526 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_karr_invariants.cpp Abstract: Extract integer linear invariants. The linear invariants are extracted according to Karr's method. A short description is in Nikolaj Bjorner, Anca Browne and Zohar Manna. Automatic Generation of Invariants and Intermediate Assertions, in CP 95. The algorithm is here adapted to Horn clauses. The idea is to maintain two data-structures for each recursive relation. We call them R and RD - R - set of linear congruences that are true of R. - RD - the dual basis of of solutions for R. RD is updated by accumulating basis vectors for solutions to R (the homogeneous dual of R) R is updated from the inhomogeneous dual of RD. Author: Nikolaj Bjorner (nbjorner) 2013-03-09 Revision History: --*/ #include"expr_safe_replace.h" #include"bool_rewriter.h" #include"for_each_expr.h" #include"dl_mk_karr_invariants.h" #include"dl_mk_backwards.h" #include"dl_mk_loop_counter.h" namespace datalog { mk_karr_invariants::mk_karr_invariants(context & ctx, unsigned priority): rule_transformer::plugin(priority, false), m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_inner_ctx(m, ctx.get_register_engine(), ctx.get_fparams()), a(m), m_pinned(m), m_cancel(false) { params_ref params; params.set_sym("default_relation", symbol("karr_relation")); params.set_sym("engine", symbol("datalog")); params.set_bool("karr", false); m_inner_ctx.updt_params(params); } mk_karr_invariants::~mk_karr_invariants() { } matrix& matrix::operator=(matrix const& other) { reset(); append(other); return *this; } void matrix::display_row( std::ostream& out, vector const& row, rational const& b, bool is_eq) { for (unsigned j = 0; j < row.size(); ++j) { out << row[j] << " "; } out << (is_eq?" = ":" >= ") << -b << "\n"; } void matrix::display_ineq( std::ostream& out, vector const& row, rational const& b, bool is_eq) { bool first = true; for (unsigned j = 0; j < row.size(); ++j) { if (!row[j].is_zero()) { if (!first && row[j].is_pos()) { out << "+ "; } if (row[j].is_minus_one()) { out << "- "; } if (row[j] > rational(1) || row[j] < rational(-1)) { out << row[j] << "*"; } out << "x" << j << " "; first = false; } } out << (is_eq?"= ":">= ") << -b << "\n"; } void matrix::display(std::ostream& out) const { for (unsigned i = 0; i < A.size(); ++i) { display_row(out, A[i], b[i], eq[i]); } } class mk_karr_invariants::add_invariant_model_converter : public model_converter { ast_manager& m; arith_util a; func_decl_ref_vector m_funcs; expr_ref_vector m_invs; public: add_invariant_model_converter(ast_manager& m): m(m), a(m), m_funcs(m), m_invs(m) {} virtual ~add_invariant_model_converter() { } void add(func_decl* p, expr* inv) { if (!m.is_true(inv)) { m_funcs.push_back(p); m_invs.push_back(inv); } } virtual void operator()(model_ref & mr) { for (unsigned i = 0; i < m_funcs.size(); ++i) { func_decl* p = m_funcs[i].get(); func_interp* f = mr->get_func_interp(p); expr_ref body(m); unsigned arity = p->get_arity(); SASSERT(0 < arity); if (f) { SASSERT(f->num_entries() == 0); if (!f->is_partial()) { bool_rewriter(m).mk_and(f->get_else(), m_invs[i].get(), body); } } else { f = alloc(func_interp, m, arity); mr->register_decl(p, f); body = m.mk_false(); // fragile: assume that relation was pruned by being infeasible. } f->set_else(body); } } virtual model_converter * translate(ast_translation & translator) { add_invariant_model_converter* mc = alloc(add_invariant_model_converter, m); for (unsigned i = 0; i < m_funcs.size(); ++i) { mc->add(translator(m_funcs[i].get()), m_invs[i].get()); } return mc; } private: void mk_body(matrix const& M, expr_ref& body) { expr_ref_vector conj(m); for (unsigned i = 0; i < M.size(); ++i) { mk_body(M.A[i], M.b[i], M.eq[i], conj); } bool_rewriter(m).mk_and(conj.size(), conj.c_ptr(), body); } void mk_body(vector const& row, rational const& b, bool is_eq, expr_ref_vector& conj) { expr_ref_vector sum(m); expr_ref zero(m), lhs(m); zero = a.mk_numeral(rational(0), true); for (unsigned i = 0; i < row.size(); ++i) { if (row[i].is_zero()) { continue; } var* var = m.mk_var(i, a.mk_int()); if (row[i].is_one()) { sum.push_back(var); } else { sum.push_back(a.mk_mul(a.mk_numeral(row[i], true), var)); } } if (!b.is_zero()) { sum.push_back(a.mk_numeral(b, true)); } lhs = a.mk_add(sum.size(), sum.c_ptr()); if (is_eq) { conj.push_back(m.mk_eq(lhs, zero)); } else { conj.push_back(a.mk_ge(lhs, zero)); } } }; void mk_karr_invariants::cancel() { m_cancel = true; m_inner_ctx.cancel(); } rule_set * mk_karr_invariants::operator()(rule_set const & source) { if (!m_ctx.karr()) { return 0; } rule_set::iterator it = source.begin(), end = source.end(); for (; it != end; ++it) { rule const& r = **it; if (r.has_negation()) { return 0; } } mk_loop_counter lc(m_ctx); mk_backwards bwd(m_ctx); scoped_ptr src_loop = lc(source); TRACE("dl", src_loop->display(tout << "source loop\n");); get_invariants(*src_loop); if (m_cancel) { return 0; } // figure out whether to update same rules as used for saturation. scoped_ptr rev_source = bwd(*src_loop); get_invariants(*rev_source); scoped_ptr src_annot = update_rules(*src_loop); rule_set* rules = lc.revert(*src_annot); rules->inherit_predicates(source); TRACE("dl", rules->display(tout);); m_pinned.reset(); m_fun2inv.reset(); return rules; } void mk_karr_invariants::get_invariants(rule_set const& src) { m_inner_ctx.reset(); rel_context_base& rctx = *m_inner_ctx.get_rel_context(); ptr_vector heads; func_decl_set const& predicates = m_ctx.get_predicates(); for (func_decl_set::iterator fit = predicates.begin(); fit != predicates.end(); ++fit) { m_inner_ctx.register_predicate(*fit, false); } m_inner_ctx.ensure_opened(); m_inner_ctx.replace_rules(src); m_inner_ctx.close(); rule_set::decl2rules::iterator dit = src.begin_grouped_rules(); rule_set::decl2rules::iterator dend = src.end_grouped_rules(); for (; dit != dend; ++dit) { heads.push_back(dit->m_key); } m_inner_ctx.rel_query(heads.size(), heads.c_ptr()); // retrieve invariants. dit = src.begin_grouped_rules(); for (; dit != dend; ++dit) { func_decl* p = dit->m_key; expr_ref fml = rctx.try_get_formula(p); if (fml && !m.is_true(fml)) { expr* inv = 0; if (m_fun2inv.find(p, inv)) { fml = m.mk_and(inv, fml); } m_pinned.push_back(fml); m_fun2inv.insert(p, fml); } } } rule_set* mk_karr_invariants::update_rules(rule_set const& src) { scoped_ptr dst = alloc(rule_set, m_ctx); rule_set::iterator it = src.begin(), end = src.end(); for (; it != end; ++it) { update_body(*dst, **it); } if (m_ctx.get_model_converter()) { add_invariant_model_converter* kmc = alloc(add_invariant_model_converter, m); rule_set::decl2rules::iterator git = src.begin_grouped_rules(); rule_set::decl2rules::iterator gend = src.end_grouped_rules(); for (; git != gend; ++git) { func_decl* p = git->m_key; expr* fml = 0; if (m_fun2inv.find(p, fml)) { kmc->add(p, fml); } } m_ctx.add_model_converter(kmc); } dst->inherit_predicates(src); return dst.detach(); } void mk_karr_invariants::update_body(rule_set& rules, rule& r) { unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); app_ref_vector tail(m); expr_ref fml(m); for (unsigned i = 0; i < tsz; ++i) { tail.push_back(r.get_tail(i)); } for (unsigned i = 0; i < utsz; ++i) { func_decl* q = r.get_decl(i); expr* fml = 0; if (m_fun2inv.find(q, fml)) { expr_safe_replace rep(m); for (unsigned j = 0; j < q->get_arity(); ++j) { rep.insert(m.mk_var(j, q->get_domain(j)), r.get_tail(i)->get_arg(j)); } expr_ref tmp(fml, m); rep(tmp); tail.push_back(to_app(tmp)); } } rule* new_rule = &r; if (tail.size() != tsz) { new_rule = rm.mk(r.get_head(), tail.size(), tail.c_ptr(), 0, r.name()); } rules.add_rule(new_rule); rm.mk_rule_rewrite_proof(r, *new_rule); // should be weakening rule. } }; z3-4.4.0/src/muz/transforms/dl_mk_quantifier_instantiation.h0000644000175000017500000000365212540347414024105 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_quantifier_instantiation.h Abstract: Convert Quantified Horn clauses into non-quantified clauses using instantiation. Author: Ken McMillan Andrey Rybalchenko Nikolaj Bjorner (nbjorner) 2013-04-02 Revision History: Based on approach suggested in the SAS 2013 paper "On Solving Universally Quantified Horn Clauses" --*/ #ifndef _DL_MK_QUANTIFIER_INSTANTIATION_H_ #define _DL_MK_QUANTIFIER_INSTANTIATION_H_ #include "dl_rule_transformer.h" #include "expr_safe_replace.h" #include "union_find.h" namespace datalog { class context; class mk_quantifier_instantiation : public rule_transformer::plugin { typedef svector > term_pairs; ast_manager& m; context& m_ctx; expr_safe_replace m_var2cnst; expr_safe_replace m_cnst2var; basic_union_find m_uf; ptr_vector m_todo; ptr_vector m_terms; ptr_vector m_binding; obj_map*> m_funs; void extract_quantifiers(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs); void collect_egraph(expr* e); void instantiate_rule(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs, rule_set& rules); void instantiate_quantifier(quantifier* q, expr_ref_vector & conjs); void instantiate_quantifier(quantifier* q, app* pat, expr_ref_vector & conjs); void match(unsigned i, app* pat, unsigned j, term_pairs& todo, quantifier* q, expr_ref_vector& conjs); void yield_binding(quantifier* q, expr_ref_vector& conjs); public: mk_quantifier_instantiation(context & ctx, unsigned priority); virtual ~mk_quantifier_instantiation(); rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_QUANTIFIER_INSTANTIATION_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_karr_invariants.h0000644000175000017500000000377012540347414022170 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_karr_invariants.h Abstract: Extract integer linear invariants. Author: Nikolaj Bjorner (nbjorner) 2013-03-08 Revision History: --*/ #ifndef _DL_MK_KARR_INVARIANTS_H_ #define _DL_MK_KARR_INVARIANTS_H_ #include"dl_context.h" #include"dl_rule_set.h" #include"dl_rule_transformer.h" #include"arith_decl_plugin.h" #include"hilbert_basis.h" namespace datalog { /** \brief Rule transformer that strengthens bodies with invariants. */ struct matrix { vector > A; vector b; svector eq; unsigned size() const { return A.size(); } void reset() { A.reset(); b.reset(); eq.reset(); } matrix& operator=(matrix const& other); void append(matrix const& other) { A.append(other.A); b.append(other.b); eq.append(other.eq); } void display(std::ostream& out) const; static void display_row( std::ostream& out, vector const& row, rational const& b, bool is_eq); static void display_ineq( std::ostream& out, vector const& row, rational const& b, bool is_eq); }; class mk_karr_invariants : public rule_transformer::plugin { class add_invariant_model_converter; context& m_ctx; ast_manager& m; rule_manager& rm; context m_inner_ctx; arith_util a; obj_map m_fun2inv; ast_ref_vector m_pinned; volatile bool m_cancel; void get_invariants(rule_set const& src); void update_body(rule_set& result, rule& r); rule_set* update_rules(rule_set const& src); public: mk_karr_invariants(context & ctx, unsigned priority); virtual ~mk_karr_invariants(); virtual void cancel(); rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_KARR_INVARIANTS_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_rule_inliner.h0000644000175000017500000001557112540347414021464 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_interp_tail_simplifier.h Abstract: Rule transformer which inlines some of the rules Author: Krystof Hoder (t-khoder) 2011-10-02. Revision History: --*/ #ifndef _DL_MK_RULE_INLINER_H_ #define _DL_MK_RULE_INLINER_H_ #include "dl_context.h" #include "dl_rule_transformer.h" #include "dl_mk_interp_tail_simplifier.h" #include "unifier.h" #include "substitution.h" #include "substitution_tree.h" namespace datalog { class rule_unifier { ast_manager& m; rule_manager& m_rm; context& m_context; /** We use this simplifier after inlining to get nicer intermediate rules */ mk_interp_tail_simplifier m_interp_simplifier; substitution m_subst; unifier m_unif; bool m_ready; bool m_normalize; unsigned m_deltas[2]; public: rule_unifier(context& ctx) : m(ctx.get_manager()), m_rm(ctx.get_rule_manager()), m_context(ctx), m_interp_simplifier(ctx), m_subst(m), m_unif(m), m_ready(false), m_normalize(true) {} /** Reset subtitution and unify tail tgt_idx of the target rule and the head of the src rule */ bool unify_rules(rule const& tgt, unsigned tgt_idx, rule const& src); /** \brief Apply unifier to rules. Return false if the resulting rule is a tautology (the interpreted tail is unsat). */ bool apply(rule const& tgt, unsigned tgt_idx, rule const& src, rule_ref& result); void display(std::ostream& stm) { m_subst.display(stm, 2, m_deltas); } /** Retrieve substitutions for src/tgt. (second argument of unify_rules). */ expr_ref_vector get_rule_subst(rule const& r, bool is_tgt); /** Control if bound variables are normalized after unification. The default is 'true': bound variables are re-mapped to an initial segment of de-Bruijn indices. */ void set_normalize(bool n) { m_normalize = n; } private: void apply(app * a, bool is_tgt, app_ref& res); /** Apply substitution to a rule tail. Tail with skipped_index is skipped, unless skipped_index is equal to UINT_MAX */ void apply(rule const& r, bool is_tgt, unsigned skipped_index, app_ref_vector& res, svector& res_neg); }; class mk_rule_inliner : public rule_transformer::plugin { class visitor : public st_visitor { context& m_context; unsigned_vector m_unifiers; svector m_can_remove, m_can_expand; obj_map m_positions; public: visitor(context& c, substitution & s): st_visitor(s), m_context(c) {} virtual bool operator()(expr* e); void reset() { m_unifiers.reset(); } void reset(unsigned sz); svector& can_remove() { return m_can_remove; } svector& can_expand() { return m_can_expand; } unsigned_vector const& add_position(expr* e, unsigned j); unsigned_vector const& del_position(expr* e, unsigned j); unsigned_vector const& get_unifiers() { return m_unifiers; } }; typedef obj_map decl_map; ast_manager & m; rule_manager & m_rm; context & m_context; th_rewriter& m_simp; rule_ref_vector m_pinned; func_decl_set m_forbidden_preds; func_decl_set m_preds_with_facts; func_decl_set m_preds_with_neg_occurrence; ast_counter m_head_pred_ctr; ast_counter m_head_pred_non_empty_tails_ctr; ast_counter m_tail_pred_ctr; rule_set m_inlined_rules; horn_subsume_model_converter* m_mc; //used in try_to_inline_rule and do_eager_inlining rule_unifier m_unifier; substitution_tree m_head_index; // for straight-line relation inlining. substitution_tree m_tail_index; substitution m_subst; visitor m_head_visitor; visitor m_tail_visitor; bool tail_matches_head(app * tail, app* head); bool try_to_inline_rule(rule& tgt, rule& src, unsigned tail_index, rule_ref& res); bool inlining_allowed(rule_set const& orig, func_decl * pred); void count_pred_occurrences(rule_set const & orig); void plan_inlining(rule_set const & orig); rule_set * create_allowed_rule_set(rule_set const & orig); bool forbid_preds_from_cycles(rule_set const & r); /** Ensure we don't inline two multi-head rules that would appear together in some tail */ bool forbid_multiple_multipliers(const rule_set & orig, rule_set const & proposed_inlined_rules); /** Return true if the rule was modified */ bool transform_rule(rule_set const& orig, rule * r, rule_set& tgt); /** Return true if some transformation was performed */ bool transform_rules(const rule_set & orig, rule_set & tgt); bool is_oriented_rewriter(rule * r, rule_stratifier const& strat); /** Return false if nothing was done with the rule. res may be set to zero if we managed to prove the rule unsatisfiable. */ bool do_eager_inlining(rule * r, rule_set const& rules, rule_ref& res); /** Inline rules even if it doesn't lead to elimination of the whole predicate. The inlining is done as long as it doesn't increase the number of rules (i.e. when only one rule defining a predicate can replace tail atom). The original rule-set must be closed before passing t this function */ bool do_eager_inlining(scoped_ptr & rules); bool has_quantifier(rule const& r) const; /** Inline predicates that are known to not be join-points. */ bool inline_linear(scoped_ptr& rules); void add_rule(rule_set const& rule_set, rule* r, unsigned i); void del_rule(rule* r, unsigned i); public: mk_rule_inliner(context & ctx, unsigned priority=35000) : plugin(priority), m(ctx.get_manager()), m_rm(ctx.get_rule_manager()), m_context(ctx), m_simp(m_context.get_rewriter()), m_pinned(m_rm), m_inlined_rules(m_context), m_mc(0), m_unifier(ctx), m_head_index(m), m_tail_index(m), m_subst(m), m_head_visitor(ctx, m_subst), m_tail_visitor(ctx, m_subst) {} virtual ~mk_rule_inliner() { } rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_INTERP_TAIL_SIMPLIFIER_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_unfold.h0000644000175000017500000000174212540347414020257 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_unfold.h Abstract: Unfold rules once, return the unfolded set of rules. Author: Nikolaj Bjorner (nbjorner) 2012-10-15 Revision History: --*/ #ifndef _DL_MK_UNFOLD_H_ #define _DL_MK_UNFOLD_H_ #include"dl_context.h" #include"dl_rule_set.h" #include"uint_set.h" #include"dl_rule_transformer.h" #include"dl_mk_rule_inliner.h" namespace datalog { /** \brief Implements an unfolding transformation. */ class mk_unfold : public rule_transformer::plugin { context& m_ctx; ast_manager& m; rule_manager& rm; rule_unifier m_unify; void expand_tail(rule& r, unsigned tail_idx, rule_set const& src, rule_set& dst); public: /** \brief Create unfold rule transformer. */ mk_unfold(context & ctx); rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_UNFOLD_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_coi_filter.cpp0000644000175000017500000001766212540347414021452 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_coi_filter.cpp Abstract: Rule transformer which removes relations which are out of the cone of influence of output relations Author: Krystof Hoder (t-khoder) 2011-10-01. Revision History: Andrey Rybalchenko (rybal) 2013-8-8 Added bottom_up pruning. --*/ #include #include"ast_pp.h" #include"dl_mk_coi_filter.h" #include"extension_model_converter.h" namespace datalog { // ----------------------------------- // // mk_coi_filter // // ----------------------------------- rule_set * mk_coi_filter::operator()(rule_set const & source) { if (source.empty()) { return 0; } scoped_ptr result1 = top_down(source); scoped_ptr result2 = bottom_up(result1?*result1:source); if (!result2) { result2 = result1.detach(); } return result2.detach(); } rule_set * mk_coi_filter::bottom_up(rule_set const & source) { func_decl_set all, reached; ptr_vector todo; rule_set::decl2rules body2rules; // initialization for reachability for (rule_set::iterator it = source.begin(); it != source.end(); ++it) { rule * r = *it; all.insert(r->get_decl()); if (r->get_uninterpreted_tail_size() == 0) { if (!reached.contains(r->get_decl())) { reached.insert(r->get_decl()); todo.insert(r->get_decl()); } } else { for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { func_decl * d = r->get_tail(i)->get_decl(); all.insert(d); rule_set::decl2rules::obj_map_entry * e = body2rules.insert_if_not_there2(d, 0); if (!e->get_data().m_value) { e->get_data().m_value = alloc(ptr_vector); } e->get_data().m_value->push_back(r); } } } rel_context_base* rc = m_context.get_rel_context(); if (rc) { func_decl_set::iterator fit = all.begin(), fend = all.end(); for (; fit != fend; ++fit) { if (!rc->is_empty_relation(*fit) && !reached.contains(*fit)) { reached.insert(*fit); todo.insert(*fit); } } } // reachability computation while (!todo.empty()) { func_decl * d = todo.back(); todo.pop_back(); ptr_vector * rules; if (!body2rules.find(d, rules)) continue; for (ptr_vector::iterator it = rules->begin(); it != rules->end(); ++it) { rule * r = *it; if (reached.contains(r->get_decl())) continue; bool contained = true; for (unsigned i = 0; contained && i < r->get_uninterpreted_tail_size(); ++i) { contained = reached.contains(r->get_tail(i)->get_decl()); } if (!contained) continue; reached.insert(r->get_decl()); todo.insert(r->get_decl()); } } // eliminate each rule when some body predicate is not reached scoped_ptr res = alloc(rule_set, m_context); res->inherit_predicates(source); for (rule_set::iterator it = source.begin(); it != source.end(); ++it) { rule * r = *it; bool contained = true; for (unsigned i = 0; contained && i < r->get_uninterpreted_tail_size(); ++i) { contained = reached.contains(r->get_tail(i)->get_decl()); } if (contained) { res->add_rule(r); } } if (res->get_num_rules() == source.get_num_rules()) { TRACE("dl", tout << "No transformation\n";); res = 0; } else { res->close(); } // set to false each unreached predicate if (m_context.get_model_converter()) { extension_model_converter* mc0 = alloc(extension_model_converter, m); for (func_decl_set::iterator it = all.begin(); it != all.end(); ++it) { if (!reached.contains(*it)) { mc0->insert(*it, m.mk_false()); } } m_context.add_model_converter(mc0); } // clean up body2rules range resources for (rule_set::decl2rules::iterator it = body2rules.begin(); it != body2rules.end(); ++it) { dealloc(it->m_value); } CTRACE("dl", 0 != res, res->display(tout);); return res.detach(); } rule_set * mk_coi_filter::top_down(rule_set const & source) { func_decl_set interesting_preds; func_decl_set pruned_preds; ptr_vector todo; { const func_decl_set& output_preds = source.get_output_predicates(); func_decl_set::iterator oend = output_preds.end(); for (func_decl_set::iterator it = output_preds.begin(); it!=oend; ++it) { todo.push_back(*it); interesting_preds.insert(*it); } } const rule_dependencies& deps = source.get_dependencies(); while (!todo.empty()) { func_decl * curr = todo.back(); todo.pop_back(); interesting_preds.insert(curr); const rule_dependencies::item_set& cdeps = deps.get_deps(curr); rule_dependencies::item_set::iterator dend = cdeps.end(); for (rule_dependencies::item_set::iterator it = cdeps.begin(); it != dend; ++it) { func_decl * dep_pred = *it; if (!interesting_preds.contains(dep_pred)) { interesting_preds.insert(dep_pred); todo.push_back(dep_pred); } } } scoped_ptr res = alloc(rule_set, m_context); res->inherit_predicates(source); rule_set::iterator rend = source.end(); for (rule_set::iterator rit = source.begin(); rit != rend; ++rit) { rule * r = *rit; func_decl * pred = r->get_decl(); if (interesting_preds.contains(pred)) { res->add_rule(r); } else if (m_context.get_model_converter()) { pruned_preds.insert(pred); } } if (res->get_num_rules() == source.get_num_rules()) { TRACE("dl", tout << "No transformation\n";); res = 0; } if (res && m_context.get_model_converter()) { func_decl_set::iterator end = pruned_preds.end(); func_decl_set::iterator it = pruned_preds.begin(); extension_model_converter* mc0 = alloc(extension_model_converter, m); for (; it != end; ++it) { const rule_vector& rules = source.get_predicate_rules(*it); expr_ref_vector fmls(m); for (unsigned i = 0; i < rules.size(); ++i) { app* head = rules[i]->get_head(); expr_ref_vector conj(m); for (unsigned j = 0; j < head->get_num_args(); ++j) { expr* arg = head->get_arg(j); if (!is_var(arg)) { conj.push_back(m.mk_eq(m.mk_var(j, m.get_sort(arg)), arg)); } } fmls.push_back(m.mk_and(conj.size(), conj.c_ptr())); } expr_ref fml(m); fml = m.mk_or(fmls.size(), fmls.c_ptr()); mc0->insert(*it, fml); } m_context.add_model_converter(mc0); } CTRACE("dl", 0 != res, res->display(tout);); return res.detach(); } }; z3-4.4.0/src/muz/transforms/dl_mk_unbound_compressor.h0000644000175000017500000000522212540347414022713 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_unbound_compressor.h Abstract: Author: Krystof Hoder (t-khoder) 2010-10-4. Revision History: --*/ #ifndef _DL_MK_UNBOUND_COMPRESSOR_H_ #define _DL_MK_UNBOUND_COMPRESSOR_H_ #include #include"map.h" #include"obj_pair_hashtable.h" #include"dl_context.h" #include"dl_rule_set.h" #include"dl_rule_transformer.h" namespace datalog { /** \brief Functor for introducing auxiliary predicates to avoid unbound variables in rule heads. A rule P(x,_) :- T(x). is replaced by P1(x) :- T(x). and for each occurrence of P in a tail of a rule, a new rule is added with P1 in its place. */ class mk_unbound_compressor : public rule_transformer::plugin { /** predicate and index of compressed argument */ typedef std::pair c_info; typedef pair_hash,unsigned_hash> c_info_hash; /** predicates that are results of compression */ typedef map > c_map; typedef hashtable > in_progress_table; typedef svector todo_stack; context & m_context; ast_manager & m; rule_manager & rm; rule_ref_vector m_rules; bool m_modified; todo_stack m_todo; in_progress_table m_in_progress; c_map m_map; /** Relations that contain facts */ func_decl_set m_non_empty_rels; ast_counter m_head_occurrence_ctr; ast_ref_vector m_pinned; bool is_unbound_argument(rule * r, unsigned head_index); bool has_unbound_head_var(rule * r); void detect_tasks(rule_set const& source, unsigned rule_index); void add_task(func_decl * pred, unsigned arg_index); void try_compress(rule_set const& source, unsigned rule_index); void add_decompression_rules(rule_set const& source, unsigned rule_index); void mk_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index, rule_ref& res); void add_decompression_rule(rule_set const& source, rule * r, unsigned tail_index, unsigned arg_index); void replace_by_decompression_rule(rule_set const& source, unsigned rule_index, unsigned tail_index, unsigned arg_index); void reset(); public: mk_unbound_compressor(context & ctx); rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_UNBOUND_COMPRESSOR_H_ */ z3-4.4.0/src/muz/transforms/dl_transforms.h0000644000175000017500000000061012540347414020470 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_transforms.h Abstract: Default transformations. Author: Nikolaj Bjorner (nbjorner) 2013-08-28. Revision History: Extracted from dl_context --*/ #ifndef _DL_TRANSFORMS_H_ #define _DL_TRANSFORMS_H_ #include "dl_context.h" namespace datalog { void apply_default_transformation(context& ctx); } #endif z3-4.4.0/src/muz/transforms/dl_mk_unbound_compressor.cpp0000644000175000017500000003256712540347414023262 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_unbound_compressor.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-10-04. Revision History: --*/ #include #include #include"dl_mk_unbound_compressor.h" namespace datalog { mk_unbound_compressor::mk_unbound_compressor(context & ctx) : plugin(500), m_context(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_rules(rm), m_pinned(m) { } void mk_unbound_compressor::reset() { m_rules.reset(); m_todo.reset(); m_in_progress.reset(); m_map.reset(); m_pinned.reset(); } bool mk_unbound_compressor::is_unbound_argument(rule * r, unsigned head_index) { app * head = r->get_head(); expr * head_arg = head->get_arg(head_index); if (!is_var(head_arg)) { return false; } unsigned var_idx = to_var(head_arg)->get_idx(); return rm.collect_tail_vars(r).contains(var_idx); } void mk_unbound_compressor::add_task(func_decl * pred, unsigned arg_index) { c_info ci = c_info(pred, arg_index); if (m_map.contains(ci)) { return; //this task was already added } unsigned parent_arity = pred->get_arity(); sort * const * parent_domain = pred->get_domain(); symbol const& parent_name = pred->get_name(); unsigned arity = parent_arity-1; ptr_vector domain; for (unsigned i = 0; i < parent_arity; i++) { if (i != arg_index) { domain.push_back(parent_domain[i]); } } std::stringstream name_suffix; name_suffix << "compr_arg_" << arg_index; func_decl * cpred = m_context.mk_fresh_head_predicate(parent_name, symbol(name_suffix.str().c_str()), arity, domain.c_ptr(), pred); m_pinned.push_back(cpred); m_todo.push_back(ci); m_map.insert(ci, cpred); } void mk_unbound_compressor::detect_tasks(rule_set const& source, unsigned rule_index) { rule * r = m_rules.get(rule_index); var_idx_set& tail_vars = rm.collect_tail_vars(r); app * head = r->get_head(); func_decl * head_pred = head->get_decl(); if (source.is_output_predicate(head_pred)) { //we don't compress output predicates return; } unsigned n = head_pred->get_arity(); rm.get_counter().reset(); rm.get_counter().count_vars(head, 1); for (unsigned i=0; iget_arg(i); if (!is_var(arg)) { continue; } unsigned var_idx = to_var(arg)->get_idx(); if (!tail_vars.contains(var_idx)) { //unbound unsigned occurence_cnt = rm.get_counter().get(var_idx); SASSERT(occurence_cnt>0); if (occurence_cnt == 1) { TRACE("dl", r->display(m_context, tout << "Compress: ");); add_task(head_pred, i); return; //we compress out the unbound arguments one by one } } } } void mk_unbound_compressor::try_compress(rule_set const& source, unsigned rule_index) { start: rule * r = m_rules.get(rule_index); var_idx_set& tail_vars = rm.collect_tail_vars(r); app * head = r->get_head(); func_decl * head_pred = head->get_decl(); unsigned head_arity = head_pred->get_arity(); rm.get_counter().reset(); rm.get_counter().count_vars(head); unsigned arg_index; for (arg_index = 0; arg_index < head_arity; arg_index++) { expr * arg = head->get_arg(arg_index); if (!is_var(arg)) { continue; } unsigned var_idx = to_var(arg)->get_idx(); if (!tail_vars.contains(var_idx)) { //unbound unsigned occurence_cnt = rm.get_counter().get(var_idx); SASSERT(occurence_cnt>0); if ( occurence_cnt==1 && m_in_progress.contains(c_info(head_pred, arg_index)) ) { //we have found what to compress break; } } } if (arg_index == head_arity) { //we didn't find anything to compress return; } SASSERT(arg_index cargs; for (unsigned i=0; iget_arg(i)); } } app_ref chead(m.mk_app(cpred, head_arity-1, cargs.c_ptr()), m); if (r->get_tail_size()==0 && m_context.get_rule_manager().is_fact(chead)) { m_non_empty_rels.insert(cpred); m_context.add_fact(chead); //remove the rule that became fact by placing the last rule on its place m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_decl()); m_rules.set(rule_index, m_rules.get(m_rules.size()-1)); m_rules.shrink(m_rules.size()-1); //since we moved the last rule to rule_index, we have to try to compress it as well if (rule_indexset_accounting_parent_object(m_context, r); m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_decl()); m_rules.set(rule_index, new_rule); m_head_occurrence_ctr.inc(m_rules.get(rule_index)->get_decl()); detect_tasks(source, rule_index); } m_modified = true; } void mk_unbound_compressor::mk_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index, rule_ref& res) { app * orig_dtail = r->get_tail(tail_index); //dtail ~ decompressed tail c_info ci(orig_dtail->get_decl(), arg_index); func_decl * dtail_pred; TRUSTME( m_map.find(ci, dtail_pred) ); ptr_vector dtail_args; unsigned orig_dtail_arity = orig_dtail->get_num_args(); for (unsigned i=0;iget_arg(i)); } } SASSERT(dtail_args.size()==dtail_pred->get_arity()); app_ref dtail(m.mk_app(dtail_pred, dtail_args.size(), dtail_args.c_ptr()), m); svector tails_negated; app_ref_vector tails(m); unsigned tail_len = r->get_tail_size(); for (unsigned i=0; iis_neg_tail(i)); if (i==tail_index && !r->is_neg_tail(i)) { tails.push_back(dtail); } else { tails.push_back(r->get_tail(i)); } } // Accumulate negated filtered rule instead // of replacing the original predicate. if (r->is_neg_tail(tail_index)) { tails_negated.push_back(true); tails.push_back(dtail); } res = m_context.get_rule_manager().mk( r->get_head(), tails.size(), tails.c_ptr(), tails_negated.c_ptr()); res->set_accounting_parent_object(m_context, r); m_context.get_rule_manager().fix_unbound_vars(res, true); } void mk_unbound_compressor::add_decompression_rule(rule_set const& source, rule * r, unsigned tail_index, unsigned arg_index) { rule_ref new_rule(m_context.get_rule_manager()); mk_decompression_rule(r, tail_index, arg_index, new_rule); unsigned new_rule_index = m_rules.size(); m_rules.push_back(new_rule); m_context.get_rule_manager().mk_rule_rewrite_proof(*r, *new_rule.get()); m_head_occurrence_ctr.inc(new_rule->get_decl()); detect_tasks(source, new_rule_index); m_modified = true; //TODO: avoid rule duplicity //If two predicates are compressed in a rule, applying decompression //to the results can cause a rule being added multiple times: //P:- R(x,y), S(x,y) //is decompressed into rules //P:- R1(x), S(x,y) //P:- R(x,y), S1(x) //and each of these rules is again decompressed giving the same rule //P:- R1(x), S1(x) //P:- R1(x), S1(x) } void mk_unbound_compressor::replace_by_decompression_rule(rule_set const& source, unsigned rule_index, unsigned tail_index, unsigned arg_index) { rule * r = m_rules.get(rule_index); rule_ref new_rule(m_context.get_rule_manager()); mk_decompression_rule(r, tail_index, arg_index, new_rule); m_rules.set(rule_index, new_rule); //we don't update the m_head_occurrence_ctr because the head predicate doesn't change detect_tasks(source, rule_index); m_modified = true; } void mk_unbound_compressor::add_decompression_rules(rule_set const& source, unsigned rule_index) { unsigned_vector compressed_tail_pred_arg_indexes; //this value is updated inside the loop if replace_by_decompression_rule is called rule_ref r(m_rules.get(rule_index), m_context.get_rule_manager()); unsigned utail_len = r->get_uninterpreted_tail_size(); unsigned tail_index=0; while (tail_indexget_tail(tail_index); func_decl * t_pred = t->get_decl(); unsigned t_arity = t_pred->get_arity(); bool is_negated_predicate = r->is_neg_tail(tail_index); compressed_tail_pred_arg_indexes.reset(); for (unsigned arg_index=0; arg_indexget_uninterpreted_tail_size() >= utail_len); //here we check that the rule replacement didn't affect other uninterpreted literals //in the tail (aside of variable renaming) SASSERT(tail_index==0 || new_rule->get_tail(tail_index-1)->get_decl()==r->get_tail(tail_index-1)->get_decl()); r = new_rule; //we have replaced the original rule, with one that has different //content of the tail_index -th tail. we will therefore not do //tail_index++, so that we examine the new tail literal as well } else { tail_index++; } } } rule_set * mk_unbound_compressor::operator()(rule_set const & source) { // TODO mc m_modified = false; rel_context_base* rel = m_context.get_rel_context(); if (rel) { rel->collect_non_empty_predicates(m_non_empty_rels); } unsigned init_rule_cnt = source.get_num_rules(); SASSERT(m_rules.empty()); for (unsigned i=0; iget_decl()); } for (unsigned i=0; i(0); if (m_modified) { result = alloc(rule_set, m_context); unsigned fin_rule_cnt = m_rules.size(); for (unsigned i=0; iadd_rule(m_rules.get(i)); } result->inherit_predicates(source); } reset(); return result; } }; z3-4.4.0/src/muz/transforms/dl_mk_subsumption_checker.h0000644000175000017500000000505612540347414023046 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mk_subsumption_checker.h Abstract: Rule transformer which checks for subsumption (currently just for subsumption with total relations) Author: Krystof Hoder (t-khoder) 2011-10-01. Revision History: --*/ #ifndef _DL_MK_SUBSUMPTION_CHECKER_H_ #define _DL_MK_SUBSUMPTION_CHECKER_H_ #include "dl_context.h" #include "dl_rule_transformer.h" #include "dl_rule_subsumption_index.h" namespace datalog { class mk_subsumption_checker : public rule_transformer::plugin { ast_manager & m; context & m_context; rule_ref_vector m_ref_holder; func_decl_set m_total_relations; /** Map that for each relation contains the rule which implies its totality. If the totality is due to the relation containing all facts, the rule stored here is zero*/ obj_map m_total_relation_defining_rules; /** Contains heads of rules of shape R(c1,c2,...cN). grouped by their predicate. This information helps to improve the results of the scan_for_relations_total_due_to_facts() function. */ obj_map *> m_ground_unconditional_rule_heads; bool m_have_new_total_rule; bool m_new_total_relation_discovery_during_transformation; bool is_total_rule(const rule * r); /** Function to be called when a new total relation is discovered */ void on_discovered_total_relation(func_decl * pred, rule * r); void scan_for_total_rules(rule_set const& rules); void scan_for_relations_total_due_to_facts(rule_set const& rules); void collect_ground_unconditional_rule_heads(const rule_set & rules); /** Return false if rule is unsatisfiable */ bool transform_rule(rule * r, rule_subsumption_index& subs_index, rule_ref & res); /** Return false if the rule set hasn't changed */ bool transform_rules(const rule_set & orig, rule_set & tgt); public: mk_subsumption_checker(context & ctx, unsigned priority=31000) : plugin(priority), m(ctx.get_manager()), m_context(ctx), m_ref_holder(ctx.get_rule_manager()), m_new_total_relation_discovery_during_transformation(true) {} ~mk_subsumption_checker() { reset_dealloc_values(m_ground_unconditional_rule_heads); } virtual rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_SUBSUMPTION_CHECKER_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_separate_negated_tails.cpp0000644000175000017500000001014412540347414024006 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: mk_separate_negated_tails.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2013-09-09 Revision History: --*/ #include "dl_mk_separate_negated_tails.h" #include "dl_context.h" namespace datalog { mk_separate_negated_tails::mk_separate_negated_tails(context& ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_ctx(ctx) {} bool mk_separate_negated_tails::has_private_vars(rule const& r, unsigned j) { get_private_vars(r, j); return !m_vars.empty(); } void mk_separate_negated_tails::get_private_vars(rule const& r, unsigned j) { m_vars.reset(); m_fv.reset(); m_fv(r.get_head()); for (unsigned i = 0; i < r.get_tail_size(); ++i) { if (i != j) { m_fv.accumulate(r.get_tail(i)); } } app* p = r.get_tail(j); for (unsigned i = 0; i < p->get_num_args(); ++i) { expr* v = p->get_arg(i); if (is_var(v)) { unsigned idx = to_var(v)->get_idx(); if (!m_fv.contains(idx)) { m_vars.push_back(v); } } } } void mk_separate_negated_tails::abstract_predicate(app* p, app_ref& q, rule_set& rules) { expr_ref_vector args(m); sort_ref_vector sorts(m); func_decl_ref fn(m); for (unsigned i = 0; i < p->get_num_args(); ++i) { expr* arg = p->get_arg(i); if (!m_vars.contains(arg)) { args.push_back(arg); sorts.push_back(m.get_sort(arg)); } } fn = m.mk_fresh_func_decl(p->get_decl()->get_name(), symbol("N"), sorts.size(), sorts.c_ptr(), m.mk_bool_sort()); m_ctx.register_predicate(fn, false); q = m.mk_app(fn, args.size(), args.c_ptr()); bool is_neg = true; rules.add_rule(rm.mk(q, 1, & p, &is_neg)); } void mk_separate_negated_tails::create_rule(rule const&r, rule_set& rules) { unsigned utsz = r.get_uninterpreted_tail_size(); unsigned ptsz = r.get_positive_tail_size(); unsigned tsz = r.get_tail_size(); app_ref_vector tail(m); app_ref p(m); svector neg; for (unsigned i = 0; i < ptsz; ++i) { tail.push_back(r.get_tail(i)); neg.push_back(false); } for (unsigned i = ptsz; i < utsz; ++i) { get_private_vars(r, i); if (!m_vars.empty()) { abstract_predicate(r.get_tail(i), p, rules); tail.push_back(p); neg.push_back(false); } else { neg.push_back(true); tail.push_back(r.get_tail(i)); } } for (unsigned i = utsz; i < tsz; ++i) { tail.push_back(r.get_tail(i)); neg.push_back(false); } rules.add_rule(rm.mk(r.get_head(), tail.size(), tail.c_ptr(), neg.c_ptr(), r.name())); } rule_set * mk_separate_negated_tails::operator()(rule_set const& src) { scoped_ptr result = alloc(rule_set, m_ctx); bool has_new_rule = false; unsigned sz = src.get_num_rules(); for (unsigned i = 0; i < sz; ++i) { bool change = false; rule & r = *src.get_rule(i); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned ptsz = r.get_positive_tail_size(); for (unsigned j = ptsz; j < utsz; ++j) { SASSERT(r.is_neg_tail(j)); if (has_private_vars(r, j)) { create_rule(r, *result); has_new_rule = true; change = true; break; } } if (!change) { result->add_rule(&r); } } if (!has_new_rule) { return 0; } else { result->inherit_predicates(src); return result.detach(); } } } z3-4.4.0/src/muz/transforms/dl_mk_filter_rules.h0000644000175000017500000000461212540347414021466 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_filter_rules.h Abstract: Author: Leonardo de Moura (leonardo) 2010-05-18. Revision History: --*/ #ifndef _DL_MK_FILTER_RULES_H_ #define _DL_MK_FILTER_RULES_H_ #include"map.h" #include"dl_rule_set.h" #include"dl_rule_transformer.h" namespace datalog { /** \brief Functor for applying a rule set transformation that creates "filters". A "filter" is a rule of the form: Head(X_1, ..., X_n) :- Tail(...) where X_1,...,X_n are distinct, and Tail contain repeated variables and/or values. After applying this functor only "filter" rules will contain atoms with repeated variables and/or values. */ class mk_filter_rules : public rule_transformer::plugin { struct filter_key { app_ref new_pred; expr_ref_buffer filter_args; filter_key(ast_manager & m) : new_pred(m), filter_args(m) {} unsigned hash() const { unsigned r = new_pred->hash(); for (unsigned i = 0; i < filter_args.size(); ++i) { r ^= filter_args[i]->hash(); } return r; } bool operator==(const filter_key & o) const { return o.new_pred==new_pred && vectors_equal(o.filter_args, filter_args); } }; typedef obj_map filter_cache; context & m_context; ast_manager & m; rule_manager & rm; filter_cache m_tail2filter; rule_set * m_result; rule * m_current; bool m_modified; ast_ref_vector m_pinned; bool is_candidate(app * pred); func_decl * mk_filter_decl(app * pred, var_idx_set const & non_local_vars); void process(rule * r); public: mk_filter_rules(context & ctx); ~mk_filter_rules(); /** \brief Return a new rule set where only filter rules contain atoms with repeated variables and/or values. */ rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_FILTER_RULES_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_bit_blast.h0000644000175000017500000000117312540347414020731 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_bit_blast.h Abstract: Functor for bit-blasting a rule set Author: Nikolaj Bjorner (nbjorner) 2012-08-30 Revision History: --*/ #ifndef _DL_MK_BIT_BLAST_H_ #define _DL_MK_BIT_BLAST_H_ #include"dl_rule_transformer.h" namespace datalog { class mk_bit_blast : public rule_transformer::plugin { class impl; impl* m_impl; public: mk_bit_blast(context & ctx, unsigned priority = 35000); ~mk_bit_blast(); rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_BIT_BLAST_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_loop_counter.cpp0000644000175000017500000001302512540347414022030 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_loop_counter.cpp Abstract: Add loop counter argument to relations. Author: Nikolaj Bjorner (nbjorner) 2013-03-31 Revision History: --*/ #include"dl_mk_loop_counter.h" #include"dl_context.h" namespace datalog { mk_loop_counter::mk_loop_counter(context & ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), m_ctx(ctx), a(m), m_refs(m) { } mk_loop_counter::~mk_loop_counter() { } app_ref mk_loop_counter::add_arg(rule_set const& src, rule_set& dst, app* fn, unsigned idx) { expr_ref_vector args(m); func_decl* new_fn, *old_fn = fn->get_decl(); args.append(fn->get_num_args(), fn->get_args()); args.push_back(m.mk_var(idx, a.mk_int())); if (!m_old2new.find(old_fn, new_fn)) { ptr_vector domain; domain.append(fn->get_num_args(), old_fn->get_domain()); domain.push_back(a.mk_int()); new_fn = m.mk_func_decl(old_fn->get_name(), domain.size(), domain.c_ptr(), old_fn->get_range()); m_old2new.insert(old_fn, new_fn); m_new2old.insert(new_fn, old_fn); m_refs.push_back(new_fn); m_ctx.register_predicate(new_fn, false); if (src.is_output_predicate(old_fn)) { dst.set_output_predicate(new_fn); } } return app_ref(m.mk_app(new_fn, args.size(), args.c_ptr()), m); } app_ref mk_loop_counter::del_arg(app* fn) { expr_ref_vector args(m); func_decl* old_fn, *new_fn = fn->get_decl(); SASSERT(fn->get_num_args() > 0); args.append(fn->get_num_args()-1, fn->get_args()); VERIFY (m_new2old.find(new_fn, old_fn)); return app_ref(m.mk_app(old_fn, args.size(), args.c_ptr()), m); } rule_set * mk_loop_counter::operator()(rule_set const & source) { m_refs.reset(); m_old2new.reset(); m_new2old.reset(); rule_manager& rm = source.get_rule_manager(); rule_set * result = alloc(rule_set, m_ctx); unsigned sz = source.get_num_rules(); rule_ref new_rule(rm); app_ref_vector tail(m); app_ref head(m); svector neg; rule_counter& vc = rm.get_counter(); for (unsigned i = 0; i < sz; ++i) { tail.reset(); neg.reset(); rule & r = *source.get_rule(i); unsigned cnt = vc.get_max_rule_var(r)+1; unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); for (unsigned j = 0; j < utsz; ++j, ++cnt) { tail.push_back(add_arg(source, *result, r.get_tail(j), cnt)); neg.push_back(r.is_neg_tail(j)); } for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(r.get_tail(j)); neg.push_back(false); } head = add_arg(source, *result, r.get_head(), cnt); // set the loop counter to be an increment of the previous bool found = false; unsigned last = head->get_num_args()-1; for (unsigned j = 0; !found && j < utsz; ++j) { if (head->get_decl() == tail[j]->get_decl()) { tail.push_back(m.mk_eq(head->get_arg(last), a.mk_add(tail[j]->get_arg(last), a.mk_numeral(rational(1), true)))); neg.push_back(false); found = true; } } // initialize loop counter to 0 if none was found. if (!found) { expr_ref_vector args(m); args.append(head->get_num_args(), head->get_args()); args[last] = a.mk_numeral(rational(0), true); head = m.mk_app(head->get_decl(), args.size(), args.c_ptr()); } new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); result->add_rule(new_rule); } // model converter: remove references to extra argument. // proof converter: remove references to extra argument as well. return result; } rule_set * mk_loop_counter::revert(rule_set const & source) { context& ctx = source.get_context(); rule_manager& rm = source.get_rule_manager(); rule_set * result = alloc(rule_set, ctx); unsigned sz = source.get_num_rules(); rule_ref new_rule(rm); app_ref_vector tail(m); app_ref head(m); svector neg; for (unsigned i = 0; i < sz; ++i) { tail.reset(); neg.reset(); rule & r = *source.get_rule(i); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); for (unsigned j = 0; j < utsz; ++j) { tail.push_back(del_arg(r.get_tail(j))); neg.push_back(r.is_neg_tail(j)); } for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(r.get_tail(j)); neg.push_back(false); } head = del_arg(r.get_head()); new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); result->add_rule(new_rule); } // model converter: ... // proof converter: ... return result; } }; z3-4.4.0/src/muz/transforms/dl_mk_rule_inliner.cpp0000644000175000017500000007657612540347414022033 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_rule_inliner.cpp Abstract: Rule transformer which simplifies interpreted tails Author: Krystof Hoder (t-khoder) 2011-10-01. Revision History: Added linear_inline 2012-9-10 (nbjorner) Disable inliner for quantified rules 2012-10-31 (nbjorner) Notes: Resolution transformation (resolve): P(x) :- Q(y), phi(x,y) Q(y) :- R(z), psi(y,z) -------------------------------------------------- P(x) :- R(z), phi(x,y), psi(y,z) Proof converter: replace assumption (*) by rule and upper assumptions. Subsumption transformation (remove rule): P(x) :- Q(y), phi(x,y) Rules --------------------------------- Rules Model converter: P(x) := P(x) or (exists y . Q(y) & phi(x,y)) --*/ #include #include "ast_pp.h" #include "rewriter.h" #include "rewriter_def.h" #include "dl_mk_rule_inliner.h" #include "fixedpoint_params.hpp" namespace datalog { // ----------------------------------- // // mk_rule_inliner::rule_unifier // // ----------------------------------- bool rule_unifier::unify_rules(const rule& tgt, unsigned tgt_idx, const rule& src) { rule_counter& vc = m_rm.get_counter(); unsigned var_cnt = std::max(vc.get_max_rule_var(tgt), vc.get_max_rule_var(src))+1; m_subst.reset(); m_subst.reserve(2, var_cnt); m_ready = m_unif(tgt.get_tail(tgt_idx), src.get_head(), m_subst); if (m_ready) { m_deltas[0] = 0; m_deltas[1] = var_cnt; TRACE("dl", output_predicate(m_context, src.get_head(), tout << "unify rules "); output_predicate(m_context, tgt.get_head(), tout << "\n"); tout << "\n";); } return m_ready; } void rule_unifier::apply(app * a, bool is_tgt, app_ref& res) { expr_ref res_e(m); TRACE("dl", output_predicate(m_context, a, tout); tout << "\n";); m_subst.apply(2, m_deltas, expr_offset(a, is_tgt ? 0 : 1), res_e); SASSERT(is_app(res_e.get())); res = to_app(res_e.get()); } void rule_unifier::apply( rule const& r, bool is_tgt, unsigned skipped_index, app_ref_vector& res, svector& res_neg) { unsigned rule_len = r.get_tail_size(); for (unsigned i = 0; i < rule_len; i++) { if (i != skipped_index) { //i can never be UINT_MAX, so we'll never skip if we're not supposed to app_ref new_tail_el(m); apply(r.get_tail(i), is_tgt, new_tail_el); res.push_back(new_tail_el); res_neg.push_back(r.is_neg_tail(i)); } } } bool rule_unifier::apply(rule const& tgt, unsigned tail_index, rule const& src, rule_ref& res) { SASSERT(m_ready); app_ref new_head(m); app_ref_vector tail(m); svector tail_neg; rule_ref simpl_rule(m_rm); apply(tgt.get_head(), true, new_head); apply(tgt, true, tail_index, tail, tail_neg); apply(src, false, UINT_MAX, tail, tail_neg); mk_rule_inliner::remove_duplicate_tails(tail, tail_neg); SASSERT(tail.size()==tail_neg.size()); res = m_rm.mk(new_head, tail.size(), tail.c_ptr(), tail_neg.c_ptr(), tgt.name(), m_normalize); res->set_accounting_parent_object(m_context, const_cast(&tgt)); TRACE("dl", tgt.display(m_context, tout << "tgt (" << tail_index << "): \n"); src.display(m_context, tout << "src:\n"); res->display(m_context, tout << "res\n"); // m_unif.display(tout << "subst:\n"); ); if (m_normalize) { m_rm.fix_unbound_vars(res, true); if (m_interp_simplifier.transform_rule(res.get(), simpl_rule)) { res = simpl_rule; return true; } else { return false; } } else { return true; } } expr_ref_vector rule_unifier::get_rule_subst(const rule& r, bool is_tgt) { SASSERT(m_ready); expr_ref_vector result(m); ptr_vector sorts; expr_ref v(m), w(m); r.get_vars(m, sorts); for (unsigned i = 0; i < sorts.size(); ++i) { v = m.mk_var(i, sorts[i]); m_subst.apply(2, m_deltas, expr_offset(v, is_tgt?0:1), w); result.push_back(w); } return result; } // ----------------------------------- // // mk_rule_inliner // // ----------------------------------- /** Inline occurrences of rule src at tail_index in tgt and return the result in res. */ bool mk_rule_inliner::try_to_inline_rule(rule& tgt, rule& src, unsigned tail_index, rule_ref& res) { SASSERT(tail_indexdisplay(m_context, tout << "interpreted tail is unsat\n");); //the interpreted part is unsatisfiable return false; } } // TBD: replace by r.has_quantifiers() and test bool mk_rule_inliner::has_quantifier(rule const& r) const { unsigned utsz = r.get_uninterpreted_tail_size(); for (unsigned i = utsz; i < r.get_tail_size(); ++i) { if (r.get_tail(i)->has_quantifiers()) return true; } return false; } void mk_rule_inliner::count_pred_occurrences(rule_set const & orig) { rel_context_base* rel = m_context.get_rel_context(); if (rel) { rel->collect_non_empty_predicates(m_preds_with_facts); } rule_set::iterator rend = orig.end(); for (rule_set::iterator rit = orig.begin(); rit!=rend; ++rit) { rule * r = *rit; func_decl * head_pred = r->get_decl(); m_head_pred_ctr.inc(head_pred); if (r->get_tail_size()>0) { m_head_pred_non_empty_tails_ctr.inc(head_pred); } unsigned ut_len = r->get_uninterpreted_tail_size(); for (unsigned i=0; iget_decl(i); m_tail_pred_ctr.inc(pred); if (r->is_neg_tail(i)) { m_preds_with_neg_occurrence.insert(pred); } } } } bool mk_rule_inliner::inlining_allowed(rule_set const& source, func_decl * pred) { if (//these three conditions are important for soundness source.is_output_predicate(pred) || m_preds_with_facts.contains(pred) || m_preds_with_neg_occurrence.contains(pred) || //this condition is used for breaking of cycles among inlined rules m_forbidden_preds.contains(pred)) { return false; } // // these conditions are optional, they avoid possible exponential increase // in the size of the problem // return //m_head_pred_non_empty_tails_ctr.get(pred)<=1 m_head_pred_ctr.get(pred) <= 1 || (m_tail_pred_ctr.get(pred) <= 1 && m_head_pred_ctr.get(pred) <= 4) ; } /** Caller has to dealloc the returned object */ rule_set * mk_rule_inliner::create_allowed_rule_set(rule_set const & orig) { rule_set * res = alloc(rule_set, m_context); unsigned rcnt = orig.get_num_rules(); for (unsigned i=0; iget_decl())) { res->add_rule(r); } } //the rule set should be stratified, since orig (which is its superset) is as well VERIFY(res->close()); return res; } /** Try to make the set of inlined predicates acyclic by forbidding inlining of one predicate from each strongly connected component. Return true if we did forbide some predicate, and false if the set of rules is already acyclic. */ bool mk_rule_inliner::forbid_preds_from_cycles(rule_set const & r) { SASSERT(r.is_closed()); bool something_forbidden = false; const rule_stratifier::comp_vector& comps = r.get_stratifier().get_strats(); rule_stratifier::comp_vector::const_iterator cend = comps.end(); for (rule_stratifier::comp_vector::const_iterator it = comps.begin(); it!=cend; ++it) { rule_stratifier::item_set * stratum = *it; if (stratum->size()==1) { continue; } SASSERT(stratum->size()>1); func_decl * first_stratum_pred = *stratum->begin(); //we're trying to break cycles by removing one predicate from each of them m_forbidden_preds.insert(first_stratum_pred); something_forbidden = true; } return something_forbidden; } bool mk_rule_inliner::forbid_multiple_multipliers(const rule_set & orig, rule_set const & proposed_inlined_rules) { bool something_forbidden = false; const rule_stratifier::comp_vector& comps = proposed_inlined_rules.get_stratifier().get_strats(); rule_stratifier::comp_vector::const_iterator cend = comps.end(); for (rule_stratifier::comp_vector::const_iterator it = comps.begin(); it!=cend; ++it) { rule_stratifier::item_set * stratum = *it; SASSERT(stratum->size()==1); func_decl * head_pred = *stratum->begin(); bool is_multi_head_pred = m_head_pred_ctr.get(head_pred)>1; bool is_multi_occurrence_pred = m_tail_pred_ctr.get(head_pred)>1; const rule_vector& pred_rules = proposed_inlined_rules.get_predicate_rules(head_pred); rule_vector::const_iterator iend = pred_rules.end(); for (rule_vector::const_iterator iit = pred_rules.begin(); iit!=iend; ++iit) { rule * r = *iit; unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti = 0; tiget_decl(ti); if (!inlining_allowed(orig, tail_pred)) { continue; } unsigned tail_pred_head_cnt = m_head_pred_ctr.get(tail_pred); if (tail_pred_head_cnt<=1) { continue; } if (is_multi_head_pred) { m_forbidden_preds.insert(head_pred); something_forbidden = true; goto process_next_pred; } if (is_multi_occurrence_pred) { m_forbidden_preds.insert(tail_pred); something_forbidden = true; } else { is_multi_head_pred = true; m_head_pred_ctr.get(head_pred) = m_head_pred_ctr.get(head_pred)*tail_pred_head_cnt; } } } process_next_pred:; } unsigned rule_cnt = orig.get_num_rules(); for (unsigned ri=0; riget_decl(); if (inlining_allowed(orig, head_pred)) { //we have already processed inlined rules continue; } bool has_multi_head_pred = false; unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti = 0; tiget_decl(ti); if (!inlining_allowed(orig, pred)) { continue; } if (m_head_pred_ctr.get(pred)<=1) { continue; } if (has_multi_head_pred) { m_forbidden_preds.insert(pred); something_forbidden = true; } else { has_multi_head_pred = true; } } } return something_forbidden; } void mk_rule_inliner::plan_inlining(rule_set const & orig) { count_pred_occurrences(orig); scoped_ptr candidate_inlined_set = create_allowed_rule_set(orig); while (forbid_preds_from_cycles(*candidate_inlined_set)) { candidate_inlined_set = create_allowed_rule_set(orig); } if (forbid_multiple_multipliers(orig, *candidate_inlined_set)) { candidate_inlined_set = create_allowed_rule_set(orig); } TRACE("dl", tout<<"rules to be inlined:\n" << (*candidate_inlined_set); ); // now we start filling in the set of the inlined rules in a topological order, // so that we inline rules into other rules SASSERT(m_inlined_rules.get_num_rules()==0); const rule_stratifier::comp_vector& comps = candidate_inlined_set->get_stratifier().get_strats(); rule_stratifier::comp_vector::const_iterator cend = comps.end(); for (rule_stratifier::comp_vector::const_iterator it = comps.begin(); it!=cend; ++it) { rule_stratifier::item_set * stratum = *it; SASSERT(stratum->size()==1); func_decl * pred = *stratum->begin(); const rule_vector& pred_rules = candidate_inlined_set->get_predicate_rules(pred); rule_vector::const_iterator iend = pred_rules.end(); for (rule_vector::const_iterator iit = pred_rules.begin(); iit!=iend; ++iit) { transform_rule(orig, *iit, m_inlined_rules); } } TRACE("dl", tout << "inlined rules after mutual inlining:\n" << m_inlined_rules; ); for (unsigned i = 0; i < m_inlined_rules.get_num_rules(); ++i) { rule* r = m_inlined_rules.get_rule(i); datalog::del_rule(m_mc, *r); } } bool mk_rule_inliner::transform_rule(rule_set const& orig, rule * r0, rule_set& tgt) { bool modified = false; rule_ref_vector todo(m_rm); todo.push_back(r0); while (!todo.empty()) { rule_ref r(todo.back(), m_rm); todo.pop_back(); unsigned pt_len = r->get_positive_tail_size(); unsigned i = 0; for (; i < pt_len && !inlining_allowed(orig, r->get_decl(i)); ++i) {}; SASSERT(!has_quantifier(*r.get())); if (i == pt_len) { //there's nothing we can inline in this rule tgt.add_rule(r); continue; } modified = true; func_decl * pred = r->get_decl(i); const rule_vector& pred_rules = m_inlined_rules.get_predicate_rules(pred); rule_vector::const_iterator iend = pred_rules.end(); for (rule_vector::const_iterator iit = pred_rules.begin(); iit!=iend; ++iit) { rule * inl_rule = *iit; rule_ref inl_result(m_rm); if (try_to_inline_rule(*r.get(), *inl_rule, i, inl_result)) { todo.push_back(inl_result); } } } if (modified) { datalog::del_rule(m_mc, *r0); } return modified; } bool mk_rule_inliner::transform_rules(const rule_set & orig, rule_set & tgt) { bool something_done = false; rule_set::iterator rend = orig.end(); for (rule_set::iterator rit = orig.begin(); rit!=rend; ++rit) { rule_ref r(*rit, m_rm); func_decl * pred = r->get_decl(); // if inlining is allowed, then we are eliminating // this relation through inlining, // so we don't add its rules to the result something_done |= !inlining_allowed(orig, pred) && transform_rule(orig, r, tgt); } if (something_done && m_mc) { for (rule_set::iterator rit = orig.begin(); rit!=rend; ++rit) { if (inlining_allowed(orig, (*rit)->get_decl())) { datalog::del_rule(m_mc, **rit); } } } return something_done; } /** Check whether rule r is oriented in a particular ordering. This is to avoid infinite cycle of inlining in the eager inliner. Out ordering is lexicographic, comparing atoms first on stratum they are in, then on arity and then on ast ID of their func_decl. */ bool mk_rule_inliner::is_oriented_rewriter(rule * r, rule_stratifier const& strat) { func_decl * head_pred = r->get_decl(); unsigned head_strat = strat.get_predicate_strat(head_pred); unsigned head_arity = head_pred->get_arity(); unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti=0; tiget_decl(ti); unsigned pred_strat = strat.get_predicate_strat(pred); SASSERT(pred_strat<=head_strat); if (pred_strat==head_strat) { if (pred->get_arity()>head_arity || (pred->get_arity()==head_arity && pred->get_id()>=head_pred->get_id()) ) { return false; } } } return true; } bool mk_rule_inliner::do_eager_inlining(rule * r, rule_set const& rules, rule_ref& res) { if (r->has_negation()) { return false; } SASSERT(rules.is_closed()); const rule_stratifier& strat = rules.get_stratifier(); func_decl * head_pred = r->get_decl(); unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti = 0; ti < pt_len; ++ti) { func_decl * pred = r->get_decl(ti); if (pred == head_pred || m_preds_with_facts.contains(pred)) { continue; } const rule_vector& pred_rules = rules.get_predicate_rules(pred); rule * inlining_candidate = 0; unsigned rule_cnt = pred_rules.size(); if (rule_cnt == 0) { inlining_candidate = 0; } else if (rule_cnt == 1) { inlining_candidate = pred_rules[0]; } else { inlining_candidate = 0; for (unsigned ri = 0; ri < rule_cnt; ++ri) { rule * pred_rule = pred_rules[ri]; if (!m_unifier.unify_rules(*r, ti, *pred_rule)) { //we skip rules which don't unify with the tail atom continue; } if (inlining_candidate != 0) { // We have two rules that can be inlined into the current // tail predicate. In this situation we don't do inlinning // on this tail atom, as we don't want the overall number // of rules to increase. goto process_next_tail; } inlining_candidate = pred_rule; } } if (inlining_candidate == 0) { // nothing unifies with the tail atom, therefore the rule is unsatisfiable // (we can say this because relation pred doesn't have any ground facts either) res = 0; datalog::del_rule(m_mc, *r); return true; } if (!is_oriented_rewriter(inlining_candidate, strat)) { // The rule which should be used for inlining isn't oriented // in a simplifying direction. Inlining with such rule might lead to // infinite loops, so we don't do it. goto process_next_tail; } if (!try_to_inline_rule(*r, *inlining_candidate, ti, res)) { datalog::del_rule(m_mc, *r); res = 0; } return true; process_next_tail:; } return false; } bool mk_rule_inliner::do_eager_inlining(scoped_ptr & rules) { scoped_ptr res = alloc(rule_set, m_context); bool done_something = false; rule_set::iterator rend = rules->end(); for (rule_set::iterator rit = rules->begin(); rit!=rend; ++rit) { rule_ref r(*rit, m_rm); rule_ref replacement(m_rm); while (r && do_eager_inlining(r, *rules, replacement)) { r = replacement; done_something = true; } if (!r) { continue; } res->add_rule(r); } if (done_something) { rules = res.detach(); } return done_something; } /** Inline predicates that are known to not be join-points. P(1,x) :- P(0,y), phi(x,y) P(0,x) :- P(1,z), psi(x,z) -> P(1,x) :- P(1,z), phi(x,y), psi(y,z) whenever P(0,x) is not unifiable with the body of the rule where it appears (P(1,z)) and P(0,x) is unifiable with at most one (?) other rule (and it does not occur negatively). */ bool mk_rule_inliner::visitor::operator()(expr* e) { m_unifiers.append(m_positions.find(e)); TRACE("dl", tout << "unifier: " << (m_unifiers.empty()?0:m_unifiers.back()); tout << " num unifiers: " << m_unifiers.size(); tout << " num positions: " << m_positions.find(e).size() << "\n"; output_predicate(m_context, to_app(e), tout); tout << "\n";); // stop visitor when we have more than 1 unifier, since that's all we want. return m_unifiers.size() <= 1; } void mk_rule_inliner::visitor::reset(unsigned sz) { m_unifiers.reset(); m_can_remove.reset(); m_can_remove.resize(sz, true); m_can_expand.reset(); m_can_expand.resize(sz, true); m_positions.reset(); } unsigned_vector const& mk_rule_inliner::visitor::add_position(expr* e, unsigned j) { obj_map::obj_map_entry * et = m_positions.insert_if_not_there2(e, unsigned_vector()); et->get_data().m_value.push_back(j); return et->get_data().m_value; } unsigned_vector const& mk_rule_inliner::visitor::del_position(expr* e, unsigned j) { obj_map::obj_map_entry * et = m_positions.find_core(e); SASSERT(et && et->get_data().m_value.contains(j)); et->get_data().m_value.erase(j); return et->get_data().m_value; } void mk_rule_inliner::add_rule(rule_set const& source, rule* r, unsigned i) { svector& can_remove = m_head_visitor.can_remove(); svector& can_expand = m_head_visitor.can_expand(); app* head = r->get_head(); func_decl* headd = head->get_decl(); m_head_visitor.add_position(head, i); m_head_index.insert(head); m_pinned.push_back(r); if (source.is_output_predicate(headd) || m_preds_with_facts.contains(headd)) { can_remove.set(i, false); TRACE("dl", output_predicate(m_context, head, tout << "cannot remove: " << i << " "); tout << "\n";); } unsigned tl_sz = r->get_uninterpreted_tail_size(); for (unsigned j = 0; j < tl_sz; ++j) { app* tail = r->get_tail(j); m_tail_visitor.add_position(tail, i); m_tail_index.insert(tail); } bool can_exp = tl_sz == 1 && r->get_positive_tail_size() == 1 && !m_preds_with_facts.contains(r->get_decl(0)) && !source.is_output_predicate(r->get_decl(0)); can_expand.set(i, can_exp); } void mk_rule_inliner::del_rule(rule* r, unsigned i) { app* head = r->get_head(); m_head_visitor.del_position(head, i); unsigned tl_sz = r->get_uninterpreted_tail_size(); for (unsigned j = 0; j < tl_sz; ++j) { app* tail = r->get_tail(j); m_tail_visitor.del_position(tail, i); } } #define PRT(_x_) ((_x_)?"T":"F") bool mk_rule_inliner::inline_linear(scoped_ptr& rules) { bool done_something = false; unsigned sz = rules->get_num_rules(); m_head_visitor.reset(sz); m_tail_visitor.reset(sz); m_head_index.reset(); m_tail_index.reset(); TRACE("dl", rules->display(tout);); rule_ref_vector acc(m_rm); for (unsigned i = 0; i < sz; ++i) { acc.push_back(rules->get_rule(i)); } // set up unification index. svector& can_remove = m_head_visitor.can_remove(); svector& can_expand = m_head_visitor.can_expand(); for (unsigned i = 0; i < sz; ++i) { add_rule(*rules, acc[i].get(), i); } // initialize substitution. rule_counter& vc = m_rm.get_counter(); unsigned max_var = 0; for (unsigned i = 0; i < sz; ++i) { rule* r = acc[i].get(); max_var = std::max(max_var, vc.get_max_var(r->get_head())); unsigned tl_sz = r->get_uninterpreted_tail_size(); for (unsigned j = 0; j < tl_sz; ++j) { max_var = std::max(max_var, vc.get_max_var(r->get_tail(j))); } } m_subst.reset(); m_subst.reserve_vars(max_var+1); m_subst.reserve_offsets(std::max(m_tail_index.get_approx_num_regs(), 2+m_head_index.get_approx_num_regs())); svector valid; valid.reset(); valid.resize(sz, true); bool allow_branching = m_context.get_params().xform_inline_linear_branch(); for (unsigned i = 0; i < sz; ++i) { while (true) { rule_ref r(acc[i].get(), m_rm); TRACE("dl", r->display(m_context, tout << "processing: " << i << "\n");); if (!valid.get(i)) { TRACE("dl", tout << "invalid: " << i << "\n";); break; } if (!can_expand.get(i)) { TRACE("dl", tout << "cannot expand: " << i << "\n";); break; } m_head_visitor.reset(); m_head_index.unify(r->get_tail(0), m_head_visitor); unsigned num_head_unifiers = m_head_visitor.get_unifiers().size(); if (num_head_unifiers != 1) { TRACE("dl", tout << "no unique unifier " << num_head_unifiers << "\n";); break; } unsigned j = m_head_visitor.get_unifiers()[0]; if (!can_remove.get(j) || !valid.get(j) || i == j) { TRACE("dl", tout << PRT(can_remove.get(j)) << " " << PRT(valid.get(j)) << " " << PRT(i != j) << "\n";); break; } rule* r2 = acc[j].get(); // check that the head of r2 only unifies with this single body position. TRACE("dl", output_predicate(m_context, r2->get_head(), tout << "unify head: "); tout << "\n";); m_tail_visitor.reset(); m_tail_index.unify(r2->get_head(), m_tail_visitor); unsigned_vector const& tail_unifiers = m_tail_visitor.get_unifiers(); unsigned num_tail_unifiers = tail_unifiers.size(); SASSERT(!tail_unifiers.empty()); if (!allow_branching && num_tail_unifiers != 1) { TRACE("dl", tout << "too many tails " << num_tail_unifiers << "\n";); break; } rule_ref rl_res(m_rm); if (!try_to_inline_rule(*r.get(), *r2, 0, rl_res)) { TRACE("dl", r->display(m_context, tout << "inlining failed\n"); r2->display(m_context, tout); ); break; } done_something = true; TRACE("dl", r->display(m_context, tout); r2->display(m_context, tout); rl_res->display(m_context, tout); ); del_rule(r, i); add_rule(*rules, rl_res.get(), i); r = rl_res; acc[i] = r.get(); can_expand.set(i, can_expand.get(j)); if (num_tail_unifiers == 1) { TRACE("dl", tout << "setting invalid: " << j << "\n";); valid.set(j, false); datalog::del_rule(m_mc, *r2); del_rule(r2, j); } max_var = std::max(max_var, vc.get_max_rule_var(*r.get())); m_subst.reserve_vars(max_var+1); } } if (done_something) { scoped_ptr res = alloc(rule_set, m_context); for (unsigned i = 0; i < sz; ++i) { if (valid.get(i)) { res->add_rule(acc[i].get()); } } res->inherit_predicates(*rules); TRACE("dl", res->display(tout);); rules = res.detach(); } return done_something; } rule_set * mk_rule_inliner::operator()(rule_set const & source) { bool something_done = false; ref hsmc; if (source.get_num_rules() == 0) { return 0; } rule_set::iterator end = source.end(); for (rule_set::iterator it = source.begin(); it != end; ++ it) { if (has_quantifier(**it)) { return 0; } } if (m_context.get_model_converter()) { hsmc = alloc(horn_subsume_model_converter, m); } m_mc = hsmc.get(); scoped_ptr res = alloc(rule_set, m_context); if (m_context.get_params().xform_inline_eager()) { TRACE("dl", source.display(tout << "before eager inlining\n");); plan_inlining(source); something_done = transform_rules(source, *res); VERIFY(res->close()); //this transformation doesn't break the negation stratification // try eager inlining if (do_eager_inlining(res)) { something_done = true; } TRACE("dl", res->display(tout << "after eager inlining\n");); } if (something_done) { res->inherit_predicates(source); } else { res = alloc(rule_set, source); } if (m_context.get_params().xform_inline_linear() && inline_linear(res)) { something_done = true; } if (!something_done) { res = 0; } else { m_context.add_model_converter(hsmc.get()); } return res.detach(); } }; z3-4.4.0/src/muz/transforms/dl_transforms.cpp0000644000175000017500000000613512540347414021033 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_transforms.cpp Abstract: Default transformations. Author: Nikolaj Bjorner (nbjorner) 2013-08-28. Revision History: Extracted from dl_context --*/ #include"dl_transforms.h" #include"dl_rule_transformer.h" #include"dl_mk_coi_filter.h" #include"dl_mk_filter_rules.h" #include"dl_mk_interp_tail_simplifier.h" #include"dl_mk_rule_inliner.h" #include"dl_mk_bit_blast.h" #include"dl_mk_array_blast.h" #include"dl_mk_karr_invariants.h" #include"dl_mk_magic_symbolic.h" #include"dl_mk_quantifier_abstraction.h" #include"dl_mk_quantifier_instantiation.h" #include"dl_mk_subsumption_checker.h" #include"dl_mk_scale.h" #include"fixedpoint_params.hpp" namespace datalog { void apply_default_transformation(context& ctx) { flet _enable_bv(ctx.bind_vars_enabled(), false); rule_transformer transf(ctx); ctx.ensure_closed(); transf.reset(); transf.register_plugin(alloc(datalog::mk_coi_filter, ctx)); transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx)); if (ctx.get_params().xform_quantify_arrays()) { transf.register_plugin(alloc(datalog::mk_quantifier_abstraction, ctx, 38000)); } transf.register_plugin(alloc(datalog::mk_quantifier_instantiation, ctx, 37000)); transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 35005)); transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 35000)); transf.register_plugin(alloc(datalog::mk_coi_filter, ctx, 34990)); transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx, 34980)); //and another round of inlining transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34975)); transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34970)); transf.register_plugin(alloc(datalog::mk_coi_filter, ctx, 34960)); transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx, 34950)); transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34940)); transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34930)); transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34920)); transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34910)); transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34900)); transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34890)); transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34880)); transf.register_plugin(alloc(datalog::mk_bit_blast, ctx, 35000)); transf.register_plugin(alloc(datalog::mk_karr_invariants, ctx, 36010)); transf.register_plugin(alloc(datalog::mk_scale, ctx, 36030)); if (!ctx.get_params().xform_quantify_arrays()) { transf.register_plugin(alloc(datalog::mk_array_blast, ctx, 36000)); } if (ctx.get_params().xform_magic()) { transf.register_plugin(alloc(datalog::mk_magic_symbolic, ctx, 36020)); } ctx.transform_rules(transf); } } z3-4.4.0/src/muz/transforms/dl_mk_slice.h0000644000175000017500000000545412540347414020073 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_slice.h Abstract: Author: Krystof Hoder (t-khoder) 2010-10-4. Revision History: --*/ #ifndef _DL_MK_SLICE_H_ #define _DL_MK_SLICE_H_ #include"dl_context.h" #include"dl_rule_set.h" #include"uint_set.h" #include"dl_rule_transformer.h" namespace datalog { /** \brief Implements a slicing rule transformation. */ class mk_slice : public rule_transformer::plugin { class slice_proof_converter; class slice_model_converter; context& m_ctx; ast_manager& m; rule_manager& rm; svector m_input; svector m_output; expr_ref_vector m_solved_vars; svector m_var_is_sliceable; obj_map m_predicates; obj_map m_sliceable; ast_ref_vector m_pinned; slice_proof_converter* m_pc; slice_model_converter* m_mc; void reset(); void init(rule_set const& source); void saturate(rule_set const& source); void display(std::ostream& out); bool prune_rule(rule& r); void init_vars(rule& r); void init_vars(app* p, bool is_output, bool is_neg_tail); bool finalize_vars(app* p); unsigned num_vars() const { return m_input.size(); } bit_vector& get_predicate_slice(func_decl* p); bit_vector& get_predicate_slice(app* p) { return get_predicate_slice(p->get_decl()); } bool is_eq(expr* e, unsigned& v, expr_ref& r); void add_free_vars(uint_set& s, expr* e); void add_var(unsigned idx); bool is_output(expr* e); bool is_output(unsigned idx); void update_rules(rule_set const& src, rule_set& dst); void update_rule(rule& r, rule_set& dst); expr_ref_vector get_tail_conjs(rule const& r); void declare_predicates(rule_set const& src, rule_set& dst); bool rule_updated(rule const& r); void update_predicate(app* p, app_ref& q); void filter_unique_vars(rule& r); void solve_vars(rule& r, uint_set& used_vars, uint_set& parameter_vars); public: /** \brief Create slice rule transformer for \c goal predicate. When applying the transformer, the \c goal must be present in the \c rule_set that is being transformed. */ mk_slice(context & ctx); virtual ~mk_slice() { } rule_set * operator()(rule_set const & source); func_decl* get_predicate(func_decl* p) { func_decl* q = p; m_predicates.find(p, q); return q; } obj_map const& get_predicates() { return m_predicates; } }; }; #endif /* _DL_MK_SLICE_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_filter_rules.cpp0000644000175000017500000001372312540347414022024 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_filter_rules.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-05-18. Revision History: --*/ #include"dl_mk_filter_rules.h" #include"dl_context.h" #include"for_each_expr.h" #include"ast_pp.h" namespace datalog { mk_filter_rules::mk_filter_rules(context & ctx): plugin(2000), m_context(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_result(0), m_pinned(m) { } mk_filter_rules::~mk_filter_rules() { ptr_vector to_dealloc; filter_cache::iterator it = m_tail2filter.begin(); filter_cache::iterator end = m_tail2filter.end(); for(; it!=end; ++it) { to_dealloc.push_back(it->m_key); } m_tail2filter.reset(); ptr_vector::iterator dit = to_dealloc.begin(); ptr_vector::iterator dend = to_dealloc.end(); for(; dit!=dend; ++dit) { dealloc(*dit); } } /** \brief Return true if \c pred is a cadidate for a "filter" rule. */ bool mk_filter_rules::is_candidate(app * pred) { if (!m_context.is_predicate(pred)) { TRACE("mk_filter_rules", tout << mk_pp(pred, m) << "\nis not a candidate because it is interpreted.\n";); return false; } var_idx_set used_vars; unsigned n = pred->get_num_args(); for (unsigned i = 0; i < n; i++) { expr * arg = pred->get_arg(i); if (m.is_value(arg)) return true; SASSERT(is_var(arg)); unsigned vidx = to_var(arg)->get_idx(); if (used_vars.contains(vidx)) return true; used_vars.insert(vidx); } return false; } /** \brief Create a "filter" (if it doesn't exist already) for the given predicate. */ func_decl * mk_filter_rules::mk_filter_decl(app * pred, var_idx_set const & non_local_vars) { sort_ref_buffer filter_domain(m); filter_key * key = alloc(filter_key, m); mk_new_rule_tail(m, pred, non_local_vars, filter_domain, key->filter_args, key->new_pred); func_decl * filter_decl = 0; if (!m_tail2filter.find(key, filter_decl)) { filter_decl = m_context.mk_fresh_head_predicate(pred->get_decl()->get_name(), symbol("filter"), filter_domain.size(), filter_domain.c_ptr(), pred->get_decl()); m_pinned.push_back(filter_decl); m_tail2filter.insert(key, filter_decl); app_ref filter_head(m); filter_head = m.mk_app(filter_decl, key->filter_args.size(), key->filter_args.c_ptr()); app * filter_tail = key->new_pred; rule * filter_rule = m_context.get_rule_manager().mk(filter_head, 1, &filter_tail, (const bool *)0); filter_rule->set_accounting_parent_object(m_context, m_current); m_result->add_rule(filter_rule); m_context.get_rule_manager().mk_rule_asserted_proof(*filter_rule); } else { dealloc(key); } SASSERT(filter_decl != 0); SASSERT(filter_decl->get_arity()==filter_domain.size()); return filter_decl; } void mk_filter_rules::process(rule * r) { m_current = r; app * new_head = r->get_head(); app_ref_vector new_tail(m); svector new_is_negated; unsigned sz = r->get_tail_size(); bool rule_modified = false; for (unsigned i = 0; i < sz; i++) { app * tail = r->get_tail(i); if (is_candidate(tail)) { TRACE("mk_filter_rules", tout << "is_candidate: " << mk_pp(tail, m) << "\n";); var_idx_set non_local_vars = rm.collect_rule_vars_ex(r, tail); func_decl * filter_decl = mk_filter_decl(tail, non_local_vars); ptr_buffer new_args; var_idx_set used_vars; unsigned num_args = tail->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = tail->get_arg(i); if (is_var(arg)) { unsigned vidx = to_var(arg)->get_idx(); if (non_local_vars.contains(vidx) && !used_vars.contains(vidx)) { new_args.push_back(arg); used_vars.insert(vidx); } } } SASSERT(new_args.size() == filter_decl->get_arity()); new_tail.push_back(m.mk_app(filter_decl, new_args.size(), new_args.c_ptr())); rule_modified = true; } else { new_tail.push_back(tail); } new_is_negated.push_back(r->is_neg_tail(i)); } if (rule_modified) { remove_duplicate_tails(new_tail, new_is_negated); SASSERT(new_tail.size() == new_is_negated.size()); rule * new_rule = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(), new_is_negated.c_ptr()); new_rule->set_accounting_parent_object(m_context, m_current); m_result->add_rule(new_rule); m_context.get_rule_manager().mk_rule_rewrite_proof(*r, *new_rule); m_modified = true; } else { m_result->add_rule(r); } } rule_set * mk_filter_rules::operator()(rule_set const & source) { m_tail2filter.reset(); m_result = alloc(rule_set, m_context); m_modified = false; unsigned num_rules = source.get_num_rules(); for (unsigned i = 0; i < num_rules; i++) { process(source.get_rule(i)); } if(!m_modified) { dealloc(m_result); return static_cast(0); } m_result->inherit_predicates(source); return m_result; } }; z3-4.4.0/src/muz/transforms/dl_mk_quantifier_instantiation.cpp0000644000175000017500000002260412540347414024436 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_quantifier_instantiation.cpp Abstract: Convert Quantified Horn clauses into non-quantified clauses using instantiation. Author: Ken McMillan Andrey Rybalchenko Nikolaj Bjorner (nbjorner) 2013-04-02 Revision History: Based on approach suggested in the SAS 2013 paper "On Solving Universally Quantified Horn Clauses" --*/ #include "dl_mk_quantifier_instantiation.h" #include "dl_context.h" #include "pattern_inference.h" namespace datalog { mk_quantifier_instantiation::mk_quantifier_instantiation( context & ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), m_ctx(ctx), m_var2cnst(m), m_cnst2var(m) { } mk_quantifier_instantiation::~mk_quantifier_instantiation() { } void mk_quantifier_instantiation::extract_quantifiers(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs) { conjs.reset(); qs.reset(); unsigned tsz = r.get_tail_size(); for (unsigned j = 0; j < tsz; ++j) { conjs.push_back(r.get_tail(j)); } qe::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; } } } void mk_quantifier_instantiation::instantiate_quantifier(quantifier* q, expr_ref_vector & conjs) { expr_ref qe(m); qe = q; m_var2cnst(qe); q = to_quantifier(qe); if (q->get_num_patterns() == 0) { proof_ref new_pr(m); pattern_inference_params params; pattern_inference infer(m, params); infer(q, qe, new_pr); q = to_quantifier(qe); } unsigned num_patterns = q->get_num_patterns(); for (unsigned i = 0; i < num_patterns; ++i) { expr * pat = q->get_pattern(i); SASSERT(m.is_pattern(pat)); instantiate_quantifier(q, to_app(pat), conjs); } } void mk_quantifier_instantiation::instantiate_quantifier(quantifier* q, app* pat, expr_ref_vector & conjs) { m_binding.reset(); m_binding.resize(q->get_num_decls()); term_pairs todo; match(0, pat, 0, todo, q, conjs); } void mk_quantifier_instantiation::match(unsigned i, app* pat, unsigned j, term_pairs& todo, quantifier* q, expr_ref_vector& conjs) { TRACE("dl", tout << "match" << mk_pp(pat, m) << "\n";); while (j < todo.size()) { expr* p = todo[j].first; expr* t = todo[j].second; if (is_var(p)) { unsigned idx = to_var(p)->get_idx(); if (!m_binding[idx]) { m_binding[idx] = t; match(i, pat, j + 1, todo, q, conjs); m_binding[idx] = 0; return; } ++j; continue; } if (!is_app(p)) { return; } app* a1 = to_app(p); unsigned id = t->get_id(); unsigned next_id = id; unsigned sz = todo.size(); do { expr* t2 = m_terms[next_id]; if (is_app(t2)) { app* a2 = to_app(t2); if (a1->get_decl() == a2->get_decl() && a1->get_num_args() == a2->get_num_args()) { for (unsigned k = 0; k < a1->get_num_args(); ++k) { todo.push_back(std::make_pair(a1->get_arg(k), a2->get_arg(k))); } match(i, pat, j + 1, todo, q, conjs); todo.resize(sz); } } next_id = m_uf.next(next_id); } while (next_id != id); return; } if (i == pat->get_num_args()) { yield_binding(q, conjs); return; } expr* arg = pat->get_arg(i); ptr_vector* terms = 0; if (m_funs.find(to_app(arg)->get_decl(), terms)) { for (unsigned k = 0; k < terms->size(); ++k) { todo.push_back(std::make_pair(arg, (*terms)[k])); match(i + 1, pat, j, todo, q, conjs); todo.pop_back(); } } } void mk_quantifier_instantiation::yield_binding(quantifier* q, expr_ref_vector& conjs) { DEBUG_CODE( for (unsigned i = 0; i < m_binding.size(); ++i) { SASSERT(m_binding[i]); }); m_binding.reverse(); expr_ref res(m); instantiate(m, q, m_binding.c_ptr(), res); m_binding.reverse(); m_cnst2var(res); conjs.push_back(res); TRACE("dl", tout << mk_pp(q, m) << "\n==>\n" << mk_pp(res, m) << "\n";); } void mk_quantifier_instantiation::collect_egraph(expr* e) { expr* e1, *e2; m_todo.push_back(e); expr_fast_mark1 visited; while (!m_todo.empty()) { e = m_todo.back(); m_todo.pop_back(); if (visited.is_marked(e)) { continue; } unsigned n = e->get_id(); if (n >= m_terms.size()) { m_terms.resize(n+1); } m_terms[n] = e; visited.mark(e); if (m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) { m_uf.merge(e1->get_id(), e2->get_id()); } if (is_app(e)) { app* ap = to_app(e); ptr_vector* terms = 0; if (!m_funs.find(ap->get_decl(), terms)) { terms = alloc(ptr_vector); m_funs.insert(ap->get_decl(), terms); } terms->push_back(e); m_todo.append(ap->get_num_args(), ap->get_args()); } } } void mk_quantifier_instantiation::instantiate_rule(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs, rule_set& rules) { rule_manager& rm = m_ctx.get_rule_manager(); expr_ref fml(m), cnst(m); var_ref var(m); ptr_vector sorts; r.get_vars(m, sorts); m_uf.reset(); m_terms.reset(); m_var2cnst.reset(); m_cnst2var.reset(); fml = m.mk_and(conjs.size(), conjs.c_ptr()); for (unsigned i = 0; i < sorts.size(); ++i) { var = m.mk_var(i, sorts[i]); cnst = m.mk_fresh_const("C", sorts[i]); m_var2cnst.insert(var, cnst); m_cnst2var.insert(cnst, var); } fml = m.mk_and(conjs.size(), conjs.c_ptr()); m_var2cnst(fml); collect_egraph(fml); for (unsigned i = 0; i < qs.size(); ++i) { instantiate_quantifier(qs[i].get(), conjs); } obj_map*>::iterator it = m_funs.begin(), end = m_funs.end(); for (; it != end; ++it) { dealloc(it->m_value); } m_funs.reset(); fml = m.mk_and(conjs.size(), conjs.c_ptr()); fml = m.mk_implies(fml, r.get_head()); TRACE("dl", r.display(m_ctx, tout); tout << mk_pp(fml, m) << "\n";); rule_set added_rules(m_ctx); proof_ref pr(m); rm.mk_rule(fml, pr, added_rules); if (r.get_proof()) { // use def-axiom to encode that new rule is a weakening of the original. proof* p1 = r.get_proof(); for (unsigned i = 0; i < added_rules.get_num_rules(); ++i) { rule* r2 = added_rules.get_rule(i); rm.to_formula(*r2, fml); pr = m.mk_modus_ponens(m.mk_def_axiom(m.mk_implies(m.get_fact(p1), fml)), p1); r2->set_proof(m, pr); } } rules.add_rules(added_rules); } rule_set * mk_quantifier_instantiation::operator()(rule_set const & source) { if (!m_ctx.instantiate_quantifiers()) { return 0; } bool has_quantifiers = false; unsigned sz = source.get_num_rules(); rule_manager& rm = m_ctx.get_rule_manager(); for (unsigned i = 0; !has_quantifiers && i < sz; ++i) { rule& r = *source.get_rule(i); has_quantifiers = has_quantifiers || rm.has_quantifiers(r); if (r.has_negation()) { return 0; } } if (!has_quantifiers) { return 0; } expr_ref_vector conjs(m); quantifier_ref_vector qs(m); rule_set * result = alloc(rule_set, m_ctx); bool instantiated = false; for (unsigned i = 0; i < sz; ++i) { rule * r = source.get_rule(i); extract_quantifiers(*r, conjs, qs); if (qs.empty()) { result->add_rule(r); } else { instantiate_rule(*r, conjs, qs, *result); instantiated = true; } } // model convertion: identity function. if (instantiated) { result->inherit_predicates(source); } else { dealloc(result); result = 0; } return result; } }; z3-4.4.0/src/muz/transforms/dl_mk_slice.cpp0000644000175000017500000007443512540347414020433 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_slice.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2012-9-12. Revision History: Consider a rule: P(x,y) :- R(x,z), phi(x,y,z) input: x, z output: x, y Let x_i, y_i, z_i be incides into the vectors x, y, z. Suppose that positions in P and R are annotated with what is slicable. Sufficient conditions for sliceability: x_i is sliceable if x_i does not appear in phi(x,y,z) and the positions where x_i is used in P and R are sliceable y_i is sliceable if y_i does not occur in phi(x,y,z), or if it occurs in phi(x,y,z) it is only in one conjunct of the form y_i = t[x_j,y_j,z_j] and the positions where y_i is used in P and R are sliceable z_i is sliceable if z_i does not occur in phi(x,y,z), or if it occurs in phi(x,y,z) it is only in one conjunct of the form y_i = t[x_j,y_j,z_i] where y_i is sliceable and the positions where z_i is used in P and R are sliceable A more refined approach may be using Gaussean elimination based on x,z and eliminating variables from x,y (expressing them in terms of a disjoint subeset of x,z). --*/ #include "dl_mk_slice.h" #include "ast_pp.h" #include "expr_functors.h" #include "dl_mk_rule_inliner.h" #include "model_smt2_pp.h" namespace datalog { /** Convert from sliced proofs to original proofs. Given sliced rule fml0: forall x y z u. p(x,y) & z = f(x,y) & phi(x,u) => p(u, z) into fml1: forall a b . q(a) & phi(a,b) => q(b) It induces mappings: theta: a |-> x, b |-> u vars: x y z u. predicates: -q(a) |-> p(x,y) +q(b) |-> z = f(x,y) => p(u,z) fml1 |-> fml0 The mapping theta is an injective function from variable indices to variable indices. We can apply it as a substitution on expressions, but we can also apply it as a transformation on substitutions. We write theta[subst] when applying theta on substitution 'subst' such that if [x |-> t] is in subst, then [theta(x) |-> theta(t)] is in the result. Given hyper-resolvent: fml1 subst1 fml2 subst2 |- fml3 where fml1 |-> fml1' with theta1 fml2 |-> fml2' with theta2 Perform the following steps: 1. [Convert fml1' fml2' to datalog rules because we have resolution routines] 2. Create subst1' := theta1[subst1] subst2' := theta2[subst2] 3. Set fml1'' := subst1'(fml1') fml2'' := subst2'(fml2') 4. Resolve fml1'' and fml2'' extract subst1'', subst2'' from resolvents. extract goal fml3' 5. Create subst1''' := subst1'' o subst1' subst2''' := subst2'' o subst2' 6. Return fml1'' subst1''' fml2'' subst2''' |- fml3' 7. Attach to fml3' the transformation ...? */ class mk_slice::slice_proof_converter : public proof_converter { context& m_ctx; ast_manager& m; rule_manager& rm; rule_ref_vector m_pinned_rules; expr_ref_vector m_pinned_exprs; obj_map m_rule2slice; // rule to sliced rule obj_map m_renaming; // rule to renaming obj_map m_sliceform2rule; // sliced formula to rule. ptr_vector m_todo; obj_map m_new_proof; rule_unifier m_unifier; slice_proof_converter(slice_proof_converter const& other); void init_form2rule() { if (!m_sliceform2rule.empty()) { return; } obj_map::iterator it = m_rule2slice.begin(); obj_map::iterator end = m_rule2slice.end(); expr_ref fml(m); for (; it != end; ++it) { rm.to_formula(*it->m_value, fml); m_pinned_exprs.push_back(fml); TRACE("dl", tout << "orig: " << mk_pp(fml, m) << "\n"; it->m_value->display(m_ctx, tout << "new:\n");); m_sliceform2rule.insert(fml, it->m_key); } } void translate_proof(proof_ref& pr) { m_todo.reset(); m_new_proof.reset(); m_todo.push_back(pr); while (!m_todo.empty()) { proof* p = m_todo.back(); if (m_new_proof.contains(p)) { m_todo.pop_back(); } else if (translate_asserted(p)) { // done } else if (translate_hyper_res(p)) { // done } else { m_new_proof.insert(p, p); m_todo.pop_back(); TRACE("dl", tout << "unhandled proof term\n" << mk_pp(p, m) << "\n";); } } pr = m_new_proof.find(pr); } bool translate_asserted(proof* p) { expr* fact = 0; rule* r = 0; if (!m.is_asserted(p, fact)) { return false; } if (!m_sliceform2rule.find(fact, r)) { TRACE("dl", tout << "does not have fact\n" << mk_pp(fact, m) << "\n";); return false; } proof_ref new_p(m); new_p = r->get_proof(); m_pinned_exprs.push_back(new_p); m_todo.pop_back(); m_new_proof.insert(p, new_p); return true; } bool translate_hyper_res(proof* p) { dl_decl_util util(m); svector > positions; expr_ref concl(m), slice_concl(m); proof_ref_vector premises0(m); vector substs, substs0; if (!m.is_hyper_resolve(p, premises0, slice_concl, positions, substs0)) { return false; } unsigned num_args = p->get_num_args(); SASSERT(num_args >= 2); bool all_found = true; for (unsigned i = 0; i < num_args-1; ++i) { proof* arg = to_app(p->get_arg(i)); SASSERT(m.is_proof(arg)); if (!m_new_proof.contains(arg)) { m_todo.push_back(arg); all_found = false; } } if (!all_found) { return true; } ptr_vector premises; proof* p0 = to_app(p->get_arg(0)); proof* p0_new = m_new_proof.find(p0); expr* fact0 = m.get_fact(p0); TRACE("dl", tout << "fact0: " << mk_pp(fact0, m) << "\n";); rule* orig0; if (!m_sliceform2rule.find(fact0, orig0)) { return false; } premises.push_back(p0_new); rule_ref r1(rm), r2(rm), r3(rm); r1 = orig0; substs.push_back(expr_ref_vector(m)); for (unsigned i = 1; i < num_args-1; ++i) { proof* p1 = to_app(p->get_arg(i)); proof* p1_new = m_new_proof.find(p1); expr* fact1 = m.get_fact(p1); TRACE("dl", tout << "fact1: " << mk_pp(fact1, m) << "\n";); rule* orig1 = 0; if (!m_sliceform2rule.find(fact1, orig1)) { return false; } premises.push_back(p1_new); // TODO: work with substitutions. r2 = orig1; unsigned idx = 0; // brittle. TBD get index from positions. VERIFY(m_unifier.unify_rules(*r1, idx, *r2)); m_unifier.apply(*r1.get(), idx, *r2.get(), r3); expr_ref_vector const sub1 = m_unifier.get_rule_subst(*r1.get(), true); for (unsigned j = 0; j < substs.size(); ++j) { apply_subst(substs[j], sub1); // size of substitutions may have grown...substs[j].resize(num_args[j]); } substs.push_back(m_unifier.get_rule_subst(*r2.get(), false)); TRACE("dl", r1->display(m_ctx, tout << "rule1:"); r2->display(m_ctx, tout << "rule2:"); r3->display(m_ctx, tout << "res:");); r1 = r3; } rm.to_formula(*r1.get(), concl); proof* new_p = m.mk_hyper_resolve(premises.size(), premises.c_ptr(), concl, positions, substs); m_pinned_exprs.push_back(new_p); m_pinned_rules.push_back(r1.get()); TRACE("dl", tout << "orig: " << mk_pp(slice_concl, m) << "\n"; r1->display(m_ctx, tout << "new:");); m_sliceform2rule.insert(slice_concl, r1.get()); m_rule2slice.insert(r1.get(), 0); m_renaming.insert(r1.get(), unsigned_vector()); m_new_proof.insert(p, new_p); m_todo.pop_back(); TRACE("dl", tout << "translated:\n" << mk_pp(p, m) << "\nto\n" << mk_pp(new_p, m) << "\n";); return true; } public: slice_proof_converter(context& ctx): m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_pinned_rules(rm), m_pinned_exprs(m), m_unifier(ctx) {} void insert(rule* orig_rule, rule* slice_rule, unsigned sz, unsigned const* renaming) { m_rule2slice.insert(orig_rule, slice_rule); m_pinned_rules.push_back(orig_rule); m_pinned_rules.push_back(slice_rule); m_renaming.insert(orig_rule, unsigned_vector(sz, renaming)); } virtual void operator()(ast_manager& m, unsigned num_source, proof * const * source, proof_ref & result) { SASSERT(num_source == 1); result = source[0]; init_form2rule(); translate_proof(result); } virtual proof_converter * translate(ast_translation & translator) { UNREACHABLE(); // this would require implementing translation for the dl_context. return 0; } }; class mk_slice::slice_model_converter : public model_converter { ast_manager& m; obj_map m_slice2old; obj_map m_sliceable; ast_ref_vector m_pinned; public: slice_model_converter(mk_slice& parent, ast_manager& m): m(m), m_pinned(m) {} void add_predicate(func_decl* old_f, func_decl* slice_f) { m_pinned.push_back(old_f); m_pinned.push_back(slice_f); m_slice2old.insert(slice_f, old_f); } void add_sliceable(func_decl* f, bit_vector const& bv) { m_pinned.push_back(f); m_sliceable.insert(f, bv); } virtual void operator()(model_ref & md) { if (m_slice2old.empty()) { return; } TRACE("dl", model_smt2_pp(tout, m, *md, 0); ); model_ref old_model = alloc(model, m); obj_map::iterator it = m_slice2old.begin(); obj_map::iterator end = m_slice2old.end(); for (; it != end; ++it) { func_decl* old_p = it->m_value; func_decl* new_p = it->m_key; bit_vector const& is_sliced = m_sliceable.find(old_p); SASSERT(is_sliced.size() == old_p->get_arity()); SASSERT(is_sliced.size() > new_p->get_arity()); func_interp* old_fi = alloc(func_interp, m, is_sliced.size()); TRACE("dl", tout << mk_pp(old_p, m) << " " << mk_pp(new_p, m) << "\n"; for (unsigned j = 0; j < is_sliced.size(); ++j) { tout << (is_sliced.get(j)?"1":"0"); } tout << "\n";); if (new_p->get_arity() == 0) { old_fi->set_else(md->get_const_interp(new_p)); } else { expr_ref_vector subst(m); expr_ref tmp(m); var_subst vs(m, false); for (unsigned i = 0; i < is_sliced.size(); ++i) { if (!is_sliced.get(i)) { subst.push_back(m.mk_var(i, old_p->get_domain(i))); } } func_interp* new_fi = md->get_func_interp(new_p); if (!new_fi) { TRACE("dl", tout << new_p->get_name() << " has no value in the current model\n";); dealloc(old_fi); continue; } if (!new_fi->is_partial()) { TRACE("dl", tout << mk_pp(new_fi->get_else(), m) << "\n";); vs(new_fi->get_else(), subst.size(), subst.c_ptr(), tmp); old_fi->set_else(tmp); } unsigned num_entries = new_fi->num_entries(); for (unsigned j = 0; j < num_entries; ++j) { expr_ref res(m); expr_ref_vector args(m); func_entry const* e = new_fi->get_entry(j); for (unsigned k = 0, l = 0; k < old_p->get_arity(); ++k) { if (!is_sliced.get(k)) { vs(e->get_arg(l++), subst.size(), subst.c_ptr(), tmp); args.push_back(tmp); } else { args.push_back(m.mk_var(k, old_p->get_domain(k))); } SASSERT(l <= new_p->get_arity()); } vs(e->get_result(), subst.size(), subst.c_ptr(), res); old_fi->insert_entry(args.c_ptr(), res.get()); } old_model->register_decl(old_p, old_fi); } } // register values that have not been sliced. unsigned sz = md->get_num_constants(); for (unsigned i = 0; i < sz; ++i) { func_decl* c = md->get_constant(i); if (!m_slice2old.contains(c)) { old_model->register_decl(c, md->get_const_interp(c)); } } sz = md->get_num_functions(); for (unsigned i = 0; i < sz; ++i) { func_decl* f = md->get_function(i); if (!m_slice2old.contains(f)) { func_interp* fi = md->get_func_interp(f); old_model->register_decl(f, fi->copy()); } } md = old_model; TRACE("dl", model_smt2_pp(tout, m, *md, 0); ); } virtual model_converter * translate(ast_translation & translator) { UNREACHABLE(); return 0; } }; mk_slice::mk_slice(context & ctx): plugin(1), m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_solved_vars(m), m_pinned(m), m_pc(0), m_mc(0) {} bit_vector& mk_slice::get_predicate_slice(func_decl* h) { if (!m_sliceable.contains(h)) { bit_vector bv; bv.resize(h->get_arity(), true); m_sliceable.insert(h, bv); } return m_sliceable.find(h); } /** \brief Saturate set of rules with respect to slicing criteria. */ void mk_slice::saturate(rule_set const& src) { bool change = true; while (change) { change = false; for (unsigned i = 0; i < src.get_num_rules(); ++i) { change = prune_rule(*src.get_rule(i)) || change; } } } void mk_slice::filter_unique_vars(rule& r) { // // Variables that occur in multiple uinterpreted predicates are not sliceable. // uint_set used_vars; for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { app* p = r.get_tail(j); for (unsigned i = 0; i < p->get_num_args(); ++i) { expr* v = p->get_arg(i); if (is_var(v)) { unsigned vi = to_var(v)->get_idx(); add_var(vi); if (used_vars.contains(vi)) { m_var_is_sliceable[vi] = false; } else { used_vars.insert(vi); } } } } } void mk_slice::solve_vars(rule& r, uint_set& used_vars, uint_set& parameter_vars) { expr_ref_vector conjs = get_tail_conjs(r); for (unsigned j = 0; j < conjs.size(); ++j) { expr* e = conjs[j].get(); expr_ref r(m); unsigned v; if (is_eq(e, v, r) && is_output(v) && m_var_is_sliceable[v]) { TRACE("dl", tout << "is_eq: " << mk_pp(e, m) << " " << (m_solved_vars[v].get()?"solved":"new") << "\n";); add_var(v); if (!m_solved_vars[v].get()) { add_free_vars(parameter_vars, r); m_solved_vars[v] = r; } else { // variables can only be solved once. add_free_vars(used_vars, e); add_free_vars(used_vars, m_solved_vars[v].get()); used_vars.insert(v); } } else { add_free_vars(used_vars, e); } } } bool mk_slice::prune_rule(rule& r) { TRACE("dl", r.display(m_ctx, tout << "prune:\n"); ); bool change = false; init_vars(r); // // if a predicate in the body takes a constant as argument, // the corresponding position is not sliceable. // for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { app* p = r.get_tail(j); bit_vector& bv = get_predicate_slice(p); for (unsigned i = 0; i < p->get_num_args(); ++i) { if (!is_var(p->get_arg(i)) && bv.get(i)) { bv.unset(i); change = true; TRACE("dl", tout << "argument " << i << " is not a variable " << p->get_decl()->get_name() << "\n";); } } } filter_unique_vars(r); // // Collect the set of variables that are solved. // Collect the occurrence count of the variables per conjunct. // uint_set used_vars, parameter_vars; solve_vars(r, used_vars, parameter_vars); uint_set::iterator it = used_vars.begin(), end = used_vars.end(); for (; it != end; ++it) { if (*it < m_var_is_sliceable.size()) { m_var_is_sliceable[*it] = false; } } // // Check if sliceable variables are either solved // or are used to solve output sliceable variables, or // don't occur in interpreted tail. // for (unsigned i = 0; i < num_vars(); ++i) { if (!m_var_is_sliceable[i]) { continue; } if (used_vars.contains(i)) { m_var_is_sliceable[i] = false; continue; } bool is_input = m_input[i]; bool is_output = m_output[i]; if (is_input && is_output) { if (m_solved_vars[i].get()) { m_var_is_sliceable[i] = false; } } else if (is_output) { if (parameter_vars.contains(i)) { m_var_is_sliceable[i] = false; } } else if (is_input) { // I can be a parameter var, but not in used_vars. } else { // variable does not correspond to // any position in predicates. } } // // Update sliceable predicates based on slicing information of variables. // change = finalize_vars(r.get_head()) || change; for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { change = finalize_vars(r.get_tail(j)) || change; } return change; } bool mk_slice::is_eq(expr* e, unsigned& v, expr_ref& r) { expr* c, *th, *el, *e1, *e2; unsigned v1, v2; expr_ref r1(m), r2(m); if (m.is_ite(e, c, th, el)) { if (is_eq(th, v1, r1) && is_eq(el, v2, r2) && v1 == v2) { v = v1; r = m.mk_ite(c, r1, r2); return true; } } if (is_var(e)) { v = to_var(e)->get_idx(); r = m.mk_true(); return true; } if (m.is_not(e,e) && is_var(e)) { v = to_var(e)->get_idx(); r = m.mk_false(); return true; } if (m.is_eq(e, e1, e2) && is_var(e1)) { v = to_var(e1)->get_idx(); r = e2; return true; } if (m.is_eq(e, e1, e2) && is_var(e2)) { v = to_var(e2)->get_idx(); r = e1; return true; } return false; } bool mk_slice::is_output(unsigned idx) { return idx < m_output.size() && m_output[idx] && !m_input[idx]; } bool mk_slice::is_output(expr* e) { if (is_var(e)) { return is_output(to_var(e)->get_idx()); } else { return false; } } void mk_slice::init_vars(rule& r) { m_input.reset(); m_output.reset(); m_var_is_sliceable.reset(); m_solved_vars.reset(); init_vars(r.get_head(), true, false); for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { init_vars(r.get_tail(j), false, r.is_neg_tail(j)); } } expr_ref_vector mk_slice::get_tail_conjs(rule const& r) { expr_ref_vector conjs(m); for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { conjs.push_back(r.get_tail(j)); } qe::flatten_and(conjs); return conjs; } void mk_slice::add_var(unsigned idx) { if (idx >= m_input.size()) { m_input.resize(idx+1, false); m_output.resize(idx+1, false); m_var_is_sliceable.resize(idx+1, true); m_solved_vars.resize(idx+1); } } void mk_slice::init_vars(app* p, bool is_output, bool is_neg_tail) { bit_vector& bv = get_predicate_slice(p); for (unsigned i = 0; i < p->get_num_args(); ++i) { if (is_neg_tail) { TRACE("dl", tout << "negated " << i << " in " << p->get_decl()->get_name() << "\n";); bv.unset(i); } expr* arg = p->get_arg(i); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); add_var(idx); if (is_output) { m_output[idx] = true; } else { m_input[idx] = true; } m_var_is_sliceable[idx] &= bv.get(i); } else { SASSERT(m.is_value(arg)); if (!is_output) { TRACE("dl", tout << "input " << i << " in " << p->get_decl()->get_name() << "\n";); bv.unset(i); } } } } bool mk_slice::finalize_vars(app* p) { bool change = false; bit_vector& bv = get_predicate_slice(p); for (unsigned i = 0; i < p->get_num_args(); ++i) { expr* arg = p->get_arg(i); if (is_var(arg) && !m_var_is_sliceable[to_var(arg)->get_idx()] && bv.get(i)) { bv.unset(i); change = true; TRACE("dl", tout << "variable is unslicable " << mk_pp(arg, m) << " for index " << i << " in " << p->get_decl()->get_name() << "\n";); } } return change; } void mk_slice::add_free_vars(uint_set& result, expr* e) { expr_free_vars fv; fv(e); for (unsigned i = 0; i < fv.size(); ++i) { if (fv[i]) { result.insert(i); } } } void mk_slice::display(std::ostream& out) { obj_map::iterator it = m_sliceable.begin(); obj_map::iterator end = m_sliceable.end(); for (; it != end; ++it) { out << it->m_key->get_name() << " "; bit_vector const& bv = it->m_value; for (unsigned i = 0; i < bv.size(); ++i) { out << (bv.get(i)?"1":"0"); } out << "\n"; } } void mk_slice::reset() { m_input.reset(); m_output.reset(); m_var_is_sliceable.reset(); m_solved_vars.reset(); m_predicates.reset(); m_pinned.reset(); } void mk_slice::declare_predicates(rule_set const& src, rule_set& dst) { obj_map::iterator it = m_sliceable.begin(), end = m_sliceable.end(); ptr_vector domain; bool has_output = false; func_decl* f; for (; it != end; ++it) { domain.reset(); func_decl* p = it->m_key; bit_vector const& bv = it->m_value; for (unsigned i = 0; i < bv.size(); ++i) { if (!bv.get(i)) { domain.push_back(p->get_domain(i)); } } if (domain.size() < bv.size()) { f = m_ctx.mk_fresh_head_predicate(p->get_name(), symbol("slice"), domain.size(), domain.c_ptr(), p); m_pinned.push_back(f); m_predicates.insert(p, f); dst.inherit_predicate(src, p, f); if (m_mc) { m_mc->add_predicate(p, f); } } else if (src.is_output_predicate(p)) { dst.set_output_predicate(p); has_output = true; } } // disable slicing if the output predicates don't occur in rules. if (!has_output) { m_predicates.reset(); } } bool mk_slice::rule_updated(rule const& r) { if (m_predicates.contains(r.get_decl())) return true; for (unsigned i = 0; i < r.get_uninterpreted_tail_size(); ++i) { if (m_predicates.contains(r.get_decl(i))) return true; } return false; } void mk_slice::update_predicate(app* p, app_ref& q) { func_decl* qd; if (m_predicates.find(p->get_decl(), qd)) { bit_vector const& bv = get_predicate_slice(p->get_decl()); ptr_vector args; for (unsigned i = 0; i < bv.size(); ++i) { if (!bv.get(i)) { args.push_back(p->get_arg(i)); } } q = m.mk_app(qd, args.size(), args.c_ptr()); } else { q = p; } } void mk_slice::update_rule(rule& r, rule_set& dst) { rule_ref new_rule(rm); if (rule_updated(r)) { init_vars(r); app_ref_vector tail(m); app_ref head(m); update_predicate(r.get_head(), head); for (unsigned i = 0; i < r.get_uninterpreted_tail_size(); ++i) { app_ref t(m); update_predicate(r.get_tail(i), t); tail.push_back(t); } expr_ref_vector conjs = get_tail_conjs(r); m_solved_vars.reset(); for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); tail.push_back(to_app(e)); } new_rule = rm.mk(head.get(), tail.size(), tail.c_ptr(), (const bool*) 0); rm.fix_unbound_vars(new_rule, false); TRACE("dl", r.display(m_ctx, tout << "replacing:\n"); new_rule->display(m_ctx, tout << "by:\n");); if (m_ctx.generate_proof_trace()) { rm.mk_rule_asserted_proof(*new_rule.get()); } } else { new_rule = &r; } dst.add_rule(new_rule.get()); if (m_pc) { m_pc->insert(&r, new_rule.get(), 0, 0); } } void mk_slice::update_rules(rule_set const& src, rule_set& dst) { for (unsigned i = 0; i < src.get_num_rules(); ++i) { update_rule(*src.get_rule(i), dst); } } rule_set * mk_slice::operator()(rule_set const & src) { rule_manager& rm = m_ctx.get_rule_manager(); for (unsigned i = 0; i < src.get_num_rules(); ++i) { if (rm.has_quantifiers(*src.get_rule(i))) { return 0; } } ref spc; ref smc; if (m_ctx.generate_proof_trace()) { spc = alloc(slice_proof_converter, m_ctx); } if (m_ctx.get_model_converter()) { smc = alloc(slice_model_converter, *this, m); } m_pc = spc.get(); m_mc = smc.get(); reset(); saturate(src); rule_set* result = alloc(rule_set, m_ctx); declare_predicates(src, *result); if (m_predicates.empty()) { // nothing could be sliced. dealloc(result); return 0; } TRACE("dl", display(tout);); update_rules(src, *result); TRACE("dl", result->display(tout);); if (m_mc) { obj_map::iterator it = m_sliceable.begin(), end = m_sliceable.end(); for (; it != end; ++it) { m_mc->add_sliceable(it->m_key, it->m_value); } } m_ctx.add_proof_converter(spc.get()); m_ctx.add_model_converter(smc.get()); return result; } }; z3-4.4.0/src/muz/transforms/dl_mk_bit_blast.cpp0000644000175000017500000002615012540347414021266 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_bit_blast.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2012-08-30 Revision History: --*/ #include "dl_mk_bit_blast.h" #include "bit_blaster_rewriter.h" #include "rewriter_def.h" #include "ast_pp.h" #include "expr_safe_replace.h" #include "filter_model_converter.h" #include "dl_mk_interp_tail_simplifier.h" #include "fixedpoint_params.hpp" #include "scoped_proof.h" #include "model_v2_pp.h" namespace datalog { // // P(v) :- Q(extract[1:1]v ++ 0), R(1 ++ extract[0:0]v). // -> // P(bv(x,y)) :- Q(bv(x,0)), R(bv(1,y)) . // // Introduce P_bv: // P_bv(x,y) :- Q_bv(x,0), R_bv(1,y) // P(bv(x,y)) :- P_bv(x,y) // Query // this model converter should be composed with a filter converter // that gets rid of the new functions. class bit_blast_model_converter : public model_converter { ast_manager& m; bv_util m_bv; func_decl_ref_vector m_old_funcs; func_decl_ref_vector m_new_funcs; public: bit_blast_model_converter(ast_manager& m): m(m), m_bv(m), m_old_funcs(m), m_new_funcs(m) {} void insert(func_decl* old_f, func_decl* new_f) { m_old_funcs.push_back(old_f); m_new_funcs.push_back(new_f); } virtual model_converter * translate(ast_translation & translator) { return alloc(bit_blast_model_converter, m); } virtual void operator()(model_ref & model) { for (unsigned i = 0; i < m_new_funcs.size(); ++i) { func_decl* p = m_new_funcs[i].get(); func_decl* q = m_old_funcs[i].get(); func_interp* f = model->get_func_interp(p); if (!f) continue; expr_ref body(m); unsigned arity_p = p->get_arity(); unsigned arity_q = q->get_arity(); TRACE("dl", model_v2_pp(tout, *model); tout << mk_pp(p, m) << "\n"; tout << mk_pp(q, m) << "\n";); SASSERT(0 < arity_p); SASSERT(f); model->register_decl(p, f->copy()); func_interp* g = alloc(func_interp, m, arity_q); if (f) { body = f->get_interp(); SASSERT(!f->is_partial()); SASSERT(body); } else { body = m.mk_false(); } unsigned idx = 0; expr_ref arg(m), proj(m); expr_safe_replace sub(m); for (unsigned j = 0; j < arity_q; ++j) { sort* s = q->get_domain(j); arg = m.mk_var(j, s); expr* t = arg; if (m_bv.is_bv_sort(s)) { unsigned sz = m_bv.get_bv_size(s); for (unsigned k = 0; k < sz; ++k) { parameter p(k); proj = m.mk_app(m_bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &t); sub.insert(m.mk_var(idx++, m.mk_bool_sort()), proj); } } else { sub.insert(m.mk_var(idx++, s), arg); } } sub(body); g->set_else(body); model->register_decl(q, g); } } }; class expand_mkbv_cfg : public default_rewriter_cfg { context& m_context; ast_manager& m; bv_util m_util; expr_ref_vector m_args, m_f_vars, m_g_vars; func_decl_ref_vector m_old_funcs; func_decl_ref_vector m_new_funcs; rule_set const* m_src; rule_set* m_dst; obj_map m_pred2blast; public: expand_mkbv_cfg(context& ctx): m_context(ctx), m(ctx.get_manager()), m_util(m), m_args(m), m_f_vars(m), m_g_vars(m), m_old_funcs(m), m_new_funcs(m), m_src(0), m_dst(0) {} ~expand_mkbv_cfg() {} void set_src(rule_set const* src) { m_src = src; } void set_dst(rule_set* dst) { m_dst = dst; } func_decl_ref_vector const& old_funcs() const { return m_old_funcs; } func_decl_ref_vector const& new_funcs() const { return m_new_funcs; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (num == 0) { if (m_src->is_output_predicate(f)) m_dst->set_output_predicate(f); return BR_FAILED; } for (unsigned i = 0; i < num; ++i) { if (!m_util.is_mkbv(args[i])) return BR_FAILED; } // // f(mk_bv(args),...) // m_args.reset(); m_g_vars.reset(); m_f_vars.reset(); expr_ref fml(m); unsigned idx = 0; for (unsigned j = 0; j < num; ++j) { expr* arg = args[j]; if (m_util.is_mkbv(arg)) { app* a = to_app(arg); unsigned sz = a->get_num_args(); for (unsigned i = 0; i < sz; ++i) { m_args.push_back(a->get_arg(i)); m_g_vars.push_back(m.mk_var(idx++,m.mk_bool_sort())); } m_f_vars.push_back(m_util.mk_bv(sz, m_g_vars.c_ptr()+m_g_vars.size()-sz)); } else { m_args.push_back(arg); m_f_vars.push_back(m.mk_var(idx++, m.get_sort(arg))); m_g_vars.push_back(m_f_vars.back()); } } func_decl* g = 0; if (!m_pred2blast.find(f, g)) { ptr_vector domain; for (unsigned i = 0; i < m_args.size(); ++i) { domain.push_back(m.get_sort(m_args[i].get())); } g = m_context.mk_fresh_head_predicate(f->get_name(), symbol("bv"), m_args.size(), domain.c_ptr(), f); m_old_funcs.push_back(f); m_new_funcs.push_back(g); m_pred2blast.insert(f, g); m_dst->inherit_predicate(*m_src, f, g); } result = m.mk_app(g, m_args.size(), m_args.c_ptr()); result_pr = 0; return BR_DONE; } }; struct expand_mkbv : public rewriter_tpl { expand_mkbv_cfg m_cfg; expand_mkbv(ast_manager& m, context& ctx): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(ctx) { } }; class mk_bit_blast::impl { context & m_context; ast_manager & m; params_ref m_params; mk_interp_tail_simplifier m_simplifier; bit_blaster_rewriter m_blaster; expand_mkbv m_rewriter; bool blast(rule *r, expr_ref& fml) { proof_ref pr(m); expr_ref fml1(m), fml2(m), fml3(m); rule_ref r2(m_context.get_rule_manager()); // We need to simplify rule before bit-blasting. if (!m_simplifier.transform_rule(r, r2)) { r2 = r; } m_context.get_rule_manager().to_formula(*r2.get(), fml1); m_blaster(fml1, fml2, pr); m_rewriter(fml2, fml3); TRACE("dl", tout << mk_pp(fml, m) << " -> " << mk_pp(fml2, m) << " -> " << mk_pp(fml3, m) << "\n";); if (fml3 != fml) { fml = fml3; return true; } else { return false; } } public: impl(context& ctx): m_context(ctx), m(ctx.get_manager()), m_params(ctx.get_params().p), m_simplifier(ctx), m_blaster(ctx.get_manager(), m_params), m_rewriter(ctx.get_manager(), ctx) { m_params.set_bool("blast_full", true); m_params.set_bool("blast_quant", true); m_blaster.updt_params(m_params); } rule_set * operator()(rule_set const & source) { // TODO pc if (!m_context.xform_bit_blast()) { return 0; } rule_manager& rm = m_context.get_rule_manager(); unsigned sz = source.get_num_rules(); expr_ref fml(m); rule_set * result = alloc(rule_set, m_context); m_rewriter.m_cfg.set_src(&source); m_rewriter.m_cfg.set_dst(result); for (unsigned i = 0; !m_context.canceled() && i < sz; ++i) { rule * r = source.get_rule(i); rm.to_formula(*r, fml); if (blast(r, fml)) { proof_ref pr(m); if (r->get_proof()) { scoped_proof _sc(m); pr = m.mk_asserted(fml); // loses original proof of r. } // TODO add logic for pc: // 1. replace fresh predicates by non-bit-blasted predicates // 2. replace pr by the proof of r. rm.mk_rule(fml, pr, *result, r->name()); } else { result->add_rule(r); } } // copy output predicates without any rule (bit-blasting not really needed) const func_decl_set& decls = source.get_output_predicates(); for (func_decl_set::iterator I = decls.begin(), E = decls.end(); I != E; ++I) { if (!source.contains(*I)) result->set_output_predicate(*I); } if (m_context.get_model_converter()) { filter_model_converter* fmc = alloc(filter_model_converter, m); bit_blast_model_converter* bvmc = alloc(bit_blast_model_converter, m); func_decl_ref_vector const& old_funcs = m_rewriter.m_cfg.old_funcs(); func_decl_ref_vector const& new_funcs = m_rewriter.m_cfg.new_funcs(); for (unsigned i = 0; i < old_funcs.size(); ++i) { fmc->insert(new_funcs[i]); bvmc->insert(old_funcs[i], new_funcs[i]); } m_context.add_model_converter(concat(bvmc, fmc)); } return result; } }; mk_bit_blast::mk_bit_blast(context & ctx, unsigned priority) : plugin(priority) { m_impl = alloc(impl, ctx); } mk_bit_blast::~mk_bit_blast() { dealloc(m_impl); } rule_set * mk_bit_blast::operator()(rule_set const & source) { return (*m_impl)(source); } }; z3-4.4.0/src/muz/transforms/dl_mk_quantifier_abstraction.h0000644000175000017500000000303212540347414023522 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_quantifier_abstraction.h Abstract: Convert clauses with array arguments to predicates into Quantified Horn clauses. Author: Ken McMillan Andrey Rybalchenko Nikolaj Bjorner (nbjorner) 2013-04-02 Revision History: Based on approach suggested in SAS 2013 paper "On Solving Universally Quantified Horn Clauses" --*/ #ifndef _DL_MK_QUANTIFIER_ABSTRACTION_H_ #define _DL_MK_QUANTIFIER_ABSTRACTION_H_ #include"dl_rule_transformer.h" #include"array_decl_plugin.h" namespace datalog { class context; class mk_quantifier_abstraction : public rule_transformer::plugin { class qa_model_converter; ast_manager& m; context& m_ctx; array_util a; func_decl_ref_vector m_refs; obj_map m_new2old; obj_map m_old2new; qa_model_converter* m_mc; func_decl* declare_pred(rule_set const& rules, rule_set& dst, func_decl* old_p); app_ref mk_head(rule_set const& rules, rule_set& dst, app* p, unsigned idx); app_ref mk_tail(rule_set const& rules, rule_set& dst, app* p); expr* mk_select(expr* a, unsigned num_args, expr* const* args); public: mk_quantifier_abstraction(context & ctx, unsigned priority); virtual ~mk_quantifier_abstraction(); rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_QUANTIFIER_ABSTRACTION_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_interp_tail_simplifier.h0000644000175000017500000000574312540347414023532 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_interp_tail_simplifier.h Abstract: Rule transformer which simplifies interpreted tails Author: Krystof Hoder (t-khoder) 2011-10-01. Revision History: --*/ #ifndef _DL_MK_INTERP_TAIL_SIMPLIFIER_H_ #define _DL_MK_INTERP_TAIL_SIMPLIFIER_H_ #include "dl_context.h" #include "dl_rule_transformer.h" #include "unifier.h" #include "substitution.h" #include "arith_decl_plugin.h" namespace datalog { class mk_interp_tail_simplifier : public rule_transformer::plugin { class rule_substitution { ast_manager& m; context& m_context; substitution m_subst; unifier m_unif; app_ref m_head; app_ref_vector m_tail; svector m_neg; rule * m_rule; void apply(app * a, app_ref& res); public: rule_substitution(context & ctx) : m(ctx.get_manager()), m_context(ctx), m_subst(m), m_unif(m), m_head(m), m_tail(m), m_rule(0) {} /** Reset substitution and get it ready for working with rule r. As long as this object is used without a reset, the rule r must exist. */ void reset(rule * r); /** Reset subtitution and unify tail tgt_idx of the target rule and the head of the src rule */ bool unify(expr * e1, expr * e2); void get_result(rule_ref & res); void display(std::ostream& stm) { m_subst.display(stm); } }; class normalizer_cfg; class normalizer_rw; ast_manager & m; context & m_context; th_rewriter & m_simp; arith_util a; rule_substitution m_rule_subst; ptr_vector m_todo; obj_hashtable m_leqs; app_ref_vector m_tail; expr_ref_vector m_itail_members; expr_ref_vector m_conj; svector m_tail_neg; normalizer_cfg* m_cfg; normalizer_rw* m_rw; void simplify_expr(app * a, expr_ref& res); /** return true if some propagation was done */ bool propagate_variable_equivalences(rule * r, rule_ref& res); /** Return true if something was modified */ bool transform_rules(const rule_set & orig, rule_set & tgt); public: mk_interp_tail_simplifier(context & ctx, unsigned priority=40000); virtual ~mk_interp_tail_simplifier(); /**If rule should be retained, assign transformed version to res and return true; if rule can be deleted, return false. This method is kind of useful, so it's public to allow other rules to use it, e.g. on their intermediate results. */ bool transform_rule(rule * r, rule_ref& res); rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_INTERP_TAIL_SIMPLIFIER_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_magic_sets.cpp0000644000175000017500000003311312540347414021436 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_magic_sets.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-10-04. Revision History: --*/ #include #include #include"ast_pp.h" #include"dl_mk_magic_sets.h" namespace datalog { mk_magic_sets::mk_magic_sets(context & ctx, func_decl* goal) : plugin(10000, true), m_context(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_pinned(m), m_goal(goal, m) { } void mk_magic_sets::reset() { m_extentional.reset(); m_todo.reset(); m_adorned_preds.reset(); m_adornments.reset(); m_magic_preds.reset(); m_pinned.reset(); } void mk_magic_sets::adornment::populate(app * lit, const var_idx_set & bound_vars) { SASSERT(empty()); unsigned arity = lit->get_num_args(); for (unsigned i = 0; i < arity; i++) { const expr * arg = lit->get_arg(i); bool bound = !is_var(arg) || bound_vars.contains(to_var(arg)->get_idx()); push_back(bound ? AD_BOUND : AD_FREE); } } std::string mk_magic_sets::adornment::to_string() const { std::string res; const_iterator eit = begin(); const_iterator eend = end(); for (; eit != eend; ++eit) { res += (*eit == AD_BOUND)?'b':'f'; } return res; } unsigned get_bound_arg_count(app * lit, const var_idx_set & bound_vars) { unsigned res = 0; unsigned n = lit->get_num_args(); for (unsigned i = 0; i < n; i++) { const expr * arg = lit->get_arg(i); if (!is_var(arg) || bound_vars.contains(to_var(arg)->get_idx())) { SASSERT(is_var(arg) || is_app(arg)); SASSERT(!is_app(arg) || to_app(arg)->get_num_args()==0); res++; } } return res; } float mk_magic_sets::get_unbound_cost(app * lit, const var_idx_set & bound_vars) { func_decl * pred = lit->get_decl(); float res = 1; unsigned n = lit->get_num_args(); for (unsigned i = 0; i < n; i++) { const expr * arg = lit->get_arg(i); if (is_var(arg) && !bound_vars.contains(to_var(arg)->get_idx())) { res *= m_context.get_sort_size_estimate(pred->get_domain(i)); } //res-=1; } return res; } /** \brief From \c cont which is list of indexes of tail literals of rule \c r, select the index pointing to a literal with at least one bound variable that will be the next bound literal in the process of creating an adorned rule. If all literals are unbound, return -1. */ int mk_magic_sets::pop_bound(unsigned_vector & cont, rule * r, const var_idx_set & bound_vars) { float best_cost; int candidate_index = -1; unsigned n = cont.size(); for (unsigned i=0; iget_tail(cont[i]); unsigned bound_cnt = get_bound_arg_count(lit, bound_vars); if (bound_cnt==0) { continue; } float cost = get_unbound_cost(lit, bound_vars); if (candidate_index==-1 || cost(n-1)) { std::swap(cont[candidate_index], cont[n-1]); } unsigned res = cont.back(); cont.pop_back(); return res; } app * mk_magic_sets::adorn_literal(app * lit, const var_idx_set & bound_vars) { SASSERT(!m_extentional.contains(lit->get_decl())); func_decl * old_pred = lit->get_decl(); SASSERT(m.is_bool(old_pred->get_range())); adornment_desc adn(old_pred); adn.m_adornment.populate(lit, bound_vars); adornment_map::entry * e = m_adorned_preds.insert_if_not_there2(adn, 0); func_decl * new_pred = e->get_data().m_value; if (new_pred==0) { std::string suffix = "ad_"+adn.m_adornment.to_string(); new_pred = m_context.mk_fresh_head_predicate( old_pred->get_name(), symbol(suffix.c_str()), old_pred->get_arity(), old_pred->get_domain(), old_pred); m_pinned.push_back(new_pred); e->get_data().m_value = new_pred; m_todo.push_back(adn); m_adornments.insert(new_pred, adn.m_adornment); } app * res = m.mk_app(new_pred, lit->get_args()); m_pinned.push_back(res); return res; } app * mk_magic_sets::create_magic_literal(app * l) { func_decl * l_pred = l->get_decl(); SASSERT(m.is_bool(l_pred->get_range())); pred_adornment_map::obj_map_entry * ae = m_adornments.find_core(l_pred); SASSERT(ae); const adornment & adn = ae->get_data().m_value; unsigned l_arity = l->get_num_args(); ptr_vector bound_args; for (unsigned i=0; iget_arg(i)); } } pred2pred::obj_map_entry * e = m_magic_preds.insert_if_not_there2(l_pred, 0); func_decl * mag_pred = e->get_data().m_value; if (mag_pred==0) { unsigned mag_arity = bound_args.size(); ptr_vector mag_domain; for (unsigned i=0; iget_domain(i)); } } mag_pred = m_context.mk_fresh_head_predicate(l_pred->get_name(), symbol("ms"), mag_arity, mag_domain.c_ptr(), l_pred); m_pinned.push_back(mag_pred); e->get_data().m_value = mag_pred; } app * res = m.mk_app(mag_pred, bound_args.c_ptr()); m_pinned.push_back(res); return res; } void mk_magic_sets::create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated, rule_set& result) { //TODO: maybe include relevant interpreted predicates from the original rule ptr_vector new_tail; svector negations; new_tail.push_back(create_magic_literal(head)); new_tail.append(tail_cnt, tail); negations.push_back(false); negations.append(tail_cnt, negated); for (unsigned i=0; iget_decl())) { continue; } app * mag_head = create_magic_literal(tail[i]); rule * r = m_context.get_rule_manager().mk(mag_head, i+1, new_tail.c_ptr(), negations.c_ptr()); TRACE("dl", r->display(m_context,tout); ); result.add_rule(r); } } void mk_magic_sets::transform_rule(const adornment & head_adornment, rule * r, rule_set& result) { app * head = r->get_head(); unsigned head_len = head->get_num_args(); SASSERT(head_len==head_adornment.size()); var_idx_set bound_vars; for (unsigned i=0; iget_arg(i); if (head_adornment[i]==AD_BOUND && is_var(arg)) { bound_vars.insert(to_var(arg)->get_idx()); } } unsigned processed_tail_len = r->get_uninterpreted_tail_size(); unsigned_vector exten_tails; unsigned_vector inten_tails; for (unsigned i=0; iget_tail(i); if (m_extentional.contains(t->get_decl())) { exten_tails.push_back(i); } else { inten_tails.push_back(i); } } ptr_vector new_tail; svector negations; while (new_tail.size()!=processed_tail_len) { bool intentional = false; int curr_index = pop_bound(exten_tails, r, bound_vars); if (curr_index==-1) { curr_index = pop_bound(inten_tails, r,bound_vars); if (curr_index!=-1) { intentional = true; } } if (curr_index==-1) { if (!exten_tails.empty()) { curr_index = exten_tails.back(); exten_tails.pop_back(); } else { SASSERT(!inten_tails.empty()); curr_index = inten_tails.back(); inten_tails.pop_back(); intentional = true; } } SASSERT(curr_index!=-1); app * curr = r->get_tail(curr_index); if (intentional) { curr = adorn_literal(curr, bound_vars); } new_tail.push_back(curr); negations.push_back(r->is_neg_tail(curr_index)); bound_vars |= rm.collect_vars(curr); } func_decl * new_head_pred; VERIFY( m_adorned_preds.find(adornment_desc(head->get_decl(), head_adornment), new_head_pred) ); app * new_head = m.mk_app(new_head_pred, head->get_args()); SASSERT(new_tail.size()==r->get_uninterpreted_tail_size()); create_magic_rules(new_head, new_tail.size(), new_tail.c_ptr(), negations.c_ptr(), result); unsigned tail_len = r->get_tail_size(); for (unsigned i=processed_tail_len; iget_tail(i)); negations.push_back(r->is_neg_tail(i)); } new_tail.push_back(create_magic_literal(new_head)); negations.push_back(false); rule * nr = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(), negations.c_ptr()); result.add_rule(nr); nr->set_accounting_parent_object(m_context, r); } void mk_magic_sets::create_transfer_rule(const adornment_desc & d, rule_set& result) { func_decl * adn_pred = m_adorned_preds.find(d); unsigned arity = adn_pred->get_arity(); SASSERT(arity == d.m_pred->get_arity()); ptr_vector args; for (unsigned i=0; iget_domain(i))); } app * lit = m.mk_app(d.m_pred, args.c_ptr()); app * adn_lit = m.mk_app(adn_pred, args.c_ptr()); app * mag_lit = create_magic_literal(adn_lit); app * tail[] = {lit, mag_lit}; rule * r = m_context.get_rule_manager().mk(adn_lit, 2, tail, 0); result.add_rule(r); } rule_set * mk_magic_sets::operator()(rule_set const & source) { if (!m_context.magic_sets_for_queries()) { return 0; } SASSERT(source.contains(m_goal)); SASSERT(source.get_predicate_rules(m_goal).size() == 1); app * goal_head = source.get_predicate_rules(m_goal)[0]->get_head(); unsigned init_rule_cnt = source.get_num_rules(); { func_decl_set intentional; for (unsigned i=0; iget_decl(); intentional.insert(pred); } //now we iterate through all predicates and collect the set of extentional ones const rule_dependencies * deps; rule_dependencies computed_deps(m_context); if (source.is_closed()) { deps = &source.get_dependencies(); } else { computed_deps.populate(source); deps = &computed_deps; } rule_dependencies::iterator it = deps->begin(); rule_dependencies::iterator end = deps->end(); for (; it!=end; ++it) { func_decl * pred = it->m_key; if (intentional.contains(pred)) { continue; } SASSERT(it->m_value->empty());//extentional predicates have no dependency m_extentional.insert(pred); } } //adornment goal_adn; //goal_adn.populate(goal_head, ); var_idx_set empty_var_idx_set; adorn_literal(goal_head, empty_var_idx_set); rule_set * result = alloc(rule_set, m_context); result->inherit_predicates(source); while (!m_todo.empty()) { adornment_desc task = m_todo.back(); m_todo.pop_back(); const rule_vector & pred_rules = source.get_predicate_rules(task.m_pred); rule_vector::const_iterator it = pred_rules.begin(); rule_vector::const_iterator end = pred_rules.end(); for (; it != end; ++it) { rule * r = *it; transform_rule(task.m_adornment, r, *result); } if (!m_context.get_rel_context()->is_empty_relation(task.m_pred)) { //we need a rule to copy facts that are already in a relation into the adorned //relation (since out intentional predicates can have facts, not only rules) create_transfer_rule(task, *result); } } app * adn_goal_head = adorn_literal(goal_head, empty_var_idx_set); app * mag_goal_head = create_magic_literal(adn_goal_head); SASSERT(mag_goal_head->is_ground()); rule * mag_goal_rule = m_context.get_rule_manager().mk(mag_goal_head, 0, 0, 0); result->add_rule(mag_goal_rule); rule * back_to_goal_rule = m_context.get_rule_manager().mk(goal_head, 1, &adn_goal_head, 0); result->add_rule(back_to_goal_rule); return result; } }; z3-4.4.0/src/muz/transforms/dl_mk_backwards.cpp0000644000175000017500000000423712540347414021266 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_backwards.cpp Abstract: Create Horn clauses for backwards flow. Author: Nikolaj Bjorner (nbjorner) 2013-04-17 Revision History: --*/ #include"dl_mk_backwards.h" #include"dl_context.h" namespace datalog { mk_backwards::mk_backwards(context & ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), m_ctx(ctx) { } mk_backwards::~mk_backwards() { } rule_set * mk_backwards::operator()(rule_set const & source) { context& ctx = source.get_context(); rule_manager& rm = source.get_rule_manager(); rule_set * result = alloc(rule_set, ctx); unsigned sz = source.get_num_rules(); rule_ref new_rule(rm); app_ref_vector tail(m); app_ref head(m); svector neg; app_ref query(m); query = m.mk_fresh_const("Q", m.mk_bool_sort()); result->set_output_predicate(query->get_decl()); m_ctx.register_predicate(query->get_decl(), false); for (unsigned i = 0; i < sz; ++i) { tail.reset(); neg.reset(); rule & r = *source.get_rule(i); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); if (!source.is_output_predicate(r.get_decl())) { tail.push_back(r.get_head()); neg.push_back(false); } for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(r.get_tail(j)); neg.push_back(false); } for (unsigned j = 0; j <= utsz; ++j) { if (j == utsz && j > 0) { break; } if (j == utsz) { head = query; } else { head = r.get_tail(j); } new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); result->add_rule(new_rule); } } TRACE("dl", result->display(tout);); return result; } }; z3-4.4.0/src/muz/transforms/dl_mk_coalesce.cpp0000644000175000017500000001501412540347414021076 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_coalesce.cpp Abstract: Coalesce rules with shared bodies. Author: Nikolaj Bjorner (nbjorner) 2012-10-15 Revision History: Notes: Implements proof rule of the form: a(x) & q(x) -> p(x), b(y) & q(y) -> p(y) ---------------------------------------------- (a(z) \/ b(z)) & q(z) -> p(z) --*/ #include "dl_mk_coalesce.h" #include "bool_rewriter.h" namespace datalog { mk_coalesce::mk_coalesce(context& ctx): rule_transformer::plugin(50, false), m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_sub1(m), m_sub2(m), m_idx(0) {} void mk_coalesce::mk_pred(app_ref& pred, app* p1, app* p2) { SASSERT(p1->get_decl() == p2->get_decl()); unsigned sz = p1->get_num_args(); expr_ref_vector args(m); for (unsigned i = 0; i < sz; ++i) { expr* a = p1->get_arg(i); expr* b = p2->get_arg(i); SASSERT(m.get_sort(a) == m.get_sort(b)); m_sub1.push_back(a); m_sub2.push_back(b); args.push_back(m.mk_var(m_idx++, m.get_sort(a))); } pred = m.mk_app(p1->get_decl(), args.size(), args.c_ptr()); } void mk_coalesce::extract_conjs(expr_ref_vector const& sub, rule const& rl, expr_ref& result) { obj_map indices; bool_rewriter bwr(m); rule_ref r(const_cast(&rl), rm); ptr_vector sorts; expr_ref_vector revsub(m), conjs(m); rl.get_vars(m, sorts); revsub.resize(sorts.size()); svector valid(sorts.size(), true); for (unsigned i = 0; i < sub.size(); ++i) { expr* e = sub[i]; sort* s = m.get_sort(e); expr_ref w(m.mk_var(i, s), m); if (is_var(e)) { unsigned v = to_var(e)->get_idx(); SASSERT(v < valid.size()); if (sorts[v]) { SASSERT(s == sorts[v]); if (valid[v]) { revsub[v] = w; valid[v] = false; } else { SASSERT(revsub[v].get()); SASSERT(m.get_sort(revsub[v].get()) == s); conjs.push_back(m.mk_eq(revsub[v].get(), w)); } } } else { SASSERT(m.is_value(e)); SASSERT(m.get_sort(e) == m.get_sort(w)); conjs.push_back(m.mk_eq(e, w)); } } for (unsigned i = 0; i < sorts.size(); ++i) { if (valid[i] && sorts[i] && !revsub[i].get()) { revsub[i] = m.mk_var(m_idx++, sorts[i]); } } var_subst vs(m, false); for (unsigned i = r->get_uninterpreted_tail_size(); i < r->get_tail_size(); ++i) { vs(r->get_tail(i), revsub.size(), revsub.c_ptr(), result); conjs.push_back(result); } bwr.mk_and(conjs.size(), conjs.c_ptr(), result); } void mk_coalesce::merge_rules(rule_ref& tgt, rule const& src) { SASSERT(same_body(*tgt.get(), src)); m_sub1.reset(); m_sub2.reset(); m_idx = 0; app_ref pred(m), head(m); expr_ref fml1(m), fml2(m), fml(m); app_ref_vector tail(m); ptr_vector sorts1, sorts2; expr_ref_vector conjs1(m), conjs(m); rule_ref res(rm); bool_rewriter bwr(m); svector is_neg; tgt->get_vars(m, sorts1); src.get_vars(m, sorts2); mk_pred(head, src.get_head(), tgt->get_head()); for (unsigned i = 0; i < src.get_uninterpreted_tail_size(); ++i) { mk_pred(pred, src.get_tail(i), tgt->get_tail(i)); tail.push_back(pred); is_neg.push_back(src.is_neg_tail(i)); } extract_conjs(m_sub1, src, fml1); extract_conjs(m_sub2, *tgt.get(), fml2); bwr.mk_or(fml1, fml2, fml); SASSERT(is_app(fml)); tail.push_back(to_app(fml)); is_neg.push_back(false); res = rm.mk(head, tail.size(), tail.c_ptr(), is_neg.c_ptr(), tgt->name()); if (m_ctx.generate_proof_trace()) { rm.to_formula(src, fml1); rm.to_formula(*tgt.get(),fml2); rm.to_formula(*res.get(),fml); #if 0 sort* ps = m.mk_proof_sort(); sort* domain[3] = { ps, ps, m.mk_bool_sort() }; func_decl* merge = m.mk_func_decl(symbol("merge-clauses"), 3, domain, ps); // TBD: ad-hoc proof rule expr* args[3] = { m.mk_asserted(fml1), m.mk_asserted(fml2), fml }; // ...m_pc->insert(m.mk_app(merge, 3, args)); #else svector > pos; vector substs; proof* p = src.get_proof(); p = m.mk_hyper_resolve(1, &p, fml, pos, substs); res->set_proof(m, p); #endif } tgt = res; } bool mk_coalesce::same_body(rule const& r1, rule const& r2) const { SASSERT(r1.get_decl() == r2.get_decl()); unsigned sz = r1.get_uninterpreted_tail_size(); if (sz != r2.get_uninterpreted_tail_size()) { return false; } for (unsigned i = 0; i < sz; ++i) { if (r1.get_decl(i) != r2.get_decl(i)) { return false; } if (r1.is_neg_tail(i) != r2.is_neg_tail(i)) { return false; } } return true; } rule_set * mk_coalesce::operator()(rule_set const & source) { rule_set* rules = alloc(rule_set, m_ctx); rules->inherit_predicates(source); rule_set::decl2rules::iterator it = source.begin_grouped_rules(), end = source.end_grouped_rules(); for (; it != end; ++it) { rule_ref_vector d_rules(rm); d_rules.append(it->m_value->size(), it->m_value->c_ptr()); for (unsigned i = 0; i < d_rules.size(); ++i) { rule_ref r1(d_rules[i].get(), rm); for (unsigned j = i + 1; j < d_rules.size(); ++j) { if (same_body(*r1.get(), *d_rules[j].get())) { merge_rules(r1, *d_rules[j].get()); d_rules[j] = d_rules.back(); d_rules.pop_back(); --j; } } rules->add_rule(r1.get()); } } return rules; } }; z3-4.4.0/src/muz/transforms/dl_mk_scale.cpp0000644000175000017500000002024012540347414020404 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_scale.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2013-08-19 Revision History: --*/ #include"dl_mk_scale.h" #include"dl_context.h" #include"fixedpoint_params.hpp" namespace datalog { class mk_scale::scale_model_converter : public model_converter { ast_manager& m; func_decl_ref_vector m_trail; arith_util a; obj_map m_new2old; public: scale_model_converter(ast_manager& m): m(m), m_trail(m), a(m) {} virtual ~scale_model_converter() {} void add_new2old(func_decl* new_f, func_decl* old_f) { m_trail.push_back(old_f); m_trail.push_back(new_f); m_new2old.insert(new_f, old_f); } virtual void operator()(model_ref& md) { model_ref old_model = alloc(model, m); obj_map::iterator it = m_new2old.begin(); obj_map::iterator end = m_new2old.end(); for (; it != end; ++it) { func_decl* old_p = it->m_value; func_decl* new_p = it->m_key; func_interp* old_fi = alloc(func_interp, m, old_p->get_arity()); if (new_p->get_arity() == 0) { old_fi->set_else(md->get_const_interp(new_p)); } else { func_interp* new_fi = md->get_func_interp(new_p); expr_ref_vector subst(m); var_subst vs(m, false); expr_ref tmp(m); if (!new_fi) { TRACE("dl", tout << new_p->get_name() << " has no value in the current model\n";); dealloc(old_fi); continue; } for (unsigned i = 0; i < old_p->get_arity(); ++i) { subst.push_back(m.mk_var(i, old_p->get_domain(i))); } subst.push_back(a.mk_numeral(rational(1), a.mk_real())); // Hedge that we don't have to handle the general case for models produced // by Horn clause solvers. SASSERT(!new_fi->is_partial() && new_fi->num_entries() == 0); vs(new_fi->get_else(), subst.size(), subst.c_ptr(), tmp); old_fi->set_else(tmp); old_model->register_decl(old_p, old_fi); } } // register values that have not been scaled. unsigned sz = md->get_num_constants(); for (unsigned i = 0; i < sz; ++i) { func_decl* c = md->get_constant(i); if (!m_new2old.contains(c)) { old_model->register_decl(c, md->get_const_interp(c)); } } sz = md->get_num_functions(); for (unsigned i = 0; i < sz; ++i) { func_decl* f = md->get_function(i); if (!m_new2old.contains(f)) { func_interp* fi = md->get_func_interp(f); old_model->register_decl(f, fi->copy()); } } md = old_model; //TRACE("dl", model_smt2_pp(tout, m, *md, 0); ); } virtual model_converter * translate(ast_translation & translator) { UNREACHABLE(); return 0; } }; mk_scale::mk_scale(context & ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), m_ctx(ctx), a(m), m_trail(m), m_eqs(m) { } mk_scale::~mk_scale() { } rule_set * mk_scale::operator()(rule_set const & source) { if (!m_ctx.scale()) { return 0; } rule_manager& rm = source.get_rule_manager(); rule_set * result = alloc(rule_set, m_ctx); unsigned sz = source.get_num_rules(); rule_ref new_rule(rm); app_ref_vector tail(m); app_ref head(m); svector neg; ptr_vector vars; ref smc; if (m_ctx.get_model_converter()) { smc = alloc(scale_model_converter, m); } m_mc = smc.get(); for (unsigned i = 0; i < sz; ++i) { rule & r = *source.get_rule(i); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); tail.reset(); vars.reset(); m_cache.reset(); m_trail.reset(); m_eqs.reset(); r.get_vars(m, vars); unsigned num_vars = vars.size(); for (unsigned j = 0; j < utsz; ++j) { tail.push_back(mk_pred(num_vars, r.get_tail(j))); } for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(mk_constraint(num_vars, r.get_tail(j))); } app_ref new_pred = mk_pred(num_vars, r.get_head()); tail.append(m_eqs); tail.push_back(a.mk_gt(m.mk_var(num_vars, a.mk_real()), a.mk_numeral(rational(0), false))); neg.resize(tail.size(), false); new_rule = rm.mk(new_pred, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); result->add_rule(new_rule); if (source.is_output_predicate(r.get_decl())) { result->set_output_predicate(new_rule->get_decl()); } } TRACE("dl", result->display(tout);); if (m_mc) { m_ctx.add_model_converter(m_mc); } m_trail.reset(); m_cache.reset(); return result; } app_ref mk_scale::mk_pred(unsigned sigma_idx, app* q) { func_decl* f = q->get_decl(); ptr_vector domain(f->get_arity(), f->get_domain()); domain.push_back(a.mk_real()); func_decl_ref g(m); g = m.mk_func_decl(f->get_name(), f->get_arity() + 1, domain.c_ptr(), f->get_range()); expr_ref_vector args(m); for (unsigned i = 0; i < q->get_num_args(); ++i) { expr* arg = q->get_arg(i); rational val; if (a.is_numeral(arg, val)) { if (val.is_zero()) { // arg is unchanged. } else if (val.is_one()) { arg = m.mk_var(sigma_idx, a.mk_real()); } else { // create a fresh variable 'v', add 'v == sigma*arg' expr* v = m.mk_var(sigma_idx + 1 + m_eqs.size(), a.mk_real()); m_eqs.push_back(m.mk_eq(v, a.mk_mul(arg, m.mk_var(sigma_idx, a.mk_real())))); arg = v; } } args.push_back(arg); } args.push_back(m.mk_var(sigma_idx, a.mk_real())); m_ctx.register_predicate(g, false); if (m_mc) { m_mc->add_new2old(g, f); } return app_ref(m.mk_app(g, q->get_num_args() + 1, args.c_ptr()), m); } app_ref mk_scale::mk_constraint(unsigned sigma_idx, app* q) { expr* r = linearize(sigma_idx, q); SASSERT(is_app(r)); return app_ref(to_app(r), m); } expr* mk_scale::linearize(unsigned sigma_idx, expr* e) { expr* r; if (m_cache.find(e, r)) { return r; } if (!is_app(e)) { return e; } expr_ref result(m); app* ap = to_app(e); if (ap->get_family_id() == m.get_basic_family_id() || a.is_add(e) || a.is_sub(e) || a.is_le(e) || a.is_ge(e) || a.is_lt(e) || a.is_gt(e)) { expr_ref_vector args(m); for (unsigned i = 0; i < ap->get_num_args(); ++i) { args.push_back(linearize(sigma_idx, ap->get_arg(i))); } result = m.mk_app(ap->get_decl(), args.size(), args.c_ptr()); } else if (a.is_numeral(e)) { result = a.mk_mul(m.mk_var(sigma_idx, a.mk_real()), e); } else { result = e; } m_trail.push_back(result); m_cache.insert(e, result); return result; } }; z3-4.4.0/src/muz/transforms/dl_mk_coalesce.h0000644000175000017500000000225512540347414020546 0ustar michaelmichael /*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_coalesce.h Abstract: Coalesce rules with shared bodies. Author: Nikolaj Bjorner (nbjorner) 2012-10-15 Revision History: --*/ #ifndef _DL_MK_COALESCE_H_ #define _DL_MK_COALESCE_H_ #include"dl_context.h" #include"dl_rule_set.h" #include"uint_set.h" #include"dl_rule_transformer.h" #include"dl_mk_rule_inliner.h" namespace datalog { /** \brief Implements an unfolding transformation. */ class mk_coalesce : public rule_transformer::plugin { context& m_ctx; ast_manager& m; rule_manager& rm; expr_ref_vector m_sub1, m_sub2; unsigned m_idx; void mk_pred(app_ref& pred, app* p1, app* p2); void extract_conjs(expr_ref_vector const& sub, rule const& rl, expr_ref& result); bool same_body(rule const& r1, rule const& r2) const; void merge_rules(rule_ref& tgt, rule const& src); public: /** \brief Create coalesced rules. */ mk_coalesce(context & ctx); rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_COALESCE_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp0000644000175000017500000004642512540347414024067 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_interp_tail_simplifier.cpp Abstract: Rule transformer which simplifies interpreted tails Author: Krystof Hoder (t-khoder) 2011-10-01. Revision History: --*/ #include #include"ast_pp.h" #include"bool_rewriter.h" #include"rewriter.h" #include"rewriter_def.h" #include"dl_mk_rule_inliner.h" #include"dl_mk_interp_tail_simplifier.h" namespace datalog { // ----------------------------------- // // mk_interp_tail_simplifier::rule_substitution // // ----------------------------------- void mk_interp_tail_simplifier::rule_substitution::reset(rule * r) { unsigned var_cnt = m_context.get_rule_manager().get_counter().get_max_rule_var(*r)+1; m_subst.reset(); m_subst.reserve(1, var_cnt); m_rule = r; } bool mk_interp_tail_simplifier::rule_substitution::unify(expr * e1, expr * e2) { SASSERT(m_rule); //we need to apply the current substitution in order to ensure the unifier //works in an incremental way expr_ref e1_s(m); expr_ref e2_s(m); m_subst.apply(e1,e1_s); m_subst.apply(e2,e2_s); //and we need to reset the cache as we're going to modify the substitution m_subst.reset_cache(); return m_unif (e1_s, e2_s, m_subst, false); } void mk_interp_tail_simplifier::rule_substitution::apply(app * a, app_ref& res) { SASSERT(m_rule); expr_ref res_e(m); m_subst.apply(a, res_e); SASSERT(is_app(res_e.get())); res = to_app(res_e.get()); } void mk_interp_tail_simplifier::rule_substitution::get_result(rule_ref & res) { SASSERT(m_rule); apply(m_rule->get_head(), m_head); m_tail.reset(); m_neg.reset(); unsigned tail_len = m_rule->get_tail_size(); for (unsigned i=0; iget_tail(i), new_tail_el); m_tail.push_back(new_tail_el); m_neg.push_back(m_rule->is_neg_tail(i)); } mk_rule_inliner::remove_duplicate_tails(m_tail, m_neg); SASSERT(m_tail.size() == m_neg.size()); res = m_context.get_rule_manager().mk(m_head, m_tail.size(), m_tail.c_ptr(), m_neg.c_ptr()); res->set_accounting_parent_object(m_context, m_rule); res->norm_vars(res.get_manager()); } // ----------------------------------- // // mk_interp_tail_simplifier // // ----------------------------------- class mk_interp_tail_simplifier::normalizer_cfg : public default_rewriter_cfg { struct expr_cmp { ast_manager& m; expr_cmp(ast_manager& m) : m(m) {} bool operator()(expr * ae, expr * be) { return cmp_expr(ae, be, 4) == -1; } template static int cmp(T a, T b) { return (a>b) ? 1 : ((a == b) ? 0 : -1); } int cmp_expr(expr * ae, expr * be, int depth) { if (ae == be) { return 0; } //remove negations bool a_neg = m.is_not(ae, ae); bool b_neg = m.is_not(be, be); if (ae==be) { return cmp(a_neg, b_neg); } if (!is_app(ae) && !is_app(be)) { return cmp(ae->get_id(), be->get_id()); } if (!is_app(ae)) { return -1; } if (!is_app(be)) { return 1; } app * a = to_app(ae); app * b = to_app(be); if (a->get_decl()!=b->get_decl()) { return cmp(a->get_decl()->get_id(), b->get_decl()->get_id()); } if (a->get_num_args()!=b->get_num_args()) { return cmp(a->get_num_args(), b->get_num_args()); } if (depth==0) { return cmp(a->get_id(),b->get_id()); } unsigned arg_cnt = a->get_num_args(); unsigned neg_comparison = 0; for (unsigned i=0; iget_arg(i); expr * arg_b = b->get_arg(i); //we normalize away negations bool a_is_neg = m.is_not(arg_a, arg_a); bool b_is_neg = m.is_not(arg_b, arg_b); if (neg_comparison==0 && a_is_neg!=b_is_neg) { neg_comparison = a_is_neg ? -1 : 1; } int res = cmp_expr(arg_a, arg_b, depth-1); if (res!=0) { return res; } } if (neg_comparison!=0) { return neg_comparison; } //by normalizing away negation we may have put non-equal terms to be equal, so here we check return cmp(a->get_id(),b->get_id()); } }; ast_manager& m; bool_rewriter m_brwr; //instead of a local variable expr_ref_vector m_app_args; expr_cmp m_expr_cmp; public: normalizer_cfg(ast_manager& m) : m(m), m_brwr(m), m_app_args(m), m_expr_cmp(m) { } static void remove_duplicates(expr_ref_vector& v) { expr * a = v[0].get(); unsigned read_idx = 1; unsigned write_idx = 1; for (;;) { while(read_idx arg_pair; bool match_arg_pair(expr * e, arg_pair& pair, bool seek_conjunction) { if (seek_conjunction) { return m.is_and(e, pair.first, pair.second); } else { return m.is_or(e, pair.first, pair.second); } } /** If inside_disjunction is false, we're inside a conjunction (and arg pairs represent disjunctions). */ app * detect_equivalence(const arg_pair& p1, const arg_pair& p2, bool inside_disjunction) { if (m.is_not(p1.first)==m.is_not(p2.first)) { return 0; } if (m.is_not(p1.second)==m.is_not(p2.second)) { return 0; } expr * first_bare = 0; if (m.is_not(p1.first, first_bare) && p2.first!=first_bare) { return 0; } if (m.is_not(p2.first, first_bare) && p1.first!=first_bare) { return 0; } SASSERT(first_bare); expr * second_bare = 0; if (m.is_not(p1.second, second_bare) && p2.second!=second_bare) { return 0; } if (m.is_not(p2.second, second_bare) && p1.second!=second_bare) { return 0; } SASSERT(second_bare); if (!m.is_bool(first_bare) || !m.is_bool(second_bare)) { return 0; } //both negations are in the same pair bool negs_together = m.is_not(p1.first)==m.is_not(p1.second); if (negs_together==inside_disjunction) { return m.mk_eq(first_bare, second_bare); } else { return m.mk_eq(first_bare, m.mk_not(second_bare)); } } bool detect_equivalences(expr_ref_vector& v, bool inside_disjunction) { bool have_pair = false; unsigned prev_pair_idx; arg_pair ap; unsigned read_idx = 0; unsigned write_idx = 0; while(read_idxget_num_args(); ++i) { m_brwr.mk_not(a->get_arg(i), tmp); m_app_args.push_back(tmp); } if (m.is_and(args[0])) { result = m.mk_or(m_app_args.size(), m_app_args.c_ptr()); } else { result = m.mk_and(m_app_args.size(), m_app_args.c_ptr()); } return BR_REWRITE2; } if (!m.is_and(f) && !m.is_or(f)) { return BR_FAILED; } if (num == 0) { if (m.is_and(f)) { result = m.mk_true(); } else { result = m.mk_false(); } return BR_DONE; } if (num == 1) { result = args[0]; return BR_DONE; } m_app_args.reset(); m_app_args.append(num, args); std::sort(m_app_args.c_ptr(), m_app_args.c_ptr()+m_app_args.size(), m_expr_cmp); remove_duplicates(m_app_args); bool have_rewritten_args = false; have_rewritten_args = detect_equivalences(m_app_args, m.is_or(f)); if (m_app_args.size()==1) { result = m_app_args[0].get(); } else { if (m.is_and(f)) { result = m.mk_and(m_app_args.size(), m_app_args.c_ptr()); } else { SASSERT(m.is_or(f)); result = m.mk_or(m_app_args.size(), m_app_args.c_ptr()); } } if (have_rewritten_args) { return BR_REWRITE1; } return BR_DONE; } }; class mk_interp_tail_simplifier::normalizer_rw : public rewriter_tpl { public: normalizer_rw(ast_manager& m, normalizer_cfg& cfg): rewriter_tpl(m, false, cfg) {} }; mk_interp_tail_simplifier::mk_interp_tail_simplifier(context & ctx, unsigned priority) : plugin(priority), m(ctx.get_manager()), m_context(ctx), m_simp(ctx.get_rewriter()), a(m), m_rule_subst(ctx), m_tail(m), m_itail_members(m), m_conj(m) { m_cfg = alloc(normalizer_cfg, m); m_rw = alloc(normalizer_rw, m, *m_cfg); } mk_interp_tail_simplifier::~mk_interp_tail_simplifier() { dealloc(m_rw); dealloc(m_cfg); } void mk_interp_tail_simplifier::simplify_expr(app * a, expr_ref& res) { expr_ref simp1_res(m); m_simp(a, simp1_res); (*m_rw)(simp1_res.get(), res); /*if (simp1_res.get()!=res.get()) { std::cout<<"pre norm:\n"<get_uninterpreted_tail_size(); unsigned len = r->get_tail_size(); if (u_len == len) { return false; } m_todo.reset(); m_leqs.reset(); for (unsigned i = u_len; i < len; i++) { m_todo.push_back(r->get_tail(i)); SASSERT(!r->is_neg_tail(i)); } m_rule_subst.reset(r); expr_ref_vector trail(m); expr_ref tmp1(m), tmp2(m); bool found_something = false; #define TRY_UNIFY(_x,_y) if (m_rule_subst.unify(_x,_y)) { found_something = true; } #define IS_FLEX(_x) (is_var(_x) || m.is_value(_x)) while (!m_todo.empty()) { expr * arg1, *arg2; expr * t0 = m_todo.back(); m_todo.pop_back(); expr* t = t0; bool neg = m.is_not(t, t); if (is_var(t)) { TRY_UNIFY(t, neg ? m.mk_false() : m.mk_true()); } else if (!neg && m.is_and(t)) { app* a = to_app(t); m_todo.append(a->get_num_args(), a->get_args()); } else if (!neg && m.is_eq(t, arg1, arg2) && IS_FLEX(arg1) && IS_FLEX(arg2)) { TRY_UNIFY(arg1, arg2); } else if (m.is_iff(t, arg1, arg2)) { //determine the polarity of the equivalence and remove the negations while (m.is_not(arg1, arg1)) neg = !neg; while (m.is_not(arg2, arg2)) neg = !neg; if (!is_var(arg1)) { std::swap(arg1, arg2); } if (!IS_FLEX(arg1) || !IS_FLEX(arg2)) { // no-op } else if (is_var(arg1) && !neg) { TRY_UNIFY(arg1, arg2); } else if (is_var(arg1) && neg && m.is_true(arg2)) { TRY_UNIFY(arg1, m.mk_false()); } else if (is_var(arg1) && neg && m.is_false(arg2)) { TRY_UNIFY(arg1, m.mk_true()); } } else if (!neg && (a.is_le(t, arg1, arg2) || a.is_ge(t, arg2, arg1))) { tmp1 = a.mk_sub(arg1, arg2); tmp2 = a.mk_sub(arg2, arg1); if (false && m_leqs.contains(tmp2) && IS_FLEX(arg1) && IS_FLEX(arg2)) { TRY_UNIFY(arg1, arg2); } else { trail.push_back(tmp1); m_leqs.insert(tmp1); } } } if (!found_something) { return false; } TRACE("dl_interp_tail_simplifier_propagation_pre", tout << "will propagate rule:\n"; r->display(m_context, tout); ); m_rule_subst.get_result(res); TRACE("dl_interp_tail_simplifier_propagation", tout << "propagated equivalences of:\n"; r->display(m_context, tout); tout << "into:\n"; res->display(m_context, tout); ); return true; } bool mk_interp_tail_simplifier::transform_rule(rule * r0, rule_ref & res) { rule_manager& rm = m_context.get_rule_manager(); rule_ref r(r0, rm); if (rm.has_quantifiers(*r)) { res = r; return true; } start: unsigned u_len = r->get_uninterpreted_tail_size(); unsigned len = r->get_tail_size(); if (u_len == len) { res = r; return true; } app_ref head(r->get_head(), m); m_tail.reset(); m_tail_neg.reset(); for (unsigned i=0; iget_tail(i)); m_tail_neg.push_back(r->is_neg_tail(i)); } bool modified = false; app_ref itail(m); if (u_len+1==len) { //we have only one interpreted tail itail = r->get_tail(u_len); SASSERT(!r->is_neg_tail(u_len)); } else { m_itail_members.reset(); for (unsigned i=u_len; iget_tail(i)); SASSERT(!r->is_neg_tail(i)); } itail = m.mk_and(m_itail_members.size(), m_itail_members.c_ptr()); modified = true; } expr_ref simp_res(m); simplify_expr(itail.get(), simp_res); modified |= itail.get() != simp_res.get(); if (m.is_false(simp_res)) { TRACE("dl", r->display(m_context, tout << "rule is infeasible\n");); return false; } SASSERT(m.is_bool(simp_res)); if (modified) { m_conj.reset(); qe::flatten_and(simp_res, m_conj); for (unsigned i = 0; i < m_conj.size(); ++i) { expr* e = m_conj[i].get(); if (is_app(e)) { m_tail.push_back(to_app(e)); } else { m_tail.push_back(m.mk_eq(e, m.mk_true())); } m_tail_neg.push_back(false); } SASSERT(m_tail.size() == m_tail_neg.size()); res = m_context.get_rule_manager().mk(head, m_tail.size(), m_tail.c_ptr(), m_tail_neg.c_ptr()); res->set_accounting_parent_object(m_context, r); } else { res = r; } rule_ref pro_var_eq_result(m_context.get_rule_manager()); if (propagate_variable_equivalences(res, pro_var_eq_result)) { SASSERT(rule_counter().get_max_rule_var(*r.get())==0 || rule_counter().get_max_rule_var(*r.get()) > rule_counter().get_max_rule_var(*pro_var_eq_result.get())); r = pro_var_eq_result; goto start; } CTRACE("dl", (res != r0), r0->display(m_context, tout << "old:\n"); res->display(m_context, tout << "new:\n");); return true; } bool mk_interp_tail_simplifier::transform_rules(const rule_set & orig, rule_set & tgt) { bool modified = false; rule_manager& rm = m_context.get_rule_manager(); rule_set::iterator rit = orig.begin(); rule_set::iterator rend = orig.end(); for (; rit!=rend; ++rit) { rule_ref new_rule(rm); if (transform_rule(*rit, new_rule)) { rm.mk_rule_rewrite_proof(**rit, *new_rule.get()); bool is_modified = *rit != new_rule; modified |= is_modified; tgt.add_rule(new_rule); } else { modified = true; } } return modified; } rule_set * mk_interp_tail_simplifier::operator()(rule_set const & source) { if (source.get_num_rules() == 0) { return 0; } rule_set * res = alloc(rule_set, m_context); if (transform_rules(source, *res)) { res->inherit_predicates(source); } else { dealloc(res); res = 0; } return res; } }; z3-4.4.0/src/muz/transforms/dl_mk_loop_counter.h0000644000175000017500000000214712540347414021500 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_loop_counter.h Abstract: Add loop counter argument to relations. Author: Nikolaj Bjorner (nbjorner) 2013-03-31 Revision History: --*/ #ifndef _DL_MK_LOOP_COUNTER_H_ #define _DL_MK_LOOP_COUNTER_H_ #include"dl_rule_transformer.h" #include"arith_decl_plugin.h" namespace datalog { class mk_loop_counter : public rule_transformer::plugin { ast_manager& m; context& m_ctx; arith_util a; func_decl_ref_vector m_refs; obj_map m_new2old; obj_map m_old2new; app_ref add_arg(rule_set const& src, rule_set& dst, app* fn, unsigned idx); app_ref del_arg(app* fn); public: mk_loop_counter(context & ctx, unsigned priority = 33000); ~mk_loop_counter(); rule_set * operator()(rule_set const & source); func_decl* get_old(func_decl* f) const { return m_new2old.find(f); } rule_set * revert(rule_set const& source); }; }; #endif /* _DL_MK_LOOP_COUNTER_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_backwards.h0000644000175000017500000000121112540347414020720 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_backwards.h Abstract: Create Horn clauses for backwards flow. Author: Nikolaj Bjorner (nbjorner) 2013-04-17 Revision History: --*/ #ifndef _DL_MK_BACKWARDS_H_ #define _DL_MK_BACKWARDS_H_ #include"dl_rule_transformer.h" namespace datalog { class mk_backwards : public rule_transformer::plugin { ast_manager& m; context& m_ctx; public: mk_backwards(context & ctx, unsigned priority = 33000); ~mk_backwards(); rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_BACKWARDS_H_ */ z3-4.4.0/src/muz/transforms/dl_mk_array_blast.cpp0000644000175000017500000002367012540347414021632 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_array_blast.cpp Abstract: Remove array stores from rules. Author: Nikolaj Bjorner (nbjorner) 2012-11-23 Revision History: --*/ #include "dl_mk_array_blast.h" #include "qe_util.h" #include "scoped_proof.h" namespace datalog { mk_array_blast::mk_array_blast(context & ctx, unsigned priority) : rule_transformer::plugin(priority, false), m_ctx(ctx), m(ctx.get_manager()), a(m), rm(ctx.get_rule_manager()), m_rewriter(m, m_params), m_simplifier(ctx), m_next_var(0) { m_params.set_bool("expand_select_store",true); m_rewriter.updt_params(m_params); } mk_array_blast::~mk_array_blast() { } bool mk_array_blast::is_store_def(expr* e, expr*& x, expr*& y) { if (m.is_iff(e, x, y) || m.is_eq(e, x, y)) { if (!a.is_store(y)) { std::swap(x,y); } if (is_var(x) && a.is_store(y)) { return true; } } return false; } expr* mk_array_blast::get_select(expr* e) const { while (a.is_select(e)) { e = to_app(e)->get_arg(0); } return e; } void mk_array_blast::get_select_args(expr* e, ptr_vector& args) const { while (a.is_select(e)) { app* ap = to_app(e); for (unsigned i = 1; i < ap->get_num_args(); ++i) { args.push_back(ap->get_arg(i)); } e = ap->get_arg(0); } } bool mk_array_blast::insert_def(rule const& r, app* e, var* v) { // // For the Ackermann reduction we would like the arrays // to be variables, so that variables can be // assumed to represent difference (alias) // classes. Ehm., Soundness of this approach depends on // if the arrays are finite domains... // if (!is_var(get_select(e))) { return false; } if (v) { m_defs.insert(e, to_var(v)); } else { if (m_next_var == 0) { ptr_vector vars; r.get_vars(m, vars); m_next_var = vars.size() + 1; } v = m.mk_var(m_next_var, m.get_sort(e)); m_defs.insert(e, v); ++m_next_var; } return true; } bool mk_array_blast::is_select_eq_var(expr* e, app*& s, var*& v) const { expr* x, *y; if (m.is_eq(e, x, y) || m.is_iff(e, x, y)) { if (a.is_select(y)) { std::swap(x,y); } if (a.is_select(x) && is_var(y)) { s = to_app(x); v = to_var(y); return true; } } return false; } bool mk_array_blast::ackermanize(rule const& r, expr_ref& body, expr_ref& head) { expr_ref_vector conjs(m), trail(m); qe::flatten_and(body, conjs); m_defs.reset(); m_next_var = 0; ptr_vector todo; obj_map cache; ptr_vector args; app_ref e1(m); app* s; var* v; for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); if (is_select_eq_var(e, s, v)) { todo.append(s->get_num_args(), s->get_args()); } else { todo.push_back(e); } } while (!todo.empty()) { expr* e = todo.back(); if (cache.contains(e)) { todo.pop_back(); continue; } if (is_var(e)) { cache.insert(e, e); todo.pop_back(); continue; } if (!is_app(e)) { return false; } app* ap = to_app(e); bool valid = true; args.reset(); for (unsigned i = 0; i < ap->get_num_args(); ++i) { expr* arg; if (cache.find(ap->get_arg(i), arg)) { args.push_back(arg); } else { todo.push_back(ap->get_arg(i)); valid = false; } } if (valid) { todo.pop_back(); e1 = m.mk_app(ap->get_decl(), args.size(), args.c_ptr()); trail.push_back(e1); if (a.is_select(ap)) { if (m_defs.find(e1, v)) { cache.insert(e, v); } else if (!insert_def(r, e1, 0)) { return false; } else { cache.insert(e, m_defs.find(e1)); } } else { cache.insert(e, e1); } } } for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); if (is_select_eq_var(e, s, v)) { args.reset(); for (unsigned j = 0; j < s->get_num_args(); ++j) { args.push_back(cache.find(s->get_arg(j))); } e1 = m.mk_app(s->get_decl(), args.size(), args.c_ptr()); if (!m_defs.contains(e1) && !insert_def(r, e1, v)) { return false; } conjs[i] = m.mk_eq(v, m_defs.find(e1)); } else { conjs[i] = cache.find(e); } } // perform the Ackermann reduction by creating implications // i1 = i2 => val1 = val2 for each equality pair: // (= val1 (select a_i i1)) // (= val2 (select a_i i2)) defs_t::iterator it1 = m_defs.begin(), end = m_defs.end(); for (; it1 != end; ++it1) { app* a1 = it1->m_key; var* v1 = it1->m_value; defs_t::iterator it2 = it1; ++it2; for (; it2 != end; ++it2) { app* a2 = it2->m_key; var* v2 = it2->m_value; TRACE("dl", tout << mk_pp(a1, m) << " " << mk_pp(a2, m) << "\n";); if (get_select(a1) != get_select(a2)) { continue; } expr_ref_vector eqs(m); ptr_vector args1, args2; get_select_args(a1, args1); get_select_args(a2, args2); for (unsigned j = 0; j < args1.size(); ++j) { eqs.push_back(m.mk_eq(args1[j], args2[j])); } conjs.push_back(m.mk_implies(m.mk_and(eqs.size(), eqs.c_ptr()), m.mk_eq(v1, v2))); } } body = m.mk_and(conjs.size(), conjs.c_ptr()); m_rewriter(body); return true; } bool mk_array_blast::blast(rule& r, rule_set& rules) { unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); expr_ref_vector conjs(m), new_conjs(m); expr_ref tmp(m); expr_safe_replace sub(m); bool change = false; bool inserted = false; for (unsigned i = 0; i < utsz; ++i) { new_conjs.push_back(r.get_tail(i)); } for (unsigned i = utsz; i < tsz; ++i) { conjs.push_back(r.get_tail(i)); } qe::flatten_and(conjs); for (unsigned i = 0; i < conjs.size(); ++i) { expr* x, *y, *e = conjs[i].get(); if (is_store_def(e, x, y)) { // enforce topological order consistency: uint_set lhs = rm.collect_vars(x); uint_set rhs_vars = rm.collect_vars(y); lhs &= rhs_vars; if (!lhs.empty()) { TRACE("dl", tout << "unusable equality " << mk_pp(e, m) << "\n";); new_conjs.push_back(e); } else { sub.insert(x, y); inserted = true; } } else { m_rewriter(e, tmp); new_conjs.push_back(tmp); } } if (!inserted) { rules.add_rule(&r); return false; } expr_ref fml1(m), fml2(m), body(m), head(m); body = m.mk_and(new_conjs.size(), new_conjs.c_ptr()); head = r.get_head(); sub(body); m_rewriter(body); sub(head); m_rewriter(head); change = ackermanize(r, body, head); if (!change) { rules.add_rule(&r); return false; } fml2 = m.mk_implies(body, head); proof_ref p(m); rule_set new_rules(m_ctx); TRACE("dl", tout << fml2 << "\n";); rm.mk_rule(fml2, p, new_rules, r.name()); rule_ref new_rule(rm); if (m_simplifier.transform_rule(new_rules.last(), new_rule)) { if (r.get_proof()) { scoped_proof _sc(m); rm.to_formula(r, fml1); p = m.mk_rewrite(fml1, fml2); p = m.mk_modus_ponens(r.get_proof(), p); new_rule->set_proof(m, p); } rules.add_rule(new_rule.get()); rm.mk_rule_rewrite_proof(r, *new_rule.get()); TRACE("dl", new_rule->display(m_ctx, tout << "new rule\n");); } return true; } rule_set * mk_array_blast::operator()(rule_set const & source) { rule_set* rules = alloc(rule_set, m_ctx); rules->inherit_predicates(source); rule_set::iterator it = source.begin(), end = source.end(); bool change = false; for (; !m_ctx.canceled() && it != end; ++it) { change = blast(**it, *rules) || change; } if (!change) { dealloc(rules); rules = 0; } return rules; } }; z3-4.4.0/src/muz/transforms/dl_mk_subsumption_checker.cpp0000644000175000017500000003025312540347414023376 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_subsumption_checker.cpp Abstract: Rule transformer which checks for subsumption (currently just for subsumption with total relations) Author: Krystof Hoder (t-khoder) 2011-10-01. Revision History: --*/ #include #include"ast_pp.h" #include "rewriter.h" #include "rewriter_def.h" #include"dl_mk_subsumption_checker.h" namespace datalog { // ----------------------------------- // // mk_subsumption_checker // // ----------------------------------- bool mk_subsumption_checker::is_total_rule(const rule * r) { if(r->get_tail_size()!=0) { return false; } unsigned pt_len = r->get_positive_tail_size(); if(pt_len!=r->get_uninterpreted_tail_size()) { //we dont' expect rules with negative tails to be total return false; } for(unsigned i=0; iget_tail(i)->get_decl(); if(!m_total_relations.contains(tail_pred)) { //this rule has a non-total predicate in the tail return false; } } unsigned t_len = r->get_positive_tail_size(); for(unsigned i=pt_len; iis_neg_tail(i)); //we assume interpreted tail not to be negated if(!m.is_true(r->get_tail(i))) { //this rule has an interpreted tail which is not constant true return false; } } var_idx_set head_vars; app * head = r->get_head(); unsigned arity = head->get_num_args(); for(unsigned i=0; iget_arg(i); if(!is_var(arg)) { return false; } unsigned idx = to_var(arg)->get_idx(); if(head_vars.contains(idx)) { return false; } head_vars.insert(idx); } SASSERT(head_vars.num_elems()==arity); return true; } void mk_subsumption_checker::on_discovered_total_relation(func_decl * pred, rule * r) { //this should be rule marking a new relation as total SASSERT(!m_total_relations.contains(pred)); SASSERT(!r || pred==r->get_decl()); SASSERT(!r || is_total_rule(r)); m_total_relations.insert(pred); m_total_relation_defining_rules.insert(pred, r); m_have_new_total_rule = true; if(r) { m_ref_holder.push_back(r); } } void mk_subsumption_checker::scan_for_total_rules(const rule_set & rules) { bool new_discovered; //we cycle through the rules until we keep discovering new total relations //(discovering a total relation migh reveal other total relations) do { new_discovered = false; rule_set::iterator rend = rules.end(); for(rule_set::iterator rit = rules.begin(); rit!=rend; ++rit) { rule * r = *rit; func_decl * head_pred = r->get_decl(); if(is_total_rule(r) && !m_total_relations.contains(head_pred)) { on_discovered_total_relation(head_pred, r); new_discovered = true; } } } while(new_discovered); } bool mk_subsumption_checker::transform_rule(rule * r, rule_subsumption_index& subs_index, rule_ref & res) { unsigned u_len = r->get_uninterpreted_tail_size(); unsigned len = r->get_tail_size(); if(u_len==0) { res = r; return true; } app_ref head(r->get_head(), m); app_ref_vector tail(m); svector tail_neg; for(unsigned i=0; iget_tail(i); bool neg = r->is_neg_tail(i); if(m_total_relations.contains(tail_atom->get_decl()) || subs_index.is_subsumed(tail_atom)) { if(neg) { //rule contains negated total relation, this means that it is unsatisfiable //and can be removed return false; } else { //we remove total relations from the tail continue; } } if(!neg && head.get()==tail_atom) { //rule contains its head positively in the tail, therefore //it will never add any new facts to the relation, so it //can be removed return false; } tail.push_back(tail_atom); tail_neg.push_back(neg); } if(tail.size()==u_len) { res = r; return true; } //we just copy the interpreted part of the tail for(unsigned i=u_len; iget_tail(i)); tail_neg.push_back(r->is_neg_tail(i)); } SASSERT(tail.size()==tail_neg.size()); res = m_context.get_rule_manager().mk(head, tail.size(), tail.c_ptr(), tail_neg.c_ptr()); res->set_accounting_parent_object(m_context, r); m_context.get_rule_manager().fix_unbound_vars(res, true); m_context.get_rule_manager().mk_rule_rewrite_proof(*r, *res.get()); return true; } bool rule_size_comparator(rule * r1, rule * r2) { return r1->get_tail_size() < r2->get_tail_size(); } bool mk_subsumption_checker::transform_rules(const rule_set & orig, rule_set & tgt) { bool modified = false; func_decl_set total_relations_with_included_rules; rule_subsumption_index subs_index(m_context); rule_ref_vector orig_rules(m_context.get_rule_manager()); orig_rules.append(orig.get_num_rules(), orig.begin()); rule * * rbegin = orig_rules.c_ptr(); rule * * rend = rbegin + orig_rules.size(); //before traversing we sort rules so that the shortest are in the beginning. //this will help make subsumption checks more efficient std::sort(rbegin, rend, rule_size_comparator); for(rule_set::iterator rit = rbegin; rit!=rend; ++rit) { rule * r = *rit; func_decl * head_pred = r->get_decl(); if(m_total_relations.contains(head_pred)) { if(!orig.is_output_predicate(head_pred) || total_relations_with_included_rules.contains(head_pred)) { //We just skip definitions of total non-output relations as //we'll eliminate them from the problem. //We also skip rules of total output relations for which we have //already output the rule which implies their totality. modified = true; continue; } rule * defining_rule; VERIFY(m_total_relation_defining_rules.find(head_pred, defining_rule)); if (defining_rule) { rule_ref totality_rule(m_context.get_rule_manager()); VERIFY(transform_rule(defining_rule, subs_index, totality_rule)); if(defining_rule!=totality_rule) { modified = true; } tgt.add_rule(totality_rule); SASSERT(totality_rule->get_decl()==head_pred); } else { modified = true; } total_relations_with_included_rules.insert(head_pred); continue; } rule_ref new_rule(m_context.get_rule_manager()); if(!transform_rule(r, subs_index, new_rule)) { modified = true; continue; } if(m_new_total_relation_discovery_during_transformation && is_total_rule(new_rule)) { on_discovered_total_relation(head_pred, new_rule.get()); } if(subs_index.is_subsumed(new_rule)) { modified = true; continue; } if(new_rule.get()!=r) { modified = true; } tgt.add_rule(new_rule); subs_index.add(new_rule); } tgt.inherit_predicates(orig); TRACE("dl", tout << "original set size: "<try_get_size(pred, rel_sz)) { continue; } unsigned arity = pred->get_arity(); if (arity > 30) { continue; } //for now we only check booleans domains for(unsigned i=0; iget_domain(i))) { goto next_pred; } } { unsigned total_size = 1< * head_store; if(m_ground_unconditional_rule_heads.find(pred, head_store)) { //Some relations may receive facts by ground unconditioned rules. //We scanned for those earlier, so now we check whether we cannot get a //better estimate of relation size from these. unsigned gnd_rule_cnt = head_store->size(); if(gnd_rule_cnt>rel_sz) { rel_sz = gnd_rule_cnt; } } SASSERT(total_size>=rel_sz); if(total_size==rel_sz) { on_discovered_total_relation(pred, 0); } } next_pred:; } } void mk_subsumption_checker::collect_ground_unconditional_rule_heads(const rule_set & rules) { rule_set::iterator rend = rules.end(); for(rule_set::iterator rit = rules.begin(); rit!=rend; ++rit) { rule * r = *rit; func_decl * pred = r->get_decl(); if(r->get_tail_size()!=0) { continue; } app * head = r->get_head(); unsigned arity = pred->get_arity(); for(unsigned i=0; iget_arg(i); if(!is_app(arg)) { goto next_rule; } } if(!m_ground_unconditional_rule_heads.contains(pred)) { m_ground_unconditional_rule_heads.insert(pred, alloc(obj_hashtable)); } m_ground_unconditional_rule_heads.find(pred)->insert(head); next_rule:; } } rule_set * mk_subsumption_checker::operator()(rule_set const & source) { // TODO mc m_have_new_total_rule = false; collect_ground_unconditional_rule_heads(source); scan_for_relations_total_due_to_facts(source); scan_for_total_rules(source); m_have_new_total_rule = false; rule_set * res = alloc(rule_set, m_context); bool modified = transform_rules(source, *res); if (!m_have_new_total_rule && !modified) { dealloc(res); return 0; } //During the construction of the new set we may discover new total relations //(by quantifier elimination on the uninterpreted tails). SASSERT(m_new_total_relation_discovery_during_transformation || !m_have_new_total_rule); while (m_have_new_total_rule) { m_have_new_total_rule = false; rule_set * old = res; res = alloc(rule_set, m_context); transform_rules(*old, *res); dealloc(old); } return res; } }; z3-4.4.0/src/muz/transforms/dl_mk_unfold.cpp0000644000175000017500000000355012540347414020611 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_unfold.cpp Abstract: Unfold rules once, return the unfolded set of rules. Author: Nikolaj Bjorner (nbjorner) 2012-10-15 Revision History: --*/ #include "dl_mk_unfold.h" namespace datalog { mk_unfold::mk_unfold(context& ctx): rule_transformer::plugin(100, false), m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_unify(ctx) {} void mk_unfold::expand_tail(rule& r, unsigned tail_idx, rule_set const& src, rule_set& dst) { SASSERT(tail_idx <= r.get_uninterpreted_tail_size()); if (tail_idx == r.get_uninterpreted_tail_size()) { dst.add_rule(&r); } else { func_decl* p = r.get_decl(tail_idx); rule_vector const& p_rules = src.get_predicate_rules(p); rule_ref new_rule(rm); for (unsigned i = 0; i < p_rules.size(); ++i) { rule const& r2 = *p_rules[i]; if (m_unify.unify_rules(r, tail_idx, r2) && m_unify.apply(r, tail_idx, r2, new_rule)) { expr_ref_vector s1 = m_unify.get_rule_subst(r, true); expr_ref_vector s2 = m_unify.get_rule_subst(r2, false); resolve_rule(rm, r, r2, tail_idx, s1, s2, *new_rule.get()); expand_tail(*new_rule.get(), tail_idx+r2.get_uninterpreted_tail_size(), src, dst); } } } } rule_set * mk_unfold::operator()(rule_set const & source) { rule_set* rules = alloc(rule_set, m_ctx); rule_set::iterator it = source.begin(), end = source.end(); for (; it != end; ++it) { expand_tail(**it, 0, source, *rules); } rules->inherit_predicates(source); return rules; } }; z3-4.4.0/src/muz/transforms/dl_mk_quantifier_abstraction.cpp0000644000175000017500000003157212540347414024067 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_quantifier_abstraction.cpp Abstract: Create quantified Horn clauses from benchmarks with arrays. Author: Ken McMillan Andrey Rybalchenko Nikolaj Bjorner (nbjorner) 2013-04-02 Revision History: --*/ #include "dl_mk_quantifier_abstraction.h" #include "dl_context.h" #include "expr_safe_replace.h" #include "expr_abstract.h" #include"fixedpoint_params.hpp" namespace datalog { // model converter: // Given model for P^(x, y, i, a[i]) // create model: P(x,y,a) == forall i . P^(x,y,i,a[i]) // requires substitution and list of bound variables. class mk_quantifier_abstraction::qa_model_converter : public model_converter { ast_manager& m; func_decl_ref_vector m_old_funcs; func_decl_ref_vector m_new_funcs; vector m_subst; vector m_sorts; vector > m_bound; public: qa_model_converter(ast_manager& m): m(m), m_old_funcs(m), m_new_funcs(m) {} virtual ~qa_model_converter() {} virtual model_converter * translate(ast_translation & translator) { return alloc(qa_model_converter, m); } void insert(func_decl* old_p, func_decl* new_p, expr_ref_vector& sub, sort_ref_vector& sorts, svector const& bound) { m_old_funcs.push_back(old_p); m_new_funcs.push_back(new_p); m_subst.push_back(sub); m_bound.push_back(bound); m_sorts.push_back(sorts); } virtual void operator()(model_ref & old_model) { model_ref new_model = alloc(model, m); for (unsigned i = 0; i < m_new_funcs.size(); ++i) { func_decl* p = m_new_funcs[i].get(); func_decl* q = m_old_funcs[i].get(); expr_ref_vector const& sub = m_subst[i]; sort_ref_vector const& sorts = m_sorts[i]; svector const& is_bound = m_bound[i]; func_interp* f = old_model->get_func_interp(p); expr_ref body(m); unsigned arity_p = p->get_arity(); unsigned arity_q = q->get_arity(); SASSERT(0 < arity_p); func_interp* g = alloc(func_interp, m, arity_q); if (f) { body = f->get_interp(); SASSERT(!f->is_partial()); SASSERT(body); } else { body = m.mk_false(); } // Create quantifier wrapper around body. TRACE("dl", tout << mk_pp(body, m) << "\n";); // 1. replace variables by the compound terms from // the original predicate. expr_safe_replace rep(m); for (unsigned i = 0; i < sub.size(); ++i) { rep.insert(m.mk_var(i, m.get_sort(sub[i])), sub[i]); } rep(body); rep.reset(); TRACE("dl", tout << mk_pp(body, m) << "\n";); // 2. replace bound variables by constants. expr_ref_vector consts(m), bound(m), free(m); svector names; ptr_vector bound_sorts; for (unsigned i = 0; i < sorts.size(); ++i) { sort* s = sorts[i]; consts.push_back(m.mk_fresh_const("C", s)); rep.insert(m.mk_var(i, s), consts.back()); if (is_bound[i]) { bound.push_back(consts.back()); names.push_back(symbol(i)); bound_sorts.push_back(s); } else { free.push_back(consts.back()); } } rep(body); rep.reset(); TRACE("dl", tout << mk_pp(body, m) << "\n";); // 3. abstract and quantify those variables that should be bound. expr_abstract(m, 0, bound.size(), bound.c_ptr(), body, body); body = m.mk_forall(names.size(), bound_sorts.c_ptr(), names.c_ptr(), body); TRACE("dl", tout << mk_pp(body, m) << "\n";); // 4. replace remaining constants by variables. for (unsigned i = 0; i < free.size(); ++i) { rep.insert(free[i].get(), m.mk_var(i, m.get_sort(free[i].get()))); } rep(body); g->set_else(body); TRACE("dl", tout << mk_pp(body, m) << "\n";); new_model->register_decl(q, g); } old_model = new_model; } }; mk_quantifier_abstraction::mk_quantifier_abstraction( context & ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), m_ctx(ctx), a(m), m_refs(m) { } mk_quantifier_abstraction::~mk_quantifier_abstraction() { } func_decl* mk_quantifier_abstraction::declare_pred(rule_set const& rules, rule_set& dst, func_decl* old_p) { if (rules.is_output_predicate(old_p)) { dst.inherit_predicate(rules, old_p, old_p); return 0; } unsigned sz = old_p->get_arity(); unsigned num_arrays = 0; for (unsigned i = 0; i < sz; ++i) { if (a.is_array(old_p->get_domain(i))) { num_arrays++; } } if (num_arrays == 0) { return 0; } func_decl* new_p = 0; if (!m_old2new.find(old_p, new_p)) { expr_ref_vector sub(m), vars(m); svector bound; sort_ref_vector domain(m), sorts(m); expr_ref arg(m); for (unsigned i = 0; i < sz; ++i) { sort* s0 = old_p->get_domain(i); unsigned lookahead = 0; sort* s = s0; while (a.is_array(s)) { lookahead += get_array_arity(s); s = get_array_range(s); } arg = m.mk_var(bound.size() + lookahead, s0); s = s0; while (a.is_array(s)) { unsigned arity = get_array_arity(s); expr_ref_vector args(m); for (unsigned j = 0; j < arity; ++j) { sort* s1 = get_array_domain(s, j); domain.push_back(s1); args.push_back(m.mk_var(bound.size(), s1)); bound.push_back(true); sorts.push_back(s1); } arg = mk_select(arg, args.size(), args.c_ptr()); s = get_array_range(s); } domain.push_back(s); bound.push_back(false); sub.push_back(arg); sorts.push_back(s0); } SASSERT(old_p->get_range() == m.mk_bool_sort()); new_p = m.mk_func_decl(old_p->get_name(), domain.size(), domain.c_ptr(), old_p->get_range()); m_refs.push_back(new_p); m_ctx.register_predicate(new_p, false); if (m_mc) { m_mc->insert(old_p, new_p, sub, sorts, bound); } m_old2new.insert(old_p, new_p); } return new_p; } app_ref mk_quantifier_abstraction::mk_head(rule_set const& rules, rule_set& dst, app* p, unsigned idx) { func_decl* new_p = declare_pred(rules, dst, p->get_decl()); if (!new_p) { return app_ref(p, m); } expr_ref_vector args(m); expr_ref arg(m); unsigned sz = p->get_num_args(); for (unsigned i = 0; i < sz; ++i) { arg = p->get_arg(i); sort* s = m.get_sort(arg); while (a.is_array(s)) { unsigned arity = get_array_arity(s); for (unsigned j = 0; j < arity; ++j) { args.push_back(m.mk_var(idx++, get_array_domain(s, j))); } arg = mk_select(arg, arity, args.c_ptr()+args.size()-arity); s = get_array_range(s); } args.push_back(arg); } TRACE("dl", tout << mk_pp(new_p, m) << "\n"; for (unsigned i = 0; i < args.size(); ++i) { tout << mk_pp(args[i].get(), m) << "\n"; }); return app_ref(m.mk_app(new_p, args.size(), args.c_ptr()), m); } app_ref mk_quantifier_abstraction::mk_tail(rule_set const& rules, rule_set& dst, app* p) { func_decl* old_p = p->get_decl(); func_decl* new_p = declare_pred(rules, dst, old_p); if (!new_p) { return app_ref(p, m); } SASSERT(new_p->get_arity() > old_p->get_arity()); unsigned num_extra_args = new_p->get_arity() - old_p->get_arity(); var_shifter shift(m); expr_ref p_shifted(m); shift(p, num_extra_args, p_shifted); app* ps = to_app(p_shifted); expr_ref_vector args(m); app_ref_vector pats(m); sort_ref_vector vars(m); svector names; expr_ref arg(m); unsigned idx = 0; unsigned sz = p->get_num_args(); for (unsigned i = 0; i < sz; ++i) { arg = ps->get_arg(i); sort* s = m.get_sort(arg); bool is_pattern = false; while (a.is_array(s)) { is_pattern = true; unsigned arity = get_array_arity(s); for (unsigned j = 0; j < arity; ++j) { vars.push_back(get_array_domain(s, j)); names.push_back(symbol(idx)); args.push_back(m.mk_var(idx++, vars.back())); } arg = mk_select(arg, arity, args.c_ptr()+args.size()-arity); s = get_array_range(s); } if (is_pattern) { pats.push_back(to_app(arg)); } args.push_back(arg); } expr* pat = 0; expr_ref pattern(m); pattern = m.mk_pattern(pats.size(), pats.c_ptr()); pat = pattern.get(); app_ref result(m); symbol qid, skid; result = m.mk_app(new_p, args.size(), args.c_ptr()); result = m.mk_eq(m.mk_forall(vars.size(), vars.c_ptr(), names.c_ptr(), result, 1, qid, skid, 1, &pat), m.mk_true()); return result; } expr * mk_quantifier_abstraction::mk_select(expr* arg, unsigned num_args, expr* const* args) { ptr_vector args2; args2.push_back(arg); args2.append(num_args, args); return a.mk_select(args2.size(), args2.c_ptr()); } rule_set * mk_quantifier_abstraction::operator()(rule_set const & source) { if (!m_ctx.quantify_arrays()) { return 0; } unsigned sz = source.get_num_rules(); for (unsigned i = 0; i < sz; ++i) { rule& r = *source.get_rule(i); if (r.has_negation()) { return 0; } } m_refs.reset(); m_old2new.reset(); m_new2old.reset(); rule_manager& rm = source.get_rule_manager(); rule_ref new_rule(rm); expr_ref_vector tail(m); app_ref head(m); expr_ref fml(m); rule_counter& vc = rm.get_counter(); if (m_ctx.get_model_converter()) { m_mc = alloc(qa_model_converter, m); } rule_set * result = alloc(rule_set, m_ctx); for (unsigned i = 0; i < sz; ++i) { tail.reset(); rule & r = *source.get_rule(i); TRACE("dl", r.display(m_ctx, tout); ); unsigned cnt = vc.get_max_rule_var(r)+1; unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); for (unsigned j = 0; j < utsz; ++j) { tail.push_back(mk_tail(source, *result, r.get_tail(j))); } for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(r.get_tail(j)); } head = mk_head(source, *result, r.get_head(), cnt); fml = m.mk_implies(m.mk_and(tail.size(), tail.c_ptr()), head); proof_ref pr(m); rm.mk_rule(fml, pr, *result); TRACE("dl", result->last()->display(m_ctx, tout);); } // proof converter: proofs are not necessarily preserved using this transformation. if (m_old2new.empty()) { dealloc(result); dealloc(m_mc); result = 0; } else { m_ctx.add_model_converter(m_mc); } m_mc = 0; return result; } }; z3-4.4.0/src/muz/bmc/0000755000175000017500000000000012540347414014010 5ustar michaelmichaelz3-4.4.0/src/muz/bmc/dl_bmc_engine.cpp0000644000175000017500000017420112540347414017266 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_bmc_engine.cpp Abstract: BMC engine for fixedpoint solver. Author: Nikolaj Bjorner (nbjorner) 2012-9-20 Revision History: --*/ #include "dl_context.h" #include "dl_rule_transformer.h" #include "dl_bmc_engine.h" #include "dl_mk_slice.h" #include "smt_kernel.h" #include "datatype_decl_plugin.h" #include "dl_decl_plugin.h" #include "bool_rewriter.h" #include "model_smt2_pp.h" #include "ast_smt_pp.h" #include "well_sorted.h" #include "rewriter_def.h" #include "dl_transforms.h" #include "dl_mk_rule_inliner.h" #include "scoped_proof.h" namespace datalog { // --------------------------------------------------------------------------- // Basic linear BMC based on indexed variables using quantified bit-vector domains. class bmc::qlinear { bmc& b; ast_manager& m; bv_util m_bv; unsigned m_bit_width; public: qlinear(bmc& b): b(b), m(b.m), m_bv(m), m_bit_width(1) {} lbool check() { setup(); m_bit_width = 4; lbool res = l_false; while (res == l_false) { b.m_solver.push(); IF_VERBOSE(1, verbose_stream() << "bit_width: " << m_bit_width << "\n";); compile(); b.checkpoint(); func_decl_ref q = mk_q_func_decl(b.m_query_pred); expr* T = m.mk_const(symbol("T"), mk_index_sort()); expr_ref fml(m.mk_app(q, T), m); b.assert_expr(fml); res = b.m_solver.check(); if (res == l_true) { res = get_model(); } b.m_solver.pop(1); ++m_bit_width; } return res; } private: sort_ref mk_index_sort() { return sort_ref(m_bv.mk_sort(m_bit_width), m); } var_ref mk_index_var() { return var_ref(m.mk_var(0, mk_index_sort()), m); } void compile() { sort_ref index_sort = mk_index_sort(); var_ref var = mk_index_var(); sort* index_sorts[1] = { index_sort }; symbol tick("T"); rule_set::decl2rules::iterator it = b.m_rules.begin_grouped_rules(); rule_set::decl2rules::iterator end = b.m_rules.end_grouped_rules(); for (; it != end; ++it) { func_decl* p = it->m_key; rule_vector const& rls = *it->m_value; // Assert: forall level . p(T) => body of rule i + equalities for head of rule i func_decl_ref pr = mk_q_func_decl(p); expr_ref pred = expr_ref(m.mk_app(pr, var.get()), m); expr_ref_vector rules(m), sub(m), conjs(m); expr_ref trm(m), rule_body(m), rule_i(m); for (unsigned i = 0; i < rls.size(); ++i) { sub.reset(); conjs.reset(); rule& r = *rls[i]; rule_i = m.mk_app(mk_q_rule(p, i), var.get()); rules.push_back(rule_i); mk_qrule_vars(r, i, sub); // apply substitution to body. var_subst vs(m, false); for (unsigned k = 0; k < p->get_arity(); ++k) { vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), trm); conjs.push_back(m.mk_eq(trm, mk_q_arg(p, k, true))); } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), trm); conjs.push_back(m.mk_eq(trm, mk_q_arg(q, k, false))); } func_decl_ref qr = mk_q_func_decl(q); conjs.push_back(m.mk_app(qr, m_bv.mk_bv_sub(var, mk_q_one()))); } for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { vs(r.get_tail(j), sub.size(), sub.c_ptr(), trm); conjs.push_back(trm); } if (r.get_uninterpreted_tail_size() > 0) { conjs.push_back(m_bv.mk_ule(mk_q_one(), var)); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); trm = m.mk_implies(rule_i, rule_body); trm = m.mk_forall(1, index_sorts, &tick, trm, 1); b.assert_expr(trm); } bool_rewriter(m).mk_or(rules.size(), rules.c_ptr(), trm); trm = m.mk_implies(pred, trm); trm = m.mk_forall(1, index_sorts, &tick, trm, 1); SASSERT(is_well_sorted(m, trm)); b.assert_expr(trm); } } void setup() { b.m_fparams.m_relevancy_lvl = 2; b.m_fparams.m_model = true; b.m_fparams.m_model_compact = true; b.m_fparams.m_mbqi = true; } void mk_qrule_vars(datalog::rule const& r, unsigned rule_id, expr_ref_vector& sub) { ptr_vector sorts; r.get_vars(m, sorts); // populate substitution of bound variables. sub.reset(); sub.resize(sorts.size()); for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { expr* arg = r.get_head()->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_q_arg(r.get_decl(), k, true); } } } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { expr* arg = r.get_tail(j)->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_q_arg(q, k, false); } } } } for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) { if (sorts[j] && !sub[j].get()) { sub[j] = mk_q_var(r.get_decl(), sorts[j], rule_id, idx++); } } } expr_ref mk_q_var(func_decl* pred, sort* s, unsigned rule_id, unsigned idx) { std::stringstream _name; _name << pred->get_name() << "#" << rule_id << "_" << idx; symbol nm(_name.str().c_str()); var_ref var = mk_index_var(); return expr_ref(m.mk_app(m.mk_func_decl(nm, mk_index_sort(), s), var), m); } expr_ref mk_q_arg(func_decl* pred, unsigned idx, bool is_current) { SASSERT(idx < pred->get_arity()); std::stringstream _name; _name << pred->get_name() << "#" << idx; symbol nm(_name.str().c_str()); expr_ref var(mk_index_var(), m); if (!is_current) { var = m_bv.mk_bv_sub(var, mk_q_one()); } return expr_ref(m.mk_app(m.mk_func_decl(nm, mk_index_sort(), pred->get_domain(idx)), var), m); } expr_ref mk_q_one() { return mk_q_num(1); } expr_ref mk_q_num(unsigned i) { return expr_ref(m_bv.mk_numeral(i, m_bit_width), m); } func_decl_ref mk_q_func_decl(func_decl* f) { std::stringstream _name; _name << f->get_name() << "#"; symbol nm(_name.str().c_str()); return func_decl_ref(m.mk_func_decl(nm, mk_index_sort(), f->get_range()), m); } func_decl_ref mk_q_rule(func_decl* f, unsigned rule_id) { std::stringstream _name; _name << f->get_name() << "#" << rule_id; symbol nm(_name.str().c_str()); return func_decl_ref(m.mk_func_decl(nm, mk_index_sort(), m.mk_bool_sort()), m); } expr_ref eval_q(model_ref& model, func_decl* f, unsigned i) { func_decl_ref fn = mk_q_func_decl(f); expr_ref t(m), result(m); t = m.mk_app(mk_q_func_decl(f).get(), mk_q_num(i)); model->eval(t, result); return result; } expr_ref eval_q(model_ref& model, expr* t, unsigned i) { expr_ref tmp(m), result(m), num(m); var_subst vs(m, false); num = mk_q_num(i); expr* nums[1] = { num }; vs(t, 1, nums, tmp); model->eval(tmp, result); return result; } lbool get_model() { rule_manager& rm = b.m_ctx.get_rule_manager(); func_decl_ref q = mk_q_func_decl(b.m_query_pred); expr_ref T(m), rule_i(m), vl(m); model_ref md; proof_ref pr(m); rule_unifier unifier(b.m_ctx); rational num; unsigned level, bv_size; b.m_solver.get_model(md); func_decl* pred = b.m_query_pred; dl_decl_util util(m); T = m.mk_const(symbol("T"), mk_index_sort()); md->eval(T, vl); VERIFY (m_bv.is_numeral(vl, num, bv_size)); SASSERT(num.is_unsigned()); level = num.get_unsigned(); SASSERT(m.is_true(eval_q(md, b.m_query_pred, level))); TRACE("bmc", model_smt2_pp(tout, m, *md, 0);); rule_ref r0(rm), r1(rm), r2(rm); while (true) { TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); expr_ref_vector sub(m); rule_vector const& rls = b.m_rules.get_predicate_rules(pred); rule* r = 0; unsigned i = 0; for (; i < rls.size(); ++i) { rule_i = m.mk_app(mk_q_rule(pred, i), mk_q_num(level).get()); TRACE("bmc", rls[i]->display(b.m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " ");); if (m.is_true(eval_q(md, rule_i, level))) { r = rls[i]; break; } } SASSERT(r); mk_qrule_vars(*r, i, sub); // we have rule, we have variable names of rule. // extract values for the variables in the rule. for (unsigned j = 0; j < sub.size(); ++j) { expr_ref vl = eval_q(md, sub[j].get(), i); if (vl) { // vl can be 0 if the interpretation does not assign a value to it. sub[j] = vl; } else { sub[j] = m.mk_var(j, m.get_sort(sub[j].get())); } } svector > positions; vector substs; expr_ref fml(m), concl(m); rm.to_formula(*r, fml); r2 = r; rm.substitute(r2, sub.size(), sub.c_ptr()); proof_ref p(m); if (r0) { VERIFY(unifier.unify_rules(*r0.get(), 0, *r2.get())); expr_ref_vector sub1 = unifier.get_rule_subst(*r0.get(), true); expr_ref_vector sub2 = unifier.get_rule_subst(*r2.get(), false); apply_subst(sub, sub2); unifier.apply(*r0.get(), 0, *r2.get(), r1); rm.to_formula(*r1.get(), concl); scoped_proof _sp(m); p = r->get_proof(); if (!p) { p = m.mk_asserted(fml); } proof* premises[2] = { pr, p }; positions.push_back(std::make_pair(0, 1)); substs.push_back(sub1); substs.push_back(sub); pr = m.mk_hyper_resolve(2, premises, concl, positions, substs); r0 = r1; } else { rm.to_formula(*r, concl); scoped_proof _sp(m); p = r->get_proof(); if (!p) { p = m.mk_asserted(fml); } if (sub.empty()) { pr = p; } else { substs.push_back(sub); proof* ps[1] = { p }; pr = m.mk_hyper_resolve(1, ps, concl, positions, substs); } r0 = r2; } if (level == 0) { SASSERT(r->get_uninterpreted_tail_size() == 0); break; } --level; SASSERT(r->get_uninterpreted_tail_size() == 1); pred = r->get_decl(0); } scoped_proof _sp(m); apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; return l_true; } }; // -------------------------------------------------------------------------- // Basic non-linear BMC based on compiling into quantifiers. class bmc::nonlinear { bmc& b; ast_manager& m; public: nonlinear(bmc& b): b(b), m(b.m) {} lbool check() { setup(); for (unsigned i = 0; ; ++i) { IF_VERBOSE(1, verbose_stream() << "level: " << i << "\n";); b.checkpoint(); expr_ref_vector fmls(m); compile(b.m_rules, fmls, i); assert_fmls(fmls); lbool res = check(i); if (res == l_undef) { return res; } if (res == l_true) { get_model(i); return res; } } } expr_ref compile_query(func_decl* query_pred, unsigned level) { expr_ref_vector vars(m); func_decl_ref level_p = mk_level_predicate(query_pred, level); for (unsigned i = 0; i < level_p->get_arity(); ++i) { std::stringstream _name; _name << query_pred->get_name() << "#" << level << "_" << i; symbol nm(_name.str().c_str()); vars.push_back(m.mk_const(nm, level_p->get_domain(i))); } return expr_ref(m.mk_app(level_p, vars.size(), vars.c_ptr()), m); } void compile(rule_set const& rules, expr_ref_vector& result, unsigned level) { bool_rewriter br(m); rule_set::decl2rules::iterator it = rules.begin_grouped_rules(); rule_set::decl2rules::iterator end = rules.end_grouped_rules(); for (; it != end; ++it) { func_decl* p = it->m_key; rule_vector const& rls = *it->m_value; // Assert: p_level(vars) => r1_level(vars) \/ r2_level(vars) \/ r3_level(vars) \/ ... // Assert: r_i_level(vars) => exists aux_vars . body of rule i for level func_decl_ref level_pred = mk_level_predicate(p, level); expr_ref_vector rules(m); expr_ref body(m), head(m); for (unsigned i = 0; i < rls.size(); ++i) { rule& r = *rls[i]; func_decl_ref rule_i = mk_level_rule(p, i, level); rules.push_back(apply_vars(rule_i)); ptr_vector rule_vars; expr_ref_vector args(m), conjs(m); r.get_vars(m, rule_vars); obj_hashtable used_vars; unsigned num_vars = 0; for (unsigned i = 0; i < r.get_decl()->get_arity(); ++i) { expr* arg = r.get_head()->get_arg(i); if (is_var(arg) && !used_vars.contains(arg)) { used_vars.insert(arg); args.push_back(arg); rule_vars[to_var(arg)->get_idx()] = 0; } else { sort* srt = m.get_sort(arg); args.push_back(m.mk_var(rule_vars.size()+num_vars, srt)); conjs.push_back(m.mk_eq(args.back(), arg)); ++num_vars; } } head = m.mk_app(rule_i, args.size(), args.c_ptr()); for (unsigned i = 0; i < r.get_tail_size(); ++i) { conjs.push_back(r.get_tail(i)); } br.mk_and(conjs.size(), conjs.c_ptr(), body); replace_by_level_predicates(level, body); body = skolemize_vars(r, args, rule_vars, body); body = m.mk_implies(head, body); body = bind_vars(body, head); result.push_back(body); } br.mk_or(rules.size(), rules.c_ptr(), body); head = apply_vars(level_pred); body = m.mk_implies(head, body); body = bind_vars(body, head); result.push_back(body); } } private: void assert_fmls(expr_ref_vector const& fmls) { for (unsigned i = 0; i < fmls.size(); ++i) { b.assert_expr(fmls[i]); } } 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_relevancy_lvl = 2; } lbool check(unsigned level) { expr_ref p = compile_query(b.m_query_pred, level); expr_ref q(m), q_at_level(m); q = m.mk_fresh_const("q",m.mk_bool_sort()); q_at_level = m.mk_implies(q, p); b.assert_expr(q_at_level); expr* qr = q.get(); return b.m_solver.check(1, &qr); } proof_ref get_proof(model_ref& md, func_decl* pred, app* prop, unsigned level) { if (b.m_cancel) { return proof_ref(0, m); } TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); rule_manager& rm = b.m_ctx.get_rule_manager(); expr_ref prop_r(m), prop_v(m), fml(m), prop_body(m), tmp(m), body(m); expr_ref_vector args(m); proof_ref_vector prs(m); proof_ref pr(m); // find the rule that was triggered by evaluating the arguments to prop. rule_vector const& rls = b.m_rules.get_predicate_rules(pred); rule* r = 0; for (unsigned i = 0; i < rls.size(); ++i) { func_decl_ref rule_i = mk_level_rule(pred, i, level); TRACE("bmc", rls[i]->display(b.m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " ");); prop_r = m.mk_app(rule_i, prop->get_num_args(), prop->get_args()); md->eval(prop_r, prop_v); if (m.is_true(prop_v)) { r = rls[i]; break; } } SASSERT(r); rm.to_formula(*r, fml); IF_VERBOSE(1, verbose_stream() << mk_pp(fml, m) << "\n";); prs.push_back(r->get_proof()); unsigned sz = r->get_uninterpreted_tail_size(); ptr_vector rule_vars; r->get_vars(m, rule_vars); args.append(prop->get_num_args(), prop->get_args()); expr_ref_vector sub = mk_skolem_binding(*r, rule_vars, args); if (sub.empty() && sz == 0) { pr = prs[0].get(); return pr; } for (unsigned j = 0; j < sub.size(); ++j) { md->eval(sub[j].get(), tmp); sub[j] = tmp; } svector > positions; vector substs; var_subst vs(m, false); substs.push_back(sub); for (unsigned j = 0; j < sz; ++j) { func_decl* head_j = r->get_decl(j); app* body_j = r->get_tail(j); vs(body_j, sub.size(), sub.c_ptr(), prop_body); prs.push_back(get_proof(md, head_j, to_app(prop_body), level-1)); positions.push_back(std::make_pair(j+1,0)); substs.push_back(expr_ref_vector(m)); } pr = m.mk_hyper_resolve(sz+1, prs.c_ptr(), prop, positions, substs); return pr; } void get_model(unsigned level) { scoped_proof _sp(m); expr_ref level_query = compile_query(b.m_query_pred, level); model_ref md; b.m_solver.get_model(md); IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *md, 0);); proof_ref pr(m); pr = get_proof(md, b.m_query_pred, to_app(level_query), level); apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; } func_decl_ref mk_level_predicate(func_decl* p, unsigned level) { std::stringstream _name; _name << p->get_name() << "#" << level; symbol nm(_name.str().c_str()); return func_decl_ref(m.mk_func_decl(nm, p->get_arity(), p->get_domain(), m.mk_bool_sort()), m); } func_decl_ref mk_level_rule(func_decl* p, unsigned rule_idx, unsigned level) { std::stringstream _name; _name << "rule:" << p->get_name() << "#" << level << "_" << rule_idx; symbol nm(_name.str().c_str()); return func_decl_ref(m.mk_func_decl(nm, p->get_arity(), p->get_domain(), m.mk_bool_sort()), m); } expr_ref apply_vars(func_decl* p) { expr_ref_vector vars(m); for (unsigned i = 0; i < p->get_arity(); ++i) { vars.push_back(m.mk_var(i, p->get_domain(i))); } return expr_ref(m.mk_app(p, vars.size(), vars.c_ptr()), m); } // remove variables from dst that are in src. void subtract_vars(ptr_vector& dst, ptr_vector const& src) { for (unsigned i = 0; i < dst.size(); ++i) { if (i >= src.size()) { break; } if (src[i]) { dst[i] = 0; } } } expr_ref_vector mk_skolem_binding(rule& r, ptr_vector const& vars, expr_ref_vector const& args) { expr_ref_vector binding(m); ptr_vector arg_sorts; for (unsigned i = 0; i < args.size(); ++i) { arg_sorts.push_back(m.get_sort(args[i])); } for (unsigned i = 0; i < vars.size(); ++i) { if (vars[i]) { func_decl_ref f = mk_body_func(r, arg_sorts, i, vars[i]); binding.push_back(m.mk_app(f, args.size(), args.c_ptr())); } else { binding.push_back(0); } } return binding; } expr_ref skolemize_vars(rule& r, expr_ref_vector const& args, ptr_vector const& vars, expr* e) { expr_ref result(m); expr_ref_vector binding = mk_skolem_binding(r, vars, args); var_subst vs(m, false); vs(e, binding.size(), binding.c_ptr(), result); return result; } func_decl_ref mk_body_func(rule& r, ptr_vector const& args, unsigned index, sort* s) { std::stringstream _name; _name << r.get_decl()->get_name() << "@" << index; symbol name(_name.str().c_str()); func_decl* f = m.mk_func_decl(name, args.size(), args.c_ptr(), s); return func_decl_ref(f, m); } expr_ref bind_vars(expr* e, expr* pat) { ptr_vector sorts; svector names; expr_ref_vector binding(m), patterns(m); expr_ref tmp(m), head(m); expr_free_vars vars; vars(e); for (unsigned i = 0; i < vars.size(); ++i) { if (vars[i]) { binding.push_back(m.mk_var(sorts.size(), vars[i])); sorts.push_back(vars[i]); names.push_back(symbol(i)); } else { binding.push_back(0); } } sorts.reverse(); if (sorts.empty()) { return expr_ref(e, m); } var_subst vs(m, false); vs(e, binding.size(), binding.c_ptr(), tmp); vs(pat, binding.size(), binding.c_ptr(), head); patterns.push_back(m.mk_pattern(to_app(head))); symbol qid, skid; return expr_ref(m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), tmp, 1, qid, skid, 1, patterns.c_ptr()), m); } public: class level_replacer { nonlinear& n; unsigned m_level; bool is_predicate(func_decl* f) { return n.b.m_ctx.is_predicate(f); } public: level_replacer(nonlinear& n, unsigned level): n(n), m_level(level) {} br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { if (is_predicate(f)) { if (m_level > 0) { result = n.m.mk_app(n.mk_level_predicate(f, m_level-1), num_args, args); } else { result = n.m.mk_false(); } return BR_DONE; } return BR_FAILED; } bool reduce_quantifier(quantifier* old_q, expr* new_body, expr_ref& result) { if (is_ground(new_body)) { result = new_body; } else { expr * const * no_pats = &new_body; result = n.m.update_quantifier(old_q, 0, 0, 1, no_pats, new_body); } return true; } }; struct level_replacer_cfg : public default_rewriter_cfg { level_replacer m_r; level_replacer_cfg(nonlinear& nl, unsigned level): m_r(nl, level) {} bool rewrite_patterns() const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { return m_r.mk_app_core(f, num, args, result); } bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { return m_r.reduce_quantifier(old_q, new_body, result); } }; class level_replacer_star : public rewriter_tpl { level_replacer_cfg m_cfg; public: level_replacer_star(nonlinear& nl, unsigned level): rewriter_tpl(nl.m, false, m_cfg), m_cfg(nl, level) {} }; private: void replace_by_level_predicates(unsigned level, expr_ref& fml) { level_replacer_star rep(*this, level); expr_ref tmp(m); rep(fml, tmp); fml = tmp; } }; // -------------------------------------------------------------------------- // Basic non-linear BMC based on compiling into data-types (it is inefficient) class bmc::nonlinear_dt { bmc& b; ast_manager& m; ast_ref_vector m_pinned; sort_ref m_path_sort; obj_map m_pred2sort; obj_map m_sort2pred; public: nonlinear_dt(bmc& b): b(b), m(b.m), m_pinned(m), m_path_sort(m) {} lbool check() { setup(); declare_datatypes(); compile(); return check_query(); } private: void setup() { m_pred2sort.reset(); m_pinned.reset(); m_sort2pred.reset(); b.m_fparams.m_relevancy_lvl = 0; b.m_fparams.m_model = true; b.m_fparams.m_model_compact = true; b.m_fparams.m_mbqi = false; b.m_fparams.m_relevancy_lvl = 2; } func_decl_ref mk_predicate(func_decl* pred) { std::stringstream _name; _name << pred->get_name() << "#"; symbol nm(_name.str().c_str()); sort* pred_trace_sort = m_pred2sort.find(pred); return func_decl_ref(m.mk_func_decl(nm, pred_trace_sort, m_path_sort, m.mk_bool_sort()), m); } func_decl_ref mk_rule(func_decl* p, unsigned rule_idx) { std::stringstream _name; _name << "rule:" << p->get_name() << "#" << rule_idx; symbol nm(_name.str().c_str()); sort* pred_trace_sort = m_pred2sort.find(p); return func_decl_ref(m.mk_func_decl(nm, pred_trace_sort, m_path_sort, m.mk_bool_sort()), m); } expr_ref mk_var(func_decl* pred, sort*s, unsigned idx, expr* path_arg, expr* trace_arg) { std::stringstream _name; _name << pred->get_name() << "#V_" << idx; symbol nm(_name.str().c_str()); func_decl_ref fn(m); fn = m.mk_func_decl(nm, m_pred2sort.find(pred), m_path_sort, s); return expr_ref(m.mk_app(fn, trace_arg, path_arg), m); } expr_ref mk_arg(func_decl* pred, unsigned idx, expr* path_arg, expr* trace_arg) { SASSERT(idx < pred->get_arity()); std::stringstream _name; _name << pred->get_name() << "#X_" << idx; symbol nm(_name.str().c_str()); func_decl_ref fn(m); fn = m.mk_func_decl(nm, m_pred2sort.find(pred), m_path_sort, pred->get_domain(idx)); return expr_ref(m.mk_app(fn, trace_arg, path_arg), m); } void mk_subst(datalog::rule& r, expr* path, app* trace, expr_ref_vector& sub) { datatype_util dtu(m); ptr_vector sorts; func_decl* p = r.get_decl(); ptr_vector const& succs = *dtu.get_datatype_constructors(m.get_sort(path)); // populate substitution of bound variables. r.get_vars(m, sorts); sub.reset(); sub.resize(sorts.size()); for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { expr* arg = r.get_head()->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_arg(p, k, path, trace); } } } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { func_decl* q = r.get_decl(j); expr_ref path_arg(m); if (j == 0) { path_arg = path; } else { path_arg = m.mk_app(succs[j], path); } for (unsigned k = 0; k < q->get_arity(); ++k) { expr* arg = r.get_tail(j)->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_arg(q, k, path_arg, trace->get_arg(j)); } } } } for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) { if (sorts[j] && !sub[j].get()) { sub[j] = mk_var(r.get_decl(), sorts[j], idx++, path, trace); } } } /** \brief compile Horn rule into co-Horn implication. forall args . R(path_var, rule_i(trace_vars)) => Body[X(path_var, rule_i(trace_vars)), Y(S_j(path_var), trace_vars_j)] */ void compile() { datatype_util dtu(m); rule_set::decl2rules::iterator it = b.m_rules.begin_grouped_rules(); rule_set::decl2rules::iterator end = b.m_rules.end_grouped_rules(); for (; it != end; ++it) { func_decl* p = it->m_key; rule_vector const& rls = *it->m_value; // Assert: p_level => r1_level \/ r2_level \/ r3_level \/ ... // where: r_i_level = body of rule i for level + equalities for head of rule i expr_ref rule_body(m), tmp(m), pred(m), trace_arg(m), fml(m); var_ref path_var(m), trace_var(m); expr_ref_vector rules(m), sub(m), conjs(m), vars(m), patterns(m); sort* pred_sort = m_pred2sort.find(p); path_var = m.mk_var(0, m_path_sort); trace_var = m.mk_var(1, pred_sort); // sort* sorts[2] = { pred_sort, m_path_sort }; ptr_vector const& cnstrs = *dtu.get_datatype_constructors(pred_sort); ptr_vector const& succs = *dtu.get_datatype_constructors(m_path_sort); SASSERT(cnstrs.size() == rls.size()); pred = m.mk_app(mk_predicate(p), trace_var.get(), path_var.get()); for (unsigned i = 0; i < rls.size(); ++i) { sub.reset(); conjs.reset(); vars.reset(); rule& r = *rls[i]; func_decl_ref rule_pred_i = mk_rule(p, i); // Create cnstr_rule_i(Vars) func_decl* cnstr = cnstrs[i]; rules.push_back(m.mk_app(rule_pred_i, trace_var.get(), path_var.get())); unsigned arity = cnstr->get_arity(); for (unsigned j = 0; j < arity; ++j) { vars.push_back(m.mk_var(arity-j,cnstr->get_domain(j))); } trace_arg = m.mk_app(cnstr, vars.size(), vars.c_ptr()); mk_subst(r, path_var, to_app(trace_arg), sub); // apply substitution to body. var_subst vs(m, false); for (unsigned k = 0; k < p->get_arity(); ++k) { vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), tmp); expr_ref arg = mk_arg(p, k, path_var, trace_arg); conjs.push_back(m.mk_eq(tmp, arg)); } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { expr_ref path_arg(m); if (j == 0) { path_arg = path_var.get(); } else { path_arg = m.mk_app(succs[j], path_var.get()); } func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), tmp); expr_ref arg = mk_arg(q, k, path_arg, vars[j].get()); conjs.push_back(m.mk_eq(tmp, arg)); } func_decl_ref q_pred = mk_predicate(q); conjs.push_back(m.mk_app(q_pred, vars[j].get(), path_arg)); } for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { vs(r.get_tail(j), sub.size(), sub.c_ptr(), tmp); conjs.push_back(tmp); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); ptr_vector q_sorts; vector names; for (unsigned i = 0; i < vars.size(); ++i) { q_sorts.push_back(m.get_sort(vars[i].get())); names.push_back(symbol(i+1)); } vars.push_back(path_var); q_sorts.push_back(m.get_sort(path_var)); names.push_back(symbol("path")); SASSERT(names.size() == q_sorts.size()); SASSERT(vars.size() == names.size()); symbol qid = r.name(), skid; tmp = m.mk_app(mk_predicate(p), trace_arg.get(), path_var.get()); patterns.reset(); patterns.push_back(m.mk_pattern(to_app(tmp))); fml = m.mk_implies(tmp, rule_body); fml = m.mk_forall(vars.size(), q_sorts.c_ptr(), names.c_ptr(), fml, 1, qid, skid, 1, patterns.c_ptr()); b.assert_expr(fml); } } } void declare_datatypes() { rule_set::decl2rules::iterator it = b.m_rules.begin_grouped_rules(); rule_set::decl2rules::iterator end = b.m_rules.end_grouped_rules(); datatype_util dtu(m); ptr_vector dts; obj_map pred_idx; for (unsigned i = 0; it != end; ++it, ++i) { pred_idx.insert(it->m_key, i); } it = b.m_rules.begin_grouped_rules(); for (; it != end; ++it) { rule_vector const& rls = *it->m_value; func_decl* pred = it->m_key; ptr_vector cnstrs; for (unsigned i = 0; i < rls.size(); ++i) { rule* r = rls[i]; ptr_vector accs; for (unsigned j = 0; j < r->get_uninterpreted_tail_size(); ++j) { func_decl* q = r->get_decl(j); unsigned idx = pred_idx.find(q); std::stringstream _name; _name << pred->get_name() << "_" << q->get_name() << j; symbol name(_name.str().c_str()); type_ref tr(idx); accs.push_back(mk_accessor_decl(name, tr)); } std::stringstream _name; _name << pred->get_name() << "_" << i; symbol name(_name.str().c_str()); _name << "?"; symbol is_name(_name.str().c_str()); cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr())); } dts.push_back(mk_datatype_decl(pred->get_name(), cnstrs.size(), cnstrs.c_ptr())); } sort_ref_vector new_sorts(m); family_id dfid = m.mk_family_id("datatype"); datatype_decl_plugin* dtp = static_cast(m.get_plugin(dfid)); VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), new_sorts)); it = b.m_rules.begin_grouped_rules(); for (unsigned i = 0; it != end; ++it, ++i) { m_pred2sort.insert(it->m_key, new_sorts[i].get()); m_sort2pred.insert(new_sorts[i].get(), it->m_key); m_pinned.push_back(new_sorts[i].get()); } if (new_sorts.size() > 0) { TRACE("bmc", dtu.display_datatype(new_sorts[0].get(), tout);); } del_datatype_decls(dts.size(), dts.c_ptr()); // declare path data-type. { new_sorts.reset(); dts.reset(); ptr_vector cnstrs; unsigned max_arity = 0; rule_set::iterator it = b.m_rules.begin(); rule_set::iterator end = b.m_rules.end(); for (; it != end; ++it) { rule* r = *it; unsigned sz = r->get_uninterpreted_tail_size(); max_arity = std::max(sz, max_arity); } cnstrs.push_back(mk_constructor_decl(symbol("Z#"), symbol("Z#?"), 0, 0)); for (unsigned i = 0; i + 1 < max_arity; ++i) { std::stringstream _name; _name << "succ#" << i; symbol name(_name.str().c_str()); _name << "?"; symbol is_name(_name.str().c_str()); std::stringstream _name2; _name2 << "get_succ#" << i; ptr_vector accs; type_ref tr(0); accs.push_back(mk_accessor_decl(name, tr)); cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr())); } dts.push_back(mk_datatype_decl(symbol("Path"), cnstrs.size(), cnstrs.c_ptr())); VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), new_sorts)); m_path_sort = new_sorts[0].get(); } } proof_ref get_proof(model_ref& md, app* trace, app* path) { datatype_util dtu(m); rule_manager& rm = b.m_ctx.get_rule_manager(); sort* trace_sort = m.get_sort(trace); func_decl* p = m_sort2pred.find(trace_sort); datalog::rule_vector const& rules = b.m_rules.get_predicate_rules(p); ptr_vector const& cnstrs = *dtu.get_datatype_constructors(trace_sort); ptr_vector const& succs = *dtu.get_datatype_constructors(m_path_sort); for (unsigned i = 0; i < cnstrs.size(); ++i) { if (trace->get_decl() == cnstrs[i]) { svector > positions; scoped_proof _sc(m); proof_ref_vector prs(m); expr_ref_vector sub(m); vector substs; proof_ref pr(m); expr_ref fml(m), head(m), tmp(m); app_ref path1(m); var_subst vs(m, false); mk_subst(*rules[i], path, trace, sub); rm.to_formula(*rules[i], fml); prs.push_back(rules[i]->get_proof()); unsigned sz = trace->get_num_args(); if (sub.empty() && sz == 0) { pr = prs[0].get(); return pr; } for (unsigned j = 0; j < sub.size(); ++j) { md->eval(sub[j].get(), tmp); sub[j] = tmp; } rule_ref rl(b.m_ctx.get_rule_manager()); rl = rules[i]; b.m_ctx.get_rule_manager().substitute(rl, sub.size(), sub.c_ptr()); substs.push_back(sub); for (unsigned j = 0; j < sz; ++j) { if (j == 0) { path1 = path; } else { path1 = m.mk_app(succs[j], path); } prs.push_back(get_proof(md, to_app(trace->get_arg(j)), path1)); positions.push_back(std::make_pair(j+1,0)); substs.push_back(expr_ref_vector(m)); } head = rl->get_head(); pr = m.mk_hyper_resolve(sz+1, prs.c_ptr(), head, positions, substs); return pr; } } UNREACHABLE(); return proof_ref(0, m); } // instantiation of algebraic data-types takes care of the rest. lbool check_query() { sort* trace_sort = m_pred2sort.find(b.m_query_pred); func_decl_ref q = mk_predicate(b.m_query_pred); expr_ref trace(m), path(m), fml(m); trace = m.mk_const(symbol("trace"), trace_sort); path = m.mk_const(symbol("path"), m_path_sort); fml = m.mk_app(q, trace.get(), path.get()); b.assert_expr(fml); while (true) { lbool is_sat = b.m_solver.check(); model_ref md; if (is_sat == l_false) { return is_sat; } b.m_solver.get_model(md); mk_answer(md, trace, path); return l_true; } } bool check_model(model_ref& md, expr* trace) { expr_ref trace_val(m), eq(m); md->eval(trace, trace_val); eq = m.mk_eq(trace, trace_val); b.m_solver.push(); b.m_solver.assert_expr(eq); lbool is_sat = b.m_solver.check(); if (is_sat != l_false) { b.m_solver.get_model(md); } b.m_solver.pop(1); if (is_sat == l_false) { IF_VERBOSE(1, verbose_stream() << "infeasible trace " << mk_pp(trace_val, m) << "\n";); eq = m.mk_not(eq); b.assert_expr(eq); } return is_sat != l_false; } void mk_answer(model_ref& md, expr_ref& trace, expr_ref& path) { IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *md, 0);); md->eval(trace, trace); md->eval(path, path); IF_VERBOSE(2, verbose_stream() << mk_pp(trace, m) << "\n"; for (unsigned i = 0; i < b.m_solver.size(); ++i) { verbose_stream() << mk_pp(b.m_solver.get_formulas()[i], m) << "\n"; }); scoped_proof _sp(m); proof_ref pr(m); pr = get_proof(md, to_app(trace), to_app(path)); apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; } }; // -------------------------------------------------------------------------- // Basic linear BMC based on incrementally unfolding the transition relation. class bmc::linear { bmc& b; ast_manager& m; public: linear(bmc& b): b(b), m(b.m) {} lbool check() { setup(); for (unsigned i = 0; ; ++i) { IF_VERBOSE(1, verbose_stream() << "level: " << i << "\n";); b.checkpoint(); compile(i); lbool res = check(i); if (res == l_undef) { return res; } if (res == l_true) { get_model(i); return res; } } } private: void get_model(unsigned level) { if (b.m_cancel) { return; } rule_manager& rm = b.m_ctx.get_rule_manager(); expr_ref level_query = mk_level_predicate(b.m_query_pred, level); model_ref md; proof_ref pr(m); rule_unifier unifier(b.m_ctx); b.m_solver.get_model(md); func_decl* pred = b.m_query_pred; SASSERT(m.is_true(md->get_const_interp(to_app(level_query)->get_decl()))); TRACE("bmc", model_smt2_pp(tout, m, *md, 0);); rule_ref r0(rm), r1(rm), r2(rm); while (true) { TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); expr_ref_vector sub(m); rule_vector const& rls = b.m_rules.get_predicate_rules(pred); rule* r = 0; unsigned i = 0; for (; i < rls.size(); ++i) { expr_ref rule_i = mk_level_rule(pred, i, level); TRACE("bmc", rls[i]->display(b.m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " ");); if (m.is_true(md->get_const_interp(to_app(rule_i)->get_decl()))) { r = rls[i]; break; } } SASSERT(r); mk_rule_vars(*r, level, i, sub); // we have rule, we have variable names of rule. // extract values for the variables in the rule. for (unsigned j = 0; j < sub.size(); ++j) { expr* vl = md->get_const_interp(to_app(sub[j].get())->get_decl()); if (vl) { // vl can be 0 if the interpretation does not assign a value to it. sub[j] = vl; } else { sub[j] = m.mk_var(j, m.get_sort(sub[j].get())); } } svector > positions; vector substs; expr_ref fml(m), concl(m); rm.to_formula(*r, fml); r2 = r; rm.substitute(r2, sub.size(), sub.c_ptr()); proof_ref p(m); { scoped_proof _sp(m); p = r->get_proof(); if (!p) { p = m.mk_asserted(fml); } } if (r0) { VERIFY(unifier.unify_rules(*r0.get(), 0, *r2.get())); expr_ref_vector sub1 = unifier.get_rule_subst(*r0.get(), true); expr_ref_vector sub2 = unifier.get_rule_subst(*r2.get(), false); apply_subst(sub, sub2); unifier.apply(*r0.get(), 0, *r2.get(), r1); rm.to_formula(*r1.get(), concl); scoped_proof _sp(m); proof* premises[2] = { pr, p }; positions.push_back(std::make_pair(0, 1)); substs.push_back(sub1); substs.push_back(sub); pr = m.mk_hyper_resolve(2, premises, concl, positions, substs); r0 = r1; } else { rm.to_formula(*r2.get(), concl); scoped_proof _sp(m); if (sub.empty()) { pr = p; } else { substs.push_back(sub); proof * ps[1] = { p }; pr = m.mk_hyper_resolve(1, ps, concl, positions, substs); } r0 = r2; } if (level == 0) { SASSERT(r->get_uninterpreted_tail_size() == 0); break; } --level; SASSERT(r->get_uninterpreted_tail_size() == 1); pred = r->get_decl(0); } scoped_proof _sp(m); apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; } void setup() { b.m_fparams.m_relevancy_lvl = 0; b.m_fparams.m_model = true; b.m_fparams.m_model_compact = true; b.m_fparams.m_mbqi = false; // m_fparams.m_auto_config = false; } lbool check(unsigned level) { expr_ref level_query = mk_level_predicate(b.m_query_pred, level); expr* q = level_query.get(); return b.m_solver.check(1, &q); } expr_ref mk_level_predicate(func_decl* p, unsigned level) { return mk_level_predicate(p->get_name(), level); } expr_ref mk_level_predicate(symbol const& name, unsigned level) { std::stringstream _name; _name << name << "#" << level; symbol nm(_name.str().c_str()); return expr_ref(m.mk_const(nm, m.mk_bool_sort()), m); } expr_ref mk_level_arg(func_decl* pred, unsigned idx, unsigned level) { SASSERT(idx < pred->get_arity()); std::stringstream _name; _name << pred->get_name() << "#" << level << "_" << idx; symbol nm(_name.str().c_str()); return expr_ref(m.mk_const(nm, pred->get_domain(idx)), m); } expr_ref mk_level_var(func_decl* pred, sort* s, unsigned rule_id, unsigned idx, unsigned level) { std::stringstream _name; _name << pred->get_name() << "#" << level << "_" << rule_id << "_" << idx; symbol nm(_name.str().c_str()); return expr_ref(m.mk_const(nm, s), m); } expr_ref mk_level_rule(func_decl* p, unsigned rule_idx, unsigned level) { std::stringstream _name; _name << "rule:" << p->get_name() << "#" << level << "_" << rule_idx; symbol nm(_name.str().c_str()); return expr_ref(m.mk_const(nm, m.mk_bool_sort()), m); } void mk_rule_vars(rule& r, unsigned level, unsigned rule_id, expr_ref_vector& sub) { ptr_vector sorts; r.get_vars(m, sorts); // populate substitution of bound variables. sub.reset(); sub.resize(sorts.size()); for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { expr* arg = r.get_head()->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_level_arg(r.get_decl(), k, level); } } } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { SASSERT(level > 0); func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { expr* arg = r.get_tail(j)->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_level_arg(q, k, level-1); } } } } for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) { if (sorts[j] && !sub[j].get()) { sub[j] = mk_level_var(r.get_decl(), sorts[j], rule_id, idx++, level); } } } void compile(unsigned level) { rule_set::decl2rules::iterator it = b.m_rules.begin_grouped_rules(); rule_set::decl2rules::iterator end = b.m_rules.end_grouped_rules(); for (; it != end; ++it) { func_decl* p = it->m_key; rule_vector const& rls = *it->m_value; // Assert: p_level => r1_level \/ r2_level \/ r3_level \/ ... // Assert: r_i_level => body of rule i for level + equalities for head of rule i expr_ref level_pred = mk_level_predicate(p, level); expr_ref_vector rules(m), sub(m), conjs(m); expr_ref rule_body(m), tmp(m); for (unsigned i = 0; i < rls.size(); ++i) { sub.reset(); conjs.reset(); rule& r = *rls[i]; expr_ref rule_i = mk_level_rule(p, i, level); rules.push_back(rule_i); if (level == 0 && r.get_uninterpreted_tail_size() > 0) { tmp = m.mk_not(rule_i); b.assert_expr(tmp); continue; } mk_rule_vars(r, level, i, sub); // apply substitution to body. var_subst vs(m, false); for (unsigned k = 0; k < p->get_arity(); ++k) { vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), tmp); conjs.push_back(m.mk_eq(tmp, mk_level_arg(p, k, level))); } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { SASSERT(level > 0); func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), tmp); conjs.push_back(m.mk_eq(tmp, mk_level_arg(q, k, level-1))); } conjs.push_back(mk_level_predicate(q, level-1)); } for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { vs(r.get_tail(j), sub.size(), sub.c_ptr(), tmp); conjs.push_back(tmp); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); rule_body = m.mk_implies(rule_i, rule_body); b.assert_expr(rule_body); } bool_rewriter(m).mk_or(rules.size(), rules.c_ptr(), tmp); tmp = m.mk_implies(level_pred, tmp); b.assert_expr(tmp); } } }; bmc::bmc(context& ctx): engine_base(ctx.get_manager(), "bmc"), m_ctx(ctx), m(ctx.get_manager()), m_solver(m, m_fparams), m_rules(ctx), m_query_pred(m), m_answer(m), m_cancel(false) { } bmc::~bmc() {} lbool bmc::query(expr* query) { m_solver.reset(); m_answer = 0; m_ctx.ensure_opened(); m_rules.reset(); datalog::rule_manager& rule_manager = m_ctx.get_rule_manager(); datalog::rule_set old_rules(m_ctx.get_rules()); rule_manager.mk_query(query, m_ctx.get_rules()); expr_ref bg_assertion = m_ctx.get_background_assertion(); apply_default_transformation(m_ctx); if (m_ctx.xform_slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); m_ctx.transform_rules(transformer); } if (m_ctx.get_rules().get_output_predicates().empty()) { return l_false; } m_query_pred = m_ctx.get_rules().get_output_predicate(); m_rules.replace_rules(m_ctx.get_rules()); m_rules.close(); m_ctx.reopen(); m_ctx.replace_rules(old_rules); checkpoint(); IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); if (m_rules.get_num_rules() == 0) { return l_false; } if (m_rules.get_predicate_rules(m_query_pred).empty()) { return l_false; } if (is_linear()) { if (m_ctx.get_engine() == QBMC_ENGINE) { qlinear ql(*this); return ql.check(); } else { linear lin(*this); return lin.check(); } } else { IF_VERBOSE(0, verbose_stream() << "WARNING: non-linear BMC is highly inefficient\n";); nonlinear nl(*this); return nl.check(); } } void bmc::assert_expr(expr* e) { TRACE("bmc", tout << mk_pp(e, m) << "\n";); m_solver.assert_expr(e); } bool bmc::is_linear() const { unsigned sz = m_rules.get_num_rules(); for (unsigned i = 0; i < sz; ++i) { if (m_rules.get_rule(i)->get_uninterpreted_tail_size() > 1) { return false; } if (m_rules.get_rule_manager().has_quantifiers(*m_rules.get_rule(i))) { return false; } } return true; } void bmc::checkpoint() { if (m_cancel) { throw default_exception("bmc canceled"); } } void bmc::cancel() { m_cancel = true; m_solver.cancel(); } void bmc::cleanup() { m_cancel = false; m_solver.reset(); } void bmc::display_certificate(std::ostream& out) const { out << mk_pp(m_answer, m) << "\n"; } void bmc::collect_statistics(statistics& st) const { m_solver.collect_statistics(st); } void bmc::reset_statistics() { m_solver.reset_statistics(); } expr_ref bmc::get_answer() { return m_answer; } void bmc::compile(rule_set const& rules, expr_ref_vector& fmls, unsigned level) { nonlinear nl(*this); nl.compile(rules, fmls, level); } expr_ref bmc::compile_query(func_decl* query_pred, unsigned level) { nonlinear nl(*this); return nl.compile_query(query_pred, level); } }; template class rewriter_tpl; z3-4.4.0/src/muz/bmc/dl_bmc_engine.h0000644000175000017500000000272612540347414016735 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_bmc_engine.h Abstract: BMC engine for fixedpoint solver. Author: Nikolaj Bjorner (nbjorner) 2012-9-20 Revision History: --*/ #ifndef _DL_BMC_ENGINE_H_ #define _DL_BMC_ENGINE_H_ #include "params.h" #include "statistics.h" #include "smt_kernel.h" #include "bv_decl_plugin.h" #include "smt_params.h" namespace datalog { class context; class bmc : public engine_base { context& m_ctx; ast_manager& m; smt_params m_fparams; smt::kernel m_solver; rule_set m_rules; func_decl_ref m_query_pred; expr_ref m_answer; volatile bool m_cancel; void checkpoint(); class nonlinear_dt; class nonlinear; class qlinear; class linear; bool is_linear() const; void assert_expr(expr* e); public: bmc(context& ctx); ~bmc(); lbool query(expr* query); void cancel(); void cleanup(); void display_certificate(std::ostream& out) const; void collect_statistics(statistics& st) const; void reset_statistics(); expr_ref get_answer(); // direct access to (new) non-linear compiler. void compile(rule_set const& rules, expr_ref_vector& fmls, unsigned level); expr_ref compile_query(func_decl* query_pred, unsigned level); }; }; #endif z3-4.4.0/src/muz/ddnf/0000755000175000017500000000000012540347414014162 5ustar michaelmichaelz3-4.4.0/src/muz/ddnf/ddnf.cpp0000644000175000017500000007112712540347414015611 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: ddnf.cpp Abstract: DDNF based engine. Author: Nikolaj Bjorner (nbjorner) 2014-08-21 Revision History: - inherits from Nuno Lopes Hassel utilities and Garvit Juniwal's DDNF engine. --*/ #include "ddnf.h" #include "dl_rule_set.h" #include "dl_context.h" #include "scoped_proof.h" #include "bv_decl_plugin.h" #include "tbv.h" namespace datalog { class ddnf_mgr; class ddnf_node; typedef ref_vector ddnf_node_vector; class ddnf_node { public: struct eq { tbv_manager& m; eq(tbv_manager& m):m(m) {} bool operator()(ddnf_node* n1, ddnf_node* n2) const { return m.equals(n1->get_tbv(), n2->get_tbv()); } }; struct hash { tbv_manager& m; hash(tbv_manager& m):m(m) {} unsigned operator()(ddnf_node* n) const { return m.hash(n->get_tbv()); } }; typedef ptr_hashtable ddnf_nodes; private: tbv_manager& tbvm; tbv const& m_tbv; ddnf_node_vector m_children; unsigned m_refs; unsigned m_id; ddnf_node::hash m_hash; ddnf_node::eq m_eq; ddnf_nodes m_descendants; friend class ddnf_mgr; public: ddnf_node(ddnf_mgr& m, tbv_manager& tbvm, tbv const& tbv, unsigned id): tbvm(tbvm), m_tbv(tbv), m_children(m), m_refs(0), m_id(id), m_hash(tbvm), m_eq(tbvm), m_descendants(DEFAULT_HASHTABLE_INITIAL_CAPACITY, m_hash, m_eq) { } ~ddnf_node() {} unsigned inc_ref() { return ++m_refs; } void dec_ref() { SASSERT(m_refs > 0); --m_refs; if (m_refs == 0) { dealloc(this); } } ddnf_nodes& descendants() { return m_descendants; } unsigned get_id() const { return m_id; } unsigned num_children() const { return m_children.size(); } ddnf_node* operator[](unsigned index) { return m_children[index].get(); } tbv const& get_tbv() const { return m_tbv; } void add_child(ddnf_node* n); void remove_child(ddnf_node* n); bool contains_child(ddnf_node* n) const; void display(std::ostream& out) const { out << "node[" << get_id() << ": "; tbvm.display(out, m_tbv); for (unsigned i = 0; i < m_children.size(); ++i) { out << " " << m_children[i]->get_id(); } out << "]"; } }; typedef ddnf_node::ddnf_nodes ddnf_nodes; class ddnf_mgr { struct stats { unsigned m_num_inserts; unsigned m_num_comparisons; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; ddnf_node* m_root; ddnf_node_vector m_noderefs; bool m_internalized; tbv_manager m_tbv; ddnf_node::hash m_hash; ddnf_node::eq m_eq; ddnf_nodes m_nodes; svector m_marked; stats m_stats; public: ddnf_mgr(unsigned n): m_noderefs(*this), m_internalized(false), m_tbv(n), m_hash(m_tbv), m_eq(m_tbv), m_nodes(DEFAULT_HASHTABLE_INITIAL_CAPACITY, m_hash, m_eq) { tbv* bX = m_tbv.allocateX(); m_root = alloc(ddnf_node, *this, m_tbv, *bX, m_nodes.size()); m_noderefs.push_back(m_root); m_nodes.insert(m_root); } ~ddnf_mgr() { m_noderefs.reset(); m_tbv.reset(); } void inc_ref(ddnf_node* n) { n->inc_ref(); } void dec_ref(ddnf_node* n) { n->dec_ref(); } void reset_accumulate() { m_marked.resize(m_nodes.size()); for (unsigned i = 0; i < m_marked.size(); ++i) { m_marked[i] = false; } } void accumulate(tbv const& t, unsigned_vector& acc) { ddnf_node* n = find(t); ptr_vector todo; todo.push_back(n); while (!todo.empty()) { n = todo.back(); todo.pop_back(); unsigned id = n->get_id(); if (m_marked[id]) continue; acc.push_back(id); m_marked[id] = true; unsigned sz = n->num_children(); for (unsigned i = 0; i < sz; ++i) { todo.push_back((*n)[i]); } } } ddnf_node* insert(tbv const& t) { SASSERT(!m_internalized); ptr_vector new_tbvs; new_tbvs.push_back(&t); for (unsigned i = 0; i < new_tbvs.size(); ++i) { tbv const& nt = *new_tbvs[i]; IF_VERBOSE(10, m_tbv.display(verbose_stream() << "insert: ", nt); verbose_stream() << "\n";); if (contains(nt)) continue; ddnf_node* n = alloc(ddnf_node, *this, m_tbv, nt, m_noderefs.size()); m_noderefs.push_back(n); m_nodes.insert(n); insert(*m_root, n, new_tbvs); } return find(t); } tbv* allocate(uint64 v, unsigned hi, unsigned lo) { return m_tbv.allocate(v, hi, lo); } tbv_manager& get_tbv_manager() { return m_tbv; } unsigned size() const { return m_noderefs.size(); } ddnf_nodes const& lookup(tbv const& t) { internalize(); return find(t)->descendants(); } void display_statistics(std::ostream& out) const { std::cout << "Number of insertions: " << m_stats.m_num_inserts << "\n"; std::cout << "Number of comparisons: " << m_stats.m_num_comparisons << "\n"; std::cout << "Number of nodes: " << size() << "\n"; } void display(std::ostream& out) const { for (unsigned i = 0; i < m_noderefs.size(); ++i) { m_noderefs[i]->display(out); out << "\n"; } } bool contains(tbv const& t) { ddnf_node dummy(*this, m_tbv, t, 0); return m_nodes.contains(&dummy); } bool well_formed() { ptr_vector todo; todo.push_back(m_root); reset_accumulate(); while (!todo.empty()) { ddnf_node* n = todo.back(); todo.pop_back(); if (m_marked[n->get_id()]) continue; m_marked[n->get_id()] = true; unsigned sz = n->num_children(); for (unsigned i = 0; i < sz; ++i) { ddnf_node* child = (*n)[i]; if (!m_tbv.contains(n->get_tbv(), child->get_tbv())) { IF_VERBOSE(0, m_tbv.display(verbose_stream() << "parent ", n->get_tbv()); m_tbv.display(verbose_stream() << " does not contains child: ", child->get_tbv()); display(verbose_stream()); ); return false; } todo.push_back(child); } } return true; } private: ddnf_node* find(tbv const& t) { ddnf_node dummy(*this, m_tbv, t, 0); return *(m_nodes.find(&dummy)); } void insert(ddnf_node& root, ddnf_node* new_n, ptr_vector& new_intersections) { tbv const& new_tbv = new_n->get_tbv(); SASSERT(m_tbv.contains(root.get_tbv(), new_tbv)); if (&root == new_n) return; ++m_stats.m_num_inserts; bool inserted = false; for (unsigned i = 0; i < root.num_children(); ++i) { ddnf_node& child = *(root[i]); ++m_stats.m_num_comparisons; if (m_tbv.contains(child.get_tbv(), new_tbv)) { inserted = true; insert(child, new_n, new_intersections); } } if (inserted) { return; } ddnf_node_vector subset_children(*this); tbv* intr = m_tbv.allocate(); for (unsigned i = 0; i < root.num_children(); ++i) { ddnf_node& child = *(root[i]); // cannot be superset SASSERT(!m_tbv.contains(child.get_tbv(),new_tbv)); // checking for subset if (m_tbv.contains(new_tbv, child.get_tbv())) { subset_children.push_back(&child); ++m_stats.m_num_comparisons; } else if (m_tbv.intersect(child.get_tbv(), new_tbv, *intr)) { // this means there is a non-full intersection new_intersections.push_back(intr); intr = m_tbv.allocate(); m_stats.m_num_comparisons += 2; } else { m_stats.m_num_comparisons += 2; } } m_tbv.deallocate(intr); for (unsigned i = 0; i < subset_children.size(); ++i) { root.remove_child(subset_children[i].get()); new_n->add_child(subset_children[i].get()); } root.add_child(new_n); } void internalize() { // populate maps (should be bit-sets) of decendants. if (m_internalized) { return; } ptr_vector todo; todo.push_back(m_root); svector done(m_noderefs.size(), false); while (!todo.empty()) { ddnf_node& n = *todo.back(); if (done[n.get_id()]) { todo.pop_back(); continue; } unsigned sz = n.num_children(); bool all_done = true; for (unsigned i = 0; i < sz; ++i) { ddnf_node* child = n[i]; if (!done[child->get_id()]) { all_done = false; todo.push_back(child); } } if (all_done) { n.descendants().insert(&n); for (unsigned i = 0; i < sz; ++i) { ddnf_node* child = n[i]; add_table(n.descendants(), child->descendants()); } done[n.get_id()] = true; todo.pop_back(); } } m_internalized = true; } void add_table(ddnf_nodes& dst, ddnf_nodes const& src) { ddnf_nodes::iterator it = src.begin(), end = src.end(); for (; it != end; ++it) { dst.insert(*it); } } }; ddnf_core::ddnf_core(unsigned n) { m_imp = alloc(ddnf_mgr, n); } ddnf_core::~ddnf_core() { dealloc(m_imp); } ddnf_node* ddnf_core::insert(tbv const& t) { return m_imp->insert(t); } tbv_manager& ddnf_core::get_tbv_manager() { return m_imp->get_tbv_manager(); } unsigned ddnf_core::size() const { return m_imp->size(); } bool ddnf_core::contains(tbv const& t) { return m_imp->contains(t); } bool ddnf_core::well_formed() { return m_imp->well_formed(); } void ddnf_core::reset_accumulate() { return m_imp->reset_accumulate(); } void ddnf_core::accumulate(tbv const& t, unsigned_vector& acc) { return m_imp->accumulate(t, acc); } void ddnf_core::display(std::ostream& out) const { m_imp->display(out); } void ddnf_core::display_statistics(std::ostream& out) const { m_imp->display_statistics(out); } void ddnf_node::add_child(ddnf_node* n) { //SASSERT(!m_tbv.is_subset(n->m_tbv)); m_children.push_back(n); } void ddnf_node::remove_child(ddnf_node* n) { m_children.erase(n); } bool ddnf_node::contains_child(ddnf_node* n) const { return m_children.contains(n); } class ddnfs { u_map m_mgrs; public: ddnfs() {} ~ddnfs() { u_map::iterator it = m_mgrs.begin(), end = m_mgrs.end(); for (; it != end; ++it) { dealloc(it->m_value); } } tbv* allocate(unsigned num_bits, uint64 v, unsigned hi, unsigned lo) { return get(num_bits).allocate(v, hi, lo); } void insert(unsigned num_bits, tbv const& t) { get(num_bits).insert(t); } ddnf_mgr& get(unsigned num_bits) { return *insert(num_bits); } ddnf_nodes const& lookup(unsigned n, tbv const& t) const { return m_mgrs.find(n)->lookup(t); } void display(std::ostream& out) const { u_map::iterator it = m_mgrs.begin(), end = m_mgrs.end(); for (; it != end; ++it) { it->m_value->display(out); } } private: ddnf_mgr* insert(unsigned n) { ddnf_mgr* m = 0; if (!m_mgrs.find(n, m)) { m = alloc(ddnf_mgr, n); m_mgrs.insert(n, m); } return m; } }; class ddnf::imp { struct stats { stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; context& m_ctx; ast_manager& m; rule_manager& rm; bv_util bv; volatile bool m_cancel; ptr_vector m_todo; ast_mark m_visited1, m_visited2; ddnfs m_ddnfs; stats m_stats; obj_map m_expr2tbv; obj_map m_cache; expr_ref_vector m_trail; context m_inner_ctx; public: imp(context& ctx): m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), bv(m), m_cancel(false), m_trail(m), m_inner_ctx(m, m_ctx.get_register_engine(), m_ctx.get_fparams()) { params_ref params; params.set_sym("engine", symbol("datalog")); m_inner_ctx.updt_params(params); } ~imp() {} lbool query(expr* query) { m_ctx.ensure_opened(); rule_set& old_rules = m_ctx.get_rules(); rm.mk_query(query, old_rules); rule_set new_rules(m_ctx); IF_VERBOSE(10, verbose_stream() << "(ddnf.preprocess)\n";); if (!pre_process_rules(old_rules)) { return l_undef; } IF_VERBOSE(10, verbose_stream() << "(ddnf.compile)\n";); if (!compile_rules1(old_rules, new_rules)) { return l_undef; } IF_VERBOSE(15, m_ddnfs.display(verbose_stream());); dump_rules(new_rules); return l_undef; // return execute_rules(new_rules); } void cancel() { m_cancel = true; m_inner_ctx.cancel(); } void cleanup() { m_cancel = false; } void reset_statistics() { m_stats.reset(); } void collect_statistics(statistics& st) const { } void display_certificate(std::ostream& out) const { expr_ref ans = get_answer(); out << mk_pp(ans, m) << "\n"; } expr_ref get_answer() const { UNREACHABLE(); return expr_ref(m.mk_true(), m); } private: proof_ref get_proof() const { scoped_proof sp(m); proof_ref pr(m); return pr; } bool pre_process_rules(rule_set const& rules) { m_visited1.reset(); m_todo.reset(); m_cache.reset(); m_expr2tbv.reset(); datalog::rule_set::iterator it = rules.begin(); datalog::rule_set::iterator end = rules.end(); for (; it != end; ++it) { if (!pre_process_rule(**it)) { return false; } } return true; } bool pre_process_rule(rule const& r) { // all predicates are monadic. unsigned utsz = r.get_uninterpreted_tail_size(); unsigned sz = r.get_tail_size(); for (unsigned i = utsz; i < sz; ++i) { m_todo.push_back(r.get_tail(i)); } if (process_todo()) { return true; } else { r.display(m_ctx, std::cout); return false; } } bool process_todo() { while (!m_todo.empty()) { expr* e = m_todo.back(); m_todo.pop_back(); if (m_visited1.is_marked(e)) { continue; } m_visited1.mark(e, true); if (is_var(e)) { continue; } if (is_quantifier(e)) { return false; } if (m.is_and(e) || m.is_or(e) || m.is_iff(e) || m.is_not(e) || m.is_implies(e)) { m_todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); continue; } if (is_ground(e)) { continue; } if (process_atomic(e)) { continue; } IF_VERBOSE(0, verbose_stream() << "Could not handle: " << mk_pp(e, m) << "\n";); return false; } return true; } bool process_atomic(expr* e) { expr* e1, *e2, *e3; unsigned lo, hi; if (m.is_eq(e, e1, e2) && bv.is_bv(e1)) { if (is_var(e1) && is_ground(e2)) { return process_eq(e, to_var(e1), bv.get_bv_size(e1)-1, 0, e2); } if (is_var(e2) && is_ground(e1)) { return process_eq(e, to_var(e2), bv.get_bv_size(e2)-1, 0, e1); } if (bv.is_extract(e1, lo, hi, e3) && is_var(e3) && is_ground(e2)) { return process_eq(e, to_var(e3), hi, lo, e2); } if (bv.is_extract(e2, lo, hi, e3) && is_var(e3) && is_ground(e1)) { return process_eq(e, to_var(e3), hi, lo, e1); } if (is_var(e1) && is_var(e2)) { return true; } } return false; } bool process_eq(expr* e, var* v, unsigned hi, unsigned lo, expr* c) { rational val; unsigned sz_c; unsigned sz_v = bv.get_bv_size(v); if (!bv.is_numeral(c, val, sz_c)) { return false; } if (!val.is_uint64()) { return false; } // v[hi:lo] = val tbv* tv = m_ddnfs.allocate(sz_v, val.get_uint64(), hi, lo); m_ddnfs.insert(sz_v, *tv); m_expr2tbv.insert(e, tv); // std::cout << mk_pp(v, m) << " " << lo << " " << hi << " " << v << " " << tbv << "\n"; return true; } void init_ctx(rule_set& rules) { m_inner_ctx.reset(); func_decl_set const& predicates = m_ctx.get_predicates(); for (func_decl_set::iterator fit = predicates.begin(); fit != predicates.end(); ++fit) { m_inner_ctx.register_predicate(*fit, false); } m_inner_ctx.ensure_opened(); m_inner_ctx.replace_rules(rules); m_inner_ctx.close(); } void dump_rules(rule_set& rules) { init_ctx(rules); m_inner_ctx.display_smt2(0, 0, std::cout); } lbool execute_rules(rule_set& rules) { init_ctx(rules); ptr_vector heads; rule_set::decl2rules::iterator dit = rules.begin_grouped_rules(); rule_set::decl2rules::iterator dend = rules.end_grouped_rules(); for (; dit != dend; ++dit) { heads.push_back(dit->m_key); } return m_inner_ctx.rel_query(heads.size(), heads.c_ptr()); } bool compile_rules1(rule_set const& rules, rule_set& new_rules) { datalog::rule_set::iterator it = rules.begin(); datalog::rule_set::iterator end = rules.end(); unsigned idx = 0; for (; it != end; ++idx, ++it) { if (!compile_rule1(**it, rules, new_rules)) { return false; } } return true; } bool compile_rule1(rule& r, rule_set const& old_rules, rule_set& new_rules) { app_ref head(m), pred(m); app_ref_vector body(m); expr_ref tmp(m); compile_predicate(r.get_head(), head); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned sz = r.get_tail_size(); for (unsigned i = 0; i < utsz; ++i) { compile_predicate(r.get_tail(i), pred); body.push_back(pred); } for (unsigned i = utsz; i < sz; ++i) { compile_expr(r.get_tail(i), tmp); body.push_back(to_app(tmp)); } rule* r_new = rm.mk(head, body.size(), body.c_ptr(), 0, r.name(), false); new_rules.add_rule(r_new); IF_VERBOSE(20, r_new->display(m_ctx, verbose_stream());); if (old_rules.is_output_predicate(r.get_decl())) { new_rules.set_output_predicate(r_new->get_decl()); } return true; } void compile_predicate(app* p, app_ref& result) { sort_ref_vector domain(m); func_decl* d = p->get_decl(); SASSERT(d->get_family_id() == null_family_id); for (unsigned i = 0; i < p->get_num_args(); ++i) { domain.push_back(compile_sort(m.get_sort(p->get_arg(i)))); } func_decl_ref fn(m); fn = m.mk_func_decl(d->get_name(), domain.size(), domain.c_ptr(), m.mk_bool_sort()); m_ctx.register_predicate(fn, false); expr_ref_vector args(m); expr_ref tmp(m); for (unsigned i = 0; i < p->get_num_args(); ++i) { compile_expr(p->get_arg(i), tmp); args.push_back(tmp); } result = m.mk_app(fn, args.size(), args.c_ptr()); } void insert_cache(expr* e, expr* r) { m_trail.push_back(r); m_cache.insert(e, r); } void compile_var(var* v, var_ref& result) { expr* r; if (m_cache.find(v, r)) { result = to_var(r); } else { unsigned idx = v->get_idx(); result = m.mk_var(idx, compile_sort(v->get_sort())); insert_cache(v, result); } } sort* compile_sort(sort* s) { if (m.is_bool(s)) { return s; } if (bv.is_bv_sort(s)) { unsigned sz = bv.get_bv_size(s); ddnf_mgr const& mgr = m_ddnfs.get(sz); unsigned num_elems = mgr.size(); unsigned nb = 1; while ((1UL << nb) <= num_elems) { ++nb; } return bv.mk_sort(nb); } UNREACHABLE(); return 0; } void compile_expr(expr* e, expr_ref& result) { expr* r = 0; if (m_cache.find(e, r)) { result = r; return; } if (is_ground(e)) { result = e; m_cache.insert(e, result); return; } if (is_var(e)) { var_ref w(m); compile_var(to_var(e), w); result = w; return; } if (m.is_and(e) || m.is_or(e) || m.is_iff(e) || m.is_not(e) || m.is_implies(e)) { app* a = to_app(e); expr_ref tmp(m); expr_ref_vector args(m); for (unsigned i = 0; i < a->get_num_args(); ++i) { compile_expr(a->get_arg(i), tmp); args.push_back(tmp); } result = m.mk_app(a->get_decl(), args.size(), args.c_ptr()); insert_cache(e, result); return; } expr* e1, *e2, *e3; unsigned lo, hi; if (m.is_eq(e, e1, e2) && bv.is_bv(e1)) { if (is_var(e1) && is_ground(e2)) { compile_eq(e, result, to_var(e1), bv.get_bv_size(e1)-1, 0, e2); } else if (is_var(e2) && is_ground(e1)) { compile_eq(e, result, to_var(e2), bv.get_bv_size(e2)-1, 0, e1); } else if (bv.is_extract(e1, lo, hi, e3) && is_var(e3) && is_ground(e2)) { compile_eq(e, result, to_var(e3), hi, lo, e2); } else if (bv.is_extract(e2, lo, hi, e3) && is_var(e3) && is_ground(e1)) { compile_eq(e, result, to_var(e3), hi, lo, e1); } else if (is_var(e1) && is_var(e2)) { var_ref v1(m), v2(m); compile_var(to_var(e1), v1); compile_var(to_var(e2), v2); result = m.mk_eq(v1, v2); } else { UNREACHABLE(); } insert_cache(e, result); return; } std::cout << mk_pp(e, m) << "\n"; UNREACHABLE(); } void compile_eq(expr* e, expr_ref& result, var* v, unsigned hi, unsigned lo, expr* c) { tbv* t; // TBD: hi, lo are ignored. VERIFY(m_expr2tbv.find(e, t)); var_ref w(m); compile_var(v, w); unsigned num_bits = bv.get_bv_size(c); ddnf_nodes const& ns = m_ddnfs.lookup(num_bits, *t); ddnf_nodes::iterator it = ns.begin(), end = ns.end(); expr_ref_vector eqs(m); sort* s = m.get_sort(w); for (; it != end; ++it) { eqs.push_back(m.mk_eq(w, bv.mk_numeral(rational((*it)->get_id()), s))); } switch (eqs.size()) { case 0: UNREACHABLE(); result = m.mk_false(); break; case 1: result = eqs[0].get(); break; default: result = m.mk_or(eqs.size(), eqs.c_ptr()); break; } } }; ddnf::ddnf(context& ctx): datalog::engine_base(ctx.get_manager(),"tabulation"), m_imp(alloc(imp, ctx)) { } ddnf::~ddnf() { dealloc(m_imp); } lbool ddnf::query(expr* query) { return m_imp->query(query); } void ddnf::cancel() { m_imp->cancel(); } void ddnf::cleanup() { m_imp->cleanup(); } void ddnf::reset_statistics() { m_imp->reset_statistics(); } void ddnf::collect_statistics(statistics& st) const { m_imp->collect_statistics(st); } void ddnf::display_certificate(std::ostream& out) const { m_imp->display_certificate(out); } expr_ref ddnf::get_answer() { return m_imp->get_answer(); } }; z3-4.4.0/src/muz/ddnf/ddnf.h0000644000175000017500000000307512540347414015253 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: ddnf.h Abstract: DDNF based engine. Author: Nikolaj Bjorner (nbjorner) 2014-08-21 Revision History: --*/ #ifndef _DDNF__H_ #define _DDNF__H_ #include "ast.h" #include "lbool.h" #include "statistics.h" #include "dl_engine_base.h" class tbv; class tbv_manager; namespace datalog { class context; class ddnf : public engine_base { class imp; imp* m_imp; public: ddnf(context& ctx); ~ddnf(); virtual lbool query(expr* query); virtual void cancel(); virtual void cleanup(); virtual void reset_statistics(); virtual void collect_statistics(statistics& st) const; virtual void display_certificate(std::ostream& out) const; virtual expr_ref get_answer(); }; class ddnf_node; class ddnf_mgr; class ddnf_core { ddnf_mgr* m_imp; public: ddnf_core(unsigned n); ~ddnf_core(); ddnf_node* insert(tbv const& t); tbv_manager& get_tbv_manager(); unsigned size() const; bool contains(tbv const& t); bool well_formed(); // // accumulate labels covered by the stream of tbvs, // such that labels that are covered by the current // tbv but not the previous tbvs are included. // void reset_accumulate(); void accumulate(tbv const& t, unsigned_vector& labels); void display(std::ostream& out) const; void display_statistics(std::ostream& out) const; }; }; #endif z3-4.4.0/src/muz/tab/0000755000175000017500000000000012540347414014015 5ustar michaelmichaelz3-4.4.0/src/muz/tab/tab_context.cpp0000644000175000017500000016436012540347414017045 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: tab_context.cpp Abstract: Tabulation/subsumption/cyclic proof context. Author: Nikolaj Bjorner (nbjorner) 2013-01-15 Revision History: --*/ #include "tab_context.h" #include "trail.h" #include "dl_rule_set.h" #include "dl_context.h" #include "dl_mk_rule_inliner.h" #include "smt_kernel.h" #include "qe_lite.h" #include "bool_rewriter.h" #include "th_rewriter.h" #include "datatype_decl_plugin.h" #include "for_each_expr.h" #include "matcher.h" #include "scoped_proof.h" #include "fixedpoint_params.hpp" namespace tb { // semantic matcher. class matcher { typedef std::pair expr_pair; ast_manager& m; svector m_todo; datatype_util m_dt; lbool is_eq(expr* _s, expr* _t) { if (_s == _t) { return l_true; } if (!is_app(_s) || !is_app(_t)) { return l_undef; } app* s = to_app(_s); app* t = to_app(_t); if (m.is_value(s) && m.is_value(t)) { IF_VERBOSE(2, verbose_stream() << "different:" << mk_pp(s, m) << " " << mk_pp(t, m) << "\n";); return l_false; } if (m_dt.is_constructor(s) && m_dt.is_constructor(t)) { if (s->get_decl() == t->get_decl()) { lbool state = l_true; for (unsigned i = 0; i < s->get_num_args(); ++i) { // move is_eq: decompose arguments to constraints. switch (is_eq(s->get_arg(i), t->get_arg(i))) { case l_undef: state = l_undef; break; case l_false: return l_false; default: break; } } return state; } else { IF_VERBOSE(2, verbose_stream() << "different constructors:" << mk_pp(s, m) << " " << mk_pp(t, m) << "\n";); return l_false; } } return l_undef; } bool match_var(var* v, app* t, substitution& s, expr_ref_vector& conds) { expr_offset r; if (s.find(v, 0, r)) { app* p = to_app(r.get_expr()); switch (is_eq(p, t)) { case l_true: break; case l_false: return false; default: conds.push_back(m.mk_eq(p, t)); break; } } else { s.insert(v, 0, expr_offset(t, 1)); } return true; } bool match_app(app* p, app* t, substitution& s, expr_ref_vector& conds) { switch(is_eq(p, t)) { case l_true: return true; case l_false: return false; default: conds.push_back(m.mk_eq(p, t)); return true; } } public: matcher(ast_manager& m): m(m), m_dt(m) {} bool operator()(app* pat, app* term, substitution& s, expr_ref_vector& conds) { // top-most term to match is a predicate. The predicates should be the same. if (pat->get_decl() != term->get_decl() || pat->get_num_args() != term->get_num_args()) { return false; } m_todo.reset(); for (unsigned i = 0; i < pat->get_num_args(); ++i) { m_todo.push_back(expr_pair(pat->get_arg(i), term->get_arg(i))); } while (!m_todo.empty()) { expr_pair const& pr = m_todo.back(); expr* p = pr.first; expr* t = pr.second; m_todo.pop_back(); if (!is_app(t)) { IF_VERBOSE(2, verbose_stream() << "term is not app\n";); return false; } else if (is_var(p) && match_var(to_var(p), to_app(t), s, conds)) { continue; } else if (!is_app(p)) { IF_VERBOSE(2, verbose_stream() << "pattern is not app\n";); return false; } else if (!match_app(to_app(p), to_app(t), s, conds)) { return false; } } return true; } }; class clause { app_ref m_head; // head predicate app_ref_vector m_predicates; // predicates used in goal expr_ref m_constraint; // side constraint unsigned m_seqno; // sequence number of goal unsigned m_index; // index of goal into set of goals unsigned m_num_vars; // maximal free variable index+1 unsigned m_predicate_index; // selected predicate unsigned m_parent_rule; // rule used to produce goal unsigned m_parent_index; // index of parent goal unsigned m_next_rule; // next rule to expand goal on unsigned m_ref; // reference count public: clause(ast_manager& m): m_head(m), m_predicates(m), m_constraint(m), m_seqno(0), m_index(0), m_num_vars(0), m_predicate_index(0), m_parent_rule(0), m_parent_index(0), m_next_rule(static_cast(-1)), m_ref(0) { } void set_seqno(unsigned seqno) { m_seqno = seqno; } unsigned get_seqno() const { return m_seqno; } unsigned get_next_rule() const { return m_next_rule; } void inc_next_rule() { m_next_rule++; } unsigned get_predicate_index() const { return m_predicate_index; } void set_predicate_index(unsigned i) { m_predicate_index = i; } unsigned get_num_predicates() const { return m_predicates.size(); } app* get_predicate(unsigned i) const { return m_predicates[i]; } expr* get_constraint() const { return m_constraint; } unsigned get_num_vars() const { return m_num_vars; } unsigned get_index() const { return m_index; } void set_index(unsigned index) { m_index = index; } app* get_head() const { return m_head; } func_decl* get_decl() const { return m_head->get_decl(); } void set_head(app* h) { m_head = h; } unsigned get_parent_index() const { return m_parent_index; } unsigned get_parent_rule() const { return m_parent_rule; } void set_parent(ref& parent) { m_parent_index = parent->get_index(); m_parent_rule = parent->get_next_rule(); } expr_ref get_body() const { ast_manager& m = get_manager(); expr_ref_vector fmls(m); expr_ref fml(m); for (unsigned i = 0; i < m_predicates.size(); ++i) { fmls.push_back(m_predicates[i]); } fmls.push_back(m_constraint); qe::flatten_and(fmls); bool_rewriter(m).mk_and(fmls.size(), fmls.c_ptr(), fml); return fml; } void get_free_vars(ptr_vector& vars) const { expr_free_vars fv; fv(m_head); for (unsigned i = 0; i < m_predicates.size(); ++i) { fv.accumulate(m_predicates[i]); } fv.accumulate(m_constraint); vars.append(fv.size(), fv.c_ptr()); } expr_ref to_formula() const { ast_manager& m = get_manager(); expr_ref body = get_body(); if (m.is_true(body)) { body = m_head; } else { body = m.mk_implies(body, m_head); } ptr_vector vars; svector names; get_free_vars(vars); mk_fresh_name fresh; fresh.add(body); vars.reverse(); for (unsigned i = 0; i < vars.size(); ++i) { names.push_back(fresh.next()); if (!vars[i]) vars[i] = m.mk_bool_sort(); } if (!vars.empty()) { body = m.mk_forall(vars.size(), vars.c_ptr(), names.c_ptr(), body); } return body; } void init(app* head, app_ref_vector const& predicates, expr* constraint) { m_index = 0; m_predicate_index = 0; m_next_rule = static_cast(-1); m_head = head; m_predicates.reset(); m_predicates.append(predicates); m_constraint = constraint; ptr_vector sorts; get_free_vars(sorts); m_num_vars = sorts.size(); reduce_equalities(); } void init(datalog::rule_ref& g) { m_index = 0; m_predicate_index = 0; m_next_rule = static_cast(-1); init_from_rule(g); reduce_equalities(); // IF_VERBOSE(1, display(verbose_stream());); } void inc_ref() { m_ref++; } void dec_ref() { --m_ref; if (m_ref == 0) { dealloc(this); } } void display(std::ostream& out) const { ast_manager& m = m_head.get_manager(); expr_ref_vector fmls(m); expr_ref fml(m); for (unsigned i = 0; i < m_predicates.size(); ++i) { fmls.push_back(m_predicates[i]); } fmls.push_back(m_constraint); bool_rewriter(m).mk_and(fmls.size(), fmls.c_ptr(), fml); if (!m.is_false(m_head)) { if (m.is_true(fml)) { fml = m_head; } else { fml = m.mk_implies(fml, m_head); } } out << mk_pp(fml, m) << "\n"; } private: ast_manager& get_manager() const { return m_head.get_manager(); } // Given a rule, initialize fields: // - m_num_vars - number of free variables in rule // - m_head - head predicate // - m_predicates - auxiliary predicates in body. // - m_constraint - side constraint // void init_from_rule(datalog::rule_ref const& r) { ast_manager& m = get_manager(); expr_ref_vector fmls(m); unsigned utsz = r->get_uninterpreted_tail_size(); unsigned tsz = r->get_tail_size(); for (unsigned i = utsz; i < tsz; ++i) { fmls.push_back(r->get_tail(i)); } m_num_vars = 1 + r.get_manager().get_counter().get_max_rule_var(*r); m_head = r->get_head(); m_predicates.reset(); for (unsigned i = 0; i < utsz; ++i) { m_predicates.push_back(r->get_tail(i)); } bool_rewriter(m).mk_and(fmls.size(), fmls.c_ptr(), m_constraint); } // Simplify a clause by applying equalities as substitutions on predicates. // x = t[y], if x does not occur in t[y], then add t[y] to subst. void reduce_equalities() { ast_manager& m = get_manager(); th_rewriter rw(m); unsigned delta[1] = { 0 }; expr_ref_vector fmls(m); expr_ref tmp(m); substitution subst(m); subst.reserve(1, get_num_vars()); qe::flatten_and(m_constraint, fmls); unsigned num_fmls = fmls.size(); for (unsigned i = 0; i < num_fmls; ++i) { if (get_subst(rw, subst, i, fmls)) { fmls[i] = m.mk_true(); } } subst.apply(1, delta, expr_offset(m_head, 0), tmp); m_head = to_app(tmp); for (unsigned i = 0; i < m_predicates.size(); ++i) { subst.apply(1, delta, expr_offset(m_predicates[i].get(), 0), tmp); m_predicates[i] = to_app(tmp); } bool_rewriter(m).mk_and(fmls.size(), fmls.c_ptr(), m_constraint); subst.apply(1, delta, expr_offset(m_constraint, 0), m_constraint); rw(m_constraint); } bool get_subst(th_rewriter& rw, substitution& S, unsigned i, expr_ref_vector& fmls) { ast_manager& m = get_manager(); unsigned delta[1] = { 0 }; expr* f = fmls[i].get(); expr_ref e(m), tr(m); expr* t, *v; S.apply(1, delta, expr_offset(f, 0), e); rw(e); fmls[i] = e; if (!m.is_eq(e, v, t)) { return false; } if (!is_var(v)) { std::swap(v, t); } if (!is_var(v)) { return false; } if (!can_be_substituted(m, t)) { return false; } SASSERT(!S.contains(to_var(v), 0)); S.push_scope(); S.insert(to_var(v)->get_idx(), 0, expr_offset(t, 0)); if (!S.acyclic()) { S.pop_scope(); return false; } fmls[i] = m.mk_true(); return true; } struct non_constructor {}; struct constructor_test { ast_manager& m; datatype_util dt; constructor_test(ast_manager& m): m(m), dt(m) {} void operator()(app* e) { if (!m.is_value(e) && !dt.is_constructor(e->get_decl())) { throw non_constructor(); } } void operator()(var* v) { } void operator()(quantifier* ) { throw non_constructor(); } }; bool can_be_substituted(ast_manager& m, expr* t) { constructor_test p(m); try { quick_for_each_expr(p, t); } catch (non_constructor) { return false; } return true; } }; // rules class rules { typedef obj_map map; vector > m_rules; map m_index; public: typedef vector >::const_iterator iterator; iterator begin() const { return m_rules.begin(); } iterator end() const { return m_rules.end(); } void init(datalog::rule_set const& rules) { reset(); datalog::rule_manager& rm = rules.get_rule_manager(); datalog::rule_ref r(rm); datalog::rule_set::iterator it = rules.begin(); datalog::rule_set::iterator end = rules.end(); for (unsigned i = 0; it != end; ++it) { r = *it; ref g = alloc(clause, rm.get_manager()); g->init(r); g->set_index(i++); insert(g); } } void insert(ref& g) { unsigned idx = m_rules.size(); m_rules.push_back(g); func_decl* f = g->get_decl(); map::obj_map_entry* e = m_index.insert_if_not_there2(f, unsigned_vector()); SASSERT(e); e->get_data().m_value.push_back(idx); } unsigned get_num_rules(func_decl* p) const { map::obj_map_entry* e = m_index.find_core(p); if (e) { return e->get_data().get_value().size(); } else { return 0; } } void get_decls(ptr_vector& decls) const { map::iterator it = m_index.begin(); map::iterator end = m_index.end(); for (; it != end; ++it) { decls.push_back(it->m_key); } } ref get_rule(func_decl* p, unsigned idx) const { map::obj_map_entry* e = m_index.find_core(p); SASSERT(p); unsigned rule_id = e->get_data().get_value()[idx]; return m_rules[rule_id]; } private: void reset() { m_rules.reset(); m_index.reset(); } }; // subsumption index structure. class index { ast_manager& m; app_ref_vector m_preds; app_ref m_head; expr_ref m_precond; expr_ref_vector m_sideconds; ref m_clause; vector > m_index; matcher m_matcher; expr_ref_vector m_refs; obj_hashtable m_sat_lits; substitution m_subst; qe_lite m_qe; uint_set m_empty_set; bool_rewriter m_rw; smt_params m_fparams; smt::kernel m_solver; volatile bool m_cancel; public: index(ast_manager& m): m(m), m_preds(m), m_head(m), m_precond(m), m_sideconds(m), m_matcher(m), m_refs(m), m_subst(m), m_qe(m), m_rw(m), m_solver(m, m_fparams), m_cancel(false) {} void insert(ref& g) { m_index.push_back(g); } bool is_subsumed(ref& g, unsigned& subsumer) { setup(*g); m_clause = g; m_solver.push(); m_solver.assert_expr(m_precond); bool found = find_match(subsumer); m_solver.pop(1); return found; } void cancel() { m_cancel = true; m_solver.cancel(); m_qe.set_cancel(true); } void cleanup() { m_solver.reset_cancel(); m_qe.set_cancel(false); m_cancel = false; } void reset() { m_index.reset(); } private: void setup(clause const& g) { m_preds.reset(); m_refs.reset(); m_sat_lits.reset(); expr_ref_vector fmls(m); expr_ref_vector vars(m); expr_ref fml(m); ptr_vector sorts; g.get_free_vars(sorts); var_subst vs(m, false); for (unsigned i = 0; i < sorts.size(); ++i) { if (!sorts[i]) { sorts[i] = m.mk_bool_sort(); } vars.push_back(m.mk_const(symbol(i), sorts[i])); } vs(g.get_head(), vars.size(), vars.c_ptr(), fml); m_head = to_app(fml); for (unsigned i = 0; i < g.get_num_predicates(); ++i) { vs(g.get_predicate(i), vars.size(), vars.c_ptr(), fml); m_preds.push_back(to_app(fml)); } vs(g.get_constraint(), vars.size(), vars.c_ptr(), fml); fmls.push_back(fml); m_precond = m.mk_and(fmls.size(), fmls.c_ptr()); IF_VERBOSE(2, verbose_stream() << "setup-match: "; for (unsigned i = 0; i < m_preds.size(); ++i) { verbose_stream() << mk_pp(m_preds[i].get(), m) << " "; } verbose_stream() << mk_pp(m_precond, m) << "\n";); } // extract pre_cond => post_cond validation obligation from match. bool find_match(unsigned& subsumer) { for (unsigned i = 0; !m_cancel && i < m_index.size(); ++i) { if (match_rule(i)) { subsumer = m_index[i]->get_seqno(); return true; } } return false; } // // check that each predicate in r is matched by some predicate in premise. // for now: skip multiple matches within the same rule (incomplete). // bool match_rule(unsigned rule_index) { clause const& g = *m_index[rule_index]; m_sideconds.reset(); m_subst.reset(); m_subst.reserve(2, g.get_num_vars()); IF_VERBOSE(2, g.display(verbose_stream() << "try-match\n");); return match_head(g); } bool match_head(clause const& g) { return m_head->get_decl() == g.get_decl() && m_matcher(m_head, g.get_head(), m_subst, m_sideconds) && match_predicates(0, g); } bool match_predicates(unsigned predicate_index, clause const& g) { if (predicate_index == g.get_num_predicates()) { return check_substitution(g); } app* q = g.get_predicate(predicate_index); for (unsigned i = 0; !m_cancel && i < m_preds.size(); ++i) { app* p = m_preds[i].get(); m_subst.push_scope(); unsigned limit = m_sideconds.size(); IF_VERBOSE(2, for (unsigned j = 0; j < predicate_index; ++j) { verbose_stream() << " "; } verbose_stream() << mk_pp(q, m) << " = " << mk_pp(p, m) << "\n"; ); if (q->get_decl() == p->get_decl() && m_matcher(q, p, m_subst, m_sideconds) && match_predicates(predicate_index + 1, g)) { return true; } m_subst.pop_scope(1); m_sideconds.resize(limit); } return false; } bool check_substitution(clause const& g) { unsigned deltas[2] = {0, 0}; expr_ref q(m), postcond(m); expr_ref_vector fmls(m_sideconds); m_subst.reset_cache(); for (unsigned i = 0; !m_cancel && i < fmls.size(); ++i) { m_subst.apply(2, deltas, expr_offset(fmls[i].get(), 0), q); fmls[i] = q; } m_subst.apply(2, deltas, expr_offset(g.get_constraint(), 0), q); fmls.push_back(q); m_qe(m_empty_set, false, fmls); qe::flatten_and(fmls); for (unsigned i = 0; i < fmls.size(); ++i) { expr_ref n = normalize(fmls[i].get()); if (m_sat_lits.contains(n)) { return false; } } m_rw.mk_and(fmls.size(), fmls.c_ptr(), postcond); if (m_cancel) { return false; } if (m.is_false(postcond)) { return false; } if (m.is_true(postcond)) { return true; } IF_VERBOSE(2, for (unsigned i = 0; i < g.get_num_predicates(); ++i) { verbose_stream() << " "; } verbose_stream() << "check: " << mk_pp(postcond, m, 7 + g.get_num_predicates()) << "\n";); if (!is_ground(postcond)) { IF_VERBOSE(1, verbose_stream() << "TBD: non-ground\n" << mk_pp(postcond, m) << "\n"; m_clause->display(verbose_stream()); verbose_stream() << "\n=>\n"; g.display(verbose_stream()); verbose_stream() << "\n";); return false; } postcond = m.mk_not(postcond); m_solver.push(); m_solver.assert_expr(postcond); lbool is_sat = m_solver.check(); if (is_sat == l_true) { expr_ref tmp(m); expr* n; model_ref mdl; m_solver.get_model(mdl); for (unsigned i = 0; i < fmls.size(); ++i) { n = fmls[i].get(); if (mdl->eval(n, tmp) && m.is_false(tmp)) { m_refs.push_back(normalize(n)); m_sat_lits.insert(m_refs.back()); } } } m_solver.pop(1); return is_sat == l_false; } expr_ref normalize(expr* e) { expr* x, *y; if (m.is_eq(e, x, y) && x->get_id() > y->get_id()) { return expr_ref(m.mk_eq(y, x), m); } else { return expr_ref(e, m); } } }; // predicate selection strategy. class selection { enum strategy { WEIGHT_SELECT, BASIC_WEIGHT_SELECT, FIRST_SELECT, VAR_USE_SELECT }; typedef svector double_vector; typedef obj_map score_map; typedef obj_map pred_map; ast_manager& m; datatype_util dt; score_map m_score_map; double_vector m_scores; double_vector m_var_scores; strategy m_strategy; pred_map m_pred_map; expr_ref_vector m_refs; double m_weight_multiply; unsigned m_update_frequency; unsigned m_next_update; public: selection(datalog::context& ctx): m(ctx.get_manager()), dt(m), m_refs(m), m_weight_multiply(1.0), m_update_frequency(20), m_next_update(20) { set_strategy(ctx.tab_selection()); } void init(rules const& rs) { reset(); double_vector& scores = m_scores; rules::iterator it = rs.begin(), end = rs.end(); for (; it != end; ++it) { ref g = *it; app* p = g->get_head(); scores.reset(); basic_score_predicate(p, scores); insert_score(p->get_decl(), scores); } normalize_scores(rs); } unsigned select(clause const& g) { switch(m_strategy) { case WEIGHT_SELECT: return weight_select(g); case BASIC_WEIGHT_SELECT: return basic_weight_select(g); case FIRST_SELECT: return trivial_select(g); case VAR_USE_SELECT: return andrei_select(g); default: return weight_select(g); } } void reset() { m_score_map.reset(); m_scores.reset(); m_var_scores.reset(); } private: // determine if constructors in p are matches by rules. bool is_reductive(app* p, double_vector const& p_scores) { func_decl* f = p->get_decl(); score_map::obj_map_entry* e = m_score_map.find_core(f); if (!e) { return false; } double_vector const& scores = e->get_data().m_value; SASSERT(scores.size() == p->get_num_args()); bool has_reductive = false; bool is_red = true; for (unsigned i = 0; is_red && i < scores.size(); ++i) { if (scores[i] >= 1) { has_reductive = true; is_red &= p_scores[i] >= 1; } } return has_reductive && is_red; } void set_strategy(symbol const& str) { if (str == symbol("weight")) { m_strategy = WEIGHT_SELECT; } if (str == symbol("basic-weight")) { m_strategy = BASIC_WEIGHT_SELECT; } else if (str == symbol("first")) { m_strategy = FIRST_SELECT; } else if (str == symbol("var-use")) { m_strategy = VAR_USE_SELECT; } else { m_strategy = WEIGHT_SELECT; } } unsigned trivial_select(clause const& g) { return 0; } unsigned andrei_select(clause const& g) { score_variables(g); double_vector& scores = m_scores; double max_score = 0; unsigned result = 0; for (unsigned i = 0; i < g.get_num_predicates(); ++i) { scores.reset(); double_vector p_scores; double score = 0; app* p = g.get_predicate(i); basic_score_predicate(p, scores); m_score_map.find(p->get_decl(), p_scores); SASSERT(p_scores.empty() || p->get_num_args() == p_scores.size()); p_scores.resize(p->get_num_args()); for (unsigned j = 0; j < p->get_num_args(); ++j) { if (is_var(p->get_arg(j))) { unsigned idx = to_var(p->get_arg(j))->get_idx(); score += m_var_scores[idx]; } else { IF_VERBOSE(2, verbose_stream() << p_scores[j] << " " << scores[j] << "\n";); score += p_scores[j]*scores[j]; } } IF_VERBOSE(2, verbose_stream() << "score: " << mk_pp(p, m) << " " << score << "\n";); if (score > max_score) { max_score = score; result = i; } } IF_VERBOSE(1, verbose_stream() << "select:" << result << "\n";); return result; } unsigned basic_weight_select(clause const& g) { double max_score = 0; unsigned result = 0; for (unsigned i = 0; i < g.get_num_predicates(); ++i) { app* p = g.get_predicate(i); double score = basic_score_predicate(p); IF_VERBOSE(2, verbose_stream() << "score: " << mk_pp(p, m) << " " << score << "\n";); if (score > max_score) { max_score = score; result = i; } } IF_VERBOSE(2, verbose_stream() << "select " << result << "\n";); return result; } unsigned weight_select(clause const& g) { prepare_weight_select(); double max_score = 0; unsigned result = 0; for (unsigned i = 0; i < g.get_num_predicates(); ++i) { app* p = g.get_predicate(i); double score = score_predicate(p); IF_VERBOSE(2, verbose_stream() << "score: " << mk_pp(p, m) << " " << score << "\n";); if (score > max_score) { max_score = score; result = i; } } IF_VERBOSE(2, verbose_stream() << "select " << result << "\n";); return result; } void score_variables(clause const& g) { m_var_scores.reset(); for (unsigned i = 0; i < g.get_num_predicates(); ++i) { app* p = g.get_predicate(i); score_variables(p); } } void score_variables(app* p) { score_map::obj_map_entry* e = m_score_map.find_core(p->get_decl()); if (!e) { return; } double_vector& scores = e->get_data().m_value; for (unsigned i = 0; i < p->get_num_args(); ++i) { if (is_var(p->get_arg(i))) { unsigned idx = to_var(p->get_arg(i))->get_idx(); if (m_var_scores.size() <= idx) { m_var_scores.resize(idx+1); } m_var_scores[idx] += scores[i]; } } } void normalize_scores(rules const& rs) { ptr_vector decls; rs.get_decls(decls); for (unsigned i = 0; i < decls.size(); ++i) { unsigned nr = rs.get_num_rules(decls[i]); score_map::obj_map_entry& e = *m_score_map.find_core(decls[i]); double_vector& scores = e.get_data().m_value; for (unsigned j = 0; j < scores.size(); ++j) { scores[j] = scores[j]/nr; } } } double basic_score_predicate(app* p) { double score = 1; for (unsigned i = 0; i < p->get_num_args(); ++i) { score += score_argument(p->get_arg(i)); } return score; } void basic_score_predicate(app* p, double_vector& scores) { for (unsigned i = 0; i < p->get_num_args(); ++i) { scores.push_back(score_argument(p->get_arg(i))); } } double score_predicate(app* p) { double score = 1; if (find_score(p, score)) { return score; } for (unsigned i = 0; i < p->get_num_args(); ++i) { score += score_argument(p->get_arg(i)); } score = adjust_score(score); insert_score(p, score); return score; } unsigned score_argument(expr* arg) { unsigned score = 0; score_argument(arg, score, 20); return score; } void score_argument(expr* arg, unsigned& score, unsigned max_score) { if (score < max_score && is_app(arg)) { app* a = to_app(arg); if (dt.is_constructor(a->get_decl())) { score += 1; for (unsigned i = 0; i < a->get_num_args(); ++i) { score_argument(a->get_arg(i), score, max_score); } } else if (m.is_value(a)) { ++score; } } } void prepare_weight_select() { SASSERT(m_next_update > 0); --m_next_update; if (m_next_update == 0) { if (m_update_frequency >= (1 << 16)) { m_update_frequency = 20; m_weight_multiply = 1.0; } m_update_frequency *= 11; m_update_frequency /= 10; m_next_update = m_update_frequency; m_weight_multiply *= 1.1; } } bool find_score(app* p, double& score) { return m_pred_map.find(p, score); } double adjust_score(double score) { return score/m_weight_multiply; } void insert_score(app* p, double score) { m_pred_map.insert(p, score); m_refs.push_back(p); } void insert_score(func_decl* f, double_vector const& scores) { score_map::obj_map_entry* e = m_score_map.find_core(f); if (e) { double_vector & old_scores = e->get_data().m_value; SASSERT(scores.size() == old_scores.size()); for (unsigned i = 0; i < scores.size(); ++i) { old_scores[i] += scores[i]; } } else { m_score_map.insert(f, scores); } } }; class unifier { ast_manager& m; ::unifier m_unifier; substitution m_S1; var_subst m_S2; expr_ref_vector m_rename; expr_ref_vector m_sub1; expr_ref_vector m_sub2; public: unifier(ast_manager& m): m(m), m_unifier(m), m_S1(m), m_S2(m, false), m_rename(m), m_sub1(m), m_sub2(m) {} bool operator()(ref& tgt, unsigned idx, ref& src, bool compute_subst, ref& result) { return unify(*tgt, idx, *src, compute_subst, result); } expr_ref_vector get_rule_subst(bool is_tgt) { if (is_tgt) { return m_sub1; } else { return m_sub2; } } bool unify(clause const& tgt, unsigned idx, clause const& src, bool compute_subst, ref& result) { qe_lite qe(m); reset(); SASSERT(tgt.get_predicate(idx)->get_decl() == src.get_decl()); unsigned var_cnt = std::max(tgt.get_num_vars(), src.get_num_vars()); m_S1.reserve(2, var_cnt); if (!m_unifier(tgt.get_predicate(idx), src.get_head(), m_S1)) { return false; } app_ref_vector predicates(m); expr_ref tmp(m), tmp2(m), constraint(m); app_ref head(m); result = alloc(clause, m); unsigned delta[2] = { 0, var_cnt }; m_S1.apply(2, delta, expr_offset(tgt.get_head(), 0), tmp); head = to_app(tmp); for (unsigned i = 0; i < tgt.get_num_predicates(); ++i) { if (i != idx) { m_S1.apply(2, delta, expr_offset(tgt.get_predicate(i), 0), tmp); predicates.push_back(to_app(tmp)); } else { for (unsigned j = 0; j < src.get_num_predicates(); ++j) { m_S1.apply(2, delta, expr_offset(src.get_predicate(j), 1), tmp); predicates.push_back(to_app(tmp)); } } } m_S1.apply(2, delta, expr_offset(tgt.get_constraint(), 0), tmp); m_S1.apply(2, delta, expr_offset(src.get_constraint(), 1), tmp2); constraint = m.mk_and(tmp, tmp2); // perform trival quantifier-elimination: uint_set index_set; expr_free_vars fv; fv(head); for (unsigned i = 0; i < predicates.size(); ++i) { fv.accumulate(predicates[i].get()); } for (unsigned i = 0; i < fv.size(); ++i) { if (fv[i]) { index_set.insert(i); } } qe(index_set, false, constraint); if (m.is_false(constraint)) { return false; } // initialize rule. result->init(head, predicates, constraint); ptr_vector vars; result->get_free_vars(vars); bool change = false; var_ref w(m); for (unsigned i = 0, j = 0; i < vars.size(); ++i) { if (vars[i]) { w = m.mk_var(j, vars[i]); m_rename.push_back(w); ++j; } else { change = true; m_rename.push_back(0); } } if (change) { m_S2(result->get_constraint(), m_rename.size(), m_rename.c_ptr(), constraint); for (unsigned i = 0; i < result->get_num_predicates(); ++i) { m_S2(result->get_predicate(i), m_rename.size(), m_rename.c_ptr(), tmp); predicates[i] = to_app(tmp); } m_S2(result->get_head(), m_rename.size(), m_rename.c_ptr(), tmp); head = to_app(tmp); result->init(head, predicates, constraint); } if (compute_subst) { extract_subst(delta, tgt, 0); extract_subst(delta, src, 1); } // init result using head, predicates, constraint return true; } private: void reset() { m_S1.reset(); m_S2.reset(); m_rename.reset(); m_sub1.reset(); m_sub2.reset(); } void extract_subst(unsigned const* delta, clause const& g, unsigned offset) { ptr_vector vars; var_ref v(m); expr_ref tmp(m); g.get_free_vars(vars); for (unsigned i = 0; i < vars.size(); ++i) { if (vars[i]) { v = m.mk_var(i, vars[i]); m_S1.apply(2, delta, expr_offset(v, offset), tmp); m_S2(tmp, m_rename.size(), m_rename.c_ptr(), tmp); insert_subst(offset, tmp); } else { insert_subst(offset, m.mk_true()); } } } void insert_subst(unsigned offset, expr* e) { if (offset == 0) { m_sub1.push_back(e); } else { m_sub2.push_back(e); } } }; class extract_delta { ast_manager& m; unifier m_unifier; public: extract_delta(ast_manager& m): m(m), m_unifier(m) {} // // Given a clause // P(s) :- P(t), Phi(x). // Compute the clauses: // acc: P(s) :- Delta(z,t), P(z), Phi(x). // delta1: Delta(z,z). // delta2: Delta(z,s) :- Delta(z,t), Phi(x). // void mk_delta_clauses(clause const& g, ref& acc, ref& delta1, ref& delta2) { SASSERT(g.get_num_predicates() > 0); app* p = g.get_head(); app* q = g.get_predicate(0); SASSERT(p->get_decl() == q->get_decl()); expr_ref_vector zs = mk_fresh_vars(g); expr_ref_vector zszs(m); func_decl_ref delta(m); sort_ref_vector dom(m); for (unsigned j = 0; j < 1; ++j) { for (unsigned i = 0; i < zs.size(); ++i) { dom.push_back(m.get_sort(zs[i].get())); zszs.push_back(zs[i].get()); } } app_ref_vector preds(m); delta = m.mk_fresh_func_decl("Delta", dom.size(), dom.c_ptr(), m.mk_bool_sort()); acc = alloc(clause, m); delta1 = alloc(clause, m); delta2 = alloc(clause, m); delta1->init(m.mk_app(delta, zszs.size(), zszs.c_ptr()), preds, m.mk_true()); for (unsigned i = 0; i < zs.size(); ++i) { zszs[i+zs.size()] = p->get_arg(i); } app_ref head(m), pred(m); head = m.mk_app(delta, zszs.size(), zszs.c_ptr()); for (unsigned i = 0; i < zs.size(); ++i) { zszs[i+zs.size()] = q->get_arg(i); } pred = m.mk_app(delta, zszs.size(), zszs.c_ptr()); preds.push_back(pred); for (unsigned i = 1; i < g.get_num_predicates(); ++i) { preds.push_back(g.get_predicate(i)); } delta2->init(head, preds, g.get_constraint()); preds.push_back(m.mk_app(q->get_decl(), zs.size(), zs.c_ptr())); acc->init(p, preds, g.get_constraint()); IF_VERBOSE(1, delta1->display(verbose_stream() << "delta1:\n"); delta2->display(verbose_stream() << "delta2:\n"); acc->display(verbose_stream() << "acc:\n");); } // // Given a sequence of clauses and inference rules // compute a super-predicate and auxiliary clauses. // // P1(x) :- P2(y), R(z) // P2(y) :- P3(z), T(u) // P3(z) :- P1(x), U(v) // => // P1(x) :- P1(x), R(z), T(u), U(v) // ref resolve_rules(unsigned num_clauses, clause*const* clauses, unsigned const* positions) { ref result = clauses[0]; ref tmp; unsigned offset = 0; for (unsigned i = 0; i + 1 < num_clauses; ++i) { clause const& cl = *clauses[i+1]; offset += positions[i]; VERIFY (m_unifier.unify(*result, offset, cl, false, tmp)); result = tmp; } return result; } private: expr_ref_vector mk_fresh_vars(clause const& g) { expr_ref_vector result(m); app* p = g.get_head(); unsigned num_vars = g.get_num_vars(); for (unsigned i = 0; i < p->get_num_args(); ++i) { result.push_back(m.mk_var(num_vars+i, m.get_sort(p->get_arg(i)))); } return result; } }; enum instruction { SELECT_RULE, SELECT_PREDICATE, BACKTRACK, SATISFIABLE, UNSATISFIABLE, CANCEL }; std::ostream& operator<<(std::ostream& out, instruction i) { switch(i) { case SELECT_RULE: return out << "select-rule"; case SELECT_PREDICATE: return out << "select-predicate"; case BACKTRACK: return out << "backtrack"; case SATISFIABLE: return out << "sat"; case UNSATISFIABLE: return out << "unsat"; case CANCEL: return out << "cancel"; } return out << "unmatched instruction"; } }; namespace datalog { class tab::imp { struct stats { stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } unsigned m_num_unfold; unsigned m_num_no_unfold; unsigned m_num_subsumed; }; context& m_ctx; ast_manager& m; rule_manager& rm; tb::index m_index; tb::selection m_selection; smt_params m_fparams; smt::kernel m_solver; mutable tb::unifier m_unifier; tb::rules m_rules; vector > m_clauses; unsigned m_seqno; tb::instruction m_instruction; lbool m_status; volatile bool m_cancel; stats m_stats; uint_set m_displayed_rules; public: imp(context& ctx): m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_index(m), m_selection(ctx), m_solver(m, m_fparams), m_unifier(m), m_rules(), m_seqno(0), m_instruction(tb::SELECT_PREDICATE), m_status(l_undef), m_cancel(false) { // m_fparams.m_relevancy_lvl = 0; m_fparams.m_mbqi = false; m_fparams.m_timeout = 1000; } ~imp() {} lbool query(expr* query) { m_ctx.ensure_opened(); m_index.reset(); m_selection.reset(); m_displayed_rules.reset(); m_rules.init(m_ctx.get_rules()); m_selection.init(m_rules); rule_set query_rules(m_ctx); rule_ref clause(rm); rm.mk_query(query, query_rules); clause = query_rules.last(); ref g = alloc(tb::clause, m); g->init(clause); g->set_head(m.mk_false()); init_clause(g); IF_VERBOSE(1, display_clause(*get_clause(), verbose_stream() << "g" << get_clause()->get_seqno() << " ");); return run(); } void cancel() { m_cancel = true; m_index.cleanup(); m_solver.cancel(); } void cleanup() { m_cancel = false; m_clauses.reset(); m_index.cleanup(); m_solver.reset_cancel(); } void reset_statistics() { m_stats.reset(); } void collect_statistics(statistics& st) const { st.update("tab.num_unfold", m_stats.m_num_unfold); st.update("tab.num_unfold_fail", m_stats.m_num_no_unfold); st.update("tab.num_subsumed", m_stats.m_num_subsumed); } void display_certificate(std::ostream& out) const { expr_ref ans = get_answer(); out << mk_pp(ans, m) << "\n"; } expr_ref get_answer() const { switch(m_status) { case l_undef: UNREACHABLE(); return expr_ref(m.mk_false(), m); case l_true: { proof_ref pr = get_proof(); return expr_ref(pr.get(), m); } case l_false: // NOT_IMPLEMENTED_YET(); return expr_ref(m.mk_true(), m); } UNREACHABLE(); return expr_ref(m.mk_true(), m); } private: void select_predicate() { tb::clause & g = *get_clause(); unsigned num_predicates = g.get_num_predicates(); if (num_predicates == 0) { m_instruction = tb::UNSATISFIABLE; IF_VERBOSE(2, g.display(verbose_stream()); ); } else { m_instruction = tb::SELECT_RULE; unsigned pi = m_selection.select(g); g.set_predicate_index(pi); IF_VERBOSE(2, verbose_stream() << mk_pp(g.get_predicate(pi), m) << "\n";); } } void apply_rule(ref& r) { ref clause = get_clause(); ref next_clause; if (m_unifier(clause, clause->get_predicate_index(), r, false, next_clause) && !query_is_tautology(*next_clause)) { init_clause(next_clause); unsigned subsumer = 0; IF_VERBOSE(1, display_rule(*clause, verbose_stream()); display_premise(*clause, verbose_stream() << "g" << next_clause->get_seqno() << " "); display_clause(*next_clause, verbose_stream()); ); if (m_index.is_subsumed(next_clause, subsumer)) { IF_VERBOSE(1, verbose_stream() << "subsumed by g" << subsumer << "\n";); m_stats.m_num_subsumed++; m_clauses.pop_back(); m_instruction = tb::SELECT_RULE; } else { m_stats.m_num_unfold++; next_clause->set_parent(clause); m_index.insert(next_clause); m_instruction = tb::SELECT_PREDICATE; } } else { m_stats.m_num_no_unfold++; m_instruction = tb::SELECT_RULE; } } void select_rule() { tb::clause& g = *get_clause(); g.inc_next_rule(); unsigned pi = g.get_predicate_index(); func_decl* p = g.get_predicate(pi)->get_decl(); unsigned num_rules = m_rules.get_num_rules(p); unsigned index = g.get_next_rule(); if (num_rules <= index) { m_instruction = tb::BACKTRACK; } else { ref rl = m_rules.get_rule(p, index); apply_rule(rl); } } void backtrack() { SASSERT(!m_clauses.empty()); m_clauses.pop_back(); if (m_clauses.empty()) { m_instruction = tb::SATISFIABLE; } else { m_instruction = tb::SELECT_RULE; } } lbool run() { m_instruction = tb::SELECT_PREDICATE; m_status = l_undef; while (true) { IF_VERBOSE(2, verbose_stream() << m_instruction << "\n";); if (m_cancel) { cleanup(); return l_undef; } switch(m_instruction) { case tb::SELECT_PREDICATE: select_predicate(); break; case tb::SELECT_RULE: select_rule(); break; case tb::BACKTRACK: backtrack(); break; case tb::SATISFIABLE: m_status = l_false; return l_false; case tb::UNSATISFIABLE: m_status = l_true; IF_VERBOSE(1, display_certificate(verbose_stream());); return l_true; case tb::CANCEL: cleanup(); m_status = l_undef; return l_undef; } } } bool query_is_tautology(tb::clause const& g) { expr_ref fml = g.to_formula(); fml = m.mk_not(fml); m_solver.push(); m_solver.assert_expr(fml); lbool is_sat = m_solver.check(); m_solver.pop(1); TRACE("dl", tout << is_sat << ":\n" << mk_pp(fml, m) << "\n";); return l_false == is_sat; } void init_clause(ref& clause) { clause->set_index(m_clauses.size()); clause->set_seqno(m_seqno++); m_clauses.push_back(clause); } ref get_clause() const { return m_clauses.back(); } void display_rule(tb::clause const& p, std::ostream& out) { func_decl* f = p.get_predicate(p.get_predicate_index())->get_decl(); ref rl = m_rules.get_rule(f, p.get_next_rule()); unsigned idx = rl->get_index(); if (!m_displayed_rules.contains(idx)) { m_displayed_rules.insert(idx); rl->display(out << "r" << p.get_next_rule() << ": "); } } void display_premise(tb::clause& p, std::ostream& out) { func_decl* f = p.get_predicate(p.get_predicate_index())->get_decl(); out << "{g" << p.get_seqno() << " " << f->get_name() << " pos: " << p.get_predicate_index() << " rule: " << p.get_next_rule() << "}\n"; } void display_clause(tb::clause& g, std::ostream& out) { g.display(out); } proof_ref get_proof() const { scoped_proof sp(m); proof_ref pr(m); proof_ref_vector prs(m); ref clause = get_clause(); ref replayed_clause; replace_proof_converter pc(m); // clause is a empty clause. // Pretend it is asserted. // It gets replaced by premises. SASSERT(clause->get_num_predicates() == 0); expr_ref root = clause->to_formula(); vector substs; while (0 != clause->get_index()) { SASSERT(clause->get_parent_index() < clause->get_index()); unsigned p_index = clause->get_parent_index(); unsigned p_rule = clause->get_parent_rule(); ref parent = m_clauses[p_index]; unsigned pi = parent->get_predicate_index(); func_decl* pred = parent->get_predicate(pi)->get_decl(); ref rl = m_rules.get_rule(pred, p_rule); VERIFY(m_unifier(parent, parent->get_predicate_index(), rl, true, replayed_clause)); expr_ref_vector s1(m_unifier.get_rule_subst(true)); expr_ref_vector s2(m_unifier.get_rule_subst(false)); resolve_rule(pc, *parent, *rl, s1, s2, *clause); clause = parent; substs.push_back(s1); } IF_VERBOSE(1, display_body_insts(substs, *clause, verbose_stream());); pc.invert(); prs.push_back(m.mk_asserted(root)); pc(m, 1, prs.c_ptr(), pr); return pr; } void display_body_insts(vector const& substs, tb::clause const& clause, std::ostream& out) const { expr_ref_vector subst(m); for (unsigned i = substs.size(); i > 0; ) { --i; apply_subst(subst, substs[i]); } expr_ref body = clause.get_body(); var_subst vs(m, false); vs(body, subst.size(), subst.c_ptr(), body); out << mk_pp(body, m) << "\n"; } void resolve_rule(replace_proof_converter& pc, tb::clause const& r1, tb::clause const& r2, expr_ref_vector const& s1, expr_ref_vector const& s2, tb::clause const& res) const { unsigned idx = r1.get_predicate_index(); expr_ref fml = res.to_formula(); vector substs; svector > positions; substs.push_back(s1); substs.push_back(s2); scoped_proof _sc(m); proof_ref pr(m); proof_ref_vector premises(m); premises.push_back(m.mk_asserted(r1.to_formula())); premises.push_back(m.mk_asserted(r2.to_formula())); positions.push_back(std::make_pair(idx+1, 0)); pr = m.mk_hyper_resolve(2, premises.c_ptr(), fml, positions, substs); pc.insert(pr); } }; tab::tab(context& ctx): datalog::engine_base(ctx.get_manager(),"tabulation"), m_imp(alloc(imp, ctx)) { } tab::~tab() { dealloc(m_imp); } lbool tab::query(expr* query) { return m_imp->query(query); } void tab::cancel() { m_imp->cancel(); } void tab::cleanup() { m_imp->cleanup(); } void tab::reset_statistics() { m_imp->reset_statistics(); } void tab::collect_statistics(statistics& st) const { m_imp->collect_statistics(st); } void tab::display_certificate(std::ostream& out) const { m_imp->display_certificate(out); } expr_ref tab::get_answer() { return m_imp->get_answer(); } }; z3-4.4.0/src/muz/tab/tab_context.h0000644000175000017500000000154312540347414016503 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: tab_context.h Abstract: Tabulation/subsumption/cyclic proof context. Author: Nikolaj Bjorner (nbjorner) 2013-01-15 Revision History: --*/ #ifndef _TAB_CONTEXT_H_ #define _TAB_CONTEXT_H_ #include "ast.h" #include "lbool.h" #include "statistics.h" #include "dl_engine_base.h" namespace datalog { class context; class tab : public engine_base { class imp; imp* m_imp; public: tab(context& ctx); ~tab(); virtual lbool query(expr* query); virtual void cancel(); virtual void cleanup(); virtual void reset_statistics(); virtual void collect_statistics(statistics& st) const; virtual void display_certificate(std::ostream& out) const; virtual expr_ref get_answer(); }; }; #endif z3-4.4.0/src/muz/base/0000755000175000017500000000000012540347414014161 5ustar michaelmichaelz3-4.4.0/src/muz/base/dl_costs.h0000644000175000017500000000525012540347414016146 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_costs.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-20. Revision History: --*/ #ifndef _DL_COSTS_H_ #define _DL_COSTS_H_ #include #include "ast.h" class stopwatch; namespace datalog { class context; class rule; class cost_recorder; struct costs { typedef unsigned time_type; time_type milliseconds; unsigned instructions; costs(); bool empty() const; void reset(); costs operator-(const costs & o) const; void operator+=(const costs & o); bool passes_thresholds(context & ctx) const; void output(std::ostream & out) const; }; class accounted_object { friend class cost_recorder; context * m_context; rule * m_parent_object; costs m_current_cost; costs m_processed_cost; bool m_being_recorded; protected: accounted_object() : m_context(0), m_parent_object(0), m_being_recorded(false) {} ~accounted_object(); public: void set_accounting_parent_object(context & ctx, rule * parent); rule * get_parent_object() const { return m_parent_object; } costs & get_current_costs() { return m_current_cost; } const costs & get_current_costs() const { return m_current_cost; } const costs & get_processed_costs() const { return m_processed_cost; } void get_total_cost(costs & result) const; bool being_recorded() const { return m_being_recorded; } void process_costs(); bool passes_output_thresholds(context & ctx) const; void output_profile(std::ostream & out) const; private: //private and undefined copy constructor and operator= to avoid the default ones accounted_object(const accounted_object &); accounted_object& operator=(const accounted_object &); }; class cost_recorder { accounted_object * m_obj; //it's a pointer to avoid everything depending on the stopwatch.h //(and transitively then on windows.h) header file stopwatch * m_stopwatch; bool m_running; uint64 m_last_time; public: cost_recorder(); ~cost_recorder(); /** \brief Start recording costs for the next object. If the recording of the previous object did not finish, it will be finished here. Also, it will be done more efficiently than if the \c finish() function was called before separately. */ void start(accounted_object *); void finish() { start(0); } }; }; #endif /* _DL_COSTS_H_ */ z3-4.4.0/src/muz/base/dl_rule.cpp0000644000175000017500000010252312540347414016316 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_rule.cpp Abstract: Author: Krystof Hoder (t-khoder) 2011-10-19. Revision History: Nikolaj Bjorner (nbjorner) 2012-10-31. Check for enabledness of fix_unbound_vars inside call. This function gets called from many rule tansformers. --*/ #include #include #include"ast_pp.h" #include"dl_context.h" #include"map.h" #include"recurse_expr_def.h" #include"dl_rule.h" #include"qe.h" #include"for_each_expr.h" #include"used_vars.h" #include"var_subst.h" #include"rewriter_def.h" #include"th_rewriter.h" #include"ast_smt2_pp.h" #include"used_symbols.h" #include"quant_hoist.h" #include"expr_replacer.h" #include"bool_rewriter.h" #include"expr_safe_replace.h" #include"filter_model_converter.h" #include"scoped_proof.h" #include"datatype_decl_plugin.h" namespace datalog { rule_manager::rule_manager(context& ctx) : m(ctx.get_manager()), m_ctx(ctx), m_body(m), m_head(m), m_args(m), m_hnf(m), m_qe(m), m_cfg(m), m_rwr(m, false, m_cfg), m_ufproc(m) {} void rule_manager::inc_ref(rule * r) { if (r) { SASSERT(r->m_ref_cnt != UINT_MAX); r->m_ref_cnt++; } } void rule_manager::dec_ref(rule * r) { if (r) { SASSERT(r->m_ref_cnt > 0); r->m_ref_cnt--; if (r->m_ref_cnt == 0) { r->deallocate(m); } } } rule_manager::remove_label_cfg::~remove_label_cfg() {} br_status rule_manager::remove_label_cfg::reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (is_decl_of(f, m_label_fid, OP_LABEL)) { SASSERT(num == 1); result = args[0]; return BR_DONE; } return BR_FAILED; } void rule_manager::remove_labels(expr_ref& fml, proof_ref& pr) { expr_ref tmp(m); m_rwr(fml, tmp); if (pr && fml != tmp) { pr = m.mk_modus_ponens(pr, m.mk_rewrite(fml, tmp)); } fml = tmp; } var_idx_set& rule_manager::collect_vars(expr* e) { return collect_vars(e, 0); } var_idx_set& rule_manager::collect_vars(expr* e1, expr* e2) { reset_collect_vars(); if (e1) accumulate_vars(e1); if (e2) accumulate_vars(e2); return finalize_collect_vars(); } void rule_manager::reset_collect_vars() { m_var_idx.reset(); m_free_vars.reset(); } var_idx_set& rule_manager::finalize_collect_vars() { unsigned sz = m_free_vars.size(); for (unsigned i = 0; i < sz; ++i) { if (m_free_vars[i]) m_var_idx.insert(i); } return m_var_idx; } var_idx_set& rule_manager::collect_tail_vars(rule * r) { reset_collect_vars(); unsigned n = r->get_tail_size(); for (unsigned i=0;iget_tail(i)); } return finalize_collect_vars(); } var_idx_set& rule_manager::collect_rule_vars_ex(rule * r, app* t) { reset_collect_vars(); unsigned n = r->get_tail_size(); accumulate_vars(r->get_head()); for (unsigned i=0;iget_tail(i) != t) { accumulate_vars(r->get_tail(i)); } } return finalize_collect_vars(); } var_idx_set& rule_manager::collect_rule_vars(rule * r) { reset_collect_vars(); unsigned n = r->get_tail_size(); accumulate_vars(r->get_head()); for (unsigned i=0;iget_tail(i)); } return finalize_collect_vars(); } void rule_manager::accumulate_vars(expr* e) { m_free_vars.accumulate(e); } void rule_manager::mk_rule(expr* fml, proof* p, rule_set& rules, symbol const& name) { scoped_proof_mode _sc(m, m_ctx.generate_proof_trace()?PGM_FINE:PGM_DISABLED); proof_ref pr(p, m); expr_ref fml1(m); bind_variables(fml, true, fml1); if (fml1 != fml && pr) { pr = m.mk_asserted(fml1); } remove_labels(fml1, pr); mk_rule_core(fml1, pr, rules, name); } void rule_manager::mk_negations(app_ref_vector& body, svector& is_negated) { for (unsigned i = 0; i < body.size(); ++i) { expr* e = body[i].get(), *e1; if (m.is_not(e, e1) && m_ctx.is_predicate(e1)) { check_app(e1); body[i] = to_app(e1); is_negated.push_back(true); } else { is_negated.push_back(false); } } } void rule_manager::mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name) { expr_ref_vector fmls(m); proof_ref_vector prs(m); m_hnf.reset(); m_hnf.set_name(name); m_hnf(fml, p, fmls, prs); for (unsigned i = 0; i < m_hnf.get_fresh_predicates().size(); ++i) { m_ctx.register_predicate(m_hnf.get_fresh_predicates()[i], false); } for (unsigned i = 0; i < fmls.size(); ++i) { mk_horn_rule(fmls[i].get(), prs[i].get(), rules, name); } } void rule_manager::mk_horn_rule(expr* fml, proof* p, rule_set& rules, symbol const& name) { m_body.reset(); m_neg.reset(); unsigned index = extract_horn(fml, m_body, m_head); hoist_compound_predicates(index, m_head, m_body); TRACE("dl_rule", tout << mk_pp(m_head, m) << " :- "; for (unsigned i = 0; i < m_body.size(); ++i) { tout << mk_pp(m_body[i].get(), m) << " "; } tout << "\n";); mk_negations(m_body, m_neg); check_valid_rule(m_head, m_body.size(), m_body.c_ptr()); rule_ref r(*this); r = mk(m_head.get(), m_body.size(), m_body.c_ptr(), m_neg.c_ptr(), name); expr_ref fml1(m); if (p) { to_formula(*r, fml1); if (fml1 == fml) { // no-op. } else if (is_quantifier(fml1)) { p = m.mk_modus_ponens(p, m.mk_symmetry(m.mk_der(to_quantifier(fml1), fml))); } else { p = m.mk_modus_ponens(p, m.mk_rewrite(fml, fml1)); } } if (m_ctx.fix_unbound_vars()) { fix_unbound_vars(r, true); } if (p) { expr_ref fml2(m); to_formula(*r, fml2); if (fml1 != fml2) { p = m.mk_modus_ponens(p, m.mk_rewrite(fml1, fml2)); } r->set_proof(m, p); } rules.add_rule(r); } unsigned rule_manager::extract_horn(expr* fml, app_ref_vector& body, app_ref& head) { expr* e1, *e2; if (::is_forall(fml)) { fml = to_quantifier(fml)->get_expr(); } unsigned index = m_counter.get_next_var(fml); if (m.is_implies(fml, e1, e2)) { m_args.reset(); head = ensure_app(e2); qe::flatten_and(e1, m_args); for (unsigned i = 0; i < m_args.size(); ++i) { body.push_back(ensure_app(m_args[i].get())); } } else { head = ensure_app(fml); } return index; } void rule_manager::hoist_compound_predicates(unsigned index, app_ref& head, app_ref_vector& body) { unsigned sz = body.size(); hoist_compound(index, head, body); for (unsigned i = 0; i < sz; ++i) { app_ref b(body[i].get(), m); hoist_compound(index, b, body); body[i] = b; } } func_decl* rule_manager::mk_query(expr* query, rule_set& rules) { ptr_vector vars; svector names; app_ref_vector body(m); expr_ref q(m); // Add implicit variables. // Remove existential prefix. bind_variables(query, false, q); quantifier_hoister qh(m); qh.pull_quantifier(false, q, 0, &names); // retrieve free variables. m_free_vars(q); vars.append(m_free_vars.size(), m_free_vars.c_ptr()); if (vars.contains(static_cast(0))) { var_subst sub(m, false); expr_ref_vector args(m); // [s0, 0, s2, ..] // [0 -> 0, 1 -> x, 2 -> 1, ..] for (unsigned i = 0, j = 0; i < vars.size(); ++i) { if (vars[i]) { args.push_back(m.mk_var(j, vars[i])); ++j; } else { args.push_back(m.mk_var(0, m.mk_bool_sort())); } } sub(q, args.size(), args.c_ptr(), q); vars.reset(); m_free_vars(q); vars.append(m_free_vars.size(), m_free_vars.c_ptr()); } SASSERT(!vars.contains(static_cast(0)) && "Unused variables have been eliminated"); // flatten body and extract query predicate. if (!is_app(q)) { throw default_exception("Query body is not well-formed"); } body.push_back(to_app(q)); flatten_body(body); func_decl* body_pred = 0; for (unsigned i = 0; i < body.size(); i++) { if (is_uninterp(body[i].get())) { body_pred = body[i]->get_decl(); break; } } // we want outermost declared variable first to // follow order of quantified variables so we reverse vars. while (vars.size() > names.size()) { names.push_back(symbol(names.size())); } vars.reverse(); names.reverse(); func_decl* qpred = m_ctx.mk_fresh_head_predicate(symbol("query"), symbol(), vars.size(), vars.c_ptr(), body_pred); m_ctx.register_predicate(qpred, false); rules.set_output_predicate(qpred); if (m_ctx.get_model_converter()) { filter_model_converter* mc = alloc(filter_model_converter, m); mc->insert(qpred); m_ctx.add_model_converter(mc); } expr_ref_vector qhead_args(m); for (unsigned i = 0; i < vars.size(); i++) { qhead_args.push_back(m.mk_var(vars.size()-i-1, vars[i])); } app_ref qhead(m.mk_app(qpred, qhead_args.c_ptr()), m); app_ref impl(m.mk_implies(q, qhead), m); expr_ref rule_expr(impl.get(), m); if (!vars.empty()) { rule_expr = m.mk_forall(vars.size(), vars.c_ptr(), names.c_ptr(), impl); } scoped_proof_mode _sc(m, m_ctx.generate_proof_trace()?PGM_FINE:PGM_DISABLED); proof_ref pr(m); if (m_ctx.generate_proof_trace()) { pr = m.mk_asserted(rule_expr); } mk_rule(rule_expr, pr, rules); return qpred; } void rule_manager::bind_variables(expr* fml, bool is_forall, expr_ref& result) { result = m_ctx.bind_vars(fml, is_forall); } void rule_manager::flatten_body(app_ref_vector& body) { expr_ref_vector r(m); for (unsigned i = 0; i < body.size(); ++i) { r.push_back(body[i].get()); } qe::flatten_and(r); body.reset(); for (unsigned i = 0; i < r.size(); ++i) { body.push_back(ensure_app(r[i].get())); } } void rule_manager::hoist_compound(unsigned& num_bound, app_ref& fml, app_ref_vector& body) { expr_ref e(m); expr* not_fml; if (m.is_not(fml, not_fml)) { fml = ensure_app(not_fml); hoist_compound(num_bound, fml, body); fml = m.mk_not(fml); return; } if (!m_ctx.is_predicate(fml)) { return; } m_args.reset(); for (unsigned i = 0; i < fml->get_num_args(); ++i) { e = fml->get_arg(i); if (!is_app(e)) { m_args.push_back(e); continue; } app* b = to_app(e); if (m.is_value(b)) { m_args.push_back(e); } else { var* v = m.mk_var(num_bound++, m.get_sort(b)); m_args.push_back(v); body.push_back(m.mk_eq(v, b)); } } fml = m.mk_app(fml->get_decl(), m_args.size(), m_args.c_ptr()); TRACE("dl_rule", tout << mk_pp(fml.get(), m) << "\n";); } class contains_predicate_proc { context const& ctx; public: struct found {}; contains_predicate_proc(context const& ctx): ctx(ctx) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app* n) { if (ctx.is_predicate(n)) throw found(); } }; bool rule_manager::contains_predicate(expr* fml) const { contains_predicate_proc proc(m_ctx); try { quick_for_each_expr(proc, fml); } catch (contains_predicate_proc::found) { return true; } return false; } bool rule_manager::is_forall(ast_manager& m, expr* e, quantifier*& q) { expr* e1, *e2; if (m.is_iff(e, e1, e2)) { if (m.is_true(e2)) { e = e1; } else if (m.is_true(e1)) { e = e2; } } if (is_quantifier(e)) { q = to_quantifier(e); return q->is_forall(); } return false; } app_ref rule_manager::ensure_app(expr* e) { SASSERT(m.is_bool(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 rule_manager::check_app(expr* e) { if (!is_app(e)) { std::ostringstream out; out << "expected application, got " << mk_pp(e, m); throw default_exception(out.str()); } } rule * rule_manager::mk(app * head, unsigned n, app * const * tail, bool const * is_negated, symbol const& name, bool normalize) { DEBUG_CODE(check_valid_rule(head, n, tail);); unsigned sz = rule::get_obj_size(n); void * mem = m.get_allocator().allocate(sz); rule * r = new (mem) rule(); r->m_head = head; r->m_name = name; r->m_tail_size = n; r->m_proof = 0; m.inc_ref(r->m_head); app * * uninterp_tail = r->m_tail; //grows upwards app * * interp_tail = r->m_tail+n; //grows downwards bool has_neg = false; for (unsigned i = 0; i < n; i++) { bool is_neg = (is_negated != 0 && is_negated[i]); app * curr = tail[i]; if (is_neg && !m_ctx.is_predicate(curr)) { curr = m.mk_not(curr); is_neg = false; } if (is_neg) { has_neg = true; } app * tail_entry = TAG(app *, curr, is_neg); if (m_ctx.is_predicate(curr)) { *uninterp_tail=tail_entry; uninterp_tail++; } else { interp_tail--; *interp_tail=tail_entry; } m.inc_ref(curr); } SASSERT(uninterp_tail==interp_tail); r->m_uninterp_cnt = static_cast(uninterp_tail - r->m_tail); if (has_neg) { //put negative predicates between positive and interpreted app * * it = r->m_tail; app * * end = r->m_tail + r->m_uninterp_cnt; while(it!=end) { bool is_neg = GET_TAG(*it)!=0; if (is_neg) { --end; std::swap(*it, *end); } else { ++it; } } r->m_positive_cnt = static_cast(it - r->m_tail); SASSERT(r->m_positive_cnt < r->m_uninterp_cnt); } else { r->m_positive_cnt = r->m_uninterp_cnt; } if (normalize) { r->norm_vars(*this); } return r; } rule * rule_manager::mk(rule const * source, symbol const& name) { return mk(source, source->get_head(), name); } rule * rule_manager::mk(rule const * source, app * new_head, symbol const& name) { unsigned n = source->get_tail_size(); unsigned sz = rule::get_obj_size(n); void * mem = m.get_allocator().allocate(sz); rule * r = new (mem) rule(); r->m_head = new_head; r->m_name = name; r->m_tail_size = n; r->m_positive_cnt = source->m_positive_cnt; r->m_uninterp_cnt = source->m_uninterp_cnt; r->m_proof = 0; m.inc_ref(r->m_head); for (unsigned i = 0; i < n; i++) { r->m_tail[i] = source->m_tail[i]; m.inc_ref(r->get_tail(i)); } return r; } void rule_manager::to_formula(rule const& r, expr_ref& fml) { ast_manager & m = fml.get_manager(); expr_ref_vector body(m); for (unsigned i = 0; i < r.get_tail_size(); i++) { body.push_back(r.get_tail(i)); if (r.is_neg_tail(i)) { body[body.size()-1] = m.mk_not(body.back()); } } fml = r.get_head(); switch (body.size()) { case 0: break; case 1: fml = m.mk_implies(body[0].get(), fml); break; default: fml = m.mk_implies(m.mk_and(body.size(), body.c_ptr()), fml); break; } m_free_vars(fml); if (m_free_vars.empty()) { return; } svector names; used_symbols<> us; m_free_vars.set_default_sort(m.mk_bool_sort()); us(fml); m_free_vars.reverse(); for (unsigned j = 0, i = 0; i < m_free_vars.size(); ++j) { for (char c = 'A'; i < m_free_vars.size() && c <= 'Z'; ++c) { func_decl_ref f(m); std::stringstream _name; _name << c; if (j > 0) _name << j; symbol name(_name.str().c_str()); if (!us.contains(name)) { names.push_back(name); ++i; } } } fml = m.mk_forall(m_free_vars.size(), m_free_vars.c_ptr(), names.c_ptr(), fml); } std::ostream& rule_manager::display_smt2(rule const& r, std::ostream & out) { expr_ref fml(m); to_formula(r, fml); return out << mk_ismt2_pp(fml, m); } void rule_manager::reduce_unbound_vars(rule_ref& r) { unsigned ut_len = r->get_uninterpreted_tail_size(); unsigned t_len = r->get_tail_size(); expr_ref_vector conjs(m); if (ut_len == t_len) { return; } reset_collect_vars(); accumulate_vars(r->get_head()); for (unsigned i = 0; i < ut_len; ++i) { accumulate_vars(r->get_tail(i)); } var_idx_set& index_set = finalize_collect_vars(); for (unsigned i = ut_len; i < t_len; ++i) { conjs.push_back(r->get_tail(i)); } m_qe(index_set, false, conjs); bool change = conjs.size() != t_len - ut_len; for (unsigned i = 0; !change && i < conjs.size(); ++i) { change = r->get_tail(ut_len+i) != conjs[i].get(); } if (change) { app_ref_vector tail(m); svector tail_neg; for (unsigned i = 0; i < ut_len; ++i) { tail.push_back(r->get_tail(i)); tail_neg.push_back(r->is_neg_tail(i)); } for (unsigned i = 0; i < conjs.size(); ++i) { tail.push_back(ensure_app(conjs[i].get())); } tail_neg.resize(tail.size(), false); r = mk(r->get_head(), tail.size(), tail.c_ptr(), tail_neg.c_ptr()); TRACE("dl", r->display(m_ctx, tout << "reduced rule\n");); } } void rule_manager::fix_unbound_vars(rule_ref& r, bool try_quantifier_elimination) { reduce_unbound_vars(r); if (!m_ctx.fix_unbound_vars()) { return; } unsigned ut_len = r->get_uninterpreted_tail_size(); unsigned t_len = r->get_tail_size(); if (ut_len == t_len) { // no interpreted tail to fix return; } var_counter vctr; app_ref_vector tail(m); svector tail_neg; app_ref head(r->get_head(), m); vctr.count_vars(head); for (unsigned i = 0; i < ut_len; i++) { app * t = r->get_tail(i); vctr.count_vars(t); tail.push_back(t); tail_neg.push_back(r->is_neg_tail(i)); } var_idx_set unbound_vars; expr_ref_vector tails_with_unbound(m); for (unsigned i = ut_len; i < t_len; i++) { app * t = r->get_tail(i); m_free_vars(t); bool has_unbound = false; unsigned iv_size = m_free_vars.size(); for (unsigned i=0; i qsorts; qsorts.resize(q_var_cnt); unsigned q_idx = 0; for (unsigned v = 0; v < m_free_vars.size(); ++v) { sort * v_sort = m_free_vars[v]; if (!v_sort) { //this variable index is not used continue; } unsigned new_idx; if (unbound_vars.contains(v)) { new_idx = q_idx++; qsorts.push_back(v_sort); } else { new_idx = v + q_var_cnt; } subst.push_back(m.mk_var(new_idx, v_sort)); } SASSERT(q_idx == q_var_cnt); svector qnames; for (unsigned i = 0; i < q_var_cnt; i++) { qnames.push_back(symbol(i)); } //quantifiers take this reversed qsorts.reverse(); qnames.reverse(); expr_ref unbound_tail_pre_quant(m), fixed_tail(m), quant_tail(m); var_subst vs(m, false); vs(unbound_tail, subst.size(), subst.c_ptr(), unbound_tail_pre_quant); quant_tail = m.mk_exists(q_var_cnt, qsorts.c_ptr(), qnames.c_ptr(), unbound_tail_pre_quant); if (try_quantifier_elimination) { TRACE("dl_rule_unbound_fix_pre_qe", tout<<"rule: "; r->display(m_ctx, tout); tout<<"tail with unbound vars: "<display(m_ctx, tout); tout<<"tail with unbound vars: "<set_accounting_parent_object(m_ctx, old_r); } void rule_manager::mk_rule_rewrite_proof(rule& old_rule, rule& new_rule) { if (&old_rule != &new_rule && !new_rule.get_proof() && old_rule.get_proof()) { expr_ref fml(m); to_formula(new_rule, fml); scoped_proof _sc(m); proof* p = m.mk_rewrite(m.get_fact(old_rule.get_proof()), fml); new_rule.set_proof(m, m.mk_modus_ponens(old_rule.get_proof(), p)); } } void rule_manager::mk_rule_asserted_proof(rule& r) { if (m_ctx.generate_proof_trace()) { scoped_proof _scp(m); expr_ref fml(m); to_formula(r, fml); r.set_proof(m, m.mk_asserted(fml)); } } void rule_manager::substitute(rule_ref& r, unsigned sz, expr*const* es) { expr_ref tmp(m); app_ref new_head(m); app_ref_vector new_tail(m); svector tail_neg; var_subst vs(m, false); vs(r->get_head(), sz, es, tmp); new_head = to_app(tmp); for (unsigned i = 0; i < r->get_tail_size(); ++i) { vs(r->get_tail(i), sz, es, tmp); new_tail.push_back(to_app(tmp)); tail_neg.push_back(r->is_neg_tail(i)); } r = mk(new_head.get(), new_tail.size(), new_tail.c_ptr(), tail_neg.c_ptr(), r->name(), false); // keep old variable indices around so we can compose with substitutions. // r->norm_vars(*this); } void rule_manager::check_valid_rule(app * head, unsigned n, app * const * tail) const { check_valid_head(head); } void rule_manager::check_valid_head(expr * head) const { SASSERT(head); if (!m_ctx.is_predicate(head)) { std::ostringstream out; out << "Illegal head. The head predicate needs to be uninterpreted and registered (as recursive) " << mk_pp(head, m); throw default_exception(out.str()); } unsigned num_args = to_app(head)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(head)->get_arg(i); if (!is_var(arg) && !m.is_value(arg)) { std::ostringstream out; out << "Illegal argument to predicate in head " << mk_pp(arg, m); throw default_exception(out.str()); } } } bool rule_manager::is_fact(app * head) const { unsigned num_args = head->get_num_args(); for (unsigned i = 0; i < num_args; i++) { if (!m.is_value(head->get_arg(i))) return false; } return true; } void rule::deallocate(ast_manager & m) { m.dec_ref(m_head); unsigned n = get_tail_size(); for (unsigned i = 0; i < n; i++) { m.dec_ref(get_tail(i)); } if (m_proof) { m.dec_ref(m_proof); } this->~rule(); m.get_allocator().deallocate(get_obj_size(n), this); } void rule::set_proof(ast_manager& m, proof* p) { if (p) { m.inc_ref(p); } if (m_proof) { m.dec_ref(m_proof); } m_proof = p; } bool rule::is_in_tail(const func_decl * p, bool only_positive) const { unsigned len = only_positive ? get_positive_tail_size() : get_uninterpreted_tail_size(); for (unsigned i = 0; i < len; i++) { if (get_tail(i)->get_decl()==p) { return true; } } return false; } // // non-predicates may appear only in the interpreted tail, it is therefore // sufficient only to check the tail. // bool rule_manager::has_uninterpreted_non_predicates(rule const& r, func_decl*& f) const { unsigned sz = r.get_tail_size(); m_ufproc.reset(); m_visited.reset(); for (unsigned i = r.get_uninterpreted_tail_size(); i < sz && !m_ufproc.found(f); ++i) { for_each_expr_core(m_ufproc, m_visited, r.get_tail(i)); } return m_ufproc.found(f); } // // Quantifiers may appear only in the interpreted tail, it is therefore // sufficient only to check the interpreted tail. // void rule_manager::has_quantifiers(rule const& r, bool& existential, bool& universal) const { unsigned sz = r.get_tail_size(); m_qproc.reset(); m_visited.reset(); for (unsigned i = r.get_uninterpreted_tail_size(); i < sz; ++i) { for_each_expr_core(m_qproc, m_visited, r.get_tail(i)); } existential = m_qproc.m_exist; universal = m_qproc.m_univ; } bool rule_manager::has_quantifiers(rule const& r) const { bool exist, univ; has_quantifiers(r, exist, univ); return exist || univ; } bool rule::has_negation() const { for (unsigned i = 0; i < get_uninterpreted_tail_size(); ++i) { if (is_neg_tail(i)) { return true; } } return false; } void rule::get_used_vars(used_vars& used) const { used.process(get_head()); unsigned sz = get_tail_size(); for (unsigned i = 0; i < sz; ++i) { used.process(get_tail(i)); } } void rule::get_vars(ast_manager& m, ptr_vector& sorts) const { sorts.reset(); used_vars used; get_used_vars(used); unsigned sz = used.get_max_found_var_idx_plus_1(); for (unsigned i = 0; i < sz; ++i) { sort* s = used.get(i); sorts.push_back(s?s:m.mk_bool_sort()); } } void rule::norm_vars(rule_manager & rm) { used_vars& used = rm.reset_used(); get_used_vars(used); unsigned first_unsused = used.get_max_found_var_idx_plus_1(); if (used.uses_all_vars(first_unsused)) { return; } ast_manager& m = rm.get_manager(); unsigned next_fresh_var = 0; expr_ref_vector subst_vals(m); for (unsigned i=0; i 0) out << ","; out << "\n "; if (is_neg_tail(i)) out << "not "; app * t = get_tail(i); if (ctx.is_predicate(t)) { output_predicate(ctx, t, out); } else { out << mk_pp(t, m); } } out << '.'; if (ctx.output_profile()) { out << " {"; output_profile(out); out << '}'; } out << '\n'; if (m_proof) { out << mk_pp(m_proof, m) << '\n'; } } bool rule_eq_proc::operator()(const rule * r1, const rule * r2) const { if (r1->get_head()!=r2->get_head()) { return false; } unsigned tail_len = r1->get_tail_size(); if (r2->get_tail_size()!=tail_len) { return false; } for (unsigned i=0; iget_tail(i)!=r2->get_tail(i)) { return false; } if (r1->is_neg_tail(i)!=r2->is_neg_tail(i)) { return false; } } return true; } unsigned rule::hash() const { unsigned res = get_head()->hash(); unsigned tail_len = get_tail_size(); for (unsigned i=0; ihash(), is_neg_tail(i))); } return res; } unsigned rule_hash_proc::operator()(const rule * r) const { return r->hash(); } }; template class rewriter_tpl; z3-4.4.0/src/muz/base/dl_context.h0000644000175000017500000004772112540347414016510 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_context.h Abstract: Author: Leonardo de Moura (leonardo) 2010-05-18. Revision History: --*/ #ifndef _DL_CONTEXT_H_ #define _DL_CONTEXT_H_ #ifdef _CYGWIN #undef min #undef max #endif #include"arith_decl_plugin.h" #include"map.h" #include"th_rewriter.h" #include"str_hashtable.h" #include"var_subst.h" #include"dl_costs.h" #include"dl_decl_plugin.h" #include"dl_rule_set.h" #include"lbool.h" #include"statistics.h" #include"params.h" #include"trail.h" #include"model_converter.h" #include"model2expr.h" #include"smt_params.h" #include"dl_rule_transformer.h" #include"expr_functors.h" #include"dl_engine_base.h" #include"bind_variables.h" #include"rule_properties.h" struct fixedpoint_params; namespace datalog { enum execution_result { OK, TIMEOUT, MEMOUT, INPUT_ERROR, APPROX, BOUNDED, CANCELED }; class relation_manager; typedef sort * relation_sort; typedef uint64 table_element; typedef svector table_fact; typedef app * relation_element; typedef app_ref relation_element_ref; class relation_fact : public app_ref_vector { public: class el_proxy { friend class relation_fact; relation_fact & m_parent; unsigned m_idx; el_proxy(relation_fact & parent, unsigned idx) : m_parent(parent), m_idx(idx) {} public: operator relation_element() const { return m_parent.get(m_idx); } relation_element operator->() const { return m_parent.get(m_idx); } relation_element operator=(const relation_element & val) const { m_parent.set(m_idx, val); return m_parent.get(m_idx); } relation_element operator=(const el_proxy & val) { m_parent.set(m_idx, val); return m_parent.get(m_idx); } }; typedef const relation_element * iterator; relation_fact(ast_manager & m) : app_ref_vector(m) {} relation_fact(ast_manager & m, unsigned sz) : app_ref_vector(m) { resize(sz); } relation_fact(context & ctx); iterator begin() const { return c_ptr(); } iterator end() const { return c_ptr()+size(); } relation_element operator[](unsigned i) const { return get(i); } el_proxy operator[](unsigned i) { return el_proxy(*this, i); } }; // attempt to modularize context code. class rel_context_base : public engine_base { public: rel_context_base(ast_manager& m, char const* name): engine_base(m, name) {} virtual ~rel_context_base() {} virtual relation_manager & get_rmanager() = 0; virtual const relation_manager & get_rmanager() const = 0; virtual relation_base & get_relation(func_decl * pred) = 0; virtual relation_base * try_get_relation(func_decl * pred) const = 0; virtual bool is_empty_relation(func_decl* pred) const = 0; virtual expr_ref try_get_formula(func_decl * pred) const = 0; virtual void display_output_facts(rule_set const& rules, std::ostream & out) const = 0; virtual void display_facts(std::ostream & out) const = 0; virtual void display_profile(std::ostream& out) = 0; virtual void restrict_predicates(func_decl_set const& predicates) = 0; virtual bool result_contains_fact(relation_fact const& f) = 0; virtual void add_fact(func_decl* pred, relation_fact const& fact) = 0; virtual void add_fact(func_decl* pred, table_fact const& fact) = 0; virtual bool has_facts(func_decl * pred) const = 0; virtual void store_relation(func_decl * pred, relation_base * rel) = 0; virtual void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred) = 0; virtual void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) = 0; virtual bool output_profile() const = 0; virtual void collect_non_empty_predicates(func_decl_set& preds) = 0; virtual void transform_rules() = 0; virtual bool try_get_size(func_decl* pred, unsigned& rel_sz) const = 0; virtual lbool saturate() = 0; }; class context { public: typedef unsigned finite_element; enum sort_kind { SK_UINT64, SK_SYMBOL }; class contains_pred : public i_expr_pred { context const& ctx; public: contains_pred(context& ctx): ctx(ctx) {} virtual ~contains_pred() {} virtual bool operator()(expr* e) { return ctx.is_predicate(e); } }; private: class sort_domain; class symbol_sort_domain; class uint64_sort_domain; class restore_rules; typedef hashtable symbol_set; typedef map sym2decl; typedef obj_map > pred2syms; typedef obj_map sort_domain_map; ast_manager & m; register_engine_base& m_register_engine; smt_params & m_fparams; params_ref m_params_ref; fixedpoint_params* m_params; bool m_generate_proof_trace; // cached configuration parameter bool m_unbound_compressor; // cached configuration parameter symbol m_default_relation; // cached configuration parameter dl_decl_util m_decl_util; th_rewriter m_rewriter; var_subst m_var_subst; rule_manager m_rule_manager; contains_pred m_contains_p; rule_properties m_rule_properties; rule_transformer m_transf; trail_stack m_trail; ast_ref_vector m_pinned; bind_variables m_bind_variables; sort_domain_map m_sorts; func_decl_set m_preds; sym2decl m_preds_by_name; pred2syms m_argument_var_names; rule_set m_rule_set; rule_set m_transformed_rule_set; expr_free_vars m_free_vars; unsigned m_rule_fmls_head; expr_ref_vector m_rule_fmls; svector m_rule_names; vector m_rule_bounds; expr_ref_vector m_background; model_converter_ref m_mc; proof_converter_ref m_pc; rel_context_base* m_rel; scoped_ptr m_engine; bool m_closed; bool m_saturation_was_run; bool m_enable_bind_variables; execution_result m_last_status; expr_ref m_last_answer; DL_ENGINE m_engine_type; volatile bool m_cancel; bool is_fact(app * head) const; bool has_sort_domain(relation_sort s) const; sort_domain & get_sort_domain(relation_sort s); const sort_domain & get_sort_domain(relation_sort s) const; class engine_type_proc; public: context(ast_manager & m, register_engine_base& re, smt_params& fp, params_ref const& p = params_ref()); ~context(); void reset(); void push(); void pop(); bool saturation_was_run() const { return m_saturation_was_run; } void notify_saturation_was_run() { m_saturation_was_run = true; } void configure_engine(); ast_manager & get_manager() const { return m; } rule_manager & get_rule_manager() { return m_rule_manager; } smt_params & get_fparams() const { return m_fparams; } fixedpoint_params const& get_params() const { return *m_params; } DL_ENGINE get_engine() { configure_engine(); return m_engine_type; } register_engine_base& get_register_engine() { return m_register_engine; } th_rewriter& get_rewriter() { return m_rewriter; } var_subst & get_var_subst() { return m_var_subst; } dl_decl_util & get_decl_util() { return m_decl_util; } bool generate_proof_trace() const; bool output_profile() const; bool output_tuples() const; bool use_map_names() const; bool fix_unbound_vars() const; symbol default_table() const; symbol default_relation() const; void set_default_relation(symbol const& s); symbol default_table_checker() const; symbol check_relation() const; bool default_table_checked() const; bool dbg_fpr_nonempty_relation_signature() const; unsigned dl_profile_milliseconds_threshold() const; bool all_or_nothing_deltas() const; bool compile_with_widening() const; bool unbound_compressor() const; void set_unbound_compressor(bool f); bool similarity_compressor() const; symbol print_aig() const; symbol tab_selection() const; unsigned similarity_compressor_threshold() const; unsigned soft_timeout() const; unsigned initial_restart_timeout() const; bool generate_explanations() const; bool explanations_on_relation_level() const; bool magic_sets_for_queries() const; bool karr() const; bool scale() const; bool magic() const; bool quantify_arrays() const; bool instantiate_quantifiers() const; bool xform_bit_blast() const; bool xform_slice() const; void register_finite_sort(sort * s, sort_kind k); /** Register uninterpreted constant to be used as an implicitly quantified variable. The variable gets quantified in the formula passed to rule::mk_rule_from_horn. */ void register_variable(func_decl* var); /* Replace constants that have been registered as variables by de-Bruijn indices and corresponding universal (if is_forall is true) or existential quantifier. */ expr_ref bind_vars(expr* fml, bool is_forall); bool& bind_vars_enabled() { return m_enable_bind_variables; } /** Register datalog relation. If named is true, we associate the predicate with its name, so that it can be retrieved by the try_get_predicate_decl() function. Auxiliary predicates introduced e.g. by rule transformations do not need to be named. */ void register_predicate(func_decl * pred, bool named); /** Restrict reltaions to set of predicates. */ void restrict_predicates(func_decl_set const& preds); /** \brief Retrieve predicates */ func_decl_set const& get_predicates() const { return m_preds; } ast_ref_vector const &get_pinned() const {return m_pinned; } bool is_predicate(func_decl* pred) const { return m_preds.contains(pred); } bool is_predicate(expr * e) const { return is_app(e) && is_predicate(to_app(e)->get_decl()); } /** \brief If a predicate name has a \c func_decl object assigned, return pointer to it; otherwise return 0. Not all \c func_decl object used as relation identifiers need to be assigned to their names. Generally, the names coming from the parses are registered here. */ func_decl * try_get_predicate_decl(symbol const& pred_name) const { func_decl * res = 0; m_preds_by_name.find(pred_name, res); return res; } /** \brief Create a fresh head predicate declaration. */ func_decl * mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix, unsigned arity, sort * const * domain, func_decl* orig_pred=0); /** \brief Return number of a symbol in a DK_SYMBOL kind sort (\see register_sort() ) */ finite_element get_constant_number(relation_sort sort, symbol s); /** \brief Return number of a symbol in a DK_UINT64 kind sort (\see register_sort() ) */ finite_element get_constant_number(relation_sort srt, uint64 el); /** \brief Output name of constant with number \c num in sort \c sort. */ void print_constant_name(relation_sort sort, uint64 num, std::ostream & out); bool try_get_sort_constant_count(relation_sort srt, uint64 & constant_count); uint64 get_sort_size_estimate(relation_sort srt); /** \brief Assign names of variables used in the declaration of a predicate. These names are used when printing out the relations to make the output conform to the one of bddbddb. */ void set_argument_names(const func_decl * pred, svector var_names); symbol get_argument_name(const func_decl * pred, unsigned arg_index); void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names); void set_output_predicate(func_decl * pred) { m_rule_set.set_output_predicate(pred); } rule_set & get_rules() { flush_add_rules(); return m_rule_set; } void get_rules_as_formulas(expr_ref_vector& fmls, expr_ref_vector& qs, svector& names); void get_raw_rule_formulas(expr_ref_vector& fmls, svector& names, vector &bounds); void add_fact(app * head); void add_fact(func_decl * pred, const relation_fact & fact); bool has_facts(func_decl * pred) const; void add_rule(rule_ref& r); void assert_expr(expr* e); expr_ref get_background_assertion(); unsigned get_num_assertions() { return m_background.size(); } expr* get_assertion(unsigned i) const { return m_background[i]; } /** Method exposed from API for adding rules. */ void add_rule(expr* rl, symbol const& name, unsigned bound = UINT_MAX); /** Update a named rule. */ void update_rule(expr* rl, symbol const& name); /** Retrieve the maximal number of relevant unfoldings of 'pred' with respect to the current state. */ unsigned get_num_levels(func_decl* pred); /** Retrieve the current cover of 'pred' up to 'level' unfoldings. Return just the delta that is known at 'level'. To obtain the full set of properties of 'pred' one should query at 'level+1', 'level+2' etc, and include level=-1. */ expr_ref get_cover_delta(int level, func_decl* pred); /** Add a property of predicate 'pred' at 'level'. It gets pushed forward when possible. */ void add_cover(int level, func_decl* pred, expr* property); /** \brief Check rule subsumption. */ bool check_subsumes(rule const& stronger_rule, rule const& weaker_rule); /** \brief Check if rule is well-formed according to engine. */ void check_rules(rule_set& r); /** \brief Return true if facts to \c pred can be added using the \c add_table_fact() function. This function should return true if \c pred is represented by a table_relation and there is no transformation of relation values before they are put into the table. */ void add_table_fact(func_decl * pred, const table_fact & fact); void add_table_fact(func_decl * pred, unsigned num_args, unsigned args[]); /** \brief To be called after all rules are added. */ void close(); void ensure_closed(); bool is_closed() { return m_closed; } /** \brief Undo the effect of the \c close operation. */ void reopen(); void ensure_opened(); model_converter_ref& get_model_converter() { return m_mc; } void add_model_converter(model_converter* mc) { m_mc = concat(m_mc.get(), mc); } proof_converter_ref& get_proof_converter() { return m_pc; } void add_proof_converter(proof_converter* pc) { m_pc = concat(m_pc.get(), pc); } void transform_rules(rule_transformer& transf); void transform_rules(rule_transformer::plugin* plugin); void replace_rules(rule_set const& rs); void record_transformed_rules(); void apply_default_transformation(); void collect_params(param_descrs& r); void updt_params(params_ref const& p); void display_rules(std::ostream & out) const { m_rule_set.display(out); } void display(std::ostream & out) const; void display_smt2(unsigned num_queries, expr* const* qs, std::ostream& out); void display_profile(std::ostream& out) const; // ----------------------------------- // // basic usage methods // // ----------------------------------- void cancel(); bool canceled() const { return m_cancel; } void cleanup(); void reset_cancel() { cleanup(); } /** \brief check if query 'q' is satisfied under asserted rules and background. If successful, return OK and into \c result assign a relation with all tuples matching the query. Otherwise return reason for failure and do not modify \c result. The numbers of variables in the query body must form a contiguous sequence starting from zero. The caller becomes an owner of the relation object returned in \c result. The relation object, however, should not outlive the datalog context since it is linked to a relation plugin in the context. */ lbool query(expr* q); /** \brief retrieve model from inductive invariant that shows query is unsat. \pre engine == 'pdr' || engine == 'duality' - this option is only supported for PDR mode and Duality mode. */ model_ref get_model(); /** \brief retrieve proof from derivation of the query. \pre engine == 'pdr' || engine == 'duality'- this option is only supported for PDR mode and Duality mode. */ proof_ref get_proof(); /** Query multiple output relations. */ lbool rel_query(unsigned num_rels, func_decl * const* rels); /** \brief retrieve last proof status. */ execution_result get_status(); void set_status(execution_result r) { m_last_status = r; } /** \brief retrieve formula corresponding to query that returns l_true. The formula describes one or more instances of the existential variables in the query that are derivable. */ expr* get_answer_as_formula(); void collect_statistics(statistics& st) const; void reset_statistics(); /** \brief Display a certificate for reachability and/or unreachability. */ void display_certificate(std::ostream& out); /** \brief query result if it contains fact. */ bool result_contains_fact(relation_fact const& f); rel_context_base* get_rel_context() { ensure_engine(); return m_rel; } private: /** Just reset all tables. */ void reset_tables(); void flush_add_rules(); void ensure_engine(); // auxilary functions for SMT2 pretty-printer. void declare_vars(expr_ref_vector& rules, mk_fresh_name& mk_fresh, std::ostream& out); //undefined and private copy constructor and operator= context(const context&); context& operator=(const context&); }; }; #endif /* _DL_CONTEXT_H_ */ z3-4.4.0/src/muz/base/dl_rule_subsumption_index.cpp0000644000175000017500000000333412540347414022155 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_rule_subsumption_index.cpp Abstract: Subsumption index for rules. Currently an underapproximation (fails to identify some subsumptions). Author: Krystof Hoder (t-khoder) 2011-10-10. Revision History: --*/ #include #include "ast_pp.h" #include "dl_rule_subsumption_index.h" namespace datalog { // ----------------------------------- // // rule_subsumption_index // // ----------------------------------- void rule_subsumption_index::handle_unconditioned_rule(rule * r) { SASSERT(r->get_tail_size()==0); app * head = r->get_head(); func_decl * pred = head->get_decl(); app_set * head_set; if(!m_unconditioned_heads.find(pred, head_set)) { head_set = alloc(app_set); m_unconditioned_heads.insert(pred, head_set); } head_set->insert(head); } void rule_subsumption_index::add(rule * r) { m_ref_holder.push_back(r); if(r->get_tail_size()==0) { handle_unconditioned_rule(r); } m_rule_set.insert(r); } bool rule_subsumption_index::is_subsumed(app * query) { func_decl * pred = query->get_decl(); app_set * head_set; if(m_unconditioned_heads.find(pred, head_set)) { if(head_set->contains(query)) { return true; } } return false; } bool rule_subsumption_index::is_subsumed(rule * r) { app * head = r->get_head(); if(is_subsumed(head)) { return true; } if(m_rule_set.contains(r)) { return true; } return false; } }; z3-4.4.0/src/muz/base/dl_rule_set.h0000644000175000017500000002174612540347414016645 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_rule_set.h Abstract: Author: Leonardo de Moura (leonardo) 2010-05-17. Revision History: --*/ #ifndef _DL_RULE_SET_H_ #define _DL_RULE_SET_H_ #include"obj_hashtable.h" #include"dl_rule.h" namespace datalog { class rule_set; class rule_dependencies { public: typedef obj_hashtable item_set; typedef obj_map deps_type; typedef deps_type::iterator iterator; private: /** Map (dependent -> set of master objects) Each master object is also present as a key of the map, even if its master set is empty. */ deps_type m_data; context & m_context; ptr_vector m_todo; expr_sparse_mark m_visited; //we need to take care with removing to avoid memory leaks void remove_m_data_entry(func_decl * key); //sometimes we need to return reference to an empty set, //so we return reference to this one. item_set m_empty_item_set; item_set & ensure_key(func_decl * pred); void insert(func_decl * depending, func_decl * master); void populate(rule const* r); public: rule_dependencies(context& ctx); rule_dependencies(const rule_dependencies & o, bool reversed = false); ~rule_dependencies(); void reset(); void populate(const rule_set & rules); void populate(unsigned n, rule * const * rules); void restrict(const item_set & allowed); void remove(func_decl * itm); void remove(const item_set & to_remove); bool empty() const { return m_data.empty(); } const item_set & get_deps(func_decl * f) const; /** \brief Number of predicates \c f depends on. */ unsigned in_degree(func_decl * f) const { return get_deps(f).size(); } /** \brief Number of predicates that depend on \c f. */ unsigned out_degree(func_decl * f) const; /** \brief If the rependency graph is acyclic, put all elements into \c res ordered so that elements can depend only on elements that are before them. If the graph is not acyclic, return false. */ bool sort_deps(ptr_vector & res); iterator begin() const { return m_data.begin(); } iterator end() const { return m_data.end(); } void display( std::ostream & out ) const; }; class rule_stratifier { public: typedef func_decl T; typedef obj_hashtable item_set; typedef ptr_vector comp_vector; typedef obj_map deps_type; private: const rule_dependencies & m_deps; comp_vector m_strats; obj_map m_preorder_nums; ptr_vector m_stack_S; ptr_vector m_stack_P; obj_map m_component_nums; comp_vector m_components; obj_map m_pred_strat_nums; unsigned m_next_preorder; unsigned m_first_preorder; /** Finds strongly connected components using the Gabow's algorithm. */ void traverse(T* el); /** Calls \c traverse to identify strognly connected components and then orders them using topological sorting. */ void process(); public: /** \remark The \c stratifier object keeps a reference to the \c deps object, so it must exist for the whole lifetime of the \c stratifier object. */ rule_stratifier(const rule_dependencies & deps) : m_deps(deps), m_next_preorder(0) { process(); } ~rule_stratifier(); /** Return vector of components ordered so that the only dependencies are from later components to earlier. */ const comp_vector & get_strats() const { return m_strats; } unsigned get_predicate_strat(func_decl * pred) const; void display( std::ostream & out ) const; }; /** \brief Datalog "Program" (aka rule set). */ class rule_set { friend class rule_dependencies; public: typedef ptr_vector pred_set_vector; typedef obj_map decl2rules; private: typedef obj_map decl2deps; context & m_context; rule_manager & m_rule_manager; rule_ref_vector m_rules; //!< all rules decl2rules m_head2rules; //!< mapping from head symbol to rules. rule_dependencies m_deps; //!< dependencies scoped_ptr m_stratifier; //!< contains stratifier object iff the rule_set is closed func_decl_set m_output_preds; //!< output predicates obj_map m_orig2pred; obj_map m_pred2orig; func_decl_ref_vector m_refs; //sometimes we need to return reference to an empty rule_vector, //so we return reference to this one. rule_vector m_empty_rule_vector; void compute_deps(); void compute_tc_deps(); bool stratified_negation(); bool check_min(); public: rule_set(context & ctx); rule_set(const rule_set & rs); ~rule_set(); ast_manager & get_manager() const; rule_manager & get_rule_manager() const { return const_cast(m_rule_manager); } context& get_context() const { return m_context; } void inherit_predicates(rule_set const& other); void inherit_predicate(rule_set const& other, func_decl* orig, func_decl* pred); func_decl* get_orig(func_decl* pred) const; func_decl* get_pred(func_decl* orig) const; /** \brief Add rule \c r to the rule set. */ void add_rule(rule * r); /** \brief Remove rule \c r from the rule set. */ void del_rule(rule * r); /** \brief Add all rules from a different rule_set. */ void add_rules(const rule_set& src); void replace_rules(const rule_set& other); /** \brief This method should be invoked after all rules are added to the rule set. It will check if the negation is indeed stratified. Return true if succeeded. \remark If new rules are added, the rule_set will be "reopen". */ bool close(); void ensure_closed(); /** \brief Undo the effect of the \c close() operation. */ void reopen(); bool is_closed() const { return m_stratifier != 0; } unsigned get_num_rules() const { return m_rules.size(); } bool empty() const { return m_rules.size() == 0; } rule * get_rule(unsigned i) const { return m_rules[i]; } rule * last() const { return m_rules[m_rules.size()-1]; } rule_ref_vector const& get_rules() const { return m_rules; } const rule_vector & get_predicate_rules(func_decl * pred) const; bool contains(func_decl* pred) const { return m_head2rules.contains(pred); } const rule_stratifier & get_stratifier() const { SASSERT(m_stratifier); return *m_stratifier; } const pred_set_vector & get_strats() const; unsigned get_predicate_strat(func_decl * pred) const; const rule_dependencies & get_dependencies() const { SASSERT(is_closed()); return m_deps; } // split predicats into founded and non-founded. void split_founded_rules(func_decl_set& founded, func_decl_set& non_founded); void reset(); void set_output_predicate(func_decl * pred) { m_refs.push_back(pred); m_output_preds.insert(pred); } bool is_output_predicate(func_decl * pred) const { return m_output_preds.contains(pred); } const func_decl_set & get_output_predicates() const { return m_output_preds; } func_decl* get_output_predicate() const { SASSERT(m_output_preds.size() == 1); return *m_output_preds.begin(); } void display(std::ostream & out) const; /** \brief Output rule dependencies. The rule set must be closed before calling this function. */ void display_deps(std::ostream & out) const; typedef rule * const * iterator; iterator begin() const { return m_rules.c_ptr(); } iterator end() const { return m_rules.c_ptr()+m_rules.size(); } decl2rules::iterator begin_grouped_rules() const { return m_head2rules.begin(); } decl2rules::iterator end_grouped_rules() const { return m_head2rules.end(); } }; inline std::ostream& operator<<(std::ostream& out, rule_set const& r) { r.display(out); return out; } }; #endif /* _DL_RULE_SET_H_ */ z3-4.4.0/src/muz/base/dl_rule_transformer.h0000644000175000017500000000541712540347414020411 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_rule_transformer.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-20. Revision History: --*/ #ifndef _DL_RULE_TRANSFORMER_H_ #define _DL_RULE_TRANSFORMER_H_ #include"map.h" #include"vector.h" #include"dl_rule.h" #include"dl_rule_set.h" namespace datalog { class context; class rule_transformer { public: class plugin; private: friend class plugin; typedef svector plugin_vector; struct plugin_comparator; context & m_context; rule_manager & m_rule_manager; bool m_dirty; svector m_plugins; void ensure_ordered(); public: rule_transformer(context & ctx); ~rule_transformer(); /** \brief Reset all registered transformers. */ void reset(); void cancel(); /** \brief Add a plugin for rule transformation. The rule_transformer object takes ownership of the plugin object. */ void register_plugin(plugin * p); /** \brief Transform the rule set using the registered transformation plugins. If the rule set has changed, return true; otherwise return false. */ bool operator()(rule_set & rules); }; class rule_transformer::plugin { friend class rule_transformer; unsigned m_priority; bool m_can_destratify_negation; rule_transformer * m_transformer; void attach(rule_transformer & transformer) { m_transformer = &transformer; } protected: /** \brief Create a plugin object for rule_transformer. The priority argument determines in what order will rules be applied to the rules (higher priority plugins will be applied first). */ plugin(unsigned priority, bool can_destratify_negation = false) : m_priority(priority), m_can_destratify_negation(can_destratify_negation), m_transformer(0) {} public: virtual ~plugin() {} unsigned get_priority() { return m_priority; } bool can_destratify_negation() const { return m_can_destratify_negation; } virtual void cancel() {} /** \brief Return \c rule_set object with containing transformed rules or 0 if no transformation was done. The caller takes ownership of the returned \c rule_set object. */ virtual rule_set * operator()(rule_set const & source) = 0; /** Removes duplicate tails. */ static void remove_duplicate_tails(app_ref_vector& tail, svector& tail_neg); }; }; #endif /* _DL_RULE_TRANSFORMER_H_ */ z3-4.4.0/src/muz/base/proof_utils.cpp0000644000175000017500000004745712540347414017253 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "dl_util.h" #include "proof_utils.h" #include "ast_smt2_pp.h" #include "var_subst.h" class reduce_hypotheses { typedef obj_hashtable expr_set; ast_manager& m; expr_ref_vector m_refs; obj_map m_cache; obj_map m_units; ptr_vector m_units_trail; unsigned_vector m_limits; obj_map m_hypmap; ptr_vector m_hyprefs; ptr_vector m_literals; void reset() { m_refs.reset(); m_cache.reset(); m_units.reset(); m_units_trail.reset(); m_limits.reset(); std::for_each(m_hyprefs.begin(), m_hyprefs.end(), delete_proc()); m_hypmap.reset(); m_hyprefs.reset(); m_literals.reset(); } void push() { m_limits.push_back(m_units_trail.size()); } void pop() { unsigned sz = m_limits.back(); while (m_units_trail.size() > sz) { m_units.remove(m_units_trail.back()); m_units_trail.pop_back(); } m_limits.pop_back(); } void get_literals(expr* clause) { m_literals.reset(); if (m.is_or(clause)) { m_literals.append(to_app(clause)->get_num_args(), to_app(clause)->get_args()); } else { m_literals.push_back(clause); } } void add_hypotheses(proof* p) { expr_set* hyps = 0; bool inherited = false; if (p->get_decl_kind() == PR_HYPOTHESIS) { hyps = alloc(expr_set); hyps->insert(m.get_fact(p)); m_hyprefs.push_back(hyps); } else { for (unsigned i = 0; i < m.get_num_parents(p); ++i) { expr_set* hyps1 = m_hypmap.find(m.get_parent(p, i)); if (hyps1) { if (!hyps) { hyps = hyps1; inherited = true; continue; } if (inherited) { hyps = alloc(expr_set,*hyps); m_hyprefs.push_back(hyps); inherited = false; } datalog::set_union(*hyps, *hyps1); } } } m_hypmap.insert(p, hyps); } expr_ref complement_lit(expr* e) { expr* e1; if (m.is_not(e, e1)) { return expr_ref(e1, m); } else { return expr_ref(m.mk_not(e), m); } } bool in_hypotheses(expr* e, expr_set* hyps) { if (!hyps) { return false; } expr_ref not_e = complement_lit(e); return hyps->contains(not_e); } bool contains_hypothesis(proof* p) { ptr_vector todo; ast_mark visit; todo.push_back(p); while (!todo.empty()) { p = todo.back(); todo.pop_back(); if (visit.is_marked(p)) { continue; } visit.mark(p, true); if (PR_HYPOTHESIS == p->get_decl_kind()) { return true; } for (unsigned i = 0; i < m.get_num_parents(p); ++i) { todo.push_back(m.get_parent(p, i)); } } return false; } bool is_closed(proof* p) { expr_set* hyps = m_hypmap.find(p); return !hyps || hyps->empty(); } public: reduce_hypotheses(ast_manager& m): m(m), m_refs(m) {} void operator()(proof_ref& pr) { proof_ref tmp(m); tmp = pr; elim(pr); reset(); CTRACE("proof_utils", contains_hypothesis(pr), tout << "Contains hypothesis:\n"; tout << mk_ismt2_pp(tmp, m) << "\n====>\n"; tout << mk_ismt2_pp(pr, m) << "\n";); } void elim(proof_ref& p) { proof_ref tmp(m); proof* result = p.get(); if (m_cache.find(p, result)) { p = result; return; } switch(p->get_decl_kind()) { case PR_HYPOTHESIS: if (!m_units.find(m.get_fact(p), result)) { result = p.get(); } add_hypotheses(result); break; case PR_LEMMA: { SASSERT(m.get_num_parents(p) == 1); tmp = m.get_parent(p, 0); elim(tmp); expr_set* hyps = m_hypmap.find(tmp); expr_set* new_hyps = 0; if (hyps) { new_hyps = alloc(expr_set, *hyps); } expr* fact = m.get_fact(p); // when hypothesis is a single literal of the form // (or A B), and the fact of p is (or A B). if (hyps && hyps->size() == 1 && in_hypotheses(fact, hyps)) { m_literals.reset(); m_literals.push_back(fact); } else { get_literals(fact); } for (unsigned i = 0; i < m_literals.size(); ++i) { expr* e = m_literals[i]; if (!in_hypotheses(e, hyps)) { m_literals[i] = m_literals.back(); m_literals.pop_back(); --i; } else { SASSERT(new_hyps); expr_ref not_e = complement_lit(e); SASSERT(new_hyps->contains(not_e)); new_hyps->remove(not_e); } } if (m_literals.empty()) { result = tmp; } else { expr_ref clause(m); if (m_literals.size() == 1) { clause = m_literals[0]; } else { clause = m.mk_or(m_literals.size(), m_literals.c_ptr()); } tmp = m.mk_lemma(tmp, clause); m_refs.push_back(tmp); result = tmp; } if (new_hyps && new_hyps->empty()) { dealloc(new_hyps); new_hyps = 0; } m_hypmap.insert(result, new_hyps); m_hyprefs.push_back(new_hyps); TRACE("proof_utils", tout << "New lemma: " << mk_pp(m.get_fact(p), m) << "\n==>\n" << mk_pp(m.get_fact(result), m) << "\n"; if (hyps) { expr_set::iterator it = hyps->begin(); expr_set::iterator end = hyps->end(); for (; it != end; ++it) { tout << "Hypothesis: " << mk_pp(*it, m) << "\n"; } }); break; } case PR_UNIT_RESOLUTION: { proof_ref_vector parents(m); parents.push_back(m.get_parent(p, 0)); push(); bool found_false = false; for (unsigned i = 1; i < m.get_num_parents(p); ++i) { tmp = m.get_parent(p, i); elim(tmp); if (m.is_false(m.get_fact(tmp))) { result = tmp; found_false = true; break; } 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); m_units_trail.push_back(m.get_fact(tmp)); } } if (found_false) { pop(); 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); if (m.is_false(clause)) { m_refs.push_back(tmp); result = tmp; 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; for (unsigned j = 0; j < m_literals.size(); ++j) { if (m.is_complement(m_literals[j], m.get_fact(parents[i].get()))) { found = true; break; } } if (!found) { // literal was removed as hypothesis. parents[i] = parents.back(); parents.pop_back(); --i; } } if (parents.size() == 1) { result = parents[0].get(); } else { result = m.mk_unit_resolution(parents.size(), parents.c_ptr()); m_refs.push_back(result); add_hypotheses(result); } pop(); break; } default: { ptr_buffer args; bool change = false; bool found_false = false; for (unsigned i = 0; i < m.get_num_parents(p); ++i) { tmp = m.get_parent(p, i); elim(tmp); if (m.is_false(m.get_fact(tmp))) { result = tmp; found_false = true; break; } // SASSERT(m.get_fact(tmp) == m.get_fact(m.get_parent(p, i))); change = change || (tmp != m.get_parent(p, i)); args.push_back(tmp); } if (found_false) { break; } if (m.has_fact(p)) { args.push_back(m.get_fact(p)); } if (change) { tmp = m.mk_app(p->get_decl(), args.size(), args.c_ptr()); m_refs.push_back(tmp); } else { tmp = p; } result = tmp; add_hypotheses(result); break; } } SASSERT(m_hypmap.contains(result)); 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) { ast_manager& m = pr.get_manager(); class reduce_hypotheses reduce(m); reduce(pr); CTRACE("proof_utils", !is_closed(m, pr), tout << mk_pp(pr, m) << "\n";); } class proof_is_closed { ast_manager& m; ptr_vector m_literals; ast_mark m_visit; void reset() { m_literals.reset(); m_visit.reset(); } bool check(proof* p) { // really just a partial check because nodes may be visited // already under a different lemma scope. if (m_visit.is_marked(p)) { return true; } bool result = false; m_visit.mark(p, true); switch(p->get_decl_kind()) { case PR_LEMMA: { unsigned sz = m_literals.size(); expr* cls = m.get_fact(p); m_literals.push_back(cls); if (m.is_or(cls)) { m_literals.append(to_app(cls)->get_num_args(), to_app(cls)->get_args()); } SASSERT(m.get_num_parents(p) == 1); result = check(m.get_parent(p, 0)); m_literals.resize(sz); break; } case PR_HYPOTHESIS: { expr* fact = m.get_fact(p); for (unsigned i = 0; i < m_literals.size(); ++i) { if (m.is_complement(m_literals[i], fact)) { result = true; break; } } break; } default: result = true; for (unsigned i = 0; i < m.get_num_parents(p); ++i) { if (!check(m.get_parent(p, i))) { result = false; break; } } break; } return result; } public: proof_is_closed(ast_manager& m): m(m) {} bool operator()(proof *p) { bool ok = check(p); reset(); return ok; } }; bool proof_utils::is_closed(ast_manager& m, proof* p) { proof_is_closed checker(m); return checker(p); } static void permute_unit_resolution(expr_ref_vector& refs, obj_map& cache, proof_ref& pr) { ast_manager& m = pr.get_manager(); proof* pr2 = 0; proof_ref_vector parents(m); proof_ref prNew(pr); if (cache.find(pr, pr2)) { pr = pr2; return; } for (unsigned i = 0; i < m.get_num_parents(pr); ++i) { prNew = m.get_parent(pr, i); permute_unit_resolution(refs, cache, prNew); parents.push_back(prNew); } prNew = pr; if (pr->get_decl_kind() == PR_UNIT_RESOLUTION && parents[0]->get_decl_kind() == PR_TH_LEMMA) { /* Unit resolution: T1: (or l_1 ... l_n l_1' ... l_m') T2: (not l_1) ... T(n+1): (not l_n) [unit-resolution T1 ... T(n+1)]: (or l_1' ... l_m') Th lemma: T1: (not l_1) ... Tn: (not l_n) [th-lemma T1 ... Tn]: (or l_{n+1} ... l_m) Such that (or l_1 .. l_n l_{n+1} .. l_m) is a theory axiom. Implement conversion: T1 |- not l_1 ... Tn |- not l_n ------------------------------- TH_LEMMA (or k_1 .. k_m j_1 ... j_m) S1 |- not k_1 ... Sm |- not k_m -------------------------------------------------------------- UNIT_RESOLUTION (or j_1 .. j_m) |-> T1 |- not l_1 ... Tn |- not l_n S1 |- not k_1 ... Sm |- not k_m ---------------------------------------------------------------- TH_LEMMA (or j_1 .. j_m) */ proof_ref_vector premises(m); proof* thLemma = parents[0].get(); for (unsigned i = 0; i < m.get_num_parents(thLemma); ++i) { premises.push_back(m.get_parent(thLemma, i)); } for (unsigned i = 1; i < parents.size(); ++i) { premises.push_back(parents[i].get()); } parameter const* params = thLemma->get_decl()->get_parameters(); unsigned num_params = thLemma->get_decl()->get_num_parameters(); SASSERT(params[0].is_symbol()); family_id tid = m.mk_family_id(params[0].get_symbol()); SASSERT(tid != null_family_id); prNew = m.mk_th_lemma(tid, m.get_fact(pr), premises.size(), premises.c_ptr(), num_params-1, params+1); } else { ptr_vector args; for (unsigned i = 0; i < parents.size(); ++i) { args.push_back(parents[i].get()); } if (m.has_fact(pr)) { args.push_back(m.get_fact(pr)); } prNew = m.mk_app(pr->get_decl(), args.size(), args.c_ptr()); } cache.insert(pr, prNew); refs.push_back(prNew); pr = prNew; } // permute unit resolution over Theory lemmas to track premises. void proof_utils::permute_unit_resolution(proof_ref& pr) { expr_ref_vector refs(pr.get_manager()); obj_map cache; ::permute_unit_resolution(refs, cache, pr); } class push_instantiations_up_cl { ast_manager& m; public: push_instantiations_up_cl(ast_manager& m): m(m) {} void operator()(proof_ref& p) { expr_ref_vector s0(m); p = push(p, s0); } private: proof* push(proof* p, expr_ref_vector const& sub) { proof_ref_vector premises(m); expr_ref conclusion(m); svector > positions; vector substs; if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) { for (unsigned i = 0; i < premises.size(); ++i) { compose(substs[i], sub); premises[i] = push(premises[i].get(), substs[i]); substs[i].reset(); } instantiate(sub, conclusion); return m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, positions, substs); } if (sub.empty()) { return p; } if (m.is_modus_ponens(p)) { SASSERT(m.get_num_parents(p) == 2); proof* p0 = m.get_parent(p, 0); proof* p1 = m.get_parent(p, 1); if (m.get_fact(p0) == m.get_fact(p)) { return push(p0, sub); } expr* e1, *e2; if (m.is_rewrite(p1, e1, e2) && is_quantifier(e1) && is_quantifier(e2) && to_quantifier(e1)->get_num_decls() == to_quantifier(e2)->get_num_decls()) { expr_ref r1(e1,m), r2(e2,m); instantiate(sub, r1); instantiate(sub, r2); p1 = m.mk_rewrite(r1, r2); return m.mk_modus_ponens(push(p0, sub), p1); } } premises.push_back(p); substs.push_back(sub); conclusion = m.get_fact(p); instantiate(sub, conclusion); return m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, positions, substs); } void compose(expr_ref_vector& sub, expr_ref_vector const& s0) { for (unsigned i = 0; i < sub.size(); ++i) { expr_ref e(m); var_subst(m, false)(sub[i].get(), s0.size(), s0.c_ptr(), e); sub[i] = e; } } void instantiate(expr_ref_vector const& sub, expr_ref& fml) { if (sub.empty()) { return; } if (!is_forall(fml)) { return; } quantifier* q = to_quantifier(fml); if (q->get_num_decls() != sub.size()) { TRACE("proof_utils", tout << "quantifier has different number of variables than substitution"; tout << mk_pp(q, m) << "\n"; tout << sub.size() << "\n";); return; } var_subst(m, false)(q->get_expr(), sub.size(), sub.c_ptr(), fml); } }; void proof_utils::push_instantiations_up(proof_ref& pr) { push_instantiations_up_cl push(pr.get_manager()); push(pr); } z3-4.4.0/src/muz/base/fixedpoint_params.pyg0000644000175000017500000002605412540347414020425 0ustar michaelmichaeldef_module_params('fixedpoint', description='fixedpoint parameters', export=True, params=(('timeout', UINT, UINT_MAX, 'set timeout'), ('engine', SYMBOL, 'auto-config', 'Select: auto-config, datalog, duality, pdr, bmc'), ('datalog.default_table', SYMBOL, 'sparse', 'default table implementation: sparse, hashtable, bitvector, interval'), ('datalog.default_relation', SYMBOL, 'pentagon', 'default relation implementation: external_relation, pentagon'), ('datalog.generate_explanations', BOOL, False, 'produce explanations for produced facts when using the datalog engine'), ('datalog.use_map_names', BOOL, True, "use names from map files when displaying tuples"), ('datalog.magic_sets_for_queries', BOOL, False, "magic set transformation will be used for queries"), ('datalog.explanations_on_relation_level', BOOL, False, 'if true, explanations are generated as history of each relation, ' + 'rather than per fact (generate_explanations must be set to true for ' + 'this option to have any effect)'), ('datalog.unbound_compressor', BOOL, True, "auxiliary relations will be introduced to avoid unbound variables " + "in rule heads"), ('datalog.similarity_compressor', BOOL, True, "rules that differ only in values of constants will be merged into " + "a single rule"), ('datalog.similarity_compressor_threshold', UINT, 11, "if similarity_compressor is on, this value determines how many " + "similar rules there must be in order for them to be merged"), ('datalog.all_or_nothing_deltas', BOOL, False, "compile rules so that it is enough for the delta relation in " + "union and widening operations to determine only whether the " + "updated relation was modified or not"), ('datalog.compile_with_widening', BOOL, False, "widening will be used to compile recursive rules"), ('datalog.default_table_checked', BOOL, False, "if true, the detault " + 'table will be default_table inside a wrapper that checks that its results ' + 'are the same as of default_table_checker table'), ('datalog.default_table_checker', SYMBOL, 'null', "see default_table_checked"), ('datalog.check_relation',SYMBOL,'null', "name of default relation to check. " + "operations on the default relation will be verified using SMT solving"), ('datalog.initial_restart_timeout', UINT, 0, "length of saturation run before the first restart (in ms), " + "zero means no restarts"), ('datalog.output_profile', BOOL, False, "determines whether profile information should be " + "output when outputting Datalog rules or instructions"), ('datalog.print.tuples', BOOL, True, "determines whether tuples for output predicates should be output"), ('datalog.profile_timeout_milliseconds', UINT, 0, "instructions and rules that took less than the threshold " + "will not be printed when printed the instruction/rule list"), ('datalog.dbg_fpr_nonempty_relation_signature', BOOL, False, "if true, finite_product_relation will attempt to avoid creating " + "inner relation with empty signature by putting in half of the " + "table columns, if it would have been empty otherwise"), ('duality.full_expand', BOOL, False, 'Fully expand derivation trees'), ('duality.no_conj', BOOL, False, 'No forced covering (conjectures)'), ('duality.feasible_edges', BOOL, True, 'Don\'t expand definitley infeasible edges'), ('duality.use_underapprox', BOOL, False, 'Use underapproximations'), ('duality.stratified_inlining', BOOL, False, 'Use stratified inlining'), ('duality.recursion_bound', UINT, UINT_MAX, 'Recursion bound for stratified inlining'), ('duality.profile', BOOL, False, 'profile run time'), ('duality.mbqi', BOOL, True, 'use model-based quantifier instantiation'), ('duality.batch_expand', BOOL, False, 'use batch expansion'), ('duality.conjecture_file', STRING, '', 'save conjectures to file'), ('pdr.bfs_model_search', BOOL, True, "use BFS strategy for expanding model search"), ('pdr.farkas', BOOL, True, "use lemma generator based on Farkas (for linear real arithmetic)"), ('generate_proof_trace', BOOL, False, "trace for 'sat' answer as proof object"), ('pdr.flexible_trace', BOOL, False, "allow PDR generate long counter-examples " + "by extending candidate trace within search area"), ('pdr.use_model_generalizer', BOOL, False, "use model for backwards propagation (instead of symbolic simulation)"), ('pdr.validate_result', BOOL, False, "validate result (by proof checking or model checking)"), ('pdr.simplify_formulas_pre', BOOL, False, "simplify derived formulas before inductive propagation"), ('pdr.simplify_formulas_post', BOOL, False, "simplify derived formulas after inductive propagation"), ('pdr.use_multicore_generalizer', BOOL, False, "extract multiple cores for blocking states"), ('pdr.use_inductive_generalizer', BOOL, True, "generalize lemmas using induction strengthening"), ('pdr.use_arith_inductive_generalizer', BOOL, False, "generalize lemmas using arithmetic heuristics for induction strengthening"), ('pdr.use_convex_closure_generalizer', BOOL, False, "generalize using convex closures of lemmas"), ('pdr.use_convex_interior_generalizer', BOOL, False, "generalize using convex interiors of lemmas"), ('pdr.cache_mode', UINT, 0, "use no (0), symbolic (1) or explicit " + "cache (2) for model search"), ('pdr.inductive_reachability_check', BOOL, False, "assume negation of the cube on the previous level when " + "checking for reachability (not only during cube weakening)"), ('pdr.max_num_contexts', UINT, 500, "maximal number of contexts to create"), ('pdr.try_minimize_core', BOOL, False, "try to reduce core size (before inductive minimization)"), ('pdr.utvpi', BOOL, True, 'Enable UTVPI strategy'), ('print_fixedpoint_extensions', BOOL, True, "use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, " + "when printing rules"), ('print_low_level_smt2', BOOL, False, "use (faster) low-level SMT2 printer (the printer is scalable " + "but the result may not be as readable)"), ('print_with_variable_declarations', BOOL, True, "use variable declarations when displaying rules " + "(instead of attempting to use original names)"), ('print_answer', BOOL, False, 'print answer instance(s) to query'), ('print_certificate', BOOL, False, 'print certificate for reachability or non-reachability'), ('print_boogie_certificate', BOOL, False, 'print certificate for reachability or non-reachability using a ' + 'format understood by Boogie'), ('print_statistics', BOOL, False, 'print statistics'), ('print_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'), ('tab.selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'), ('xform.bit_blast', BOOL, False, 'bit-blast bit-vectors'), ('xform.magic', BOOL, False, "perform symbolic magic set transformation"), ('xform.scale', BOOL, False, "add scaling variable to linear real arithemtic clauses"), ('xform.inline_linear', BOOL, True, "try linear inlining method"), ('xform.inline_eager', BOOL, True, "try eager inlining of rules"), ('xform.inline_linear_branch', BOOL, False, "try linear inlining method with potential expansion"), ('xform.fix_unbound_vars', BOOL, False, "fix unbound variables in tail"), ('xform.unfold_rules', UINT, 0, "unfold rules statically using iterative squarring"), ('xform.slice', BOOL, True, "simplify clause set using slicing"), ('xform.karr', BOOL, False, "Add linear invariants to clauses using Karr's method"), ('xform.quantify_arrays', BOOL, False, "create quantified Horn clauses from clauses with arrays"), ('xform.instantiate_quantifiers', BOOL, False, "instantiate quantified Horn clauses using E-matching heuristic"), ('xform.coalesce_rules', BOOL, False, "coalesce rules"), ('duality.enable_restarts', BOOL, False, 'DUALITY: enable restarts'), )) z3-4.4.0/src/muz/base/hnf.h0000644000175000017500000000204412540347414015105 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ /*-- Module Name: hnf.h Abstract: Horn normal form convertion. Author: Notes: Very similar to NNF. --*/ #ifndef _HNF_H_ #define _HNF_H_ #include"ast.h" #include"params.h" #include"defined_names.h" #include"proof_converter.h" class hnf { class imp; imp * m_imp; public: hnf(ast_manager & m); ~hnf(); void operator()(expr * n, // [IN] expression that should be put into Horn NF proof* p, // [IN] proof of n expr_ref_vector & rs, // [OUT] resultant (conjunction) of expressions proof_ref_vector& ps // [OUT] proofs of rs ); void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } void set_cancel(bool f); void set_name(symbol const& name); void reset(); func_decl_ref_vector const& get_fresh_predicates(); }; #endif /* _HNF_H_ */ z3-4.4.0/src/muz/base/dl_boogie_proof.cpp0000644000175000017500000002303312540347414020016 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ /** Example from Boogie: (derivation (step s!4 (main_loop_LoopHead true true) rule!3 (subst (= assertsPassed@@1 true) (= assertsPassed@2@@0 true) (= main_loop_LoopHead_assertsPassed true) ) (labels @950 +895 +668 +670 +893 +899 ) (ref true )) (step s!3 (main true false) rule!1 (subst (= assertsPassed true) (= assertsPassed@0 true) (= assertsPassed@2 false) (= main_assertsPassed false) ) (labels @839 +763 +547 +546 +761 +544 +545 +si_fcall_805 +681 +768 ) (ref s!4 )) (step s!2 (main_SeqInstr true false) rule!2 (subst (= assertsPassed@@0 true) (= assertsPassed@0@@0 false) (= main_SeqInstr_assertsPassed false) ) (labels @890 +851 +572 +si_fcall_883 +853 ) (ref s!3 )) (step s!1 (@Fail!0) rule!4 (subst (= assertsPassed@@2 true) (= main_SeqInstr_assertsPassed@@0 false) ) (labels ) (ref s!2 )) ) (model "tickleBool -> { true -> true false -> true else -> true } ") */ #include "dl_boogie_proof.h" #include "model_pp.h" #include "proof_utils.h" #include "ast_pp.h" #include "qe_util.h" namespace datalog { /** \brief push hyper-resolution steps upwards such that every use of hyper-resolution uses a premise that is not derived from hyper-resolution. perform the following rewrite: hr(hr(p1,p2,p3,..),p4,p5) => hr(p1,hr(p2,p4),p3,..,p5) */ void mk_input_resolution(proof_ref& pr) { ast_manager& m = pr.get_manager(); proof_ref pr1(m); proof_ref_vector premises1(m), premises2(m), premises(m); expr_ref conclusion1(m), conclusion2(m), conclusion(m); svector > positions1, positions2, positions; vector substs1, substs2, substs; if (m.is_hyper_resolve(pr, premises1, conclusion1, positions1, substs1) && m.is_hyper_resolve(premises1[0].get(), premises, conclusion2, positions, substs2)) { for (unsigned i = 1; i < premises1.size(); ++i) { pr1 = premises1[i].get(); mk_input_resolution(pr1); premises1[i] = pr1; } for (unsigned i = 0; i < premises.size(); ++i) { pr1 = premises[i].get(); mk_input_resolution(pr1); premises[i] = pr1; } unsigned sz = premises.size(); for (unsigned i = 1; i < sz; ++i) { proof* premise = premises[i].get(); expr_ref_vector literals(m); expr* l1, *l2; if (!m.is_implies(premise, l1, l2)) { continue; } qe::flatten_and(l1, literals); positions2.reset(); premises2.reset(); premises2.push_back(premise); substs2.reset(); for (unsigned j = 0; j < literals.size(); ++j) { expr* lit = literals[j].get(); for (unsigned k = 1; k < premises1.size(); ++k) { if (m.get_fact(premises1[k].get()) == lit) { premises2.push_back(premises1[k].get()); positions2.push_back(std::make_pair(j+1,0)); substs2.push_back(expr_ref_vector(m)); break; } } } premises[i] = m.mk_hyper_resolve(premises2.size(), premises2.c_ptr(), l2, positions2, substs2); } conclusion = conclusion1; pr = m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, positions, substs); } } void boogie_proof::set_proof(proof* p) { std::cout << "set proof\n"; m_proof = p; proof_utils::push_instantiations_up(m_proof); mk_input_resolution(m_proof); std::cout << "proof set\n"; } void boogie_proof::set_model(model* m) { m_model = m; } void boogie_proof::pp(std::ostream& out) { if (m_proof) { pp_proof(out); } if (m_model) { model_pp(out, *m_model); } } void boogie_proof::pp_proof(std::ostream& out) { vector steps; ptr_vector rules; rules.push_back(m_proof); steps.push_back(step()); obj_map index; index.insert(m_proof, 0); for (unsigned j = 0; j < rules.size(); ++j) { proof* p = rules[j]; proof_ref_vector premises(m); expr_ref conclusion(m); svector > positions; vector substs; expr* tmp; steps[j].m_fact = m.get_fact(p); m.is_implies(steps[j].m_fact, tmp, steps[j].m_fact); get_subst(p, steps[j].m_subst); get_labels(p, steps[j].m_labels); if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) { for (unsigned i = 1; i < premises.size(); ++i) { proof* premise = premises[i].get(); unsigned position = 0; if (!index.find(premise, position)) { position = rules.size(); rules.push_back(premise); steps.push_back(step()); index.insert(premise, position); } steps[j].m_refs.push_back(position); } get_rule_name(premises[0].get(), steps[j].m_rule_name); } } for (unsigned j = steps.size(); j > 0; ) { --j; step &s = steps[j]; // TBD // s.m_labels; // set references, compensate for reverse ordering. for (unsigned i = 0; i < s.m_refs.size(); ++i) { s.m_refs[i] = rules.size()-1-s.m_refs[i]; } } steps.reverse(); pp_steps(out, steps); } /** \brief extract the instantiation by searching for the first occurrence of a hyper-resolution rule that produces an instance. */ void boogie_proof::get_subst(proof* p, subst& s) { ptr_vector todo; todo.push_back(p); ast_mark visited; std::cout << "get_subst\n" << mk_pp(p, m) << "\n"; while (!todo.empty()) { proof* p = todo.back(); todo.pop_back(); if (visited.is_marked(p)) { continue; } visited.mark(p, true); proof_ref_vector premises(m); expr_ref conclusion(m); svector > positions; vector substs; if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) { expr_ref_vector const& sub = substs[0]; if (!sub.empty()) { quantifier* q = to_quantifier(m.get_fact(premises[0].get())); unsigned sz = sub.size(); SASSERT(sz == q->get_num_decls()); for (unsigned i = 0; i < sz; ++i) { s.push_back(std::make_pair(q->get_decl_name(sz-1-i), sub[i])); } return; } } unsigned sz = m.get_num_parents(p); for (unsigned i = 0; i < sz; ++i) { todo.push_back(m.get_parent(p, i)); } } } void boogie_proof::get_rule_name(proof* p, symbol& n) { } void boogie_proof::get_labels(proof* p, labels& lbls) { } void boogie_proof::pp_steps(std::ostream& out, vector& steps) { out << "(derivation\n"; for (unsigned i = 0; i < steps.size(); ++i) { pp_step(out, i, steps[i]); } out << ")\n"; } // step :: "(" "step" step-name fact rule-name subst labels premises ")" void boogie_proof::pp_step(std::ostream& out, unsigned id, step& s) { out << "(step\n"; out << " s!" << id << " "; pp_fact(out, s.m_fact); out << " " << s.m_rule_name << "\n"; pp_subst(out << " ", s.m_subst); pp_labels(out << " ", s.m_labels); pp_premises(out << " ", s.m_refs); out << ")\n"; } // fact :: "(" predicate theory-term* ")" void boogie_proof::pp_fact(std::ostream& out, expr* fact) { out << mk_pp(fact, m) << "\n"; } // subst :: "(" "subst" assignment* ")" void boogie_proof::pp_subst(std::ostream& out, subst& s) { out << "(subst"; for (unsigned i = 0; i < s.size(); ++i) { pp_assignment(out, s[i].first, s[i].second); } out << ")\n"; } // assignment :: "(" "=" variable theory-term ")" void boogie_proof::pp_assignment(std::ostream& out, symbol const& v, expr* t) { out << "\n (= " << v << " " << mk_pp(t, m) << ")"; } // labels :: "(" "labels" label* ")" void boogie_proof::pp_labels(std::ostream& out, labels& lbls) { out << "(labels"; for (unsigned i = 0; i < lbls.size(); ++i) { out << " " << lbls[i]; } out << ")\n"; } // premises "(" "ref" step-name* ")" void boogie_proof::pp_premises(std::ostream& out, refs& refs) { out << "(ref"; for (unsigned i = 0; i < refs.size(); ++i) { out << " s!" << refs[i]; } out << ")\n"; } } z3-4.4.0/src/muz/base/proof_utils.h0000644000175000017500000000163312540347414016702 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: proof_utils.h Abstract: Utilities for transforming proofs. Author: Nikolaj Bjorner (nbjorner) 2012-10-12. Revision History: --*/ #ifndef _PROOF_UTILS_H_ #define _PROOF_UTILS_H_ class proof_utils { public: /** \brief reduce the set of hypotheses used in the proof. */ static void reduce_hypotheses(proof_ref& pr); /** \brief Check that a proof does not contain open hypotheses. */ static bool is_closed(ast_manager& m, proof* p); /** \brief Permute unit resolution rule with th-lemma */ static void permute_unit_resolution(proof_ref& pr); /** \brief Push instantiations created in hyper-resolutions up to leaves. This produces a "ground" proof where leaves are annotated by instantiations. */ static void push_instantiations_up(proof_ref& pr); }; #endif z3-4.4.0/src/muz/base/dl_rule_subsumption_index.h0000644000175000017500000000263412540347414021624 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_rule_subsumption_index.h Abstract: Subsumption index for rules. Currently an underapproximation (fails to identify some subsumptions). Author: Krystof Hoder (t-khoder) 2011-10-10. Revision History: --*/ #ifndef _DL_RULE_SUBSUMPTION_INDEX_H_ #define _DL_RULE_SUBSUMPTION_INDEX_H_ #include "dl_context.h" namespace datalog { class rule_subsumption_index { //private and undefined copy constroctor rule_subsumption_index(rule_subsumption_index const&); //private and undefined operator= rule_subsumption_index& operator=(rule_subsumption_index const&); typedef obj_hashtable app_set; ast_manager & m; context & m_context; rule_ref_vector m_ref_holder; obj_map m_unconditioned_heads; hashtable m_rule_set; void handle_unconditioned_rule(rule * r); public: rule_subsumption_index(context & ctx) : m(ctx.get_manager()), m_context(ctx), m_ref_holder(ctx.get_rule_manager()) {} ~rule_subsumption_index() { reset_dealloc_values(m_unconditioned_heads); } void add(rule * r); bool is_subsumed(rule * r); bool is_subsumed(app * query); }; }; #endif /* _DL_RULE_SUBSUMPTION_INDEX_H_ */ z3-4.4.0/src/muz/base/bind_variables.cpp0000644000175000017500000001071212540347414017632 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: bind_variables.cpp Abstract: Utility to find constants that are declared as variables. Author: Nikolaj Bjorner (nbjorner) 9-24-2014 Notes: --*/ #include "bind_variables.h" bind_variables::bind_variables(ast_manager & m): m(m), m_vars(m), m_pinned(m) {} bind_variables::~bind_variables() { } expr_ref bind_variables::operator()(expr* fml, bool is_forall) { if (m_vars.empty()) { return expr_ref(fml, m); } SASSERT(m_pinned.empty()); expr_ref result = abstract(fml, m_cache, 0); if (!m_names.empty()) { m_bound.reverse(); m_names.reverse(); result = m.mk_quantifier(is_forall, m_bound.size(), m_bound.c_ptr(), m_names.c_ptr(), result); } m_pinned.reset(); m_cache.reset(); m_names.reset(); m_bound.reset(); for (var2bound::iterator it = m_var2bound.begin(); it != m_var2bound.end(); ++it) { it->m_value = 0; } return result; } expr_ref bind_variables::abstract(expr* term, cache_t& cache, unsigned scope) { unsigned sz = m_todo.size(); m_todo.push_back(term); m_args.reset(); expr* b, *arg; while (m_todo.size() > sz) { expr* e = m_todo.back(); if (cache.contains(e)) { m_todo.pop_back(); continue; } switch(e->get_kind()) { case AST_VAR: { SASSERT(to_var(e)->get_idx() < scope); // mixing bound variables and free is possible for the caller, // but not proper use. // So we assert here even though we don't check for it. cache.insert(e, e); m_todo.pop_back(); break; } case AST_APP: { app* a = to_app(e); var2bound::obj_map_entry* w = m_var2bound.find_core(a); if (w) { var* v = w->get_data().m_value; if (!v) { // allocate a bound index. v = m.mk_var(m_names.size(), m.get_sort(a)); m_names.push_back(a->get_decl()->get_name()); m_bound.push_back(m.get_sort(a)); w->get_data().m_value = v; m_pinned.push_back(v); } if (scope == 0) { cache.insert(e, v); } else { var* v1 = m.mk_var(scope + v->get_idx(), m.get_sort(v)); m_pinned.push_back(v1); cache.insert(e, v1); } m_todo.pop_back(); break; } bool all_visited = true; bool some_diff = false; m_args.reset(); for (unsigned i = 0; i < a->get_num_args(); ++i) { arg = a->get_arg(i); if (!cache.find(arg, b)) { m_todo.push_back(arg); all_visited = false; } else if (all_visited) { m_args.push_back(b); if (b != arg) { some_diff = true; } } } if (all_visited) { if (some_diff) { b = m.mk_app(a->get_decl(), m_args.size(), m_args.c_ptr()); m_pinned.push_back(b); } else { b = a; } cache.insert(e, b); m_todo.pop_back(); } break; } case AST_QUANTIFIER: { quantifier* q = to_quantifier(e); expr_ref_buffer patterns(m); expr_ref result1(m); unsigned new_scope = scope + q->get_num_decls(); cache_t new_cache; for (unsigned i = 0; i < q->get_num_patterns(); ++i) { patterns.push_back(abstract(q->get_pattern(i), new_cache, new_scope)); } result1 = abstract(q->get_expr(), new_cache, new_scope); b = m.update_quantifier(q, patterns.size(), patterns.c_ptr(), result1.get()); m_pinned.push_back(b); cache.insert(e, b); m_todo.pop_back(); break; } default: UNREACHABLE(); } } return expr_ref(cache.find(term), m); } void bind_variables::add_var(app* v) { m_vars.push_back(v); m_var2bound.insert(v, 0); } z3-4.4.0/src/muz/base/dl_rule_set.cpp0000644000175000017500000006570312540347414017201 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_rule_set.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-05-17. Revision History: --*/ #include #include #include"dl_context.h" #include"dl_rule_set.h" #include"ast_pp.h" namespace datalog { rule_dependencies::rule_dependencies(context& ctx): m_context(ctx) { } rule_dependencies::rule_dependencies(const rule_dependencies & o, bool reversed): m_context(o.m_context) { if (reversed) { iterator oit = o.begin(); iterator oend = o.end(); for (; oit!=oend; ++oit) { func_decl * pred = oit->m_key; item_set & orig_items = *oit->get_value(); ensure_key(pred); item_set::iterator dit = orig_items.begin(); item_set::iterator dend = orig_items.end(); for (; dit!=dend; ++dit) { func_decl * master_pred = *dit; insert(master_pred, pred); } } } else { iterator oit = o.begin(); iterator oend = o.end(); for (; oit!=oend; ++oit) { func_decl * pred = oit->m_key; item_set & orig_items = *oit->get_value(); m_data.insert(pred, alloc(item_set, orig_items)); } } } rule_dependencies::~rule_dependencies() { reset(); } void rule_dependencies::reset() { reset_dealloc_values(m_data); } void rule_dependencies::remove_m_data_entry(func_decl * key) { item_set * itm_set = m_data.find(key); dealloc(itm_set); m_data.remove(key); } rule_dependencies::item_set & rule_dependencies::ensure_key(func_decl * pred) { deps_type::obj_map_entry * e = m_data.insert_if_not_there2(pred, 0); if (!e->get_data().m_value) { e->get_data().m_value = alloc(item_set); } return *e->get_data().m_value; } void rule_dependencies::insert(func_decl * depending, func_decl * master) { SASSERT(m_data.contains(master)); //see m_data documentation item_set & s = ensure_key(depending); s.insert(master); } void rule_dependencies::populate(const rule_set & rules) { SASSERT(m_data.empty()); rule_set::decl2rules::iterator it = rules.m_head2rules.begin(); rule_set::decl2rules::iterator end = rules.m_head2rules.end(); for (; it != end; ++it) { ptr_vector * rules = it->m_value; ptr_vector::iterator it2 = rules->begin(); ptr_vector::iterator end2 = rules->end(); for (; it2 != end2; ++it2) { populate(*it2); } } } void rule_dependencies::populate(unsigned n, rule * const * rules) { SASSERT(m_data.empty()); for (unsigned i=0; iget_decl()->get_name() << "\n";); m_visited.reset(); func_decl * d = r->get_decl(); func_decl_set & s = ensure_key(d); for (unsigned i = 0; i < r->get_tail_size(); ++i) { m_todo.push_back(r->get_tail(i)); } while (!m_todo.empty()) { expr* e = m_todo.back(); m_todo.pop_back(); if (m_visited.is_marked(e)) { continue; } m_visited.mark(e, true); if (is_app(e)) { app* a = to_app(e); d = a->get_decl(); if (m_context.is_predicate(d)) { // insert d and ensure the invariant // that every predicate is present as // a key in m_data s.insert(d); ensure_key(d); } m_todo.append(a->get_num_args(), a->get_args()); } else if (is_quantifier(e)) { m_todo.push_back(to_quantifier(e)->get_expr()); } } } const rule_dependencies::item_set & rule_dependencies::get_deps(func_decl * f) const { deps_type::obj_map_entry * e = m_data.find_core(f); if (!e) { return m_empty_item_set; } SASSERT(e->get_data().get_value()); return *e->get_data().get_value(); } void rule_dependencies::restrict(const item_set & allowed) { ptr_vector to_remove; iterator pit = begin(); iterator pend = end(); for (; pit!=pend; ++pit) { func_decl * pred = pit->m_key; if (!allowed.contains(pred)) { to_remove.insert(pred); continue; } item_set& itms = *pit->get_value(); set_intersection(itms, allowed); } ptr_vector::iterator rit = to_remove.begin(); ptr_vector::iterator rend = to_remove.end(); for (; rit != rend; ++rit) { remove_m_data_entry(*rit); } } void rule_dependencies::remove(func_decl * itm) { remove_m_data_entry(itm); iterator pit = begin(); iterator pend = end(); for (; pit != pend; ++pit) { item_set & itms = *pit->get_value(); itms.remove(itm); } } void rule_dependencies::remove(const item_set & to_remove) { item_set::iterator rit = to_remove.begin(); item_set::iterator rend = to_remove.end(); for (; rit!=rend; ++rit) { remove_m_data_entry(*rit); } iterator pit = begin(); iterator pend = end(); for (; pit!=pend; ++pit) { item_set * itms = pit->get_value(); set_difference(*itms, to_remove); } } unsigned rule_dependencies::out_degree(func_decl * f) const { unsigned res = 0; iterator pit = begin(); iterator pend = end(); for (; pit!=pend; ++pit) { item_set & itms = *pit->get_value(); if (itms.contains(f)) { res++; } } return res; } bool rule_dependencies::sort_deps(ptr_vector & res) { typedef obj_map deg_map; unsigned init_len = res.size(); deg_map degs; unsigned curr_index = init_len; rule_dependencies reversed(*this, true); iterator pit = begin(); iterator pend = end(); for (; pit!=pend; ++pit) { func_decl * pred = pit->m_key; unsigned deg = in_degree(pred); if (deg==0) { res.push_back(pred); } else { degs.insert(pred, deg); } } while (curr_indexget_data().m_value; SASSERT(child_deg>0); child_deg--; if (child_deg==0) { res.push_back(child); } } curr_index++; } if (res.size() < init_len + m_data.size()) { res.shrink(init_len); return false; } SASSERT(res.size()==init_len+m_data.size()); return true; } void rule_dependencies::display(std::ostream & out ) const { iterator pit = begin(); iterator pend = end(); for (; pit != pend; ++pit) { func_decl * pred = pit->m_key; const item_set & deps = *pit->m_value; item_set::iterator dit=deps.begin(); item_set::iterator dend=deps.end(); if (dit == dend) { out<get_name()<<" - \n"; } for (; dit != dend; ++dit) { func_decl * dep = *dit; out << pred->get_name() << " -> " << dep->get_name() << "\n"; } } } // ----------------------------------- // // rule_set // // ----------------------------------- rule_set::rule_set(context & ctx) : m_context(ctx), m_rule_manager(ctx.get_rule_manager()), m_rules(m_rule_manager), m_deps(ctx), m_stratifier(0), m_refs(ctx.get_manager()) { } rule_set::rule_set(const rule_set & other) : m_context(other.m_context), m_rule_manager(other.m_rule_manager), m_rules(m_rule_manager), m_deps(other.m_context), m_stratifier(0), m_refs(m_context.get_manager()) { add_rules(other); if (other.m_stratifier) { VERIFY(close()); } } rule_set::~rule_set() { reset(); } void rule_set::reset() { m_rules.reset(); reset_dealloc_values(m_head2rules); m_deps.reset(); m_stratifier = 0; m_output_preds.reset(); m_orig2pred.reset(); m_pred2orig.reset(); m_refs.reset(); } ast_manager & rule_set::get_manager() const { return m_context.get_manager(); } func_decl* rule_set::get_orig(func_decl* pred) const { func_decl* orig = pred; m_pred2orig.find(pred, orig); return orig; } func_decl* rule_set::get_pred(func_decl* orig) const { func_decl* pred = orig; m_orig2pred.find(orig, pred); return pred; } void rule_set::inherit_predicates(rule_set const& other) { m_refs.append(other.m_refs); set_union(m_output_preds, other.m_output_preds); { obj_map::iterator it = other.m_orig2pred.begin(); obj_map::iterator end = other.m_orig2pred.end(); for (; it != end; ++it) { m_orig2pred.insert(it->m_key, it->m_value); } } { obj_map::iterator it = other.m_pred2orig.begin(); obj_map::iterator end = other.m_pred2orig.end(); for (; it != end; ++it) { m_pred2orig.insert(it->m_key, it->m_value); } } } void rule_set::inherit_predicate(rule_set const& other, func_decl* orig, func_decl* pred) { if (other.is_output_predicate(orig)) { set_output_predicate(pred); } orig = other.get_orig(orig); m_refs.push_back(pred); m_refs.push_back(orig); m_orig2pred.insert(orig, pred); m_pred2orig.insert(pred, orig); } void rule_set::add_rule(rule * r) { TRACE("dl_verbose", r->display(m_context, tout << "add:");); SASSERT(!is_closed()); m_rules.push_back(r); app * head = r->get_head(); SASSERT(head != 0); func_decl * d = head->get_decl(); decl2rules::obj_map_entry* e = m_head2rules.insert_if_not_there2(d, 0); if (!e->get_data().m_value) e->get_data().m_value = alloc(ptr_vector); e->get_data().m_value->push_back(r); } void rule_set::del_rule(rule * r) { TRACE("dl", r->display(m_context, tout << "del:");); func_decl* d = r->get_decl(); rule_vector* rules = m_head2rules.find(d); #define DEL_VECTOR(_v) \ for (unsigned i = (_v).size(); i > 0; ) { \ --i; \ if ((_v)[i] == r) { \ (_v)[i] = (_v).back(); \ (_v).pop_back(); \ break; \ } \ } \ DEL_VECTOR(*rules); DEL_VECTOR(m_rules); } void rule_set::ensure_closed() { if (!is_closed()) { VERIFY(close()); } } bool rule_set::close() { SASSERT(!is_closed()); //the rule_set is not already closed m_deps.populate(*this); m_stratifier = alloc(rule_stratifier, m_deps); if (!stratified_negation() || !check_min()) { m_stratifier = 0; m_deps.reset(); return false; } return true; } void rule_set::reopen() { if (is_closed()) { m_stratifier = 0; m_deps.reset(); } } /** \brief Return true if the negation is indeed stratified. */ bool rule_set::stratified_negation() { ptr_vector::const_iterator it = m_rules.c_ptr(); ptr_vector::const_iterator end = m_rules.c_ptr()+m_rules.size(); for (; it != end; it++) { rule * r = *it; app * head = r->get_head(); func_decl * head_decl = head->get_decl(); unsigned n = r->get_uninterpreted_tail_size(); for (unsigned i = r->get_positive_tail_size(); i < n; i++) { SASSERT(r->is_neg_tail(i)); func_decl * tail_decl = r->get_tail(i)->get_decl(); unsigned neg_strat = get_predicate_strat(tail_decl); unsigned head_strat = get_predicate_strat(head_decl); SASSERT(head_strat>=neg_strat); //head strat can never be lower than that of a tail if (head_strat == neg_strat) { return false; } } } return true; } bool rule_set::check_min() { // For now, we check the following: // // if a min aggregation function occurs in an SCC, is this SCC // free of any other non-monotonic functions, e.g. negation? const unsigned NEG_BIT = 1U << 0; const unsigned MIN_BIT = 1U << 1; ptr_vector::const_iterator it = m_rules.c_ptr(); ptr_vector::const_iterator end = m_rules.c_ptr() + m_rules.size(); unsigned_vector component_status(m_stratifier->get_strats().size()); for (; it != end; it++) { rule * r = *it; app * head = r->get_head(); func_decl * head_decl = head->get_decl(); unsigned head_strat = get_predicate_strat(head_decl); unsigned n = r->get_tail_size(); for (unsigned i = 0; i < n; i++) { func_decl * tail_decl = r->get_tail(i)->get_decl(); unsigned strat = get_predicate_strat(tail_decl); if (r->is_neg_tail(i)) { SASSERT(strat < component_status.size()); component_status[strat] |= NEG_BIT; } if (r->is_min_tail(i)) { SASSERT(strat < component_status.size()); component_status[strat] |= MIN_BIT; } } } const unsigned CONFLICT = NEG_BIT | MIN_BIT; for (unsigned k = 0; k < component_status.size(); ++k) { if (component_status[k] == CONFLICT) return false; } return true; } void rule_set::replace_rules(const rule_set & src) { if (this != &src) { reset(); add_rules(src); } } void rule_set::add_rules(const rule_set & src) { SASSERT(!is_closed()); unsigned n = src.get_num_rules(); for (unsigned i = 0; i < n; i++) { add_rule(src.get_rule(i)); } inherit_predicates(src); } const rule_vector & rule_set::get_predicate_rules(func_decl * pred) const { decl2rules::obj_map_entry * e = m_head2rules.find_core(pred); if (!e) { return m_empty_rule_vector; } return *e->get_data().m_value; } const rule_set::pred_set_vector & rule_set::get_strats() const { SASSERT(m_stratifier); return m_stratifier->get_strats(); } unsigned rule_set::get_predicate_strat(func_decl * pred) const { SASSERT(m_stratifier); return m_stratifier->get_predicate_strat(pred); } void rule_set::split_founded_rules(func_decl_set& founded, func_decl_set& non_founded) { founded.reset(); non_founded.reset(); { decl2rules::iterator it = begin_grouped_rules(), end = end_grouped_rules(); for (; it != end; ++it) { non_founded.insert(it->m_key); } } bool change = true; while (change) { change = false; func_decl_set::iterator it = non_founded.begin(), end = non_founded.end(); for (; it != end; ++it) { rule_vector const& rv = get_predicate_rules(*it); bool found = false; for (unsigned i = 0; !found && i < rv.size(); ++i) { rule const& r = *rv[i]; bool is_founded = true; for (unsigned j = 0; is_founded && j < r.get_uninterpreted_tail_size(); ++j) { is_founded = founded.contains(r.get_decl(j)); } if (is_founded) { founded.insert(*it); non_founded.remove(*it); change = true; found = true; } } } } } void rule_set::display(std::ostream & out) const { out << "; rule count: " << get_num_rules() << "\n"; out << "; predicate count: " << m_head2rules.size() << "\n"; func_decl_set::iterator pit = m_output_preds.begin(); func_decl_set::iterator pend = m_output_preds.end(); for (; pit != pend; ++pit) { out << "; output: " << (*pit)->get_name() << '\n'; } decl2rules::iterator it = m_head2rules.begin(); decl2rules::iterator end = m_head2rules.end(); for (; it != end; ++it) { ptr_vector * rules = it->m_value; ptr_vector::iterator it2 = rules->begin(); ptr_vector::iterator end2 = rules->end(); for (; it2 != end2; ++it2) { rule * r = *it2; if (!r->passes_output_thresholds(m_context)) { continue; } r->display(m_context, out); } } } void rule_set::display_deps( std::ostream & out ) const { const pred_set_vector & strats = get_strats(); pred_set_vector::const_iterator sit = strats.begin(); pred_set_vector::const_iterator send = strats.end(); for (; sit!=send; ++sit) { func_decl_set & strat = **sit; func_decl_set::iterator fit=strat.begin(); func_decl_set::iterator fend=strat.end(); bool non_empty = false; for (; fit!=fend; ++fit) { func_decl * first = *fit; const func_decl_set & deps = m_deps.get_deps(first); func_decl_set::iterator dit=deps.begin(); func_decl_set::iterator dend=deps.end(); for (; dit!=dend; ++dit) { non_empty = true; func_decl * dep = *dit; out<get_name()<<" -> "<get_name()<<"\n"; } } if (non_empty && sit!=send) { out << "\n"; } } } // ----------------------------------- // // rule_stratifier // // ----------------------------------- rule_stratifier::~rule_stratifier() { comp_vector::iterator it = m_strats.begin(); comp_vector::iterator end = m_strats.end(); for (; it!=end; ++it) { SASSERT(*it); dealloc(*it); } } unsigned rule_stratifier::get_predicate_strat(func_decl * pred) const { unsigned num; if (!m_pred_strat_nums.find(pred, num)) { //the number of the predicate is not stored, therefore it did not appear //in the algorithm and therefore it does not depend on anything and nothing //depends on it. So it is safe to assign zero strate to it, although it is //not strictly true. num = 0; } return num; } void rule_stratifier::traverse(T* el) { unsigned p_num; if (m_preorder_nums.find(el, p_num)) { if (p_num < m_first_preorder) { //traversed in a previous sweep return; } if (m_component_nums.contains(el)) { //we already assigned a component for el return; } while (!m_stack_P.empty()) { unsigned on_stack_num; VERIFY( m_preorder_nums.find(m_stack_P.back(), on_stack_num) ); if (on_stack_num <= p_num) { break; } m_stack_P.pop_back(); } } else { p_num=m_next_preorder++; m_preorder_nums.insert(el, p_num); m_stack_S.push_back(el); m_stack_P.push_back(el); const item_set & children = m_deps.get_deps(el); item_set::iterator cit=children.begin(); item_set::iterator cend=children.end(); for (; cit!=cend; ++cit) { traverse(*cit); } if (el == m_stack_P.back()) { unsigned comp_num = m_components.size(); item_set * new_comp = alloc(item_set); m_components.push_back(new_comp); T* s_el; do { s_el=m_stack_S.back(); m_stack_S.pop_back(); new_comp->insert(s_el); m_component_nums.insert(s_el, comp_num); } while (s_el!=el); m_stack_P.pop_back(); } } } void rule_stratifier::process() { if (m_deps.empty()) { return; } //detect strong components rule_dependencies::iterator it = m_deps.begin(); rule_dependencies::iterator end = m_deps.end(); for (; it!=end; ++it) { T * el = it->m_key; //we take a note of the preorder number with which this sweep started m_first_preorder = m_next_preorder; traverse(el); } //do topological sorting //degres of components (number of inter-component edges ending up in the component) svector in_degrees; in_degrees.resize(m_components.size()); //init in_degrees it = m_deps.begin(); end = m_deps.end(); for (; it != end; ++it) { T * el = it->m_key; item_set * out_edges = it->m_value; unsigned el_comp; VERIFY( m_component_nums.find(el, el_comp) ); item_set::iterator eit = out_edges->begin(); item_set::iterator eend = out_edges->end(); for (; eit!=eend; ++eit) { T * tgt = *eit; unsigned tgt_comp = m_component_nums.find(tgt); if (el_comp != tgt_comp) { in_degrees[tgt_comp]++; } } } // We put components whose indegree is zero to m_strats and assign its // m_components entry to zero. unsigned comp_cnt = m_components.size(); for (unsigned i = 0; i < comp_cnt; i++) { if (in_degrees[i] == 0) { m_strats.push_back(m_components[i]); m_components[i] = 0; } } SASSERT(!m_strats.empty()); //the component graph is acyclic and non-empty //we remove edges from components with zero indegre building the topological ordering unsigned strats_index = 0; while (strats_index < m_strats.size()) { //m_strats.size() changes inside the loop! item_set * comp = m_strats[strats_index]; item_set::iterator cit=comp->begin(); item_set::iterator cend=comp->end(); for (; cit!=cend; ++cit) { T * el = *cit; const item_set & deps = m_deps.get_deps(el); item_set::iterator eit=deps.begin(); item_set::iterator eend=deps.end(); for (; eit!=eend; ++eit) { T * tgt = *eit; unsigned tgt_comp; VERIFY( m_component_nums.find(tgt, tgt_comp) ); //m_components[tgt_comp]==0 means the edge is intra-component. //Otherwise it would go to another component, but it is not possible, since //as m_components[tgt_comp]==0, its indegree has already reached zero. if (m_components[tgt_comp]) { SASSERT(in_degrees[tgt_comp]>0); in_degrees[tgt_comp]--; if (in_degrees[tgt_comp]==0) { m_strats.push_back(m_components[tgt_comp]); m_components[tgt_comp] = 0; } } traverse(*cit); } } strats_index++; } //we have managed to topologicaly order all the components SASSERT(std::find_if(m_components.begin(), m_components.end(), std::bind1st(std::not_equal_to(), (item_set*)0)) == m_components.end()); //reverse the strats array, so that the only the later components would depend on earlier ones std::reverse(m_strats.begin(), m_strats.end()); SASSERT(m_pred_strat_nums.empty()); unsigned strat_cnt = m_strats.size(); for (unsigned strat_index=0; strat_indexbegin(); item_set::iterator cend=comp->end(); for (; cit != cend; ++cit) { T * el = *cit; m_pred_strat_nums.insert(el, strat_index); } } //finalize structures that are not needed anymore m_preorder_nums.finalize(); m_stack_S.finalize(); m_stack_P.finalize(); m_component_nums.finalize(); m_components.finalize(); } void rule_stratifier::display(std::ostream& out) const { m_deps.display(out << "dependencies\n"); out << "strata\n"; for (unsigned i = 0; i < m_strats.size(); ++i) { item_set::iterator it = m_strats[i]->begin(); item_set::iterator end = m_strats[i]->end(); for (; it != end; ++it) { out << (*it)->get_name() << " "; } out << "\n"; } } }; z3-4.4.0/src/muz/base/dl_boogie_proof.h0000644000175000017500000000535212540347414017467 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ /** output :: derivation model derivation :: "(" "derivation" step* ")" step :: "(" "step" step-name fact rule-name subst labels premises ")" step-name :: identifier rule-name :: identifier fact :: "(" predicate theory-term* ")" subst :: "(" "subst" assignment* ")" assignment :: "(" "=" variable theory-term ")" labels :: "(" "labels" label* ")" premises :: "(" "ref" step-name* ")" model :: "(" "model" smtlib2-model ")" In each step the "fact" is derivable by hyper-resolution from the named premises and the named rule, under the given substitution for the universally quantified variables in the rule. The premises of each step must have occurred previously in the step sequence. The last fact is a nullary placeholder predicate representing satisfaction of the query (its name is arbitrary). The labels list consists of all the positively labeled sub-formulas whose truth is used in the proof, and all the negatively labeled formulas whose negation is used. A theory-term is a ground term using only interpreted constants of the background theories. The smtlib2-model gives an interpretation of the uninterpreted constants in the background theory under which the derivation is valid. Currently it is a quoted string in the old z3 model format, for compatibility with Boogie, however, this should be changed to the new model format (using define-fun) when Boogie supports this. */ #include "ast.h" #include "model.h" namespace datalog { class boogie_proof { typedef vector > subst; typedef svector labels; typedef unsigned_vector refs; struct step { symbol m_rule_name; expr* m_fact; subst m_subst; labels m_labels; refs m_refs; }; ast_manager& m; proof_ref m_proof; model_ref m_model; void pp_proof(std::ostream& out); void pp_steps(std::ostream& out, vector& steps); void pp_step(std::ostream& out, unsigned i, step& s); void pp_fact(std::ostream& out, expr* fact); void pp_subst(std::ostream& out, subst& s); void pp_assignment(std::ostream& out, symbol const& v, expr* t); void pp_labels(std::ostream& out, labels& lbls); void pp_premises(std::ostream& out, refs& refs); void get_subst(proof* p, subst& sub); void get_rule_name(proof* p, symbol&); void get_labels(proof* p, labels&); public: boogie_proof(ast_manager& m): m(m), m_proof(m), m_model(0) {} void set_proof(proof* p); void set_model(model* m); void pp(std::ostream& out); }; } z3-4.4.0/src/muz/base/dl_engine_base.h0000644000175000017500000000440212540347414017250 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_engine_base.h Abstract: Base class for Datalog engines. Author: Nikolaj Bjorner (nbjorner) 2013-08-28 Revision History: --*/ #ifndef _DL_ENGINE_BASE_H_ #define _DL_ENGINE_BASE_H_ #include "model.h" namespace datalog { enum DL_ENGINE { DATALOG_ENGINE, PDR_ENGINE, QPDR_ENGINE, BMC_ENGINE, QBMC_ENGINE, TAB_ENGINE, CLP_ENGINE, DUALITY_ENGINE, DDNF_ENGINE, LAST_ENGINE }; class engine_base { ast_manager& m; std::string m_name; public: engine_base(ast_manager& m, char const* name): m(m), m_name(name) {} virtual ~engine_base() {} virtual expr_ref get_answer() = 0; virtual lbool query(expr* q) = 0; virtual lbool query(unsigned num_rels, func_decl*const* rels) { return l_undef; } virtual void reset_statistics() {} virtual void display_profile(std::ostream& out) const {} virtual void collect_statistics(statistics& st) const {} virtual unsigned get_num_levels(func_decl* pred) { throw default_exception(std::string("get_num_levels is not supported for ") + m_name); } virtual expr_ref get_cover_delta(int level, func_decl* pred) { throw default_exception(std::string("operation is not supported for ") + m_name); } virtual void add_cover(int level, func_decl* pred, expr* property) { throw default_exception(std::string("operation is not supported for ") + m_name); } virtual void display_certificate(std::ostream& out) const { throw default_exception(std::string("certificates are not supported for ") + m_name); } virtual model_ref get_model() { return model_ref(alloc(model, m)); } virtual proof_ref get_proof() { return proof_ref(m.mk_asserted(m.mk_true()), m); } virtual void updt_params() {} virtual void cancel() {} virtual void cleanup() {} }; class context; class register_engine_base { public: virtual engine_base* mk_engine(DL_ENGINE engine_type) = 0; virtual void set_context(context* ctx) = 0; }; } #endif z3-4.4.0/src/muz/base/dl_util.cpp0000644000175000017500000005034312540347414016326 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_util.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-05-20. Revision History: --*/ #include #include #include #ifdef _WINDOWS #include #endif #include"ast_pp.h" #include"bool_rewriter.h" #include"for_each_expr.h" #include"scoped_proof.h" #include"dl_context.h" #include"dl_rule.h" #include"dl_util.h" #include"stopwatch.h" namespace datalog { verbose_action::verbose_action(char const* msg, unsigned lvl): m_lvl(lvl), m_sw(0) { IF_VERBOSE(m_lvl, (verbose_stream() << msg << "...").flush(); m_sw = alloc(stopwatch); m_sw->start();); } verbose_action::~verbose_action() { double sec = 0.0; if (m_sw) m_sw->stop(); sec = m_sw?m_sw->get_seconds():0.0; if (sec < 0.001) sec = 0.0; IF_VERBOSE(m_lvl, (verbose_stream() << sec << "s\n").flush(); ); dealloc(m_sw); } bool contains_var(expr * trm, unsigned var_idx) { expr_free_vars fv; fv(trm); return fv.contains(var_idx); } unsigned count_variable_arguments(app * pred) { SASSERT(is_uninterp(pred)); unsigned res = 0; unsigned n = pred->get_num_args(); for (unsigned i = 0; i < n; i++) { expr * arg = pred->get_arg(i); if (is_var(arg)) { res++; } } return res; } void mk_new_rule_tail(ast_manager & m, app * pred, var_idx_set const & non_local_vars, unsigned & next_idx, varidx2var_map & varidx2var, sort_ref_buffer & new_rule_domain, expr_ref_buffer & new_rule_args, app_ref & new_pred) { expr_ref_buffer new_args(m); unsigned n = pred->get_num_args(); for (unsigned i = 0; i < n; i++) { expr * arg = pred->get_arg(i); if (m.is_value(arg)) { new_args.push_back(arg); } else { SASSERT(is_var(arg)); int vidx = to_var(arg)->get_idx(); var * new_var = 0; if (!varidx2var.find(vidx, new_var)) { new_var = m.mk_var(next_idx, to_var(arg)->get_sort()); next_idx++; varidx2var.insert(vidx, new_var); if (non_local_vars.contains(vidx)) { // other predicates used this variable... so it should be in the domain of the filter new_rule_domain.push_back(to_var(arg)->get_sort()); new_rule_args.push_back(new_var); } } SASSERT(new_var != 0); new_args.push_back(new_var); } } new_pred = m.mk_app(pred->get_decl(), new_args.size(), new_args.c_ptr()); } void apply_subst(expr_ref_vector& tgt, expr_ref_vector const& sub) { ast_manager& m = tgt.get_manager(); var_subst vs(m, false); expr_ref tmp(m); for (unsigned i = 0; i < tgt.size(); ++i) { if (tgt[i].get()) { vs(tgt[i].get(), sub.size(), sub.c_ptr(), tmp); tgt[i] = tmp; } else { tgt[i] = sub[i]; } } for (unsigned i = tgt.size(); i < sub.size(); ++i) { tgt.push_back(sub[i]); } } void output_predicate(context & ctx, app * f, std::ostream & out) { func_decl * pred_decl = f->get_decl(); unsigned arity = f->get_num_args(); out << pred_decl->get_name() << '('; for (unsigned i = 0; i < arity; i++) { expr * arg = f->get_arg(i); if (i != 0) { out << ','; } if (is_var(arg)) { out << "#" << to_var(arg)->get_idx(); } else { out << mk_pp(arg, ctx.get_manager()); } } out << ")"; } void display_predicate(context & ctx, app * f, std::ostream & out) { output_predicate(ctx, f, out); out << "\n"; } void display_fact(context & ctx, app * f, std::ostream & out) { func_decl * pred_decl = f->get_decl(); unsigned arity = f->get_num_args(); out << "\t("; for(unsigned i = 0; i < arity; i++) { if (i != 0) { out << ','; } expr * arg = f->get_arg(i); uint64 sym_num; SASSERT(is_app(arg)); VERIFY( ctx.get_decl_util().is_numeral_ext(to_app(arg), sym_num) ); relation_sort sort = pred_decl->get_domain(i); out << ctx.get_argument_name(pred_decl, i) << '='; ctx.print_constant_name(sort, sym_num, out); out << '(' << sym_num << ')'; } out << ")\n"; } void idx_set_union(idx_set & tgt, const idx_set & src) { idx_set::iterator vit = src.begin(); idx_set::iterator vend = src.end(); for(;vit!=vend;++vit) { tgt.insert(*vit); } } bool variable_intersection::values_match(const expr * v1, const expr * v2) { //return !m_manager.are_distinct(v1, v2); return v1==v2; } bool variable_intersection::args_match(const app * f1, const app * f2) { unsigned n=size(); for (unsigned i = 0; i < n; i++) { unsigned f1_index, f2_index; get(i, f1_index, f2_index); if (!values_match(f1->get_arg(f1_index),f2->get_arg(f2_index))) { return false; } } return true; } bool variable_intersection::args_self_match(const app * f) { if(!args_match(f,f)) { return false; } unsigned n = m_const_indexes.size(); for(unsigned i=0; iget_arg(f_index), m_consts[i].get())) { return false; } } return true; } void variable_intersection::populate_self(const app * a) { SASSERT(is_uninterp(a)); //TODO: optimize quadratic complexity //TODO: optimize number of checks when variable occurs multiple times unsigned arity = a->get_num_args(); for(unsigned i1=0; i1get_arg(i1); if(is_var(e1)) { var* v1=to_var(e1); for(unsigned i2=i1+1; i2get_arg(i2); if(!is_var(e2)) { continue; } var* v2=to_var(e2); if(v1->get_idx()==v2->get_idx()) { add_pair(i1, i2); } } } else { SASSERT(is_app(e1)); app * c1 = to_app(e1); SASSERT(c1->get_num_args()==0); //c1 must be a constant m_const_indexes.push_back(i1); m_consts.push_back(c1); SASSERT(m_const_indexes.size()==m_consts.size()); } } } void rule_counter::count_rule_vars(const rule * r, int coef) { reset(); count_vars(r->get_head(), 1); unsigned n = r->get_tail_size(); for (unsigned i = 0; i < n; i++) { count_vars(r->get_tail(i), coef); } } unsigned rule_counter::get_max_rule_var(const rule & r) { m_todo.push_back(r.get_head()); m_scopes.push_back(0); unsigned n = r.get_tail_size(); bool has_var = false; for (unsigned i = 0; i < n; i++) { m_todo.push_back(r.get_tail(i)); m_scopes.push_back(0); } return get_max_var(has_var); } void del_rule(horn_subsume_model_converter* mc, rule& r) { if (mc) { ast_manager& m = mc->get_manager(); expr_ref_vector body(m); for (unsigned i = 0; i < r.get_tail_size(); ++i) { if (r.is_neg_tail(i)) { body.push_back(m.mk_not(r.get_tail(i))); } else { body.push_back(r.get_tail(i)); } } TRACE("dl_dr", tout << mk_pp(r.get_head(), m) << " :- \n"; for (unsigned i = 0; i < body.size(); ++i) { tout << mk_pp(body[i].get(), m) << "\n"; }); mc->insert(r.get_head(), body.size(), body.c_ptr()); } } void resolve_rule(rule_manager& rm, replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx, expr_ref_vector const& s1, expr_ref_vector const& s2, rule const& res) { if (!pc) return; ast_manager& m = s1.get_manager(); expr_ref fml1(m), fml2(m), fml3(m); rm.to_formula(r1, fml1); rm.to_formula(r2, fml2); rm.to_formula(res, fml3); vector substs; svector > positions; substs.push_back(s1); substs.push_back(s2); scoped_proof _sc(m); proof_ref pr(m); proof_ref_vector premises(m); premises.push_back(m.mk_asserted(fml1)); premises.push_back(m.mk_asserted(fml2)); positions.push_back(std::make_pair(idx+1, 0)); TRACE("dl", tout << premises[0]->get_id() << " " << mk_pp(premises[0].get(), m) << "\n"; for (unsigned i = 0; i < s1.size(); ++i) { tout << mk_pp(s1[i], m) << " "; } tout << "\n"; tout << premises[1]->get_id() << " " << mk_pp(premises[1].get(), m) << "\n"; for (unsigned i = 0; i < s2.size(); ++i) { tout << mk_pp(s2[i], m) << " "; } tout << "\n"; ); pr = m.mk_hyper_resolve(2, premises.c_ptr(), fml3, positions, substs); pc->insert(pr); } void resolve_rule(rule_manager& rm, rule const& r1, rule const& r2, unsigned idx, expr_ref_vector const& s1, expr_ref_vector const& s2, rule& res) { if (!r1.get_proof()) { return; } SASSERT(r2.get_proof()); ast_manager& m = s1.get_manager(); expr_ref fml(m); rm.to_formula(res, fml); vector substs; svector > positions; substs.push_back(s1); substs.push_back(s2); scoped_proof _sc(m); proof_ref pr(m); proof_ref_vector premises(m); premises.push_back(r1.get_proof()); premises.push_back(r2.get_proof()); positions.push_back(std::make_pair(idx+1, 0)); TRACE("dl", tout << premises[0]->get_id() << " " << mk_pp(premises[0].get(), m) << "\n"; for (unsigned i = 0; i < s1.size(); ++i) { tout << mk_pp(s1[i], m) << " "; } tout << "\n"; tout << premises[1]->get_id() << " " << mk_pp(premises[1].get(), m) << "\n"; for (unsigned i = 0; i < s2.size(); ++i) { tout << mk_pp(s2[i], m) << " "; } tout << "\n"; ); pr = m.mk_hyper_resolve(2, premises.c_ptr(), fml, positions, substs); res.set_proof(m, pr); } class skip_model_converter : public model_converter { public: skip_model_converter() {} virtual model_converter * translate(ast_translation & translator) { return alloc(skip_model_converter); } }; model_converter* mk_skip_model_converter() { return alloc(skip_model_converter); } class skip_proof_converter : public proof_converter { virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) { SASSERT(num_source == 1); result = source[0]; } virtual proof_converter * translate(ast_translation & translator) { return alloc(skip_proof_converter); } }; proof_converter* mk_skip_proof_converter() { return alloc(skip_proof_converter); } void reverse_renaming(ast_manager & m, const expr_ref_vector & src, expr_ref_vector & tgt) { SASSERT(tgt.empty()); unsigned src_sz = src.size(); unsigned src_ofs = src_sz-1; unsigned max_var_idx = 0; for(unsigned i=0; iget_idx(); if(var_idx>max_var_idx) { max_var_idx=var_idx; } } unsigned tgt_sz = max_var_idx+1; unsigned tgt_ofs = tgt_sz-1; tgt.resize(tgt_sz, 0); for(unsigned i=0; iget_idx(); tgt[tgt_ofs-var_idx] = m.mk_var(i, v->get_sort()); } } void print_renaming(const expr_ref_vector & cont, std::ostream & out) { unsigned len = cont.size(); out << "("; for(int i=len-1; i>=0; i--) { out << (len-1-i) <<"->"; if(cont.get(i)==0) { out << "{none}"; } else { out << to_var(cont.get(i))->get_idx(); } if(i!=0) { out << ","; } } out << ")\n"; } void cycle_from_permutation(unsigned_vector & permutation, unsigned_vector & cycle) { try_remove_cycle_from_permutation(permutation, cycle); DEBUG_CODE( //here we assert that there is at most one cycle in the permutation unsigned_vector aux; SASSERT(!try_remove_cycle_from_permutation(permutation, aux)); ); } bool try_remove_cycle_from_permutation(unsigned_vector & permutation, unsigned_vector & cycle) { SASSERT(cycle.empty()); DEBUG_CODE( counter ctr; ctr.count(permutation); SASSERT(permutation.empty() || ctr.get_max_positive()==permutation.size()-1); SASSERT(permutation.empty() || ctr.get_positive_count()==permutation.size()); ); unsigned sz = permutation.size(); for(unsigned i=0; i extension.size() && extension == std::string(name+len-extension.size())) { res.push_back(directory+std::string(name)); } } while(FindNextFileA(hFind, &findFileData)); FindClose(hFind); } if(traverse_subdirs) { std::string subdirPattern = directory+"*.*"; hFind = FindFirstFileA(subdirPattern.c_str(), &findFileData); if (hFind != INVALID_HANDLE_VALUE) { do { if(findFileData.cFileName[0]=='.') { continue; } get_file_names(directory+findFileData.cFileName, extension, traverse_subdirs, res); } while(FindNextFileA(hFind, &findFileData)); FindClose(hFind); } } #else NOT_IMPLEMENTED_YET(); #endif } bool file_exists(std::string name) { struct stat st; if(stat(name.c_str(),&st) == 0) { return true; } return false; } bool is_directory(std::string name) { if(!file_exists(name)) { return false; } struct stat status; stat(name.c_str(), &status); return (status.st_mode&S_IFDIR)!=0; } std::string get_file_name_without_extension(std::string name) { size_t slash_index = name.find_last_of("\\/"); size_t dot_index = name.rfind("."); size_t ofs = (slash_index==std::string::npos) ? 0 : slash_index+1; size_t count = (dot_index!=std::string::npos && dot_index>ofs) ? (dot_index-ofs) : std::string::npos; return name.substr(ofs, count); } bool string_to_uint64(const char * s, uint64 & res) { #if _WINDOWS int converted = sscanf_s(s, "%I64u", &res); #else int converted = sscanf(s, "%llu", &res); #endif if(converted==0) { return false; } SASSERT(converted==1); return true; } bool read_uint64(const char * & s, uint64 & res) { static const uint64 max_but_one_digit = ULLONG_MAX/10; static const uint64 max_but_one_digit_safe = (ULLONG_MAX-9)/10; if(*s<'0' || *s>'9') { return false; } res=*s-'0'; s++; while(*s>='0' && *s<='9') { if(res>max_but_one_digit_safe) { if(res>max_but_one_digit) { return false; //overflow } res*=10; char digit = *s-'0'; if(static_cast(ULLONG_MAX-res)-digit<0) { return false; //overflow } res+=digit; } else { res*=10; res+=*s-'0'; s++; } } return true; } std::string to_string(uint64 num) { std::stringstream stm; stm< m_names; ptr_vector m_sorts; quantifier_hoister m_qh; obj_map m_memoize_disj; obj_map m_memoize_proof; func_decl_ref_vector m_fresh_predicates; expr_ref_vector m_body; proof_ref_vector m_defs; contains_predicate_proc m_proc; expr_free_vars m_free_vars; ast_fast_mark1 m_mark1; public: imp(ast_manager & m): m(m), m_produce_proofs(false), m_cancel(false), m_todo(m), m_proofs(m), m_refs(m), m_name("P"), m_qh(m), m_fresh_predicates(m), m_body(m), m_defs(m), m_proc(*this) { } bool is_horn(expr* n) { expr* n1, *n2; while (is_forall(n)) n = to_quantifier(n)->get_expr(); if (m.is_implies(n, n1, n2) && is_predicate(n2)) { app* a1 = to_app(n1); if (m.is_and(a1)) { for (unsigned i = 0; i < a1->get_num_args(); ++i) { if (!is_predicate(a1->get_arg(i)) && contains_predicate(a1->get_arg(i))) { return false; } } } else if (!is_predicate(a1) && contains_predicate(a1)) { return false; } return true; } return false; } void operator()(expr * n, proof* p, expr_ref_vector& result, proof_ref_vector& ps) { if (is_horn(n)) { result.push_back(n); ps.push_back(p); return; } expr_ref fml(m); proof_ref pr(m); m_todo.reset(); m_proofs.reset(); m_refs.reset(); m_memoize_disj.reset(); m_memoize_proof.reset(); m_fresh_predicates.reset(); m_todo.push_back(n); m_proofs.push_back(p); m_produce_proofs = p != 0; while (!m_todo.empty() && !m_cancel) { fml = m_todo.back(); pr = m_proofs.back(); m_todo.pop_back(); m_proofs.pop_back(); mk_horn(fml, pr); if (fml) { result.push_back(fml); ps.push_back(pr); } } TRACE("hnf", tout << mk_pp(n, m) << "\n==>\n"; for (unsigned i = 0; i < result.size(); ++i) { tout << mk_pp(result[i].get(), m) << "\n"; }); } void set_cancel(bool f) { m_cancel = f; } void set_name(symbol const& n) { if (n == symbol::null) { m_name = symbol("P"); } else { m_name = n; } } func_decl_ref_vector const& get_fresh_predicates() { return m_fresh_predicates; } void reset() { m_cancel = false; m_todo.reset(); m_proofs.reset(); m_refs.reset(); m_memoize_disj.reset(); m_memoize_proof.reset(); m_fresh_predicates.reset(); } ast_manager& get_manager() { return m; } private: bool produce_proofs() const { return m_produce_proofs; } bool is_predicate(expr* p) const { return is_app(p) && is_predicate(to_app(p)->get_decl()); } bool is_predicate(func_decl* f) const { return m.is_bool(f->get_range()) && f->get_family_id() == null_family_id; } bool contains_predicate(expr* fml) { try { quick_for_each_expr(m_proc, m_mark1, fml); m_mark1.reset(); } catch (contains_predicate_proc::found) { m_mark1.reset(); return true; } return false; } void mk_horn(expr_ref& fml, proof_ref& premise) { SASSERT(!premise || fml == m.get_fact(premise)); expr* e1, *e2; expr_ref fml0(m), fml1(m), fml2(m), head(m); proof_ref p(m); fml0 = fml; m_names.reset(); m_sorts.reset(); m_body.reset(); m_defs.reset(); m_qh.pull_quantifier(true, fml0, &m_sorts, &m_names); if (premise){ fml1 = bind_variables(fml0); if (!m_sorts.empty()) { proof* p1 = m.mk_pull_quant(fml, to_quantifier(fml1)); premise = mk_modus_ponens(premise, p1); fml = fml1; } } head = fml0; while (m.is_implies(head, e1, e2)) { m_body.push_back(e1); head = e2; } qe::flatten_and(m_body); if (premise) { p = m.mk_rewrite(fml0, mk_implies(m_body, head)); } // // Case: // A \/ B -> C // => // A -> C // B -> C // if (m_body.size() == 1 && m.is_or(m_body[0].get()) && contains_predicate(m_body[0].get())) { app* _or = to_app(m_body[0].get()); unsigned sz = _or->get_num_args(); expr* const* args = _or->get_args(); for (unsigned i = 0; i < sz; ++i) { m_todo.push_back(bind_variables(m.mk_implies(args[i], head))); m_proofs.push_back(0); } if (premise) { expr_ref f1 = bind_variables(mk_implies(m_body, head)); expr* f2 = m.mk_and(sz, m_todo.c_ptr()+m_todo.size()-sz); proof_ref p2(m), p3(m); p2 = m.mk_def_axiom(m.mk_iff(f1, f2)); p3 = mk_quant_intro(fml, f1, p); p2 = mk_transitivity(p3, p2); p2 = mk_modus_ponens(premise, p2); for (unsigned i = 0; i < sz; ++i) { m_proofs[m_proofs.size()-sz+i] = m.mk_and_elim(p2, i); } } fml = 0; return; } eliminate_disjunctions(m_body, m_defs); p = mk_congruence(p, m_body, head, m_defs); eliminate_quantifier_body(m_body, m_defs); p = mk_congruence(p, m_body, head, m_defs); fml2 = mk_implies(m_body, head); fml = bind_variables(fml2); if (premise) { SASSERT(p); p = mk_quant_intro(fml1, fml, p); premise = mk_modus_ponens(premise, p); } } proof* mk_quant_intro(expr* e1, expr* e2, proof* p) { if (m_sorts.empty()) { return p; } quantifier* q1 = to_quantifier(e1); quantifier* q2 = to_quantifier(e2); if (m.is_iff(m.get_fact(p))) { return m.mk_quant_intro(q1, q2, p); } if (m.is_oeq(m.get_fact(p))) { return m.mk_oeq_quant_intro(q1, q2, p); } UNREACHABLE(); return p; } void eliminate_disjunctions(expr_ref_vector::element_ref& body, proof_ref_vector& proofs) { expr* b = body.get(); expr* e1; bool negate_args = false; bool is_disj = false; unsigned num_disj = 0; expr* const* disjs = 0; if (!contains_predicate(b)) { return; } TRACE("hnf", tout << mk_pp(b, m) << "\n";); if (m.is_or(b)) { is_disj = true; negate_args = false; num_disj = to_app(b)->get_num_args(); disjs = to_app(b)->get_args(); } if (m.is_not(b, e1) && m.is_and(e1)) { is_disj = true; negate_args = true; num_disj = to_app(e1)->get_num_args(); disjs = to_app(e1)->get_args(); } if (is_disj) { app* old_head = 0; if (m_memoize_disj.find(b, old_head)) { body = old_head; } else { app_ref head = mk_fresh_head(b); proof_ref_vector defs(m); for (unsigned i = 0; i < num_disj; ++i) { expr* e = disjs[i]; if (negate_args) { e = m.mk_not(e); } m_todo.push_back(bind_variables(m.mk_implies(e, head))); m_proofs.push_back(0); if (produce_proofs()) { defs.push_back(m.mk_def_intro(m_todo.back())); m_proofs[m_proofs.size()-1] = defs.back(); } } if (produce_proofs()) { proof* p = m.mk_apply_defs(body.get(), head, defs.size(), defs.c_ptr()); m_refs.push_back(p); m_memoize_proof.insert(b, p); } m_memoize_disj.insert(b, head); m_refs.push_back(b); m_refs.push_back(head); // update the body to be the newly introduced head relation body = head; } if (produce_proofs()) { proofs.push_back(m_memoize_proof.find(b)); } } } app_ref mk_fresh_head(expr* e) { ptr_vector sorts1; m_free_vars(e); expr_ref_vector args(m); for (unsigned i = 0; i < m_free_vars.size(); ++i) { if (m_free_vars[i]) { args.push_back(m.mk_var(i, m_free_vars[i])); sorts1.push_back(m_free_vars[i]); } } func_decl_ref f(m); f = m.mk_fresh_func_decl(m_name.str().c_str(), "", sorts1.size(), sorts1.c_ptr(), m.mk_bool_sort()); m_fresh_predicates.push_back(f); return app_ref(m.mk_app(f, args.size(), args.c_ptr()), m); } void eliminate_disjunctions(expr_ref_vector& body, proof_ref_vector& proofs) { for (unsigned i = 0; i < body.size(); ++i) { expr_ref_vector::element_ref r = body[i]; eliminate_disjunctions(r, proofs); } } void eliminate_quantifier_body(expr_ref_vector::element_ref& body, proof_ref_vector& proofs) { if (is_forall(body.get()) && contains_predicate(body.get())) { quantifier* q = to_quantifier(body.get()); expr* e = q->get_expr(); if (!is_predicate(e)) { app_ref head = mk_fresh_head(e); m_todo.push_back(bind_variables(m.mk_implies(e, head))); m_proofs.push_back(0); body = m.update_quantifier(q, head); if (produce_proofs()) { proof* def_intro = m.mk_def_intro(m_todo.back()); proof* def_proof = m.mk_apply_def(e, head, def_intro); proofs.push_back(m.mk_nnf_neg(q, body.get(), 1, &def_proof)); m_proofs[m_proofs.size()-1] = def_intro; } } } } void eliminate_quantifier_body(expr_ref_vector& body, proof_ref_vector& proofs) { for (unsigned i = 0; i < body.size(); ++i) { expr_ref_vector::element_ref r = body[i]; eliminate_quantifier_body(r, proofs); } } app_ref mk_implies(expr_ref_vector const& body, expr* head) { switch (body.size()) { case 0: return app_ref(to_app(head), m); case 1: return app_ref(m.mk_implies(body[0], head), m); default: return app_ref(m.mk_implies(m.mk_and(body.size(), body.c_ptr()), head), m); } } proof_ref mk_congruence(proof* p1, expr_ref_vector const& body, expr* head, proof_ref_vector& defs) { if (defs.empty()) { return proof_ref(p1, m); } else { SASSERT(p1); proof_ref p2(m), p3(m); app_ref fml = mk_implies(body, head); expr* fact = m.get_fact(p1); if (m.is_iff(fact)) { p1 = m.mk_iff_oeq(p1); fact = m.get_fact(p1); } VERIFY (m.is_oeq(fact) || m.is_eq(fact)); app* e2 = to_app(to_app(fact)->get_arg(1)); p2 = m.mk_oeq_congruence(e2, fml, defs.size(), defs.c_ptr()); p3 = mk_transitivity(p1, p2); defs.reset(); return proof_ref(p3, m); } } proof_ref mk_modus_ponens(proof* premise, proof* eq) { proof_ref result(m); result = m.mk_modus_ponens(premise, eq); if (m.get_fact(premise) == m.get_fact(result)) { result = premise; } return result; } proof* mk_transitivity(proof* p1, proof* p2) { if (p1) { app* f = to_app(m.get_fact(p1)); if (f->get_arg(0) == f->get_arg(1)) { return p2; } } if (p2) { app* f = to_app(m.get_fact(p2)); if (f->get_arg(0) == f->get_arg(1)) { return p1; } } return m.mk_transitivity(p1, p2); } expr_ref bind_variables(expr* e) { SASSERT(m_sorts.size() == m_names.size()); if (m_sorts.empty()) { return expr_ref(e, m); } return expr_ref(m.mk_forall(m_sorts.size(), m_sorts.c_ptr(), m_names.c_ptr(), e), m); } }; hnf::hnf(ast_manager & m) { m_imp = alloc(imp, m); } hnf::~hnf() { dealloc(m_imp); } void hnf::operator()(expr * n, proof* p, expr_ref_vector & rs, proof_ref_vector& ps) { m_imp->operator()(n, p, rs, ps); TRACE("hnf", ast_manager& m = rs.get_manager(); tout << mk_ismt2_pp(n, m) << "\nHNF result:\n"; for (unsigned i = 0; i < rs.size(); ++i) { tout << mk_pp(rs[i].get(), m) << "\n"; } ); } void hnf::set_cancel(bool f) { m_imp->set_cancel(f); } void hnf::set_name(symbol const& n) { m_imp->set_name(n); } void hnf::reset() { m_imp->reset(); } func_decl_ref_vector const& hnf::get_fresh_predicates() { return m_imp->get_fresh_predicates(); } z3-4.4.0/src/muz/base/dl_rule_transformer.cpp0000644000175000017500000001040412540347414020734 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_rule_transformer.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-20. Revision History: --*/ #include #include #include"dl_context.h" #include"dl_rule_transformer.h" #include"stopwatch.h" namespace datalog { rule_transformer::rule_transformer(context & ctx) : m_context(ctx), m_rule_manager(m_context.get_rule_manager()), m_dirty(false) { } rule_transformer::~rule_transformer() { reset(); } void rule_transformer::reset() { plugin_vector::iterator it = m_plugins.begin(); plugin_vector::iterator end = m_plugins.end(); for(; it!=end; ++it) { dealloc(*it); } m_plugins.reset(); m_dirty = false; } void rule_transformer::cancel() { plugin_vector::iterator it = m_plugins.begin(); plugin_vector::iterator end = m_plugins.end(); for(; it!=end; ++it) { (*it)->cancel(); } } struct rule_transformer::plugin_comparator { bool operator()(rule_transformer::plugin * p1, rule_transformer::plugin * p2) { return p1->get_priority() > p2->get_priority(); } }; void rule_transformer::ensure_ordered() { if (m_dirty) { std::sort(m_plugins.begin(), m_plugins.end(), plugin_comparator()); m_dirty = false; } } void rule_transformer::register_plugin(plugin * p) { m_plugins.push_back(p); p->attach(*this); m_dirty=true; } bool rule_transformer::operator()(rule_set & rules) { ensure_ordered(); bool modified = false; TRACE("dl_rule_transf", tout<<"init:\n"; rules.display(tout); ); rule_set* new_rules = alloc(rule_set, rules); plugin_vector::iterator it = m_plugins.begin(); plugin_vector::iterator end = m_plugins.end(); for(; it!=end && !m_context.canceled(); ++it) { plugin & p = **it; IF_VERBOSE(1, verbose_stream() << "(transform " << typeid(p).name() << "...";); stopwatch sw; sw.start(); rule_set * new_rules1 = p(*new_rules); sw.stop(); double sec = sw.get_seconds(); if (sec < 0.001) sec = 0.0; if (!new_rules1) { IF_VERBOSE(1, verbose_stream() << "no-op " << sec << "s)\n";); continue; } if (p.can_destratify_negation() && !new_rules1->is_closed() && !new_rules1->close()) { warning_msg("a rule transformation skipped " "because it destratified negation"); dealloc(new_rules1); IF_VERBOSE(1, verbose_stream() << "no-op " << sec << "s)\n";); continue; } modified = true; dealloc(new_rules); new_rules = new_rules1; new_rules->ensure_closed(); IF_VERBOSE(1, verbose_stream() << new_rules->get_num_rules() << " rules " << sec << "s)\n";); TRACE("dl_rule_transf", tout << typeid(p).name()<<":\n"; new_rules->display(tout); ); } if (modified) { rules.replace_rules(*new_rules); } dealloc(new_rules); return modified; } //------------------------------ // //rule_transformer::plugin // //------------------------------ void rule_transformer::plugin::remove_duplicate_tails(app_ref_vector& tail, svector& tail_neg) { //one set for positive and one for negative obj_hashtable tail_apps[2]; unsigned i=0; while(i Author: Leonardo de Moura (leonardo) 2010-05-17. Revision History: --*/ #ifndef _DL_RULE_H_ #define _DL_RULE_H_ #include"ast.h" #include"dl_costs.h" #include"dl_util.h" #include"used_vars.h" #include"proof_converter.h" #include"model_converter.h" #include"ast_counter.h" #include"rewriter.h" #include"hnf.h" #include"qe_lite.h" #include"var_subst.h" #include"datatype_decl_plugin.h" namespace datalog { class rule; class rule_manager; class rule_set; class table; class context; typedef obj_ref rule_ref; typedef ref_vector rule_ref_vector; typedef ptr_vector rule_vector; struct uninterpreted_function_finder_proc { ast_manager& m; datatype_util m_dt; dl_decl_util m_dl; bool m_found; func_decl* m_func; uninterpreted_function_finder_proc(ast_manager& m): m(m), m_dt(m), m_dl(m), m_found(false), m_func(0) {} void operator()(var * n) { } void operator()(quantifier * n) { } void operator()(app * n) { if (is_uninterp(n) && !m_dl.is_rule_sort(n->get_decl()->get_range())) { m_found = true; m_func = n->get_decl(); } else if (m_dt.is_accessor(n)) { sort* s = m.get_sort(n->get_arg(0)); SASSERT(m_dt.is_datatype(s)); if (m_dt.get_datatype_constructors(s)->size() > 1) { m_found = true; m_func = n->get_decl(); } } } void reset() { m_found = false; m_func = 0; } bool found(func_decl*& f) const { f = m_func; return m_found; } }; struct quantifier_finder_proc { bool m_exist; bool m_univ; quantifier_finder_proc() : m_exist(false), m_univ(false) {} void operator()(var * n) { } void operator()(quantifier * n) { if (n->is_forall()) { m_univ = true; } else { SASSERT(n->is_exists()); m_exist = true; } } void operator()(app * n) { } void reset() { m_exist = m_univ = false; } }; /** \brief Manager for the \c rule class \remark \c rule_manager objects are interchangable as long as they contain the same \c ast_manager object. */ class rule_manager { class remove_label_cfg : public default_rewriter_cfg { family_id m_label_fid; public: remove_label_cfg(ast_manager& m): m_label_fid(m.get_label_family_id()) {} virtual ~remove_label_cfg(); br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr); }; ast_manager& m; context& m_ctx; rule_counter m_counter; used_vars m_used; var_idx_set m_var_idx; expr_free_vars m_free_vars; app_ref_vector m_body; app_ref m_head; expr_ref_vector m_args; svector m_neg; hnf m_hnf; qe_lite m_qe; remove_label_cfg m_cfg; rewriter_tpl m_rwr; mutable uninterpreted_function_finder_proc m_ufproc; mutable quantifier_finder_proc m_qproc; mutable expr_sparse_mark m_visited; // only the context can create a rule_manager friend class context; explicit rule_manager(context& ctx); /** \brief Move functions from predicate tails into the interpreted tail by introducing new variables. */ void hoist_compound_predicates(unsigned num_bound, app_ref& head, app_ref_vector& body); void hoist_compound(unsigned& num_bound, app_ref& fml, app_ref_vector& body); void flatten_body(app_ref_vector& body); void remove_labels(expr_ref& fml, proof_ref& pr); app_ref ensure_app(expr* e); void check_app(expr* e); bool contains_predicate(expr* fml) const; void bind_variables(expr* fml, bool is_forall, expr_ref& result); void mk_negations(app_ref_vector& body, svector& is_negated); void mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name); void mk_horn_rule(expr* fml, proof* p, rule_set& rules, symbol const& name); static expr_ref mk_implies(app_ref_vector const& body, expr* head); unsigned extract_horn(expr* fml, app_ref_vector& body, app_ref& head); /** \brief Perform cheap quantifier elimination to reduce the number of variables in the interpreted tail. */ void reduce_unbound_vars(rule_ref& r); void reset_collect_vars(); var_idx_set& finalize_collect_vars(); public: ast_manager& get_manager() const { return m; } void inc_ref(rule * r); void dec_ref(rule * r); used_vars& reset_used() { m_used.reset(); return m_used; } var_idx_set& collect_vars(expr * pred); var_idx_set& collect_vars(expr * e1, expr* e2); var_idx_set& collect_rule_vars(rule * r); var_idx_set& collect_rule_vars_ex(rule * r, app* t); var_idx_set& collect_tail_vars(rule * r); void accumulate_vars(expr* pred); // ptr_vector& get_var_sorts() { return m_vars; } var_idx_set& get_var_idx() { return m_var_idx; } /** \brief Create a Datalog rule from a Horn formula. The formula is of the form (forall (...) (forall (...) (=> (and ...) head))) */ void mk_rule(expr* fml, proof* p, rule_set& rules, symbol const& name = symbol::null); /** \brief Create a Datalog query from an expression. The formula is of the form (exists (...) (exists (...) (and ...)) */ func_decl* mk_query(expr* query, rule_set& rules); /** \brief Create a Datalog rule head :- tail[0], ..., tail[n-1]. Return 0 if it is not a valid rule. \remark A tail may contain negation. tail[i] is assumed to be negated if is_neg != 0 && is_neg[i] == true */ rule * mk(app * head, unsigned n, app * const * tail, bool const * is_neg = 0, symbol const& name = symbol::null, bool normalize = true); /** \brief Create a rule with the same tail as \c source and with a specified head. */ rule * mk(rule const * source, app * new_head, symbol const& name = symbol::null); /** \brief Create a copy of the given rule. */ rule * mk(rule const * source, symbol const& name = symbol::null); /** make sure there are not non-quantified variables that occur only in interpreted predicates */ void fix_unbound_vars(rule_ref& r, bool try_quantifier_elimination); /** \brief add proof that new rule is obtained by rewriting old rule. */ void mk_rule_rewrite_proof(rule& old_rule, rule& new_rule); /** \brief tag rule as asserted. */ void mk_rule_asserted_proof(rule& r); /** \brief apply substitution to variables of rule. */ void substitute(rule_ref& r, unsigned sz, expr*const* es); /** \brief Check that head :- tail[0], ..., tail[n-1] is a valid Datalog rule. */ void check_valid_rule(app * head, unsigned n, app * const * tail) const; /** \brief Check that \c head may occur as a Datalog rule head. */ void check_valid_head(expr * head) const; /** \brief Return true if \c head may occur as a fact. */ bool is_fact(app * head) const; static bool is_forall(ast_manager& m, expr* e, quantifier*& q); rule_counter& get_counter() { return m_counter; } void to_formula(rule const& r, expr_ref& result); std::ostream& display_smt2(rule const& r, std::ostream & out); bool has_uninterpreted_non_predicates(rule const& r, func_decl*& f) const; void has_quantifiers(rule const& r, bool& existential, bool& universal) const; bool has_quantifiers(rule const& r) const; }; class rule : public accounted_object { friend class rule_manager; app * m_head; proof* m_proof; unsigned m_tail_size:20; // unsigned m_reserve:12; unsigned m_ref_cnt; unsigned m_positive_cnt; unsigned m_uninterp_cnt; symbol m_name; /** The following field is an array of tagged pointers. - Tag 0: the atom is not negated - Tag 1: the atom is negated. The order of tail formulas is the following: uninterpreted positive, uninterpreted negative, interpreted. The negated flag is never set for interpreted tails. */ app * m_tail[0]; static unsigned get_obj_size(unsigned n) { return sizeof(rule) + n * sizeof(app *); } rule() : m_ref_cnt(0) {} ~rule() {} void deallocate(ast_manager & m); void get_used_vars(used_vars& uv) const; public: proof * get_proof() const { return m_proof; } void set_proof(ast_manager& m, proof* p); app * get_head() const { return m_head; } func_decl* get_decl() const { return get_head()->get_decl(); } unsigned get_tail_size() const { return m_tail_size; } /** \brief Return number of positive uninterpreted predicates in the tail. These predicates are the first in the tail. */ unsigned get_positive_tail_size() const { return m_positive_cnt; } unsigned get_uninterpreted_tail_size() const { return m_uninterp_cnt; } /** \brief Return i-th tail atom. The first \c get_uninterpreted_tail_size() atoms are uninterpreted and the first \c get_positive_tail_size() are uninterpreted and non-negated. */ app * get_tail(unsigned i) const { SASSERT(i < m_tail_size); return UNTAG(app *, m_tail[i]); } func_decl* get_decl(unsigned i) const { SASSERT(i < get_uninterpreted_tail_size()); return get_tail(i)->get_decl(); } bool is_neg_tail(unsigned i) const { SASSERT(i < m_tail_size); return GET_TAG(m_tail[i]) == 1; } /** A predicate P(Xj) can be annotated by adding an interpreted predicate of the form ((_ min P N) ...) where N is the column number that should be used for the min aggregation function. Such an interpreted predicate is an example for which this function returns true. */ bool is_min_tail(unsigned i) const { return dl_decl_plugin::is_aggregate(get_tail(i)->get_decl()); } /** Check whether predicate p is in the interpreted tail. If only_positive is true, only the positive predicate tail atoms are checked. */ bool is_in_tail(const func_decl * p, bool only_positive=false) const; bool has_negation() const; /** \brief Store in d the (direct) dependencies of the given rule. */ void norm_vars(rule_manager & rm); void get_vars(ast_manager& m, ptr_vector& sorts) const; void display(context & ctx, std::ostream & out) const; symbol const& name() const { return m_name; } unsigned hash() const; }; struct rule_eq_proc { bool operator()(const rule * r1, const rule * r2) const; }; struct rule_hash_proc { unsigned operator()(const rule * r) const; }; }; #endif /* _DL_RULE_H_ */ z3-4.4.0/src/muz/base/dl_costs.cpp0000644000175000017500000000742012540347414016502 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_costs.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-20. Revision History: --*/ #include "debug.h" #include "stopwatch.h" #include "dl_context.h" #include "dl_rule.h" #include "dl_costs.h" namespace datalog { // ----------------------------------- // // costs // // ----------------------------------- costs::costs() : milliseconds(0), instructions(0) {} bool costs::empty() const { return !milliseconds && !instructions; } void costs::reset() { milliseconds = 0; instructions = 0; } costs costs::operator-(const costs & o) const { costs res(*this); SASSERT(milliseconds>o.milliseconds); res.milliseconds-=o.milliseconds; SASSERT(instructions>o.instructions); res.instructions-=o.instructions; return res; } void costs::operator+=(const costs & o) { milliseconds+=o.milliseconds; instructions+=o.instructions; } bool costs::passes_thresholds(context & ctx) const { return milliseconds >= ctx.dl_profile_milliseconds_threshold(); } void costs::output(std::ostream & out) const { out << "instr: " << instructions << " time: " << milliseconds << "ms"; } // ----------------------------------- // // accounted_object // // ----------------------------------- accounted_object::~accounted_object() { if(m_parent_object) { SASSERT(m_context); m_context->get_rule_manager().dec_ref(m_parent_object); } } void accounted_object::set_accounting_parent_object(context & ctx, rule * parent) { if(m_parent_object) { SASSERT(m_context); SASSERT(m_context==&ctx); m_context->get_rule_manager().dec_ref(m_parent_object); } m_context = &ctx; m_parent_object = parent; m_context->get_rule_manager().inc_ref(m_parent_object); } void accounted_object::process_costs() { costs delta = get_current_costs(); if(delta.empty()) { return; } get_current_costs().reset(); accounted_object * obj = this; do { obj->m_processed_cost+=delta; obj=obj->m_parent_object; } while(obj); } void accounted_object::get_total_cost(costs & result) const { result.reset(); result+=m_current_cost; result+=m_processed_cost; } bool accounted_object::passes_output_thresholds(context & ctx) const { costs c; get_total_cost(c); return c.passes_thresholds(ctx); } void accounted_object::output_profile(std::ostream & out) const { costs c; get_total_cost(c); c.output(out); } // ----------------------------------- // // cost_recorder // // ----------------------------------- cost_recorder::cost_recorder() : m_obj(0) { m_stopwatch = alloc(stopwatch); m_stopwatch->start(); } cost_recorder::~cost_recorder() { if(m_obj) { finish(); } dealloc(m_stopwatch); } void cost_recorder::start(accounted_object * obj) { uint64 curr_time = static_cast(m_stopwatch->get_current_seconds()*1000); if(m_obj) { costs::time_type time_delta = static_cast(curr_time-m_last_time); costs & c = m_obj->get_current_costs(); c.instructions++; c.milliseconds+=time_delta; m_obj->m_being_recorded = false; } m_running = obj!=0; m_obj = obj; m_last_time = curr_time; if(obj) { m_obj->m_being_recorded = true; } } }; z3-4.4.0/src/muz/base/rule_properties.h0000644000175000017500000000320112540347414017551 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: rule_properties.h Abstract: Collect properties of rules. Author: Nikolaj Bjorner (nbjorner) 9-25-2014 Notes: --*/ #ifndef _RULE_PROPERTIES_H_ #define _RULE_PROPERTIES_H_ #include"ast.h" #include"datatype_decl_plugin.h" #include"bv_decl_plugin.h" #include"dl_rule.h" namespace datalog { class rule_properties { ast_manager& m; rule_manager& rm; context& m_ctx; i_expr_pred& m_is_predicate; datatype_util m_dt; dl_decl_util m_dl; bv_util m_bv; bool m_generate_proof; rule* m_rule; obj_map m_quantifiers; obj_map m_uninterp_funs; ptr_vector m_interp_pred; ptr_vector m_negative_rules; ptr_vector m_inf_sort; void insert(ptr_vector& rules, rule* r); public: rule_properties(ast_manager & m, rule_manager& rm, context& ctx, i_expr_pred& is_predicate); ~rule_properties(); void set_generate_proof(bool generate_proof) { m_generate_proof = generate_proof; } void collect(rule_set const& r); void check_quantifier_free(); void check_uninterpreted_free(); void check_existential_tail(); void check_for_negated_predicates(); void check_nested_free(); void check_infinite_sorts(); void operator()(var* n); void operator()(quantifier* n); void operator()(app* n); void reset(); }; } #endif /* _RULE_PROPERTIES_H_ */ z3-4.4.0/src/muz/base/dl_util.h0000644000175000017500000005562512540347414016003 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_util.h Abstract: Datalog utility function and structures. Author: Leonardo de Moura (leonardo) 2010-05-20. Revision History: --*/ #ifndef _DL_UTIL_H_ #define _DL_UTIL_H_ #include"ast.h" #include"hashtable.h" #include"obj_hashtable.h" #include"uint_set.h" #include"horn_subsume_model_converter.h" #include"replace_proof_converter.h" #include"substitution.h" #include"ast_counter.h" #include"statistics.h" #include"lbool.h" #include"qe_util.h" namespace datalog { class context; class rule; class relation_base; class relation_manager; class table_base; class pentagon_relation; class relation_fact; class relation_signature; class rule_manager; class verbose_action { unsigned m_lvl; class stopwatch* m_sw; public: verbose_action(char const* msg, unsigned lvl = 11); ~verbose_action(); }; enum PDR_CACHE_MODE { NO_CACHE, HASH_CACHE, CONSTRAINT_CACHE, LAST_CACHE_MODE }; struct std_string_hash_proc { unsigned operator()(const std::string & s) const { return string_hash(s.c_str(), static_cast(s.length()), 17); } }; // typedef int_hashtable > idx_set; typedef uint_set idx_set; typedef idx_set var_idx_set; typedef u_map varidx2var_map; typedef obj_hashtable func_decl_set; //!< Rule dependencies. typedef vector string_vector; bool contains_var(expr * trm, unsigned var_idx); /** \brief Return number of arguments of \c pred that are variables */ unsigned count_variable_arguments(app * pred); template void copy_nonvariables(app * src, T& tgt) { unsigned n = src->get_num_args(); for (unsigned i = 0; i < n; i++) { expr * arg = src->get_arg(i); if (!is_var(arg)) { tgt[i]=arg; } } } /** \brief Auxiliary function used to create a tail based on \c pred for a new rule. The variables in \c pred are re-assigned using \c next_idx and \c varidx2var. A variable is considered non-local to the rule if it is in the set \c non_local_vars. Non-local variables are coppied to new_rule_args, and their sorts to \c new_rule_domain. The new predicate is stored in \c new_pred. */ void mk_new_rule_tail(ast_manager & m, app * pred, var_idx_set const & non_local_vars, unsigned & next_idx, varidx2var_map & varidx2var, sort_ref_buffer & new_rule_domain, expr_ref_buffer & new_rule_args, app_ref & new_pred); /** \brief Simpler version of the previous function. Initializes next_idx with 0, and an empty varid2var */ inline void mk_new_rule_tail(ast_manager & m, app * pred, var_idx_set const & non_local_vars, sort_ref_buffer & new_rule_domain, expr_ref_buffer & new_rule_args, app_ref & new_pred) { unsigned next_idx = 0; varidx2var_map varidx2var; mk_new_rule_tail(m, pred, non_local_vars, next_idx, varidx2var, new_rule_domain, new_rule_args, new_pred); } /** \brief Print a predicate \c f to the stream \c out. */ void display_predicate(context & ctx, app * f, std::ostream & out); /** \brief Like \c display_predicate, just without the final '\n' character. */ void output_predicate(context & ctx, app * f, std::ostream & out); /** \brief Print a fact \c f to the stream \c out in a format conforming to Bddbddb. */ void display_fact(context & ctx, app * f, std::ostream & out); class variable_intersection { bool values_match(const expr * v1, const expr * v2); unsigned_vector m_args1; unsigned_vector m_args2; unsigned_vector m_const_indexes; app_ref_vector m_consts; static unsigned expr_cont_get_size(app * a) { return a->get_num_args(); } static expr * expr_cont_get(app * a, unsigned i) { return a->get_arg(i); } static unsigned expr_cont_get_size(const ptr_vector & v) { return v.size(); } static unsigned expr_cont_get_size(const expr_ref_vector & v) { return v.size(); } static expr * expr_cont_get(const ptr_vector & v, unsigned i) { return v[i]; } static expr * expr_cont_get(const expr_ref_vector & v, unsigned i) { return v[i]; } public: variable_intersection(ast_manager & m) : m_consts(m) {} unsigned size() const { return m_args1.size(); } const unsigned * get_cols1() const { return m_args1.c_ptr(); } const unsigned * get_cols2() const { return m_args2.c_ptr(); } bool empty() const { return size()==0; } void get(unsigned i, unsigned & index1, unsigned & index2) const { index1=m_args1[i]; index2=m_args2[i]; } void reset() { m_args1.reset(); m_args2.reset(); m_const_indexes.reset(); m_consts.reset(); } bool args_match(const app * f1, const app * f2); bool args_self_match(const app * f); /** \brief Fill arguments of \c f1 into corresponding positions in \c tgt using its \c operator[]. */ template void fill_into_second(const app * f1, T & tgt) const { unsigned n=size(); for(unsigned i=0; iget_arg(f1_index); } } void add_pair(unsigned idx1, unsigned idx2) { m_args1.push_back(idx1); m_args2.push_back(idx2); } /** Find pairs of indexes of arguments of \c a1 and \c a2 that correspond to the same variable. Here we do not detect the constant arguments in \c a1 and \c a2. */ template void populate(const T1 & a1, const T2 & a2) { //TODO: optimize quadratic complexity //TODO: optimize number of checks when variable occurs multiple times unsigned a1num = expr_cont_get_size(a1); unsigned a2num = expr_cont_get_size(a2); for(unsigned i1=0; i1get_idx()==v2->get_idx()) { add_pair(i1, i2); } } } } /** Find pairs of indexes of arguments of \c a that correspond to the same variable and indexes that correspond to a constant. */ void populate_self(const app * a); }; template void project_out_vector_columns(T & container, unsigned removed_col_cnt, const unsigned * removed_cols) { if(removed_col_cnt==0) { return; } unsigned n = container.size(); unsigned ofs = 1; unsigned r_i = 1; for(unsigned i=removed_cols[0]+1; i void project_out_vector_columns(ref_vector & container, unsigned removed_col_cnt, const unsigned * removed_cols) { if(removed_col_cnt==0) { return; } unsigned n = container.size(); unsigned ofs = 1; int r_i = 1; for(unsigned i=removed_cols[0]+1; i void project_out_vector_columns(T & container, const unsigned_vector removed_cols) { project_out_vector_columns(container, removed_cols.size(), removed_cols.c_ptr()); } /** \brief Take a single cycle permutation and store it in the form of a cycle. The function modifies the \c permutation vector */ void cycle_from_permutation(unsigned_vector & permutation, unsigned_vector & cycle); /** \brief If \c permutation is an identity, return false. Otherwise remove one cycle from the permutation, store it in the form of a cycle in \c cycle and return true. Using this function one can retrieve all cycles in a permutation. \c cycle must be empty before calling the function. */ bool try_remove_cycle_from_permutation(unsigned_vector & permutation, unsigned_vector & cycle); void collect_sub_permutation(const unsigned_vector & permutation, const unsigned_vector & translation, unsigned_vector & res, bool & identity); template void permutate_by_cycle(T & container, unsigned cycle_len, const unsigned * permutation_cycle) { if(cycle_len<2) { return; } typename T::data aux = container[permutation_cycle[0]]; for(unsigned i=1; i void permutate_by_cycle(ref_vector & container, unsigned cycle_len, const unsigned * permutation_cycle) { if(cycle_len<2) { return; } T * aux = container.get(permutation_cycle[0]); for(unsigned i=1; i void permutate_by_cycle(T & container, const unsigned_vector permutation_cycle) { permutate_by_cycle(container, permutation_cycle.size(), permutation_cycle.c_ptr()); } class rule_counter : public var_counter { public: rule_counter(){} void count_rule_vars(const rule * r, int coef = 1); unsigned get_max_rule_var(const rule& r); }; void del_rule(horn_subsume_model_converter* mc, rule& r); void resolve_rule(rule_manager& rm, replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx, expr_ref_vector const& s1, expr_ref_vector const& s2, rule const& res); void resolve_rule(rule_manager& rm, rule const& r1, rule const& r2, unsigned idx, expr_ref_vector const& s1, expr_ref_vector const& s2, rule& res); model_converter* mk_skip_model_converter(); proof_converter* mk_skip_proof_converter(); void reverse_renaming(ast_manager & m, const expr_ref_vector & src, expr_ref_vector & tgt); void print_renaming(const expr_ref_vector & cont, std::ostream & out); /** \brief Update tgt with effect of applying substitution from 'sub' to it. tgt is extended by variables that are substituted by 'sub'. We use the convention that the entry at index 'i' corresponds to variable with de-Bruijn index 'i'. */ void apply_subst(expr_ref_vector& tgt, expr_ref_vector const& sub); // ----------------------------------- // // container functions // // ----------------------------------- template void set_intersection(Set1 & tgt, const Set2 & src) { svector to_remove; typename Set1::iterator vit = tgt.begin(); typename Set1::iterator vend = tgt.end(); for(;vit!=vend;++vit) { typename Set1::data itm=*vit; if(!src.contains(itm)) { to_remove.push_back(itm); } } while(!to_remove.empty()) { tgt.remove(to_remove.back()); to_remove.pop_back(); } } template void set_difference(Set & tgt, const Set & to_remove) { typename Set::iterator vit = to_remove.begin(); typename Set::iterator vend = to_remove.end(); for(;vit!=vend;++vit) { typename Set::data itm=*vit; tgt.remove(itm); } } template void set_union(Set1 & tgt, const Set2 & to_add) { typename Set2::iterator vit = to_add.begin(); typename Set2::iterator vend = to_add.end(); for(;vit!=vend;++vit) { typename Set1::data itm=*vit; tgt.insert(itm); } } void idx_set_union(idx_set & tgt, const idx_set & src); template void unite_disjoint_maps(T & tgt, const T & src) { typename T::iterator it = src.begin(); typename T::iterator end = src.end(); for(; it!=end; ++it) { SASSERT(!tgt.contains(it->m_key)); tgt.insert(it->m_key, it->m_value); } } template void collect_map_range(T & acc, const U & map) { typename U::iterator it = map.begin(); typename U::iterator end = map.end(); for(; it!=end; ++it) { acc.push_back(it->m_value); } } template void print_container(const T & begin, const T & end, std::ostream & out) { T it = begin; out << "("; bool first = true; for(; it!=end; ++it) { if(first) { first = false; } else { out << ","; } out << (*it); } out << ")"; } template void print_container(const T & cont, std::ostream & out) { print_container(cont.begin(), cont.end(), out); } template void print_container(const ref_vector & cont, std::ostream & out) { print_container(cont.c_ptr(), cont.c_ptr() + cont.size(), out); } template void print_map(const T & cont, std::ostream & out) { typename T::iterator it = cont.begin(); typename T::iterator end = cont.end(); out << "("; bool first = true; for(; it!=end; ++it) { if(first) { first = false; } else { out << ","; } out << it->m_key << "->" << it->m_value; } out << ")"; } template unsigned find_index(const It & begin, const It & end, const V & val) { unsigned idx = 0; It it = begin; for(; it!=end; it++, idx++) { if(*it==val) { return idx; } } return UINT_MAX; } template bool containers_equal(const T & begin1, const T & end1, const U & begin2, const U & end2) { T it1 = begin1; U it2 = begin2; for(; it1!=end1 && it2!=end2; ++it1, ++it2) { if(*it1!=*it2) { return false; } } return it1==end1 && it2==end2; } template bool vectors_equal(const T & c1, const U & c2) { if(c1.size()!=c2.size()) { return false; } typename T::data * it1 = c1.c_ptr(); typename T::data * end1 = c1.c_ptr()+c1.size(); typename U::data * it2 = c2.c_ptr(); for(; it1!=end1; ++it1, ++it2) { if(*it1!=*it2) { return false; } } return true; } template struct default_obj_chash { unsigned operator()(T const& cont, unsigned i) const { return cont[i]->hash(); } }; template unsigned obj_vector_hash(const T & cont) { return get_composite_hash(cont, cont.size(),default_kind_hash_proc(), default_obj_chash()); } template struct obj_vector_hash_proc { unsigned operator()(const T & cont) const { return obj_vector_hash(cont); } }; template struct svector_hash_proc { unsigned operator()(const svector & cont) const { return svector_hash()(cont); } }; template struct vector_eq_proc { bool operator()(const T & c1, const T & c2) const { return vectors_equal(c1, c2); } }; template void dealloc_ptr_vector_content(ptr_vector & v) { typename ptr_vector::iterator it = v.begin(); typename ptr_vector::iterator end = v.end(); for(; it!=end; ++it) { dealloc(*it); } } /** \brief Add elements from an iterable object \c src into the vector \c vector. */ template void push_into_vector(VectType & vector, const U & src) { typename U::iterator it = src.begin(); typename U::iterator end = src.end(); for(; it!=end; ++it) { vector.push_back(*it); } } template void push_into_vector(VectType & vector, const ref_vector & src) { U * const * it = src.begin(); U * const * end = src.end(); for(; it!=end; ++it) { vector.push_back(*it); } } template void insert_into_set(SetType & tgt, const U & src) { typename U::const_iterator it = src.begin(); typename U::const_iterator end = src.end(); for(; it!=end; ++it) { tgt.insert(*it); } } /** \brief Remove the first occurence of \c el from \c v and return \c true. If \c el is not present in \c v, return \c false. The order of elements in \c v is not preserved. */ template bool remove_from_vector(T & v, const typename T::data & el) { unsigned sz = v.size(); for(unsigned i=0; i */ template void reset_dealloc_values(map & m) { typename map::iterator it = m.begin(); typename map::iterator end = m.end(); for (; it != end; ++it) { dealloc(it->m_value); } m.reset(); } template struct aux__index_comparator { T* m_keys; aux__index_comparator(T* keys) : m_keys(keys) {} bool operator()(unsigned a, unsigned b) { return m_keys[a] void sort_two_arrays(unsigned len, T* keys, U* vals) { if(len<2) { return; } if(len==2) { if(keys[0]>keys[1]) { std::swap(keys[0], keys[1]); std::swap(vals[0], vals[1]); } return; } unsigned_vector numbers; for(unsigned i=0; i cmp(keys); std::sort(numbers.begin(), numbers.end(), cmp); for(unsigned i=0; i void add_sequence_without_set(unsigned start, unsigned count, const Container & complement, unsigned_vector & v) { unsigned after_last = start+count; for(unsigned i=start; i void universal_delete(T* ptr) { dealloc(ptr); } /** \brief If it is possible to convert the beginning of \c s to uint64, store the result of conversion and return true; otherwise return false. */ bool string_to_uint64(const char * s, uint64 & res); std::string to_string(uint64 num); /** \brief Read the sequence of decimal digits starting at \c s and interpret it as uint64. If successful, \c res will contain the read number and \c s will point to the first non-digit character, and true is returned. If the first character is not a digit, no parameter is modified and false is returned. If the uint64 overflows, \c points to the character which caused the overflow and false is returned. */ bool read_uint64(const char * & s, uint64 & res); }; #endif /* _DL_UTIL_H_ */ z3-4.4.0/src/muz/base/rule_properties.cpp0000644000175000017500000001355112540347414020115 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: rule_properties.cpp Abstract: Collect properties of rules. Author: Nikolaj Bjorner (nbjorner) 9-25-2014 Notes: --*/ #include"expr_functors.h" #include"rule_properties.h" #include"dl_rule_set.h" #include"for_each_expr.h" #include"dl_context.h" using namespace datalog; rule_properties::rule_properties(ast_manager & m, rule_manager& rm, context& ctx, i_expr_pred& p): m(m), rm(rm), m_ctx(ctx), m_is_predicate(p), m_dt(m), m_dl(m), m_bv(m), m_generate_proof(false) {} rule_properties::~rule_properties() {} void rule_properties::collect(rule_set const& rules) { reset(); rule_set::iterator it = rules.begin(), end = rules.end(); expr_sparse_mark visited; for (; it != end; ++it) { rule* r = *it; m_rule = r; unsigned ut_size = r->get_uninterpreted_tail_size(); unsigned t_size = r->get_tail_size(); if (r->has_negation()) { m_negative_rules.push_back(r); } for (unsigned i = ut_size; i < t_size; ++i) { for_each_expr_core(*this, visited, r->get_tail(i)); } if (m_generate_proof && !r->get_proof()) { rm.mk_rule_asserted_proof(*r); } for (unsigned i = 0; m_inf_sort.empty() && i < r->get_decl()->get_arity(); ++i) { sort* d = r->get_decl()->get_domain(i); if (!m.is_bool(d) && !m_dl.is_finite_sort(d) && !m_bv.is_bv_sort(d)) { m_inf_sort.push_back(m_rule); } } } } void rule_properties::check_quantifier_free() { if (!m_quantifiers.empty()) { rule* r = m_quantifiers.begin()->m_value; std::stringstream stm; stm << "cannot process quantifier in rule "; r->display(m_ctx, stm); throw default_exception(stm.str()); } } void rule_properties::check_for_negated_predicates() { if (!m_negative_rules.empty()) { rule* r = m_negative_rules[0]; std::stringstream stm; stm << "Rule contains negative predicate "; r->display(m_ctx, stm); throw default_exception(stm.str()); } } void rule_properties::check_uninterpreted_free() { if (!m_uninterp_funs.empty()) { func_decl* f = m_uninterp_funs.begin()->m_key; rule* r = m_uninterp_funs.begin()->m_value; std::stringstream stm; stm << "Uninterpreted '" << f->get_name() << "' in "; r->display(m_ctx, stm); throw default_exception(stm.str()); } } void rule_properties::check_infinite_sorts() { if (!m_inf_sort.empty()) { std::stringstream stm; rule* r = m_inf_sort.back(); stm << "Rule contains infinite sorts in rule "; r->display(m_ctx, stm); throw default_exception(stm.str()); } } void rule_properties::check_nested_free() { if (!m_interp_pred.empty()) { std::stringstream stm; rule* r = m_interp_pred[0]; stm << "Rule contains nested predicates "; r->display(m_ctx, stm); throw default_exception(stm.str()); } } void rule_properties::check_existential_tail() { ast_mark visited; ptr_vector todo, tocheck; for (unsigned i = 0; i < m_interp_pred.size(); ++i) { rule& r = *m_interp_pred[i]; unsigned ut_size = r.get_uninterpreted_tail_size(); unsigned t_size = r.get_tail_size(); for (unsigned i = ut_size; i < t_size; ++i) { todo.push_back(r.get_tail(i)); } } context::contains_pred contains_p(m_ctx); check_pred check_pred(contains_p, m); while (!todo.empty()) { expr* e = todo.back(), *e1, *e2; todo.pop_back(); if (visited.is_marked(e)) { continue; } visited.mark(e, true); if (m_is_predicate(e)) { } else if (m.is_and(e) || m.is_or(e)) { todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); } else if (m.is_implies(e, e1, e2)) { tocheck.push_back(e1); todo.push_back(e2); } else if (is_quantifier(e)) { tocheck.push_back(to_quantifier(e)->get_expr()); } else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) && m.is_true(e1)) { todo.push_back(e2); } else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) && m.is_true(e2)) { todo.push_back(e1); } else { tocheck.push_back(e); } } for (unsigned i = 0; i < tocheck.size(); ++i) { expr* e = tocheck[i]; if (check_pred(e)) { std::ostringstream out; out << "recursive predicate " << mk_ismt2_pp(e, m) << " occurs nested in the body of a rule"; throw default_exception(out.str()); } } } void rule_properties::insert(ptr_vector& rules, rule* r) { if (rules.empty() || rules.back() != r) { rules.push_back(r); } } void rule_properties::operator()(var* n) { } void rule_properties::operator()(quantifier* n) { m_quantifiers.insert(n, m_rule); } void rule_properties::operator()(app* n) { if (m_is_predicate(n)) { insert(m_interp_pred, m_rule); } else if (is_uninterp(n) && !m_dl.is_rule_sort(n->get_decl()->get_range())) { m_uninterp_funs.insert(n->get_decl(), m_rule); } else if (m_dt.is_accessor(n)) { sort* s = m.get_sort(n->get_arg(0)); SASSERT(m_dt.is_datatype(s)); if (m_dt.get_datatype_constructors(s)->size() > 1) { m_uninterp_funs.insert(n->get_decl(), m_rule); } } else { } } void rule_properties::reset() { m_quantifiers.reset(); m_uninterp_funs.reset(); m_interp_pred.reset(); m_negative_rules.reset(); m_inf_sort.reset(); } z3-4.4.0/src/muz/base/bind_variables.h0000644000175000017500000000173212540347414017301 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: bind_variables.h Abstract: Utility to find constants that are declard as varaibles. Author: Nikolaj Bjorner (nbjorner) 9-24-2014 Notes: --*/ #ifndef _BIND_VARIABLES_H_ #define _BIND_VARIABLES_H_ #include"ast.h" class bind_variables { typedef obj_map var2bound; typedef obj_map cache_t; ast_manager& m; app_ref_vector m_vars; obj_map m_cache; var2bound m_var2bound; expr_ref_vector m_pinned; ptr_vector m_bound; svector m_names; ptr_vector m_todo; ptr_vector m_args; expr_ref abstract(expr* fml, cache_t& cache, unsigned scope); public: bind_variables(ast_manager & m); ~bind_variables(); expr_ref operator()(expr* fml, bool is_forall); void add_var(app* v); }; #endif /* _BIND_VARIABLES_H_ */ z3-4.4.0/src/muz/base/dl_context.cpp0000644000175000017500000012366112540347414017041 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_context.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-05-18. Revision History: --*/ #include #include #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"dl_context.h" #include"for_each_expr.h" #include"ast_smt_pp.h" #include"ast_smt2_pp.h" #include"datatype_decl_plugin.h" #include"scoped_proof.h" #include"fixedpoint_params.hpp" namespace datalog { // ----------------------------------- // // context::sort_domain // // ----------------------------------- class context::sort_domain { private: sort_kind m_kind; protected: sort_ref m_sort; bool m_limited_size; uint64 m_size; sort_domain(sort_kind k, context & ctx, sort * s) : m_kind(k), m_sort(s, ctx.get_manager()) { m_limited_size = ctx.get_decl_util().try_get_size(s, m_size); } public: virtual ~sort_domain() {} sort_kind get_kind() const { return m_kind; } virtual unsigned get_constant_count() const = 0; virtual void print_element(finite_element el_num, std::ostream & out) = 0; }; class context::symbol_sort_domain : public sort_domain { typedef map sym2num; typedef svector num2sym; sym2num m_el_numbers; num2sym m_el_names; public: symbol_sort_domain(context & ctx, sort * s) : sort_domain(SK_SYMBOL, ctx, s) {} finite_element get_number(symbol sym) { //we number symbols starting from zero, so table->size() is equal to the //index of the symbol to be added next unsigned newIdx = m_el_numbers.size(); sym2num::entry* sym_e = m_el_numbers.insert_if_not_there2(sym, newIdx); unsigned idx=sym_e->get_data().m_value; if (idx==newIdx) { m_el_names.push_back(sym); SASSERT(m_el_names.size()==m_el_numbers.size()); } if (m_limited_size && idx>=m_size) { std::stringstream sstm; sstm << "sort " << m_sort->get_name() << " contains more constants than its declared size " << m_size; throw default_exception(sstm.str()); } return idx; } virtual unsigned get_constant_count() const { return m_el_names.size(); } virtual void print_element(finite_element el_num, std::ostream & out) { if (el_num>=m_el_names.size()) { out << el_num; return; } out << m_el_names[el_num]; } }; class context::uint64_sort_domain : public sort_domain { typedef map > el2num; typedef svector num2el; el2num m_el_numbers; num2el m_el_names; public: uint64_sort_domain(context & ctx, sort * s) : sort_domain(SK_UINT64, ctx, s) {} finite_element get_number(uint64 el) { //we number symbols starting from zero, so table->size() is equal to the //index of the symbol to be added next unsigned newIdx = m_el_numbers.size(); el2num::entry* sym_e = m_el_numbers.insert_if_not_there2(el, newIdx); unsigned idx=sym_e->get_data().m_value; if (idx==newIdx) { m_el_names.push_back(el); SASSERT(m_el_names.size()==m_el_numbers.size()); } if (m_limited_size && idx>=m_size) { std::stringstream sstm; sstm << "sort " << m_sort->get_name() << " contains more constants than its declared size " << m_size; throw default_exception(sstm.str()); } return idx; } virtual unsigned get_constant_count() const { return m_el_names.size(); } virtual void print_element(finite_element el_num, std::ostream & out) { if (el_num >= m_el_names.size()) { out << "get_name() << ":" << el_num << '>'; return; } out << m_el_names[el_num]; } }; // ----------------------------------- // // trail stack for restoring rules // // ----------------------------------- class context::restore_rules : public trail { rule_set* m_old_rules; void reset() { dealloc(m_old_rules); m_old_rules = 0; } public: restore_rules(rule_set& r): m_old_rules(alloc(rule_set, r)) {} virtual ~restore_rules() {} virtual void undo(context& ctx) { ctx.replace_rules(*m_old_rules); reset(); } }; template class restore_vec_size_trail : public trail { Vec& m_vector; unsigned m_old_size; public: restore_vec_size_trail(Vec& v): m_vector(v), m_old_size(v.size()) {} virtual ~restore_vec_size_trail() {} virtual void undo(Ctx& ctx) { m_vector.shrink(m_old_size); } }; void context::push() { m_trail.push_scope(); m_trail.push(restore_rules(m_rule_set)); m_trail.push(restore_vec_size_trail(m_rule_fmls)); m_trail.push(restore_vec_size_trail(m_background)); } void context::pop() { if (m_trail.get_num_scopes() == 0) { throw default_exception("there are no backtracking points to pop to"); } if(m_engine.get()){ if(get_engine() != DUALITY_ENGINE) throw default_exception("operation is not supported by engine"); } m_trail.pop_scope(1); } // ----------------------------------- // // context // // ----------------------------------- context::context(ast_manager & m, register_engine_base& re, smt_params& fp, params_ref const& pa): m(m), m_register_engine(re), m_fparams(fp), m_params_ref(pa), m_params(alloc(fixedpoint_params, m_params_ref)), m_decl_util(m), m_rewriter(m), m_var_subst(m), m_rule_manager(*this), m_contains_p(*this), m_rule_properties(m, m_rule_manager, *this, m_contains_p), m_transf(*this), m_trail(*this), m_pinned(m), m_bind_variables(m), m_rule_set(*this), m_transformed_rule_set(*this), m_rule_fmls_head(0), m_rule_fmls(m), m_background(m), m_mc(0), m_rel(0), m_engine(0), m_closed(false), m_saturation_was_run(false), m_enable_bind_variables(true), m_last_status(OK), m_last_answer(m), m_engine_type(LAST_ENGINE), m_cancel(false) { re.set_context(this); updt_params(pa); } context::~context() { reset(); dealloc(m_params); } void context::reset() { m_trail.reset(); m_rule_set.reset(); m_rule_fmls_head = 0; m_rule_fmls.reset(); m_rule_names.reset(); m_rule_bounds.reset(); m_argument_var_names.reset(); m_preds.reset(); m_preds_by_name.reset(); reset_dealloc_values(m_sorts); m_engine = 0; m_rel = 0; } bool context::is_fact(app * head) const { return m_rule_manager.is_fact(head); } bool context::has_sort_domain(relation_sort s) const { return m_sorts.contains(s); } context::sort_domain & context::get_sort_domain(relation_sort s) { return *m_sorts.find(s); } const context::sort_domain & context::get_sort_domain(relation_sort s) const { return *m_sorts.find(s); } bool context::generate_proof_trace() const { return m_generate_proof_trace; } bool context::output_profile() const { return m_params->datalog_output_profile(); } bool context::output_tuples() const { return m_params->datalog_print_tuples(); } bool context::use_map_names() const { return m_params->datalog_use_map_names(); } bool context::fix_unbound_vars() const { return m_params->xform_fix_unbound_vars(); } symbol context::default_table() const { return m_params->datalog_default_table(); } symbol context::default_relation() const { return m_default_relation; } void context::set_default_relation(symbol const& s) { m_default_relation = s; } symbol context::print_aig() const { return m_params->print_aig(); } symbol context::check_relation() const { return m_params->datalog_check_relation(); } symbol context::default_table_checker() const { return m_params->datalog_default_table_checker(); } bool context::default_table_checked() const { return m_params->datalog_default_table_checked(); } bool context::dbg_fpr_nonempty_relation_signature() const { return m_params->datalog_dbg_fpr_nonempty_relation_signature(); } unsigned context::dl_profile_milliseconds_threshold() const { return m_params->datalog_profile_timeout_milliseconds(); } bool context::all_or_nothing_deltas() const { return m_params->datalog_all_or_nothing_deltas(); } bool context::compile_with_widening() const { return m_params->datalog_compile_with_widening(); } bool context::unbound_compressor() const { return m_unbound_compressor; } void context::set_unbound_compressor(bool f) { m_unbound_compressor = f; } bool context::similarity_compressor() const { return m_params->datalog_similarity_compressor(); } unsigned context::similarity_compressor_threshold() const { return m_params->datalog_similarity_compressor_threshold(); } unsigned context::soft_timeout() const { return m_fparams.m_timeout; } unsigned context::initial_restart_timeout() const { return m_params->datalog_initial_restart_timeout(); } bool context::generate_explanations() const { return m_params->datalog_generate_explanations(); } bool context::explanations_on_relation_level() const { return m_params->datalog_explanations_on_relation_level(); } bool context::magic_sets_for_queries() const { return m_params->datalog_magic_sets_for_queries(); } symbol context::tab_selection() const { return m_params->tab_selection(); } bool context::xform_slice() const { return m_params->xform_slice(); } bool context::xform_bit_blast() const { return m_params->xform_bit_blast(); } bool context::karr() const { return m_params->xform_karr(); } bool context::scale() const { return m_params->xform_scale(); } bool context::magic() const { return m_params->xform_magic(); } bool context::quantify_arrays() const { return m_params->xform_quantify_arrays(); } bool context::instantiate_quantifiers() const { return m_params->xform_instantiate_quantifiers(); } void context::register_finite_sort(sort * s, sort_kind k) { m_pinned.push_back(s); SASSERT(!m_sorts.contains(s)); sort_domain * dom; switch (k) { case SK_SYMBOL: dom = alloc(symbol_sort_domain, *this, s); break; case SK_UINT64: dom = alloc(uint64_sort_domain, *this, s); break; default: UNREACHABLE(); } m_sorts.insert(s, dom); } void context::register_variable(func_decl* var) { m_bind_variables.add_var(m.mk_const(var)); } expr_ref context::bind_vars(expr* fml, bool is_forall) { if (m_enable_bind_variables) { return m_bind_variables(fml, is_forall); } else { return expr_ref(fml, m); } } void context::register_predicate(func_decl * decl, bool named) { if (!is_predicate(decl)) { m_pinned.push_back(decl); m_preds.insert(decl); TRACE("dl", tout << mk_pp(decl, m) << "\n";); if (named) { m_preds_by_name.insert(decl->get_name(), decl); } } } void context::restrict_predicates(func_decl_set const& preds) { m_preds.reset(); func_decl_set::iterator it = preds.begin(), end = preds.end(); for (; it != end; ++it) { TRACE("dl", tout << mk_pp(*it, m) << "\n";); m_preds.insert(*it); } } context::finite_element context::get_constant_number(relation_sort srt, symbol sym) { sort_domain & dom0 = get_sort_domain(srt); SASSERT(dom0.get_kind() == SK_SYMBOL); symbol_sort_domain & dom = static_cast(dom0); return dom.get_number(sym); } context::finite_element context::get_constant_number(relation_sort srt, uint64 el) { sort_domain & dom0 = get_sort_domain(srt); SASSERT(dom0.get_kind()==SK_UINT64); uint64_sort_domain & dom = static_cast(dom0); return dom.get_number(el); } void context::print_constant_name(relation_sort srt, uint64 num, std::ostream & out) { if (has_sort_domain(srt)) { SASSERT(num<=UINT_MAX); get_sort_domain(srt).print_element(static_cast(num), out); } else { out << num; } } bool context::try_get_sort_constant_count(relation_sort srt, uint64 & constant_count) { if (!has_sort_domain(srt)) { return false; } constant_count = get_sort_domain(srt).get_constant_count(); return true; } uint64 context::get_sort_size_estimate(relation_sort srt) { if (get_decl_util().is_rule_sort(srt)) { return 1; } uint64 res; if (!try_get_sort_constant_count(srt, res)) { sort_size sz = srt->get_num_elements(); if (sz.is_finite()) { res = sz.size(); } else { res = std::numeric_limits::max(); } } return res; } void context::set_argument_names(const func_decl * pred, svector var_names) { SASSERT(!m_argument_var_names.contains(pred)); m_argument_var_names.insert(pred, var_names); } symbol context::get_argument_name(const func_decl * pred, unsigned arg_index) { pred2syms::obj_map_entry * e = m_argument_var_names.find_core(pred); if (!e) { std::stringstream name_stm; name_stm << '#' << arg_index; return symbol(name_stm.str().c_str()); } SASSERT(arg_index < e->get_data().m_value.size()); return e->get_data().m_value[arg_index]; } void context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) { if (relation_name_cnt > 0) { ensure_engine(); } if (relation_name_cnt > 0 && m_rel) { m_rel->set_predicate_representation(pred, relation_name_cnt, relation_names); } } func_decl * context::mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix, unsigned arity, sort * const * domain, func_decl* orig_pred) { func_decl* new_pred = m.mk_fresh_func_decl(prefix, suffix, arity, domain, m.mk_bool_sort()); register_predicate(new_pred, true); if (m_rel) { m_rel->inherit_predicate_kind(new_pred, orig_pred); } return new_pred; } void context::add_rule(expr* rl, symbol const& name, unsigned bound) { m_rule_fmls.push_back(rl); m_rule_names.push_back(name); m_rule_bounds.push_back(bound); } void context::flush_add_rules() { datalog::rule_manager& rm = get_rule_manager(); scoped_proof_mode _scp(m, generate_proof_trace()?PGM_FINE:PGM_DISABLED); while (m_rule_fmls_head < m_rule_fmls.size()) { expr* fml = m_rule_fmls[m_rule_fmls_head].get(); proof* p = generate_proof_trace()?m.mk_asserted(fml):0; rm.mk_rule(fml, p, m_rule_set, m_rule_names[m_rule_fmls_head]); ++m_rule_fmls_head; } check_rules(m_rule_set); } // // Update a rule with a new. // It requires basic subsumption. // void context::update_rule(expr* rl, symbol const& name) { datalog::rule_manager& rm = get_rule_manager(); proof* p = 0; if (generate_proof_trace()) { p = m.mk_asserted(rl); } unsigned size_before = m_rule_set.get_num_rules(); rm.mk_rule(rl, p, m_rule_set, name); unsigned size_after = m_rule_set.get_num_rules(); if (size_before + 1 != size_after) { std::stringstream strm; strm << "Rule " << name << " has a non-trivial body. It cannot be modified"; throw default_exception(strm.str()); } // The new rule is inserted last: rule_ref r(m_rule_set.get_rule(size_before), rm); rule_ref_vector const& rls = m_rule_set.get_rules(); rule* old_rule = 0; for (unsigned i = 0; i < size_before; ++i) { if (rls[i]->name() == name) { if (old_rule) { std::stringstream strm; strm << "Rule " << name << " occurs twice. It cannot be modified"; m_rule_set.del_rule(r); throw default_exception(strm.str()); } old_rule = rls[i]; } } if (old_rule) { if (!check_subsumes(*old_rule, *r)) { std::stringstream strm; strm << "Old rule "; old_rule->display(*this, strm); strm << "does not subsume new rule "; r->display(*this, strm); m_rule_set.del_rule(r); throw default_exception(strm.str()); } m_rule_set.del_rule(old_rule); } } bool context::check_subsumes(rule const& stronger_rule, rule const& weaker_rule) { if (stronger_rule.get_head() != weaker_rule.get_head()) { return false; } for (unsigned i = 0; i < stronger_rule.get_tail_size(); ++i) { app* t = stronger_rule.get_tail(i); bool found = false; for (unsigned j = 0; j < weaker_rule.get_tail_size(); ++j) { app* s = weaker_rule.get_tail(j); if (s == t) { found = true; break; } } if (!found) { return false; } } return true; } unsigned context::get_num_levels(func_decl* pred) { ensure_engine(); return m_engine->get_num_levels(pred); } expr_ref context::get_cover_delta(int level, func_decl* pred) { ensure_engine(); return m_engine->get_cover_delta(level, pred); } void context::add_cover(int level, func_decl* pred, expr* property) { ensure_engine(); m_engine->add_cover(level, pred, property); } void context::check_rules(rule_set& r) { m_rule_properties.set_generate_proof(generate_proof_trace()); switch(get_engine()) { case DATALOG_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_quantifier_free(); m_rule_properties.check_uninterpreted_free(); m_rule_properties.check_nested_free(); m_rule_properties.check_infinite_sorts(); break; case PDR_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); m_rule_properties.check_uninterpreted_free(); break; case QPDR_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_for_negated_predicates(); m_rule_properties.check_uninterpreted_free(); break; case BMC_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_for_negated_predicates(); break; case QBMC_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); break; case TAB_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); break; case DUALITY_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); break; case CLP_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); break; case DDNF_ENGINE: break; case LAST_ENGINE: default: UNREACHABLE(); break; } } void context::add_rule(rule_ref& r) { m_rule_set.add_rule(r); } void context::add_fact(func_decl * pred, const relation_fact & fact) { if (get_engine() == DATALOG_ENGINE) { ensure_engine(); m_rel->add_fact(pred, fact); } else { expr_ref rule(m.mk_app(pred, fact.size(), (expr*const*)fact.c_ptr()), m); add_rule(rule, symbol::null); } } void context::add_fact(app * head) { SASSERT(is_fact(head)); relation_fact fact(get_manager()); unsigned n = head->get_num_args(); for (unsigned i = 0; i < n; i++) { fact.push_back(to_app(head->get_arg(i))); } add_fact(head->get_decl(), fact); } bool context::has_facts(func_decl * pred) const { return m_rel && m_rel->has_facts(pred); } void context::add_table_fact(func_decl * pred, const table_fact & fact) { if (get_engine() == DATALOG_ENGINE) { ensure_engine(); m_rel->add_fact(pred, fact); } else { relation_fact rfact(m); for (unsigned i = 0; i < fact.size(); ++i) { rfact.push_back(m_decl_util.mk_numeral(fact[i], pred->get_domain()[i])); } add_fact(pred, rfact); } } void context::add_table_fact(func_decl * pred, unsigned num_args, unsigned args[]) { if (pred->get_arity() != num_args) { std::ostringstream out; out << "miss-matched number of arguments passed to " << mk_ismt2_pp(pred, m) << " " << num_args << " passed"; throw default_exception(out.str()); } table_fact fact; for (unsigned i = 0; i < num_args; ++i) { fact.push_back(args[i]); } add_table_fact(pred, fact); } void context::close() { SASSERT(!m_closed); if (!m_rule_set.close()) { throw default_exception("Negation is not stratified!"); } m_closed = true; } void context::ensure_closed() { if (!m_closed) { close(); } } void context::ensure_opened() { if (m_closed) { reopen(); } } void context::reopen() { SASSERT(m_closed); m_rule_set.reopen(); m_closed = false; } void context::transform_rules(rule_transformer::plugin* plugin) { flet _enable_bv(m_enable_bind_variables, false); rule_transformer transformer(*this); transformer.register_plugin(plugin); transform_rules(transformer); } void context::transform_rules(rule_transformer& transf) { SASSERT(m_closed); //we must finish adding rules before we start transforming them TRACE("dl", display_rules(tout);); if (transf(m_rule_set)) { //we have already ensured the negation is stratified and transformations //should not break the stratification m_rule_set.ensure_closed(); TRACE("dl", display_rules(tout);); TRACE("dl_verbose", display(tout);); } } void context::replace_rules(rule_set const & rs) { SASSERT(!m_closed); m_rule_set.replace_rules(rs); if (m_rel) { m_rel->restrict_predicates(get_predicates()); } } void context::record_transformed_rules() { m_transformed_rule_set.replace_rules(m_rule_set); } void context::apply_default_transformation() { } void context::collect_params(param_descrs& p) { fixedpoint_params::collect_param_descrs(p); insert_timeout(p); } void context::updt_params(params_ref const& p) { m_params_ref.copy(p); if (m_engine.get()) m_engine->updt_params(); m_generate_proof_trace = m_params->generate_proof_trace(); m_unbound_compressor = m_params->datalog_unbound_compressor(); m_default_relation = m_params->datalog_default_relation(); } expr_ref context::get_background_assertion() { expr_ref result(m); switch (m_background.size()) { case 0: result = m.mk_true(); break; case 1: result = m_background[0].get(); break; default: result = m.mk_and(m_background.size(), m_background.c_ptr()); break; } return result; } void context::assert_expr(expr* e) { TRACE("dl", tout << mk_ismt2_pp(e, m) << "\n";); m_background.push_back(e); } void context::cancel() { m_cancel = true; m_last_status = CANCELED; m_transf.cancel(); if (m_engine) m_engine->cancel(); } void context::cleanup() { m_cancel = false; m_last_status = OK; if (m_engine) m_engine->cleanup(); } class context::engine_type_proc { ast_manager& m; arith_util a; datatype_util dt; DL_ENGINE m_engine_type; public: engine_type_proc(ast_manager& m): m(m), a(m), dt(m), m_engine_type(DATALOG_ENGINE) {} DL_ENGINE get_engine() const { return m_engine_type; } void operator()(expr* e) { if (is_quantifier(e)) { m_engine_type = QPDR_ENGINE; } else if (m_engine_type != QPDR_ENGINE) { if (a.is_int_real(e)) { m_engine_type = PDR_ENGINE; } else if (is_var(e) && m.is_bool(e)) { m_engine_type = PDR_ENGINE; } else if (dt.is_datatype(m.get_sort(e))) { m_engine_type = PDR_ENGINE; } } } }; void context::configure_engine() { if (m_engine_type != LAST_ENGINE) { return; } symbol e = m_params->engine(); if (e == symbol("datalog")) { m_engine_type = DATALOG_ENGINE; } else if (e == symbol("pdr")) { m_engine_type = PDR_ENGINE; } else if (e == symbol("qpdr")) { m_engine_type = QPDR_ENGINE; } else if (e == symbol("bmc")) { m_engine_type = BMC_ENGINE; } else if (e == symbol("qbmc")) { m_engine_type = QBMC_ENGINE; } else if (e == symbol("tab")) { m_engine_type = TAB_ENGINE; } else if (e == symbol("clp")) { m_engine_type = CLP_ENGINE; } else if (e == symbol("duality")) { m_engine_type = DUALITY_ENGINE; } else if (e == symbol("ddnf")) { m_engine_type = DDNF_ENGINE; } if (m_engine_type == LAST_ENGINE) { expr_fast_mark1 mark; engine_type_proc proc(m); m_engine_type = DATALOG_ENGINE; for (unsigned i = 0; m_engine_type == DATALOG_ENGINE && i < m_rule_set.get_num_rules(); ++i) { rule * r = m_rule_set.get_rule(i); quick_for_each_expr(proc, mark, r->get_head()); for (unsigned j = 0; j < r->get_tail_size(); ++j) { quick_for_each_expr(proc, mark, r->get_tail(j)); } m_engine_type = proc.get_engine(); } for (unsigned i = m_rule_fmls_head; m_engine_type == DATALOG_ENGINE && i < m_rule_fmls.size(); ++i) { expr* fml = m_rule_fmls[i].get(); while (is_quantifier(fml)) { fml = to_quantifier(fml)->get_expr(); } quick_for_each_expr(proc, mark, fml); m_engine_type = proc.get_engine(); } } } lbool context::query(expr* query) { m_mc = mk_skip_model_converter(); m_last_status = OK; m_last_answer = 0; switch (get_engine()) { case DATALOG_ENGINE: case PDR_ENGINE: case QPDR_ENGINE: case BMC_ENGINE: case QBMC_ENGINE: case TAB_ENGINE: case CLP_ENGINE: case DDNF_ENGINE: flush_add_rules(); break; case DUALITY_ENGINE: // this lets us use duality with SAS 2013 abstraction if(quantify_arrays()) flush_add_rules(); break; default: UNREACHABLE(); } ensure_engine(); return m_engine->query(query); } model_ref context::get_model() { ensure_engine(); return m_engine->get_model(); } proof_ref context::get_proof() { ensure_engine(); return m_engine->get_proof(); } void context::ensure_engine() { if (!m_engine.get()) { m_engine = m_register_engine.mk_engine(get_engine()); m_engine->updt_params(); // break abstraction. if (get_engine() == DATALOG_ENGINE) { m_rel = dynamic_cast(m_engine.get()); } } } lbool context::rel_query(unsigned num_rels, func_decl * const* rels) { ensure_engine(); return m_engine->query(num_rels, rels); } expr* context::get_answer_as_formula() { if (m_last_answer) { return m_last_answer.get(); } ensure_engine(); m_last_answer = m_engine->get_answer(); return m_last_answer.get(); } void context::display_certificate(std::ostream& out) { ensure_engine(); m_engine->display_certificate(out); } void context::display(std::ostream & out) const { display_rules(out); if (m_rel) m_rel->display_facts(out); } void context::display_profile(std::ostream& out) const { out << "\n---------------\n"; out << "Original rules\n"; display_rules(out); out << "\n---------------\n"; out << "Transformed rules\n"; m_transformed_rule_set.display(out); if (m_rel) { m_rel->display_profile(out); } } void context::reset_statistics() { if (m_engine) { m_engine->reset_statistics(); } } void context::collect_statistics(statistics& st) const { if (m_engine) { m_engine->collect_statistics(st); } unsigned long long max_mem = memory::get_max_used_memory(); unsigned long long mem = memory::get_allocation_size(); st.update("max memory", static_cast(max_mem)/(1024.0*1024.0)); st.update("memory", static_cast(mem)/(1024.0*1024.0)); } execution_result context::get_status() { return m_last_status; } bool context::result_contains_fact(relation_fact const& f) { return m_rel && m_rel->result_contains_fact(f); } // NB: algebraic data-types declarations will not be printed. class free_func_visitor { ast_manager& m; func_decl_set m_funcs; obj_hashtable m_sorts; public: free_func_visitor(ast_manager& m): m(m) {} void operator()(var * n) { } void operator()(app * n) { m_funcs.insert(n->get_decl()); sort* s = m.get_sort(n); if (s->get_family_id() == null_family_id) { m_sorts.insert(s); } } void operator()(quantifier * n) { } func_decl_set& funcs() { return m_funcs; } obj_hashtable& sorts() { return m_sorts; } }; static void collect_free_funcs(unsigned sz, expr* const* exprs, expr_mark& visited, free_func_visitor& v, mk_fresh_name& fresh_names) { for (unsigned i = 0; i < sz; ++i) { expr* e = exprs[i]; for_each_expr(v, visited, e); while (is_quantifier(e)) { e = to_quantifier(e)->get_expr(); } fresh_names.add(e); } } void context::get_raw_rule_formulas(expr_ref_vector& rules, svector& names, vector &bounds) { for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { expr_ref r = bind_vars(m_rule_fmls[i].get(), true); rules.push_back(r.get()); names.push_back(m_rule_names[i]); bounds.push_back(m_rule_bounds[i]); } } void context::get_rules_as_formulas(expr_ref_vector& rules, expr_ref_vector& queries, svector& names) { expr_ref fml(m); rule_manager& rm = get_rule_manager(); // ensure that rules are all using bound variables. for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) { ptr_vector sorts; m_free_vars(m_rule_fmls[i].get()); if (!m_free_vars.empty()) { rm.mk_rule(m_rule_fmls[i].get(), 0, m_rule_set, m_rule_names[i]); m_rule_fmls[i] = m_rule_fmls.back(); m_rule_names[i] = m_rule_names.back(); m_rule_fmls.pop_back(); m_rule_names.pop_back(); m_rule_bounds.pop_back(); --i; } } rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end(); for (; it != end; ++it) { rule* r = *it; rm.to_formula(*r, fml); func_decl* h = r->get_decl(); if (m_rule_set.is_output_predicate(h)) { expr* body = fml; expr* e2; if (is_quantifier(body)) { quantifier* q = to_quantifier(body); expr* e = q->get_expr(); VERIFY(m.is_implies(e, body, e2)); fml = m.mk_quantifier(false, q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), body); } else { VERIFY(m.is_implies(body, body, e2)); fml = body; } queries.push_back(fml); } else { rules.push_back(fml); names.push_back(r->name()); } } for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) { rules.push_back(m_rule_fmls[i].get()); names.push_back(m_rule_names[i]); } } void context::display_smt2(unsigned num_queries, expr* const* qs, std::ostream& out) { ast_manager& m = get_manager(); free_func_visitor visitor(m); expr_mark visited; func_decl_set rels; unsigned num_axioms = m_background.size(); expr* const* axioms = m_background.c_ptr(); expr_ref fml(m); expr_ref_vector rules(m), queries(m); svector names; bool use_fixedpoint_extensions = m_params->print_fixedpoint_extensions(); bool print_low_level = m_params->print_low_level_smt2(); bool do_declare_vars = m_params->print_with_variable_declarations(); #define PP(_e_) if (print_low_level) out << mk_smt_pp(_e_, m); else ast_smt2_pp(out, _e_, env); get_rules_as_formulas(rules, queries, names); queries.append(num_queries, qs); smt2_pp_environment_dbg env(m); mk_fresh_name fresh_names; collect_free_funcs(num_axioms, axioms, visited, visitor, fresh_names); collect_free_funcs(rules.size(), rules.c_ptr(), visited, visitor, fresh_names); collect_free_funcs(queries.size(), queries.c_ptr(), visited, visitor, fresh_names); func_decl_set funcs; func_decl_set::iterator it = visitor.funcs().begin(); func_decl_set::iterator end = visitor.funcs().end(); for (; it != end; ++it) { func_decl* f = *it; if (f->get_family_id() != null_family_id) { // } else if (is_predicate(f) && use_fixedpoint_extensions) { rels.insert(f); } else { funcs.insert(f); } } if (!use_fixedpoint_extensions) { out << "(set-logic HORN)\n"; } it = funcs.begin(), end = funcs.end(); obj_hashtable& sorts = visitor.sorts(); obj_hashtable::iterator sit = sorts.begin(), send = sorts.end(); for (; sit != send; ++sit) { PP(*sit); } for (; it != end; ++it) { func_decl* f = *it; PP(f); out << "\n"; } it = rels.begin(); end = rels.end(); for (; it != end; ++it) { func_decl* f = *it; out << "(declare-rel " << f->get_name() << " ("; for (unsigned i = 0; i < f->get_arity(); ++i) { ast_smt2_pp(out, f->get_domain(i), env); if (i + 1 < f->get_arity()) { out << " "; } } out << "))\n"; } if (use_fixedpoint_extensions && do_declare_vars) { declare_vars(rules, fresh_names, out); } if (num_axioms > 0 && !use_fixedpoint_extensions) { throw default_exception("Background axioms cannot be used with SMT-LIB2 HORN format"); } for (unsigned i = 0; i < num_axioms; ++i) { out << "(assert "; PP(axioms[i]); out << ")\n"; } for (unsigned i = 0; i < rules.size(); ++i) { out << (use_fixedpoint_extensions?"(rule ":"(assert "); expr* r = rules[i].get(); symbol nm = names[i]; if (symbol::null != nm) { out << "(! "; } PP(r); if (symbol::null != nm) { out << " :named "; while (fresh_names.contains(nm)) { std::ostringstream s; s << nm << "!"; nm = symbol(s.str().c_str()); } fresh_names.add(nm); if (is_smt2_quoted_symbol(nm)) { out << mk_smt2_quoted_symbol(nm); } else { out << nm; } out << ")"; } out << ")\n"; } if (use_fixedpoint_extensions) { for (unsigned i = 0; i < queries.size(); ++i) { out << "(query "; PP(queries[i].get()); out << ")\n"; } } else { for (unsigned i = 0; i < queries.size(); ++i) { if (queries.size() > 1) out << "(push)\n"; out << "(assert "; expr_ref q(m); q = m.mk_not(queries[i].get()); PP(q); out << ")\n"; out << "(check-sat)\n"; if (queries.size() > 1) out << "(pop)\n"; } } } void context::declare_vars(expr_ref_vector& rules, mk_fresh_name& fresh_names, std::ostream& out) { // // replace bound variables in rules by 'var declarations' // First remove quantifers, then replace bound variables // by fresh constants. // smt2_pp_environment_dbg env(m); var_subst vsubst(m, false); expr_ref_vector fresh_vars(m), subst(m); expr_ref res(m); obj_map var_idxs; obj_map max_vars; for (unsigned i = 0; i < rules.size(); ++i) { expr* r = rules[i].get(); if (!is_quantifier(r)) { continue; } quantifier* q = to_quantifier(r); if (!q->is_forall()) { continue; } if (has_quantifiers(q->get_expr())) { continue; } max_vars.reset(); subst.reset(); unsigned max_var = 0; unsigned num_vars = q->get_num_decls(); for (unsigned j = 0; j < num_vars; ++j) { sort* s = q->get_decl_sort(num_vars-1-j); // maximal var for the given sort. if (!max_vars.find(s, max_var)) { max_var = 0; } else { ++max_var; } max_vars.insert(s, max_var); // index into fresh variable array. // unsigned fresh_var_idx = 0; obj_map::obj_map_entry* e = var_idxs.insert_if_not_there2(s, unsigned_vector()); unsigned_vector& vars = e->get_data().m_value; if (max_var >= vars.size()) { SASSERT(vars.size() == max_var); vars.push_back(fresh_vars.size()); symbol name = fresh_names.next(); fresh_vars.push_back(m.mk_const(name, s)); out << "(declare-var " << name << " "; ast_smt2_pp(out, s, env); out << ")\n"; } subst.push_back(fresh_vars[vars[max_var]].get()); } vsubst(q->get_expr(), subst.size(), subst.c_ptr(), res); rules[i] = res.get(); } } }; z3-4.4.0/src/muz/rel/0000755000175000017500000000000012540347414014031 5ustar michaelmichaelz3-4.4.0/src/muz/rel/dl_compiler.h0000644000175000017500000003162612540347414016503 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_compiler.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #ifndef _DL_COMPILER_H_ #define _DL_COMPILER_H_ #include #include #include #include "ast.h" #include "hashtable.h" #include "map.h" #include "obj_pair_hashtable.h" #include "ref_vector.h" #include "vector.h" #include "dl_base.h" #include "dl_context.h" #include "dl_instruction.h" namespace datalog { class compiler { typedef instruction::reg_idx reg_idx; typedef hashtable int_set; typedef u_map int2int; typedef u_map int2ints; typedef obj_map pred2idx; typedef unsigned_vector var_vector; typedef ptr_vector func_decl_vector; enum assembling_column_kind { ACK_BOUND_VAR, ACK_UNBOUND_VAR, ACK_CONSTANT }; /** \brief instruction for assembling head relation from a joint relation built from rule evaluation. ACK_BOUND_VAR(source_column) - encodes that the column contains a variable bound in the body. ACK_UNBOUND_VAR(var_index) - encodes that the column contains a variable that is unbound (by the corresponding rule body), var_index is the de-Brujin index (var->get_idx()) of the variable associated with the column. ACK_CONSTANT(constant) - encodes that the column contains the constant. Examples: P(x) :- Q(x,y), Q(y,z) The variables in the body relation are [x, y, y, z] indexed as 0, 1, 2, 3. The variable x gets the instruction ACK_BOUND_VAR(0) P(u,x) :- Q(x,y), Q(y,z) The variable u gets the instruction ACK_UNBOUND_VAR(#0) P(1, x) :- Q(x,y), Q(y,z) The instruction for column 0 is ACK_CONSTANT(1) */ struct assembling_column_info { relation_sort domain; // domain of the column assembling_column_kind kind; // "instruction" tag union { unsigned source_column; // for ACK_BOUND_VAR unsigned var_index; // for ACK_UNBOUND_VAR relation_element constant; // for ACK_CONSTANT }; }; class instruction_observer : public instruction_block::instruction_observer { compiler & m_parent; rule * m_current; public: instruction_observer(compiler & parent) : m_parent(parent), m_current(0) {} void start_rule(rule * r) { SASSERT(!m_current); m_current=r; } void finish_rule() { m_current = 0; } virtual void notify(instruction * i) { if(m_current) { i->set_accounting_parent_object(m_parent.m_context, m_current); } } }; context & m_context; rule_set const & m_rule_set; /** Invariant: the \c m_top_level_code never contains the loop that is being constructed, so instruction that need to be executed before the loop can be pushed into it. */ instruction_block & m_top_level_code; pred2idx m_pred_regs; vector m_reg_signatures; obj_pair_map m_constant_registers; obj_pair_map m_total_registers; obj_map m_empty_tables_registers; instruction_observer m_instruction_observer; expr_free_vars m_free_vars; /** \brief Finds all the min aggregation functions in the premise of a given rule. */ static void find_min_aggregates(const rule * r, ptr_vector& min_aggregates); /** \brief Decides whether a predicate is subject to a min aggregation function. If \c decl is subject to a min aggregation function, the output parameters are written with the neccessary information. \returns true if the output paramaters have been written */ static bool prepare_min_aggregate(const func_decl * decl, const ptr_vector& min_aggregates, unsigned_vector & group_by_cols, unsigned & min_col); /** If true, the union operation on the underlying structure only provides the information whether the updated relation has changed or not. In this case we do not get anything from using delta relations at position of input relations in the saturation loop, so we would not do it. */ bool all_or_nothing_deltas() const { return m_context.all_or_nothing_deltas(); } /** If true, we compile the saturation loops in a way that allows us to use widening. */ bool compile_with_widening() const { return m_context.compile_with_widening(); } reg_idx get_fresh_register(const relation_signature & sig); reg_idx get_register(const relation_signature & sig, bool reuse, reg_idx r); reg_idx get_single_column_register(const relation_sort s); /** \brief Allocate registers for predicates in \c pred and add them into the \c regs map. \c regs must not already contain any predicate from \c preds. */ void get_fresh_registers(const func_decl_set & preds, pred2idx & regs); void make_join(reg_idx t1, reg_idx t2, const variable_intersection & vars, reg_idx & result, bool reuse_t1, instruction_block & acc); void make_min(reg_idx source, reg_idx & target, const unsigned_vector & group_by_cols, const unsigned min_col, instruction_block & acc); void make_join_project(reg_idx t1, reg_idx t2, const variable_intersection & vars, const unsigned_vector & removed_cols, reg_idx & result, bool reuse_t1, instruction_block & acc); void make_filter_interpreted_and_project(reg_idx src, app_ref & cond, const unsigned_vector & removed_cols, reg_idx & result, bool reuse, instruction_block & acc); void make_select_equal_and_project(reg_idx src, const relation_element val, unsigned col, reg_idx & result, bool reuse, instruction_block & acc); /** \brief Create add an union or widen operation and put it into \c acc. */ void make_union(reg_idx src, reg_idx tgt, reg_idx delta, bool widening, instruction_block & acc); void make_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols, reg_idx & result, bool reuse, instruction_block & acc); void make_rename(reg_idx src, unsigned cycle_len, const unsigned * permutation_cycle, reg_idx & result, bool reuse, instruction_block & acc); void make_clone(reg_idx src, reg_idx & result, instruction_block & acc); /** \brief Into \c acc add code that will assemble columns of a relation according to description in \c acis0. The source for bound variables is the table in register \c src. If \c src is \c execution_context::void_register, it is assumed to be a full relation with empty signature. */ void make_assembling_code(rule* compiled_rule, func_decl* head_pred, reg_idx src, const svector & acis0, reg_idx & result, bool & dealloc, instruction_block & acc); void make_dealloc_non_void(reg_idx r, instruction_block & acc); void make_add_constant_column(func_decl* pred, reg_idx src, const relation_sort s, const relation_element val, reg_idx & result, bool & dealloc, instruction_block & acc); void make_add_unbound_column(rule* compiled_rule, unsigned col_idx, func_decl* pred, reg_idx src, const relation_sort s, reg_idx & result, bool & dealloc, instruction_block & acc); void make_full_relation(func_decl* pred, const relation_signature & sig, reg_idx & result, instruction_block & acc); void add_unbound_columns_for_negation(rule* compiled_rule, func_decl* pred, reg_idx& single_res, expr_ref_vector& single_res_expr, bool & dealloc, instruction_block& acc); void make_duplicate_column(reg_idx src, unsigned col, reg_idx & result, bool reuse, instruction_block & acc); void ensure_predicate_loaded(func_decl * pred, instruction_block & acc); /** \brief For rule \c r with two positive uninterpreted predicates put into \c res indexes of local variables in a table that results from join of the two positive predicates. Used to get input for the "project" part of join-project. */ void get_local_indexes_for_projection(rule * r, unsigned_vector & res); void get_local_indexes_for_projection(app * t, var_counter & globals, unsigned ofs, unsigned_vector & res); /** \brief Into \c acc add instructions that will add new facts following from the rule into \c head_reg, and add the facts that were not in \c head_reg before into \c delta_reg. */ void compile_rule_evaluation_run(rule * r, reg_idx head_reg, const reg_idx * tail_regs, reg_idx delta_reg, bool use_widening, instruction_block & acc); void compile_rule_evaluation(rule * r, const pred2idx * input_deltas, reg_idx output_delta, bool use_widening, instruction_block & acc); /** \brief Generate code to evaluate rules corresponding to predicates in \c head_preds. The rules are evaluated in the order their heads appear in the \c head_preds vector. */ void compile_preds(const func_decl_vector & head_preds, const func_decl_set & widened_preds, const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc); /** \brief Generate code to evaluate predicates in a stratum based on their non-recursive rules. */ void compile_preds_init(const func_decl_vector & head_preds, const func_decl_set & widened_preds, const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc); void make_inloop_delta_transition(const pred2idx & global_head_deltas, const pred2idx & global_tail_deltas, const pred2idx & local_deltas, instruction_block & acc); void compile_loop(const func_decl_vector & head_preds, const func_decl_set & widened_preds, const pred2idx & global_head_deltas, const pred2idx & global_tail_deltas, const pred2idx & local_deltas, instruction_block & acc); void compile_dependent_rules(const func_decl_set & head_preds, const pred2idx * input_deltas, const pred2idx & output_deltas, bool add_saturation_marks, instruction_block & acc); void detect_chains(const func_decl_set & preds, func_decl_vector & ordered_preds, func_decl_set & global_deltas); /** Return true if there is no dependency inside the \c rules stratum. The head predicates in stratum must be strongly connected by dependency. */ bool is_nonrecursive_stratum(const func_decl_set & preds) const; /** input_deltas==0 --> we use the actual content of relations instead of deltas */ void compile_nonrecursive_stratum(const func_decl_set & preds, const pred2idx * input_deltas, const pred2idx & output_deltas, bool add_saturation_marks, instruction_block & acc); void compile_strats(const rule_stratifier & stratifier, const pred2idx * input_deltas, const pred2idx & output_deltas, bool add_saturation_marks, instruction_block & acc); bool all_saturated(const func_decl_set & preds) const; void reset(); explicit compiler(context & ctx, rule_set const & rules, instruction_block & top_level_code) : m_context(ctx), m_rule_set(rules), m_top_level_code(top_level_code), m_instruction_observer(*this) {} /** \brief Compile \c rules in to pseudocode. Instructions to load data and perform computations put into \c execution_code */ void do_compilation(instruction_block & execution_code, instruction_block & termination_code); public: static void compile(context & ctx, rule_set const & rules, instruction_block & execution_code, instruction_block & termination_code) { compiler(ctx, rules, execution_code) .do_compilation(execution_code, termination_code); } }; }; #endif /* _DL_COMPILER_H_ */ z3-4.4.0/src/muz/rel/dl_mk_explanations.cpp0000644000175000017500000010142712540347414020415 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_explanations.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-11-08. Revision History: --*/ #include #include"ast_pp.h" #include"ast_smt_pp.h" #include"dl_finite_product_relation.h" #include"dl_product_relation.h" #include"dl_sieve_relation.h" #include"dl_mk_explanations.h" namespace datalog { // ----------------------------------- // // explanation_relation_plugin declaration // // ----------------------------------- class explanation_relation; class explanation_relation_plugin : public relation_plugin { friend class explanation_relation; class join_fn; class project_fn; class rename_fn; class union_fn; class foreign_union_fn; class assignment_filter_fn; class negation_filter_fn; class intersection_filter_fn; bool m_relation_level_explanations; func_decl_ref m_union_decl; vector > m_pool; app * mk_union(app * a1, app * a2) { return get_ast_manager().mk_app(m_union_decl, a1, a2); } public: static symbol get_name(bool relation_level) { return symbol(relation_level ? "relation_explanation" : "fact_explanation"); } explanation_relation_plugin(bool relation_level, relation_manager & manager) : relation_plugin(get_name(relation_level), manager), m_relation_level_explanations(relation_level), m_union_decl(mk_explanations::get_union_decl(get_context()), get_ast_manager()) {} ~explanation_relation_plugin() { for (unsigned i = 0; i < m_pool.size(); ++i) { for (unsigned j = 0; j < m_pool[i].size(); ++j) { dealloc(m_pool[i][j]); } } } virtual bool can_handle_signature(const relation_signature & s) { unsigned n=s.size(); for (unsigned i=0; i(relation_base::get_plugin()); } virtual void to_formula(expr_ref& fml) const { ast_manager& m = fml.get_manager(); fml = m.mk_eq(m.mk_var(0, m.get_sort(m_data[0])), m_data[0]); } bool is_undefined(unsigned col_idx) const { return m_data[col_idx]==0; } bool no_undefined() const { if (empty()) { return true; } unsigned n = get_signature().size(); for (unsigned i=0; i(get_plugin().mk_empty(get_signature())); res->m_empty = m_empty; SASSERT(res->m_data.empty()); res->m_data.append(m_data); return res; } virtual relation_base * complement(func_decl* pred) const { explanation_relation * res = static_cast(get_plugin().mk_empty(get_signature())); if (empty()) { res->set_undefined(); } return res; } void display_explanation(app * expl, std::ostream & out) const { if (expl) { //TODO: some nice explanation output ast_smt_pp pp(get_plugin().get_ast_manager()); pp.display_expr_smt2(out, expl); } else { out << ""; } } virtual void display(std::ostream & out) const { if (empty()) { out << "\n"; return; } unsigned sz = get_signature().size(); for (unsigned i=0; i s.size() && !m_pool[s.size()].empty()) { explanation_relation* r = m_pool[s.size()].back(); m_pool[s.size()].pop_back(); r->m_empty = true; r->m_data.reset(); return r; } return alloc(explanation_relation, *this, s); } void explanation_relation_plugin::recycle(explanation_relation* r) { relation_signature const& sig = r->get_signature(); if (m_pool.size() <= sig.size()) { m_pool.resize(sig.size()+1); } m_pool[sig.size()].push_back(r); } class explanation_relation_plugin::join_fn : public convenient_relation_join_fn { public: join_fn(const relation_signature & sig1, const relation_signature & sig2) : convenient_relation_join_fn(sig1, sig2, 0, 0, 0) {} virtual relation_base * operator()(const relation_base & r1_0, const relation_base & r2_0) { const explanation_relation & r1 = static_cast(r1_0); const explanation_relation & r2 = static_cast(r2_0); explanation_relation_plugin & plugin = r1.get_plugin(); explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); if (!r1.empty() && !r2.empty()) { res->m_empty = false; SASSERT(res->m_data.empty()); res->m_data.append(r1.m_data); res->m_data.append(r2.m_data); } return res; } }; relation_join_fn * explanation_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (&r1.get_plugin()!=this || &r2.get_plugin()!=this) { return 0; } if (col_cnt!=0) { return 0; } return alloc(join_fn, r1.get_signature(), r2.get_signature()); } class explanation_relation_plugin::project_fn : public convenient_relation_project_fn { public: project_fn(const relation_signature & sig, unsigned col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(sig, col_cnt, removed_cols) {} virtual relation_base * operator()(const relation_base & r0) { const explanation_relation & r = static_cast(r0); explanation_relation_plugin & plugin = r.get_plugin(); explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); if (!r.empty()) { relation_fact proj_data = r.m_data; project_out_vector_columns(proj_data, m_removed_cols); res->assign_data(proj_data); } return res; } }; relation_transformer_fn * explanation_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt, const unsigned * removed_cols) { if (&r.get_plugin()!=this) { return 0; } return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); } class explanation_relation_plugin::rename_fn : public convenient_relation_rename_fn { public: rename_fn(const relation_signature & sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) : convenient_relation_rename_fn(sig, permutation_cycle_len, permutation_cycle) {} virtual relation_base * operator()(const relation_base & r0) { const explanation_relation & r = static_cast(r0); explanation_relation_plugin & plugin = r.get_plugin(); explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); if (!r.empty()) { relation_fact permutated_data = r.m_data; permutate_by_cycle(permutated_data, m_cycle); res->assign_data(permutated_data); } return res; } }; relation_transformer_fn * explanation_relation_plugin::mk_rename_fn(const relation_base & r, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { return alloc(rename_fn, r.get_signature(), permutation_cycle_len, permutation_cycle); } class explanation_relation_plugin::union_fn : public relation_union_fn { scoped_ptr m_delta_union_fun; public: virtual void operator()(relation_base & tgt0, const relation_base & src0, relation_base * delta0) { explanation_relation & tgt = static_cast(tgt0); const explanation_relation & src = static_cast(src0); explanation_relation * delta = delta0 ? static_cast(delta0) : 0; explanation_relation_plugin & plugin = tgt.get_plugin(); if (!src.no_undefined() || !tgt.no_undefined() || (delta && !delta->no_undefined())) { UNREACHABLE(); } if (src.empty()) { return; } if (plugin.m_relation_level_explanations) { tgt.unite_with_data(src.m_data); if (delta) { if (!m_delta_union_fun) { m_delta_union_fun = plugin.get_manager().mk_union_fn(*delta, src); SASSERT(m_delta_union_fun); } (*m_delta_union_fun)(*delta, src); } } else { if (tgt.empty()) { tgt.assign_data(src.m_data); if (delta && delta->empty()) { delta->assign_data(src.m_data); } } } } }; class explanation_relation_plugin::foreign_union_fn : public relation_union_fn { scoped_ptr m_delta_union_fun; public: virtual void operator()(relation_base & tgt0, const relation_base & src, relation_base * delta0) { explanation_relation & tgt = static_cast(tgt0); explanation_relation * delta = delta0 ? static_cast(delta0) : 0; if (src.empty()) { return; } tgt.set_undefined(); if (delta) { delta->set_undefined(); } } }; relation_union_fn * explanation_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || (delta && !check_kind(*delta))) { return 0; } if (!check_kind(src)) { //this is to handle the product relation return alloc(foreign_union_fn); } return alloc(union_fn); } class explanation_relation_plugin::assignment_filter_fn : public relation_mutator_fn { ast_manager & m_manager; var_subst & m_subst; unsigned m_col_idx; app_ref m_new_rule; public: assignment_filter_fn(context & ctx, unsigned col_idx, app_ref new_rule) : m_manager(ctx.get_manager()), m_subst(ctx.get_var_subst()), m_col_idx(col_idx), m_new_rule(new_rule) {} virtual void operator()(relation_base & r0) { explanation_relation & r = static_cast(r0); if (!r.is_undefined(m_col_idx)) { UNREACHABLE(); } unsigned sz = r.get_signature().size(); ptr_vector subst_arg; subst_arg.resize(sz, 0); unsigned ofs = sz-1; for (unsigned i=0; iget_arg(0); expr * arg2 = cond->get_arg(1); if (is_var(arg2)) { std::swap(arg1, arg2); } if (!is_var(arg1) || !is_app(arg2)) { return 0; } var * col_var = to_var(arg1); app * new_rule = to_app(arg2); if (!get_context().get_decl_util().is_rule_sort(col_var->get_sort())) { return 0; } unsigned col_idx = col_var->get_idx(); return alloc(assignment_filter_fn, get_context(), col_idx, app_ref(new_rule, get_ast_manager())); } class explanation_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn { public: virtual void operator()(relation_base & r, const relation_base & neg) { if (!neg.empty()) { r.reset(); } } }; relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_negation_fn(const relation_base & r, const relation_base & neg, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { if (&r.get_plugin()!=this || &neg.get_plugin()!=this) { return 0; } return alloc(negation_filter_fn); } class explanation_relation_plugin::intersection_filter_fn : public relation_intersection_filter_fn { func_decl_ref m_union_decl; public: intersection_filter_fn(explanation_relation_plugin & plugin) : m_union_decl(plugin.m_union_decl) {} virtual void operator()(relation_base & tgt0, const relation_base & src0) { explanation_relation & tgt = static_cast(tgt0); const explanation_relation & src = static_cast(src0); if (src.empty()) { tgt.reset(); return; } if (tgt.empty()) { return; } unsigned sz = tgt.get_signature().size(); for (unsigned i=0; iget_decl()==m_union_decl.get()) { if (curr_tgt->get_arg(0)==curr_src || curr_tgt->get_arg(1)==curr_src) { tgt.m_data.set(i, curr_src); continue; } } //the intersection is imprecise because we do nothing here, but it is good enough for //the purpose of explanations } } }; relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_intersection_fn( const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt, const unsigned * tgt_cols, const unsigned * src_cols) { if (&tgt.get_plugin()!=this || &src.get_plugin()!=this) { return 0; } //this checks the join is one to one on all columns if (tgt.get_signature()!=src.get_signature() || joined_col_cnt!=tgt.get_signature().size() || !containers_equal(tgt_cols, tgt_cols+joined_col_cnt, src_cols, src_cols+joined_col_cnt)) { return 0; } counter ctr; ctr.count(joined_col_cnt, tgt_cols); if (ctr.get_max_counter_value()>1 || (joined_col_cnt && ctr.get_max_positive()!=joined_col_cnt-1)) { return 0; } return alloc(intersection_filter_fn, *this); } // ----------------------------------- // // mk_explanations // // ----------------------------------- mk_explanations::mk_explanations(context & ctx) : plugin(50000), m_manager(ctx.get_manager()), m_context(ctx), m_decl_util(ctx.get_decl_util()), m_relation_level(ctx.explanations_on_relation_level()), m_pinned(m_manager) { m_e_sort = m_decl_util.mk_rule_sort(); m_pinned.push_back(m_e_sort); relation_manager & rmgr = ctx.get_rel_context()->get_rmanager(); symbol er_symbol = explanation_relation_plugin::get_name(m_relation_level); m_er_plugin = static_cast(rmgr.get_relation_plugin(er_symbol)); if (!m_er_plugin) { m_er_plugin = alloc(explanation_relation_plugin, m_relation_level, rmgr); rmgr.register_plugin(m_er_plugin); if (!m_relation_level) { DEBUG_CODE( finite_product_relation_plugin * dummy; SASSERT(!rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy)); ); rmgr.register_plugin(alloc(finite_product_relation_plugin, *m_er_plugin, rmgr)); } } DEBUG_CODE( if (!m_relation_level) { finite_product_relation_plugin * dummy; SASSERT(rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy)); } ); } func_decl * mk_explanations::get_union_decl(context & ctx) { ast_manager & m = ctx.get_manager(); sort_ref s(ctx.get_decl_util().mk_rule_sort(), m); //can it happen that the function name would collide with some other symbol? //if functions can be overloaded by their ranges, it should be fine. return m.mk_func_decl(symbol("e_union"), s, s, s); } void mk_explanations::assign_rel_level_kind(func_decl * e_decl, func_decl * orig) { SASSERT(m_relation_level); relation_manager & rmgr = m_context.get_rel_context()->get_rmanager(); unsigned sz = e_decl->get_arity(); relation_signature sig; rmgr.from_predicate(e_decl, sig); svector inner_sieve(sz-1, true); inner_sieve.push_back(false); svector expl_sieve(sz-1, false); expl_sieve.push_back(true); sieve_relation_plugin & sieve_plugin = sieve_relation_plugin::get_plugin(rmgr); family_id inner_kind = rmgr.get_requested_predicate_kind(orig); //may be null_family_id family_id inner_sieve_kind = sieve_plugin.get_relation_kind(sig, inner_sieve, inner_kind); family_id expl_kind = m_er_plugin->get_kind(); family_id expl_sieve_kind = sieve_plugin.get_relation_kind(sig, expl_sieve, expl_kind); rel_spec product_spec; product_spec.push_back(inner_sieve_kind); product_spec.push_back(expl_sieve_kind); family_id pred_kind = product_relation_plugin::get_plugin(rmgr).get_relation_kind(sig, product_spec); rmgr.set_predicate_kind(e_decl, pred_kind); } func_decl * mk_explanations::get_e_decl(func_decl * orig_decl) { decl_map::obj_map_entry * e = m_e_decl_map.insert_if_not_there2(orig_decl, 0); if (e->get_data().m_value==0) { relation_signature e_domain; e_domain.append(orig_decl->get_arity(), orig_decl->get_domain()); e_domain.push_back(m_e_sort); func_decl * new_decl = m_context.mk_fresh_head_predicate(orig_decl->get_name(), symbol("expl"), e_domain.size(), e_domain.c_ptr(), orig_decl); m_pinned.push_back(new_decl); e->get_data().m_value = new_decl; if (m_relation_level) { assign_rel_level_kind(new_decl, orig_decl); } } return e->get_data().m_value; } app * mk_explanations::get_e_lit(app * lit, unsigned e_var_idx) { expr_ref_vector args(m_manager); func_decl * e_decl = get_e_decl(lit->get_decl()); args.append(lit->get_num_args(), lit->get_args()); args.push_back(m_manager.mk_var(e_var_idx, m_e_sort)); return m_manager.mk_app(e_decl, args.c_ptr()); } symbol mk_explanations::get_rule_symbol(rule * r) { if (r->name() == symbol::null) { std::stringstream sstm; r->display(m_context, sstm); std::string res = sstm.str(); res = res.substr(0, res.find_last_not_of('\n')+1); return symbol(res.c_str()); } else { return r->name(); } } rule * mk_explanations::get_e_rule(rule * r) { rule_counter ctr; ctr.count_rule_vars(r); unsigned max_var; unsigned next_var = ctr.get_max_positive(max_var) ? (max_var+1) : 0; unsigned head_var = next_var++; app_ref e_head(get_e_lit(r->get_head(), head_var), m_manager); app_ref_vector e_tail(m_manager); svector neg_flags; unsigned pos_tail_sz = r->get_positive_tail_size(); for (unsigned i=0; iget_tail(i), e_var)); neg_flags.push_back(false); } unsigned tail_sz = r->get_tail_size(); for (unsigned i=pos_tail_sz; iget_tail(i)); neg_flags.push_back(r->is_neg_tail(i)); } symbol rule_repr = get_rule_symbol(r); expr_ref_vector rule_expr_args(m_manager); for (unsigned tail_idx=0; tail_idxget_arg(tail->get_num_args()-1)); } else { //this adds argument values and the explanation term //(values will be substituted for variables at runtime by the finite_product_relation) rule_expr_args.append(tail->get_num_args(), tail->get_args()); } } //rule_expr contains rule function with string representation of the rule as symbol and //for each positive uninterpreted tail it contains its argument values and its explanation term expr * rule_expr = m_decl_util.mk_rule(rule_repr, rule_expr_args.size(), rule_expr_args.c_ptr()); app_ref e_record(m_manager.mk_eq(m_manager.mk_var(head_var, m_e_sort), rule_expr), m_manager); e_tail.push_back(e_record); neg_flags.push_back(false); SASSERT(e_tail.size()==neg_flags.size()); return m_context.get_rule_manager().mk(e_head, e_tail.size(), e_tail.c_ptr(), neg_flags.c_ptr()); } void mk_explanations::transform_rules(const rule_set & src, rule_set & dst) { rule_set::iterator rit = src.begin(); rule_set::iterator rend = src.end(); for (; rit!=rend; ++rit) { rule * e_rule = get_e_rule(*rit); dst.add_rule(e_rule); } //add rules that will (for output predicates) copy facts from explained relations back to //the original ones expr_ref_vector lit_args(m_manager); decl_set::iterator pit = src.get_output_predicates().begin(); decl_set::iterator pend = src.get_output_predicates().end(); for (; pit != pend; ++pit) { func_decl * orig_decl = *pit; lit_args.reset(); unsigned arity = orig_decl->get_arity(); for (unsigned i=0; iget_domain(i))); } app_ref orig_lit(m_manager.mk_app(orig_decl, lit_args.c_ptr()), m_manager); app_ref e_lit(get_e_lit(orig_lit, arity), m_manager); app * tail[] = { e_lit.get() }; dst.add_rule(m_context.get_rule_manager().mk(orig_lit, 1, tail, 0)); } } void mk_explanations::translate_rel_level_relation(relation_manager & rmgr, relation_base & orig, relation_base & e_rel) { SASSERT(m_e_fact_relation); SASSERT(e_rel.get_plugin().is_product_relation()); product_relation & prod_rel = static_cast(e_rel); SASSERT(prod_rel.size()==2); SASSERT(prod_rel[0].get_plugin().is_sieve_relation()); SASSERT(prod_rel[1].get_plugin().is_sieve_relation()); sieve_relation * srels[] = { static_cast(&prod_rel[0]), static_cast(&prod_rel[1]) }; if (&srels[0]->get_inner().get_plugin()==m_er_plugin) { std::swap(srels[0], srels[1]); } SASSERT(&srels[0]->get_inner().get_plugin()==&orig.get_plugin()); SASSERT(&srels[1]->get_inner().get_plugin()==m_er_plugin); relation_base & new_orig = srels[0]->get_inner(); explanation_relation & expl_rel = static_cast(srels[1]->get_inner()); { scoped_ptr orig_union_fun = rmgr.mk_union_fn(new_orig, orig); SASSERT(orig_union_fun); (*orig_union_fun)(new_orig, orig); } { scoped_ptr expl_union_fun = rmgr.mk_union_fn(expl_rel, *m_e_fact_relation); SASSERT(expl_union_fun); (*expl_union_fun)(expl_rel, *m_e_fact_relation); } } void mk_explanations::transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst) { if (!m_e_fact_relation) { relation_signature expl_singleton_sig; expl_singleton_sig.push_back(m_e_sort); relation_base * expl_singleton = rmgr.mk_empty_relation(expl_singleton_sig, m_er_plugin->get_kind()); relation_fact es_fact(m_manager); es_fact.push_back(m_decl_util.mk_fact(symbol("fact"))); expl_singleton->add_fact(es_fact); SASSERT(&expl_singleton->get_plugin()==m_er_plugin); m_e_fact_relation = static_cast(expl_singleton); } func_decl_set predicates(m_context.get_predicates()); decl_set::iterator it = predicates.begin(); decl_set::iterator end = predicates.end(); for (; it!=end; ++it) { func_decl * orig_decl = *it; TRACE("dl", tout << mk_pp(orig_decl, m_manager) << "\n";); func_decl * e_decl = get_e_decl(orig_decl); if (!rmgr.try_get_relation(orig_decl) && !src.contains(orig_decl)) { // there are no facts or rules for this predicate continue; } dst.inherit_predicate(src, orig_decl, e_decl); relation_base & orig_rel = rmgr.get_relation(orig_decl); relation_base & e_rel = rmgr.get_relation(e_decl); SASSERT(e_rel.empty()); //the e_rel should be a new relation if (m_relation_level) { translate_rel_level_relation(rmgr, orig_rel, e_rel); } else { scoped_ptr product_fun = rmgr.mk_join_fn(orig_rel, *m_e_fact_relation, 0, 0, 0); SASSERT(product_fun); scoped_rel aux_extended_rel = (*product_fun)(orig_rel, *m_e_fact_relation); TRACE("dl", tout << aux_extended_rel << " " << aux_extended_rel->get_plugin().get_name() << "\n"; tout << e_rel.get_plugin().get_name() << "\n";); scoped_ptr union_fun = rmgr.mk_union_fn(e_rel, *aux_extended_rel); TRACE("dl", tout << union_fun << "\n";); SASSERT(union_fun); (*union_fun)(e_rel, *aux_extended_rel); } } } rule_set * mk_explanations::operator()(rule_set const & source) { if (source.empty()) { return 0; } if (!m_context.generate_explanations()) { return 0; } rule_set * res = alloc(rule_set, m_context); transform_facts(m_context.get_rel_context()->get_rmanager(), source, *res); transform_rules(source, *res); return res; } }; z3-4.4.0/src/muz/rel/dl_check_table.h0000644000175000017500000001203512540347414017106 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_check_table.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2010-11-15 Revision History: --*/ #ifndef _DL_CHECK_TABLE_H_ #define _DL_CHECK_TABLE_H_ #include "dl_base.h" #include "dl_decl_plugin.h" #include "dl_relation_manager.h" namespace datalog { class check_table; class check_table_plugin : public table_plugin { friend class check_table; table_plugin& m_checker; table_plugin& m_tocheck; unsigned m_count; protected: class join_fn; class join_project_fn; class union_fn; class transformer_fn; class rename_fn; class project_fn; class select_equal_and_project_fn; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; class filter_interpreted_and_project_fn; class filter_by_negation_fn; public: check_table_plugin(relation_manager & manager, symbol const& checker, symbol const& tocheck) : table_plugin(symbol("check"), manager), m_checker(*manager.get_table_plugin(checker)), m_tocheck(*manager.get_table_plugin(tocheck)), m_count(0) {} virtual table_base * mk_empty(const table_signature & s); virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols); virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta); virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols); virtual table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, const table_element & value, unsigned col); virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); virtual table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, const unsigned * identical_cols); virtual table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, unsigned col); virtual table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition); virtual table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); virtual table_intersection_filter_fn * mk_filter_by_negation_fn( const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols); virtual bool can_handle_signature(table_signature const& s); private: static check_table& get(table_base& r); static check_table const & get(table_base const& r); static table_base& checker(table_base& r); static table_base const& checker(table_base const& r); static table_base* checker(table_base* r); static table_base const* checker(table_base const* r); static table_base& tocheck(table_base& r); static table_base const& tocheck(table_base const& r); static table_base* tocheck(table_base* r); static table_base const* tocheck(table_base const* r); }; class check_table : public table_base { friend class check_table_plugin; table_base* m_checker; table_base* m_tocheck; check_table(check_table_plugin & p, const table_signature & sig); check_table(check_table_plugin & p, const table_signature & sig, table_base* tocheck, table_base* checker); virtual ~check_table(); bool well_formed() const; public: check_table_plugin & get_plugin() const { return static_cast(table_base::get_plugin()); } virtual bool empty() const; virtual void add_fact(const table_fact & f); virtual void remove_fact(const table_element* fact); virtual bool contains_fact(const table_fact & f) const; virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const; virtual table_base * clone() const; virtual iterator begin() const { SASSERT(well_formed()); return m_tocheck->begin(); } virtual iterator end() const { return m_tocheck->end(); } virtual unsigned get_size_estimate_rows() const { return m_tocheck->get_size_estimate_rows(); } virtual unsigned get_size_estimate_bytes() const { return m_tocheck->get_size_estimate_bytes(); } }; }; #endif /* _DL_CHECK_TABLE_H_ */ z3-4.4.0/src/muz/rel/dl_bound_relation.h0000644000175000017500000001403412540347414017667 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_bound_relation.h Abstract: Basic (strict upper) bound relation. Author: Nikolaj Bjorner (nbjorner) 2010-2-11 Revision History: --*/ #ifndef _DL_BOUND_RELATION_H_ #define _DL_BOUND_RELATION_H_ #include "dl_context.h" #include "dl_relation_manager.h" #include "dl_base.h" #include "uint_set.h" #include "dl_vector_relation.h" #include "dl_interval_relation.h" #include "arith_decl_plugin.h" #include "basic_simplifier_plugin.h" namespace datalog { class bound_relation; class bound_relation_plugin : public relation_plugin { friend class bound_relation; class join_fn; class project_fn; class rename_fn; class union_fn; class union_fn_i; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; class filter_intersection_fn; arith_util m_arith; basic_simplifier_plugin m_bsimp; public: bound_relation_plugin(relation_manager& m); virtual bool can_handle_signature(const relation_signature & s); static symbol get_name() { return symbol("bound_relation"); } virtual relation_base * mk_empty(const relation_signature & s); virtual relation_base * mk_full(func_decl* p, const relation_signature & s); virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols); virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols); virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col); virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); virtual relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { return 0; } #if 0 virtual intersection_filter_fn * mk_filter_by_intersection_fn( const relation_base & t, const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) { return 0; } #endif static bound_relation* get(relation_base* r); private: static bound_relation& get(relation_base& r); static bound_relation const & get(relation_base const& r); static bool is_interval_relation(relation_base const& r); static interval_relation& get_interval_relation(relation_base& r); static interval_relation const& get_interval_relation(relation_base const& r); }; struct uint_set2 { uint_set lt; uint_set le; uint_set2(uint_set2 const& other):lt(other.lt), le(other.le) {} uint_set2() {} bool operator==(const uint_set2& other) const { return other.lt == lt && other.le == le; } bool operator!=(const uint_set2& other) const { return other.lt != lt || other.le != le; } }; inline std::ostream & operator<<(std::ostream & target, const uint_set2 & s) { return target << s.lt << " " << s.le; } class bound_relation_helper { public: static void mk_project_t(uint_set2& t, unsigned_vector const& renaming); }; class bound_relation : public vector_relation { friend class bound_relation_plugin; svector > m_todo; public: bound_relation(bound_relation_plugin& p, relation_signature const& s, bool is_empty); bound_relation& operator=(bound_relation const& other); virtual bool empty() const { return m_empty; } virtual void add_fact(const relation_fact & f); virtual bool contains_fact(const relation_fact & f) const; virtual bound_relation * clone() const; virtual bound_relation * complement(func_decl* p) const; virtual void to_formula(expr_ref& fml) const; bound_relation_plugin& get_plugin() const; void mk_union_i(interval_relation const& src, bound_relation* delta, bool is_widen); void mk_lt(unsigned i, unsigned j); void mk_lt(unsigned i); void mk_le(unsigned i, unsigned j); bool is_lt(unsigned i, unsigned j) const; virtual bool is_precise() const { return false; } private: typedef uint_set2 T; virtual T mk_intersect(T const& t1, T const& t2, bool& is_empty) const; virtual T mk_widen(T const& t1, T const& t2) const; virtual T mk_unite(T const& t1, T const& t2) const; virtual T mk_eq(union_find<> const& old_eqs, union_find<> const& new_eqs, T const& t) const; virtual void mk_rename_elem(T& i, unsigned col_cnt, unsigned const* cycle); virtual bool is_subset_of(T const& t1, T const& t2) const; virtual bool is_full(T const& t) const; virtual bool is_empty(unsigned idx, T const& t) const; virtual void display_index(unsigned idx, T const& t, std::ostream& out) const; void normalize(T const& src, T& dst) const; void normalize(uint_set const& src, uint_set& dst) const; }; }; #endif z3-4.4.0/src/muz/rel/doc.cpp0000644000175000017500000005220312540347414015304 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: doc.cpp Abstract: difference of cubes. Author: Nuno Lopes (a-nlopes) 2013-03-01 Nikolaj Bjorner (nbjorner) 2014-09-15 Revision History: Based on ternary_diff_bitvector by Nuno Lopes. --*/ #include "doc.h" #include "smt_kernel.h" #include "expr_safe_replace.h" #include "smt_params.h" #include "ast_util.h" #include "ast_pp.h" doc_manager::doc_manager(unsigned n): m(n), m_alloc("doc") { m_full = m.allocateX(); } doc_manager::~doc_manager() { m.deallocate(m_full); } doc* doc_manager::allocate() { return allocate(m.allocate()); } doc* doc_manager::allocate1() { return allocate(m.allocate1()); } doc* doc_manager::allocate0() { return allocate(m.allocate0()); } doc* doc_manager::allocateX() { return allocate(m.allocateX()); } doc* doc_manager::allocate(doc const& src) { doc* r = allocate(m.allocate(src.pos())); for (unsigned i = 0; i < src.neg().size(); ++i) { r->neg().push_back(m.allocate(src.neg()[i])); } return r; } doc* doc_manager::allocate(tbv* t) { SASSERT(t); void* mm = m_alloc.allocate(sizeof(doc)); return new (mm) doc(t); } doc* doc_manager::allocate(tbv const& src) { return allocate(m.allocate(src)); } doc* doc_manager::allocate(uint64 n) { return allocate(m.allocate(n)); } doc* doc_manager::allocate(rational const& r) { return allocate(m.allocate(r)); } doc* doc_manager::allocate(uint64 n, unsigned hi, unsigned lo) { return allocate(m.allocate(n, hi, lo)); } doc* doc_manager::allocate(doc const& src, unsigned const* permutation) { doc* r = allocate(m.allocate(src.pos(), permutation)); for (unsigned i = 0; i < src.neg().size(); ++i) { r->neg().push_back(m.allocate(src.neg()[i], permutation)); } return r; } void doc_manager::deallocate(doc* src) { if (!src) return; m.deallocate(&src->pos()); src->neg().reset(m); src->~doc(); m_alloc.deallocate(sizeof(doc), src); } void doc_manager::copy(doc& dst, doc const& src) { m.copy(dst.pos(), src.pos()); dst.neg().reset(m); for (unsigned i = 0; i < src.neg().size(); ++i) { dst.neg().push_back(m.allocate(src.neg()[i])); } } doc& doc_manager::fill0(doc& src) { src.neg().reset(m); m.fill0(src.pos()); return src; } doc& doc_manager::fill1(doc& src) { src.neg().reset(m); m.fill1(src.pos()); return src; } doc& doc_manager::fillX(doc& src) { src.neg().reset(m); m.fillX(src.pos()); return src; } unsigned doc_manager::get_size_estimate_bytes(const doc& d) const { return m.get_size_estimate_bytes(d.pos()) + d.neg().get_size_estimate_bytes(m) + sizeof(doc); } bool doc_manager::set_and(doc& dst, doc const& src) { // (A \ B) & (C \ D) = (A & C) \ (B u D) if (!m.set_and(dst.pos(), src.pos())) return false; dst.neg().intersect(m, dst.pos()); tbv_ref t(m); for (unsigned i = 0; i < src.neg().size(); ++i) { t = m.allocate(src.neg()[i]); if (m.set_and(*t, dst.pos())) { dst.neg().insert(m, t.detach()); } } return fold_neg(dst); } bool doc_manager::set_and(doc& dst, tbv const& src) { // (A \ B) & C = (A & C) \ (B & C) if (!m.set_and(dst.pos(), src)) return false; dst.neg().intersect(m, src); return fold_neg(dst); } bool doc_manager::well_formed(doc const& d) const { if (!m.is_well_formed(d.pos())) return false; for (unsigned i = 0; i < d.neg().size(); ++i) { if (!m.is_well_formed(d.neg()[i])) return false; if (!m.contains(d.pos(), d.neg()[i])) return false; } return true; } bool doc_manager::fold_neg(doc& dst) { start_over: for (unsigned i = 0; i < dst.neg().size(); ++i) { if (m.contains(dst.neg()[i], dst.pos())) return false; unsigned index; unsigned count = diff_by_012(dst.pos(), dst.neg()[i], index); if (count != 2) { if (count == 0) { return false; } else if (count == 3) { dst.neg().erase(tbvm(), i); --i; } else { // count == 1: m.set(dst.pos(), index, neg(dst.neg()[i][index])); dst.neg().intersect(tbvm(), dst.pos()); goto start_over; } } } SASSERT(well_formed(dst)); return true; } unsigned doc_manager::diff_by_012(tbv const& pos, tbv const& neg, unsigned& index) { unsigned n = num_tbits(); unsigned count = 0; for (unsigned i = 0; i < n; ++i) { tbit b1 = pos[i]; tbit b2 = neg[i]; SASSERT(b1 != BIT_z && b2 != BIT_z); if (b1 != b2) { if (count == 1) return 2; if (b1 == BIT_x) { index = i; count = 1; } else if (b2 != BIT_x) { return 3; } } } return count; } void doc_manager::set(doc& d, unsigned idx, tbit value) { m.set(d.pos(), idx, value); for (unsigned i = 0; i < d.neg().size(); ++i) { tbit b = d.neg()[i][idx]; if (b != BIT_x && value != BIT_x && value != b) { d.neg().erase(tbvm(), i); --i; } else { m.set(d.neg()[i], idx, value); } } } // // merge range from [lo:lo+length-1] with each index in equivalence class. // under assumption of equalities and columns that are discarded. // bool doc_manager::merge( doc& d, unsigned lo, unsigned length, subset_ints const& equalities, bit_vector const& discard_cols) { for (unsigned i = 0; i < length; ++i) { unsigned idx = lo + i; if (!merge(d, idx, equalities, discard_cols)) return false; } return true; } bool doc_manager::merge(doc& d, unsigned idx, subset_ints const& equalities, bit_vector const& discard_cols) { unsigned root = equalities.find(idx); idx = root; unsigned num_x = 0; unsigned root1 = root; tbit value = BIT_x; do { switch (d[idx]) { case BIT_0: if (value == BIT_1) return false; value = BIT_0; break; case BIT_1: if (value == BIT_0) return false; value = BIT_1; break; case BIT_x: ++num_x; if (!discard_cols.get(idx)) { root1 = idx; } break; default: UNREACHABLE(); break; } idx = equalities.next(idx); } while (idx != root); TRACE("doc", tout << "num_x: " << num_x << " value: " << value << "\n";); if (num_x == 0) { // nothing to do. } else if (value != BIT_x) { do { if (d[idx] == BIT_x) { set(d, idx, value); } idx = equalities.next(idx); } while (idx != root); } else { bool all_x = true; if (!d.neg().is_empty()) { idx = root; do { for (unsigned i = 0; all_x && i < d.neg().size(); ++i) { all_x = (BIT_x == d.neg()[i][idx]); } idx = equalities.next(idx); } while (idx != root && all_x); } idx = root; do { if ((!discard_cols.get(idx) || !all_x) && idx != root1) { tbv* t = m.allocate(d.pos()); m.set(*t, idx, BIT_0); m.set(*t, root1, BIT_1); d.neg().insert(tbvm(), t); t = m.allocate(d.pos()); m.set(*t, idx, BIT_1); m.set(*t, root1, BIT_0); d.neg().insert(tbvm(), t); } idx = equalities.next(idx); } while (idx != root); } return true; } bool doc_manager::intersect(doc const& A, doc const& B, doc& result) { copy(result, A); return set_and(result, B); } // // 1. If n = 0,1: can project directly. // 2. If tbv_i uses X in all positions with vars or constant where tbv is constant: can project directly. // 3. Perform resolution on remaining tbv_i // // tbv & ~tbv1 & ~tbv2 & .. & ~tbv_n // Semantics of ~tbv1 is that it is a clause of literals. // indices where BIT_1 is set are negative. // indices where BIT_0 is set are positive. // doc* doc_manager::project(doc_manager& dstm, bit_vector const& to_delete, doc const& src) { tbv_manager& dstt = dstm.m; tbv_ref t(dstt); t = dstt.project(to_delete, src.pos()); doc* r = dstm.allocate(t.detach()); SASSERT(r); if (src.neg().is_empty()) { return r; } // // A negation can be projected directly if it does not constrain // deleted variables. // tbv_vector todo, new_todo; for (unsigned i = 0; i < src.neg().size(); ++i) { todo.push_back(tbvm().allocate(src.neg()[i])); } unsigned idx; bool done = false; while (!todo.empty() && !done) { switch(pick_resolvent(src.pos(), todo, to_delete, idx)) { case project_is_empty: t = dstt.allocate(r->pos()); r->neg().push_back(t.detach()); done = true; break; case project_monolithic: done = true; break; case project_neg: case project_pos: for (unsigned i = 0; i < todo.size(); ++i) { tbv& tx = *todo[i]; if (tx[idx] == BIT_x) { new_todo.push_back(&tx); } else { m.deallocate(&tx); } } std::swap(new_todo, todo); new_todo.reset(); break; case project_resolve: { utbv pos, neg; for (unsigned i = 0; i < todo.size(); ++i) { tbv& tx = *todo[i]; switch(tx[idx]) { case BIT_x: new_todo.push_back(&tx); break; case BIT_0: neg.push_back(&tx); break; case BIT_1: pos.push_back(&tx); break; default: UNREACHABLE(); break; } } TRACE("doc", tout << "pos: "; for (unsigned i = 0; i < pos.size(); ++i) { tbvm().display(tout, pos[i]) << " "; } tout << "\nneg: "; for (unsigned i = 0; i < neg.size(); ++i) { tbvm().display(tout, neg[i]) << " "; } tout << "\n"; ); SASSERT(pos.size() > 0 && neg.size() > 0); tbv_ref t1(m); for (unsigned j = 0; j < pos.size(); ++j) { for (unsigned k = 0; k < neg.size(); ++k) { t1 = m.allocate(pos[j]); m.set(*t1, idx, BIT_x); if (tbvm().set_and(*t1, neg[k])) { m.set(*t1, idx, BIT_x); new_todo.push_back(t1.detach()); } } } pos.reset(m); neg.reset(m); std::swap(todo, new_todo); new_todo.reset(); break; } case project_done: { for (unsigned i = 0; i < todo.size(); ++i) { t = dstt.project(to_delete, *todo[i]); if (dstt.equals(r->pos(), *t)) { r->neg().reset(dstt); r->neg().push_back(t.detach()); break; } if (r->neg().size() > 0 && dstt.equals(r->neg()[0], *t)) { continue; } r->neg().push_back(t.detach()); } done = true; break; } } } for (unsigned i = 0; i < todo.size(); ++i) { m.deallocate(todo[i]); } return r; } doc* doc_manager::join(const doc& d1, const doc& d2, doc_manager& dm1, const unsigned_vector& cols1, const unsigned_vector& cols2) { doc_ref d(*this, allocateX()); tbv_ref t(m); tbv& pos = d->pos(); utbv& neg = d->neg(); unsigned mid = dm1.num_tbits(); unsigned hi = num_tbits(); m.set(pos, d1.pos(), mid - 1, 0); m.set(pos, d2.pos(), hi - 1, mid); SASSERT(well_formed(*d)); // first fix bits for (unsigned i = 0; i < cols1.size(); ++i) { unsigned idx1 = cols1[i]; unsigned idx2 = mid + cols2[i]; tbit v1 = pos[idx1]; tbit v2 = pos[idx2]; if (v1 == BIT_x) { if (v2 != BIT_x) m.set(pos, idx1, v2); } else if (v2 == BIT_x) { m.set(pos, idx2, v1); } else if (v1 != v2) { // columns don't match return 0; } SASSERT(well_formed(*d)); } // fix equality of don't care columns for (unsigned i = 0; i < cols1.size(); ++i) { unsigned idx1 = cols1[i]; unsigned idx2 = mid + cols2[i]; unsigned v1 = pos[idx1]; unsigned v2 = pos[idx2]; if (v1 == BIT_x && v2 == BIT_x) { // add to subtracted TBVs: 1xx0 and 0xx1 t = m.allocate(pos); m.set(*t, idx1, BIT_0); m.set(*t, idx2, BIT_1); neg.push_back(t.detach()); t = m.allocate(pos); m.set(*t, idx1, BIT_1); m.set(*t, idx2, BIT_0); neg.push_back(t.detach()); } SASSERT(well_formed(*d)); } // handle subtracted TBVs: 1010 -> 1010xxx for (unsigned i = 0; i < d1.neg().size(); ++i) { t = m.allocateX(); m.set(*t, d1.neg()[i], mid - 1, 0); if (m.set_and(*t, pos)) neg.push_back(t.detach()); SASSERT(well_formed(*d)); } for (unsigned i = 0; i < d2.neg().size(); ++i) { t = m.allocateX(); m.set(*t, d2.neg()[i], hi - 1, mid); if (m.set_and(*t, pos)) neg.push_back(t.detach()); SASSERT(well_formed(*d)); } SASSERT(well_formed(*d)); return d.detach(); } doc_manager::project_action_t doc_manager::pick_resolvent( tbv const& pos, tbv_vector const& neg, bit_vector const& to_delete, unsigned& idx) { if (neg.empty()) return project_done; for (unsigned j = 0; j < neg.size(); ++j) { if (m.equals(pos, *neg[j])) return project_is_empty; } unsigned best_pos = UINT_MAX; unsigned best_neg = UINT_MAX; unsigned best_idx = UINT_MAX; for (unsigned i = 0; i < num_tbits(); ++i) { if (!to_delete.get(i)) continue; if (pos[i] != BIT_x) continue; unsigned num_pos = 0, num_neg = 0; tbit b1 = (*neg[0])[i]; if (b1 == BIT_0) num_neg++; if (b1 == BIT_1) num_pos++; bool monolithic = true; for (unsigned j = 1; j < neg.size(); ++j) { tbit b2 = (*neg[j])[i]; if (b1 != b2) { monolithic = false; } if (b2 == BIT_0) num_neg++; if (b2 == BIT_1) num_pos++; } if (monolithic && b1 != BIT_x) { idx = i; return project_monolithic; } if (monolithic && b1 == BIT_x) { continue; } SASSERT(!monolithic); if (num_pos == 0) { SASSERT(num_neg > 0); idx = i; return project_neg; } if (num_neg == 0) { SASSERT(num_pos > 0); idx = i; return project_pos; } if ((best_pos >= num_pos && best_neg >= num_neg) || num_neg == 1 || num_pos == 1) { best_pos = num_pos; best_neg = num_neg; best_idx = i; } } if (best_idx != UINT_MAX) { idx = best_idx; return project_resolve; } else { return project_done; } } void doc_manager::complement(doc const& src, doc_vector& result) { result.reset(); if (is_full(src)) { return; } doc* r = allocateX(); r->neg().push_back(m.allocate(src.pos())); result.push_back(r); for (unsigned i = 0; i < src.neg().size(); ++i) { result.push_back(allocate(src.neg()[i])); } } // (A \ {A1}) \ (B \ {B1}) // (A & !A1 & & !B) | (A & B1 & !A1) // A \ {A1 u B} u (A & B1) \ {A1} void doc_manager::subtract(doc const& A, doc const& B, doc_vector& result) { doc_ref r(*this); tbv_ref t(m); r = allocate(A); t = m.allocate(B.pos()); if (m.set_and(*t, A.pos())) { r->neg().insert(m, t.detach()); } if (fold_neg(*r)) result.push_back(r.detach()); for (unsigned i = 0; i < B.neg().size(); ++i) { r = allocate(A); if (set_and(*r, B.neg()[i])) { result.push_back(r.detach()); } } } bool doc_manager::equals(doc const& a, doc const& b) const { if (!m.equals(a.pos(), b.pos())) return false; if (a.neg().size() != b.neg().size()) return false; for (unsigned i = 0; i < a.neg().size(); ++i) { if (!m.equals(a.neg()[i], b.neg()[i])) return false; } return true; } bool doc_manager::is_full(doc const& src) const { return src.neg().is_empty() && m.equals(src.pos(), *m_full); } bool doc_manager::is_empty_complete(ast_manager& m, doc const& src) { if (src.neg().size() == 0) return false; smt_params fp; smt::kernel s(m, fp); expr_ref fml = to_formula(m, src); s.assert_expr(fml); lbool res = s.check(); if (res == l_true) { return false; } SASSERT(res == l_false); return true; } unsigned doc_manager::hash(doc const& src) const { unsigned r = 0; for (unsigned i = 0; i < src.neg().size(); ++i) { r = 2*r + m.hash(src.neg()[i]); } return r + m.hash(src.pos()); } // approximation // A \ (A1 u A2) contains B \ (B1 u B2) // if // A contains B // B1 contains A1 or B2 contains A1 // B1 contains A2 or B2 contains A2 bool doc_manager::contains(doc const& a, doc const& b) const { if (!m.contains(a.pos(), b.pos())) return false; for (unsigned i = 0; i < a.neg().size(); ++i) { bool found = false; for (unsigned j = 0; !found && j < b.neg().size(); ++j) { found = m.contains(b.neg()[j],a.neg()[i]); } if (!found) return false; } return true; } bool doc_manager::contains(doc const& a, unsigned_vector const& colsa, doc const& b, unsigned_vector const& colsb) const { if (!m.contains(a.pos(), colsa, b.pos(), colsb)) return false; for (unsigned i = 0; i < a.neg().size(); ++i) { bool found = false; for (unsigned j = 0; !found && j < b.neg().size(); ++j) { found = m.contains(b.neg()[j], colsb, a.neg()[i], colsa); } if (!found) return false; } return true; } std::ostream& doc_manager::display(std::ostream& out, doc const& b) const { if (num_tbits() == 0) return out << "[]"; return display(out, b, num_tbits()-1, 0); } std::ostream& doc_manager::display(std::ostream& out, doc const& b, unsigned hi, unsigned lo) const { m.display(out, b.pos(), hi, lo); if (b.neg().is_empty()) return out; out << " \\ "; b.neg().display(m, out, hi, lo); return out; } void doc_manager::verify_project(ast_manager& m, doc_manager& dstm, bit_vector const& to_delete, doc const& src, doc const& dst) { expr_ref fml1 = to_formula(m, src); expr_ref fml2 = dstm.to_formula(m, dst); project_rename(fml2, to_delete); project_expand(fml1, to_delete); check_equiv(m, fml1, fml2); } void doc_manager::check_equiv(ast_manager& m, expr* fml1, expr* fml2) { smt_params fp; smt::kernel solver(m, fp); expr_ref fml(m); fml = m.mk_not(m.mk_eq(fml1, fml2)); solver.assert_expr(fml); lbool res = solver.check(); if (res != l_false) { TRACE("doc", tout << mk_pp(fml1, m) << "\n"; tout << mk_pp(fml2, m) << "\n"; ); UNREACHABLE(); throw 0; } SASSERT(res == l_false); } expr_ref doc_manager::to_formula(ast_manager& m, doc const& src) { expr_ref result(m); expr_ref_vector conj(m); conj.push_back(tbvm().to_formula(m, src.pos())); for (unsigned i = 0; i < src.neg().size(); ++i) { conj.push_back(m.mk_not(tbvm().to_formula(m, src.neg()[i]))); } result = mk_and(m, conj.size(), conj.c_ptr()); return result; } void doc_manager::project_expand(expr_ref& fml, bit_vector const& to_delete) { ast_manager& m = fml.get_manager(); expr_ref tmp1(m), tmp2(m); for (unsigned i = 0; i < num_tbits(); ++i) { if (to_delete.get(i)) { expr_safe_replace rep1(m), rep2(m); rep1.insert(tbvm().mk_var(m, i), m.mk_true()); rep1(fml, tmp1); rep2.insert(tbvm().mk_var(m, i), m.mk_false()); rep2(fml, tmp2); if (tmp1 == tmp2) { fml = tmp1; } else { fml = m.mk_or(tmp1, tmp2); } } } } void doc_manager::project_rename(expr_ref& fml, bit_vector const& to_delete) { ast_manager& m = fml.get_manager(); expr_safe_replace rep(m); for (unsigned i = 0, j = 0; i < num_tbits(); ++i) { if (!to_delete.get(i)) { rep.insert(tbvm().mk_var(m, j), tbvm().mk_var(m, i)); ++j; } } rep(fml); } z3-4.4.0/src/muz/rel/dl_instruction.h0000644000175000017500000003204112540347414017242 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_instruction.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #ifndef _DL_INSTRUCTION_H_ #define _DL_INSTRUCTION_H_ #include #include #include #include "ast.h" #include "vector.h" #include "dl_base.h" #include "dl_costs.h" #include "dl_context.h" namespace datalog { class execution_context; class instruction_block; class rel_context; inline void check_overflow(unsigned i) { if (i == UINT_MAX) { throw out_of_memory_error(); } } // ----------------------------------- // // execution_context // // ----------------------------------- class execution_context { public: typedef relation_base * reg_type; typedef vector reg_vector; typedef unsigned reg_idx; /** \brief A register number that should never be referenced to. Can stand e.g. for a tail table in a rule with no tail. */ static const reg_idx void_register = UINT_MAX; private: typedef u_map reg_annotations; context & m_context; reg_vector m_registers; reg_annotations m_reg_annotation; stopwatch * m_stopwatch; unsigned m_timelimit_ms; //zero means no limit public: execution_context(context & context); ~execution_context(); void reset(); rel_context & get_rel_context(); rel_context const & get_rel_context() const; void set_timelimit(unsigned time_in_ms); void reset_timelimit(); bool should_terminate(); struct stats { unsigned m_join; unsigned m_project; unsigned m_filter; unsigned m_total; unsigned m_unary_singleton; unsigned m_filter_by_negation; unsigned m_select_equal_project; unsigned m_join_project; unsigned m_project_rename; unsigned m_union; unsigned m_filter_interp_project; unsigned m_filter_id; unsigned m_filter_eq; unsigned m_min; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; stats m_stats; void collect_statistics(statistics& st) const; /** \brief Return reference to \c i -th register that contains pointer to a relation. If register contains zero, it should be treated as if it contains an empty relation. */ reg_type reg(reg_idx i) const { if (i >= m_registers.size()) { return 0; } return m_registers[i]; } /** \brief Return value of the register and assign zero into it place. */ reg_type release_reg(reg_idx i) { SASSERT(i < m_registers.size()); SASSERT(m_registers[i]); reg_type res = m_registers[i]; m_registers[i] = 0; return res; } /** \brief Assign value to a register. If it was non-empty, deallocate its content first. */ void set_reg(reg_idx i, reg_type val) { if (i >= m_registers.size()) { check_overflow(i); m_registers.resize(i+1,0); } if (m_registers[i]) { m_registers[i]->deallocate(); } m_registers[i] = val; } void make_empty(reg_idx i) { if (reg(i)) { set_reg(i, 0); } } unsigned register_count() const { return m_registers.size(); } bool get_register_annotation(reg_idx reg, std::string & res) const { return m_reg_annotation.find(reg, res); } void set_register_annotation(reg_idx reg, std::string str) { m_reg_annotation.insert(reg, str); } void report_big_relations(unsigned threshold, std::ostream & out) const; }; // ----------------------------------- // // instruction // // ----------------------------------- /** \brief Base class for instructions used in datalog saturation. A relation in a register is owned by that register and is not referenced from anywhere else. Instructions that move context of one register to another leave the source register empty and deallocate the previous content of the target register. */ class instruction : public accounted_object { typedef u_map fn_cache; fn_cache m_fn_cache; static const int rk_encode_base = 1024; inline static unsigned encode_kind(family_id k) { SASSERT(k bool find_fn(const relation_base & r, T* & result) const { return m_fn_cache.find(encode_kind(r.get_kind()), reinterpret_cast(result)); } template bool find_fn(const relation_base & r1, const relation_base & r2, T*& result) const { return m_fn_cache.find(encode_kinds(r1.get_kind(), r2.get_kind()), reinterpret_cast(result)); } template bool find_fn(const relation_base & r1, const relation_base & r2, const relation_base & r3, T * & result) const { return m_fn_cache.find(encode_kinds(r1.get_kind(), r2.get_kind(), r3.get_kind()), reinterpret_cast(result)); } void store_fn(const relation_base & r, base_relation_fn * fn) { m_fn_cache.insert(encode_kind(r.get_kind()), fn); } void store_fn(const relation_base & r1, const relation_base & r2, base_relation_fn * fn) { m_fn_cache.insert(encode_kinds(r1.get_kind(), r2.get_kind()), fn); } void store_fn(const relation_base & r1, const relation_base & r2, const relation_base & r3, base_relation_fn * fn) { m_fn_cache.insert(encode_kinds(r1.get_kind(), r2.get_kind(), r3.get_kind()), fn); } /** Process not only costs associated with the current instruction, but in case of block instructions, process also costs associated with its child instructions. */ virtual void process_all_costs(); /** \brief Output one line header of the current instruction. The newline character at the end should not be printed. */ virtual void display_head_impl(execution_context const & ctx, std::ostream & out) const { out << ""; } /** \brief If relevant, output the body of the current instruction. Each line must be prepended by \c indentation and ended by a newline character. */ virtual void display_body_impl(execution_context const & ctx, std::ostream & out, std::string indentation) const {} void log_verbose(execution_context& ctx); public: typedef execution_context::reg_type reg_type; typedef execution_context::reg_idx reg_idx; virtual ~instruction(); virtual bool perform(execution_context & ctx) = 0; virtual void make_annotations(execution_context & ctx) = 0; void display(execution_context const& ctx, std::ostream & out) const { display_indented(ctx, out, ""); } void display_indented(execution_context const & ctx, std::ostream & out, std::string indentation) const; static instruction * mk_load(ast_manager & m, func_decl * pred, reg_idx tgt); /** \brief The store operation moves the relation from a register into the context. The register is set to zero after the operation. */ static instruction * mk_store(ast_manager & m, func_decl * pred, reg_idx src); static instruction * mk_dealloc(reg_idx reg); //maybe not necessary static instruction * mk_clone(reg_idx from, reg_idx to); static instruction * mk_move(reg_idx from, reg_idx to); /** \brief Return instruction that performs \c body as long as at least one register in \c control_regs contains non-empty relation. The instruction object takes over the ownership of the \c body object. */ static instruction * mk_while_loop(unsigned control_reg_cnt, const reg_idx * control_regs, instruction_block * body); static instruction * mk_join(reg_idx rel1, reg_idx rel2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, reg_idx result); static instruction * mk_filter_equal(ast_manager & m, reg_idx reg, const relation_element & value, unsigned col); static instruction * mk_filter_identical(reg_idx reg, unsigned col_cnt, const unsigned * identical_cols); static instruction * mk_filter_interpreted(reg_idx reg, app_ref & condition); static instruction * mk_filter_interpreted_and_project(reg_idx src, app_ref & condition, unsigned col_cnt, const unsigned * removed_cols, reg_idx result); static instruction * mk_union(reg_idx src, reg_idx tgt, reg_idx delta); static instruction * mk_widen(reg_idx src, reg_idx tgt, reg_idx delta); static instruction * mk_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols, reg_idx tgt); static instruction * mk_join_project(reg_idx rel1, reg_idx rel2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, reg_idx result); static instruction * mk_min(reg_idx source, reg_idx target, const unsigned_vector & group_by_cols, const unsigned min_col); static instruction * mk_rename(reg_idx src, unsigned cycle_len, const unsigned * permutation_cycle, reg_idx tgt); static instruction * mk_filter_by_negation(reg_idx tgt, reg_idx neg_rel, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); static instruction * mk_select_equal_and_project(ast_manager & m, reg_idx src, const relation_element & value, unsigned col, reg_idx result); static instruction * mk_unary_singleton(ast_manager & m, func_decl* pred, const relation_sort & s, const relation_element & val, reg_idx tgt); static instruction * mk_total(const relation_signature & sig, func_decl* pred, reg_idx tgt); /** \brief The mark_saturated instruction marks a relation as saturated, so that after next restart it does not have to be part of the saturation again. */ static instruction * mk_mark_saturated(ast_manager & m, func_decl * pred); static instruction * mk_assert_signature(const relation_signature & s, reg_idx tgt); void collect_statistics(statistics& st) const; }; // ----------------------------------- // // instruction_block // // ----------------------------------- class instruction_block { public: struct instruction_observer { virtual ~instruction_observer() {} virtual void notify(instruction * i) {} }; private: typedef ptr_vector instr_seq_type; instr_seq_type m_data; instruction_observer* m_observer; public: instruction_block() : m_observer(0) {} ~instruction_block(); void reset(); void push_back(instruction * i) { m_data.push_back(i); if (m_observer) { m_observer->notify(i); } } void set_observer(instruction_observer * o) { SASSERT(o==0 || m_observer==0); m_observer = o; } void collect_statistics(statistics& st) const; /** \brief Perform instructions in the block. If the run was interrupted before completion, return false; otherwise return true. The execution can terminate before completion if the function \c execution_context::should_terminate() returns true. */ bool perform(execution_context & ctx) const; void process_all_costs(); void make_annotations(execution_context & ctx); void display(execution_context const & ctx, std::ostream & out) const { display_indented(ctx, out, ""); } void display_indented(execution_context const & ctx, std::ostream & out, std::string indentation) const; unsigned num_instructions() const { return m_data.size(); } }; }; #endif /* _DL_INSTRUCTION_H_ */ z3-4.4.0/src/muz/rel/dl_mk_similarity_compressor.cpp0000644000175000017500000004573512540347414022363 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_similarity_compressor.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-10-22. Revision History: --*/ #include #include #include"dl_mk_similarity_compressor.h" #include"dl_relation_manager.h" namespace datalog { mk_similarity_compressor::mk_similarity_compressor(context & ctx) : plugin(5000), m_context(ctx), m_manager(ctx.get_manager()), m_threshold_count(ctx.similarity_compressor_threshold()), m_result_rules(ctx.get_rule_manager()), m_modified(false), m_pinned(m_manager) { SASSERT(m_threshold_count>1); } void mk_similarity_compressor::reset() { m_rules.reset(); m_result_rules.reset(); m_pinned.reset(); } /** Allows to traverse head and positive tails in a single for loop starting from -1 */ static app * get_by_tail_index(rule * r, int idx) { if (idx < 0) { return r->get_head(); } SASSERT(idx < static_cast(r->get_positive_tail_size())); return r->get_tail(idx); } template static int aux_compare(T a, T b) { return (a>b) ? 1 : ( (a==b) ? 0 : -1); } template static int aux_compare(T* a, T* b); static int compare_var_args(app* t1, app* t2) { SASSERT(t1->get_num_args()==t2->get_num_args()); int res; unsigned n = t1->get_num_args(); for (unsigned i = 0; i < n; i++) { expr * a1 = t1->get_arg(i); expr * a2 = t2->get_arg(i); res = aux_compare(is_var(a1), is_var(a2)); if (res != 0) { return res; } if (is_var(a1)) { res = aux_compare(to_var(a1)->get_idx(), to_var(a2)->get_idx()); if (res != 0) { return res; } } } return 0; } static int compare_args(app* t1, app* t2, int & skip_countdown) { SASSERT(t1->get_num_args()==t2->get_num_args()); int res; unsigned n = t1->get_num_args(); for (unsigned i=0; iget_arg(i))) { SASSERT(t1->get_arg(i) == t2->get_arg(i)); continue; } if ((skip_countdown--) == 0) { continue; } res = aux_compare(t1->get_arg(i)->get_id(), t2->get_arg(i)->get_id()); if (res!=0) { return res; } } return 0; } /** \brief Return 0 if r1 and r2 could be similar. If the rough similarity equaivelance class of r1 is greater than the one of r2, return 1; otherwise return -1. Two rules are in the same rough similarity class if they differ only in constant arguments of positive uninterpreted predicates. */ static int rough_compare(rule * r1, rule * r2) { int res = aux_compare(r1->get_tail_size(), r2->get_tail_size()); if (res!=0) { return res; } res = aux_compare(r1->get_uninterpreted_tail_size(), r2->get_uninterpreted_tail_size()); if (res!=0) { return res; } res = aux_compare(r1->get_positive_tail_size(), r2->get_positive_tail_size()); if (res!=0) { return res; } int pos_tail_sz = r1->get_positive_tail_size(); for (int i=-1; iget_decl()->get_id(), t2->get_decl()->get_id()); if (res!=0) { return res; } res = compare_var_args(t1, t2); if (res!=0) { return res; } } unsigned tail_sz = r1->get_tail_size(); for (unsigned i=pos_tail_sz; iget_tail(i)->get_id(), r2->get_tail(i)->get_id()); if (res!=0) { return res; } } return 0; } /** \c r1 and \c r2 must be equal according to the \c rough_compare function for this function to be called. */ static int total_compare(rule * r1, rule * r2, int skipped_arg_index = INT_MAX) { SASSERT(rough_compare(r1, r2)==0); int pos_tail_sz = r1->get_positive_tail_size(); for (int i=-1; i info_vector; static void collect_const_indexes(app * t, int tail_index, info_vector & res) { unsigned n = t->get_num_args(); for (unsigned i=0; iget_arg(i))) { continue; } res.push_back(const_info(tail_index, i)); } } static void collect_const_indexes(rule * r, info_vector & res) { collect_const_indexes(r->get_head(), -1, res); unsigned pos_tail_sz = r->get_positive_tail_size(); for (unsigned i=0; iget_tail(i), i, res); } } template static void collect_orphan_consts(rule * r, const info_vector & const_infos, T & tgt) { unsigned const_cnt = const_infos.size(); tgt.reset(); for (unsigned i=0; iget_arg(inf.arg_index()))); SASSERT(tgt.back()->get_num_args()==0); } } template static void collect_orphan_sorts(rule * r, const info_vector & const_infos, T & tgt) { unsigned const_cnt = const_infos.size(); tgt.reset(); for (unsigned i=0; iget_decl()->get_domain(inf.arg_index())); } } /** \brief From the \c tail_indexes and \c arg_indexes remove elements corresponding to constants that are the same in rules \c *first ... \c *(after_last-1). */ static void remove_stable_constants(rule_vector::iterator first, rule_vector::iterator after_last, info_vector & const_infos) { SASSERT(after_last-first>1); unsigned const_cnt = const_infos.size(); ptr_vector vals; rule * r = *(first++); collect_orphan_consts(r, const_infos, vals); SASSERT(vals.size()==const_cnt); rule_vector::iterator it = first; for (; it!=after_last; ++it) { for (unsigned i=0; iget_arg(const_infos[i].arg_index())); if (vals[i]!=val) { vals[i] = 0; } } } unsigned removed_cnt = 0; for (unsigned i=0; i vals; ptr_vector sorts; rule * r = *(first++); collect_orphan_consts(r, const_infos, vals); collect_orphan_sorts(r, const_infos, sorts); SASSERT(vals.size()==const_cnt); vector possible_parents(const_cnt); for (unsigned i=1; iget_head()->get_num_args() - count_variable_arguments(r->get_head()); unsigned pos_tail_sz = r->get_positive_tail_size(); for (unsigned i=0; iget_tail(i)->get_num_args() - count_variable_arguments(r->get_tail(i)); } return res; } static bool initial_comparator(rule * r1, rule * r2) { int res = rough_compare(r1, r2); if (res!=0) { return res>0; } return total_compare(r1, r2)>0; } class arg_ignoring_comparator { unsigned m_ignored_index; public: arg_ignoring_comparator(unsigned ignored_index) : m_ignored_index(ignored_index) {} bool operator()(rule * r1, rule * r2) const { return total_compare(r1, r2, m_ignored_index)>0; } bool eq(rule * r1, rule * r2) const { return total_compare(r1, r2, m_ignored_index)==0; } }; void mk_similarity_compressor::merge_class(rule_vector::iterator first, rule_vector::iterator after_last) { SASSERT(after_last-first>1); info_vector const_infos; rule * r = *first; //an arbitrary representative of the class collect_const_indexes(r, const_infos); remove_stable_constants(first, after_last, const_infos); unsigned const_cnt = const_infos.size(); SASSERT(const_cnt>0); detect_equal_constants(first, after_last, const_infos); //The aux relation contains column for each constant which does not have an earlier constant //that it is equal to (i.e. only has no parent) ptr_vector aux_domain; collect_orphan_sorts(r, const_infos, aux_domain); func_decl* head_pred = r->get_decl(); symbol const& name_prefix = head_pred->get_name(); std::string name_suffix = "sc_" + to_string(const_cnt); func_decl * aux_pred = m_context.mk_fresh_head_predicate(name_prefix, symbol(name_suffix.c_str()), aux_domain.size(), aux_domain.c_ptr(), head_pred); m_pinned.push_back(aux_pred); relation_fact val_fact(m_manager, const_cnt); rule_vector::iterator it = first; for (; it!=after_last; ++it) { collect_orphan_consts(*it, const_infos, val_fact); m_context.add_fact(aux_pred, val_fact); } m_context.get_rel_context()->get_rmanager().mark_saturated(aux_pred); app * new_head = r->get_head(); ptr_vector new_tail; svector new_negs; unsigned tail_sz = r->get_tail_size(); for (unsigned i=0; iget_tail(i)); new_negs.push_back(r->is_neg_tail(i)); } rule_counter ctr; ctr.count_rule_vars(r); unsigned max_var_idx, new_var_idx_base; if (ctr.get_max_positive(max_var_idx)) { new_var_idx_base = max_var_idx+1; } else { new_var_idx_base = 0; } ptr_vector const_vars; //variables at indexes of their corresponding constants expr_ref_vector aux_vars(m_manager); //variables as arguments for the auxiliary predicate unsigned aux_column_index = 0; for (unsigned i=0; i mod_args(mod_tail->get_num_args(), mod_tail->get_args()); for (; iget_decl(), mod_args.c_ptr()); m_pinned.push_back(upd_tail); mod_tail = upd_tail; } app_ref aux_tail(m_manager.mk_app(aux_pred, aux_vars.c_ptr()), m_manager); new_tail.push_back(aux_tail); new_negs.push_back(false); rule * new_rule = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(), new_negs.c_ptr()); m_result_rules.push_back(new_rule); //TODO: allow for a rule to have multiple parent objects new_rule->set_accounting_parent_object(m_context, r); m_modified = true; } void mk_similarity_compressor::process_class(rule_set const& source, rule_vector::iterator first, rule_vector::iterator after_last) { SASSERT(first!=after_last); //remove duplicates { rule_vector::iterator it = first; rule_vector::iterator prev = it; ++it; while(it!=after_last) { if (it!=after_last && total_compare(*prev, *it)==0) { --after_last; std::swap(*it, *after_last); m_modified = true; } else { prev = it; ++it; } } } SASSERT(first!=after_last); unsigned const_cnt = get_constant_count(*first); #if 0 for (unsigned ignored_index=0; ignored_indexm_threshold_count) { merge_class(grp_begin, it); //group was processed, so we remove it from the class if (it==after_last) { after_last=grp_begin; it=after_last; } else { while(it!=grp_begin) { std::swap(*--it, *--after_last); } } } grp_begin = it; grp_size = 0; } } } #endif //TODO: compress also rules with pairs (or tuples) of equal constants #if 1 if (const_cnt>0 && !source.is_output_predicate((*first)->get_decl())) { unsigned rule_cnt = static_cast(after_last-first); if (rule_cnt>m_threshold_count) { merge_class(first, after_last); return; } } #endif //put rules which weren't merged into result rule_vector::iterator it = first; for (; it!=after_last; ++it) { m_result_rules.push_back(*it); } } rule_set * mk_similarity_compressor::operator()(rule_set const & source) { // TODO mc m_modified = false; unsigned init_rule_cnt = source.get_num_rules(); SASSERT(m_rules.empty()); for (unsigned i=0; i(0); if (m_modified) { result = alloc(rule_set, m_context); unsigned fin_rule_cnt = m_result_rules.size(); for (unsigned i=0; iadd_rule(m_result_rules.get(i)); } result->inherit_predicates(source); } reset(); return result; } }; z3-4.4.0/src/muz/rel/dl_bound_relation.cpp0000644000175000017500000005736512540347414020240 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_bound_relation.cpp Abstract: Basic (strict upper) bound relation. Author: Nikolaj Bjorner (nbjorner) 2010-2-11 Revision History: --*/ #include "dl_bound_relation.h" #include "debug.h" #include "ast_pp.h" namespace datalog { bound_relation_plugin::bound_relation_plugin(relation_manager& m): relation_plugin(bound_relation_plugin::get_name(), m), m_arith(get_ast_manager()), m_bsimp(get_ast_manager()) { } bool bound_relation_plugin::can_handle_signature(const relation_signature & sig) { for (unsigned i = 0; i < sig.size(); ++i) { if (!m_arith.is_int(sig[i]) && !m_arith.is_real(sig[i])) { return false; } } return true; } bound_relation& bound_relation_plugin::get(relation_base& r) { return dynamic_cast(r); } bound_relation const & bound_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); } bound_relation* bound_relation_plugin::get(relation_base* r) { return dynamic_cast(r); } bool bound_relation_plugin::is_interval_relation(relation_base const& r) { return symbol("interval_relation") == r.get_plugin().get_name(); } interval_relation& bound_relation_plugin::get_interval_relation(relation_base& r) { SASSERT(is_interval_relation(r)); return dynamic_cast(r); } interval_relation const& bound_relation_plugin::get_interval_relation(relation_base const& r) { SASSERT(is_interval_relation(r)); return dynamic_cast(r); } relation_base * bound_relation_plugin::mk_empty(const relation_signature & s) { return alloc(bound_relation, *this, s, true); } relation_base * bound_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { return alloc(bound_relation, *this, s, false); } class bound_relation_plugin::join_fn : public convenient_relation_join_fn { public: join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2) { } virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { bound_relation const& r1 = get(_r1); bound_relation const& r2 = get(_r2); bound_relation_plugin& p = r1.get_plugin(); bound_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); return result; } }; relation_join_fn * bound_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(r1) || !check_kind(r2)) { return 0; } return alloc(join_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2); } class bound_relation_plugin::project_fn : public convenient_relation_project_fn { public: project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { } virtual relation_base * operator()(const relation_base & _r) { bound_relation const& r = get(_r); bound_relation_plugin& p = r.get_plugin(); bound_relation* result = get(p.mk_full(0, get_result_signature())); result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); return result; } }; relation_transformer_fn * bound_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt, const unsigned * removed_cols) { return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); } class bound_relation_plugin::rename_fn : public convenient_relation_rename_fn { public: rename_fn(const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) { } virtual relation_base * operator()(const relation_base & _r) { bound_relation const& r = get(_r); bound_relation_plugin& p = r.get_plugin(); bound_relation* result = get(p.mk_full(0, get_result_signature())); result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); return result; } }; relation_transformer_fn * bound_relation_plugin::mk_rename_fn(const relation_base & r, unsigned cycle_len, const unsigned * permutation_cycle) { if(check_kind(r)) { return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle); } return 0; } class bound_relation_plugin::union_fn : public relation_union_fn { bool m_is_widen; public: union_fn(bool is_widen) : m_is_widen(is_widen) { } virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { TRACE("bound_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); get(_r).mk_union(get(_src), get(_delta), m_is_widen); } }; class bound_relation_plugin::union_fn_i : public relation_union_fn { bool m_is_widen; public: union_fn_i(bool is_widen) : m_is_widen(is_widen) { } virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { TRACE("bound_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); get(_r).mk_union_i(get_interval_relation(_src), get(_delta), m_is_widen); TRACE("bound_relation", _r.display(tout << "dst':\n");); } }; relation_union_fn * bound_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (check_kind(tgt) && is_interval_relation(src) && (!delta || check_kind(*delta))) { return alloc(union_fn_i, false); } if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { return alloc(union_fn, false); } return 0; } relation_union_fn * bound_relation_plugin::mk_widen_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (check_kind(tgt) && is_interval_relation(src) && (!delta || check_kind(*delta))) { return alloc(union_fn_i, true); } if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { return alloc(union_fn, true); } return 0; } class bound_relation_plugin::filter_identical_fn : public relation_mutator_fn { unsigned_vector m_cols; public: filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) : m_cols(col_cnt, identical_cols) {} virtual void operator()(relation_base & r) { for (unsigned i = 1; i < m_cols.size(); ++i) { get(r).equate(m_cols[0], m_cols[i]); } } }; relation_mutator_fn * bound_relation_plugin::mk_filter_identical_fn( const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { if(check_kind(t)) { return alloc(filter_identical_fn, col_cnt, identical_cols); } return 0; } class bound_relation_plugin::filter_equal_fn : public relation_mutator_fn { public: filter_equal_fn(relation_element const& value, unsigned col) {} virtual void operator()(relation_base & r) { } }; relation_mutator_fn * bound_relation_plugin::mk_filter_equal_fn(const relation_base & r, const relation_element & value, unsigned col) { if (check_kind(r)) { return alloc(filter_equal_fn, value, col); } return 0; } class bound_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { enum kind_t { NOT_APPLICABLE, EQ_VAR, EQ_SUB, LT_VAR, LE_VAR, K_FALSE }; app_ref m_cond; app_ref m_lt; arith_util m_arith; interval_relation* m_interval; unsigned_vector m_vars; kind_t m_kind; unsigned get_var(expr* a) { SASSERT(is_var(a)); return to_var(a)->get_idx(); } // x = z - y void mk_sub_eq(expr* x, expr* z, expr* y) { SASSERT(is_var(x)); SASSERT(is_var(z)); SASSERT(is_var(y)); m_vars.push_back(get_var(x)); m_vars.push_back(get_var(z)); m_vars.push_back(get_var(y)); m_kind = EQ_SUB; } void mk_lt(expr* l, expr* r) { SASSERT(is_var(l)); SASSERT(is_var(r)); m_vars.push_back(get_var(l)); m_vars.push_back(get_var(r)); m_lt = m_arith.mk_lt(l, r); m_kind = LT_VAR; } void mk_le(expr* l, expr* r) { SASSERT(is_var(l)); SASSERT(is_var(r)); m_vars.push_back(get_var(l)); m_vars.push_back(get_var(r)); m_kind = LE_VAR; } void mk_eq(expr* l, expr* r) { m_vars.push_back(get_var(l)); m_vars.push_back(get_var(r)); m_kind = EQ_VAR; } public: filter_interpreted_fn(ast_manager& m, app* cond) : m_cond(cond, m), m_lt(m), m_arith(m), m_interval(0), m_kind(NOT_APPLICABLE) { expr* l, *r, *r1, *r2, *c2; rational n1; if ((m_arith.is_lt(cond, l, r) || m_arith.is_gt(cond, r, l)) && is_var(l) && is_var(r)) { mk_lt(l, r); } else if (m.is_not(cond, c2) && (m_arith.is_ge(c2, l, r) || m_arith.is_le(c2, r, l)) && is_var(l) && is_var(r)) { mk_lt(l, r); } else if ((m_arith.is_le(cond, l, r) || m_arith.is_ge(cond, r, l)) && is_var(l) && is_var(r)) { mk_le(l, r); } else if (m.is_not(cond, c2) && (m_arith.is_gt(c2, l, r) || m_arith.is_lt(c2, r, l)) && is_var(l) && is_var(r)) { mk_le(l, r); } else if (m.is_false(cond)) { m_kind = K_FALSE; } else if (m.is_eq(cond, l, r) && is_var(l) && is_var(r)) { mk_eq(l, r); } else if (m.is_eq(cond, l, r) && m_arith.is_sub(r, r1, r2) && is_var(l) && is_var(r1) && is_var(r2)) { mk_sub_eq(l, r1, r2); } else if (m.is_eq(cond, l, r) && m_arith.is_sub(l, r1, r2) && is_var(r) && is_var(r1) && is_var(r2)) { mk_sub_eq(r, r1, r2); } else if (m.is_eq(cond, l, r) && m_arith.is_add(r, r1, r2) && m_arith.is_numeral(r1, n1) && n1.is_pos() && is_var(l) && is_var(r2)) { mk_lt(r2, l); } else if (m.is_eq(cond, l, r) && m_arith.is_add(r, r1, r2) && m_arith.is_numeral(r2, n1) && n1.is_pos() && is_var(l) && is_var(r1)) { mk_lt(r1, l); } else { } } // // x = z - y // x = y // x < y // x <= y // x < y + z // void operator()(relation_base& t) { TRACE("dl", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); bound_relation& r = get(t); switch(m_kind) { case K_FALSE: r.set_empty(); break; case NOT_APPLICABLE: break; case EQ_VAR: r.equate(m_vars[0], m_vars[1]); break; case EQ_SUB: // TBD break; case LT_VAR: r.mk_lt(m_vars[0], m_vars[1]); break; case LE_VAR: r.mk_le(m_vars[0], m_vars[1]); break; default: UNREACHABLE(); break; } TRACE("dl", t.display(tout << "result\n");); } bool supports_attachment(relation_base& t) { return is_interval_relation(t); } void attach(relation_base& t) { SASSERT(is_interval_relation(t)); interval_relation& r = get_interval_relation(t); m_interval = &r; } }; relation_mutator_fn * bound_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { return alloc(filter_interpreted_fn, t.get_plugin().get_ast_manager(), condition); } // ----------------------------- // bound_relation void bound_relation_helper::mk_project_t(uint_set2& t, unsigned_vector const& renaming) { if (t.lt.empty() && t.le.empty()) { return; } uint_set::iterator it = t.lt.begin(), end = t.lt.end(); unsigned_vector ltv, lev; for (; it != end; ++it) { ltv.push_back(renaming[*it]); } it = t.le.begin(), end = t.le.end(); for (; it != end; ++it) { lev.push_back(renaming[*it]); } TRACE("dl", tout << "project: "; for (unsigned i = 0; i < renaming.size(); ++i) if (renaming[i] == UINT_MAX) tout << i << " "; tout << ": "; it = t.lt.begin(); end = t.lt.end(); for (; it != end; ++it) tout << *it << " "; tout << " le "; it = t.le.begin(); end = t.le.end(); for (; it != end; ++it) tout << *it << " "; tout << " => "; for (unsigned i = 0; i < ltv.size(); ++i) tout << ltv[i] << " "; tout << " le "; for (unsigned i = 0; i < lev.size(); ++i) tout << lev[i] << " "; tout << "\n";); t.lt.reset(); for (unsigned i = 0; i < ltv.size(); ++i) { t.lt.insert(ltv[i]); } t.le.reset(); for (unsigned i = 0; i < lev.size(); ++i) { t.le.insert(lev[i]); } } bound_relation::bound_relation(bound_relation_plugin& p, relation_signature const& s, bool is_empty): vector_relation(p, s, is_empty, uint_set2()) { } uint_set2 bound_relation::mk_intersect(uint_set2 const& t1, uint_set2 const& t2, bool& is_empty) const { is_empty = false; uint_set2 r(t1); r.lt |= t2.lt; r.le |= t2.le; return r; } uint_set2 bound_relation::mk_widen(uint_set2 const& t1, uint_set2 const& t2) const { return mk_unite(t1, t2); } uint_set2 bound_relation::mk_unite(uint_set2 const& t1, uint_set2 const& t2) const { uint_set2 s1(t1); s1.lt &= t2.lt; s1.le &= t2.le; return s1; } uint_set2 bound_relation::mk_eq(union_find<> const& old_eqs, union_find<> const& new_eqs, uint_set2 const& t) const { unsigned sz = old_eqs.get_num_vars(); SASSERT(sz == new_eqs.get_num_vars()); uint_set2 result; for (unsigned i = 0; i < sz; ++i) { if (t.lt.contains(i)) { unsigned j = i; do { result.lt.insert(new_eqs.find(j)); j = old_eqs.next(j); } while (j != i); } if (t.le.contains(i)) { unsigned j = i; do { result.le.insert(new_eqs.find(j)); j = old_eqs.next(j); } while (j != i); } } return result; } bool bound_relation::is_subset_of(uint_set2 const& t1, uint_set2 const& t2) const { uint_set2 s1, s2; normalize(t1, s1); normalize(t2, s2); return s1.lt.subset_of(s2.lt) && s1.le.subset_of(s2.le); } void bound_relation::mk_rename_elem(uint_set2& t, unsigned col_cnt, unsigned const* cycle) { // [ 0 -> 2 -> 3 -> 0] if (col_cnt == 0) return; unsigned col1, col2; col1 = find(cycle[0]); col2 = find(cycle[col_cnt-1]); bool has_col2_lt = t.lt.contains(col2); t.lt.remove(col2); bool has_col2_le = t.le.contains(col2); t.le.remove(col2); for (unsigned i = 0; i + 1 < col_cnt; ++i) { col1 = find(cycle[i]); col2 = find(cycle[i+1]); if (t.lt.contains(col1)) { t.lt.remove(col1); t.lt.insert(col2); } if (t.le.contains(col1)) { t.le.remove(col1); t.le.insert(col2); } } if (has_col2_lt) { col1 = find(cycle[0]); t.lt.insert(col1); } if (has_col2_le) { col1 = find(cycle[0]); t.le.insert(col1); } } bool bound_relation::is_full(uint_set2 const& t) const { return t.lt.empty() && t.le.empty(); } bool bound_relation::is_empty(unsigned index, uint_set2 const& t) const { return t.lt.contains(find(index)) || t.le.contains(find(index)); } void bound_relation::normalize(uint_set const& src, uint_set& dst) const { uint_set::iterator it = src.begin(), end = src.end(); for (; it != end; ++it) { dst.insert(find(*it)); } } void bound_relation::normalize(uint_set2 const& src, uint_set2& dst) const { normalize(src.lt, dst.lt); normalize(src.le, dst.le); } void bound_relation::mk_lt(unsigned i) { uint_set2& dst = (*this)[i]; while (!m_todo.empty()) { unsigned j = m_todo.back().first; bool strict = m_todo.back().second; if (i == j && strict) { m_todo.reset(); m_empty = true; return; } m_todo.pop_back(); if (i == j) { continue; } uint_set2& src = (*m_elems)[j]; uint_set::iterator it = src.lt.begin(), end = src.lt.end(); for(; it != end; ++it) { m_todo.push_back(std::make_pair(*it, true)); } it = src.le.begin(), end = src.le.end(); for(; it != end; ++it) { m_todo.push_back(std::make_pair(*it, strict)); } if (strict) { dst.lt.insert(j); } else { dst.le.insert(j); } } } void bound_relation::mk_lt(unsigned i, unsigned j) { m_todo.reset(); i = find(i); m_todo.push_back(std::make_pair(find(j), true)); mk_lt(i); } void bound_relation::mk_le(unsigned i, unsigned j) { m_todo.reset(); i = find(i); m_todo.push_back(std::make_pair(find(j), false)); mk_lt(i); } bool bound_relation::is_lt(unsigned i, unsigned j) const { return (*this)[i].lt.contains(find(j)); } void bound_relation::add_fact(const relation_fact & f) { bound_relation r(get_plugin(), get_signature(), false); for (unsigned i = 0; i < f.size(); ++i) { scoped_ptr fe = get_plugin().mk_filter_equal_fn(r, f[i], i); (*fe)(r); } mk_union(r, 0, false); } bool bound_relation::contains_fact(const relation_fact & f) const { if (empty()) { return false; } // this is a very rough approximation. return true; } bound_relation * bound_relation::clone() const { bound_relation* result = 0; if (empty()) { result = bound_relation_plugin::get(get_plugin().mk_empty(get_signature())); } else { result = bound_relation_plugin::get(get_plugin().mk_full(0, get_signature())); result->copy(*this); } return result; } void bound_relation::mk_union_i(interval_relation const& src, bound_relation* delta, bool is_widen) { unsigned size = get_signature().size(); for (unsigned i = 0; i < size; ++i) { if (find(i) != i) { continue; } uint_set2& s = (*this)[i]; ext_numeral const& lo = src[i].sup(); if (lo.is_infinite()) { s.lt.reset(); s.le.reset(); continue; } uint_set::iterator it = s.lt.begin(), end = s.lt.end(); for(; it != end; ++it) { ext_numeral const& hi = src[*it].inf(); if (hi.is_infinite() || lo.to_rational() >= hi.to_rational()) { s.lt.remove(*it); } } it = s.le.begin(), end = s.le.end(); for(; it != end; ++it) { ext_numeral const& hi = src[*it].inf(); if (hi.is_infinite() || lo.to_rational() > hi.to_rational()) { s.le.remove(*it); } } } } bound_relation * bound_relation::complement(func_decl* p) const { UNREACHABLE(); return 0; } void bound_relation::to_formula(expr_ref& fml) const { ast_manager& m = get_plugin().get_ast_manager(); arith_util& arith = get_plugin().m_arith; basic_simplifier_plugin& bsimp = get_plugin().m_bsimp; expr_ref_vector conjs(m); relation_signature const& sig = get_signature(); for (unsigned i = 0; i < sig.size(); ++i) { if (i != find(i)) { conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), m.mk_var(find(i), sig[find(i)]))); continue; } uint_set2 const& upper = (*this)[i]; uint_set::iterator it = upper.lt.begin(), end = upper.lt.end(); for (; it != end; ++it) { conjs.push_back(arith.mk_lt(m.mk_var(i, sig[i]), m.mk_var(*it, sig[*it]))); } it = upper.le.begin(), end = upper.le.end(); for (; it != end; ++it) { conjs.push_back(arith.mk_le(m.mk_var(i, sig[i]), m.mk_var(*it, sig[*it]))); } } bsimp.mk_and(conjs.size(), conjs.c_ptr(), fml); } void bound_relation::display_index(unsigned i, uint_set2 const& src, std::ostream & out) const { uint_set::iterator it = src.lt.begin(), end = src.lt.end(); out << "#" << i; if (!src.lt.empty()) { out << " < "; for(; it != end; ++it) { out << *it << " "; } } if (!src.le.empty()) { it = src.le.begin(), end = src.le.end(); out << " <= "; for(; it != end; ++it) { out << *it << " "; } } if (src.lt.empty() && src.le.empty()) { out << " < oo"; } out << "\n"; } bound_relation_plugin& bound_relation::get_plugin() const { return dynamic_cast(relation_base::get_plugin()); } }; z3-4.4.0/src/muz/rel/dl_table_relation.cpp0000644000175000017500000004662612540347414020216 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_table_relation.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #include #include"dl_context.h" #include"dl_relation_manager.h" #include"dl_table_relation.h" namespace datalog { // ----------------------------------- // // table_relation_plugin // // ----------------------------------- symbol table_relation_plugin::create_plugin_name(const table_plugin &p) { std::string name = std::string("tr_") + p.get_name().bare_str(); return symbol(name.c_str()); } bool table_relation_plugin::can_handle_signature(const relation_signature & s) { table_signature tsig; return get_manager().relation_signature_to_table(s, tsig) && m_table_plugin.can_handle_signature(tsig); } relation_base * table_relation_plugin::mk_empty(const relation_signature & s) { table_signature tsig; if (!get_manager().relation_signature_to_table(s, tsig)) { return 0; } table_base * t = m_table_plugin.mk_empty(tsig); return alloc(table_relation, *this, s, t); } relation_base * table_relation_plugin::mk_full(const relation_signature & s, func_decl* p, family_id kind) { table_signature tsig; if(!get_manager().relation_signature_to_table(s, tsig)) { return 0; } table_base * t = m_table_plugin.mk_full(p, tsig, kind); return alloc(table_relation, *this, s, t); } /** The newly created object takes ownership of the \c t object. */ relation_base * table_relation_plugin::mk_from_table(const relation_signature & s, table_base * t) { if (&t->get_plugin() == &m_table_plugin) return alloc(table_relation, *this, s, t); table_relation_plugin& other = t->get_manager().get_table_relation_plugin(t->get_plugin()); return alloc(table_relation, other, s, t); } class table_relation_plugin::tr_join_project_fn : public convenient_relation_join_project_fn { scoped_ptr m_tfun; public: tr_join_project_fn(const relation_signature & s1, const relation_signature & s2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, table_join_fn * tfun) : convenient_relation_join_project_fn(s1, s2, col_cnt, cols1, cols2, removed_col_cnt, removed_cols), m_tfun(tfun) {} virtual relation_base * operator()(const relation_base & t1, const relation_base & t2) { SASSERT(t1.from_table()); SASSERT(t2.from_table()); table_relation_plugin & plugin = static_cast(t1.get_plugin()); const table_relation & tr1 = static_cast(t1); const table_relation & tr2 = static_cast(t2); table_base * tres = (*m_tfun)(tr1.get_table(), tr2.get_table()); TRACE("dl_table_relation", tout << "# join => "; tres->display(tout);); if(&tres->get_plugin()!=&plugin.m_table_plugin) { IF_VERBOSE(1, verbose_stream() << "new type returned\n";); //Operation returned a table of different type than the one which is associated with //this plugin. We need to get a correct table_relation_plugin and create the relation //using it. return plugin.get_manager().get_table_relation_plugin(tres->get_plugin()) .mk_from_table(get_result_signature(), tres); } return plugin.mk_from_table(get_result_signature(), tres); } }; relation_join_fn * table_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if(!r1.from_table() || !r2.from_table()) { return 0; } const table_relation & tr1 = static_cast(r1); const table_relation & tr2 = static_cast(r2); table_join_fn * tfun = get_manager().mk_join_fn(tr1.get_table(), tr2.get_table(), col_cnt, cols1, cols2); if(!tfun) { return 0; } return alloc(tr_join_project_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2, 0, static_cast(0), tfun); } relation_join_fn * table_relation_plugin::mk_join_project_fn(const relation_base & r1, const relation_base & r2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { if(!r1.from_table() || !r2.from_table()) { return 0; } const table_relation & tr1 = static_cast(r1); const table_relation & tr2 = static_cast(r2); table_join_fn * tfun = get_manager().mk_join_project_fn(tr1.get_table(), tr2.get_table(), joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); SASSERT(tfun); return alloc(tr_join_project_fn, r1.get_signature(), r2.get_signature(), joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols, tfun); } class table_relation_plugin::tr_transformer_fn : public convenient_relation_transformer_fn { scoped_ptr m_tfun; public: tr_transformer_fn(const relation_signature & rsig, table_transformer_fn * tfun) : m_tfun(tfun) { get_result_signature() = rsig; } virtual relation_base * operator()(const relation_base & t) { SASSERT(t.from_table()); table_relation_plugin & plugin = static_cast(t.get_plugin()); const table_relation & tr = static_cast(t); table_base * tres = (*m_tfun)(tr.get_table()); TRACE("dl_table_relation", tout << "# transform => "; tres->display(tout);); if(&tres->get_plugin()!=&plugin.m_table_plugin) { //Transformation returned a table of different type than the one which is associated with this plugin. //We need to get a correct table_relation_plugin and create the relation using it. return plugin.get_manager().get_table_relation_plugin(tres->get_plugin()) .mk_from_table(get_result_signature(), tres); } return plugin.mk_from_table(get_result_signature(), tres); } }; relation_transformer_fn * table_relation_plugin::mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) { if(!t.from_table()) { return 0; } const table_relation & tr = static_cast(t); table_transformer_fn * tfun = get_manager().mk_project_fn(tr.get_table(), col_cnt, removed_cols); SASSERT(tfun); relation_signature sig; relation_signature::from_project(t.get_signature(), col_cnt, removed_cols, sig); return alloc(tr_transformer_fn, sig, tfun); } relation_transformer_fn * table_relation_plugin::mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { if(!t.from_table()) { return 0; } const table_relation & tr = static_cast(t); table_transformer_fn * tfun = get_manager().mk_rename_fn(tr.get_table(), permutation_cycle_len, permutation_cycle); SASSERT(tfun); relation_signature sig; relation_signature::from_rename(t.get_signature(), permutation_cycle_len, permutation_cycle, sig); return alloc(tr_transformer_fn, sig, tfun); } relation_transformer_fn * table_relation_plugin::mk_permutation_rename_fn(const relation_base & t, const unsigned * permutation) { if(!t.from_table()) { return 0; } const table_relation & tr = static_cast(t); table_transformer_fn * tfun = get_manager().mk_permutation_rename_fn(tr.get_table(), permutation); SASSERT(tfun); relation_signature sig; relation_signature::from_permutation_rename(t.get_signature(), permutation, sig); return alloc(tr_transformer_fn, sig, tfun); } relation_transformer_fn * table_relation_plugin::mk_select_equal_and_project_fn(const relation_base & t, const relation_element & value, unsigned col) { if(!t.from_table()) { return 0; } const table_relation & tr = static_cast(t); table_element tvalue; get_manager().relation_to_table(tr.get_signature()[col], value, tvalue); table_transformer_fn * tfun = get_manager().mk_select_equal_and_project_fn(tr.get_table(), tvalue, col); SASSERT(tfun); relation_signature res_sig; relation_signature::from_project(t.get_signature(), 1, &col, res_sig); return alloc(tr_transformer_fn, res_sig, tfun); } /** Union functor that can unite table relation into any other relation (using any delta relation) by iterating through the table and calling \c add_fact of the target relation. */ class table_relation_plugin::universal_target_union_fn : public relation_union_fn { virtual void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) { SASSERT(src.from_table()); const table_relation & tr_src = static_cast(src); relation_manager & rmgr = tr_src.get_manager(); relation_signature sig = tr_src.get_signature(); SASSERT(tgt.get_signature()==sig); SASSERT(!delta || delta->get_signature()==sig); table_base::iterator it = tr_src.get_table().begin(); table_base::iterator end = tr_src.get_table().end(); table_fact tfact; relation_fact rfact(rmgr.get_context()); for (; it != end; ++it) { it->get_fact(tfact); rmgr.table_fact_to_relation(sig, tfact, rfact); if(delta) { if(!tgt.contains_fact(rfact)) { tgt.add_new_fact(rfact); delta->add_fact(rfact); } } else { tgt.add_fact(rfact); } } TRACE("dl_table_relation", tout << "# universal union => "; tgt.display(tout);); } }; class table_relation_plugin::tr_union_fn : public relation_union_fn { scoped_ptr m_tfun; public: tr_union_fn(table_union_fn * tfun) : m_tfun(tfun) {} virtual void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) { SASSERT(tgt.from_table()); SASSERT(src.from_table()); SASSERT(!delta || delta->from_table()); table_relation & tr_tgt = static_cast(tgt); const table_relation & tr_src = static_cast(src); table_relation * tr_delta = static_cast(delta); (*m_tfun)(tr_tgt.get_table(), tr_src.get_table(), tr_delta ? &tr_delta->get_table() : 0); TRACE("dl_table_relation", tout << "# union => "; tr_tgt.get_table().display(tout);); } }; relation_union_fn * table_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if(!src.from_table()) { return 0; } if(!tgt.from_table() || (delta && !delta->from_table())) { return alloc(universal_target_union_fn); } const table_relation & tr_tgt = static_cast(tgt); const table_relation & tr_src = static_cast(src); const table_relation * tr_delta = static_cast(delta); table_union_fn * tfun = get_manager().mk_union_fn(tr_tgt.get_table(), tr_src.get_table(), tr_delta ? &tr_delta->get_table() : 0); SASSERT(tfun); return alloc(tr_union_fn, tfun); } class table_relation_plugin::tr_mutator_fn : public relation_mutator_fn { scoped_ptr m_tfun; public: tr_mutator_fn(table_mutator_fn * tfun) : m_tfun(tfun) {} virtual void operator()(relation_base & r) { SASSERT(r.from_table()); table_relation & tr = static_cast(r); (*m_tfun)(tr.get_table()); TRACE("dl_table_relation", tout << "# mutator => "; tr.get_table().display(tout);); } }; relation_mutator_fn * table_relation_plugin::mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { if(!t.from_table()) { return 0; } const table_relation & tr = static_cast(t); table_mutator_fn * tfun = get_manager().mk_filter_identical_fn(tr.get_table(), col_cnt, identical_cols); SASSERT(tfun); return alloc(tr_mutator_fn, tfun); } relation_mutator_fn * table_relation_plugin::mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col) { if(!t.from_table()) { return 0; } const table_relation & tr = static_cast(t); table_element tvalue; get_manager().relation_to_table(tr.get_signature()[col], value, tvalue); table_mutator_fn * tfun = get_manager().mk_filter_equal_fn(tr.get_table(), tvalue, col); SASSERT(tfun); return alloc(tr_mutator_fn, tfun); } relation_mutator_fn * table_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { bool condition_needs_transforming = false; if(!t.from_table() || condition_needs_transforming) { return 0; } const table_relation & tr = static_cast(t); table_mutator_fn * tfun = get_manager().mk_filter_interpreted_fn(tr.get_table(), condition); SASSERT(tfun); return alloc(tr_mutator_fn, tfun); } relation_transformer_fn * table_relation_plugin::mk_filter_interpreted_and_project_fn(const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { if (!t.from_table()) return 0; const table_relation & tr = static_cast(t); table_transformer_fn * tfun = get_manager().mk_filter_interpreted_and_project_fn(tr.get_table(), condition, removed_col_cnt, removed_cols); SASSERT(tfun); relation_signature sig; relation_signature::from_project(t.get_signature(), removed_col_cnt, removed_cols, sig); return alloc(tr_transformer_fn, sig, tfun); } class table_relation_plugin::tr_intersection_filter_fn : public relation_intersection_filter_fn { scoped_ptr m_tfun; public: tr_intersection_filter_fn(table_intersection_filter_fn * tfun) : m_tfun(tfun) {} virtual void operator()(relation_base & r, const relation_base & src) { SASSERT(r.from_table()); SASSERT(src.from_table()); table_relation & tr = static_cast(r); const table_relation & tr_src = static_cast(src); (*m_tfun)(tr.get_table(), tr_src.get_table()); TRACE("dl_table_relation", tout << "# negation_filter => "; tr.get_table().display(tout);); } }; relation_intersection_filter_fn * table_relation_plugin::mk_filter_by_intersection_fn(const relation_base & r, const relation_base & src, unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * src_cols) { if(!r.from_table() || !src.from_table()) { return 0; } const table_relation & tr = static_cast(r); const table_relation & tr_neg = static_cast(src); table_intersection_filter_fn * tfun = get_manager().mk_filter_by_intersection_fn(tr.get_table(), tr_neg.get_table(), joined_col_cnt, r_cols, src_cols); if(!tfun) { return 0; } return alloc(tr_intersection_filter_fn, tfun); } relation_intersection_filter_fn * table_relation_plugin::mk_filter_by_negation_fn(const relation_base & r, const relation_base & negated_rel, unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * negated_cols) { if(!r.from_table() || !negated_rel.from_table()) { return 0; } const table_relation & tr = static_cast(r); const table_relation & tr_neg = static_cast(negated_rel); table_intersection_filter_fn * tfun = get_manager().mk_filter_by_negation_fn(tr.get_table(), tr_neg.get_table(), joined_col_cnt, r_cols, negated_cols); SASSERT(tfun); return alloc(tr_intersection_filter_fn, tfun); } // ----------------------------------- // // table_relation // // ----------------------------------- void table_relation::add_table_fact(const table_fact & f) { get_table().add_fact(f); } void table_relation::add_fact(const relation_fact & f) { SASSERT(f.size()==get_signature().size()); table_fact vals; get_manager().relation_fact_to_table(get_signature(), f, vals); get_table().add_fact(vals); TRACE("dl_table_relation", tout << "# add fact => "; get_table().display(tout);); } bool table_relation::contains_fact(const relation_fact & f) const { table_fact vals; get_manager().relation_fact_to_table(get_signature(), f, vals); return get_table().contains_fact(vals); } relation_base * table_relation::clone() const { table_base * tres = get_table().clone(); return get_plugin().mk_from_table(get_signature(), tres); } relation_base * table_relation::complement(func_decl* p) const { table_base * tres = get_table().complement(p); return get_plugin().mk_from_table(get_signature(), tres); } void table_relation::display_tuples(func_decl & pred, std::ostream & out) const { context & ctx = get_manager().get_context(); unsigned arity = pred.get_arity(); out << "Tuples in " << pred.get_name() << ": \n"; table_base::iterator it = get_table().begin(); table_base::iterator end = get_table().end(); table_fact fact; for (; it != end; ++it) { it->get_fact(fact); out << "\t("; for(unsigned i=0;i namespace datalog { check_relation::check_relation(check_relation_plugin& p, relation_signature const& sig, relation_base* r): relation_base(p, sig), m(p.get_ast_manager()), m_relation(r), m_fml(m) { m_relation->to_formula(m_fml); } check_relation::~check_relation() { m_relation->deallocate(); } void check_relation::check_equiv(char const* objective, expr* f1, expr* f2) const { get_plugin().check_equiv(objective, f1, f2); } void check_relation::consistent_formula() { expr_ref fml(m); m_relation->to_formula(fml); if (m_fml != fml) { IF_VERBOSE(0, display(verbose_stream() << "relation does not have a consistent formula");); } } expr_ref check_relation::mk_eq(relation_fact const& f) const { relation_signature const& sig = get_signature(); expr_ref_vector conjs(m); for (unsigned i = 0; i < sig.size(); ++i) { conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), f[i])); } return expr_ref(mk_and(m, conjs.size(), conjs.c_ptr()), m); } expr_ref check_relation::ground(expr* fml) const { return get_plugin().ground(*this, fml); } expr_ref check_relation_plugin::ground(relation_base const& dst) const { expr_ref fml(m); dst.to_formula(fml); return ground(dst, fml); } expr_ref check_relation_plugin::ground(relation_base const& dst, expr* fml) const { relation_signature const& sig = dst.get_signature(); var_subst sub(m, false); expr_ref_vector vars(m); for (unsigned i = 0; i < sig.size(); ++i) { vars.push_back(m.mk_const(symbol(i), sig[i])); } expr_ref result(m); sub(fml, vars.size(), vars.c_ptr(), result); return result; } void check_relation::add_fact(const relation_fact & f) { expr_ref fml1(m); m_relation->add_fact(f); m_relation->to_formula(fml1); m_fml = m.mk_or(m_fml, mk_eq(f)); check_equiv("add_fact", ground(m_fml), ground(fml1)); m_fml = fml1; } void check_relation::add_new_fact(const relation_fact & f) { expr_ref fml1(m); m_relation->add_new_fact(f); m_relation->to_formula(fml1); m_fml = m.mk_or(m_fml, mk_eq(f)); check_equiv("add_fact", ground(m_fml), ground(fml1)); m_fml = fml1; } bool check_relation::empty() const { bool result = m_relation->empty(); if (result && !m.is_false(m_fml)) { check_equiv("empty", m.mk_false(), ground(m_fml)); } return result; } bool check_relation::fast_empty() const { bool result = m_relation->fast_empty(); if (result && !m.is_false(m_fml)) { check_equiv("fast_empty", m.mk_false(), ground(m_fml)); } return result; } void check_relation::reset() { m_relation->reset(); m_fml = m.mk_false(); } bool check_relation::contains_fact(const relation_fact & f) const { bool result = m_relation->contains_fact(f); expr_ref fml1(m), fml2(m); fml1 = mk_eq(f); fml2 = m.mk_and(m_fml, fml1); if (result) { check_equiv("contains fact", ground(fml1), ground(fml2)); } else if (!m.is_false(m_fml)) { check_equiv("contains fact", ground(fml2), m.mk_false()); } return result; } check_relation * check_relation::clone() const { check_relation* result = check_relation_plugin::get(get_plugin().mk_empty(get_signature())); result->m_relation->deallocate(); result->m_relation = m_relation->clone(); result->m_relation->to_formula(result->m_fml); if (m_fml != result->m_fml) { check_equiv("clone", ground(m_fml), ground(result->m_fml)); } return result; } check_relation * check_relation::complement(func_decl* f) const { check_relation* result = check_relation_plugin::get(get_plugin().mk_empty(get_signature())); result->m_relation->deallocate(); result->m_relation = m_relation->complement(f); result->m_relation->to_formula(result->m_fml); expr_ref fml(m); fml = m.mk_not(m_fml); check_equiv("complement", ground(fml), ground(result->m_fml)); return result; } void check_relation::to_formula(expr_ref& fml) const { fml = m_fml; } check_relation_plugin& check_relation::get_plugin() const { return static_cast(relation_base::get_plugin()); } void check_relation::display(std::ostream& out) const { m_relation->display(out); out << m_fml << "\n"; } // ------------- check_relation_plugin::check_relation_plugin(relation_manager& rm): relation_plugin(check_relation_plugin::get_name(), rm), m(rm.get_context().get_manager()), m_base(0) { } check_relation_plugin::~check_relation_plugin() { } check_relation& check_relation_plugin::get(relation_base& r) { return dynamic_cast(r); } check_relation* check_relation_plugin::get(relation_base* r) { return r?dynamic_cast(r):0; } check_relation const & check_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); } bool check_relation_plugin::can_handle_signature(const relation_signature & sig) { return m_base && m_base->can_handle_signature(sig); } relation_base * check_relation_plugin::mk_empty(const relation_signature & sig) { relation_base* r = m_base->mk_empty(sig); check_relation* result = alloc(check_relation, *this, sig, r); if (result->m_fml != m.mk_false()) { check_equiv("mk_empty", result->ground(result->m_fml), m.mk_false()); } return result; } relation_base * check_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { relation_base* r = m_base->mk_full(p, s); check_relation* result = alloc(check_relation, *this, s, r); if (result->m_fml != m.mk_true()) { check_equiv("mk_full", result->ground(result->m_fml), m.mk_true()); } return result; } class check_relation_plugin::join_fn : public convenient_relation_join_fn { scoped_ptr m_join; public: join_fn(relation_join_fn* j, const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2), m_join(j) {} virtual ~join_fn() {} virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { check_relation const& t1 = get(r1); check_relation const& t2 = get(r2); check_relation_plugin& p = t1.get_plugin(); relation_base* r = (*m_join)(t1.rb(), t2.rb()); p.verify_join(r1, r2, *r, m_cols1, m_cols2); return alloc(check_relation, p, r->get_signature(), r); } }; relation_join_fn * check_relation_plugin::mk_join_fn( const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { relation_join_fn* j = m_base->mk_join_fn(get(t1).rb(), get(t2).rb(), col_cnt, cols1, cols2); return j?alloc(join_fn, j, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2):0; } class check_relation_plugin::join_project_fn : public convenient_relation_join_project_fn { scoped_ptr m_join; public: join_project_fn( relation_join_fn* j, const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned* removed_cols) : convenient_join_project_fn(o1_sig, o2_sig, col_cnt, cols1, cols2, removed_col_cnt, removed_cols), m_join(j) {} virtual ~join_project_fn() {} virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { check_relation const& t1 = get(r1); check_relation const& t2 = get(r2); check_relation_plugin& p = t1.get_plugin(); relation_base* r = (*m_join)(t1.rb(), t2.rb()); p.verify_join_project(r1, r2, *r, m_cols1, m_cols2, m_removed_cols); return alloc(check_relation, p, r->get_signature(), r); } }; relation_join_fn * check_relation_plugin::mk_join_project_fn( const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { relation_join_fn* j = m_base->mk_join_project_fn(get(t1).rb(), get(t2).rb(), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); return j?alloc(join_project_fn, j, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, removed_col_cnt, removed_cols):0; } void check_relation_plugin::verify_filter_project( relation_base const& src, relation_base const& dst, app* cond, unsigned_vector const& removed_cols) { expr_ref fml1(m), fml2(m); src.to_formula(fml1); dst.to_formula(fml2); fml1 = m.mk_and(cond, fml1); verify_project(src, fml1, dst, fml2, removed_cols); } void check_relation_plugin::verify_project( relation_base const& src, relation_base const& dst, unsigned_vector const& removed_cols) { expr_ref fml1(m), fml2(m); src.to_formula(fml1); dst.to_formula(fml2); verify_project(src, fml1, dst, fml2, removed_cols); } void check_relation_plugin::verify_project( relation_base const& src, expr* f1, relation_base const& dst, expr* f2, unsigned_vector const& removed_cols) { expr_ref fml1 = ground(dst, mk_project(src.get_signature(), f1, removed_cols)); expr_ref fml2 = ground(dst, f2); check_equiv("project", fml1, fml2); } expr_ref check_relation_plugin::mk_project( relation_signature const& sig, expr* fml, unsigned_vector const& removed_cols) { expr_ref fml1(m); ptr_vector bound; svector names; expr_ref_vector vars(m); unsigned rm_cnt = removed_cols.size(); for (unsigned i = 0, j = 0, k = 0; i < sig.size(); ++i) { if (j < rm_cnt && removed_cols[j] == i) { std::ostringstream strm; strm << "x" << j; bound.push_back(sig[i]); names.push_back(symbol(strm.str().c_str())); vars.push_back(m.mk_var(j, sig[i])); ++j; } else { vars.push_back(m.mk_var(k + rm_cnt, sig[i])); ++k; } } var_subst sub(m, false); sub(fml, vars.size(), vars.c_ptr(), fml1); bound.reverse(); fml1 = m.mk_exists(bound.size(), bound.c_ptr(), names.c_ptr(), fml1); return fml1; } void check_relation_plugin::verify_join_project( relation_base const& t1, relation_base const& t2, relation_base const& t, unsigned_vector const& cols1, unsigned_vector const& cols2, unsigned_vector const& rm_cols) { ast_manager& m = get_ast_manager(); relation_signature const& sigA = t1.get_signature(); relation_signature const& sigB = t2.get_signature(); relation_signature sig1; sig1.append(sigA); sig1.append(sigB); expr_ref fml1 = mk_join(t1, t2, cols1, cols2); fml1 = mk_project(sig1, fml1, rm_cols); fml1 = ground(t, fml1); expr_ref fml2(m); t.to_formula(fml2); fml2 = ground(t, fml2); check_equiv("join_project", fml1, fml2); } expr_ref check_relation_plugin::mk_join( relation_base const& t1, relation_base const& t2, unsigned_vector const& cols1, unsigned_vector const& cols2) { ast_manager& m = get_ast_manager(); expr_ref fml1(m), fml2(m), fml3(m); relation_signature const& sig1 = t1.get_signature(); relation_signature const& sig2 = t2.get_signature(); var_ref var1(m), var2(m); t1.to_formula(fml1); t2.to_formula(fml2); var_subst sub(m, false); expr_ref_vector vars(m); for (unsigned i = 0; i < sig2.size(); ++i) { vars.push_back(m.mk_var(i + sig1.size(), sig2[i])); } sub(fml2, vars.size(), vars.c_ptr(), fml2); fml1 = m.mk_and(fml1, fml2); for (unsigned i = 0; i < cols1.size(); ++i) { unsigned v1 = cols1[i]; unsigned v2 = cols2[i]; var1 = m.mk_var(v1, sig1[v1]); var2 = m.mk_var(v2 + sig1.size(), sig2[v2]); fml1 = m.mk_and(m.mk_eq(var1, var2), fml1); } return fml1; } void check_relation_plugin::verify_permutation( relation_base const& src, relation_base const& dst, unsigned_vector const& cycle) { unsigned_vector perm; relation_signature const& sig1 = src.get_signature(); relation_signature const& sig2 = dst.get_signature(); for (unsigned i = 0; i < sig1.size(); ++i) { perm.push_back(i); } for (unsigned i = 0; i < cycle.size(); ++i) { unsigned j = (i + 1)%cycle.size(); unsigned col1 = cycle[i]; unsigned col2 = cycle[j]; perm[col2] = col1; } for (unsigned i = 0; i < perm.size(); ++i) { SASSERT(sig2[perm[i]] == sig1[i]); } expr_ref_vector sub(m); for (unsigned i = 0; i < perm.size(); ++i) { sub.push_back(m.mk_var(perm[i], sig1[i])); } var_subst subst(m, false); expr_ref fml1(m), fml2(m); src.to_formula(fml1); dst.to_formula(fml2); subst(fml1, sub.size(), sub.c_ptr(), fml1); expr_ref_vector vars(m); for (unsigned i = 0; i < sig2.size(); ++i) { vars.push_back(m.mk_const(symbol(i), sig2[i])); } subst(fml1, vars.size(), vars.c_ptr(), fml1); subst(fml2, vars.size(), vars.c_ptr(), fml2); check_equiv("permutation", fml1, fml2); } void check_relation_plugin::verify_join( relation_base const& t1, relation_base const& t2, relation_base const& t, unsigned_vector const& cols1, unsigned_vector const& cols2) { expr_ref fml1 = ground(t, mk_join(t1, t2, cols1, cols2)); expr_ref fml2 = ground(t); check_equiv("join", fml1, fml2); } void check_relation_plugin::verify_filter(expr* fml0, relation_base const& t, expr* cond) { expr_ref fml1(m), fml2(m); fml1 = m.mk_and(fml0, cond); t.to_formula(fml2); relation_signature const& sig = t.get_signature(); expr_ref_vector vars(m); var_subst sub(m, false); for (unsigned i = 0; i < sig.size(); ++i) { std::stringstream strm; strm << "x" << i; vars.push_back(m.mk_const(symbol(strm.str().c_str()), sig[i])); } sub(fml1, vars.size(), vars.c_ptr(), fml1); sub(fml2, vars.size(), vars.c_ptr(), fml2); check_equiv("filter", fml1, fml2); } void check_relation_plugin::check_contains(char const* objective, expr* fml1, expr* fml2) { expr_ref fml0(m); fml0 = m.mk_and(fml1, fml2); check_equiv(objective, fml0, fml2); } void check_relation_plugin::check_equiv(char const* objective, expr* fml1, expr* fml2) { TRACE("doc", tout << mk_pp(fml1, m) << "\n"; tout << mk_pp(fml2, m) << "\n";); smt_params fp; smt::kernel solver(m, fp); expr_ref tmp(m); tmp = m.mk_not(m.mk_eq(fml1, fml2)); solver.assert_expr(tmp); lbool res = solver.check(); if (res == l_false) { IF_VERBOSE(3, verbose_stream() << objective << " verified\n";); } else { IF_VERBOSE(3, verbose_stream() << "NOT verified " << res << "\n"; verbose_stream() << mk_pp(fml1, m) << "\n"; verbose_stream() << mk_pp(fml2, m) << "\n"; verbose_stream().flush(); ); throw 0; } } void check_relation_plugin::verify_union(expr* dst0, relation_base const& src, relation_base const& dst, expr* delta0, relation_base const* delta) { expr_ref fml1(m), fml2(m); src.to_formula(fml1); dst.to_formula(fml2); fml1 = m.mk_or(fml1, dst0); relation_signature const& sig = dst.get_signature(); expr_ref_vector vars(m); var_subst sub(m, false); for (unsigned i = 0; i < sig.size(); ++i) { std::stringstream strm; strm << "x" << i; vars.push_back(m.mk_const(symbol(strm.str().c_str()), sig[i])); } sub(fml1, vars.size(), vars.c_ptr(), fml1); sub(fml2, vars.size(), vars.c_ptr(), fml2); check_equiv("union", fml1, fml2); if (delta) { expr_ref d0(m), d(m); delta->to_formula(d); IF_VERBOSE(3, verbose_stream() << "verify delta " << d << "\n";); // delta >= dst \ dst0 // dst \ dst0 == delta & dst & \ dst0 expr_ref fml4(m), fml5(m); fml4 = m.mk_and(fml2, m.mk_not(dst0)); sub(fml4, vars.size(), vars.c_ptr(), fml4); sub(d, vars.size(), vars.c_ptr(), d); check_contains("union_delta low", d, fml4); // // delta >= delta0 // sub(delta0, vars.size(), vars.c_ptr(), d0); check_contains("union delta0", d, d0); // // dst u delta0 = delta u dst0 // fml4 = m.mk_or(fml2, delta0); fml5 = m.mk_or(d, dst0); sub(fml4, vars.size(), vars.c_ptr(), fml4); sub(fml5, vars.size(), vars.c_ptr(), fml5); check_equiv("union no overflow", fml4, fml5); } } class check_relation_plugin::union_fn : public relation_union_fn { scoped_ptr m_union; public: union_fn(relation_union_fn* m): m_union(m) {} virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { TRACE("doc", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); check_relation& r = get(_r); check_relation const& src = get(_src); check_relation* d = get(_delta); expr_ref fml0 = r.m_fml; expr_ref delta0(r.m_fml.get_manager()); if (d) d->to_formula(delta0); (*m_union)(r.rb(), src.rb(), d?(&d->rb()):0); r.get_plugin().verify_union(fml0, src.rb(), r.rb(), delta0, d?(&d->rb()):0); r.rb().to_formula(r.m_fml); if (d) d->rb().to_formula(d->m_fml); } }; relation_union_fn * check_relation_plugin::mk_union_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { relation_base const* d1 = delta?(&(get(*delta).rb())):0; relation_union_fn* u = m_base->mk_union_fn(get(tgt).rb(), get(src).rb(), d1); return u?alloc(union_fn, u):0; } relation_union_fn * check_relation_plugin::mk_widen_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { relation_base const* d1 = delta?(&(get(*delta).rb())):0; relation_union_fn* u = m_base->mk_widen_fn(get(tgt).rb(), get(src).rb(), d1); return u?alloc(union_fn, u):0; } class check_relation_plugin::filter_identical_fn : public relation_mutator_fn { unsigned_vector m_cols; scoped_ptr m_filter; public: filter_identical_fn(relation_mutator_fn* f, unsigned col_cnt, const unsigned *identical_cols) : m_cols(col_cnt, identical_cols), m_filter(f) { } virtual ~filter_identical_fn() {} virtual void operator()(relation_base & _r) { check_relation& r = get(_r); check_relation_plugin& p = r.get_plugin(); ast_manager& m = p.m; expr_ref cond(m); relation_signature const& sig = r.get_signature(); expr_ref_vector conds(m); unsigned c1 = m_cols[0]; for (unsigned i = 1; i < m_cols.size(); ++i) { unsigned c2 = m_cols[i]; conds.push_back(m.mk_eq(m.mk_var(c1, sig[c1]), m.mk_var(c2, sig[c2]))); } cond = mk_and(m, conds.size(), conds.c_ptr()); r.consistent_formula(); (*m_filter)(r.rb()); p.verify_filter(r.m_fml, r.rb(), cond); r.rb().to_formula(r.m_fml); } }; relation_mutator_fn * check_relation_plugin::mk_filter_identical_fn( const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { relation_mutator_fn* r = m_base->mk_filter_identical_fn(get(t).rb(), col_cnt, identical_cols); return r?alloc(filter_identical_fn, r, col_cnt, identical_cols):0; } class check_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { scoped_ptr m_mutator; app_ref m_condition; public: filter_interpreted_fn(relation_mutator_fn* r, app_ref& condition) : m_mutator(r), m_condition(condition) { } virtual ~filter_interpreted_fn() {} virtual void operator()(relation_base & tb) { check_relation& r = get(tb); check_relation_plugin& p = r.get_plugin(); expr_ref fml = r.m_fml; (*m_mutator)(r.rb()); p.verify_filter(fml, r.rb(), m_condition); r.rb().to_formula(r.m_fml); } }; relation_mutator_fn * check_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { relation_mutator_fn* r = m_base->mk_filter_interpreted_fn(get(t).rb(), condition); app_ref cond(condition, m); return r?alloc(filter_interpreted_fn, r, cond):0; } class check_relation_plugin::project_fn : public convenient_relation_project_fn { scoped_ptr m_project; public: project_fn(relation_transformer_fn* p, relation_base const & t, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(t.get_signature(), removed_col_cnt, removed_cols), m_project(p) { } virtual ~project_fn() {} virtual relation_base * operator()(const relation_base & tb) { check_relation const& t = get(tb); check_relation_plugin& p = t.get_plugin(); relation_base* r = (*m_project)(t.rb()); p.verify_project(tb, *r, m_removed_cols); return alloc(check_relation, p, r->get_signature(), r); } }; relation_transformer_fn * check_relation_plugin::mk_project_fn( const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) { relation_transformer_fn* p = m_base->mk_project_fn(get(t).rb(), col_cnt, removed_cols); return p?alloc(project_fn, p, t, col_cnt, removed_cols):0; } class check_relation_plugin::rename_fn : public convenient_relation_rename_fn { scoped_ptr m_permute; public: rename_fn(relation_transformer_fn* permute, relation_base const& t, unsigned cycle_len, const unsigned * cycle) : convenient_relation_rename_fn(t.get_signature(), cycle_len, cycle), m_permute(permute) { } virtual ~rename_fn() {} virtual relation_base * operator()(const relation_base & _t) { check_relation const& t = get(_t); check_relation_plugin& p = t.get_plugin(); relation_signature const& sig = get_result_signature(); relation_base* r = (*m_permute)(t.rb()); p.verify_permutation(t.rb(), *r, m_cycle); return alloc(check_relation, p, sig, r); } }; relation_transformer_fn * check_relation_plugin::mk_rename_fn( const relation_base & r, unsigned cycle_len, const unsigned * permutation_cycle) { relation_transformer_fn* p = m_base->mk_rename_fn(get(r).rb(), cycle_len, permutation_cycle); return p?alloc(rename_fn, p, r, cycle_len, permutation_cycle):0; } class check_relation_plugin::filter_equal_fn : public relation_mutator_fn { scoped_ptr m_filter; relation_element m_val; unsigned m_col; public: filter_equal_fn(relation_mutator_fn* filter, relation_base const& t, const relation_element val, unsigned col): m_filter(filter), m_val(val), m_col(col) {} virtual ~filter_equal_fn() { } virtual void operator()(relation_base & tb) { check_relation & t = get(tb); check_relation_plugin& p = t.get_plugin(); (*m_filter)(t.rb()); expr_ref fml = t.m_fml; t.rb().to_formula(t.m_fml); fml = p.m.mk_and(fml, p.m.mk_eq(p.m.mk_var(m_col, t.get_signature()[m_col]), m_val)); p.check_equiv("filter_equal", t.ground(fml), t.ground(t.m_fml)); } }; relation_mutator_fn * check_relation_plugin::mk_filter_equal_fn( const relation_base & t, const relation_element & value, unsigned col) { relation_mutator_fn* r = m_base->mk_filter_equal_fn(get(t).rb(), value, col); return r?alloc(filter_equal_fn, r, t, value, col):0; } class check_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn { scoped_ptr m_filter; const unsigned_vector m_t_cols; const unsigned_vector m_neg_cols; public: negation_filter_fn( relation_intersection_filter_fn* filter, unsigned joined_col_cnt, const unsigned *t_cols, const unsigned *neg_cols) : m_filter(filter), m_t_cols(joined_col_cnt, t_cols), m_neg_cols(joined_col_cnt, neg_cols) { SASSERT(joined_col_cnt > 0); } virtual void operator()(relation_base& tb, const relation_base& negb) { check_relation& t = get(tb); check_relation const& n = get(negb); check_relation_plugin& p = t.get_plugin(); ast_manager& m = p.get_ast_manager(); expr_ref dst0(m); t.to_formula(dst0); (*m_filter)(t.rb(), n.rb()); t.rb().to_formula(t.m_fml); p.verify_filter_by_negation(dst0, t.rb(), n.rb(), m_t_cols, m_neg_cols); } }; relation_intersection_filter_fn * check_relation_plugin::mk_filter_by_negation_fn( const relation_base& t, const relation_base& neg, unsigned joined_col_cnt, const unsigned *t_cols, const unsigned *negated_cols) { relation_intersection_filter_fn* f = m_base->mk_filter_by_negation_fn(get(t).rb(), get(neg).rb(), joined_col_cnt, t_cols, negated_cols); return f?alloc(negation_filter_fn, f, joined_col_cnt, t_cols, negated_cols):0; } /* The filter_by_negation postcondition: filter_by_negation(tgt, neg, columns in tgt: c1,...,cN, corresponding columns in neg: d1,...,dN): tgt_1:={x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } */ void check_relation_plugin::verify_filter_by_negation( expr* dst0, relation_base const& dst, relation_base const& neg, unsigned_vector const& cols1, unsigned_vector const& cols2) { relation_signature const& sig1 = dst.get_signature(); relation_signature const& sig2 = neg.get_signature(); expr_ref dstf(m), negf(m); std::cout << mk_pp(dst0, m) << "\n"; expr_ref_vector eqs(m); dst.to_formula(dstf); std::cout << mk_pp(dstf, m) << "\n"; neg.to_formula(negf); std::cout << mk_pp(negf, m) << "\n"; eqs.push_back(negf); for (unsigned i = 0; i < cols1.size(); ++i) { var_ref v1(m), v2(m); unsigned c1 = cols1[i]; unsigned c2 = cols2[i]; SASSERT(sig1[c1] == sig2[c2]); v1 = m.mk_var(sig2.size() + c1, sig1[c1]); v2 = m.mk_var(c2, sig2[c2]); eqs.push_back(m.mk_eq(v1, v2)); } negf = mk_and(m, eqs.size(), eqs.c_ptr()); ptr_vector rev_sig2(sig2.size(), sig2.c_ptr()); rev_sig2.reverse(); svector names; for (unsigned i = 0; i < sig2.size(); ++i) { names.push_back(symbol(i)); } negf = m.mk_exists(rev_sig2.size(), rev_sig2.c_ptr(), names.c_ptr(), negf); negf = m.mk_and(dst0, m.mk_not(negf)); negf = ground(dst, negf); dstf = ground(dst, dstf); std::cout << negf << "\n"; std::cout << dstf << "\n"; check_equiv("filter by negation", dstf, negf); } class check_relation_plugin::filter_proj_fn : public convenient_relation_project_fn { app_ref m_cond; scoped_ptr m_xform; public: filter_proj_fn(relation_transformer_fn* xform, relation_base const& t, app_ref& cond, unsigned col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(t.get_signature(), col_cnt, removed_cols), m_cond(cond), m_xform(xform) {} virtual ~filter_proj_fn() {} virtual relation_base* operator()(const relation_base & tb) { check_relation const & t = get(tb); check_relation_plugin& p = t.get_plugin(); relation_base* r = (*m_xform)(t.rb()); p.verify_filter_project(t.rb(), *r, m_cond, m_removed_cols); relation_signature const& sig = get_result_signature(); return alloc(check_relation, p, sig, r); } }; relation_transformer_fn * check_relation_plugin::mk_filter_interpreted_and_project_fn( const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { relation_transformer_fn* r = m_base->mk_filter_interpreted_and_project_fn(get(t).rb(), condition, removed_col_cnt, removed_cols); app_ref cond(condition, m); return r?alloc(filter_proj_fn, r, t, cond, removed_col_cnt, removed_cols):0; } } z3-4.4.0/src/muz/rel/dl_check_table.cpp0000644000175000017500000004371512540347414017452 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_check_table.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2010-11-15 Revision History: --*/ #include "dl_check_table.h" #include "dl_table.h" namespace datalog { bool check_table_plugin::can_handle_signature(table_signature const& s) { return m_tocheck.can_handle_signature(s) && m_checker.can_handle_signature(s); } check_table & check_table_plugin::get(table_base& r) { return static_cast(r); } check_table const & check_table_plugin::get(table_base const& r) { return static_cast(r); } table_base& check_table_plugin::checker(table_base& r) { return *get(r).m_checker; } table_base const& check_table_plugin::checker(table_base const& r) { return *get(r).m_checker; } table_base* check_table_plugin::checker(table_base* r) { return r?(get(*r).m_checker):0; } table_base const* check_table_plugin::checker(table_base const* r) { return r?(get(*r).m_checker):0; } table_base& check_table_plugin::tocheck(table_base& r) { return *get(r).m_tocheck; } table_base const& check_table_plugin::tocheck(table_base const& r) { return *get(r).m_tocheck; } table_base* check_table_plugin::tocheck(table_base* r) { return r?(get(*r).m_tocheck):0; } table_base const* check_table_plugin::tocheck(table_base const* r) { return r?(get(*r).m_tocheck):0; } table_base * check_table_plugin::mk_empty(const table_signature & s) { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); table_base* checker = m_checker.mk_empty(s); table_base* tocheck = m_tocheck.mk_empty(s); return alloc(check_table, *this, s, tocheck, checker); } class check_table_plugin::join_fn : public table_join_fn { scoped_ptr m_tocheck; scoped_ptr m_checker; public: join_fn(check_table_plugin& p, const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { m_tocheck = p.get_manager().mk_join_fn(tocheck(t1), tocheck(t2), col_cnt, cols1, cols2); m_checker = p.get_manager().mk_join_fn(checker(t1), checker(t2), col_cnt, cols1, cols2); } virtual table_base* operator()(const table_base & t1, const table_base & t2) { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2)); table_base* tchecker = (*m_checker)(checker(t1), checker(t2)); check_table* result = alloc(check_table, get(t1).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); return result; } }; table_join_fn * check_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(t1) || !check_kind(t2)) { return 0; } return alloc(join_fn, *this, t1, t2, col_cnt, cols1, cols2); } class check_table_plugin::join_project_fn : public table_join_fn { scoped_ptr m_tocheck; scoped_ptr m_checker; public: join_project_fn(check_table_plugin& p, const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { m_tocheck = p.get_manager().mk_join_project_fn(tocheck(t1), tocheck(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); m_checker = p.get_manager().mk_join_project_fn(checker(t1), checker(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } virtual table_base* operator()(const table_base & t1, const table_base & t2) { table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2)); table_base* tchecker = (*m_checker)(checker(t1), checker(t2)); check_table* result = alloc(check_table, get(t1).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); return result; } }; table_join_fn * check_table_plugin::mk_join_project_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { if (!check_kind(t1) || !check_kind(t2)) { return 0; } return alloc(join_project_fn, *this, t1, t2, col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } class check_table_plugin::union_fn : public table_union_fn { scoped_ptr m_tocheck; scoped_ptr m_checker; public: union_fn(check_table_plugin& p, table_base const& tgt, const table_base& src, table_base const* delta) { m_tocheck = p.get_manager().mk_union_fn(tocheck(tgt), tocheck(src), tocheck(delta)); m_checker = p.get_manager().mk_union_fn(checker(tgt), checker(src), checker(delta)); } virtual void operator()(table_base& tgt, const table_base& src, table_base* delta) { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); (*m_tocheck)(tocheck(tgt), tocheck(src), tocheck(delta)); (*m_checker)(checker(tgt), checker(src), checker(delta)); get(tgt).well_formed(); if (delta) { get(*delta).well_formed(); } } }; table_union_fn * check_table_plugin::mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return 0; } return alloc(union_fn, *this, tgt, src, delta); } class check_table_plugin::project_fn : public table_transformer_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; public: project_fn(check_table_plugin& p, const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { m_checker = p.get_manager().mk_project_fn(checker(t), col_cnt, removed_cols); m_tocheck = p.get_manager().mk_project_fn(tocheck(t), col_cnt, removed_cols); } table_base* operator()(table_base const& src) { table_base* tchecker = (*m_checker)(checker(src)); table_base* ttocheck = (*m_tocheck)(tocheck(src)); check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker); return result; } }; table_transformer_fn * check_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { if (!check_kind(t)) { return 0; } return alloc(project_fn, *this, t, col_cnt, removed_cols); } class check_table_plugin::select_equal_and_project_fn : public table_transformer_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; public: select_equal_and_project_fn(check_table_plugin& p, const table_base & t, const table_element & value, unsigned col) { m_checker = p.get_manager().mk_select_equal_and_project_fn(checker(t), value, col); m_tocheck = p.get_manager().mk_select_equal_and_project_fn(tocheck(t), value, col); } table_base* operator()(table_base const& src) { table_base* tchecker = (*m_checker)(checker(src)); table_base* ttocheck = (*m_tocheck)(tocheck(src)); check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker); return result; } }; table_transformer_fn * check_table_plugin::mk_select_equal_and_project_fn(const table_base & t, const table_element & value, unsigned col) { if (!check_kind(t)) { return 0; } return alloc(select_equal_and_project_fn, *this, t, value, col); } class check_table_plugin::rename_fn : public table_transformer_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; public: rename_fn(check_table_plugin& p, const table_base & t, unsigned cycle_len, unsigned const* cycle) { m_checker = p.get_manager().mk_rename_fn(checker(t), cycle_len, cycle); m_tocheck = p.get_manager().mk_rename_fn(tocheck(t), cycle_len, cycle); } table_base* operator()(table_base const& src) { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); table_base* tchecker = (*m_checker)(checker(src)); table_base* ttocheck = (*m_tocheck)(tocheck(src)); check_table* result = alloc(check_table, get(src).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); return result; } }; table_transformer_fn * check_table_plugin::mk_rename_fn(const table_base & t, unsigned len, const unsigned * cycle) { if (!check_kind(t)) { return 0; } return alloc(rename_fn, *this, t, len, cycle); } class check_table_plugin::filter_identical_fn : public table_mutator_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; public: filter_identical_fn(check_table_plugin& p, const table_base & t,unsigned cnt, unsigned const* cols) { m_checker = p.get_manager().mk_filter_identical_fn(checker(t), cnt, cols); m_tocheck = p.get_manager().mk_filter_identical_fn(tocheck(t), cnt, cols); } void operator()(table_base & t) { (*m_checker)(checker(t)); (*m_tocheck)(tocheck(t)); get(t).well_formed(); } }; table_mutator_fn * check_table_plugin::mk_filter_identical_fn(const table_base & t, unsigned col_cnt, const unsigned * identical_cols) { if (check_kind(t)) { return alloc(filter_identical_fn, *this, t, col_cnt, identical_cols); } return 0; } class check_table_plugin::filter_equal_fn : public table_mutator_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; public: filter_equal_fn(check_table_plugin& p, const table_base & t, const table_element & v, unsigned col) { m_checker = p.get_manager().mk_filter_equal_fn(checker(t), v, col); m_tocheck = p.get_manager().mk_filter_equal_fn(tocheck(t), v, col); } virtual void operator()(table_base& src) { (*m_checker)(checker(src)); (*m_tocheck)(tocheck(src)); get(src).well_formed(); } }; table_mutator_fn * check_table_plugin::mk_filter_equal_fn(const table_base & t, const table_element & value, unsigned col) { if (check_kind(t)) { return alloc(filter_equal_fn, *this, t, value, col); } return 0; } class check_table_plugin::filter_interpreted_fn : public table_mutator_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; public: filter_interpreted_fn(check_table_plugin& p, const table_base & t, app * condition) { m_checker = p.get_manager().mk_filter_interpreted_fn(checker(t), condition); m_tocheck = p.get_manager().mk_filter_interpreted_fn(tocheck(t), condition); } virtual void operator()(table_base& src) { (*m_checker)(checker(src)); (*m_tocheck)(tocheck(src)); get(src).well_formed(); } }; table_mutator_fn * check_table_plugin::mk_filter_interpreted_fn(const table_base & t, app * condition) { if (check_kind(t)) { return alloc(filter_interpreted_fn, *this, t, condition); } return 0; } class check_table_plugin::filter_interpreted_and_project_fn : public table_transformer_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; public: filter_interpreted_and_project_fn(check_table_plugin& p, const table_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { m_checker = p.get_manager().mk_filter_interpreted_and_project_fn(checker(t), condition, removed_col_cnt, removed_cols); m_tocheck = p.get_manager().mk_filter_interpreted_and_project_fn(tocheck(t), condition, removed_col_cnt, removed_cols); } table_base* operator()(table_base const& src) { table_base* tchecker = (*m_checker)(checker(src)); table_base* ttocheck = (*m_tocheck)(tocheck(src)); check_table* result = alloc(check_table, get(src).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); return result; } }; table_transformer_fn * check_table_plugin::mk_filter_interpreted_and_project_fn(const table_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { if (check_kind(t)) { return alloc(filter_interpreted_and_project_fn, *this, t, condition, removed_col_cnt, removed_cols); } return 0; } class check_table_plugin::filter_by_negation_fn : public table_intersection_filter_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; public: filter_by_negation_fn( check_table_plugin& p, const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { m_checker = p.get_manager().mk_filter_by_negation_fn(checker(t), checker(negated_obj), joined_col_cnt, t_cols, negated_cols); m_tocheck = p.get_manager().mk_filter_by_negation_fn(tocheck(t), tocheck(negated_obj), joined_col_cnt, t_cols, negated_cols); } virtual void operator()(table_base& src, table_base const& negated_obj) { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); (*m_checker)(checker(src), checker(negated_obj)); (*m_tocheck)(tocheck(src), tocheck(negated_obj)); get(src).well_formed(); } }; table_intersection_filter_fn * check_table_plugin::mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { if (check_kind(t) && check_kind(negated_obj)) { return alloc(filter_by_negation_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols); } return 0; } // ------------------ // check_table check_table::check_table(check_table_plugin & p, const table_signature & sig): table_base(p, sig) { (well_formed()); } check_table::check_table(check_table_plugin & p, const table_signature & sig, table_base* tocheck, table_base* checker): table_base(p, sig), m_checker(checker), m_tocheck(tocheck) { well_formed(); } check_table::~check_table() { m_tocheck->deallocate(); m_checker->deallocate(); } bool check_table::well_formed() const { get_plugin().m_count++; iterator it = m_tocheck->begin(), end = m_tocheck->end(); for (; it != end; ++it) { table_fact fact; it->get_fact(fact); if (!m_checker->contains_fact(fact)) { m_tocheck->display(verbose_stream()); m_checker->display(verbose_stream()); verbose_stream() << get_plugin().m_count << "\n"; UNREACHABLE(); fatal_error(0); return false; } } iterator it2 = m_checker->begin(), end2 = m_checker->end(); for (; it2 != end2; ++it2) { table_fact fact; it2->get_fact(fact); if (!m_tocheck->contains_fact(fact)) { m_tocheck->display(verbose_stream()); m_checker->display(verbose_stream()); verbose_stream() << get_plugin().m_count << "\n"; UNREACHABLE(); fatal_error(0); return false; } } return true; } bool check_table::empty() const { if (m_tocheck->empty() != m_checker->empty()) { m_tocheck->display(verbose_stream()); m_checker->display(verbose_stream()); verbose_stream() << get_plugin().m_count << "\n"; fatal_error(0); } return m_tocheck->empty(); } void check_table::add_fact(const table_fact & f) { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); m_tocheck->add_fact(f); m_checker->add_fact(f); well_formed(); } void check_table::remove_fact(const table_element* f) { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); m_tocheck->remove_fact(f); m_checker->remove_fact(f); well_formed(); } bool check_table::contains_fact(const table_fact & f) const { return m_checker->contains_fact(f); } table_base * check_table::clone() const { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->clone(), m_checker->clone()); return result; } table_base * check_table::complement(func_decl* p, const table_element * func_columns) const { check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->complement(p, func_columns), m_checker->complement(p, func_columns)); return result; } }; z3-4.4.0/src/muz/rel/dl_lazy_table.h0000644000175000017500000002667012540347414017022 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_lazy_table.h Abstract: Structure for delaying table operations. Author: Nikolaj Bjorner (nbjorner) 2013-09-04 Revision History: --*/ #ifndef _DL_LAZY_TABLE_H_ #define _DL_LAZY_TABLE_H_ #include "dl_base.h" #include "ref.h" namespace datalog { class lazy_table; class lazy_table_plugin : public table_plugin { friend class lazy_table; class join_fn; class project_fn; class union_fn; class rename_fn; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; class filter_by_negation_fn; table_plugin& m_plugin; static symbol mk_name(table_plugin& p); public: lazy_table_plugin(table_plugin& p): table_plugin(mk_name(p), p.get_manager()), m_plugin(p) {} virtual bool can_handle_signature(const table_signature & s) { return m_plugin.can_handle_signature(s); } virtual table_base * mk_empty(const table_signature & s); virtual void set_cancel(bool f) { m_plugin.set_cancel(f); } static table_plugin* mk_sparse(relation_manager& rm); protected: virtual table_join_fn * mk_join_fn( const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); virtual table_union_fn * mk_union_fn( const table_base & tgt, const table_base & src, const table_base * delta); virtual table_transformer_fn * mk_project_fn( const table_base & t, unsigned col_cnt, const unsigned * removed_cols); virtual table_transformer_fn * mk_rename_fn( const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); virtual table_mutator_fn * mk_filter_identical_fn( const table_base & t, unsigned col_cnt, const unsigned * identical_cols); virtual table_mutator_fn * mk_filter_equal_fn( const table_base & t, const table_element & value, unsigned col); virtual table_mutator_fn * mk_filter_interpreted_fn( const table_base & t, app * condition); virtual table_intersection_filter_fn * mk_filter_by_negation_fn( const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols); static lazy_table const& get(table_base const& tb); static lazy_table& get(table_base& tb); static lazy_table const* get(table_base const* tb); static lazy_table* get(table_base* tb); }; enum lazy_table_kind { LAZY_TABLE_BASE, LAZY_TABLE_JOIN, LAZY_TABLE_PROJECT, LAZY_TABLE_RENAME, LAZY_TABLE_FILTER_IDENTICAL, LAZY_TABLE_FILTER_EQUAL, LAZY_TABLE_FILTER_INTERPRETED, LAZY_TABLE_FILTER_BY_NEGATION }; class lazy_table_ref { protected: lazy_table_plugin& m_plugin; table_signature m_signature; unsigned m_ref; scoped_rel m_table; relation_manager& rm() { return m_plugin.get_manager(); } virtual table_base* force() = 0; public: lazy_table_ref(lazy_table_plugin& p, table_signature const& sig): m_plugin(p), m_signature(sig), m_ref(0) {} virtual ~lazy_table_ref() {} void inc_ref() { ++m_ref; } void dec_ref() { --m_ref; if (0 == m_ref) dealloc(this); } void release_table() { m_table.release(); } virtual lazy_table_kind kind() const = 0; table_signature const& get_signature() const { return m_signature; } lazy_table_plugin & get_lplugin() const { return m_plugin; } table_base* eval() { if (!m_table) { m_table = force(); } SASSERT(m_table); return m_table.get(); } }; class lazy_table : public table_base { protected: mutable ref m_ref; public: lazy_table(lazy_table_ref* t): table_base(t->get_lplugin(), t->get_signature()), m_ref(t) {} virtual ~lazy_table() {} lazy_table_plugin& get_lplugin() const { return dynamic_cast(table_base::get_plugin()); } virtual table_base * clone() const; virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const; virtual bool empty() const; virtual bool contains_fact(const table_fact & f) const; virtual void remove_fact(table_element const* fact); virtual void remove_facts(unsigned fact_cnt, const table_fact * facts); virtual void remove_facts(unsigned fact_cnt, const table_element * facts); virtual void reset(); virtual void add_fact(table_fact const& f); virtual unsigned get_size_estimate_rows() const { return 1; } virtual unsigned get_size_estimate_bytes() const { return 1; } virtual bool knows_exact_size() const { return false; } table_base* eval() const; virtual table_base::iterator begin() const; virtual table_base::iterator end() const; lazy_table_ref* get_ref() const { return m_ref.get(); } void set(lazy_table_ref* r) { m_ref = r; } }; class lazy_table_base : public lazy_table_ref { public: lazy_table_base(lazy_table_plugin & p, table_base* table) : lazy_table_ref(p, table->get_signature()) { m_table = table; // SASSERT(&p.m_plugin == &table->get_lplugin()); } virtual ~lazy_table_base() {} virtual lazy_table_kind kind() const { return LAZY_TABLE_BASE; } virtual table_base* force() { return m_table.get(); } }; class lazy_table_join : public lazy_table_ref { unsigned_vector m_cols1; unsigned_vector m_cols2; ref m_t1; ref m_t2; public: lazy_table_join(unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, lazy_table const& t1, lazy_table const& t2, table_signature const& sig) : lazy_table_ref(t1.get_lplugin(), sig), m_cols1(col_cnt, cols1), m_cols2(col_cnt, cols2), m_t1(t1.get_ref()), m_t2(t2.get_ref()) { } virtual ~lazy_table_join() {} virtual lazy_table_kind kind() const { return LAZY_TABLE_JOIN; } unsigned_vector const& cols1() const { return m_cols1; } unsigned_vector const& cols2() const { return m_cols2; } lazy_table_ref* t1() const { return m_t1.get(); } lazy_table_ref* t2() const { return m_t2.get(); } virtual table_base* force(); }; class lazy_table_project : public lazy_table_ref { unsigned_vector m_cols; ref m_src; public: lazy_table_project(unsigned col_cnt, const unsigned * cols, lazy_table const& src, table_signature const& sig) : lazy_table_ref(src.get_lplugin(), sig), m_cols(col_cnt, cols), m_src(src.get_ref()) {} virtual ~lazy_table_project() {} virtual lazy_table_kind kind() const { return LAZY_TABLE_PROJECT; } unsigned_vector const& cols() const { return m_cols; } lazy_table_ref* src() const { return m_src.get(); } virtual table_base* force(); }; class lazy_table_rename : public lazy_table_ref { unsigned_vector m_cols; ref m_src; public: lazy_table_rename(unsigned col_cnt, const unsigned * cols, lazy_table const& src, table_signature const& sig) : lazy_table_ref(src.get_lplugin(), sig), m_cols(col_cnt, cols), m_src(src.get_ref()) {} virtual ~lazy_table_rename() {} virtual lazy_table_kind kind() const { return LAZY_TABLE_RENAME; } unsigned_vector const& cols() const { return m_cols; } lazy_table_ref* src() const { return m_src.get(); } virtual table_base* force(); }; class lazy_table_filter_identical : public lazy_table_ref { unsigned_vector m_cols; ref m_src; public: lazy_table_filter_identical(unsigned col_cnt, const unsigned * cols, lazy_table const& src) : lazy_table_ref(src.get_lplugin(), src.get_signature()), m_cols(col_cnt, cols), m_src(src.get_ref()) {} virtual ~lazy_table_filter_identical() {} virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_IDENTICAL; } unsigned_vector const& cols() const { return m_cols; } lazy_table_ref* src() const { return m_src.get(); } virtual table_base* force(); }; class lazy_table_filter_equal : public lazy_table_ref { unsigned m_col; table_element m_value; ref m_src; public: lazy_table_filter_equal(unsigned col, table_element value, lazy_table const& src) : lazy_table_ref(src.get_lplugin(), src.get_signature()), m_col(col), m_value(value), m_src(src.get_ref()) {} virtual ~lazy_table_filter_equal() {} virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_EQUAL; } unsigned col() const { return m_col; } table_element value() const { return m_value; } lazy_table_ref* src() const { return m_src.get(); } virtual table_base* force(); }; class lazy_table_filter_interpreted : public lazy_table_ref { app_ref m_condition; ref m_src; public: lazy_table_filter_interpreted(lazy_table const& src, app* condition) : lazy_table_ref(src.get_lplugin(), src.get_signature()), m_condition(condition, src.get_lplugin().get_ast_manager()), m_src(src.get_ref()) {} virtual ~lazy_table_filter_interpreted() {} virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_INTERPRETED; } app* condition() const { return m_condition; } lazy_table_ref* src() const { return m_src.get(); } virtual table_base* force(); }; class lazy_table_filter_by_negation : public lazy_table_ref { ref m_tgt; ref m_src; unsigned_vector m_cols1; unsigned_vector m_cols2; public: lazy_table_filter_by_negation(lazy_table const& tgt, lazy_table const& src, unsigned_vector const& c1, unsigned_vector const& c2) : lazy_table_ref(tgt.get_lplugin(), tgt.get_signature()), m_tgt(tgt.get_ref()), m_src(src.get_ref()), m_cols1(c1), m_cols2(c2) {} virtual ~lazy_table_filter_by_negation() {} virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_BY_NEGATION; } lazy_table_ref* tgt() const { return m_tgt.get(); } lazy_table_ref* src() const { return m_src.get(); } unsigned_vector const& cols1() const { return m_cols1; } unsigned_vector const& cols2() const { return m_cols2; } virtual table_base* force(); }; } #endif z3-4.4.0/src/muz/rel/aig_exporter.h0000644000175000017500000000376212540347414016702 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: aig_exporter.h Abstract: Export AIG files from horn clauses --*/ #ifndef _AIG_EXPORTER_H_ #define _AIG_EXPORTER_H_ #include "aig.h" #include "dl_rule_set.h" #include #include #include "rel_context.h" namespace datalog { class aig_exporter { public: aig_exporter(const rule_set& rules, context& ctx, const fact_vector *facts = 0); void operator()(std::ostream& out); private: typedef obj_map decl_id_map; typedef obj_map aig_expr_id_map; typedef std::map, unsigned> and_gates_map; const rule_set& m_rules; const fact_vector *m_facts; ast_manager& m; rule_manager& m_rm; aig_manager m_aigm; decl_id_map m_decl_id_map; unsigned m_next_decl_id; aig_expr_id_map m_aig_expr_id_map; unsigned m_next_aig_expr_id; and_gates_map m_and_gates_map; unsigned m_num_and_gates; expr_ref_vector m_latch_vars, m_latch_varsp; expr_ref_vector m_ruleid_var_set, m_ruleid_varp_set; unsigned_vector m_input_vars; std::stringstream m_buffer; void mk_latch_vars(unsigned n); expr* get_latch_var(unsigned i, const expr_ref_vector& vars); void assert_pred_id(func_decl *decl, const expr_ref_vector& vars, expr_ref_vector& exprs); void collect_var_substs(substitution& subst, const app *h, const expr_ref_vector& vars, expr_ref_vector& eqs); unsigned expr_to_aig(const expr *e); unsigned neg(unsigned id) const; unsigned mk_and(unsigned id1, unsigned id2); unsigned mk_or(unsigned id1, unsigned id2); unsigned get_var(const expr *e); unsigned mk_var(const expr *e); unsigned mk_input_var(const expr *e = 0); unsigned mk_expr_id(); }; } #endif z3-4.4.0/src/muz/rel/dl_mk_explanations.h0000644000175000017500000000444612540347414020065 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_explanations.h Abstract: Author: Krystof Hoder (t-khoder) 2010-11-08. Revision History: --*/ #ifndef _DL_MK_EXPLANATIONS_H_ #define _DL_MK_EXPLANATIONS_H_ #include "dl_context.h" #include "dl_rule_transformer.h" namespace datalog { class explanation_relation; class explanation_relation_plugin; class mk_explanations : public rule_transformer::plugin { typedef obj_map decl_map; ast_manager & m_manager; context & m_context; dl_decl_util & m_decl_util; bool m_relation_level; ast_ref_vector m_pinned; explanation_relation_plugin * m_er_plugin; sort * m_e_sort; scoped_rel m_e_fact_relation; decl_map m_e_decl_map; symbol get_rule_symbol(rule * r); app * get_e_lit(app * lit, unsigned e_var_idx); rule * get_e_rule(rule * r); /** If \c m_relation_level is true, ensure \c e_decl predicate will be represented by the right relation object. \c orig is the predicate corresponding to \c e_decl without the explanation column. */ void assign_rel_level_kind(func_decl * e_decl, func_decl * orig); void translate_rel_level_relation(relation_manager & rmgr, relation_base & orig, relation_base & e_rel); void transform_rules(const rule_set & src, rule_set & dst); void transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst); public: /** If relation_level is true, the explanation will not be stored for each fact, but we will rather store history of the whole relation. */ mk_explanations(context & ctx); /** \brief Return explanation predicate that corresponds to \c orig_decl. */ func_decl * get_e_decl(func_decl * orig_decl); static func_decl * get_union_decl(context & ctx); func_decl * get_union_decl() const { return get_union_decl(m_context); } rule_set * operator()(rule_set const & source); static expr* get_explanation(relation_base const& r); }; }; #endif /* _DL_MK_EXPLANATIONS_H_ */ z3-4.4.0/src/muz/rel/udoc_relation.cpp0000644000175000017500000013645512540347414017402 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: udoc_relation.cpp Abstract: Relation based on union of DOCs. Author: Nuno Lopes (a-nlopes) 2013-03-01 Nikolaj Bjorner (nbjorner) 2014-09-15 Revision History: Revised version of dl_hassel_diff facilities. Notes: --*/ #include "udoc_relation.h" #include "dl_relation_manager.h" #include "qe_util.h" #include "ast_util.h" #include "smt_kernel.h" namespace datalog { udoc_relation::udoc_relation(udoc_plugin& p, relation_signature const& sig): relation_base(p, sig), dm(p.dm(p.num_signature_bits(sig))) { unsigned column = 0; for (unsigned i = 0; i < sig.size(); ++i) { m_column_info.push_back(column); column += p.num_sort_bits(sig[i]); } m_column_info.push_back(column); } udoc_relation::~udoc_relation() { reset(); } void udoc_relation::reset() { m_elems.reset(dm); } void udoc_relation::expand_column_vector(unsigned_vector& v, const udoc_relation* other) const { unsigned_vector orig; orig.swap(v); for (unsigned i = 0; i < orig.size(); ++i) { unsigned col, limit; if (orig[i] < get_num_cols()) { col = column_idx(orig[i]); limit = col + column_num_bits(orig[i]); } else { unsigned idx = orig[i] - get_num_cols(); col = get_num_bits() + other->column_idx(idx); limit = col + other->column_num_bits(idx); } for (; col < limit; ++col) { v.push_back(col); } } } doc* udoc_relation::fact2doc(const relation_fact & f) const { doc* d = dm.allocate0(); for (unsigned i = 0; i < f.size(); ++i) { unsigned bv_size; rational val; VERIFY(get_plugin().is_numeral(f[i], val, bv_size)); SASSERT(bv_size == column_num_bits(i)); unsigned lo = column_idx(i); unsigned hi = column_idx(i + 1); dm.tbvm().set(d->pos(),val, hi-1, lo); } return d; } void udoc_relation::add_fact(const relation_fact & f) { doc* d = fact2doc(f); m_elems.insert(dm, d); } void udoc_relation::add_new_fact(const relation_fact & f) { m_elems.push_back(fact2doc(f)); } bool udoc_relation::empty() const { return m_elems.is_empty_complete(get_plugin().m, dm); } bool udoc_relation::contains_fact(const relation_fact & f) const { doc_ref d(dm, fact2doc(f)); return m_elems.contains(dm, *d); } udoc_relation * udoc_relation::clone() const { udoc_relation* result = udoc_plugin::get(get_plugin().mk_empty(get_signature())); for (unsigned i = 0; i < m_elems.size(); ++i) { result->m_elems.push_back(dm.allocate(m_elems[i])); } return result; } udoc_relation * udoc_relation::complement(func_decl* f) const { udoc_relation* result = udoc_plugin::get(get_plugin().mk_empty(get_signature())); m_elems.complement(dm, result->m_elems); return result; } void udoc_relation::to_formula(expr_ref& fml) const { ast_manager& m = fml.get_manager(); expr_ref_vector disj(m); for (unsigned i = 0; i < m_elems.size(); ++i) { disj.push_back(to_formula(m_elems[i])); } fml = mk_or(m, disj.size(), disj.c_ptr()); } expr_ref udoc_relation::to_formula(doc const& d) const { ast_manager& m = get_plugin().get_ast_manager(); expr_ref result(m); expr_ref_vector conjs(m); conjs.push_back(to_formula(d.pos())); for (unsigned i = 0; i < d.neg().size(); ++i) { conjs.push_back(m.mk_not(to_formula(d.neg()[i]))); } result = mk_and(m, conjs.size(), conjs.c_ptr()); return result; } expr_ref udoc_relation::to_formula(tbv const& t) const { udoc_plugin& p = get_plugin(); ast_manager& m = p.get_ast_manager(); expr_ref result(m); expr_ref_vector conjs(m); for (unsigned i = 0; i < get_num_cols(); ++i) { var_ref v(m); v = m.mk_var(i, get_signature()[i]); unsigned lo = column_idx(i); unsigned hi = column_idx(i+1); rational r(0); unsigned lo1 = lo; bool is_x = true; for (unsigned j = lo; j < hi; ++j) { switch(t[j]) { case BIT_0: if (is_x) is_x = false, lo1 = j, r.reset(); break; case BIT_1: if (is_x) is_x = false, lo1 = j, r.reset(); r += rational::power_of_two(j - lo1); break; case BIT_x: if (!is_x) { SASSERT(p.bv.is_bv_sort(get_signature()[i])); conjs.push_back(m.mk_eq(p.bv.mk_extract(j-1-lo,lo1-lo,v), p.bv.mk_numeral(r,j-lo1))); } is_x = true; break; default: UNREACHABLE(); } } if (!is_x) { expr_ref num(m); if (lo1 == lo) { num = p.mk_numeral(r, get_signature()[i]); conjs.push_back(m.mk_eq(v, num)); } else { num = p.bv.mk_numeral(r, hi-lo1); conjs.push_back(m.mk_eq(p.bv.mk_extract(hi-1-lo,lo1-lo,v), num)); } } } result = mk_and(m, conjs.size(), conjs.c_ptr()); return result; } udoc_plugin& udoc_relation::get_plugin() const { return static_cast(relation_base::get_plugin()); } void udoc_relation::display(std::ostream& out) const { m_elems.display(dm, out); out << "\n"; } unsigned udoc_relation::get_size_estimate_bytes() const { return sizeof(*this) + m_elems.get_size_estimate_bytes(dm); } // ------------- udoc_plugin::udoc_plugin(relation_manager& rm): relation_plugin(udoc_plugin::get_name(), rm), m(rm.get_context().get_manager()), bv(m), dl(m), m_disable_fast_pass(false) { } udoc_plugin::~udoc_plugin() { u_map::iterator it = m_dms.begin(), end = m_dms.end(); for (; it != end; ++it) { dealloc(it->m_value); } } udoc_relation& udoc_plugin::get(relation_base& r) { return dynamic_cast(r); } udoc_relation* udoc_plugin::get(relation_base* r) { return r?dynamic_cast(r):0; } udoc_relation const & udoc_plugin::get(relation_base const& r) { return dynamic_cast(r); } doc_manager& udoc_plugin::dm(relation_signature const& sig) { return dm(num_signature_bits(sig)); } doc_manager& udoc_plugin::dm(unsigned n) { doc_manager* r; if (!m_dms.find(n, r)) { r = alloc(doc_manager, n); m_dms.insert(n, r); } return *r; } bool udoc_relation::is_var_range(expr* e, unsigned& hi, unsigned& lo, unsigned& v) const { udoc_plugin& p = get_plugin(); if (is_var(e)) { v = to_var(e)->get_idx(); hi = p.num_sort_bits(e)-1; lo = 0; return true; } expr* e2; if (p.bv.is_extract(e, lo, hi, e2) && is_var(e2)) { v = to_var(e2)->get_idx(); SASSERT(lo <= hi); return true; } return false; } expr* udoc_plugin::mk_numeral(rational const& r, sort* s) { if (bv.is_bv_sort(s)) { return bv.mk_numeral(r, s); } if (m.is_bool(s)) { if (r.is_zero()) return m.mk_false(); return m.mk_true(); } SASSERT(dl.is_finite_sort(s)); return dl.mk_numeral(r.get_uint64(), s); } bool udoc_plugin::is_numeral(expr* e, rational& r, unsigned& num_bits) { if (bv.is_numeral(e, r, num_bits)) return true; if (m.is_true(e)) { r = rational(1); num_bits = 1; return true; } if (m.is_false(e)) { r = rational(0); num_bits = 1; return true; } uint64 n, sz; ast_manager& m = get_ast_manager(); if (dl.is_numeral(e, n) && dl.try_get_size(m.get_sort(e), sz)) { num_bits = 0; while (sz > 0) ++num_bits, sz = sz/2; r = rational(n, rational::ui64()); return true; } return false; } unsigned udoc_plugin::num_sort_bits(sort* s) const { unsigned num_bits = 0; if (bv.is_bv_sort(s)) return bv.get_bv_size(s); if (m.is_bool(s)) return 1; uint64 sz; if (dl.try_get_size(s, sz)) { while (sz > 0) ++num_bits, sz /= 2; return num_bits; } UNREACHABLE(); return 0; } unsigned udoc_plugin::num_signature_bits(relation_signature const& sig) { unsigned result = 0; for (unsigned i = 0; i < sig.size(); ++i) { result += num_sort_bits(sig[i]); } return result; } bool udoc_plugin::is_finite_sort(sort* s) const { return bv.is_bv_sort(s) || dl.is_finite_sort(s); } bool udoc_plugin::can_handle_signature(const relation_signature & sig) { for (unsigned i = 0; i < sig.size(); ++i) { if (!is_finite_sort(sig[i])) return false; } return true; } relation_base * udoc_plugin::mk_empty(const relation_signature & sig) { return alloc(udoc_relation, *this, sig); } relation_base * udoc_plugin::mk_full(func_decl* p, const relation_signature & s) { udoc_relation* r = get(mk_empty(s)); r->get_udoc().push_back(dm(s).allocateX()); return r; } class udoc_plugin::join_fn : public convenient_relation_join_fn { doc_manager& dm; doc_manager& dm1; doc_manager& dm2; public: join_fn(udoc_plugin& p, udoc_relation const& t1, udoc_relation const& t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_relation_join_fn(t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2), dm(p.dm(get_result_signature())), dm1(t1.get_dm()), dm2(t2.get_dm()) { t1.expand_column_vector(m_cols1); t2.expand_column_vector(m_cols2); } virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { udoc_relation const& r1 = get(_r1); udoc_relation const& r2 = get(_r2); TRACE("doc", r1.display(tout << "r1:\n"); r2.display(tout << "r2:\n");); udoc_plugin& p = r1.get_plugin(); relation_signature const& sig = get_result_signature(); udoc_relation * result = alloc(udoc_relation, p, sig); udoc const& d1 = r1.get_udoc(); udoc const& d2 = r2.get_udoc(); udoc& r = result->get_udoc(); r.join(d1, d2, dm, dm1, m_cols1, m_cols2); TRACE("doc", result->display(tout << "result:\n");); IF_VERBOSE(3, result->display(verbose_stream() << "join result:\n");); SASSERT(r.well_formed(result->get_dm())); return result; } }; relation_join_fn * udoc_plugin::mk_join_fn( const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(t1) || !check_kind(t2)) { return 0; } return alloc(join_fn, *this, get(t1), get(t2), col_cnt, cols1, cols2); } class udoc_plugin::project_fn : public convenient_relation_project_fn { bit_vector m_to_delete; public: project_fn(udoc_relation const & t, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(t.get_signature(), removed_col_cnt, removed_cols) { t.expand_column_vector(m_removed_cols); unsigned n = t.get_dm().num_tbits(); m_to_delete.resize(n, false); for (unsigned i = 0; i < m_removed_cols.size(); ++i) { m_to_delete.set(m_removed_cols[i], true); } } virtual relation_base * operator()(const relation_base & tb) { TRACE("doc", tb.display(tout << "src:\n");); udoc_relation const& t = get(tb); udoc_plugin& p = t.get_plugin(); udoc_relation* r = udoc_plugin::get(p.mk_empty(get_result_signature())); doc_manager& dm1 = t.get_dm(); doc_manager& dm2 = r->get_dm(); doc_ref d2(dm2); udoc const& ud1 = t.get_udoc(); udoc& ud2 = r->get_udoc(); for (unsigned i = 0; i < ud1.size(); ++i) { d2 = dm1.project(dm2, m_to_delete, ud1[i]); ud2.push_back(d2.detach()); } TRACE("doc", tout << "final size: " << r->get_size_estimate_rows() << '\n';); SASSERT(ud2.well_formed(dm2)); return r; } }; relation_transformer_fn * udoc_plugin::mk_project_fn( const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) { if (!check_kind(t)) return 0; return alloc(project_fn, get(t), col_cnt, removed_cols); } class udoc_plugin::rename_fn : public convenient_relation_rename_fn { unsigned_vector m_permutation; public: rename_fn(udoc_relation const& t, unsigned cycle_len, const unsigned * cycle) : convenient_relation_rename_fn(t.get_signature(), cycle_len, cycle) { udoc_plugin& p = t.get_plugin(); ast_manager& m = p.get_ast_manager(); relation_signature const& sig1 = t.get_signature(); relation_signature const& sig2 = get_result_signature(); unsigned_vector permutation0, column_info; for (unsigned i = 0; i < t.get_num_bits(); ++i) { m_permutation.push_back(i); } for (unsigned i = 0; i < sig1.size(); ++i) { permutation0.push_back(i); } for (unsigned i = 0; i < cycle_len; ++i) { unsigned j = (i + 1)%cycle_len; unsigned col1 = cycle[i]; unsigned col2 = cycle[j]; permutation0[col2] = col1; } unsigned column = 0; for (unsigned i = 0; i < sig2.size(); ++i) { column_info.push_back(column); column += p.num_sort_bits(sig2[i]); } column_info.push_back(column); SASSERT(column == t.get_num_bits()); TRACE("doc", sig1.output(m, tout << "sig1: "); tout << "\n"; sig2.output(m, tout << "sig2: "); tout << "\n"; tout << "permute: "; for (unsigned i = 0; i < permutation0.size(); ++i) { tout << permutation0[i] << " "; } tout << "\n"; tout << "cycle: "; for (unsigned i = 0; i < cycle_len; ++i) { tout << cycle[i] << " "; } tout << "\n"; ); // 0 -> 2 // [3:2:1] -> [1:2:3] // [3,4,5,1,2,0] for (unsigned i = 0; i < sig1.size(); ++i) { unsigned len = t.column_num_bits(i); unsigned lo1 = t.column_idx(i); unsigned col2 = permutation0[i]; unsigned lo2 = column_info[col2]; SASSERT(lo2 + len <= t.get_num_bits()); SASSERT(lo1 + len <= t.get_num_bits()); for (unsigned k = 0; k < len; ++k) { m_permutation[k + lo1] = k + lo2; } } } virtual relation_base * operator()(const relation_base & _r) { udoc_relation const& r = get(_r); TRACE("doc", r.display(tout << "r:\n");); udoc_plugin& p = r.get_plugin(); relation_signature const& sig = get_result_signature(); udoc_relation* result = alloc(udoc_relation, p, sig); udoc const& src = r.get_udoc(); udoc& dst = result->get_udoc(); doc_manager& dm = r.get_dm(); SASSERT(&result->get_dm() == &dm); for (unsigned i = 0; i < src.size(); ++i) { dst.push_back(dm.allocate(src[i], m_permutation.c_ptr())); } TRACE("doc", result->display(tout << "result:\n");); SASSERT(dst.well_formed(dm)); return result; } }; relation_transformer_fn * udoc_plugin::mk_rename_fn( const relation_base & r, unsigned cycle_len, const unsigned * permutation_cycle) { if (check_kind(r)) { return alloc(rename_fn, get(r), cycle_len, permutation_cycle); } else { return 0; } } class udoc_plugin::union_fn : public relation_union_fn { public: union_fn() {} virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { TRACE("doc", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); udoc_relation& r = get(_r); udoc_relation const& src = get(_src); udoc_relation* d = get(_delta); doc_manager& dm = r.get_dm(); udoc* d1 = 0; if (d) d1 = &d->get_udoc(); IF_VERBOSE(3, r.display(verbose_stream() << "orig: ");); r.get_plugin().mk_union(dm, r.get_udoc(), src.get_udoc(), d1); SASSERT(r.get_udoc().well_formed(dm)); SASSERT(!d1 || d1->well_formed(dm)); TRACE("doc", _r.display(tout << "dst':\n"); ); IF_VERBOSE(3, r.display(verbose_stream() << "union: ");); IF_VERBOSE(3, if (d) d->display(verbose_stream() << "delta: ");); } }; void udoc_plugin::mk_union(doc_manager& dm, udoc& dst, udoc const& src, udoc* delta) { bool deltaempty = delta ? delta->is_empty() : false; if (dst.is_empty()) { for (unsigned i = 0; i < src.size(); ++i) { dst.push_back(dm.allocate(src[i])); if (delta) { if (deltaempty) delta->push_back(dm.allocate(src[i])); else delta->insert(dm, dm.allocate(src[i])); } } } else { for (unsigned i = 0; i < src.size(); ++i) { if (dst.insert(dm, dm.allocate(src[i])) && delta) { if (deltaempty) delta->push_back(dm.allocate(src[i])); else delta->insert(dm, dm.allocate(src[i])); } } } } relation_union_fn * udoc_plugin::mk_union_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return 0; } return alloc(union_fn); } relation_union_fn * udoc_plugin::mk_widen_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { return mk_union_fn(tgt, src, delta); } class udoc_plugin::filter_identical_fn : public relation_mutator_fn { unsigned_vector m_cols; unsigned m_size; bit_vector m_empty_bv; union_find_default_ctx union_ctx; union_find<> m_equalities; public: filter_identical_fn(const relation_base & _r, unsigned col_cnt, const unsigned *identical_cols) : m_cols(col_cnt), m_equalities(union_ctx) { udoc_relation const& r = get(_r); m_size = r.column_num_bits(identical_cols[0]); m_empty_bv.resize(r.get_num_bits(), false); for (unsigned i = 0; i < col_cnt; ++i) { m_cols[i] = r.column_idx(identical_cols[i]); } for (unsigned i = 0, e = m_empty_bv.size(); i < e; ++i) { m_equalities.mk_var(); } for (unsigned i = 1; i < col_cnt; ++i) { for (unsigned j = 0; j < m_size; ++j) { m_equalities.merge(m_cols[0]+j ,m_cols[i]+j); } } } virtual void operator()(relation_base & _r) { udoc_relation& r = get(_r); udoc& d = r.get_udoc(); doc_manager& dm = r.get_dm(); d.merge(dm, m_cols[0], m_size, m_equalities, m_empty_bv); SASSERT(d.well_formed(dm)); TRACE("doc", tout << "final size: " << r.get_size_estimate_rows() << '\n';); } }; relation_mutator_fn * udoc_plugin::mk_filter_identical_fn( const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { return check_kind(t)?alloc(filter_identical_fn, t, col_cnt, identical_cols):0; } class udoc_plugin::filter_equal_fn : public relation_mutator_fn { doc_manager& dm; doc* m_filter; public: filter_equal_fn(udoc_plugin& p, const udoc_relation & t, const relation_element val, unsigned col): dm(p.dm(t.get_signature())) { rational r; unsigned num_bits; VERIFY(p.is_numeral(val, r, num_bits)); m_filter = dm.allocateX(); unsigned lo = t.column_idx(col); unsigned hi = t.column_idx(col+1); SASSERT(num_bits == hi - lo); dm.tbvm().set(m_filter->pos(), r, hi-1, lo); } virtual ~filter_equal_fn() { dm.deallocate(m_filter); } virtual void operator()(relation_base & tb) { udoc_relation & t = get(tb); t.get_udoc().intersect(dm, *m_filter); SASSERT(t.get_udoc().well_formed(t.get_dm())); } }; relation_mutator_fn * udoc_plugin::mk_filter_equal_fn( const relation_base & t, const relation_element & value, unsigned col) { if (!check_kind(t)) return 0; return alloc(filter_equal_fn, *this, get(t), value, col); } bool udoc_relation::is_guard(unsigned n, expr* const* gs) const { for (unsigned i = 0; i < n; ++i) { if (!is_guard(gs[i])) return false; } return true; } bool udoc_relation::is_guard(expr* g) const { udoc_plugin& p = get_plugin(); ast_manager& m = p.get_ast_manager(); bv_util& bv = p.bv; expr* e1, *e2; unsigned hi, lo, v; if (m.is_and(g) || m.is_or(g) || m.is_not(g) || m.is_true(g) || m.is_false(g)) { return is_guard(to_app(g)->get_num_args(), to_app(g)->get_args()); } if (m.is_eq(g, e1, e2) && bv.is_bv(e1)) { if (is_var_range(e1, hi, lo, v) && is_ground(e2)) return true; if (is_var_range(e2, hi, lo, v) && is_ground(e1)) return true; } if (is_var(g)) { return true; } return false; } void udoc_relation::extract_guard(expr* cond, expr_ref& guard, expr_ref& rest) const { rest.reset(); ast_manager& m = get_plugin().get_ast_manager(); expr_ref_vector conds(m), guards(m), rests(m); conds.push_back(cond); qe::flatten_and(conds); for (unsigned i = 0; i < conds.size(); ++i) { expr* g = conds[i].get(); if (is_guard(g)) { guards.push_back(g); } else { rests.push_back(g); } } guard = mk_and(m, guards.size(), guards.c_ptr()); rest = mk_and(m, rests.size(), rests.c_ptr()); } void udoc_relation::extract_equalities(expr* g, expr_ref& rest, subset_ints& equalities, unsigned_vector& roots) const { rest.reset(); ast_manager& m = get_plugin().get_ast_manager(); expr_ref_vector conds(m); conds.push_back(g); qe::flatten_and(conds); expr* e1, *e2; for (unsigned i = 0; i < conds.size(); ++i) { expr* g = conds[i].get(); if (m.is_eq(g, e1, e2)) { extract_equalities(e1, e2, conds, equalities, roots); conds[i] = conds.back(); conds.pop_back(); } } rest = mk_and(m, conds.size(), conds.c_ptr()); } void udoc_relation::extract_equalities( expr* e1, expr* e2, expr_ref_vector& conds, subset_ints& equalities, unsigned_vector& roots) const { udoc_plugin& p = get_plugin(); ast_manager& m = p.get_ast_manager(); bv_util& bv = p.bv; th_rewriter rw(m); unsigned hi, lo1, lo2, hi1, hi2, v1, v2; if (bv.is_concat(e2)) { std::swap(e1, e2); } if (bv.is_concat(e1)) { expr_ref e3(m); app* a1 = to_app(e1); hi = p.num_sort_bits(e1)-1; unsigned n = a1->get_num_args(); for (unsigned i = 0; i < n; ++i) { expr* e = a1->get_arg(i); unsigned sz = p.num_sort_bits(e); e3 = bv.mk_extract(hi, hi-sz+1, e2); rw(e3); extract_equalities(e, e3, conds, equalities, roots); hi -= sz; } return; } if (is_var_range(e1, hi1, lo1, v1) && is_var_range(e2, hi2, lo2, v2)) { unsigned col1 = column_idx(v1); lo1 += col1; hi1 += col1; unsigned col2 = column_idx(v2); lo2 += col2; hi2 += col2; for (unsigned j = 0; j <= hi1-lo1; ++j) { roots.push_back(lo1 + j); equalities.merge(lo1 + j, lo2 + j); } return; } conds.push_back(m.mk_eq(e1, e2)); } void udoc_relation::compile_guard(expr* g, udoc& d, bit_vector const& discard_cols) const { d.reset(dm); d.push_back(dm.allocateX()); apply_guard(g, d, discard_cols); } void udoc_relation::apply_guard(expr* g, udoc& result, bit_vector const& discard_cols) const { // datastructure to store equalities with columns that will be projected out union_find_default_ctx union_ctx; subset_ints equalities(union_ctx); for (unsigned i = 0, e = discard_cols.size(); i < e; ++i) { equalities.mk_var(); } apply_guard(g, result, equalities, discard_cols); } bool udoc_relation::apply_ground_eq(doc_ref& d, unsigned v, unsigned hi, unsigned lo, expr* c) const { udoc_plugin& p = get_plugin(); unsigned num_bits; rational r; unsigned col = column_idx(v); lo += col; hi += col; if (p.is_numeral(c, r, num_bits)) { d = dm.allocateX(); dm.tbvm().set(d->pos(), r, hi, lo); return true; } // other cases? return false; } bool udoc_relation::apply_bv_eq( expr* e1, expr* e2, bit_vector const& discard_cols, udoc& result) const { udoc_plugin& p = get_plugin(); ast_manager& m = p.get_ast_manager(); bv_util& bv = p.bv; th_rewriter rw(m); doc_ref d(get_dm()); unsigned hi, lo, lo1, lo2, hi1, hi2, v, v1, v2; if (bv.is_concat(e2)) { std::swap(e1, e2); } if (bv.is_concat(e1)) { expr_ref e3(m); app* a1 = to_app(e1); hi = p.num_sort_bits(e1)-1; unsigned n = a1->get_num_args(); for (unsigned i = 0; i < n; ++i) { expr* e = a1->get_arg(i); unsigned sz = p.num_sort_bits(e); e3 = bv.mk_extract(hi, hi-sz+1, e2); rw(e3); if (!apply_bv_eq(e, e3, discard_cols, result)) return false; hi -= sz; } return true; } if (is_ground(e1)) { std::swap(e1, e2); } if (is_var_range(e1, hi, lo, v) && is_ground(e2) && apply_ground_eq(d, v, hi, lo, e2)) { result.intersect(dm, *d); return true; } if (is_var_range(e1, hi1, lo1, v1) && is_var_range(e2, hi2, lo2, v2)) { unsigned idx1 = lo1 + column_idx(v1); unsigned idx2 = lo2 + column_idx(v2); unsigned length = hi1-lo1+1; result.merge(dm, idx1, idx2, length, discard_cols); return true; } return false; } void udoc_relation::apply_guard( expr* g, udoc& result, subset_ints const& equalities, bit_vector const& discard_cols) const { ast_manager& m = get_plugin().get_ast_manager(); bv_util& bv = get_plugin().bv; expr *e0, *e1, *e2; unsigned hi, lo, v; doc_ref d(get_dm()); if (result.is_empty()) { } else if (m.is_true(g)) { } else if (m.is_false(g)) { result.reset(dm); } else if (m.is_and(g)) { for (unsigned i = 0; !result.is_empty() && i < to_app(g)->get_num_args(); ++i) { apply_guard(to_app(g)->get_arg(i), result, equalities, discard_cols); } } else if (m.is_not(g, e0) && m.is_eq(e0, e1, e2) && bv.is_bv(e1) && is_var_range(e1, hi, lo, v) && is_ground(e2) && apply_ground_eq(d, v, hi, lo, e2)) { result.subtract(dm, *d); } else if (m.is_not(g, e0) && m.is_eq(e0, e2, e1) && bv.is_bv(e1) && is_var_range(e1, hi, lo, v) && is_ground(e2) && apply_ground_eq(d, v, hi, lo, e2)) { result.subtract(dm, *d); } else if (m.is_not(g, e1)) { udoc sub; sub.push_back(dm.allocateX()); // TODO: right now we state that no columns are discarded to avoid // silent column merging. This can be optimized if the set of merged // columns is returned so that here we remove different columns. bit_vector empty; empty.resize(discard_cols.size(), false); apply_guard(e1, sub, equalities, empty); result.subtract(dm, sub); result.simplify(dm); TRACE("doc", result.display(dm, tout << "result0:") << "\n"; sub.display(dm, tout << "sub:") << "\n";); sub.reset(dm); TRACE("doc", result.display(dm, tout << "result:") << "\n";); } else if (m.is_or(g)) { udoc sub; sub.push_back(dm.allocateX()); for (unsigned i = 0; !sub.is_empty() && i < to_app(g)->get_num_args(); ++i) { expr_ref arg(m); arg = mk_not(m, to_app(g)->get_arg(i)); apply_guard(arg, sub, equalities, discard_cols); } TRACE("doc", result.display(dm, tout << "result0:") << "\n";); result.subtract(dm, sub); TRACE("doc", sub.display(dm, tout << "sub:") << "\n"; result.display(dm, tout << "result:") << "\n";); sub.reset(dm); } else if (is_var(g)) { SASSERT(m.is_bool(g)); unsigned v = to_var(g)->get_idx(); unsigned idx = column_idx(v); doc_ref d(dm); d = dm.allocateX(); dm.set(*d, idx, BIT_1); result.intersect(dm, *d); } else if ((m.is_eq(g, e1, e2) || m.is_iff(g, e1, e2)) && m.is_bool(e1)) { udoc diff1, diff2; diff1.push_back(dm.allocateX()); diff2.push_back(dm.allocateX()); expr_ref f1(m), f2(m); f1 = mk_not(m, e1); f2 = mk_not(m, e2); apply_guard(e1, diff1, equalities, discard_cols); apply_guard(f2, diff1, equalities, discard_cols); result.subtract(dm, diff1); diff1.reset(dm); apply_guard(f1, diff2, equalities, discard_cols); apply_guard(e2, diff2, equalities, discard_cols); result.subtract(dm, diff2); diff2.reset(dm); } else if (m.is_eq(g, e1, e2) && bv.is_bv(e1)) { if (apply_bv_eq(e1, e2, discard_cols, result)) { // done } else { goto failure_case; } } else { failure_case: std::ostringstream strm; strm << "Guard expression is not handled" << mk_pp(g, m); throw default_exception(strm.str()); } } class udoc_plugin::filter_interpreted_fn : public relation_mutator_fn { union_find_default_ctx union_ctx; doc_manager& dm; expr_ref m_original_condition; expr_ref m_reduced_condition; udoc m_udoc; bit_vector m_empty_bv; subset_ints m_equalities; public: filter_interpreted_fn(const udoc_relation & t, ast_manager& m, app *condition) : dm(t.get_dm()), m_original_condition(condition, m), m_reduced_condition(m), m_equalities(union_ctx) { unsigned num_bits = t.get_num_bits(); m_empty_bv.resize(num_bits, false); expr_ref guard(m); for (unsigned i = 0; i < num_bits; ++i) { m_equalities.mk_var(); } t.extract_guard(condition, guard, m_reduced_condition); t.compile_guard(guard, m_udoc, m_empty_bv); TRACE("doc", tout << "original condition: " << mk_pp(condition, m) << "\n"; tout << "remaining condition: " << m_reduced_condition << "\n"; m_udoc.display(dm, tout) << "\n";); } virtual ~filter_interpreted_fn() { m_udoc.reset(dm); } virtual void operator()(relation_base & tb) { udoc_relation & t = get(tb); udoc& u = t.get_udoc(); SASSERT(u.well_formed(dm)); u.intersect(dm, m_udoc); SASSERT(u.well_formed(dm)); t.apply_guard(m_reduced_condition, u, m_equalities, m_empty_bv); SASSERT(u.well_formed(dm)); u.simplify(dm); SASSERT(u.well_formed(dm)); TRACE("doc", tout << "final size: " << t.get_size_estimate_rows() << '\n';); IF_VERBOSE(3, t.display(verbose_stream());); } }; relation_mutator_fn * udoc_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { return check_kind(t)?alloc(filter_interpreted_fn, get(t), get_ast_manager(), condition):0; } class udoc_plugin::join_project_fn : public convenient_relation_join_project_fn { #if 0 udoc_plugin::join_fn m_joiner; #endif bit_vector m_to_delete; public: join_project_fn( udoc_relation const& t1, udoc_relation const& t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, unsigned const* rm_cols) : convenient_relation_join_project_fn( t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, removed_col_cnt, rm_cols) #if 0 , m_joiner(t1.get_plugin(), t1, t2, col_cnt, cols1, cols2) #endif { unsigned num_bits1 = t1.get_num_bits(); unsigned num_bits = num_bits1 + t2.get_num_bits(); unsigned_vector removed_cols(removed_col_cnt, rm_cols); t1.expand_column_vector(removed_cols, &t2); t1.expand_column_vector(m_cols1); t2.expand_column_vector(m_cols2); m_to_delete.resize(num_bits, false); for (unsigned i = 0; i < removed_cols.size(); ++i) { m_to_delete.set(removed_cols[i], true); } } // TBD: replace this by "join" given below. virtual relation_base* operator()(relation_base const& t1, relation_base const& t2) { #if 1 return join(get(t1), get(t2)); #else udoc_relation *joined = get(m_joiner(t1, t2)); relation_base* result = 0; if (joined->fast_empty()) { result = t1.get_plugin().mk_empty(get_result_signature()); } else { project_fn projector(*joined, m_removed_cols.size(), m_removed_cols.c_ptr()); result = projector(*joined); } joined->deallocate(); return result; #endif } private: udoc_relation* join(udoc_relation const& t1, udoc_relation const& t2) { relation_signature prod_signature; prod_signature.append(t1.get_signature()); prod_signature.append(t2.get_signature()); udoc const& d1 = t1.get_udoc(); udoc const& d2 = t2.get_udoc(); doc_manager& dm1 = t1.get_dm(); udoc_plugin& p = t1.get_plugin(); doc_manager& dm_prod = p.dm(prod_signature); udoc_relation* result = get(p.mk_empty(get_result_signature())); udoc& res = result->get_udoc(); doc_manager& dm_res = result->get_dm(); for (unsigned i = 0; i < d1.size(); ++i) { for (unsigned j = 0; j < d2.size(); ++j) { doc_ref d(dm_prod, dm_prod.join(d1[i], d2[j], dm1, m_cols1, m_cols2)); if (d) { res.insert(dm_res, dm_prod.project(dm_res, m_to_delete, *d)); IF_VERBOSE(2, if (res.size() > 0 && 0 == res.size() % 10000) { verbose_stream() << "result size: " << res.size() << " i:" << i << " j:" << j << " " << 100*i/d1.size() << "% complete\n"; }); } } } TRACE("doc", result->display(tout);); return result; } }; class udoc_plugin::join_project_and_fn : public relation_join_fn { public: join_project_and_fn() {} virtual relation_base* operator()(relation_base const& t1, relation_base const& t2) { udoc_relation *result = get(t1.clone()); result->get_udoc().intersect(result->get_dm(), get(t2).get_udoc()); return result; } }; relation_join_fn * udoc_plugin::mk_join_project_fn( relation_base const& t1, relation_base const& t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { if (!check_kind(t1) || !check_kind(t2)) return 0; // special case where we have h(X) :- f(X), g(X). if (joined_col_cnt == removed_col_cnt && t1.get_signature().size() == joined_col_cnt && t2.get_signature().size() == joined_col_cnt) { for (unsigned i = 0; i < removed_col_cnt; ++i) { if (removed_cols[i] != i || cols1[i] != cols2[i]) goto general_fn; } return alloc(join_project_and_fn); } general_fn: return alloc(join_project_fn, get(t1), get(t2), joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } // // Notes: // 1. this code could use some cleanup and simplification. // 2. It is also not very efficient in the copy routines. // They fall back to copying each bit instead of a chunk. // 3. Argument about correctness is needed as comments. // 4. Unit/stress test cases are needed. // class udoc_plugin::negation_filter_fn : public relation_intersection_filter_fn { struct mk_remove_cols { mk_remove_cols(relation_base const& t1, relation_base const& t2, unsigned_vector& remove_cols) { unsigned sz1 = t1.get_signature().size(); unsigned sz2 = t2.get_signature().size(); for (unsigned i = 0; i < sz2; ++i) { remove_cols.push_back(sz1 + i); } } }; unsigned_vector m_t_cols; unsigned_vector m_neg_cols; unsigned_vector m_remove_cols; mk_remove_cols m_mk_remove_cols; join_project_fn m_join_project; bool m_is_subtract; //bool m_is_aliased; public: negation_filter_fn(const udoc_relation & t, const udoc_relation & neg, unsigned joined_col_cnt, const unsigned *t_cols, const unsigned *neg_cols) : m_t_cols(joined_col_cnt, t_cols), m_neg_cols(joined_col_cnt, neg_cols), m_mk_remove_cols(t, neg, m_remove_cols), m_join_project(t, neg, joined_col_cnt, t_cols, neg_cols, m_remove_cols.size(), m_remove_cols.c_ptr()), m_is_subtract(false)//, /*m_is_aliased(true) */{ SASSERT(joined_col_cnt > 0 || neg.get_signature().size() == 0); m_is_subtract = (joined_col_cnt == t.get_signature().size()); m_is_subtract &= (joined_col_cnt == neg.get_signature().size()); svector found(joined_col_cnt, false); for (unsigned i = 0; m_is_subtract && i < joined_col_cnt; ++i) { m_is_subtract = !found[t_cols[i]] && (t_cols[i] == neg_cols[i]); found[t_cols[i]] = true; } t.expand_column_vector(m_t_cols); neg.expand_column_vector(m_neg_cols); } virtual void operator()(relation_base& tb, const relation_base& negb) { udoc_relation& t = get(tb); udoc_relation const& n = get(negb); IF_VERBOSE(3, t.display(verbose_stream() << "dst:");); IF_VERBOSE(3, n.display(verbose_stream() << "neg:");); if (t.fast_empty() || n.fast_empty()) return; /* TODO: double check if (!m_is_aliased && !p.m_disable_fast_pass) { // fast_pass(t, n); } */ if (n.get_signature().empty()) t.get_udoc().reset(t.get_dm()); else if (m_is_subtract) t.get_udoc().subtract(t.get_dm(), n.get_udoc()); else slow_pass(t, n); } private: /* void fast_pass(udoc_relation& t, const udoc_relation& n) { SASSERT(!m_is_aliased); udoc & dst = t.get_udoc(); udoc const & neg = n.get_udoc(); doc_manager& dmt = t.get_dm(); doc_manager& dmn = n.get_dm(); udoc result; for (unsigned i = 0; i < dst.size(); ++i) { bool subsumed = false; for (unsigned j = 0; j < neg.size(); ++j) { if (dmn.contains(neg[j], m_neg_cols, dst[i], m_t_cols)) { dmt.deallocate(&dst[i]); subsumed = true; break; } } if (!subsumed) result.push_back(&dst[i]); } std::swap(dst, result); } */ void slow_pass(udoc_relation& t, udoc_relation const& n) { doc_manager& dmt = t.get_dm(); udoc_relation* jp = get(m_join_project(t, n)); if (!jp->fast_empty()) { t.get_udoc().subtract(dmt, jp->get_udoc()); } TRACE("doc", t.display(tout); tout << "\n"; jp->display(tout); tout << "\n";); jp->deallocate(); } }; relation_intersection_filter_fn * udoc_plugin::mk_filter_by_negation_fn( const relation_base& t, const relation_base& neg, unsigned joined_col_cnt, const unsigned *t_cols, const unsigned *negated_cols) { if (!check_kind(t) || !check_kind(neg)) return 0; return alloc(negation_filter_fn, get(t), get(neg), joined_col_cnt, t_cols, negated_cols); } class udoc_plugin::filter_proj_fn : public convenient_relation_project_fn { union_find_default_ctx union_ctx; doc_manager& dm; expr_ref m_original_condition; expr_ref m_reduced_condition; udoc m_udoc; udoc m_udoc2; bit_vector m_to_delete; // map: col idx -> bool (whether the column is to be removed) subset_ints m_equalities; unsigned_vector m_roots; public: filter_proj_fn(const udoc_relation & t, ast_manager& m, app *condition, unsigned col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(t.get_signature(), col_cnt, removed_cols), dm(t.get_dm()), m_original_condition(condition, m), m_reduced_condition(m), m_equalities(union_ctx) { unsigned num_bits = t.get_num_bits(); t.expand_column_vector(m_removed_cols); m_to_delete.resize(num_bits, false); for (unsigned i = 0; i < num_bits; ++i) { m_equalities.mk_var(); } for (unsigned i = 0; i < m_removed_cols.size(); ++i) { m_to_delete.set(m_removed_cols[i], true); } expr_ref guard(m), non_eq_cond(condition, m); t.extract_equalities(condition, non_eq_cond, m_equalities, m_roots); t.extract_guard(non_eq_cond, guard, m_reduced_condition); t.compile_guard(guard, m_udoc, m_to_delete); } virtual ~filter_proj_fn() { m_udoc.reset(dm); } virtual relation_base* operator()(const relation_base & tb) { udoc_relation const & t = get(tb); udoc const& u1 = t.get_udoc(); doc_manager& dm = t.get_dm(); m_udoc2.copy(dm, u1); m_udoc2.intersect(dm, m_udoc); t.apply_guard(m_reduced_condition, m_udoc2, m_equalities, m_to_delete); m_udoc2.merge(dm, m_roots, m_equalities, m_to_delete); SASSERT(m_udoc2.well_formed(dm)); udoc_relation* r = get(t.get_plugin().mk_empty(get_result_signature())); doc_manager& dm2 = r->get_dm(); for (unsigned i = 0; i < m_udoc2.size(); ++i) { doc* d = dm.project(dm2, m_to_delete, m_udoc2[i]); r->get_udoc().insert(dm2, d); SASSERT(r->get_udoc().well_formed(dm2)); } m_udoc2.reset(dm); IF_VERBOSE(3, r->display(verbose_stream() << "filter project result:\n");); return r; } }; relation_transformer_fn * udoc_plugin::mk_filter_interpreted_and_project_fn( const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { return check_kind(t)?alloc(filter_proj_fn, get(t), get_ast_manager(), condition, removed_col_cnt, removed_cols):0; } } z3-4.4.0/src/muz/rel/tbv.h0000644000175000017500000000734012540347414015001 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: tbv.h Abstract: ternary bit-vector utilities. Author: Nikolaj Bjorner (nbjorner) 2014-09-15 Revision History: --*/ #ifndef _TBV_H_ #define _TBV_H_ #include "fixed_bit_vector.h" #include "rational.h" #include "ast.h" class tbv; enum tbit { BIT_z = 0x0, BIT_0 = 0x1, BIT_1 = 0x2, BIT_x = 0x3 }; inline tbit neg(tbit t) { return (tbit)(t ^ 0x3); } class tbv_manager { friend class tbv; fixed_bit_vector_manager m; ptr_vector allocated_tbvs; public: tbv_manager(unsigned n): m(2*n) {} ~tbv_manager(); void reset(); tbv* allocate(); tbv* allocate1(); tbv* allocate0(); tbv* allocateX(); tbv* allocate(tbv const& bv); tbv* allocate(uint64 n); tbv* allocate(rational const& r); tbv* allocate(uint64 n, unsigned hi, unsigned lo); tbv* allocate(tbv const& bv, unsigned const* permutation); tbv* allocate(char const* bv); void deallocate(tbv* bv); unsigned get_size_estimate_bytes(const tbv&) const { return m.num_bytes(); } void copy(tbv& dst, tbv const& src) const; unsigned num_tbits() const { return m.num_bits()/2; } tbv& reset(tbv& bv) const { return fill0(bv); } tbv& fill0(tbv& bv) const; tbv& fill1(tbv& bv) const; tbv& fillX(tbv& bv) const; bool set_and(tbv& dst, tbv const& src) const; tbv& set_or(tbv& dst, tbv const& src) const; void complement(tbv const& src, ptr_vector& result); bool equals(tbv const& a, tbv const& b) const; unsigned hash(tbv const& src) const; bool contains(tbv const& a, tbv const& b) const; bool contains(tbv const& a, unsigned_vector const& colsa, tbv const& b, unsigned_vector const& colsb) const; bool intersect(tbv const& a, tbv const& b, tbv& result); std::ostream& display(std::ostream& out, tbv const& b) const; std::ostream& display(std::ostream& out, tbv const& b, unsigned hi, unsigned lo) const; tbv* project(bit_vector const& to_delete, tbv const& src); bool is_well_formed(tbv const& b) const; // - does not contain BIT_z; void set(tbv& dst, uint64 n, unsigned hi, unsigned lo); void set(tbv& dst, rational const& r, unsigned hi, unsigned lo); void set(tbv& dst, tbv const& other, unsigned hi, unsigned lo); void set(tbv& dst, unsigned index, tbit value); static void debug_alloc(); expr_ref to_formula(ast_manager& m, tbv const& src); expr_ref mk_var(ast_manager& m, unsigned i); }; class tbv: private fixed_bit_vector { friend class fixed_bit_vector_manager; friend class tbv_manager; public: struct eq { tbv_manager& m; eq(tbv_manager& m):m(m) {} bool operator()(tbv const& d1, tbv const& d2) const { return m.equals(d1, d2); } }; struct hash { tbv_manager& m; hash(tbv_manager& m):m(m) {} unsigned operator()(tbv const& d) const { return m.hash(d); } }; tbit operator[](unsigned idx) const { return (tbit)get(idx); } private: unsigned get(unsigned index) const { index *= 2; return (fixed_bit_vector::get(index) << 1) | (unsigned)fixed_bit_vector::get(index+1); } }; class tbv_ref { tbv_manager& mgr; tbv* d; public: tbv_ref(tbv_manager& mgr):mgr(mgr),d(0) {} tbv_ref(tbv_manager& mgr, tbv* d):mgr(mgr),d(d) {} ~tbv_ref() { if (d) mgr.deallocate(d); } tbv_ref& operator=(tbv* d2) { if (d) mgr.deallocate(d); d = d2; return *this; } tbv& operator*() { return *d; } tbv* operator->() { return d; } tbv* get() { return d; } tbv* detach() { tbv* result = d; d = 0; return result; } }; #endif /* _TBV_H_ */ z3-4.4.0/src/muz/rel/dl_table.cpp0000644000175000017500000006440512540347414016314 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_table.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-01. Revision History: --*/ #include"dl_context.h" #include"dl_util.h" #include"dl_table.h" #include"dl_relation_manager.h" namespace datalog { // ----------------------------------- // // hashtable_table // // ----------------------------------- table_base * hashtable_table_plugin::mk_empty(const table_signature & s) { SASSERT(can_handle_signature(s)); return alloc(hashtable_table, *this, s); } class hashtable_table_plugin::join_fn : public convenient_table_join_fn { unsigned m_joined_col_cnt; public: join_fn(const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_table_join_fn(t1_sig, t2_sig, col_cnt, cols1, cols2), m_joined_col_cnt(col_cnt) {} virtual table_base * operator()(const table_base & t1, const table_base & t2) { const hashtable_table & ht1 = static_cast(t1); const hashtable_table & ht2 = static_cast(t2); hashtable_table_plugin & plugin = ht1.get_plugin(); hashtable_table * res = static_cast(plugin.mk_empty(get_result_signature())); hashtable_table::storage::iterator els1it = ht1.m_data.begin(); hashtable_table::storage::iterator els1end = ht1.m_data.end(); hashtable_table::storage::iterator els2end = ht2.m_data.end(); table_fact acc; for(; els1it!=els1end; ++els1it) { const table_fact & row1 = *els1it; hashtable_table::storage::iterator els2it = ht2.m_data.begin(); for(; els2it!=els2end; ++els2it) { const table_fact & row2 = *els2it; bool match=true; for(unsigned i=0; im_data.insert(acc); } } return res; } }; table_join_fn * hashtable_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if(t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind()) { return 0; } return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); } class hashtable_table::our_iterator_core : public iterator_core { const hashtable_table & m_parent; storage::iterator m_inner; storage::iterator m_end; class our_row : public row_interface { const our_iterator_core & m_parent; public: our_row(const our_iterator_core & parent) : row_interface(parent.m_parent), m_parent(parent) {} virtual void get_fact(table_fact & result) const { result = *m_parent.m_inner; } virtual table_element operator[](unsigned col) const { return (*m_parent.m_inner)[col]; } }; our_row m_row_obj; public: our_iterator_core(const hashtable_table & t, bool finished) : m_parent(t), m_inner(finished ? t.m_data.end() : t.m_data.begin()), m_end(t.m_data.end()), m_row_obj(*this) {} virtual bool is_finished() const { return m_inner==m_end; } virtual row_interface & operator*() { SASSERT(!is_finished()); return m_row_obj; } virtual void operator++() { SASSERT(!is_finished()); ++m_inner; } }; table_base::iterator hashtable_table::begin() const { return mk_iterator(alloc(our_iterator_core, *this, false)); } table_base::iterator hashtable_table::end() const { return mk_iterator(alloc(our_iterator_core, *this, true)); } // ----------------------------------- // // bitvector_table // // ----------------------------------- bool bitvector_table_plugin::can_handle_signature(const table_signature & sig) { if(sig.functional_columns()!=0) { return false; } unsigned cols = sig.size(); unsigned shift = 0; for (unsigned i = 0; i < cols; ++i) { unsigned s = static_cast(sig[i]); if (s != sig[i] || !is_power_of_two(s)) { return false; } unsigned num_bits = 0; unsigned bit_pos = 1; for (num_bits = 1; num_bits < 32; ++num_bits) { if (bit_pos & s) { break; } bit_pos <<= 1; } shift += num_bits; if (shift >= 32) { return false; } } return true; } table_base * bitvector_table_plugin::mk_empty(const table_signature & s) { SASSERT(can_handle_signature(s)); return alloc(bitvector_table, *this, s); } class bitvector_table::bv_iterator : public iterator_core { bitvector_table const& m_bv; unsigned m_offset; class our_row : public caching_row_interface { const bv_iterator& m_parent; public: our_row(const bv_iterator & p) : caching_row_interface(p.m_bv), m_parent(p) {} virtual void get_fact(table_fact& result) const { if (result.size() < size()) { result.resize(size(), 0); } m_parent.m_bv.offset2fact(m_parent.m_offset, result); } }; our_row m_row_obj; public: bv_iterator(const bitvector_table& bv, bool end): m_bv(bv), m_offset(end?m_bv.m_bv.size():0), m_row_obj(*this) { if (!is_finished() && !m_bv.m_bv.get(m_offset)) { ++(*this); } } virtual bool is_finished() const { return m_offset == m_bv.m_bv.size(); } virtual row_interface & operator*() { SASSERT(!is_finished()); return m_row_obj; } virtual void operator++() { SASSERT(!is_finished()); ++m_offset; while (!is_finished() && !m_bv.m_bv.get(m_offset)) { ++m_offset; } m_row_obj.reset(); } }; bitvector_table::bitvector_table(bitvector_table_plugin & plugin, const table_signature & sig) : table_base(plugin, sig) { SASSERT(plugin.can_handle_signature(sig)); m_num_cols = sig.size(); unsigned shift = 0; for (unsigned i = 0; i < m_num_cols; ++i) { unsigned s = static_cast(sig[i]); if (s != sig[i] || !is_power_of_two(s)) { throw default_exception("bit-vector table is specialized to small domains that are powers of two"); } m_shift.push_back(shift); m_mask.push_back(s - 1); unsigned num_bits = 0; unsigned bit_pos = 1; for (num_bits = 1; num_bits < 32; ++num_bits) { if (bit_pos & s) { break; } bit_pos <<= 1; } shift += num_bits; if (shift >= 32) { throw default_exception("bit-vector table is specialized to small domains that are powers of two"); } m_bv.reserve(1 << shift); } } unsigned bitvector_table::fact2offset(const table_element* f) const { unsigned result = 0; for (unsigned i = 0; i < m_num_cols; ++i) { SASSERT(f[i]> m_shift[i]); } } void bitvector_table::add_fact(const table_fact & f) { m_bv.set(fact2offset(f.c_ptr())); } void bitvector_table::remove_fact(const table_element* fact) { m_bv.unset(fact2offset(fact)); } bool bitvector_table::contains_fact(const table_fact & f) const { return m_bv.get(fact2offset(f.c_ptr())); } table_base::iterator bitvector_table::begin() const { return mk_iterator(alloc(bv_iterator, *this, false)); } table_base::iterator bitvector_table::end() const { return mk_iterator(alloc(bv_iterator, *this, true)); } // ----------------------------------- // // equivalence_table // // ----------------------------------- bool equivalence_table_plugin::can_handle_signature(const table_signature & sig) { return sig.functional_columns() == 0 && sig.size() == 2 && sig[0] < UINT_MAX && sig[0] == sig[1]; } bool equivalence_table_plugin::is_equivalence_table(table_base const& tbl) const { if (tbl.get_kind() != get_kind()) return false; equivalence_table const& t = static_cast(tbl); return !t.is_sparse(); } table_base * equivalence_table_plugin::mk_empty(const table_signature & s) { TRACE("dl", for (unsigned i = 0; i < s.size(); ++i) tout << s[i] << " "; tout << "\n";); SASSERT(can_handle_signature(s)); return alloc(equivalence_table, *this, s); } class equivalence_table_plugin::select_equal_and_project_fn : public table_transformer_fn { unsigned m_val; table_sort m_sort; public: select_equal_and_project_fn(const table_signature & sig, table_element val, unsigned col) : m_val(static_cast(val)), m_sort(sig[0]) { SASSERT(val <= UINT_MAX); SASSERT(col == 0 || col == 1); SASSERT(sig.functional_columns() == 0); SASSERT(sig.size() == 2); SASSERT(sig[0] < UINT_MAX && sig[0] == sig[1]); } virtual table_base* operator()(const table_base& tb) { TRACE("dl", tout << "\n";); table_plugin & plugin = tb.get_plugin(); table_plugin* rp = plugin.get_manager().get_table_plugin(symbol("sparse")); SASSERT(rp); table_signature sig; sig.push_back(m_sort); table_base* result = rp->mk_empty(sig); equivalence_table const& eq_table = static_cast(tb); if (eq_table.is_valid(m_val)) { table_fact fact; fact.resize(1); unsigned r = m_val; do { fact[0] = r; result->add_fact(fact); r = eq_table.m_uf.next(r); } while (r != m_val); } TRACE("dl", tb.display(tout << "src:\n"); result->display(tout << "result\n");); return result; } }; table_transformer_fn * equivalence_table_plugin::mk_select_equal_and_project_fn( const table_base & t, const table_element & value, unsigned col) { return alloc(select_equal_and_project_fn, t.get_signature(), value, col); } class equivalence_table_plugin::union_fn : public table_union_fn { equivalence_table_plugin& m_plugin; void mk_union1(equivalence_table & tgt, const equivalence_table & src, table_base * delta) { unsigned num_vars = src.m_uf.get_num_vars(); table_fact fact; fact.resize(2); for (unsigned i = 0; i < num_vars; ++i) { if (src.is_valid(i) && src.m_uf.find(i) == i) { fact[0] = i; equivalence_table::class_iterator it = src.class_begin(i); equivalence_table::class_iterator end = src.class_end(i); for (; it != end; ++it) { fact[1] = *it; if (!tgt.contains_fact(fact)) { tgt.add_fact(fact); if (delta) { delta->add_fact(fact); } } } } } } void mk_union2(equivalence_table & tgt, const table_base & src, table_base * delta) { table_fact fact; table_base::iterator it = src.begin(), end = src.end(); for (; it != end; ++it) { it->get_fact(fact); if (!tgt.contains_fact(fact)) { tgt.add_fact(fact); if (delta) { delta->add_fact(fact); TRACE("dl", tout << "Add: "; for (unsigned i = 0; i < fact.size(); ++i) tout << fact[i] << " "; tout << "\n";); } } } } public: union_fn(equivalence_table_plugin& p) : m_plugin(p) {} virtual void operator()(table_base & tgt0, const table_base & src, table_base * delta) { TRACE("dl", tout << "union\n";); equivalence_table & tgt = static_cast(tgt0); if (m_plugin.is_equivalence_table(src)) { mk_union1(tgt, static_cast(src), delta); } else { mk_union2(tgt, src, delta); } TRACE("dl", src.display(tout << "src\n"); tgt.display(tout << "tgt\n"); if (delta) delta->display(tout << "delta\n");); } }; table_union_fn * equivalence_table_plugin::mk_union_fn( const table_base & tgt, const table_base & src, const table_base * delta) { if (!is_equivalence_table(tgt) || tgt.get_signature() != src.get_signature() || (delta && delta->get_signature() != tgt.get_signature())) { return 0; } return alloc(union_fn,*this); } class equivalence_table_plugin::join_project_fn : public convenient_table_join_project_fn { equivalence_table_plugin& m_plugin; public: join_project_fn( equivalence_table_plugin& plugin, const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_table_join_project_fn(t1_sig, t2_sig, col_cnt, cols1, cols2, removed_col_cnt, removed_cols), m_plugin(plugin) { m_removed_cols.push_back(UINT_MAX); } virtual table_base * operator()(const table_base & tb1, const table_base & tb2) { SASSERT(m_cols1.size() == 1); const table_signature & res_sign = get_result_signature(); table_plugin * plugin = &tb1.get_plugin(); if (!plugin->can_handle_signature(res_sign)) { plugin = &tb2.get_plugin(); if (!plugin->can_handle_signature(res_sign)) { plugin = &tb1.get_manager().get_appropriate_plugin(res_sign); } } SASSERT(plugin->can_handle_signature(res_sign)); table_base * result = plugin->mk_empty(res_sign); if (m_plugin.is_equivalence_table(tb1)) { mk_join(0, m_cols1[0], static_cast(tb1), 2, m_cols2[0], tb2, result); } else if (m_plugin.is_equivalence_table(tb2)) { mk_join(tb1.get_signature().size(), m_cols2[0], static_cast(tb2), 0, m_cols1[0], tb1, result); } else { UNREACHABLE(); } TRACE("dl", tb1.display(tout << "tb1\n"); tb2.display(tout << "tb2\n"); result->display(tout << "result\n");); return result; } private: table_base * mk_join(unsigned offs1, unsigned col1, equivalence_table const & t1, unsigned offs2, unsigned col2, table_base const& t2, table_base* res) { table_base::iterator els2it = t2.begin(); table_base::iterator els2end = t2.end(); table_fact acc, proj; acc.resize(t1.get_signature().size() + t2.get_signature().size()); for(; els2it != els2end; ++els2it) { const table_base::row_interface & row2 = *els2it; table_element const& e2 = row2[col2]; equivalence_table::class_iterator it = t1.class_begin(e2); equivalence_table::class_iterator end = t1.class_end(e2); if (it != end) { for (unsigned i = 0; i < row2.size(); ++i) { acc[i+offs2] = row2[i]; } } for (; it != end; ++it) { acc[offs1+col1] = e2; acc[offs1+1-col1] = *it; mk_project(acc, proj); TRACE("dl", for (unsigned i = 0; i < proj.size(); ++i) tout << proj[i] << " "; tout << "\n";); res->add_fact(proj); } } return res; } virtual void mk_project(table_fact const & f, table_fact & p) const { unsigned sz = f.size(); p.reset(); for (unsigned i = 0, r = 0; i < sz; ++i) { if (r < m_removed_cols.size() && m_removed_cols[r] == i) { ++r; } else { p.push_back(f[i]); } } } }; table_join_fn * equivalence_table_plugin::mk_join_project_fn( const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { if (col_cnt != 1) { TRACE("dl", tout << "WARNING: join_project on multiple columns is not implemented\n";); return 0; } if (is_equivalence_table(t1) || is_equivalence_table(t2)) { return alloc(join_project_fn, *this, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } return 0; } class equivalence_table::eq_iterator : public iterator_core { equivalence_table const& m_eq; unsigned m_last; unsigned m_current; unsigned m_next; class our_row : public caching_row_interface { const eq_iterator& m_parent; public: our_row(const eq_iterator & p) : caching_row_interface(p.m_eq), m_parent(p) {} virtual void get_fact(table_fact& result) const { if (result.size() < size()) { result.resize(size(), 0); } result[0] = m_parent.m_current; result[1] = m_parent.m_next; } virtual table_element operator[](unsigned col) const { if (col == 0) return m_parent.m_current; if (col == 1) return m_parent.m_next; UNREACHABLE(); return 0; } }; our_row m_row_obj; public: eq_iterator(const equivalence_table& eq, bool end): m_eq(eq), m_last(eq.m_uf.get_num_vars()), m_current(end?m_last:0), m_next(0), m_row_obj(*this) { while (m_current < m_last && !m_eq.is_valid(m_current)) { m_current++; m_next = m_current; } } virtual bool is_finished() const { return m_current == m_last; } virtual row_interface & operator*() { SASSERT(!is_finished()); return m_row_obj; } virtual void operator++() { SASSERT(!is_finished()); m_next = m_eq.m_uf.next(m_next); if (m_next == m_current) { do { m_current++; m_next = m_current; } while (m_current < m_last && !m_eq.is_valid(m_current)); } } }; equivalence_table::equivalence_table(equivalence_table_plugin & plugin, const table_signature & sig) : table_base(plugin, sig), m_uf(m_ctx), m_sparse(0) { SASSERT(plugin.can_handle_signature(sig)); } equivalence_table::~equivalence_table() { if (is_sparse()) { m_sparse->deallocate(); } } void equivalence_table::add_fact(const table_fact & f) { if (is_sparse()) { add_fact_sparse(f); } else { TRACE("dl_verbose", for (unsigned i = 0; i < f.size(); ++i) tout << f[i] << " "; tout << "\n";); while (first(f) >= m_uf.get_num_vars()) m_uf.mk_var(); while (second(f) >= m_uf.get_num_vars()) m_uf.mk_var(); m_uf.merge(first(f), second(f)); m_valid.reserve(m_uf.get_num_vars()); m_valid.set(first(f)); m_valid.set(second(f)); } } void equivalence_table::remove_fact(const table_element* fact) { mk_sparse(); m_sparse->remove_fact(fact); } void equivalence_table::mk_sparse() { if (m_sparse) return; TRACE("dl",tout << "\n";); table_plugin & plugin = get_plugin(); table_plugin* rp = plugin.get_manager().get_table_plugin(symbol("sparse")); SASSERT(rp); table_base* result = rp->mk_empty(get_signature()); table_base::iterator it = begin(), e = end(); table_fact fact; for (; it != e; ++it) { it->get_fact(fact); result->add_fact(fact); } m_sparse = result; } void equivalence_table::add_fact_sparse(table_fact const& f) { table_base::iterator it = m_sparse->begin(), end = m_sparse->end(); vector to_add; to_add.push_back(f); table_fact f1(f); f1[0] = f[1]; f1[1] = f[0]; to_add.push_back(f1); f1[0] = f[1]; f1[1] = f[1]; to_add.push_back(f1); f1[0] = f[0]; f1[1] = f[0]; to_add.push_back(f1); for (; it != end; ++it) { if ((*it)[0] == f[0]) { f1[0] = f[1]; f1[1] = (*it)[1]; to_add.push_back(f1); std::swap(f1[0],f1[1]); to_add.push_back(f1); } } for (unsigned i = 0; i < to_add.size(); ++i) { m_sparse->add_fact(to_add[i]); } } bool equivalence_table::contains_fact(const table_fact & f) const { TRACE("dl_verbose", for (unsigned i = 0; i < f.size(); ++i) tout << f[i] << " "; tout << "\n";); if (is_sparse()) { return m_sparse->contains_fact(f); } return is_valid(first(f)) && is_valid(second(f)) && m_uf.find(first(f)) == m_uf.find(second(f)); } table_base* equivalence_table::clone() const { if (is_sparse()) { return m_sparse->clone(); } TRACE("dl",tout << "\n";); table_plugin & plugin = get_plugin(); table_base* result = plugin.mk_empty(get_signature()); table_fact fact; fact.resize(2); for (unsigned i = 0; i < m_uf.get_num_vars(); ++i) { if (m_valid.get(i) && m_uf.find(i) == i) { unsigned n = m_uf.next(i); fact[0] = i; while (n != i) { fact[1] = n; result->add_fact(fact); n = m_uf.next(n); } } } return result; } table_base::iterator equivalence_table::begin() const { if (is_sparse()) return m_sparse->begin(); return mk_iterator(alloc(eq_iterator, *this, false)); } table_base::iterator equivalence_table::end() const { if (is_sparse()) return m_sparse->end(); return mk_iterator(alloc(eq_iterator, *this, true)); } equivalence_table::class_iterator equivalence_table::class_begin(table_element const& _e) const { SASSERT(!is_sparse()); unsigned e = static_cast(_e); return class_iterator(*this, e, !is_valid(e)); } equivalence_table::class_iterator equivalence_table::class_end(table_element const& _e) const { SASSERT(!is_sparse()); unsigned e = static_cast(_e); return class_iterator(*this, e, true); } void equivalence_table::display(std::ostream& out) const { if (is_sparse()) { m_sparse->display(out); return; } for (unsigned i = 0; i < m_uf.get_num_vars(); ++i) { if (is_valid(i) && m_uf.find(i) == i) { unsigned j = i, last = i; do { out << "<" << i << " " << j << ">\n"; j = m_uf.next(j); } while (last != j); } } } unsigned equivalence_table::get_size_estimate_rows() const { if (is_sparse()) return m_sparse->get_size_estimate_rows(); return static_cast(get_signature()[0]); } unsigned equivalence_table::get_size_estimate_bytes() const { if (is_sparse()) return m_sparse->get_size_estimate_bytes(); return static_cast(get_signature()[0]); } bool equivalence_table::knows_exact_size() const { return (!is_sparse() || m_sparse->knows_exact_size()); } }; z3-4.4.0/src/muz/rel/dl_interval_relation.h0000644000175000017500000001233512540347414020406 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_interval_relation.h Abstract: Basic interval reatlion. Author: Nikolaj Bjorner (nbjorner) 2010-2-11 Revision History: --*/ #ifndef _DL_INTERVAL_RELATION_H_ #define _DL_INTERVAL_RELATION_H_ #include "dl_context.h" #include "dl_relation_manager.h" #include "dl_base.h" #include "old_interval.h" #include "dl_vector_relation.h" #include "arith_decl_plugin.h" #include "basic_simplifier_plugin.h" namespace datalog { class interval_relation; class interval_relation_plugin : public relation_plugin { v_dependency_manager m_dep; interval m_empty; arith_util m_arith; class join_fn; class project_fn; class rename_fn; class union_fn; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; friend class interval_relation; interval unite(interval const& src1, interval const& src2); interval widen(interval const& src1, interval const& src2); interval meet(interval const& src1, interval const& src2, bool& is_empty); v_dependency_manager & dep() const { return const_cast(m_dep); } public: interval_relation_plugin(relation_manager& m); virtual bool can_handle_signature(const relation_signature & s); static symbol get_name() { return symbol("interval_relation"); } virtual relation_base * mk_empty(const relation_signature & s); virtual relation_base * mk_full(func_decl* p, const relation_signature & s); virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols); virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols); virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col); virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); static bool is_empty(unsigned idx, interval const& i); static bool is_infinite(interval const& i); private: static interval_relation& get(relation_base& r); static interval_relation const & get(relation_base const& r); bool is_linear(expr* e, unsigned& pos, unsigned& neg, rational& k, bool is_pos) const; // x + k <= y bool is_le(app* cond, unsigned& x, rational& k, unsigned& y, bool& is_int) const; // x + k < y bool is_lt(app* cond, unsigned& x, rational& k, unsigned& y) const; // x + k = y bool is_eq(app* cond, unsigned& x, rational& k, unsigned& y) const; }; class interval_relation : public vector_relation { friend class interval_relation_plugin; friend class interval_relation_plugin::filter_equal_fn; public: interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty); virtual void add_fact(const relation_fact & f); virtual bool contains_fact(const relation_fact & f) const; virtual interval_relation * clone() const; virtual interval_relation * complement(func_decl*) const; virtual void to_formula(expr_ref& fml) const; interval_relation_plugin& get_plugin() const; void filter_interpreted(app* cond); virtual bool is_precise() const { return false; } private: virtual interval mk_intersect(interval const& t1, interval const& t2, bool& is_empty) const { return get_plugin().meet(t1, t2, is_empty); } virtual interval mk_unite(interval const& t1, interval const& t2) const { return get_plugin().unite(t1,t2); } virtual interval mk_widen(interval const& t1, interval const& t2) const { return get_plugin().widen(t1,t2); } virtual bool is_subset_of(interval const& t1, interval const& t2) const { NOT_IMPLEMENTED_YET(); return false; } virtual bool is_full(interval const& t) const { return interval_relation_plugin::is_infinite(t); } virtual bool is_empty(unsigned idx, interval const& t) const { return interval_relation_plugin::is_empty(idx, t); } virtual void mk_rename_elem(interval& i, unsigned col_cnt, unsigned const* cycle); virtual void display_index(unsigned idx, interval const & i, std::ostream& out) const; void mk_intersect(unsigned idx, interval const& i); }; }; #endif z3-4.4.0/src/muz/rel/dl_mk_partial_equiv.h0000644000175000017500000000174612540347414020225 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_partial_equiv.h Abstract: Rule transformer which identifies predicates that are partial equivalence relations. Author: Nikolaj Bjorner (nbjorner) 2012-05-14 Revision History: --*/ #ifndef _DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_ #define _DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_ #include "dl_context.h" #include "dl_rule_transformer.h" namespace datalog { class mk_partial_equivalence_transformer : public rule_transformer::plugin { ast_manager & m; context & m_context; public: mk_partial_equivalence_transformer(context & ctx, unsigned priority=45000) : plugin(priority), m(ctx.get_manager()), m_context(ctx) {} rule_set * operator()(rule_set const & source); private: bool is_symmetry(rule const* r); bool is_transitivity(rule const* r); }; }; #endif /* _DL_MK_PARTIAL_EQUIV_TRANSFORMER_H_ */ z3-4.4.0/src/muz/rel/dl_mk_simple_joins.h0000644000175000017500000000253212540347414020045 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_simple_joins.h Abstract: Author: Leonardo de Moura (leonardo) 2010-05-20. Revision History: --*/ #ifndef _DL_MK_SIMPLE_JOINS_H_ #define _DL_MK_SIMPLE_JOINS_H_ #include"map.h" #include"obj_pair_hashtable.h" #include"dl_context.h" #include"dl_rule_set.h" #include"dl_rule_transformer.h" namespace datalog { /** \brief Functor for creating rules that contain simple joins. A simple join is the join of two tables. After applying this transformation, every rule has at most one join. So, the rules will have the form HEAD :- TAIL. HEAD :- TAIL_1, TAIL_2. We also assume a rule may contain interpreted expressions that work as filtering conditions. So, we may also have: HEAD :- TAIL, C_1, ..., C_n. HEAD :- TAIL_1, TAIL_2, C_1, ..., C_n. Where the C_i's are interpreted expressions. We say that a rule containing C_i's is a rule with a "big tail". */ class mk_simple_joins : public rule_transformer::plugin { context & m_context; rule_manager & rm; public: mk_simple_joins(context & ctx); rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_SIMPLE_JOINS_H_ */ z3-4.4.0/src/muz/rel/udoc_relation.h0000644000175000017500000001571712540347414017044 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: udoc_relation.h Abstract: Relation based on union of DOCs. Author: Nuno Lopes (a-nlopes) 2013-03-01 Nikolaj Bjorner (nbjorner) 2014-09-15 Revision History: --*/ #ifndef _UDOC_RELATION_H_ #define _UDOC_RELATION_H_ #include "doc.h" #include "dl_base.h" namespace datalog { class udoc_plugin; class udoc_relation; class udoc_relation : public relation_base { friend class udoc_plugin; doc_manager& dm; mutable udoc m_elems; unsigned_vector m_column_info; doc* fact2doc(relation_fact const& f) const; expr_ref to_formula(tbv const& t) const; expr_ref to_formula(doc const& d) const; public: udoc_relation(udoc_plugin& p, relation_signature const& s); virtual ~udoc_relation(); virtual void reset(); virtual void add_fact(const relation_fact & f); virtual void add_new_fact(const relation_fact & f); virtual bool contains_fact(const relation_fact & f) const; virtual udoc_relation * clone() const; virtual udoc_relation * complement(func_decl*) const; virtual void to_formula(expr_ref& fml) const; udoc_plugin& get_plugin() const; virtual bool fast_empty() const { return m_elems.is_empty(); } virtual bool empty() const; virtual void display(std::ostream& out) const; virtual bool is_precise() const { return true; } virtual unsigned get_size_estimate_rows() const { return m_elems.size(); } virtual unsigned get_size_estimate_bytes() const; doc_manager& get_dm() const { return dm; } udoc const& get_udoc() const { return m_elems; } udoc& get_udoc() { return m_elems; } unsigned get_num_records() const { return m_elems.size(); } unsigned get_num_bits() const { return m_column_info.back(); } unsigned get_num_cols() const { return m_column_info.size()-1; } unsigned column_idx(unsigned col) const { return m_column_info[col]; } unsigned column_num_bits(unsigned col) const { return m_column_info[col+1] - m_column_info[col]; } void expand_column_vector(unsigned_vector& v, const udoc_relation* other = 0) const; void extract_guard(expr* condition, expr_ref& guard, expr_ref& rest) const; bool is_guard(expr* g) const; bool is_guard(unsigned n, expr* const *g) const; void compile_guard(expr* g, udoc& result, bit_vector const& discard_cols) const; void extract_equalities(expr* g, expr_ref& rest, subset_ints& equalities, unsigned_vector& roots) const; void extract_equalities( expr* e1, expr* e2, expr_ref_vector& conds, subset_ints& equalities, unsigned_vector& roots) const; void apply_guard(expr* g, udoc& result, bit_vector const& discard_cols) const; void apply_guard(expr* g, udoc& result, subset_ints const& equalities, bit_vector const& discard_cols) const; bool apply_ground_eq(doc_ref& d, unsigned v, unsigned hi, unsigned lo, expr* c) const; bool apply_bv_eq(expr* e1, expr* e2, bit_vector const& discard_cols, udoc& result) const; bool is_var_range(expr* e, unsigned& hi, unsigned& lo, unsigned& v) const; }; class udoc_plugin : public relation_plugin { friend class udoc_relation; class join_fn; class join_project_fn; class join_project_and_fn; class project_fn; class union_fn; class rename_fn; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; class filter_by_negation_fn; class filter_by_union_fn; class filter_proj_fn; class negation_filter_fn; ast_manager& m; bv_util bv; dl_decl_util dl; u_map m_dms; bool m_disable_fast_pass; doc_manager& dm(unsigned sz); doc_manager& dm(relation_signature const& sig); static udoc_relation& get(relation_base& r); static udoc_relation* get(relation_base* r); static udoc_relation const & get(relation_base const& r); void mk_union(doc_manager& dm, udoc& dst, udoc const& src, udoc* delta); bool is_numeral(expr* e, rational& r, unsigned& num_bits); unsigned num_sort_bits(expr* e) const { return num_sort_bits(get_ast_manager().get_sort(e)); } unsigned num_sort_bits(sort* s) const; bool is_finite_sort(sort* s) const; unsigned num_signature_bits(relation_signature const& sig); expr* mk_numeral(rational const& r, sort* s); public: udoc_plugin(relation_manager& rm); ~udoc_plugin(); virtual bool can_handle_signature(const relation_signature & s); static symbol get_name() { return symbol("doc"); } virtual relation_base * mk_empty(const relation_signature & s); virtual relation_base * mk_full(func_decl* p, const relation_signature & s); virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols); virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols); virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col); virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); virtual relation_intersection_filter_fn * mk_filter_by_negation_fn( const relation_base& t, const relation_base& neg, unsigned joined_col_cnt, const unsigned *t_cols, const unsigned *negated_cols); virtual relation_transformer_fn * mk_filter_interpreted_and_project_fn( const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); virtual relation_join_fn * mk_join_project_fn( relation_base const& t1, relation_base const& t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols); void disable_fast_pass() { m_disable_fast_pass = true; } }; }; #endif /* _UDOC_RELATION_H_ */ z3-4.4.0/src/muz/rel/tbv.cpp0000644000175000017500000002032212540347414015327 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: tbv.cpp Abstract: ternary bit-vector utilities. Author: Nikolaj Bjorner (nbjorner) 2014-09-15 Revision History: --*/ #include "tbv.h" #include "hashtable.h" #include "ast_util.h" static bool s_debug_alloc = false; void tbv_manager::debug_alloc() { s_debug_alloc = true; } tbv_manager::~tbv_manager() { DEBUG_CODE( ptr_vector::iterator it = allocated_tbvs.begin(); ptr_vector::iterator end = allocated_tbvs.end(); for (; it != end; ++it) { std::cout << "dangling: " << (*it) << "\n"; TRACE("doc", tout << "dangling: " << (*it) << "\n";); }); } void tbv_manager::reset() { m.reset(); } tbv* tbv_manager::allocate() { tbv* r = reinterpret_cast(m.allocate()); DEBUG_CODE( if (s_debug_alloc) { TRACE("doc", tout << allocated_tbvs.size() << " " << r << "\n";); } allocated_tbvs.insert(r); ); return r; } tbv* tbv_manager::allocate1() { tbv* v = allocate(); fill1(*v); return v; } tbv* tbv_manager::allocate0() { tbv* v = allocate(); fill0(*v); return v; } tbv* tbv_manager::allocateX() { tbv* v = allocate(); fillX(*v); return v; } tbv* tbv_manager::allocate(tbv const& bv) { tbv* r = allocate(); copy(*r, bv); return r; } tbv* tbv_manager::allocate(uint64 val) { tbv* v = allocate0(); for (unsigned bit = num_tbits(); bit > 0;) { --bit; if (val & (1ULL << bit)) { set(*v, bit, BIT_1); } else { set(*v, bit, BIT_0); } } return v; } tbv* tbv_manager::allocate(uint64 val, unsigned hi, unsigned lo) { tbv* v = allocateX(); SASSERT(64 >= num_tbits() && num_tbits() > hi && hi >= lo); set(*v, val, hi, lo); return v; } tbv* tbv_manager::allocate(tbv const& bv, unsigned const* permutation) { tbv* r = allocate(); unsigned sz = num_tbits(); for (unsigned i = 0; i < sz; ++i) { set(*r, permutation[i], bv[i]); } return r; } tbv* tbv_manager::allocate(char const* bv) { tbv* result = allocateX(); unsigned i = 0, sz = num_tbits(); while(*bv && i < sz) { if (*bv == '0') set(*result, i++, BIT_0); else if (*bv == '1') set(*result, i++, BIT_1); else if (*bv == '*') i++; else if (*bv == 'x') i++; else if (i == 0 && (*bv == ' ' || *bv == '\t')) ; else break; ++bv; } return result; } tbv* tbv_manager::project(bit_vector const& to_delete, tbv const& src) { tbv* r = allocate(); unsigned i, j; unsigned n = to_delete.size(); for (i = 0, j = 0; i < n; ++i) { if (!to_delete.get(i)) { set(*r, j, src[i]); ++j; } } SASSERT(num_tbits() == j); return r; } void tbv_manager::set(tbv& dst, unsigned index, tbit value) { SASSERT(index < num_tbits()); m.set(dst, 2*index, (value & 2) != 0); m.set(dst, 2*index+1, (value & 1) != 0); } void tbv_manager::set(tbv& dst, uint64 val, unsigned hi, unsigned lo) { SASSERT(lo <= hi && hi < num_tbits()); for (unsigned i = 0; i < hi - lo + 1; ++i) { set(dst, lo + i, (val & (1ULL << i))?BIT_1:BIT_0); } } void tbv_manager::set(tbv& dst, rational const& r, unsigned hi, unsigned lo) { SASSERT(lo <= hi && hi < num_tbits()); if (r.is_uint64()) { set(dst, r.get_uint64(), hi, lo); return; } for (unsigned i = 0; i < hi - lo + 1; ++i) { if (bitwise_and(r, rational::power_of_two(i)).is_zero()) set(dst, lo + i, BIT_0); else set(dst, lo + i, BIT_1); } } void tbv_manager::set(tbv& dst, tbv const& other, unsigned hi, unsigned lo) { dst.set(other, 2*hi+1, 2*lo); } tbv* tbv_manager::allocate(rational const& r) { if (r.is_uint64()) { return allocate(r.get_uint64()); } tbv* v = allocate0(); for (unsigned bit = num_tbits(); bit > 0; ) { --bit; if (bitwise_and(r, rational::power_of_two(bit)).is_zero()) { set(*v, bit, BIT_0); } else { set(*v, bit, BIT_1); } } return v; } void tbv_manager::deallocate(tbv* bv) { DEBUG_CODE( if (!allocated_tbvs.contains(bv)) { std::cout << "double deallocate: " << bv << "\n"; UNREACHABLE(); } if (s_debug_alloc) { TRACE("doc", tout << "deallocate: " << bv << "\n";); } allocated_tbvs.erase(bv);); m.deallocate(bv); } void tbv_manager::copy(tbv& dst, tbv const& src) const { m.copy(dst, src); } tbv& tbv_manager::fill0(tbv& bv) const { // 10101010 = 2 + 8 + 32 + 128 memset(bv.m_data, 2 + 8 + 32 + 128, m.num_bytes()); return bv; } tbv& tbv_manager::fill1(tbv& bv) const { // 01010101 = 1 + 4 + 16 + 64 memset(bv.m_data, 1 + 4 + 16 + 64, m.num_bytes()); return bv; } tbv& tbv_manager::fillX(tbv& bv) const { m.fill1(bv); return bv; } tbv& tbv_manager::set_or(tbv& dst, tbv const& src) const { m.set_or(dst, src); return dst; } bool tbv_manager::set_and(tbv& dst, tbv const& src) const { m.set_and(dst, src); return is_well_formed(dst); } bool tbv_manager::is_well_formed(tbv const& dst) const { unsigned nw = m.num_words(); unsigned w; for (unsigned i = 0; i + 1 < nw; ++i) { w = dst.get_word(i); w = w | (w << 1) | 0x55555555; if (w != 0xFFFFFFFF) return false; } if (nw > 0) { w = m.last_word(dst); w = w | (w << 1) | 0x55555555 | ~m.get_mask(); if (w != 0xFFFFFFFF) return false; } return true; } void tbv_manager::complement(tbv const& src, ptr_vector& result) { tbv* r; unsigned n = num_tbits(); for (unsigned i = 0; i < n; ++i) { switch (src.get(i)) { case BIT_0: r = allocate(src); set(*r, i, BIT_1); result.push_back(r); break; case BIT_1: r = allocate(src); set(*r, i, BIT_0); result.push_back(r); break; default: break; } } } bool tbv_manager::equals(tbv const& a, tbv const& b) const { return m.equals(a, b); } unsigned tbv_manager::hash(tbv const& src) const { return m.hash(src); } bool tbv_manager::contains(tbv const& a, tbv const& b) const { return m.contains(a, b); } bool tbv_manager::contains(tbv const& a, unsigned_vector const& colsa, tbv const& b, unsigned_vector const& colsb) const { for (unsigned i = 0; i < colsa.size(); ++i) { tbit bit_a = a[colsa[i]]; if (bit_a == BIT_x) continue; if (bit_a != b[colsb[i]]) return false; } return true; } bool tbv_manager::intersect(tbv const& a, tbv const& b, tbv& result) { copy(result, a); return set_and(result, b); } std::ostream& tbv_manager::display(std::ostream& out, tbv const& b, unsigned hi, unsigned lo) const { SASSERT(lo <= hi && hi < num_tbits()); for (unsigned i = hi+1; i-- > lo; ) { switch (b.get(i)) { case BIT_0: out << '0'; break; case BIT_1: out << '1'; break; case BIT_x: out << 'x'; break; case BIT_z: out << 'z'; break; default: UNREACHABLE(); } } return out; } std::ostream& tbv_manager::display(std::ostream& out, tbv const& b) const { if (num_tbits() == 0) return out << "[]"; return display(out, b, num_tbits()-1, 0); } expr_ref tbv_manager::to_formula(ast_manager& m, tbv const& src) { expr_ref result(m); expr_ref_vector conj(m); for (unsigned i = 0; i < num_tbits(); ++i) { switch (src[i]) { case BIT_0: conj.push_back(m.mk_not(m.mk_const(symbol(i), m.mk_bool_sort()))); break; case BIT_1: conj.push_back(m.mk_const(symbol(i), m.mk_bool_sort())); break; default: break; } } result = mk_and(m, conj.size(), conj.c_ptr()); return result; } expr_ref tbv_manager::mk_var(ast_manager& m, unsigned i) { return expr_ref(m.mk_const(symbol(i), m.mk_bool_sort()), m); } z3-4.4.0/src/muz/rel/dl_base.h0000644000175000017500000014514412540347414015604 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_base.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-23. Revision History: --*/ #ifndef _DL_BASE_H_ #define _DL_BASE_H_ #define DL_LEAK_HUNTING 0 #include #include"ast.h" #include"map.h" #include"vector.h" #include"ref.h" #include"dl_util.h" #include"dl_context.h" namespace datalog { class context; class relation_manager; template class scoped_rel { T* m_t; public: scoped_rel(T* t) : m_t(t) {} ~scoped_rel() { if (m_t) { universal_delete(m_t); } } scoped_rel() : m_t(0) {} scoped_rel& operator=(T* t) { if (m_t && t != m_t) { universal_delete(m_t); } m_t = t; return *this; } T* operator->() { return m_t; } const T* operator->() const { return m_t; } T& operator*() { return *m_t; } const T& operator*() const { return *m_t; } operator bool() const { return m_t!=0; } T* get() const { return m_t; } /** \brief Remove object from \c scoped_rel without deleting it. */ T* release() { T* res = m_t; m_t = 0; return res; } }; ast_manager & get_ast_manager_from_rel_manager(const relation_manager & rm); context & get_context_from_rel_manager(const relation_manager & rm); typedef func_decl_set decl_set; #if DL_LEAK_HUNTING void leak_guard_check(const symbol & s); #endif void universal_delete(relation_base* ptr); void universal_delete(table_base* ptr); void dealloc_ptr_vector_content(ptr_vector & v); /** Termplate class containing common infrastructure for relations and tables */ template struct tr_infrastructure { typedef typename Traits::plugin plugin; typedef typename Traits::base_object base_object; typedef typename Traits::element element; typedef typename Traits::fact fact; typedef typename Traits::sort sort; typedef typename Traits::signature_base_base signature_base_base; //this must be a vector-like type typedef typename Traits::signature signature; //this must be a vector-like type /** The client submits an initial class to be used as a base for signature. Then we extend it by the common signature methods into a signature_base class which then the client inherits from to obtain the actual signature class. */ class signature_base : public signature_base_base { public: bool operator==(const signature & o) const { unsigned n=signature_base_base::size(); if(n!=o.size()) { return false; } return memcmp(this->c_ptr(), o.c_ptr(), n*sizeof(sort))==0; /*for(unsigned i=0; i=2); result=src; permutate_by_cycle(result, cycle_len, permutation_cycle); } /** \brief Into \c result assign signature \c src with reordered columns. */ static void from_permutation_rename(const signature & src, const unsigned * permutation, signature & result) { result.reset(); unsigned n = src.size(); for(unsigned i=0; i row'[col] >= row[col] } */ class min_fn : public base_fn { public: virtual base_object * operator()(const base_object & t) = 0; }; class transformer_fn : public base_fn { public: virtual base_object * operator()(const base_object & t) = 0; }; class union_fn : public base_fn { public: virtual void operator()(base_object & tgt, const base_object & src, base_object * delta) = 0; void operator()(base_object & tgt, const base_object & src) { (*this)(tgt, src, static_cast(0)); } }; /** \brief Mutator for relations that propagate constraints as a consequence of combination. - supports_attachment is used to query the mutator if it allows communicating constraints to relations of the kind of the relation. - attach is used to associate downstream clients. It assumes that the relation kind is supported (supports_kind returns true) */ class mutator_fn : public base_fn { public: virtual void operator()(base_object & t) = 0; virtual bool supports_attachment(base_object& other) { return false; } virtual void attach(base_object& other) { UNREACHABLE(); } }; class intersection_filter_fn : public base_fn { public: virtual void operator()(base_object & t, const base_object & intersected_obj) = 0; }; class intersection_join_filter_fn : public base_fn { public: virtual void operator()(base_object & t, const base_object & inter1, const base_object& inter2) = 0; }; class default_join_project_fn; /** \brief Plugin class providing factory functions for a table and operations on it. The functor factory functions (mk_*_fn) may return 0. It means that the plugin is unable to perform the operation on relations/tables of the particular kind. */ class plugin_object { friend class relation_manager; friend class check_table_plugin; friend class check_relation_plugin; family_id m_kind; symbol m_name; relation_manager & m_manager; protected: plugin_object(symbol const& name, relation_manager & manager) : m_kind(null_family_id), m_name(name), m_manager(manager) {} /** \brief Check \c r is of the same kind as the plugin. */ bool check_kind(base_object const& r) const { return &r.get_plugin()==this; } public: virtual ~plugin_object() {} virtual void initialize(family_id fid) { m_kind = fid; } family_id get_kind() const { return m_kind; } symbol const& get_name() const { return m_name; } virtual void set_cancel(bool f) {} relation_manager & get_manager() const { return m_manager; } ast_manager& get_ast_manager() const { return datalog::get_ast_manager_from_rel_manager(m_manager); } context& get_context() const { return datalog::get_context_from_rel_manager(m_manager); } virtual bool can_handle_signature(const signature & s) = 0; virtual bool can_handle_signature(const signature & s, family_id kind) { return can_handle_signature(s); } /** \brief Create empty table/relation with given signature and return pointer to it. Precondition: can_handle_signature(s)==true */ virtual base_object * mk_empty(const signature & s) = 0; /** \brief Create empty table/relation with signature \c s and kind \c kind. Precondition: &orig.get_plugin()==this */ virtual base_object * mk_empty(const signature & s, family_id kind) { SASSERT(kind==get_kind()); //if plugin uses multiple kinds, this function needs to be overriden return mk_empty(s); } /** \brief Create empty table/relation of the same specification as \c orig and return pointer to it. Precondition: &orig.get_plugin()==this */ virtual base_object * mk_empty(const base_object & orig) { return mk_empty(orig.get_signature(), orig.get_kind()); } /** \brief Create full table/relation with given signature and return pointer to it. Precondition: can_handle_signature(s)==true */ virtual base_object * mk_full(func_decl* p, const signature & s) { base_object * aux = mk_empty(s); base_object * res = aux->complement(p); aux->deallocate(); return res; } virtual base_object * mk_full(func_decl* p, const signature & s, family_id kind) { if (kind == get_kind() || kind == null_family_id) { return mk_full(p, s); } base_object * aux = mk_empty(s, kind); base_object * res = aux->complement(p); aux->deallocate(); return res; } protected: //see \c relation_manager for documentation of the operations virtual join_fn * mk_join_fn(const base_object & t1, const base_object & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { return 0; } virtual transformer_fn * mk_project_fn(const base_object & t, unsigned col_cnt, const unsigned * removed_cols) { return 0; } virtual join_fn * mk_join_project_fn(const base_object & t1, const base_object & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { return 0; } virtual transformer_fn * mk_rename_fn(const base_object & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { return 0; } virtual transformer_fn * mk_permutation_rename_fn(const base_object & t, const unsigned * permutation) { return 0; } public: virtual union_fn * mk_union_fn(const base_object & tgt, const base_object & src, const base_object * delta) { return 0; } protected: virtual union_fn * mk_widen_fn(const base_object & tgt, const base_object & src, const base_object * delta) { return 0; } virtual mutator_fn * mk_filter_identical_fn(const base_object & t, unsigned col_cnt, const unsigned * identical_cols) { return 0; } virtual mutator_fn * mk_filter_equal_fn(const base_object & t, const element & value, unsigned col) { return 0; } virtual mutator_fn * mk_filter_interpreted_fn(const base_object & t, app * condition) { return 0; } virtual transformer_fn * mk_filter_interpreted_and_project_fn(const base_object & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { return 0; } virtual transformer_fn * mk_select_equal_and_project_fn(const base_object & t, const element & value, unsigned col) { return 0; } virtual intersection_filter_fn * mk_filter_by_intersection_fn(const base_object & t, const base_object & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) { return 0; } virtual intersection_filter_fn * mk_filter_by_negation_fn(const base_object & t, const base_object & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { return 0; } virtual intersection_join_filter_fn * mk_filter_by_negated_join_fn( const base_object & t, const base_object & src1, const base_object & src2, unsigned_vector const& t_cols, unsigned_vector const& src_cols, unsigned_vector const& src1_cols, unsigned_vector const& src2_cols) { return 0; } }; class base_ancestor { plugin & m_plugin; signature m_signature; family_id m_kind; #if DL_LEAK_HUNTING sort_ref m_leak_guard; #endif protected: base_ancestor(plugin & p, const signature & s) : m_plugin(p), m_signature(s), m_kind(p.get_kind()) #if DL_LEAK_HUNTING , m_leak_guard(p.get_ast_manager().mk_fresh_sort(p.get_name().bare_str()), p.get_ast_manager()) #endif { #if DL_LEAK_HUNTING leak_guard_check(m_leak_guard->get_name()); #endif } #if DL_LEAK_HUNTING base_ancestor(const base_ancestor & o) : m_plugin(o.m_plugin), m_signature(o.m_signature), m_kind(o.m_kind), m_leak_guard(m_plugin.get_ast_manager().mk_fresh_sort(m_plugin.get_name().bare_str()), m_plugin.get_ast_manager()) { leak_guard_check(m_leak_guard->get_name()); } #endif virtual ~base_ancestor() {} void set_kind(family_id kind) { SASSERT(kind>=0); m_kind = kind; } /** Since the destructor is protected, we cannot use the \c dealloc macro. */ void destroy() { SASSERT(this); this->~base_ancestor(); #if _DEBUG memory::deallocate(__FILE__, __LINE__, this); #else memory::deallocate(this); #endif } public: /** Deallocate the current object. Pointers and references to the current object become invalid after a call to this function. */ virtual void deallocate() { destroy(); } /** It must hold that operations created for particular table/relation are able to operate on tables/relations of the same signature and kind. In most cases it is sufficient to use the kind of the plugin object. However, it there is some parameter that is not reflected in the signature, the plugin may need to allocate different kind numbers to the tables is creates. */ family_id get_kind() const { return m_kind; } const signature & get_signature() const { return m_signature; } plugin & get_plugin() const { return m_plugin; } relation_manager & get_manager() const { return get_plugin().get_manager(); } virtual bool empty() const = 0; /** \brief fast emptiness check. This may be partial. The requirement is that if fast_empty returns true then the table or relation is in fact empty. It is allowed to return false even if the relation is empty. */ virtual bool fast_empty() const { return empty(); } virtual void add_fact(const fact & f) = 0; /** \brief Like \c add_fact, only here the caller guarantees that the fact is not present in the table yet. */ virtual void add_new_fact(const fact & f) { add_fact(f); } virtual bool contains_fact(const fact & f) const = 0; virtual void reset() = 0; /** \brief Return table/relation that contains the same data as the current one. */ virtual base_object * clone() const = 0; virtual bool can_swap(const base_object & o) const { return false; } virtual void swap(base_object & o) { std::swap(m_kind, o.m_kind); #if DL_LEAK_HUNTING m_leak_guard = get_plugin().get_ast_manager().mk_fresh_sort(get_plugin().get_name().bare_str()); o.m_leak_guard = get_plugin().get_ast_manager().mk_fresh_sort(get_plugin().get_name().bare_str()); leak_guard_check(m_leak_guard->get_name()); leak_guard_check(o.m_leak_guard->get_name()); #endif } virtual unsigned get_size_estimate_rows() const { return UINT_MAX; } virtual unsigned get_size_estimate_bytes() const { return UINT_MAX; } virtual bool knows_exact_size() const { return false; } unsigned num_columns() const { return get_signature().size(); } virtual void display(std::ostream & out) const = 0; }; class convenient_join_fn : public join_fn { signature m_result_sig; protected: unsigned_vector m_cols1; unsigned_vector m_cols2; convenient_join_fn(const signature & o1_sig, const signature & o2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : m_cols1(col_cnt, cols1), m_cols2(col_cnt, cols2) { signature::from_join(o1_sig, o2_sig, col_cnt, cols1, cols2, m_result_sig); } const signature & get_result_signature() const { return m_result_sig; } }; class convenient_join_project_fn : public join_fn { signature m_result_sig; protected: unsigned_vector m_cols1; unsigned_vector m_cols2; //it is non-const because it needs to be modified in sparse_table version of the join_project operator unsigned_vector m_removed_cols; convenient_join_project_fn(const signature & o1_sig, const signature & o2_sig, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) : m_cols1(joined_col_cnt, cols1), m_cols2(joined_col_cnt, cols2), m_removed_cols(removed_col_cnt, removed_cols) { signature::from_join_project(o1_sig, o2_sig, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols, m_result_sig); } const signature & get_result_signature() const { return m_result_sig; } }; class convenient_transformer_fn : public transformer_fn { signature m_result_sig; protected: signature & get_result_signature() { return m_result_sig; } const signature & get_result_signature() const { return m_result_sig; } }; class convenient_project_fn : public convenient_transformer_fn { protected: unsigned_vector m_removed_cols; convenient_project_fn(const signature & orig_sig, unsigned col_cnt, const unsigned * removed_cols) : m_removed_cols(col_cnt, removed_cols) { signature::from_project(orig_sig, col_cnt, removed_cols, convenient_transformer_fn::get_result_signature()); } }; class convenient_rename_fn : public convenient_transformer_fn { protected: const unsigned_vector m_cycle; convenient_rename_fn(const signature & orig_sig, unsigned cycle_len, const unsigned * permutation_cycle) : m_cycle(cycle_len, permutation_cycle) { signature::from_rename(orig_sig, cycle_len, permutation_cycle, convenient_transformer_fn::get_result_signature()); } }; class convenient_negation_filter_fn : public intersection_filter_fn { protected: unsigned m_joined_col_cnt; const unsigned_vector m_cols1; const unsigned_vector m_cols2; bool m_all_neg_bound; //all columns are bound at least once bool m_overlap; //one column in negated table is bound multiple times svector m_bound; convenient_negation_filter_fn(const base_object & tgt, const base_object & neg_t, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) : m_joined_col_cnt(joined_col_cnt), m_cols1(joined_col_cnt, t_cols), m_cols2(joined_col_cnt, negated_cols) { unsigned neg_sig_size = neg_t.get_signature().size(); m_overlap = false; m_bound.resize(neg_sig_size, false); for(unsigned i=0; i void make_neg_bindings(T & tgt_neg, const U & src) const { SASSERT(m_all_neg_bound); SASSERT(!m_overlap); for(unsigned i=0; i bool bindings_match(const T & tgt_neg, const U & src) const { for(unsigned i=0; i renamer_vector; unsigned_vector m_permutation; //this is valid only before m_renamers_initialized becomes true bool m_renamers_initialized; renamer_vector m_renamers; public: default_permutation_rename_fn(const base_object & o, const unsigned * permutation) : m_permutation(o.get_signature().size(), permutation), m_renamers_initialized(false) {} ~default_permutation_rename_fn() { dealloc_ptr_vector_content(m_renamers); } base_object * operator()(const base_object & o) { const base_object * res = &o; scoped_rel res_scoped; if(m_renamers_initialized) { typename renamer_vector::iterator rit = m_renamers.begin(); typename renamer_vector::iterator rend = m_renamers.end(); for(; rit!=rend; ++rit) { res_scoped = (**rit)(*res); res = res_scoped.get(); } } else { SASSERT(m_renamers.empty()); unsigned_vector cycle; while(try_remove_cycle_from_permutation(m_permutation, cycle)) { transformer_fn * renamer = o.get_manager().mk_rename_fn(*res, cycle); SASSERT(renamer); m_renamers.push_back(renamer); cycle.reset(); res_scoped = (*renamer)(*res); res = res_scoped.get(); } m_renamers_initialized = true; } if(res_scoped) { SASSERT(res==res_scoped.get()); //we don't want to delete the last one since we'll be returning it return res_scoped.release(); } else { SASSERT(res==&o); return res->clone(); } } }; }; // ----------------------------------- // // relation_base // // ----------------------------------- class relation_signature; class relation_plugin; class relation_base; typedef ptr_vector relation_signature_base0; typedef ptr_hash relation_sort_hash; struct relation_traits { typedef relation_plugin plugin; typedef relation_base base_object; typedef relation_element element; typedef relation_fact fact; typedef relation_sort sort; typedef relation_signature_base0 signature_base_base; typedef relation_signature signature; }; typedef tr_infrastructure relation_infrastructure; typedef relation_infrastructure::base_fn base_relation_fn; typedef relation_infrastructure::join_fn relation_join_fn; typedef relation_infrastructure::transformer_fn relation_transformer_fn; typedef relation_infrastructure::union_fn relation_union_fn; typedef relation_infrastructure::mutator_fn relation_mutator_fn; typedef relation_infrastructure::intersection_filter_fn relation_intersection_filter_fn; typedef relation_infrastructure::intersection_join_filter_fn relation_intersection_join_filter_fn; typedef relation_infrastructure::convenient_join_fn convenient_relation_join_fn; typedef relation_infrastructure::convenient_join_project_fn convenient_relation_join_project_fn; typedef relation_infrastructure::convenient_transformer_fn convenient_relation_transformer_fn; typedef relation_infrastructure::convenient_project_fn convenient_relation_project_fn; typedef relation_infrastructure::convenient_rename_fn convenient_relation_rename_fn; typedef relation_infrastructure::convenient_negation_filter_fn convenient_relation_negation_filter_fn; typedef relation_infrastructure::identity_transformer_fn identity_relation_transformer_fn; typedef relation_infrastructure::identity_mutator_fn identity_relation_mutator_fn; typedef relation_infrastructure::identity_intersection_filter_fn identity_relation_intersection_filter_fn; typedef relation_infrastructure::default_permutation_rename_fn default_relation_permutation_rename_fn; class relation_signature : public relation_infrastructure::signature_base { public: bool operator!=(const relation_signature & o) const { return !(*this==o); } void output(ast_manager & m, std::ostream & out) const; struct hash { unsigned operator()(relation_signature const& s) const { return obj_vector_hash(s); } }; struct eq { bool operator()(relation_signature const& s1, relation_signature const& s2) const { return s1 == s2; } }; }; class relation_plugin : public relation_infrastructure::plugin_object { protected: enum special_relation_type { ST_ORDINARY, ST_TABLE_RELATION, ST_FINITE_PRODUCT_RELATION, ST_PRODUCT_RELATION, ST_SIEVE_RELATION }; private: special_relation_type m_special_type; protected: relation_plugin(symbol const& name, relation_manager & manager, special_relation_type special_type = ST_ORDINARY) : plugin_object(name, manager), m_special_type(special_type) {} public: bool from_table() const { return m_special_type==ST_TABLE_RELATION; } bool is_finite_product_relation() const { return m_special_type==ST_FINITE_PRODUCT_RELATION; } bool is_product_relation() const { return m_special_type==ST_PRODUCT_RELATION; } bool is_sieve_relation() const { return m_special_type==ST_SIEVE_RELATION; } /** \brief If true, the relation can contain only one or zero elements. Having this zero allows the finite_product_relation to perform some operations in a simpler way. (KH: I started implementing finite_product_relation::inner_singleton_union_fn that takes advantage of it, but it's not finished.) */ virtual bool is_singleton_relation() const { return false; } }; class relation_base : public relation_infrastructure::base_ancestor { protected: relation_base(relation_plugin & plugin, const relation_signature & s) : base_ancestor(plugin, s) {} virtual ~relation_base() {} public: virtual relation_base * complement(func_decl* p) const = 0; virtual void reset(); virtual void display_tuples(func_decl & pred, std::ostream & out) const { out << "Tuples in " << pred.get_name() << ": \n"; display(out); } virtual void to_formula(expr_ref& fml) const = 0; bool from_table() const { return get_plugin().from_table(); } virtual bool is_precise() const { return true; } }; typedef ptr_vector relation_vector; // ----------------------------------- // // table_base // // ----------------------------------- class table_signature; class table_plugin; class table_base; typedef uint64 table_sort; typedef svector table_signature_base0; typedef uint64_hash table_sort_hash; typedef uint64_hash table_element_hash; struct table_traits { typedef table_plugin plugin; typedef table_base base_object; typedef table_element element; typedef table_fact fact; typedef table_sort sort; typedef table_signature_base0 signature_base_base; typedef table_signature signature; }; typedef tr_infrastructure table_infrastructure; typedef table_infrastructure::base_fn base_table_fn; typedef table_infrastructure::join_fn table_join_fn; typedef table_infrastructure::min_fn table_min_fn; typedef table_infrastructure::transformer_fn table_transformer_fn; typedef table_infrastructure::union_fn table_union_fn; typedef table_infrastructure::mutator_fn table_mutator_fn; typedef table_infrastructure::intersection_filter_fn table_intersection_filter_fn; typedef table_infrastructure::intersection_join_filter_fn table_intersection_join_filter_fn; typedef table_infrastructure::convenient_join_fn convenient_table_join_fn; typedef table_infrastructure::convenient_join_project_fn convenient_table_join_project_fn; typedef table_infrastructure::convenient_transformer_fn convenient_table_transformer_fn; typedef table_infrastructure::convenient_project_fn convenient_table_project_fn; typedef table_infrastructure::convenient_rename_fn convenient_table_rename_fn; typedef table_infrastructure::convenient_negation_filter_fn convenient_table_negation_filter_fn; typedef table_infrastructure::identity_transformer_fn identity_table_transformer_fn; typedef table_infrastructure::identity_mutator_fn identity_table_mutator_fn; typedef table_infrastructure::identity_intersection_filter_fn identity_table_intersection_filter_fn; typedef table_infrastructure::default_permutation_rename_fn default_table_permutation_rename_fn; class table_row_mutator_fn { public: /** \brief The function is called for a particular table row. The \c func_columns contains a pointer to an array of functional column values that can be modified. If the function returns true, the modification will appear in the table; otherwise the row will be deleted. It is possible that one call to the function stands for multiple table rows that share the same functional column values. */ virtual bool operator()(table_element * func_columns) = 0; }; class table_row_pair_reduce_fn { public: /** \brief The function is called for pair of table rows that became duplicit due to projection. The values that are in the first array after return from the function will be used for the resulting row. It is assumed that the function is idempotent: when the two functional sub-tuples are equal, the result is assumed to be equal to them as well, so the function may not be evaluated for them. */ virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) = 0; }; class table_signature : public table_infrastructure::signature_base { public: struct hash { unsigned operator()(table_signature const& s) const { return svector_hash()(s); } }; struct eq { bool operator()(table_signature const& s1, table_signature const& s2) const { return s1 == s2; } }; private: unsigned m_functional_columns; public: table_signature() : m_functional_columns(0) {} void swap(table_signature & s) { signature_base::swap(s); std::swap(m_functional_columns, s.m_functional_columns); } /** \brief The returned value is the number of last columns that are functional. The uniqueness is enforced on non-functional columns. When projection causes two facts to have equal non-functional parts, it is not defined which one of them is retained. */ unsigned functional_columns() const { return m_functional_columns; } void set_functional_columns(unsigned val) { SASSERT(size()>=val); m_functional_columns = val; } /** \brief Return index of the first functional column, or the size of the signature if there are no functional columns. */ unsigned first_functional() const { return size()-m_functional_columns; } bool operator==(const table_signature & o) const { return signature_base::operator==(o) && m_functional_columns==o.m_functional_columns; } bool operator!=(const table_signature & o) const { return !(*this==o); } /** \brief return true iof the two signatures are equal when we ignore which columns are functional. */ bool equal_up_to_fn_mark(const table_signature & o) const { return signature_base::operator==(o); } /** \brief Into \c result assign signature of result of join of relations with signatures \c s1 and \c s2. The result is (non-functional of s1)(non-functional of s2)(functional of s1)(functional of s2) */ static void from_join(const table_signature & s1, const table_signature & s2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, table_signature & result); static void from_join_project(const table_signature & s1, const table_signature & s2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, table_signature & result); /** \brief Into \c result assign signature projected from \c src. The array of removed columns must be sorted in ascending order. If we remove at least one non-functional column, all the columns in the result are non-functional. */ static void from_project(const table_signature & src, unsigned col_cnt, const unsigned * removed_cols, table_signature & result); static void from_project_with_reduce(const table_signature & src, unsigned col_cnt, const unsigned * removed_cols, table_signature & result); /** \brief Into \c result assign signature \c src with reordered columns. Permutations between functional and nonfunctional columns are not allowed. */ static void from_rename(const table_signature & src, unsigned cycle_len, const unsigned * permutation_cycle, table_signature & result) { signature_base::from_rename(src, cycle_len, permutation_cycle, result); result.set_functional_columns(src.functional_columns()); #if Z3DEBUG unsigned first_src_fun = src.size()-src.functional_columns(); bool in_func = permutation_cycle[0]>=first_src_fun; for(unsigned i=1;i=first_src_fun)); } #endif } /** \brief Into \c result assign signature \c src with reordered columns. Permutations mixing functional and nonfunctional columns are not allowed. */ static void from_permutation_rename(const table_signature & src, const unsigned * permutation, table_signature & result) { signature_base::from_permutation_rename(src, permutation, result); result.set_functional_columns(src.functional_columns()); #if Z3DEBUG unsigned sz = src.size(); unsigned first_src_fun = sz-src.functional_columns(); for(unsigned i=first_src_fun;i=first_src_fun); } #endif } }; class table_plugin : public table_infrastructure::plugin_object { friend class relation_manager; class min_fn; protected: table_plugin(symbol const& n, relation_manager & manager) : plugin_object(n, manager) {} public: virtual bool can_handle_signature(const table_signature & s) { return s.functional_columns()==0; } protected: virtual table_min_fn * mk_min_fn(const table_base & t, unsigned_vector & group_by_cols, const unsigned col); /** If the returned value is non-zero, the returned object must take ownership of \c mapper. Otherwise \c mapper must remain unmodified. */ virtual table_mutator_fn * mk_map_fn(const table_base & t, table_row_mutator_fn * mapper) { return 0; } /** If the returned value is non-zero, the returned object must take ownership of \c reducer. Otherwise \c reducer must remain unmodified. */ virtual table_transformer_fn * mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) { return 0; } }; class table_base : public table_infrastructure::base_ancestor { protected: table_base(table_plugin & plugin, const table_signature & s) : base_ancestor(plugin, s) {} virtual ~table_base() {} public: virtual table_base * clone() const; virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const; virtual bool empty() const; /** \brief Return true if table contains fact that corresponds to \c f in all non-functional columns. */ virtual bool contains_fact(const table_fact & f) const; /** \brief If \c f (i.e. its non-functional part) is not present in the table, add it and return true. Otherwise update \c f, so that the values of functional columns correspond to the ones present in the table. */ virtual bool suggest_fact(table_fact & f); /** \brief If \c f (i.e. its non-functional part) is not present in the table, return false. Otherwise update \c f, so that the values of functional columns correspond to the ones present in the table and return true. */ virtual bool fetch_fact(table_fact & f) const; /** \brief Ensure fact \c f is present in the table (including the values of its functional columns). */ virtual void ensure_fact(const table_fact & f); virtual void remove_fact(const table_fact & fact) { SASSERT(fact.size() == get_signature().size()); remove_fact(fact.c_ptr()); } virtual void remove_fact(table_element const* fact) = 0; virtual void remove_facts(unsigned fact_cnt, const table_fact * facts); virtual void remove_facts(unsigned fact_cnt, const table_element * facts); virtual void reset(); class row_interface; virtual void display(std::ostream & out) const; /** \brief Convert table to a formula that encodes the table. The columns correspond to bound variables indexed as 0, .., sig.size()-1 */ virtual void to_formula(relation_signature const& sig, expr_ref& fml) const; protected: class iterator_core { unsigned m_ref_cnt; public: iterator_core() : m_ref_cnt(0) {} virtual ~iterator_core() {} void inc_ref() { m_ref_cnt++; } void dec_ref() { SASSERT(m_ref_cnt>0); m_ref_cnt--; if(m_ref_cnt==0) { dealloc(this); } } virtual bool is_finished() const = 0; virtual row_interface & operator*() = 0; virtual void operator++() = 0; virtual bool operator==(const iterator_core & it) { //we worry about the equality operator only because of checking //the equality with the end() iterator if(is_finished() && it.is_finished()) { return true; } return false; } private: //private and undefined copy constructor and assignment operator iterator_core(const iterator_core &); iterator_core & operator=(const iterator_core &); }; struct row_iterator_core { unsigned m_ref_cnt; public: row_iterator_core() : m_ref_cnt(0) {} virtual ~row_iterator_core() {} void inc_ref() { m_ref_cnt++; } void dec_ref() { SASSERT(m_ref_cnt>0); m_ref_cnt--; if(m_ref_cnt==0) { dealloc(this); } } virtual bool is_finished() const = 0; virtual table_element operator*() = 0; virtual void operator++() = 0; virtual bool operator==(const row_iterator_core & it) { //we worry about the equality operator only because of checking //the equality with the end() iterator if(is_finished() && it.is_finished()) { return true; } return false; } private: //private and undefined copy constructor and assignment operator row_iterator_core(const row_iterator_core &); row_iterator_core & operator=(const row_iterator_core &); }; public: class iterator { friend class table_base; ref m_core; iterator(iterator_core * core) : m_core(core) {} public: /** \brief Return reference to a row_interface object for the current row. The reference is valid only until the \c operator++() is called or until the iterator is invalidated. */ row_interface & operator*() { return *(*m_core); } row_interface * operator->() { return &(*(*m_core)); } iterator & operator++() { ++(*m_core); return *this; } bool operator==(const iterator & it) { return (*m_core)==(*it.m_core); } bool operator!=(const iterator & it) { return !operator==(it); } }; class row_iterator { friend class table_base; friend class row_interface; ref m_core; row_iterator(row_iterator_core * core) : m_core(core) {} public: table_element operator*() { return *(*m_core); } row_iterator & operator++() { ++(*m_core); return *this; } bool operator==(const row_iterator & it) { return (*m_core)==(*it.m_core); } bool operator!=(const row_iterator & it) { return !operator==(it); } }; virtual iterator begin() const = 0; virtual iterator end() const = 0; class row_interface { class fact_row_iterator; const table_base & m_parent_table; public: typedef row_iterator iterator; typedef row_iterator const_iterator; row_interface(const table_base & parent_table) : m_parent_table(parent_table) {} virtual ~row_interface() {} virtual table_element operator[](unsigned col) const = 0; unsigned size() const { return m_parent_table.get_signature().size(); } virtual void get_fact(table_fact & result) const; virtual row_iterator begin() const; virtual row_iterator end() const; virtual void display(std::ostream & out) const; }; protected: class caching_row_interface : public row_interface { mutable table_fact m_current; bool populated() const { return !m_current.empty(); } void ensure_populated() const { if(!populated()) { get_fact(m_current); } } public: caching_row_interface(const table_base & parent) : row_interface(parent) {} virtual void get_fact(table_fact & result) const = 0; virtual table_element operator[](unsigned col) const { ensure_populated(); return m_current[col]; } /** \brief Resets the cache of the row object. Must be called when the row object begins to represent a different row in the table. */ void reset() { m_current.reset(); } }; //This function is here to create iterator instances in classes that derive from table_base. //We do not want to make the constructor of the iterator class public, and being private, the //inheritor classes cannot see it directly. static iterator mk_iterator(iterator_core * core) { return iterator(core); } }; /** \brief Populate vector \c renaming_args so that it can be used as an argument to \c var_subst. The renaming we want is one that transforms variables with numbers of indexes of \c map into the values of at those indexes. If a value if \c UINT_MAX, it means we do not transform the index corresponding to it. */ void get_renaming_args(const unsigned_vector & map, const relation_signature & orig_sig, expr_ref_vector & renaming_arg); }; #endif /* _DL_BASE_H_ */ z3-4.4.0/src/muz/rel/aig_exporter.cpp0000644000175000017500000002547612540347414017243 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: aig_exporter.cpp Abstract: Export AIG files from horn clauses --*/ #include "aig_exporter.h" #include "dl_context.h" #include namespace datalog { aig_exporter::aig_exporter(const rule_set& rules, context& ctx, const fact_vector *facts) : m_rules(rules), m_facts(facts), m(ctx.get_manager()), m_rm(ctx.get_rule_manager()), m_aigm(m), m_next_decl_id(1), m_next_aig_expr_id(2), m_num_and_gates(0), m_latch_vars(m), m_latch_varsp(m), m_ruleid_var_set(m), m_ruleid_varp_set(m) { std::set predicates; for (rule_set::decl2rules::iterator I = m_rules.begin_grouped_rules(), E = m_rules.end_grouped_rules(); I != E; ++I) { predicates.insert(I->m_key); } for (fact_vector::const_iterator I = facts->begin(), E = facts->end(); I != E; ++I) { predicates.insert(I->first); } // reserve pred id = 0 for initalization purposes unsigned num_preds = (unsigned)predicates.size() + 1; // poor's man round-up log2 unsigned preds_bitsize = log2(num_preds); if ((1U << preds_bitsize) < num_preds) ++preds_bitsize; SASSERT((1U << preds_bitsize) >= num_preds); for (unsigned i = 0; i < preds_bitsize; ++i) { m_ruleid_var_set.push_back(m.mk_fresh_const("rule_id", m.mk_bool_sort())); m_ruleid_varp_set.push_back(m.mk_fresh_const("rule_id_p", m.mk_bool_sort())); } } void aig_exporter::mk_latch_vars(unsigned n) { for (unsigned i = m_latch_vars.size(); i <= n; ++i) { m_latch_vars.push_back(m.mk_fresh_const("latch_var", m.mk_bool_sort())); m_latch_varsp.push_back(m.mk_fresh_const("latch_varp", m.mk_bool_sort())); } SASSERT(m_latch_vars.size() > n); } expr* aig_exporter::get_latch_var(unsigned i, const expr_ref_vector& vars) { mk_latch_vars(i); return vars.get(i); } void aig_exporter::assert_pred_id(func_decl *decl, const expr_ref_vector& vars, expr_ref_vector& exprs) { unsigned id = 0; if (decl && !m_decl_id_map.find(decl, id)) { id = m_next_decl_id++; SASSERT(id < (1U << vars.size())); m_decl_id_map.insert(decl, id); } for (unsigned i = 0; i < vars.size(); ++i) { exprs.push_back((id & (1U << i)) ? vars[i] : m.mk_not(vars[i])); } } void aig_exporter::collect_var_substs(substitution& subst, const app *h, const expr_ref_vector& vars, expr_ref_vector& eqs) { for (unsigned i = 0; i < h->get_num_args(); ++i) { expr *arg = h->get_arg(i); expr *latchvar = get_latch_var(i, vars); if (is_var(arg)) { var *v = to_var(arg); expr_offset othervar; if (subst.find(v, 0, othervar)) { eqs.push_back(m.mk_eq(latchvar, othervar.get_expr())); } else { subst.insert(v, 0, expr_offset(latchvar, 0)); } } else { eqs.push_back(m.mk_eq(latchvar, arg)); } } } void aig_exporter::operator()(std::ostream& out) { expr_ref_vector transition_function(m), output_preds(m); var_ref_vector input_vars(m); rule_counter& vc = m_rm.get_counter(); expr_ref_vector exprs(m); substitution subst(m); for (rule_set::decl2rules::iterator I = m_rules.begin_grouped_rules(), E = m_rules.end_grouped_rules(); I != E; ++I) { for (rule_vector::iterator II = I->get_value()->begin(), EE = I->get_value()->end(); II != EE; ++II) { rule *r = *II; unsigned numqs = r->get_positive_tail_size(); if (numqs > 1) { std::cerr << "non-linear clauses not supported\n"; exit(-1); } if (numqs != r->get_uninterpreted_tail_size()) { std::cerr << "negation of queries not supported\n"; exit(-1); } exprs.reset(); assert_pred_id(numqs ? r->get_tail(0)->get_decl() : 0, m_ruleid_var_set, exprs); assert_pred_id(r->get_head()->get_decl(), m_ruleid_varp_set, exprs); subst.reset(); subst.reserve(1, vc.get_max_rule_var(*r)+1); if (numqs) collect_var_substs(subst, r->get_tail(0), m_latch_vars, exprs); collect_var_substs(subst, r->get_head(), m_latch_varsp, exprs); for (unsigned i = numqs; i < r->get_tail_size(); ++i) { expr_ref e(m); subst.apply(r->get_tail(i), e); exprs.push_back(e); } transition_function.push_back(m.mk_and(exprs.size(), exprs.c_ptr())); } } // collect table facts if (m_facts) { for (fact_vector::const_iterator I = m_facts->begin(), E = m_facts->end(); I != E; ++I) { exprs.reset(); assert_pred_id(0, m_ruleid_var_set, exprs); assert_pred_id(I->first, m_ruleid_varp_set, exprs); for (unsigned i = 0; i < I->second.size(); ++i) { exprs.push_back(m.mk_eq(get_latch_var(i, m_latch_varsp), I->second[i])); } transition_function.push_back(m.mk_and(exprs.size(), exprs.c_ptr())); } } expr *tr = m.mk_or(transition_function.size(), transition_function.c_ptr()); aig_ref aig = m_aigm.mk_aig(tr); expr_ref aig_expr(m); m_aigm.to_formula(aig, aig_expr); #if 0 std::cout << mk_pp(tr, m) << "\n\n"; std::cout << mk_pp(aig_expr, m) << "\n\n"; #endif // make rule_id vars latches for (unsigned i = 0; i < m_ruleid_var_set.size(); ++i) { m_latch_vars.push_back(m_ruleid_var_set.get(i)); m_latch_varsp.push_back(m_ruleid_varp_set.get(i)); } // create vars for latches for (unsigned i = 0; i < m_latch_vars.size(); ++i) { mk_var(m_latch_vars.get(i)); mk_input_var(m_latch_varsp.get(i)); } unsigned tr_id = expr_to_aig(aig_expr); // create latch next state variables: (ite tr varp var) unsigned_vector latch_varp_ids; for (unsigned i = 0; i < m_latch_vars.size(); ++i) { unsigned in_val = mk_and(tr_id, get_var(m_latch_varsp.get(i))); unsigned latch_val = mk_and(neg(tr_id), get_var(m_latch_vars.get(i))); latch_varp_ids.push_back(mk_or(in_val, latch_val)); } m_latch_varsp.reset(); // create output variable (true iff an output predicate is derivable) unsigned output_id = 0; { expr_ref_vector output(m); const func_decl_set& preds = m_rules.get_output_predicates(); for (func_decl_set::iterator I = preds.begin(), E = preds.end(); I != E; ++I) { exprs.reset(); assert_pred_id(*I, m_ruleid_var_set, exprs); output.push_back(m.mk_and(exprs.size(), exprs.c_ptr())); } expr *out = m.mk_or(output.size(), output.c_ptr()); aig = m_aigm.mk_aig(out); m_aigm.to_formula(aig, aig_expr); output_id = expr_to_aig(aig_expr); #if 0 std::cout << "output formula\n"; std::cout << mk_pp(out, m) << "\n\n"; std::cout << mk_pp(aig_expr, m) << "\n\n"; #endif } // 1) print header // aag var_index inputs latches outputs andgates out << "aag " << (m_next_aig_expr_id-1)/2 << ' ' << m_input_vars.size() << ' ' << m_latch_vars.size() << " 1 " << m_num_and_gates << '\n'; // 2) print inputs for (unsigned i = 0; i < m_input_vars.size(); ++i) { out << m_input_vars[i] << '\n'; } // 3) print latches for (unsigned i = 0; i < m_latch_vars.size(); ++i) { out << get_var(m_latch_vars.get(i)) << ' ' << latch_varp_ids[i] << '\n'; } // 4) print outputs (just one for now) out << output_id << '\n'; // 5) print formula out << m_buffer.str(); } unsigned aig_exporter::expr_to_aig(const expr *e) { unsigned id; if (m_aig_expr_id_map.find(e, id)) return id; if (is_uninterp_const(e)) return get_var(e); switch (e->get_kind()) { case AST_APP: { const app *a = to_app(e); switch (a->get_decl_kind()) { case OP_OR: SASSERT(a->get_num_args() > 0); id = expr_to_aig(a->get_arg(0)); for (unsigned i = 1; i < a->get_num_args(); ++i) { id = mk_or(id, expr_to_aig(a->get_arg(i))); } m_aig_expr_id_map.insert(e, id); return id; case OP_NOT: return neg(expr_to_aig(a->get_arg(0))); case OP_FALSE: return 0; case OP_TRUE: return 1; } break;} case AST_VAR: return get_var(e); default: UNREACHABLE(); } UNREACHABLE(); return 0; } unsigned aig_exporter::neg(unsigned id) const { return (id % 2) ? (id-1) : (id+1); } unsigned aig_exporter::mk_and(unsigned id1, unsigned id2) { if (id1 > id2) std::swap(id1, id2); std::pair key(id1, id2); and_gates_map::const_iterator I = m_and_gates_map.find(key); if (I != m_and_gates_map.end()) return I->second; unsigned id = mk_expr_id(); m_buffer << id << ' ' << id1 << ' ' << id2 << '\n'; m_and_gates_map[key] = id; ++m_num_and_gates; return id; } unsigned aig_exporter::mk_or(unsigned id1, unsigned id2) { return neg(mk_and(neg(id1), neg(id2))); } unsigned aig_exporter::get_var(const expr *e) { unsigned id; if (m_aig_expr_id_map.find(e, id)) return id; return mk_input_var(e); } unsigned aig_exporter::mk_var(const expr *e) { SASSERT(!m_aig_expr_id_map.contains(e)); unsigned id = mk_expr_id(); m_aig_expr_id_map.insert(e, id); return id; } unsigned aig_exporter::mk_input_var(const expr *e) { SASSERT(!m_aig_expr_id_map.contains(e)); unsigned id = mk_expr_id(); m_input_vars.push_back(id); if (e) m_aig_expr_id_map.insert(e, id); return id; } unsigned aig_exporter::mk_expr_id() { unsigned id = m_next_aig_expr_id; m_next_aig_expr_id += 2; return id; } } z3-4.4.0/src/muz/rel/dl_lazy_table.cpp0000644000175000017500000003704112540347414017347 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_lazy_table.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2013-09-04 Revision History: --*/ #include "dl_lazy_table.h" #include "dl_relation_manager.h" #include namespace datalog { // ------------------ // lazy_table_plugin: symbol lazy_table_plugin::mk_name(table_plugin& p) { std::ostringstream strm; strm << "lazy_" << p.get_name(); return symbol(strm.str().c_str()); } table_base * lazy_table_plugin::mk_empty(const table_signature & s) { return alloc(lazy_table, alloc(lazy_table_base, *this, m_plugin.mk_empty(s))); } lazy_table const& lazy_table_plugin::get(table_base const& tb) { return dynamic_cast(tb); } lazy_table& lazy_table_plugin::get(table_base& tb) { return dynamic_cast(tb); } lazy_table const* lazy_table_plugin::get(table_base const* tb) { return dynamic_cast(tb); } lazy_table* lazy_table_plugin::get(table_base* tb) { return dynamic_cast(tb); } // -------------------------- // lazy_table_plugin::join_fn class lazy_table_plugin::join_fn : public convenient_table_join_fn { public: join_fn(table_signature const& s1, table_signature const& s2, unsigned col_cnt, unsigned const* cols1, unsigned const* cols2): convenient_table_join_fn(s1, s2, col_cnt, cols1, cols2) {} virtual table_base* operator()(const table_base& _t1, const table_base& _t2) { lazy_table const& t1 = get(_t1); lazy_table const& t2 = get(_t2); lazy_table_ref* tr = alloc(lazy_table_join, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr(), t1, t2, get_result_signature()); return alloc(lazy_table, tr); } }; table_join_fn * lazy_table_plugin::mk_join_fn( const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (check_kind(t1) && check_kind(t2)) { return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); } else { return 0; } } // ------------------------ // lazy_table_plugin::union class lazy_table_plugin::union_fn : public table_union_fn { public: void operator()(table_base & _tgt, const table_base & _src, table_base * _delta) { lazy_table& tgt = get(_tgt); lazy_table const& src = get(_src); lazy_table* delta = get(_delta); table_base const* t_src = src.eval(); table_base * t_tgt = tgt.eval(); table_base * t_delta = delta?delta->eval():0; verbose_action _t("union"); table_union_fn* m = tgt.get_lplugin().get_manager().mk_union_fn(*t_tgt, *t_src, t_delta); SASSERT(m); (*m)(*t_tgt, *t_src, t_delta); dealloc(m); } }; table_union_fn* lazy_table_plugin::mk_union_fn( const table_base & tgt, const table_base & src, const table_base * delta) { if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { return alloc(union_fn); } else { return 0; } } // -------------------------- // lazy_table_plugin::project class lazy_table_plugin::project_fn : public convenient_table_project_fn { public: project_fn(table_signature const& orig_sig, unsigned cnt, unsigned const* cols): convenient_table_project_fn(orig_sig, cnt, cols) {} virtual table_base* operator()(table_base const& _t) { lazy_table const& t = get(_t); return alloc(lazy_table, alloc(lazy_table_project, m_removed_cols.size(), m_removed_cols.c_ptr(), t, get_result_signature())); } }; table_transformer_fn * lazy_table_plugin::mk_project_fn( const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { if (check_kind(t)) { return alloc(project_fn, t.get_signature(), col_cnt, removed_cols); } else { return 0; } } // ------------------------- // lazy_table_plugin::rename class lazy_table_plugin::rename_fn : public convenient_table_rename_fn { public: rename_fn(table_signature const& orig_sig, unsigned cnt, unsigned const* cols): convenient_table_rename_fn(orig_sig, cnt, cols) {} virtual table_base* operator()(table_base const& _t) { lazy_table const& t = get(_t); return alloc(lazy_table, alloc(lazy_table_rename, m_cycle.size(), m_cycle.c_ptr(), t, get_result_signature())); } }; table_transformer_fn * lazy_table_plugin::mk_rename_fn( const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { if (check_kind(t)) { return alloc(rename_fn, t.get_signature(), col_cnt, removed_cols); } else { return 0; } } // ----------------------------------- // lazy_table_plugin::filter_identical class lazy_table_plugin::filter_identical_fn : public table_mutator_fn { unsigned_vector m_cols; public: filter_identical_fn(unsigned cnt, unsigned const* cols): m_cols(cnt, cols) {} virtual void operator()(table_base& _t) { lazy_table& t = get(_t); t.set(alloc(lazy_table_filter_identical, m_cols.size(), m_cols.c_ptr(), t)); } }; table_mutator_fn * lazy_table_plugin::mk_filter_identical_fn( const table_base & t, unsigned col_cnt, const unsigned * identical_cols) { if (check_kind(t)) { return alloc(filter_identical_fn, col_cnt, identical_cols); } else { return 0; } } // ------------------------------------- // lazy_table_plugin::filter_interpreted class lazy_table_plugin::filter_interpreted_fn : public table_mutator_fn { app_ref m_condition; public: filter_interpreted_fn(app_ref& p): m_condition(p) {} virtual void operator()(table_base& _t) { lazy_table& t = get(_t); t.set(alloc(lazy_table_filter_interpreted, t, m_condition)); } }; table_mutator_fn * lazy_table_plugin::mk_filter_interpreted_fn( const table_base & t, app* condition) { if (check_kind(t)) { app_ref cond(condition, get_ast_manager()); return alloc(filter_interpreted_fn, cond); } else { return 0; } } // ------------------------------------- // lazy_table_plugin::filter_by_negation class lazy_table_plugin::filter_by_negation_fn : public table_intersection_filter_fn { unsigned_vector m_cols1; unsigned_vector m_cols2; public: filter_by_negation_fn(unsigned cnt, unsigned const* cols1, unsigned const* cols2): m_cols1(cnt, cols1), m_cols2(cnt, cols2) {} virtual void operator()(table_base & _t, const table_base & _intersected_obj) { lazy_table& t = get(_t); lazy_table const& it = get(_intersected_obj); t.set(alloc(lazy_table_filter_by_negation, t, it, m_cols1, m_cols2)); } }; table_intersection_filter_fn * lazy_table_plugin::mk_filter_by_negation_fn( const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { if (check_kind(t) && check_kind(negated_obj)) { return alloc(filter_by_negation_fn, joined_col_cnt, t_cols, negated_cols); } else { return 0; } } // ------------------------------- // lazy_table_plugin::filter_equal class lazy_table_plugin::filter_equal_fn : public table_mutator_fn { table_element m_value; unsigned m_col; public: filter_equal_fn(const table_element & value, unsigned col): m_value(value), m_col(col) { } virtual void operator()(table_base& _t) { lazy_table& t = get(_t); t.set(alloc(lazy_table_filter_equal, m_col, m_value, t)); } }; table_mutator_fn * lazy_table_plugin::mk_filter_equal_fn( const table_base & t, const table_element & value, unsigned col) { if (check_kind(t)) { return alloc(filter_equal_fn, value, col); } else { return 0; } } table_plugin* lazy_table_plugin::mk_sparse(relation_manager& rm) { table_plugin* sp = rm.get_table_plugin(symbol("sparse")); SASSERT(sp); if (sp) { return alloc(lazy_table_plugin, *sp); } else { return 0; } } // ---------- // lazy_table table_base * lazy_table::clone() const { table_base* t = eval(); verbose_action _t("clone"); return alloc(lazy_table, alloc(lazy_table_base, get_lplugin(), t->clone())); } table_base * lazy_table::complement(func_decl* p, const table_element * func_columns) const { table_base* t = eval()->complement(p, func_columns); return alloc(lazy_table, alloc(lazy_table_base, get_lplugin(), t)); } bool lazy_table::empty() const { return m_ref->eval()->empty(); } bool lazy_table::contains_fact(const table_fact & f) const { return m_ref->eval()->contains_fact(f); } void lazy_table::remove_fact(table_element const* fact) { m_ref->eval()->remove_fact(fact); } void lazy_table::remove_facts(unsigned fact_cnt, const table_fact * facts) { m_ref->eval()->remove_facts(fact_cnt, facts); } void lazy_table::remove_facts(unsigned fact_cnt, const table_element * facts) { m_ref->eval()->remove_facts(fact_cnt, facts); } void lazy_table::reset() { m_ref = alloc(lazy_table_base, get_lplugin(), get_lplugin().m_plugin.mk_empty(get_signature())); } void lazy_table::add_fact(table_fact const& f) { m_ref->eval()->add_fact(f); } table_base::iterator lazy_table::begin() const { return eval()->begin(); } table_base::iterator lazy_table::end() const { return eval()->end(); } table_base* lazy_table::eval() const { return m_ref->eval(); } // ------------------------- // eval table_base* lazy_table_join::force() { SASSERT(!m_table); table_base* t1 = m_t1->eval(); table_base* t2 = m_t2->eval(); verbose_action _t("join"); table_join_fn* join = rm().mk_join_fn(*t1, *t2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); m_table = (*join)(*t1, *t2); dealloc(join); return m_table.get(); } table_base* lazy_table_project::force() { SASSERT(!m_table); switch(m_src->kind()) { case LAZY_TABLE_JOIN: { lazy_table_join& src = dynamic_cast(*m_src); table_base* t1 = src.t1()->eval(); table_base* t2 = src.t2()->eval(); table_join_fn* j_fn = rm().mk_join_project_fn(*t1, *t2, src.cols1(), src.cols2(), m_cols); if (j_fn) { verbose_action _t("join_project"); m_table = (*j_fn)(*t1, *t2); dealloc(j_fn); } break; } case LAZY_TABLE_FILTER_INTERPRETED: { lazy_table_filter_interpreted& src = dynamic_cast(*m_src); table_transformer_fn* tr = rm().mk_filter_interpreted_and_project_fn(*src.eval(), src.condition(), m_cols.size(), m_cols.c_ptr()); if (tr) { verbose_action _t("filter_interpreted_project"); m_table = (*tr)(*src.eval()); dealloc(tr); } break; } case LAZY_TABLE_FILTER_EQUAL: { lazy_table_filter_equal& src = dynamic_cast(*m_src); table_base* t = src.eval(); table_transformer_fn* tr = rm().mk_select_equal_and_project_fn(*t, src.value(), src.col()); if (tr) { verbose_action _t("select_equal_project"); m_table = (*tr)(*t); dealloc(tr); } break; } default: break; } if (m_table) { return m_table.get(); } table_base* src = m_src->eval(); verbose_action _t("project"); table_transformer_fn* project = rm().mk_project_fn(*src, m_cols.size(), m_cols.c_ptr()); SASSERT(project); m_table = (*project)(*src); dealloc(project); return m_table.get(); } table_base* lazy_table_rename::force() { SASSERT(!m_table); table_base* src = m_src->eval(); verbose_action _t("rename"); table_transformer_fn* rename = rm().mk_rename_fn(*src, m_cols.size(), m_cols.c_ptr()); m_table = (*rename)(*src); dealloc(rename); return m_table.get(); } table_base* lazy_table_filter_identical::force() { SASSERT(!m_table); m_table = m_src->eval(); m_src->release_table(); m_src = 0; verbose_action _t("filter_identical"); table_mutator_fn* m = rm().mk_filter_identical_fn(*m_table, m_cols.size(), m_cols.c_ptr()); SASSERT(m); (*m)(*m_table); dealloc(m); return m_table.get(); } table_base* lazy_table_filter_equal::force() { SASSERT(!m_table); m_table = m_src->eval(); m_src->release_table(); m_src = 0; verbose_action _t("filter_equal"); table_mutator_fn* m = rm().mk_filter_equal_fn(*m_table, m_value, m_col); SASSERT(m); (*m)(*m_table); dealloc(m); return m_table.get(); } table_base* lazy_table_filter_interpreted::force() { SASSERT(!m_table); m_table = m_src->eval(); m_src->release_table(); m_src = 0; verbose_action _t("filter_interpreted"); table_mutator_fn* m = rm().mk_filter_interpreted_fn(*m_table, m_condition); SASSERT(m); (*m)(*m_table); dealloc(m); return m_table.get(); } table_base* lazy_table_filter_by_negation::force() { SASSERT(!m_table); m_table = m_tgt->eval(); m_tgt->release_table(); m_tgt = 0; switch(m_src->kind()) { case LAZY_TABLE_JOIN: { lazy_table_join& src = dynamic_cast(*m_src); table_base* t1 = src.t1()->eval(); table_base* t2 = src.t2()->eval(); verbose_action _t("filter_by_negation_join"); table_intersection_join_filter_fn* jn = rm().mk_filter_by_negated_join_fn(*m_table, *t1, *t2, cols1(), cols2(), src.cols1(), src.cols2()); if (jn) { (*jn)(*m_table, *t1, *t2); dealloc(jn); return m_table.get(); } break; } default: break; } table_base* src = m_src->eval(); verbose_action _t("filter_by_negation"); table_intersection_filter_fn* m = rm().mk_filter_by_negation_fn(*m_table, *src, m_cols1, m_cols2); SASSERT(m); (*m)(*m_table, *src); dealloc(m); return m_table.get(); } } z3-4.4.0/src/muz/rel/dl_table.h0000644000175000017500000002051112540347414015747 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_table.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-01. Revision History: --*/ #ifndef _DL_TABLE_H_ #define _DL_TABLE_H_ #include #include #include #include "ast.h" #include "bit_vector.h" #include "buffer.h" #include "hashtable.h" #include "map.h" #include "ref_vector.h" #include "vector.h" #include "union_find.h" #include "dl_base.h" #include "dl_util.h" #include "bit_vector.h" namespace datalog { class context; class variable_intersection; // ----------------------------------- // // hashtable_table // // ----------------------------------- class hashtable_table; class hashtable_table_plugin : public table_plugin { friend class hashtable_table; protected: class join_fn; public: typedef hashtable_table table; hashtable_table_plugin(relation_manager & manager) : table_plugin(symbol("hashtable"), manager) {} virtual table_base * mk_empty(const table_signature & s); virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); }; class hashtable_table : public table_base { friend class hashtable_table_plugin; friend class hashtable_table_plugin::join_fn; class our_iterator_core; typedef hashtable, vector_eq_proc > storage; storage m_data; hashtable_table(hashtable_table_plugin & plugin, const table_signature & sig) : table_base(plugin, sig) {} public: hashtable_table_plugin & get_plugin() const { return static_cast(table_base::get_plugin()); } virtual void add_fact(const table_fact & f) { m_data.insert(f); } virtual void remove_fact(const table_element* fact) { table_fact f(get_signature().size(), fact); m_data.remove(f); } virtual bool contains_fact(const table_fact & f) const { return m_data.contains(f); } virtual iterator begin() const; virtual iterator end() const; virtual unsigned get_size_estimate_rows() const { return m_data.size(); } virtual unsigned get_size_estimate_bytes() const { return m_data.size()*get_signature().size()*8; } virtual bool knows_exact_size() const { return true; } }; // ----------------------------------- // // bitvector_table // // ----------------------------------- class bitvector_table; class bitvector_table_plugin : public table_plugin { public: typedef bitvector_table table; bitvector_table_plugin(relation_manager & manager) : table_plugin(symbol("bitvector"), manager) {} virtual bool can_handle_signature(const table_signature & s); virtual table_base * mk_empty(const table_signature & s); }; class bitvector_table : public table_base { friend class bitvector_table_plugin; class bv_iterator; bit_vector m_bv; unsigned m_num_cols; unsigned_vector m_shift; unsigned_vector m_mask; unsigned fact2offset(const table_element* f) const; void offset2fact(unsigned offset, table_fact& f) const; bitvector_table(bitvector_table_plugin & plugin, const table_signature & sig); public: virtual void add_fact(const table_fact & f); virtual void remove_fact(const table_element* fact); virtual bool contains_fact(const table_fact & f) const; virtual iterator begin() const; virtual iterator end() const; }; // ------------------------------------------- // Equivalence table. // Really: partial equivalence relation table. // ------------------------------------------- class equivalence_table; class equivalence_table_plugin : public table_plugin { class union_fn; class select_equal_and_project_fn; class join_project_fn; bool is_equivalence_table(table_base const& tbl) const; public: typedef equivalence_table table; equivalence_table_plugin(relation_manager & manager) : table_plugin(symbol("equivalence"), manager) {} virtual bool can_handle_signature(const table_signature & s); virtual table_base * mk_empty(const table_signature & s); protected: virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta); virtual table_transformer_fn * mk_select_equal_and_project_fn( const table_base & t, const table_element & value, unsigned col); virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols); #if 0 virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols); virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); const table_element & value, unsigned col); virtual table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols); #endif }; class equivalence_table : public table_base { friend class equivalence_table_plugin; class eq_iterator; union_find_default_ctx m_ctx; bit_vector m_valid; union_find<> m_uf; table_base* m_sparse; equivalence_table(equivalence_table_plugin & plugin, const table_signature & sig); virtual ~equivalence_table(); unsigned first(table_fact const& f) const { return static_cast(f[0]); } unsigned second(table_fact const& f) const { return static_cast(f[1]); } bool is_valid(unsigned entry) const { return entry < m_valid.size() && m_valid.get(entry); } bool is_sparse() const { return m_sparse != 0; } // iterator over equivalence class of 'n'. class class_iterator { equivalence_table const& m_parent; unsigned m_current; unsigned m_last; bool m_end; public: class_iterator(equivalence_table const& s, unsigned n, bool end): m_parent(s), m_current(n), m_last(n), m_end(end) {} unsigned operator*() { return m_current; } class_iterator& operator++() { m_current = m_parent.m_uf.next(m_current); m_end = (m_current == m_last); return *this; } bool operator==(const class_iterator & it) const { return (m_end && it.m_end) || (!m_end && !it.m_end && m_current == it.m_current); } bool operator!=(const class_iterator & it) const { return !operator==(it); } }; class_iterator class_begin(table_element const& e) const; class_iterator class_end(table_element const& e) const; void add_fact_sparse(table_fact const& f); void mk_sparse(); public: virtual void add_fact(const table_fact & f); virtual void remove_fact(const table_element* fact); virtual bool contains_fact(const table_fact & f) const; virtual table_base* clone() const; virtual iterator begin() const; virtual iterator end() const; virtual unsigned get_size_estimate_rows() const; virtual unsigned get_size_estimate_bytes() const; virtual bool knows_exact_size() const; virtual void display(std::ostream & out) const; }; }; #endif /* _DL_TABLE_H_ */ z3-4.4.0/src/muz/rel/karr_relation.h0000644000175000017500000000516212540347414017042 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: karr_relation.h Abstract: Extract integer linear invariants. Author: Nikolaj Bjorner (nbjorner) 2013-03-08 Revision History: --*/ #ifndef _KARR_RELATION_H_ #define _KARR_RELATION_H_ #include"dl_mk_karr_invariants.h" #include"dl_relation_manager.h" namespace datalog { class karr_relation; class karr_relation_plugin : public relation_plugin { arith_util a; hilbert_basis m_hb; class join_fn; class project_fn; class rename_fn; class union_fn; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; friend class karr_relation; public: karr_relation_plugin(relation_manager& rm): relation_plugin(karr_relation_plugin::get_name(), rm), a(get_ast_manager()) {} virtual bool can_handle_signature(const relation_signature & sig) { return get_manager().get_context().karr(); } static symbol get_name() { return symbol("karr_relation"); } virtual void set_cancel(bool f); virtual relation_base * mk_empty(const relation_signature & s); virtual relation_base * mk_full(func_decl* p, const relation_signature & s); static karr_relation& get(relation_base& r); static karr_relation const & get(relation_base const& r); virtual relation_join_fn * mk_join_fn( const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols); virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols); virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col); virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); private: bool dualizeI(matrix& dst, matrix const& src); void dualizeH(matrix& dst, matrix const& src); }; }; #endif /* _DL_MK_KARR_INVARIANTS_H_ */ z3-4.4.0/src/muz/rel/dl_sieve_relation.h0000644000175000017500000001725712540347414017705 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_sieve_relation.h Abstract: Author: Krystof Hoder (t-khoder) 2010-11-11. Revision History: --*/ #ifndef _DL_SIEVE_RELATION_H_ #define _DL_SIEVE_RELATION_H_ #include "dl_context.h" #include "dl_relation_manager.h" namespace datalog { class sieve_relation; class sieve_relation_plugin : public relation_plugin { friend class sieve_relation; public: struct rel_spec { svector m_inner_cols; family_id m_inner_kind; /** Create uninitialized rel_spec. */ rel_spec() {} /** \c inner_kind==null_family_id means we will not specify a relation kind when requesting the relation object from the relation_manager. \c inner_kind==null_family_id cannot hold in a specification of existing relation object. */ rel_spec(unsigned sig_sz, const bool * inner_cols, family_id inner_kind=null_family_id) : m_inner_cols(sig_sz, inner_cols), m_inner_kind(inner_kind) {} bool operator==(const rel_spec & o) const { return m_inner_kind==o.m_inner_kind && vectors_equal(m_inner_cols, o.m_inner_cols); } struct hash { unsigned operator()(const rel_spec & s) const { return svector_hash()(s.m_inner_cols)^s.m_inner_kind; } }; }; private: class join_fn; class transformer_fn; class union_fn; class filter_fn; class negation_filter_fn; rel_spec_store > m_spec_store; family_id get_relation_kind(sieve_relation & r, const bool * inner_columns); void extract_inner_columns(const relation_signature & s, relation_plugin & inner, svector & inner_columns); void extract_inner_signature(const relation_signature & s, relation_signature & inner_sig); void collect_inner_signature(const relation_signature & s, const svector & inner_columns, relation_signature & inner_sig); public: static symbol get_name() { return symbol("sieve_relation"); } static sieve_relation_plugin& get_plugin(relation_manager & rmgr); static sieve_relation& get(relation_base& r); static sieve_relation const & get(relation_base const& r); static sieve_relation* get(relation_base* r); static sieve_relation const* get(relation_base const* r); sieve_relation_plugin(relation_manager & manager); virtual void initialize(family_id fid); family_id get_relation_kind(const relation_signature & sig, const bool * inner_columns, family_id inner_kind); family_id get_relation_kind(const relation_signature & sig, const svector & inner_columns, family_id inner_kind) { SASSERT(sig.size()==inner_columns.size()); return get_relation_kind(sig, inner_columns.c_ptr(), inner_kind); } virtual bool can_handle_signature(const relation_signature & s); virtual relation_base * mk_empty(const relation_signature & s); sieve_relation * mk_empty(const sieve_relation & original); virtual relation_base * mk_empty(const relation_base & original); virtual relation_base * mk_empty(const relation_signature & s, family_id kind); sieve_relation * mk_empty(const relation_signature & s, relation_plugin & inner_plugin); virtual relation_base * mk_full(func_decl* p, const relation_signature & s); sieve_relation * mk_full(func_decl* p, const relation_signature & s, relation_plugin & inner_plugin); sieve_relation * mk_from_inner(const relation_signature & s, const bool * inner_columns, relation_base * inner_rel); sieve_relation * mk_from_inner(const relation_signature & s, const svector inner_columns, relation_base * inner_rel) { SASSERT(inner_columns.size()==s.size()); return mk_from_inner(s, inner_columns.c_ptr(), inner_rel); } protected: virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols); virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols); virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col); virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, const relation_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols); }; // ----------------------------------- // // sieve_relation // // ----------------------------------- class sieve_relation : public relation_base { friend class sieve_relation_plugin; friend class sieve_relation_plugin::join_fn; friend class sieve_relation_plugin::transformer_fn; friend class sieve_relation_plugin::union_fn; friend class sieve_relation_plugin::filter_fn; svector m_inner_cols; unsigned_vector m_sig2inner; unsigned_vector m_inner2sig; unsigned_vector m_ignored_cols; //in ascending order, so that it can be used in project-like functions scoped_rel m_inner; sieve_relation(sieve_relation_plugin & p, const relation_signature & s, const bool * inner_columns, relation_base * inner); public: sieve_relation_plugin & get_plugin() const { return static_cast(relation_base::get_plugin()); } bool is_inner_col(unsigned idx) const { return m_sig2inner[idx]!=UINT_MAX; } unsigned get_inner_col(unsigned idx) const { SASSERT(is_inner_col(idx)); return m_sig2inner[idx]; } bool no_sieved_columns() const { return m_ignored_cols.size()==0; } bool no_inner_columns() const { return m_ignored_cols.size()==get_signature().size(); } relation_base & get_inner() { return *m_inner; } const relation_base & get_inner() const { return *m_inner; } virtual void add_fact(const relation_fact & f); virtual bool contains_fact(const relation_fact & f) const; virtual sieve_relation * clone() const; virtual relation_base * complement(func_decl*p) const; virtual void to_formula(expr_ref& fml) const; virtual bool empty() const { return get_inner().empty(); } virtual void reset() { get_inner().reset(); } virtual unsigned get_size_estimate_rows() const { return get_inner().get_size_estimate_rows(); } virtual unsigned get_size_estimate_bytes() const { return get_inner().get_size_estimate_bytes(); } virtual void display(std::ostream & out) const; }; }; #endif /* _DL_SIEVE_RELATION_H_ */ z3-4.4.0/src/muz/rel/dl_mk_simple_joins.cpp0000644000175000017500000007274112540347414020411 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_simple_joins.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-05-20. Revision History: --*/ #include #include #include #include"dl_mk_simple_joins.h" #include"dl_relation_manager.h" #include"ast_pp.h" #include"trace.h" namespace datalog { mk_simple_joins::mk_simple_joins(context & ctx): plugin(1000), m_context(ctx), rm(ctx.get_rule_manager()) { } class join_planner { typedef float cost; class pair_info { cost m_total_cost; /** \brief Number of rules longer than two that contain this pair. This number is being updated by \c add_rule and \remove rule. Even though between adding a rule and removing it, the length of a rule can decrease without this pair being notified about it, it will surely see the decrease from length 3 to 2 which the threshold for rule being counted in this counter. */ unsigned m_consumers; bool m_stratified; unsigned m_src_stratum; public: var_idx_set m_all_nonlocal_vars; rule_vector m_rules; pair_info() : m_consumers(0), m_stratified(true), m_src_stratum(0) {} bool can_be_joined() const { return m_consumers > 0; } cost get_cost() const { SASSERT(m_consumers > 0); cost amortized = m_total_cost/m_consumers; if (m_stratified) { return amortized * ( (amortized>0) ? (1/16.0f) : 16.0f); } else { return amortized; } } /** \brief Add rule \c r among rules interested in current predicate pair. The \c pl.m_rule_content entry of the rule has to be properly filled in by the time of a call to this function */ void add_rule(join_planner & pl, app * t1, app * t2, rule * r, const var_idx_set & non_local_vars_normalized, const var_idx_set & non_local_vars) { if (m_rules.empty()) { m_total_cost = pl.compute_cost(t1, t2, non_local_vars); m_src_stratum = std::max(pl.get_stratum(t1->get_decl()), pl.get_stratum(t2->get_decl())); } m_rules.push_back(r); if (pl.m_rules_content.find(r).size()>2) { m_consumers++; } if (m_stratified) { unsigned head_stratum = pl.get_stratum(r->get_decl()); SASSERT(head_stratum>=m_src_stratum); if (head_stratum==m_src_stratum) { m_stratified = false; } } idx_set_union(m_all_nonlocal_vars, non_local_vars_normalized); } /** \brief Remove rule from the pair record. Return true if no rules remain in the pair, and so it should be removed. */ bool remove_rule(rule * r, unsigned original_length) { TRUSTME( remove_from_vector(m_rules, r) ); if (original_length>2) { SASSERT(m_consumers>0); m_consumers--; } SASSERT(!m_rules.empty() || m_consumers==0); return m_rules.empty(); } private: pair_info & operator=(const pair_info &); //to avoid the implicit one }; typedef std::pair app_pair; typedef map, obj_ptr_hash >, default_eq > cost_map; typedef map, ptr_hash, ptr_eq > rule_pred_map; context & m_context; ast_manager & m; rule_manager & rm; var_subst & m_var_subst; rule_set & m_rs_aux_copy; //reference to a rule_set that will allow to ask for stratum levels cost_map m_costs; ptr_vector m_interpreted; rule_pred_map m_rules_content; rule_ref_vector m_introduced_rules; ptr_hashtable, ptr_eq > m_modified_rules; ast_ref_vector m_pinned; mutable ptr_vector m_vars; public: join_planner(context & ctx, rule_set & rs_aux_copy) : m_context(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_var_subst(ctx.get_var_subst()), m_rs_aux_copy(rs_aux_copy), m_introduced_rules(ctx.get_rule_manager()), m_pinned(ctx.get_manager()) { } ~join_planner() { cost_map::iterator it = m_costs.begin(); cost_map::iterator end = m_costs.end(); for (; it != end; ++it) { dealloc(it->m_value); } m_costs.reset(); } private: void get_normalizer(app * t, unsigned & next_var, expr_ref_vector & result) const { SASSERT(result.size()>0); unsigned res_ofs = result.size()-1; unsigned n=t->get_num_args(); for(unsigned i=0; iget_arg(i))); var * v = to_var(t->get_arg(i)); unsigned var_idx = v->get_idx(); if (result[res_ofs-var_idx]==0) { result[res_ofs-var_idx]=m.mk_var(next_var, v->get_sort()); next_var++; } } } void get_normalizer(app * t1, app * t2, expr_ref_vector & result) const { SASSERT(result.empty()); if (t1->get_num_args()==0 && t2->get_num_args()==0) { return; //nothing to normalize } SASSERT(!t1->is_ground() || !t2->is_ground()); unsigned max_var_idx = 0; { var_idx_set& orig_var_set = rm.collect_vars(t1, t2); var_idx_set::iterator ovit = orig_var_set.begin(); var_idx_set::iterator ovend = orig_var_set.end(); for(; ovit!=ovend; ++ovit) { unsigned var_idx = *ovit; if (var_idx>max_var_idx) { max_var_idx = var_idx; } } } if (t1->get_decl()!=t2->get_decl()) { if (t1->get_decl()->get_id()get_decl()->get_id()) { std::swap(t1, t2); } } else { int_vector norm1(max_var_idx+1, -1); int_vector norm2(max_var_idx+1, -1); unsigned n=t1->get_num_args(); SASSERT(n==t2->get_num_args()); for(unsigned i=0; iget_arg(i)); var * v2 = to_var(t2->get_arg(i)); if (v1->get_sort()!=v2->get_sort()) { //different sorts mean we can distinguish the two terms if (v1->get_sort()->get_id()get_sort()->get_id()) { std::swap(t1, t2); } break; } unsigned v1_idx = v1->get_idx(); unsigned v2_idx = v2->get_idx(); //since the rules already went through the mk_filter_rules transformer, //variables must be linear SASSERT(norm1[v1_idx]==-1); SASSERT(norm2[v2_idx]==-1); if (norm2[v1_idx]!=norm1[v2_idx]) { //now we can distinguish the two terms if (norm2[v1_idx](0)); unsigned next_var = 0; get_normalizer(t1, next_var, result); get_normalizer(t2, next_var, result); } app_pair get_key(app * t1, app * t2) { expr_ref_vector norm_subst(m); get_normalizer(t1, t2, norm_subst); expr_ref t1n_ref(m); expr_ref t2n_ref(m); m_var_subst(t1, norm_subst.size(), norm_subst.c_ptr(), t1n_ref); m_var_subst(t2, norm_subst.size(), norm_subst.c_ptr(), t2n_ref); app * t1n = to_app(t1n_ref); app * t2n = to_app(t2n_ref); if (t1n>t2n) { std::swap(t1n, t2n); } m_pinned.push_back(t1n); m_pinned.push_back(t2n); /* IF_VERBOSE(0, print_renaming(norm_subst, verbose_stream()); display_predicate(m_context, t1, verbose_stream()); display_predicate(m_context, t2, verbose_stream()); display_predicate(m_context, t1n, verbose_stream()); display_predicate(m_context, t2n, verbose_stream());); */ return app_pair(t1n, t2n); } /** \brief Add rule \c r among rules interested in predicate pair \c t1, \c t2. The \c m_rule_content entry of the rule \c r has to be properly filled in by the time of a call to this function */ void register_pair(app * t1, app * t2, rule * r, const var_idx_set & non_local_vars) { SASSERT(t1!=t2); cost_map::entry * e = m_costs.insert_if_not_there2(get_key(t1, t2), 0); pair_info * & ptr_inf = e->get_data().m_value; if (ptr_inf==0) { ptr_inf = alloc(pair_info); } pair_info & inf = *ptr_inf; expr_ref_vector normalizer(m); get_normalizer(t1, t2, normalizer); unsigned norm_ofs = normalizer.size()-1; var_idx_set normalized_vars; var_idx_set::iterator vit = non_local_vars.begin(); var_idx_set::iterator vend = non_local_vars.end(); for(; vit!=vend; ++vit) { unsigned norm_var = to_var(normalizer.get(norm_ofs-*vit))->get_idx(); normalized_vars.insert(norm_var); } inf.add_rule(*this, t1, t2, r, normalized_vars, non_local_vars); TRACE("dl", tout << mk_pp(t1, m) << " " << mk_pp(t2, m) << " "; vit = non_local_vars.begin(); for (; vit != vend; ++vit) tout << *vit << " "; tout << "\n"; r->display(m_context, tout); if (inf.can_be_joined()) tout << "cost: " << inf.get_cost() << "\n";); } pair_info & get_pair(app_pair key) const { return *m_costs.find(key); } void remove_rule_from_pair(app_pair key, rule * r, unsigned original_len) { pair_info * ptr = &get_pair(key); if (ptr->remove_rule(r, original_len)) { SASSERT(ptr->m_rules.empty()); m_costs.remove(key); dealloc(ptr); } } void register_rule(rule * r) { rule_counter counter; counter.count_rule_vars(r, 1); ptr_vector & rule_content = m_rules_content.insert_if_not_there2(r, ptr_vector())->get_data().m_value; SASSERT(rule_content.empty()); unsigned pos_tail_size=r->get_positive_tail_size(); for(unsigned i=0; iget_tail(i)); } for(unsigned i=0; i+1 < pos_tail_size; i++) { app * t1 = r->get_tail(i); var_idx_set t1_vars = rm.collect_vars(t1); counter.count_vars(t1, -1); //temporarily remove t1 variables from counter for(unsigned j=i+1; jget_tail(j); counter.count_vars(t2, -1); //temporarily remove t2 variables from counter var_idx_set scope_vars = rm.collect_vars(t2); scope_vars |= t1_vars; var_idx_set non_local_vars; counter.collect_positive(non_local_vars); counter.count_vars(t2, 1); //restore t2 variables in counter set_intersection(non_local_vars, scope_vars); register_pair(t1, t2, r, non_local_vars); } counter.count_vars(t1, 1); //restore t1 variables in counter } } bool extract_argument_info(unsigned var_idx, app * t, expr_ref_vector & args, ptr_vector & domain) { unsigned n=t->get_num_args(); for(unsigned i=0; iget_arg(i)); if (v->get_idx()==var_idx) { args.push_back(v); domain.push_back(m.get_sort(v)); return true; } } return false; } void join_pair(app_pair pair_key) { app * t1 = pair_key.first; app * t2 = pair_key.second; pair_info & inf = get_pair(pair_key); SASSERT(!inf.m_rules.empty()); var_idx_set & output_vars = inf.m_all_nonlocal_vars; expr_ref_vector args(m); ptr_vector domain; unsigned arity = output_vars.num_elems(); idx_set::iterator ovit=output_vars.begin(); idx_set::iterator ovend=output_vars.end(); //TODO: improve quadratic complexity for(;ovit!=ovend;++ovit) { unsigned var_idx=*ovit; bool found=extract_argument_info(var_idx, t1, args, domain); if (!found) { found=extract_argument_info(var_idx, t2, args, domain); } SASSERT(found); } SASSERT(args.size()==arity); SASSERT(domain.size()==arity); rule * one_parent = inf.m_rules.back(); func_decl* parent_head = one_parent->get_decl(); const char * one_parent_name = parent_head->get_name().bare_str(); std::string parent_name; if (inf.m_rules.size()>1) { parent_name = one_parent_name + std::string("_and_") + to_string(inf.m_rules.size()-1); } else { parent_name = one_parent_name; } func_decl * decl = m_context.mk_fresh_head_predicate( symbol(parent_name.c_str()), symbol("split"), arity, domain.c_ptr(), parent_head); app_ref head(m.mk_app(decl, arity, args.c_ptr()), m); app * tail[] = {t1, t2}; rule * new_rule = m_context.get_rule_manager().mk(head, 2, tail, 0); //TODO: update accounting so that it can handle multiple parents new_rule->set_accounting_parent_object(m_context, one_parent); m_introduced_rules.push_back(new_rule); //here we copy the inf.m_rules vector because inf.m_rules will get changed //in the iteration. Also we use hashtable instead of vector because we do //not want to process one rule twice. typedef ptr_hashtable, default_eq > rule_hashtable; rule_hashtable relevant_rules; insert_into_set(relevant_rules, inf.m_rules); rule_hashtable::iterator rit = relevant_rules.begin(); rule_hashtable::iterator rend = relevant_rules.end(); for(; rit!=rend; ++rit) { apply_binary_rule(*rit, pair_key, head); } // SASSERT(!m_costs.contains(pair_key)); } void replace_edges(rule * r, const ptr_vector & removed_tails, const ptr_vector & added_tails0, const ptr_vector & rule_content) { SASSERT(removed_tails.size()>=added_tails0.size()); unsigned len = rule_content.size(); unsigned original_len = len+removed_tails.size()-added_tails0.size(); ptr_vector added_tails(added_tails0); //we need a copy since we'll be modifying it unsigned rt_sz = removed_tails.size(); //remove edges between removed tails for(unsigned i=0; iget_head(); var_counter counter; counter.count_vars(head, 1); unsigned tail_size=r->get_tail_size(); unsigned pos_tail_size=r->get_positive_tail_size(); for(unsigned i=pos_tail_size; iget_tail(i), 1); } for(unsigned i=0; i & rule_content = m_rules_content.find(r); unsigned len = rule_content.size(); if (len==1) { return; } func_decl * t1_pred = t1->get_decl(); func_decl * t2_pred = t2->get_decl(); ptr_vector removed_tails; ptr_vector added_tails; for(unsigned i1=0; i1get_decl()!=t1_pred) { continue; } unsigned i2start = (t1_pred==t2_pred) ? (i1+1) : 0; for(unsigned i2=i2start; i2get_decl()!=t2_pred) { continue; } if (get_key(rt1, rt2)!=pair_key) { continue; } expr_ref_vector normalizer(m); get_normalizer(rt1, rt2, normalizer); expr_ref_vector denormalizer(m); reverse_renaming(m, normalizer, denormalizer); expr_ref new_transf(m); m_var_subst(t_new, denormalizer.size(), denormalizer.c_ptr(), new_transf); app * new_lit = to_app(new_transf); m_pinned.push_back(new_lit); rule_content[i1]=new_lit; rule_content[i2]=rule_content.back(); rule_content.pop_back(); len--; //here the bound of both loops changes!!! removed_tails.push_back(rt1); removed_tails.push_back(rt2); added_tails.push_back(new_lit); //this exits the inner loop, the outer one continues in case there will //be other matches break; } } SASSERT(!removed_tails.empty()); SASSERT(!added_tails.empty()); m_modified_rules.insert(r); replace_edges(r, removed_tails, added_tails, rule_content); } cost get_domain_size(func_decl * pred, unsigned arg_index) const { relation_sort sort = pred->get_domain(arg_index); return static_cast(m_context.get_sort_size_estimate(sort)); //unsigned sz; //if (!m_context.get_sort_size(sort, sz)) { // sz=UINT_MAX; //} //return static_cast(sz); } unsigned get_stratum(func_decl * pred) const { return m_rs_aux_copy.get_predicate_strat(pred); } cost estimate_size(app * t) const { func_decl * pred = t->get_decl(); unsigned n=pred->get_arity(); rel_context_base* rel = m_context.get_rel_context(); if (!rel) { return cost(1); } relation_manager& rm = rel->get_rmanager(); if ( (m_context.saturation_was_run() && rm.try_get_relation(pred)) || rm.is_saturated(pred)) { SASSERT(rm.try_get_relation(pred)); //if it is saturated, it should exist unsigned rel_size_int = rel->get_relation(pred).get_size_estimate_rows(); if (rel_size_int!=0) { cost rel_size = static_cast(rel_size_int); cost curr_size = rel_size; for(unsigned i=0; iget_arg(i))) { curr_size /= get_domain_size(pred, i); } } return curr_size; } } cost res = 1; for(unsigned i=0; iget_arg(i))) { res *= get_domain_size(pred, i); } } return res; } cost compute_cost(app * t1, app * t2, const var_idx_set & non_local_vars) const { func_decl * t1_pred = t1->get_decl(); func_decl * t2_pred = t2->get_decl(); cost inters_size = 1; variable_intersection vi(m_context.get_manager()); vi.populate(t1, t2); unsigned n = vi.size(); // remove contributions from joined columns. for(unsigned i=0; iget_arg(arg_index1))); if (non_local_vars.contains(to_var(t1->get_arg(arg_index1))->get_idx())) { inters_size *= get_domain_size(t1_pred, arg_index1); } //joined arguments must have the same domain SASSERT(get_domain_size(t1_pred, arg_index1)==get_domain_size(t2_pred, arg_index2)); } // remove contributions from projected columns. for (unsigned i = 0; i < t1->get_num_args(); ++i) { if (is_var(t1->get_arg(i)) && !non_local_vars.contains(to_var(t1->get_arg(i))->get_idx())) { inters_size *= get_domain_size(t1_pred, i); } } for (unsigned i = 0; i < t2->get_num_args(); ++i) { if (is_var(t2->get_arg(i)) && !non_local_vars.contains(to_var(t2->get_arg(i))->get_idx())) { inters_size *= get_domain_size(t2_pred, i); } } cost res = estimate_size(t1)*estimate_size(t2)/ inters_size; // (inters_size*inters_size); //cost res = -inters_size; /*unsigned t1_strat = get_stratum(t1_pred); SASSERT(t1_strat<=m_head_stratum); if (t1_strat0) { res /= 2; } else { res *= 2; } } }*/ TRACE("report_costs", display_predicate(m_context, t1, tout); display_predicate(m_context, t2, tout); tout << res << "\n";); return res; } bool pick_best_pair(app_pair & p) { app_pair best; bool found = false; cost best_cost; cost_map::iterator it = m_costs.begin(); cost_map::iterator end = m_costs.end(); for(; it!=end; ++it) { app_pair key = it->m_key; pair_info & inf = *it->m_value; if (!inf.can_be_joined()) { continue; } cost c = inf.get_cost(); if (!found || cm_key; ptr_vector content = rcit->m_value; SASSERT(content.size()<=2); if (content.size()==orig_r->get_positive_tail_size()) { //rule did not change result->add_rule(orig_r); continue; } ptr_vector tail(content); svector negs(tail.size(), false); unsigned or_len = orig_r->get_tail_size(); for(unsigned i=orig_r->get_positive_tail_size(); iget_tail(i)); negs.push_back(orig_r->is_neg_tail(i)); } rule * new_rule = m_context.get_rule_manager().mk(orig_r->get_head(), tail.size(), tail.c_ptr(), negs.c_ptr()); new_rule->set_accounting_parent_object(m_context, orig_r); m_context.get_rule_manager().mk_rule_rewrite_proof(*orig_r, *new_rule); result->add_rule(new_rule); } while (!m_introduced_rules.empty()) { result->add_rule(m_introduced_rules.back()); m_context.get_rule_manager().mk_rule_asserted_proof(*m_introduced_rules.back()); m_introduced_rules.pop_back(); } result->inherit_predicates(source); return result; } }; rule_set * mk_simple_joins::operator()(rule_set const & source) { rule_set rs_aux_copy(m_context); rs_aux_copy.replace_rules(source); if (!rs_aux_copy.is_closed()) { rs_aux_copy.close(); } join_planner planner(m_context, rs_aux_copy); return planner.run(source); } }; z3-4.4.0/src/muz/rel/dl_mk_partial_equiv.cpp0000644000175000017500000001077712540347414020564 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_partial_equiv.cpp Abstract: Rule transformer which identifies predicates that are partial equivalence relations. Author: Nikolaj Bjorner (nbjorner) 2012-05-14 Revision History: --*/ #include "dl_mk_partial_equiv.h" #include "dl_relation_manager.h" #include "ast_pp.h" namespace datalog { bool mk_partial_equivalence_transformer::is_symmetry(rule const* r) { func_decl* p = r->get_decl(); return p->get_arity() == 2 && p->get_domain(0) == p->get_domain(1) && r->get_tail_size() == 1 && r->get_tail(0)->get_decl() == p && r->get_head()->get_arg(0) == r->get_tail(0)->get_arg(1) && r->get_head()->get_arg(1) == r->get_tail(0)->get_arg(0) && is_var(r->get_head()->get_arg(0)) && is_var(r->get_head()->get_arg(1)) && r->get_head()->get_arg(0) != r->get_head()->get_arg(1); } bool mk_partial_equivalence_transformer::is_transitivity(rule const* r) { func_decl* p = r->get_decl(); if (p->get_arity() != 2 || p->get_domain(0) != p->get_domain(1) || r->get_tail_size() != 2 || r->get_tail(0)->get_decl() != p || r->get_tail(1)->get_decl() != p) { return false; } app* h = r->get_head(); app* a = r->get_tail(0); app* b = r->get_tail(1); expr* x1 = h->get_arg(0); expr* x2 = h->get_arg(1); expr* a1 = a->get_arg(0); expr* a2 = a->get_arg(1); expr* b1 = b->get_arg(0); expr* b2 = b->get_arg(1); if (!(is_var(x1) && is_var(x2) && is_var(a1) && is_var(a2) && is_var(b1) && is_var(b2))) { return false; } if (x1 == x2 || a1 == a2 || b1 == b2) { return false; } if (a2 == b1) { if (x1 == b2 && x2 == a1) { return true; } if (x1 == a1 && x2 == b2) { return true; } return false; } if (a1 == b2) { if (x1 == b1 && x2 == a2) { return true; } if (x1 == a2 && x2 == b1) { return true; } return false; } return false; ; } rule_set * mk_partial_equivalence_transformer::operator()(rule_set const & source) { // TODO mc if (source.get_num_rules() == 0) { return 0; } if (m_context.get_engine() != DATALOG_ENGINE) { return 0; } relation_manager & rm = m_context.get_rel_context()->get_rmanager(); rule_set::decl2rules::iterator it = source.begin_grouped_rules(); rule_set::decl2rules::iterator end = source.end_grouped_rules(); rule_set* res = alloc(rule_set, m_context); for (; it != end; ++it) { func_decl* p = it->m_key; rule_vector const& rv = *(it->m_value); bool has_symmetry = false; bool has_transitivity = false; unsigned i_symmetry, i_transitivity; family_id kind = rm.get_requested_predicate_kind(p); for (unsigned i = 0; i < rv.size(); ++i) { if (kind != null_family_id) { res->add_rule(rv[i]); } else if (is_symmetry(rv[i])) { i_symmetry = i; has_symmetry = true; } else if (is_transitivity(rv[i])) { i_transitivity = i; has_transitivity = true; } else { res->add_rule(rv[i]); } } if (has_symmetry && !has_transitivity) { res->add_rule(rv[i_symmetry]); } else if (!has_symmetry && has_transitivity) { res->add_rule(rv[i_transitivity]); } else if (has_symmetry && has_transitivity) { TRACE("dl", tout << "updating predicate " << mk_pp(p, m) << " to partial equivalence\n";); SASSERT(kind == null_family_id); rm.set_predicate_kind(p, rm.get_table_plugin(symbol("equivalence"))->get_kind()); } } if (res->get_num_rules() == source.get_num_rules()) { dealloc(res); return 0; } res->inherit_predicates(source); return res; } }; z3-4.4.0/src/muz/rel/dl_mk_similarity_compressor.h0000644000175000017500000000324512540347414022016 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_similarity_compressor.h Abstract: Author: Krystof Hoder (t-khoder) 2010-10-22. Revision History: --*/ #ifndef _DL_MK_SIMILARITY_COMPRESSOR_H_ #define _DL_MK_SIMILARITY_COMPRESSOR_H_ #include #include"map.h" #include"obj_pair_hashtable.h" #include"dl_context.h" #include"dl_rule_set.h" #include"dl_rule_transformer.h" namespace datalog { /** \brief Functor for merging groups of similar rules. A rule sequence P("1",x):-Q(x). ... P("N",x):-Q(x). will be replaced by P(y,x):-Q(x), Aux(y). and a set of facts Aux("1"). ... Aux("N"). Similar transformation is performed when the varying constant appears in the positive tail. */ class mk_similarity_compressor : public rule_transformer::plugin { context & m_context; ast_manager & m_manager; /** number of similar rules necessary for a group to be introduced */ unsigned m_threshold_count; rule_vector m_rules; rule_ref_vector m_result_rules; bool m_modified; ast_ref_vector m_pinned; void merge_class(rule_vector::iterator first, rule_vector::iterator after_last); void process_class(rule_set const& source, rule_vector::iterator first, rule_vector::iterator after_last); void reset(); public: mk_similarity_compressor(context & ctx); rule_set * operator()(rule_set const & source); }; }; #endif /* _DL_MK_SIMILARITY_COMPRESSOR_H_ */ z3-4.4.0/src/muz/rel/dl_external_relation.h0000644000175000017500000001254512540347414020407 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_external_relation.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2010-05-10 Revision History: --*/ #ifndef _DL_EXTERNAL_RELATION_H_ #define _DL_EXTERNAL_RELATION_H_ #include "dl_base.h" namespace datalog { class external_relation; class external_relation_context { public: virtual ~external_relation_context() {} virtual family_id get_family_id() const = 0; // reduce arguments. virtual void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) = 0; // overwrite terms passed in outs vector with values computed by function. virtual void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) = 0; }; class external_relation_plugin : public relation_plugin { friend class external_relation; class join_fn; class project_fn; class rename_fn; class union_fn; class filter_identical_fn; class filter_interpreted_fn; class negation_filter_fn; external_relation_context& m_ext; public: external_relation_plugin(external_relation_context& ctx, relation_manager & m); virtual bool can_handle_signature(const relation_signature & s) { return true; } static symbol get_name() { return symbol("external_relation"); } virtual relation_base * mk_empty(const relation_signature & s); virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols); virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols); virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col); virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, const relation_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols); private: static external_relation& get(relation_base& r); static external_relation const & get(relation_base const& r); void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) { m_ext.reduce(f, num_args, args, result); } void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) { m_ext.reduce_assign(f, num_args, args, num_out, outs); } sort* get_relation_sort(relation_signature const& sig); sort* get_column_sort(unsigned col, sort* relation_sort); void mk_filter_fn(sort* s, app* condition, func_decl_ref& f); family_id get_family_id(); }; class external_relation : public relation_base { friend class external_relation_plugin; friend class external_relation_plugin::join_fn; friend class external_relation_plugin::project_fn; friend class external_relation_plugin::rename_fn; friend class external_relation_plugin::union_fn; friend class external_relation_plugin::filter_identical_fn; friend class external_relation_plugin::filter_interpreted_fn; friend class external_relation_plugin::negation_filter_fn; expr_ref m_rel; func_decl_ref m_select_fn; func_decl_ref m_store_fn; func_decl_ref m_is_empty_fn; unsigned size() const { return get_signature().size(); } sort* get_sort() const { return m_rel.get_manager().get_sort(m_rel); } void mk_accessor(decl_kind k, func_decl_ref& fn, const relation_fact& f, bool destructive, expr_ref& res) const; external_relation(external_relation_plugin & p, const relation_signature & s, expr* r); virtual ~external_relation(); public: external_relation_plugin & get_plugin() const; virtual bool empty() const; virtual void add_fact(const relation_fact & f); virtual bool contains_fact(const relation_fact & f) const; virtual external_relation * clone() const; virtual external_relation * complement(func_decl*) const; virtual void display(std::ostream & out) const; virtual void display_tuples(func_decl & pred, std::ostream & out) const; expr* get_relation() const { return m_rel.get(); } virtual void to_formula(expr_ref& fml) const { fml = get_relation(); } }; }; #endif z3-4.4.0/src/muz/rel/dl_relation_manager.h0000644000175000017500000007351712540347414020205 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_relation_manager.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-24. Revision History: --*/ #ifndef _DL_RELATION_MANAGER_H_ #define _DL_RELATION_MANAGER_H_ #include"map.h" #include"vector.h" #include"dl_base.h" namespace datalog { class context; class dl_decl_util; class table_relation; class table_relation_plugin; class finite_product_relation; class finite_product_relation_plugin; class sieve_relation; class sieve_relation_plugin; class rule_set; class relation_manager { class empty_signature_relation_join_fn; class default_relation_join_project_fn; class default_relation_select_equal_and_project_fn; class default_relation_intersection_filter_fn; class default_relation_filter_interpreted_and_project_fn; class default_relation_apply_sequential_fn; class auxiliary_table_transformer_fn; class auxiliary_table_filter_fn; class default_table_join_fn; class default_table_project_fn; class null_signature_table_project_fn; class default_table_join_project_fn; class default_table_rename_fn; class default_table_union_fn; class default_table_filter_equal_fn; class default_table_filter_identical_fn; class default_table_filter_interpreted_fn; class default_table_filter_interpreted_and_project_fn; class default_table_negation_filter_fn; class default_table_filter_not_equal_fn; class default_table_select_equal_and_project_fn; class default_table_map_fn; class default_table_project_with_reduce_fn; typedef obj_map decl2kind_map; typedef u_map kind2plugin_map; typedef map, ptr_eq > tp2trp_map; typedef map, ptr_eq > rp2fprp_map; typedef obj_map relation_map; typedef ptr_vector table_plugin_vector; typedef ptr_vector relation_plugin_vector; context & m_context; table_plugin_vector m_table_plugins; relation_plugin_vector m_relation_plugins; //table_relation_plugins corresponding to table_plugins tp2trp_map m_table_relation_plugins; rp2fprp_map m_finite_product_relation_plugins; kind2plugin_map m_kind2plugin; table_plugin * m_favourite_table_plugin; relation_plugin * m_favourite_relation_plugin; relation_map m_relations; decl_set m_saturated_rels; family_id m_next_table_fid; family_id m_next_relation_fid; /** Map specifying what kind of relation should be used to represent particular predicate. */ decl2kind_map m_pred_kinds; void register_relation_plugin_impl(relation_plugin * plugin); relation_manager(const relation_manager &); //private and undefined copy constructor relation_manager & operator=(const relation_manager &); //private and undefined operator= public: relation_manager(context & ctx) : m_context(ctx), m_favourite_table_plugin(0), m_favourite_relation_plugin(0), m_next_table_fid(0), m_next_relation_fid(0) {} virtual ~relation_manager(); void reset(); void reset_relations(); context & get_context() const { return m_context; } dl_decl_util & get_decl_util() const; family_id get_next_table_fid() { return m_next_table_fid++; } family_id get_next_relation_fid(relation_plugin & claimer); /** Set what kind of relation is going to be used to represent the predicate \c pred. This function can be called only before the relation object for \c pred is created (i.e. before the \c get_relation function is called with \c pred as argument for the first time). */ void set_predicate_kind(func_decl * pred, family_id kind); /** Return the relation kind that was requested to represent the predicate \c pred by \c set_predicate_kind. If there was no such request, return \c null_family_id. */ family_id get_requested_predicate_kind(func_decl * pred); relation_base & get_relation(func_decl * pred); relation_base * try_get_relation(func_decl * pred) const; /** \brief Store the relation \c rel under the predicate \c pred. The \c relation_manager takes over the relation object. */ void store_relation(func_decl * pred, relation_base * rel); bool is_saturated(func_decl * pred) const { return m_saturated_rels.contains(pred); } void mark_saturated(func_decl * pred) { m_saturated_rels.insert(pred); } void reset_saturated_marks() { if(!m_saturated_rels.empty()) { m_saturated_rels.reset(); } } void collect_non_empty_predicates(decl_set & res) const; void restrict_predicates(const decl_set & preds); void register_plugin(table_plugin * plugin); /** table_relation_plugins should not be passed to this function since they are created automatically when registering a table plugin. */ void register_plugin(relation_plugin * plugin) { SASSERT(!plugin->from_table()); register_relation_plugin_impl(plugin); } table_plugin & get_appropriate_plugin(const table_signature & t); relation_plugin & get_appropriate_plugin(const relation_signature & t); table_plugin * try_get_appropriate_plugin(const table_signature & t); relation_plugin * try_get_appropriate_plugin(const relation_signature & t); table_plugin * get_table_plugin(symbol const& s); relation_plugin * get_relation_plugin(symbol const& s); relation_plugin & get_relation_plugin(family_id kind); void set_favourite_plugin(relation_plugin* p) { m_favourite_relation_plugin = p; } table_relation_plugin & get_table_relation_plugin(table_plugin & tp); bool try_get_finite_product_relation_plugin(const relation_plugin & inner, finite_product_relation_plugin * & res); table_base * mk_empty_table(const table_signature & s); relation_base * mk_implicit_relation(const relation_signature & s, app * expr); relation_base * mk_empty_relation(const relation_signature & s, family_id kind); relation_base * mk_empty_relation(const relation_signature & s, func_decl* pred); relation_base * mk_full_relation(const relation_signature & s, func_decl* pred, family_id kind); relation_base * mk_full_relation(const relation_signature & s, func_decl* pred); relation_base * mk_table_relation(const relation_signature & s, table_base * table); bool mk_empty_table_relation(const relation_signature & s, relation_base * & result); bool is_non_explanation(relation_signature const& s) const; /** \brief Convert relation value to table one. This function can be called only for the relation sorts that have a table counterpart. */ void relation_to_table(const relation_sort & sort, const relation_element & from, table_element & to); void table_to_relation(const relation_sort & sort, const table_element & from, relation_element & to); void table_to_relation(const relation_sort & sort, const table_element & from, const relation_fact::el_proxy & to); void table_to_relation(const relation_sort & sort, const table_element & from, relation_element_ref & to); bool relation_sort_to_table(const relation_sort & from, table_sort & to); void from_predicate(func_decl * pred, unsigned arg_index, relation_sort & result); void from_predicate(func_decl * pred, relation_signature & result); /** \brief Convert relation signature to table signature and return true if successful. If false is returned, the value of \c to is undefined. */ bool relation_signature_to_table(const relation_signature & from, table_signature & to); void relation_fact_to_table(const relation_signature & s, const relation_fact & from, table_fact & to); void table_fact_to_relation(const relation_signature & s, const table_fact & from, relation_fact & to); void set_cancel(bool f); // ----------------------------------- // // relation operations // // ----------------------------------- //TODO: If multiple operation implementations are available, we may want to do something to //select the best one here. /** If \c allow_product_relation is true, we will create a join that builds a product relation, if there is no other way to do the join. If \c allow_product_relation is false, we will return zero in that case. */ relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, bool allow_product_relation=true); relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, const unsigned_vector & cols1, const unsigned_vector & cols2, bool allow_product_relation=true) { SASSERT(cols1.size()==cols2.size()); return mk_join_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), allow_product_relation); } table_min_fn * mk_min_fn(const table_base & t, unsigned_vector & group_by_cols, const unsigned col); /** \brief Return functor that transforms a table into one that lacks columns listed in \c removed_cols array. The \c removed_cols cotains columns of table \c t in strictly ascending order. */ relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols); relation_transformer_fn * mk_project_fn(const relation_base & t, const unsigned_vector & removed_cols) { return mk_project_fn(t, removed_cols.size(), removed_cols.c_ptr()); } /** \brief Return an operation that is a composition of a join an a project operation. */ relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, bool allow_product_relation_join=true); relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, const unsigned_vector & cols1, const unsigned_vector & cols2, const unsigned_vector & removed_cols, bool allow_product_relation_join=true) { return mk_join_project_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), removed_cols.size(), removed_cols.c_ptr(), allow_product_relation_join); } relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); relation_transformer_fn * mk_rename_fn(const relation_base & t, const unsigned_vector & permutation_cycle) { return mk_rename_fn(t, permutation_cycle.size(), permutation_cycle.c_ptr()); } /** Like \c mk_rename_fn, only the permutation is not specified by cycle, but by a permutated array of column number. */ relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, const unsigned * permutation); relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, const unsigned_vector permutation) { SASSERT(t.get_signature().size()==permutation.size()); return mk_permutation_rename_fn(t, permutation.c_ptr()); } /** The post-condition for an ideal union operation is be Union(tgt, src, delta): tgt_1==tgt_0 \union src delta_1== delta_0 \union ( tgt_1 \setminus tgt_0 ) A required post-condition is Union(tgt, src, delta): tgt_1==tgt_0 \union src tgt_1==tgt_0 => delta_1==delta_0 delta_0 \subset delta_1 delta_1 \subset (delta_0 \union tgt_1) ( tgt_1 \setminus tgt_0 ) \subset delta_1 So that a sufficient implementation is Union(tgt, src, delta) { oldTgt:=tgt.clone(); tgt:=tgt \union src if(tgt!=oldTgt) { delta:=delta \union src //also ?delta \union tgt? would work } } If rules are compiled with all_or_nothing_deltas parameter set to true, a sufficient post-condition is Union(tgt, src, delta): tgt_1==tgt_0 \union src (tgt_1==tgt_0 || delta_0 is non-empty) <=> delta_1 is non-empty */ relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src) { return mk_union_fn(tgt, src, static_cast(0)); } /** Similar to union, but this one should be used inside loops to allow for abstract domain convergence. */ relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols); relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, const unsigned_vector identical_cols) { return mk_filter_identical_fn(t, identical_cols.size(), identical_cols.c_ptr()); } relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col); relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); relation_mutator_fn * mk_apply_sequential_fn(unsigned n, relation_mutator_fn* * mutators); /** \brief Operations that returns all rows of \c t for which is column \c col equal to \c value with the column \c col removed. This operation can often be efficiently implemented and is useful for evaluating rules of the form F(x):-P("c",x). */ relation_transformer_fn * mk_select_equal_and_project_fn(const relation_base & t, const relation_element & value, unsigned col); relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt, const unsigned * tgt_cols, const unsigned * src_cols); relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt, const relation_base & src, const unsigned_vector & tgt_cols, const unsigned_vector & src_cols) { SASSERT(tgt_cols.size()==src_cols.size()); return mk_filter_by_intersection_fn(tgt, src, tgt_cols.size(), tgt_cols.c_ptr(), src_cols.c_ptr()); } relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt, const relation_base & src); /** The filter_by_negation postcondition: filter_by_negation(tgt, neg, columns in tgt: c1,...,cN, corresponding columns in neg: d1,...,dN): tgt_1:={x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } */ relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, const relation_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols); relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, const relation_base & negated_obj, const unsigned_vector & t_cols, const unsigned_vector & negated_cols) { SASSERT(t_cols.size()==negated_cols.size()); return mk_filter_by_negation_fn(t, negated_obj, t_cols.size(), t_cols.c_ptr(), negated_cols.c_ptr()); } // ----------------------------------- // // table operations // // ----------------------------------- table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, const unsigned_vector & cols1, const unsigned_vector & cols2) { SASSERT(cols1.size()==cols2.size()); return mk_join_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr()); } /** \brief Return functor that transforms a table into one that lacks columns listed in \c removed_cols array. The \c removed_cols cotains columns of table \c t in strictly ascending order. If a project operation removes a non-functional column, all functional columns become non-functional (so that none of the values in functional columns are lost) */ table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols); table_transformer_fn * mk_project_fn(const table_base & t, const unsigned_vector & removed_cols) { return mk_project_fn(t, removed_cols.size(), removed_cols.c_ptr()); } /** \brief Return an operation that is a composition of a join an a project operation. This operation is equivalent to the two operations performed separately, unless functional columns are involved. The ordinary project would make all of the functional columns into non-functional if any non-functional column was removed. In function, however, we group columns into equivalence classes (according to the equalities in \c cols1 and \c cols2) and make everything non-functional only if some equivalence class of non-functional columns would have no non-functional columns remain after the removal. This behavior is implemented in the \c table_signature::from_join_project function. */ table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols); table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, const unsigned_vector & cols1, const unsigned_vector & cols2, const unsigned_vector & removed_cols) { return mk_join_project_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), removed_cols.size(), removed_cols.c_ptr()); } table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); table_transformer_fn * mk_rename_fn(const table_base & t, const unsigned_vector & permutation_cycle) { return mk_rename_fn(t, permutation_cycle.size(), permutation_cycle.c_ptr()); } /** Like \c mk_rename_fn, only the permutation is not specified by cycle, but by a permutated array of column number. */ table_transformer_fn * mk_permutation_rename_fn(const table_base & t, const unsigned * permutation); table_transformer_fn * mk_permutation_rename_fn(const table_base & t, const unsigned_vector permutation) { SASSERT(t.get_signature().size()==permutation.size()); return mk_permutation_rename_fn(t, permutation.c_ptr()); } /** The post-condition for an ideal union operation is be Union(tgt, src, delta): tgt_1==tgt_0 \union src delta_1== delta_0 \union ( tgt_1 \setminus tgt_0 ) A required post-condition is Union(tgt, src, delta): tgt_1==tgt_0 \union src tgt_1==tgt_0 => delta_1==delta_0 delta_0 \subset delta_1 delta_1 \subset (delta_0 \union tgt_1) ( tgt_1 \setminus tgt_0 ) \subset delta_1 So that a sufficient implementation is Union(tgt, src, delta) { oldTgt:=tgt.clone(); tgt:=tgt \union src if(tgt!=oldTgt) { delta:=delta \union src //also ?delta \union tgt? would work } } If rules are compiled with all_or_nothing_deltas parameter set to true, a sufficient post-condition is Union(tgt, src, delta): tgt_1==tgt_0 \union src (tgt_1==tgt_0 || delta_0 is non-empty) <=> delta_1 is non-empty */ table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta); table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src) { return mk_union_fn(tgt, src, static_cast(0)); } /** Similar to union, but this one should be used inside loops to allow for abstract domain convergence. */ table_union_fn * mk_widen_fn(const table_base & tgt, const table_base & src, const table_base * delta); table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, const unsigned * identical_cols); table_mutator_fn * mk_filter_identical_fn(const table_base & t, const unsigned_vector identical_cols) { return mk_filter_identical_fn(t, identical_cols.size(), identical_cols.c_ptr()); } table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, unsigned col); table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition); table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); /** \brief Operations that returns all rows of \c t for which is column \c col equal to \c value with the column \c col removed. This operation can often be efficiently implemented and is useful for evaluating rules of the form F(x):-P("c",x). */ table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, const table_element & value, unsigned col); table_intersection_filter_fn * mk_filter_by_intersection_fn(const table_base & t, const table_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols); table_intersection_filter_fn * mk_filter_by_intersection_fn(const table_base & t, const table_base & src, const unsigned_vector & t_cols, const unsigned_vector & src_cols) { SASSERT(t_cols.size()==src_cols.size()); return mk_filter_by_intersection_fn(t, src, t_cols.size(), t_cols.c_ptr(), src_cols.c_ptr()); } /** The filter_by_negation postcondition: filter_by_negation(tgt, neg, columns in tgt: c1,...,cN, corresponding columns in neg: d1,...,dN): tgt_1:={x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } */ table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols); table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, const unsigned_vector & t_cols, const unsigned_vector & negated_cols) { SASSERT(t_cols.size()==negated_cols.size()); return mk_filter_by_negation_fn(t, negated_obj, t_cols.size(), t_cols.c_ptr(), negated_cols.c_ptr()); } /** combined filter by negation with a join. */ table_intersection_join_filter_fn* mk_filter_by_negated_join_fn( const table_base & t, const table_base & src1, const table_base & src2, unsigned_vector const& t_cols, unsigned_vector const& src_cols, unsigned_vector const& src1_cols, unsigned_vector const& src2_cols); /** \c t must contain at least one functional column. Created object takes ownership of the \c mapper object. */ virtual table_mutator_fn * mk_map_fn(const table_base & t, table_row_mutator_fn * mapper); /** \c t must contain at least one functional column. Created object takes ownership of the \c mapper object. */ virtual table_transformer_fn * mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols, table_row_pair_reduce_fn * reducer); // ----------------------------------- // // output functions // // ----------------------------------- std::string to_nice_string(const relation_element & el) const; /** This one may give a nicer representation of \c el than the \c to_nice_string(const relation_element & el) function, by unsing the information about the sort of the element. */ std::string to_nice_string(const relation_sort & s, const relation_element & el) const; std::string to_nice_string(const relation_sort & s) const; std::string to_nice_string(const relation_signature & s) const; void display(std::ostream & out) const; void display_relation_sizes(std::ostream & out) const; void display_output_tables(rule_set const& rules, std::ostream & out) const; private: relation_intersection_filter_fn * try_mk_default_filter_by_intersection_fn(const relation_base & t, const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols); }; /** This is a helper class for relation_plugins whose relations can be of various kinds. */ template > class rel_spec_store { typedef relation_signature::hash r_hash; typedef relation_signature::eq r_eq; typedef map family_id_idx_store; typedef map sig2store; typedef u_map family_id2spec; typedef map sig2spec_store; relation_plugin & m_parent; svector m_allocated_kinds; sig2store m_kind_assignment; sig2spec_store m_kind_specs; relation_manager & get_manager() { return m_parent.get_manager(); } void add_new_kind() { add_available_kind(get_manager().get_next_relation_fid(m_parent)); } public: rel_spec_store(relation_plugin & parent) : m_parent(parent) {} ~rel_spec_store() { reset_dealloc_values(m_kind_assignment); reset_dealloc_values(m_kind_specs); } void add_available_kind(family_id k) { m_allocated_kinds.push_back(k); } bool contains_signature(relation_signature const& sig) const { return m_kind_assignment.contains(sig); } family_id get_relation_kind(const relation_signature & sig, const Spec & spec) { typename sig2store::entry * e = m_kind_assignment.find_core(sig); if(!e) { e = m_kind_assignment.insert_if_not_there2(sig, alloc(family_id_idx_store)); m_kind_specs.insert(sig, alloc(family_id2spec)); } family_id_idx_store & ids = *e->get_data().m_value; unsigned res_idx; if(!ids.find(spec, res_idx)) { res_idx = ids.size(); if(res_idx==m_allocated_kinds.size()) { add_new_kind(); } SASSERT(res_idxinsert(m_allocated_kinds[res_idx], spec); } return m_allocated_kinds[res_idx]; } void get_relation_spec(const relation_signature & sig, family_id kind, Spec & spec) { family_id2spec * idspecs = m_kind_specs.find(sig); spec = idspecs->find(kind); } }; }; #endif /* _DL_RELATION_MANAGER_H_ */ z3-4.4.0/src/muz/rel/dl_finite_product_relation.cpp0000644000175000017500000030345512540347414022141 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_finite_product_relation.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #include #include"dl_context.h" #include"dl_relation_manager.h" #include"dl_table_relation.h" #include"dl_finite_product_relation.h" #include"bool_rewriter.h" namespace datalog { //static variables const table_sort finite_product_relation::s_rel_idx_sort = INT_MAX; void universal_delete(finite_product_relation* ptr) { ptr->deallocate(); } // ----------------------------------- // // finite_product_relation_plugin // // ----------------------------------- finite_product_relation & finite_product_relation_plugin::get(relation_base & r) { SASSERT(r.get_plugin().is_finite_product_relation()); return static_cast(r); } const finite_product_relation & finite_product_relation_plugin::get(const relation_base & r) { SASSERT(r.get_plugin().is_finite_product_relation()); return static_cast(r); } finite_product_relation * finite_product_relation_plugin::get(relation_base * r) { SASSERT(!r || r->get_plugin().is_finite_product_relation()); return static_cast(r); } const finite_product_relation * finite_product_relation_plugin::get(const relation_base * r) { SASSERT(!r || r->get_plugin().is_finite_product_relation()); return static_cast(r); } symbol finite_product_relation_plugin::get_name(relation_plugin & inner_plugin) { std::string str = std::string("fpr_")+inner_plugin.get_name().bare_str(); return symbol(str.c_str()); } finite_product_relation_plugin & finite_product_relation_plugin::get_plugin(relation_manager & rmgr, relation_plugin & inner) { finite_product_relation_plugin * res; if(!rmgr.try_get_finite_product_relation_plugin(inner, res)) { res = alloc(finite_product_relation_plugin, inner, rmgr); rmgr.register_plugin(res); } return *res; } finite_product_relation_plugin::finite_product_relation_plugin(relation_plugin & inner_plugin, relation_manager & manager) : relation_plugin(get_name(inner_plugin), manager, ST_FINITE_PRODUCT_RELATION), m_inner_plugin(inner_plugin), m_spec_store(*this) { } void finite_product_relation_plugin::initialize(family_id fid) { relation_plugin::initialize(fid); m_spec_store.add_available_kind(get_kind()); } family_id finite_product_relation_plugin::get_relation_kind(finite_product_relation & r, const bool * table_columns) { const relation_signature & sig = r.get_signature(); svector table_cols_vect(sig.size(), table_columns); return m_spec_store.get_relation_kind(sig, rel_spec(table_cols_vect)); } void finite_product_relation_plugin::get_all_possible_table_columns(relation_manager & rmgr, const relation_signature & s, svector & table_columns) { SASSERT(table_columns.empty()); unsigned s_sz = s.size(); for(unsigned i=0; i table_columns; get_all_possible_table_columns(s, table_columns); #ifndef _EXTERNAL_RELEASE unsigned s_sz = s.size(); unsigned rel_col_cnt = 0; for(unsigned i=0; icomplement_self(p); return res; } bool finite_product_relation_plugin::can_convert_to_table_relation(const finite_product_relation & r) { return r.m_other_sig.empty(); } table_relation * finite_product_relation_plugin::to_table_relation(const finite_product_relation & r) { SASSERT(can_convert_to_table_relation(r)); r.garbage_collect(true); //now all rows in the table will correspond to rows in the resulting table_relation const table_base & t = r.get_table(); unsigned removed_col = t.get_signature().size()-1; scoped_ptr project_fun = get_manager().mk_project_fn(r.get_table(), 1, &removed_col); table_base * res_table = (*project_fun)(t); SASSERT(res_table->get_signature().functional_columns()==0); return static_cast(get_manager().mk_table_relation(r.get_signature(), res_table)); } bool finite_product_relation_plugin::can_be_converted(const relation_base & r) { if(&r.get_plugin()==&get_inner_plugin()) { //can be converted by mk_from_inner_relation return true; } if(r.from_table()) { //We can convert directly from table plugin only if the inner plugin can handle empty signatures. //TODO: If the inner plugin cannot handle empty signatures, we may try to move some of the //table columns into the inner relation signature. return get_inner_plugin().can_handle_signature(relation_signature()); } return false; } finite_product_relation * finite_product_relation_plugin::mk_from_table_relation(const table_relation & r) { func_decl* pred = 0; const relation_signature & sig = r.get_signature(); const table_base & t = r.get_table(); table_plugin & tplugin = r.get_table().get_plugin(); relation_signature inner_sig; //empty signature for the inner relation if(!get_inner_plugin().can_handle_signature(inner_sig)) { return 0; } table_signature idx_singleton_sig; idx_singleton_sig.push_back(finite_product_relation::s_rel_idx_sort); idx_singleton_sig.set_functional_columns(1); scoped_rel idx_singleton; if(tplugin.can_handle_signature(idx_singleton_sig)) { idx_singleton = tplugin.mk_empty(idx_singleton_sig); } else { idx_singleton = get_manager().mk_empty_table(idx_singleton_sig); } table_fact idx_singleton_fact; idx_singleton_fact.push_back(0); idx_singleton->add_fact(idx_singleton_fact); scoped_ptr join_fun = get_manager().mk_join_fn(t, *idx_singleton, 0, 0, 0); SASSERT(join_fun); scoped_rel res_table = (*join_fun)(t, *idx_singleton); svector table_cols(sig.size(), true); finite_product_relation * res = mk_empty(sig, table_cols.c_ptr()); //this one does not need to be deleted -- it will be taken over by \c res in the \c init function relation_base * inner_rel = get_inner_plugin().mk_full(pred, inner_sig, get_inner_plugin().get_kind()); relation_vector rels; rels.push_back(inner_rel); res->init(*res_table, rels, true); return res; } finite_product_relation * finite_product_relation_plugin::mk_from_inner_relation(const relation_base & r) { SASSERT(&r.get_plugin()==&get_inner_plugin()); const relation_signature & sig = r.get_signature(); table_signature idx_singleton_sig; idx_singleton_sig.push_back(finite_product_relation::s_rel_idx_sort); idx_singleton_sig.set_functional_columns(1); scoped_rel idx_singleton = get_manager().mk_empty_table(idx_singleton_sig); table_fact idx_singleton_fact; idx_singleton_fact.push_back(0); idx_singleton->add_fact(idx_singleton_fact); svector table_cols(sig.size(), false); finite_product_relation * res = mk_empty(sig, table_cols.c_ptr()); relation_vector rels; rels.push_back(r.clone()); res->init(*idx_singleton, rels, true); return res; } class finite_product_relation_plugin::converting_join_fn : public convenient_relation_join_fn { finite_product_relation_plugin & m_plugin; scoped_ptr m_native_join; finite_product_relation * convert(const relation_base & r) { SASSERT(&r.get_plugin()!=&m_plugin); if(&r.get_plugin()==&m_plugin.get_inner_plugin()) { return m_plugin.mk_from_inner_relation(r); } SASSERT(r.from_table()); const table_relation & tr = static_cast(r); finite_product_relation * res = m_plugin.mk_from_table_relation(tr); SASSERT(res); return res; } public: converting_join_fn(finite_product_relation_plugin & plugin, const relation_signature & sig1, const relation_signature & sig2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_relation_join_fn(sig1, sig2, col_cnt, cols1, cols2), m_plugin(plugin) {} virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { scoped_rel r1_conv; if(&r1.get_plugin()!=&m_plugin) { r1_conv = convert(r1); } scoped_rel r2_conv; if(&r2.get_plugin()!=&m_plugin) { r2_conv = convert(r2); } const finite_product_relation & fpr1 = r1_conv ? *r1_conv : get(r1); const finite_product_relation & fpr2 = r2_conv ? *r2_conv : get(r2); SASSERT(&fpr1.get_plugin()==&m_plugin); SASSERT(&fpr2.get_plugin()==&m_plugin); if(!m_native_join) { m_native_join = m_plugin.get_manager().mk_join_fn(fpr1, fpr2, m_cols1, m_cols2, false); } return (*m_native_join)(fpr1, fpr2); } }; class finite_product_relation_plugin::join_fn : public convenient_relation_join_fn { scoped_ptr m_tjoin_fn; scoped_ptr m_rjoin_fn; unsigned_vector m_t_joined_cols1; unsigned_vector m_t_joined_cols2; unsigned_vector m_r_joined_cols1; unsigned_vector m_r_joined_cols2; //Column equalities betweet table and inner relations. //The columns numbers correspont to the columns of the table/inner relation //in the result of the join operation unsigned_vector m_tr_table_joined_cols; unsigned_vector m_tr_rel_joined_cols; scoped_ptr m_filter_tr_identities; scoped_ptr m_tjoined_second_rel_remover; //determines which columns of the result are table columns and which are in the inner relation svector m_res_table_columns; public: class join_maker : public table_row_mutator_fn { join_fn & m_parent; const finite_product_relation & m_r1; const finite_product_relation & m_r2; relation_vector & m_rjoins; public: join_maker(join_fn & parent, const finite_product_relation & r1, const finite_product_relation & r2, relation_vector & rjoins) : m_parent(parent), m_r1(r1), m_r2(r2), m_rjoins(rjoins) {} virtual bool operator()(table_element * func_columns) { const relation_base & or1 = m_r1.get_inner_rel(func_columns[0]); const relation_base & or2 = m_r2.get_inner_rel(func_columns[1]); SASSERT(&or1); SASSERT(&or2); unsigned new_rel_num = m_rjoins.size(); m_rjoins.push_back(m_parent.do_rjoin(or1, or2)); func_columns[0]=new_rel_num; return true; } }; join_fn(const finite_product_relation & r1, const finite_product_relation & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_relation_join_fn(r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2) { unsigned second_table_after_join_ofs = r1.m_table2sig.size(); unsigned second_inner_rel_after_join_ofs = r1.m_other2sig.size(); for(unsigned i=0;i tjoined = (*m_tjoin_fn)(r1.get_table(), r2.get_table()); relation_vector joined_orelations; { join_maker * mutator = alloc(join_maker, *this, r1, r2, joined_orelations); //dealocated in inner_join_mapper scoped_ptr inner_join_mapper = rmgr.mk_map_fn(*tjoined, mutator); (*inner_join_mapper)(*tjoined); } if(!m_tjoined_second_rel_remover) { unsigned removed_col = tjoined->get_signature().size()-1; m_tjoined_second_rel_remover = rmgr.mk_project_fn(*tjoined, 1, &removed_col); } //remove the second functional column from tjoined to get a table that corresponds //to the table signature of the resulting relation scoped_rel res_table = (*m_tjoined_second_rel_remover)(*tjoined); relation_plugin & res_oplugin = joined_orelations.empty() ? r1.m_other_plugin : joined_orelations.back()->get_plugin(); //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. //It would however need to be somehow inferred for the new signature. finite_product_relation * res = alloc(finite_product_relation, r1.get_plugin(), get_result_signature(), m_res_table_columns.c_ptr(), res_table->get_plugin(), res_oplugin, null_family_id); res->init(*res_table, joined_orelations, true); if(m_tr_table_joined_cols.size()) { //There were some shared variables between the table and the relation part. //We enforce those equalities here. if(!m_filter_tr_identities) { m_filter_tr_identities = plugin.mk_filter_identical_pairs(*res, m_tr_table_joined_cols.size(), m_tr_table_joined_cols.c_ptr(), m_tr_rel_joined_cols.c_ptr()); SASSERT(m_filter_tr_identities); } (*m_filter_tr_identities)(*res); } return res; } }; relation_join_fn * finite_product_relation_plugin::mk_join_fn(const relation_base & rb1, const relation_base & rb2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if(!check_kind(rb1) || !check_kind(rb2)) { bool r1foreign = &rb1.get_plugin()!=this; bool r2foreign = &rb2.get_plugin()!=this; if( (!r1foreign || can_be_converted(rb1)) && (!r2foreign || can_be_converted(rb2))) { return alloc(converting_join_fn, *this, rb1.get_signature(), rb2.get_signature(), col_cnt, cols1, cols2); } return 0; } const finite_product_relation & r1 = get(rb1); const finite_product_relation & r2 = get(rb2); return alloc(join_fn, r1, r2, col_cnt, cols1, cols2); } class finite_product_relation_plugin::project_fn : public convenient_relation_project_fn { unsigned_vector m_removed_table_cols; unsigned_vector m_removed_rel_cols; scoped_ptr m_rel_projector; scoped_ptr m_inner_rel_union; //determines which columns of the result are table columns and which are in the inner relation svector m_res_table_columns; public: project_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(r.get_signature(), col_cnt, removed_cols) { SASSERT(col_cnt>0); for(unsigned i=0; ii); m_res_table_columns.push_back(r.is_table_column(i)); } } class project_reducer : public table_row_pair_reduce_fn { project_fn & m_parent; relation_vector & m_relations; public: project_reducer(project_fn & parent, relation_vector & relations) : m_parent(parent), m_relations(relations) {} virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { relation_base * tgt = m_relations[static_cast(func_columns[0])]->clone(); relation_base & src = *m_relations[static_cast(merged_func_columns[0])]; if(!m_parent.m_inner_rel_union) { m_parent.m_inner_rel_union = tgt->get_manager().mk_union_fn(*tgt, src); } (*m_parent.m_inner_rel_union)(*tgt, src); unsigned new_idx = m_relations.size(); m_relations.push_back(tgt); func_columns[0] = new_idx; } }; virtual relation_base * operator()(const relation_base & rb) { const finite_product_relation & r = get(rb); finite_product_relation_plugin & plugin = r.get_plugin(); const table_base & rtable = r.get_table(); relation_manager & rmgr = plugin.get_manager(); r.garbage_collect(false); relation_vector res_relations; unsigned orig_rel_cnt = r.m_others.size(); for(unsigned i=0; iclone() : 0); } SASSERT(res_relations.size()==orig_rel_cnt); bool shared_res_table = false; const table_base * res_table; if(m_removed_table_cols.empty()) { shared_res_table = true; res_table = &rtable; } else { project_reducer * preducer = alloc(project_reducer, *this, res_relations); scoped_ptr tproject = rmgr.mk_project_with_reduce_fn(rtable, m_removed_table_cols.size(), m_removed_table_cols.c_ptr(), preducer); res_table = (*tproject)(rtable); } relation_plugin * res_oplugin = 0; if(!m_removed_rel_cols.empty()) { unsigned res_rel_cnt = res_relations.size(); for(unsigned i=0; ideallocate(); if(!res_oplugin) { res_oplugin = &res_relations[i]->get_plugin(); } } } if(!res_oplugin) { res_oplugin = &r.m_other_plugin; } //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. //It would however need to be somehow inferred for the new signature. finite_product_relation * res = alloc(finite_product_relation, r.get_plugin(), get_result_signature(), m_res_table_columns.c_ptr(), res_table->get_plugin(), *res_oplugin, null_family_id); res->init(*res_table, res_relations, false); if(!shared_res_table) { const_cast(res_table)->deallocate(); } return res; } }; relation_transformer_fn * finite_product_relation_plugin::mk_project_fn(const relation_base & rb, unsigned col_cnt, const unsigned * removed_cols) { if(&rb.get_plugin()!=this) { return 0; } return alloc(project_fn, get(rb), col_cnt, removed_cols); } class finite_product_relation_plugin::rename_fn : public convenient_relation_rename_fn { scoped_ptr m_table_renamer; scoped_ptr m_rel_renamer; bool m_rel_identity; unsigned_vector m_rel_permutation; //determines which columns of the result are table columns and which are in the inner relation svector m_res_table_columns; public: rename_fn(const finite_product_relation & r, unsigned cycle_len, const unsigned * permutation_cycle) : convenient_relation_rename_fn(r.get_signature(), cycle_len, permutation_cycle) { SASSERT(cycle_len>1); unsigned sig_sz = r.get_signature().size(); unsigned_vector permutation; add_sequence(0, sig_sz, permutation); permutate_by_cycle(permutation, cycle_len, permutation_cycle); unsigned_vector table_permutation; bool table_identity = true; m_rel_identity = true; for(unsigned new_i=0; new_iclone() : 0); } if(!m_rel_identity) { unsigned res_rel_cnt = res_relations.size(); for(unsigned i=0; i inner_rel = res_relations[i]; if(!m_rel_renamer) { m_rel_renamer = r.get_manager().mk_permutation_rename_fn(*inner_rel, m_rel_permutation); } res_relations[i] = (*m_rel_renamer)(*inner_rel); } } scoped_rel res_table_scoped; const table_base * res_table = &rtable; if(m_table_renamer) { res_table_scoped = (*m_table_renamer)(*res_table); res_table = res_table_scoped.get(); } //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. //It would however need to be somehow inferred for the new signature. finite_product_relation * res = alloc(finite_product_relation, r.get_plugin(), get_result_signature(), m_res_table_columns.c_ptr(), res_table->get_plugin(), r.m_other_plugin, null_family_id); res->init(*res_table, res_relations, false); return res; } }; relation_transformer_fn * finite_product_relation_plugin::mk_rename_fn(const relation_base & rb, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { if(&rb.get_plugin()!=this) { return 0; } const finite_product_relation & r = get(rb); return alloc(rename_fn, r, permutation_cycle_len, permutation_cycle); } class finite_product_relation_plugin::union_fn : public relation_union_fn { bool m_use_delta; unsigned_vector m_data_cols;//non-functional columns in the product-relation table (useful for creating operations) scoped_ptr m_common_join; //result of the join contains (data columns), tgt_rel_idx, src_rel_idx scoped_ptr m_rel_union; scoped_ptr m_table_union; scoped_ptr m_remove_overlaps; scoped_ptr m_remove_src_column_from_overlap; //this one is populated only if we're doing union with delta scoped_ptr m_delta_merging_union; scoped_ptr m_overlap_delta_table_builder; public: union_fn(const finite_product_relation & tgt, bool use_delta) : m_use_delta(use_delta) {} relation_union_fn & get_inner_rel_union_op(relation_base & r) { if(!m_rel_union) { m_rel_union = r.get_manager().mk_union_fn(r, r, m_use_delta ? &r : 0); } return *m_rel_union; } class union_mapper : public table_row_mutator_fn { union_fn & m_parent; finite_product_relation & m_tgt; const finite_product_relation & m_src; table_base * m_delta_indexes; //table with signature (updated tgt rel index, delta_index in m_delta_rels) relation_vector * m_delta_rels; table_fact m_di_fact; //auxiliary fact for inserting into \c m_delta_indexes public: /** If \c delta_indexes is 0, it means we are not collecting indexes. */ union_mapper(union_fn & parent, finite_product_relation & tgt, const finite_product_relation & src, table_base * delta_indexes, relation_vector * delta_rels) : m_parent(parent), m_tgt(tgt), m_src(src), m_delta_indexes(delta_indexes), m_delta_rels(delta_rels) {} virtual ~union_mapper() {} virtual bool operator()(table_element * func_columns) { relation_base & otgt_orig = m_tgt.get_inner_rel(func_columns[0]); const relation_base & osrc = m_src.get_inner_rel(func_columns[1]); relation_base * otgt = otgt_orig.clone(); unsigned new_tgt_idx = m_tgt.get_next_rel_idx(); m_tgt.set_inner_rel(new_tgt_idx, otgt); if(m_delta_indexes) { relation_base * odelta = otgt->get_plugin().mk_empty(otgt->get_signature()); m_parent.get_inner_rel_union_op(*otgt)(*otgt, osrc, odelta); unsigned delta_idx = m_delta_rels->size(); m_delta_rels->push_back(odelta); m_di_fact.reset(); m_di_fact.push_back(new_tgt_idx); m_di_fact.push_back(delta_idx); m_delta_indexes->add_fact(m_di_fact); } else { m_parent.get_inner_rel_union_op(*otgt)(*otgt, osrc); } func_columns[0]=new_tgt_idx; return true; } }; /** Makes a table whose last column has indexes to relations in \c src into a table with indexes to relation \c tgt. */ class src_copying_mapper : public table_row_mutator_fn { finite_product_relation & m_tgt; const finite_product_relation & m_src; public: /** If \c delta_indexes is 0, it means we are not collecting indexes. */ src_copying_mapper(finite_product_relation & tgt, const finite_product_relation & src) : m_tgt(tgt), m_src(src) {} virtual bool operator()(table_element * func_columns) { const relation_base & osrc = m_src.get_inner_rel(func_columns[0]); unsigned new_tgt_idx = m_tgt.get_next_rel_idx(); m_tgt.set_inner_rel(new_tgt_idx, osrc.clone()); func_columns[0]=new_tgt_idx; return true; } }; virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { finite_product_relation & tgt = get(tgtb); const finite_product_relation & src0 = get(srcb); finite_product_relation * delta = get(deltab); relation_manager & rmgr = tgt.get_manager(); scoped_rel src_aux_copy; //copy of src in case its specification needs to be modified if(!vectors_equal(tgt.m_table2sig, src0.m_table2sig) || (delta && !vectors_equal(tgt.m_table2sig, delta->m_table2sig)) ) { src_aux_copy = src0.clone(); ptr_vector orig_rels; orig_rels.push_back(src_aux_copy.get()); orig_rels.push_back(&tgt); if(delta) { orig_rels.push_back(delta); } if(!finite_product_relation::try_unify_specifications(orig_rels)) { throw default_exception("finite_product_relation union: cannot convert relations to common specification"); } } const finite_product_relation & src = src_aux_copy ? *src_aux_copy : src0; table_plugin & tplugin = tgt.get_table_plugin(); if(!m_common_join) { unsigned data_cols_cnt = tgt.m_table_sig.size()-1; for(unsigned i=0; i table_overlap = (*m_common_join)(tgt.get_table(), src.get_table()); scoped_rel delta_indexes; relation_vector delta_rels; if(m_use_delta) { table_signature di_sig; di_sig.push_back(finite_product_relation::s_rel_idx_sort); di_sig.push_back(finite_product_relation::s_rel_idx_sort); di_sig.set_functional_columns(1); delta_indexes = tplugin.mk_empty(di_sig); } { union_mapper * umapper = alloc(union_mapper, *this, tgt, src, delta_indexes.get(), &delta_rels); scoped_ptr mapping_fn = rmgr.mk_map_fn(*table_overlap, umapper); (*mapping_fn)(*table_overlap); } if(!m_remove_src_column_from_overlap) { unsigned removed_cols[] = { table_overlap->get_signature().size()-1 }; m_remove_src_column_from_overlap = rmgr.mk_project_fn(*table_overlap, 1, removed_cols); } //transform table_overlap into the signature of tgt.get_table(), so that the functional //column contains indexes of the united relations scoped_rel regular_overlap = (*m_remove_src_column_from_overlap)(*table_overlap); if(!m_remove_overlaps) { m_remove_overlaps = rmgr.mk_filter_by_negation_fn(tgt.get_table(), *regular_overlap, m_data_cols, m_data_cols); } //in tgt keep only the rows that are in tgt only (*m_remove_overlaps)(tgt.get_table(), *regular_overlap); //add rows in which tgt and src overlapped if(!m_table_union) { m_table_union = rmgr.mk_union_fn(tgt.get_table(), tgt.get_table()); } (*m_table_union)(tgt.get_table(), *regular_overlap); scoped_rel src_only = src.get_table().clone(); (*m_remove_overlaps)(*src_only, *regular_overlap); scoped_rel src_only2; //a copy of src_only for use in building the delta if(m_use_delta) { src_only2 = src_only->clone(); } { src_copying_mapper * cpmapper = alloc(src_copying_mapper, tgt, src); scoped_ptr mapping_fn = rmgr.mk_map_fn(*src_only, cpmapper); (*mapping_fn)(*src_only); } //add rows that were only in src (*m_table_union)(tgt.get_table(), *src_only); if(m_use_delta) { bool extending_delta = !delta->empty(); //current delta, we will add it to the deltab argument later if it was not given to us empty finite_product_relation * cdelta; if(extending_delta) { cdelta = delta->get_plugin().mk_empty(*delta); } else { cdelta = delta; } if(!m_overlap_delta_table_builder) { unsigned table_fn_col = regular_overlap->get_signature().size()-1; unsigned first_col = 0; unsigned removed_cols[] = { table_fn_col, table_fn_col+1 }; m_overlap_delta_table_builder = rmgr.mk_join_project_fn(*regular_overlap, *delta_indexes, 1, &table_fn_col, &first_col, 2, removed_cols); } scoped_rel overlap_delta_table = (*m_overlap_delta_table_builder)(*regular_overlap, *delta_indexes); cdelta->init(*overlap_delta_table, delta_rels, true); { src_copying_mapper * cpmapper = alloc(src_copying_mapper, *cdelta, src); scoped_ptr mapping_fn = rmgr.mk_map_fn(*src_only2, cpmapper); (*mapping_fn)(*src_only2); } //add rows that were only in src (*m_table_union)(cdelta->get_table(), *src_only2); if(extending_delta) { if(!m_delta_merging_union) { m_delta_merging_union = rmgr.mk_union_fn(*delta, *cdelta); } (*m_delta_merging_union)(*delta, *cdelta); cdelta->deallocate(); } } } }; #if 0 /** Union operation taking advantage of the fact that the inner relation of all the arguments is a singleton relation. */ class finite_product_relation_plugin::inner_singleton_union_fn : public relation_union_fn { class offset_row_mapper : public table_row_mutator_fn { public: unsigned m_ofs; virtual bool operator()(table_element * func_columns) { func_columns[0] += m_ofs; return true; } }; // [Leo]: gcc complained about the following class. // It does not have a constructor and uses a reference class inner_relation_copier : public table_row_mutator_fn { finite_product_relation & m_tgt; const finite_product_relation & m_src; finite_product_relation * m_delta; unsigned m_tgt_ofs; unsigned m_delta_ofs; public: virtual bool operator()(table_element * func_columns) { unsigned src_idx = static_cast(func_columns[0]); unsigned tgt_idx = src_idx + m_tgt_ofs; unsigned delta_idx = m_delta ? (src_idx + m_delta_ofs) : 0; SASSERT(!m_delta || m_tgt.m_others[tgt_idx]==m_delta->m_others[delta_idx]); SASSERT(m_tgt.m_others[tgt_idx]==0 || m_tgt.m_others[tgt_idx]==m_src.m_others[src_idx]); if(m_tgt.m_others[tgt_idx]==0) { m_tgt.m_others[tgt_idx] = m_src.m_others[src_idx]->clone(); if(m_delta) { m_delta->m_others[delta_idx] = m_src.m_others[src_idx]->clone(); } } return true; } }; scoped_ptr m_t_union_fun; offset_row_mapper * m_offset_mapper_obj; //initialized together with m_offset_mapper_fun, and deallocated by it scoped_ptr m_offset_mapper_fun; public: virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { finite_product_relation & tgt = get(tgtb); const finite_product_relation & src = get(srcb); finite_product_relation * delta = get(deltab); finite_product_relation_plugin & plugin = tgt.get_plugin(); relation_manager & rmgr = plugin.get_manager(); //we want only non-empty inner relations to remain tgt.garbage_collect(true); src.garbage_collect(true); table_base & tgt_table = tgt.get_table(); const table_base & src_table = src.get_table(); scoped_rel offsetted_src = src_table.clone(); if(!m_offset_mapper_fun) { m_offset_mapper_obj = alloc(offset_row_mapper); m_offset_mapper_fun = rmgr.mk_map_fn(*offsetted_src, m_offset_mapper_obj); } unsigned src_rel_offset = tgt.m_others.size(); m_offset_mapper_obj->m_ofs = src_rel_offset; (*m_offset_mapper_fun)(*offsetted_src); //if we need to generate a delta, we get collect it into an empty relation and then union //it with the delta passed in as argument. scoped_rel loc_delta = delta ? get(plugin.mk_empty(*delta)) : 0; //even if we don't need to generate the delta for the caller, we still want to have a delta //table to know which relations to copy. scoped_rel loc_delta_table_scoped; if(!loc_delta) { loc_delta_table_scoped = tgt_table.get_plugin().mk_empty(tgt_table); } table_base * loc_delta_table = loc_delta ? &loc_delta->get_table() : loc_delta_table_scoped.get(); if(!m_t_union_fun) { m_t_union_fun = rmgr.mk_union_fn(tgt_table, *offsetted_src, loc_delta_table); } (*m_t_union_fun)(tgt_table, *offsetted_src, loc_delta_table); //TODO: copy the relations into tgt and (possibly) delta using inner_relation_copier //TODO: unite the local delta with the delta passed in as an argument NOT_IMPLEMENTED_YET(); } }; #endif class finite_product_relation_plugin::converting_union_fn : public relation_union_fn { scoped_ptr m_tr_union_fun; public: virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { SASSERT(srcb.get_plugin().is_finite_product_relation()); const finite_product_relation & src = get(srcb); finite_product_relation_plugin & plugin = src.get_plugin(); scoped_rel tr_src = plugin.to_table_relation(src); if(!m_tr_union_fun) { m_tr_union_fun = plugin.get_manager().mk_union_fn(tgtb, *tr_src, deltab); SASSERT(m_tr_union_fun); } (*m_tr_union_fun)(tgtb, *tr_src, deltab); } }; relation_union_fn * finite_product_relation_plugin::mk_union_fn(const relation_base & tgtb, const relation_base & srcb, const relation_base * deltab) { if(&srcb.get_plugin()!=this) { return 0; } const finite_product_relation & src = get(srcb); if(&tgtb.get_plugin()!=this || (deltab && &deltab->get_plugin()!=this) ) { if(can_convert_to_table_relation(src)) { return alloc(converting_union_fn); } return 0; } const finite_product_relation * delta = get(deltab); return alloc(union_fn, get(tgtb), delta!=0); } class finite_product_relation_plugin::filter_identical_fn : public relation_mutator_fn { //the table and relation columns that should be identical //the column numbering is local to the table or inner relation unsigned_vector m_table_cols; unsigned_vector m_rel_cols; scoped_ptr m_table_filter; scoped_ptr m_rel_filter; scoped_ptr m_tr_filter; public: filter_identical_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * identical_cols) : m_table_filter(0), m_rel_filter(0), m_tr_filter(0) { finite_product_relation_plugin & plugin = r.get_plugin(); for(unsigned i=0; i1) { m_table_filter = r.get_manager().mk_filter_identical_fn(r.get_table(), m_table_cols.size(), m_table_cols.c_ptr()); SASSERT(m_table_filter); } if(!m_table_cols.empty() && !m_rel_cols.empty()) { unsigned tr_filter_table_cols[] = { m_table_cols[0] }; unsigned tr_filter_rel_cols[] = { m_rel_cols[0] }; m_tr_filter = plugin.mk_filter_identical_pairs(r, 1, tr_filter_table_cols, tr_filter_rel_cols); SASSERT(m_tr_filter); } } void ensure_rel_filter(const relation_base & orel) { SASSERT(m_rel_cols.size()>1); if(m_rel_filter) { return; } m_rel_filter = orel.get_manager().mk_filter_identical_fn(orel, m_rel_cols.size(), m_rel_cols.c_ptr()); SASSERT(m_rel_filter); } virtual void operator()(relation_base & rb) { finite_product_relation & r = get(rb); if(m_table_cols.size()>1) { (*m_table_filter)(r.get_table()); } if(m_rel_cols.size()>1) { r.garbage_collect(true); unsigned rel_cnt = r.m_others.size(); for(unsigned rel_idx=0; rel_idx m_table_filter; scoped_ptr m_rel_filter; unsigned m_col; app_ref m_value; public: filter_equal_fn(const finite_product_relation & r, const relation_element & value, unsigned col) : m_col(col), m_value(value, r.get_context().get_manager()) { if(r.is_table_column(col)) { table_element tval; r.get_manager().relation_to_table(r.get_signature()[col], value, tval); m_table_filter = r.get_manager().mk_filter_equal_fn(r.get_table(), tval, r.m_sig2table[col]); } } virtual void operator()(relation_base & rb) { finite_product_relation & r = get(rb); if(m_table_filter) { (*m_table_filter)(r.get_table()); return; } r.garbage_collect(false); relation_vector & inner_rels = r.m_others; unsigned rel_cnt = inner_rels.size(); for(unsigned i=0; i m_table_filter; scoped_ptr m_rel_filter; app_ref m_cond; idx_set m_table_cond_columns; idx_set m_rel_cond_columns; //like m_table_cond_columns and m_rel_cond_columns, only the indexes are local to the //table/relation, not to the signature of the whole relation idx_set m_table_local_cond_columns; idx_set m_rel_local_cond_columns; /** If equal to 0, it means the interpreted condition uses all table columns. Then the original table is used instead of the result of the projection. */ scoped_ptr m_table_cond_columns_project; /** \brief Column indexes of the global relations to which correspond the data columns in the table that is result of applying the \c m_table_cond_columns_project functor. */ unsigned_vector m_global_origins_of_projected_columns; scoped_ptr m_assembling_join_project; /** \brief Renaming that transforms the variable numbers pointing to the global relation into variables that point to the inner relation variables. The elements that do not correspond to columns of the inner relation (but rather to the table columns) is modified in \c operator() when evaluating the condition for all the relevant combinations of table values. */ expr_ref_vector m_renaming_for_inner_rel; public: filter_interpreted_fn(const finite_product_relation & r, app * condition) : m_manager(r.get_context().get_manager()), m_subst(r.get_context().get_var_subst()), m_cond(condition, m_manager), m_renaming_for_inner_rel(m_manager) { relation_manager & rmgr = r.get_manager(); rule_manager& rm = r.get_context().get_rule_manager(); idx_set& cond_columns = rm.collect_vars(m_cond); unsigned sig_sz = r.get_signature().size(); for(unsigned i=0; i tproj_scope; const table_base * tproj; if(m_table_cond_columns_project) { tproj_scope = (*m_table_cond_columns_project)(rtable); tproj = tproj_scope.get(); } else { tproj = &rtable; } unsigned projected_data_cols = tproj->get_signature().size()-1; SASSERT(m_table_cond_columns.num_elems()==projected_data_cols); table_signature filtered_sig = tproj->get_signature(); filtered_sig.push_back(finite_product_relation::s_rel_idx_sort); filtered_sig.set_functional_columns(1); relation_vector new_rels; scoped_rel filtered_table = tplugin.mk_empty(filtered_sig); table_fact f; unsigned renaming_ofs = m_renaming_for_inner_rel.size()-1; table_base::iterator pit = tproj->begin(); table_base::iterator pend = tproj->end(); for(; pit!=pend; ++pit) { pit->get_fact(f); unsigned old_rel_idx = static_cast(f.back()); const relation_base & old_rel = r.get_inner_rel(old_rel_idx); //put the table values into the substitution for(unsigned i=0; i filter = rmgr.mk_filter_interpreted_fn(*new_rel, to_app(inner_cond)); (*filter)(*new_rel); if(new_rel->empty()) { new_rel->deallocate(); continue; } unsigned new_rel_idx = new_rels.size(); new_rels.push_back(new_rel); f.push_back(new_rel_idx); filtered_table->add_fact(f); } if(!m_assembling_join_project) { unsigned_vector table_cond_columns_vect; for(unsigned i=0; i new_table = (*m_assembling_join_project)(rtable, *filtered_table); r.reset(); r.init(*new_table, new_rels, true); } }; relation_mutator_fn * finite_product_relation_plugin::mk_filter_interpreted_fn(const relation_base & rb, app * condition) { if(&rb.get_plugin()!=this) { return 0; } return alloc(filter_interpreted_fn, get(rb), condition); } class finite_product_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn { unsigned_vector m_r_cols; unsigned_vector m_neg_cols; scoped_ptr m_table_neg_filter; scoped_ptr m_table_neg_complement_selector; scoped_ptr m_neg_intersection_join; scoped_ptr m_table_intersection_join; scoped_ptr m_table_overlap_union; scoped_ptr m_table_subtract; scoped_ptr m_inner_subtract; scoped_ptr m_overlap_table_last_column_remover; scoped_ptr m_r_table_union; bool m_table_overlaps_only; unsigned_vector m_r_shared_table_cols; unsigned_vector m_neg_shared_table_cols; unsigned_vector m_r_shared_rel_cols; unsigned_vector m_neg_shared_rel_cols; public: negation_filter_fn(const finite_product_relation & r, const finite_product_relation & neg, unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * neg_cols) : m_r_cols(joined_col_cnt, r_cols), m_neg_cols(joined_col_cnt, neg_cols), m_table_overlaps_only(true) { const table_signature & tsig = r.m_table_sig; const table_base & rtable = r.get_table(); relation_manager & rmgr = r.get_manager(); for(unsigned i=0; iget_signature().size() , all_rel_cols); m_parent.m_inner_subtract = m_r.get_manager().mk_filter_by_negation_fn(*r_inner, inters_inner, all_rel_cols, all_rel_cols); } (*m_parent.m_inner_subtract)(*r_inner, inters_inner); unsigned new_rel_num = m_r.get_next_rel_idx(); m_r.set_inner_rel(new_rel_num, r_inner); func_columns[0]=new_rel_num; return true; } }; virtual void operator()(relation_base & rb, const relation_base & negb) { finite_product_relation & r = get(rb); const finite_product_relation & neg = get(negb); if(m_table_overlaps_only) { handle_only_tables_overlap_case(r, neg); return; } finite_product_relation_plugin & plugin = r.get_plugin(); if(!m_neg_intersection_join) { } scoped_rel intersection = get((*m_neg_intersection_join)(r, neg)); SASSERT(&intersection->get_plugin()==&plugin); //the result of join is also finite product SASSERT(r.get_signature()==intersection->get_signature()); table_base & r_table = r.get_table(); table_plugin & tplugin = r_table.get_plugin(); relation_manager & rmgr = r.get_manager(); //we need to do this before we perform the \c m_table_subtract //the sigrature of the \c table_overlap0 relation is //(data_columns)(original rel idx)(subtracted rel idx) scoped_rel table_overlap0 = (*m_table_intersection_join)(r_table, intersection->get_table()); //the rows that don't appear in the table of the intersection are safe to stay (*m_table_subtract)(r_table, intersection->get_table()); //now we will examine the rows that do appear in the intersection //There are no functional columns in the \c table_overlap0 relation (because of //the project we did). We need to make both rel idx columns functional. //We do not lose any rows, since the data columns by themselves are unique. table_signature table_overlap_sig(table_overlap0->get_signature()); table_overlap_sig.set_functional_columns(2); scoped_rel table_overlap = tplugin.mk_empty(table_overlap_sig); if(!m_table_overlap_union) { m_table_overlap_union = rmgr.mk_union_fn(*table_overlap, *table_overlap0); SASSERT(m_table_overlap_union); } (*m_table_overlap_union)(*table_overlap, *table_overlap0); { rel_subtractor * mutator = alloc(rel_subtractor, *this, r, *intersection); scoped_ptr mapper = rmgr.mk_map_fn(*table_overlap, mutator); (*mapper)(*table_overlap); } if(!m_overlap_table_last_column_remover) { unsigned removed_col = table_overlap->get_signature().size()-1; m_overlap_table_last_column_remover = rmgr.mk_project_fn(*table_overlap, 1, &removed_col); } scoped_rel final_overlapping_rows_table = (*m_overlap_table_last_column_remover)(*table_overlap); if(!m_r_table_union) { m_r_table_union = rmgr.mk_union_fn(r_table, *final_overlapping_rows_table); } (*m_r_table_union)(r_table, *final_overlapping_rows_table); } }; relation_intersection_filter_fn * finite_product_relation_plugin::mk_filter_by_negation_fn(const relation_base & rb, const relation_base & negb, unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * negated_cols) { if(&rb.get_plugin()!=this || &negb.get_plugin()!=this) { return 0; } return alloc(negation_filter_fn, get(rb), get(negb), joined_col_cnt, r_cols, negated_cols); } class finite_product_relation_plugin::filter_identical_pairs_fn : public relation_mutator_fn { scoped_ptr m_tproject_fn; //if zero, no columns need to be projected away unsigned m_col_cnt; unsigned_vector m_table_cols; unsigned_vector m_rel_cols; scoped_ptr m_assembling_join_project; scoped_ptr m_updating_union; public: filter_identical_pairs_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * table_cols, const unsigned * rel_cols) : m_col_cnt(col_cnt), m_table_cols(col_cnt, table_cols), m_rel_cols(col_cnt, rel_cols) { SASSERT(col_cnt>0); const table_signature & tsig = r.m_table_sig; unsigned t_sz = tsig.size(); sort_two_arrays(col_cnt, m_table_cols.begin(), m_rel_cols.begin()); SASSERT(m_table_cols.back() tproj; if(m_tproject_fn) { tproj = (*m_tproject_fn)(r.get_table()); } else { tproj = r.get_table().clone(); } SASSERT(m_col_cnt+1==tproj->get_signature().size()); table_signature filtered_sig = tproj->get_signature(); filtered_sig.push_back(finite_product_relation::s_rel_idx_sort); filtered_sig.set_functional_columns(1); relation_vector new_rels; scoped_rel filtered_table = tplugin.mk_empty(filtered_sig); table_fact f; table_base::iterator pit = tproj->begin(); table_base::iterator pend = tproj->end(); for(; pit!=pend; ++pit) { pit->get_fact(f); unsigned old_rel_idx = static_cast(f.back()); const relation_base & old_rel = r.get_inner_rel(old_rel_idx); relation_base * new_rel = old_rel.clone(); for(unsigned i=0; i filter = rmgr.mk_filter_equal_fn(*new_rel, r_el, m_rel_cols[i]); (*filter)(*new_rel); } if(new_rel->empty()) { new_rel->deallocate(); continue; } unsigned new_rel_idx = new_rels.size(); new_rels.push_back(new_rel); f.push_back(new_rel_idx); filtered_table->add_fact(f); } if(!m_assembling_join_project) { m_assembling_join_project = mk_assembler_of_filter_result(rtable, *filtered_table, m_table_cols); } scoped_rel new_table = (*m_assembling_join_project)(r.get_table(), *filtered_table); r.reset(); r.init(*new_table, new_rels, true); } }; relation_mutator_fn * finite_product_relation_plugin::mk_filter_identical_pairs(const finite_product_relation & r, unsigned col_cnt, const unsigned * table_cols, const unsigned * rel_cols) { return alloc(filter_identical_pairs_fn, r, col_cnt, table_cols, rel_cols); } table_join_fn * finite_product_relation_plugin::mk_assembler_of_filter_result(const table_base & relation_table, const table_base & filtered_table, const unsigned_vector & selected_columns) { table_plugin & tplugin = relation_table.get_plugin(); const table_signature & rtable_sig = relation_table.get_signature(); unsigned rtable_sig_sz = rtable_sig.size(); unsigned selected_col_cnt = selected_columns.size(); SASSERT(selected_col_cnt+2==filtered_table.get_signature().size()); SASSERT(rtable_sig.functional_columns()==1); SASSERT(filtered_table.get_signature().functional_columns()==1); unsigned_vector rtable_joined_cols; rtable_joined_cols.append(selected_col_cnt, selected_columns.c_ptr()); //filtered table cols rtable_joined_cols.push_back(rtable_sig_sz-1); //unfiltered relation indexes unsigned_vector filtered_joined_cols; add_sequence(0, selected_col_cnt, filtered_joined_cols); //filtered table cols filtered_joined_cols.push_back(selected_col_cnt); //unfiltered relation indexes SASSERT(rtable_joined_cols.size()==filtered_joined_cols.size()); //the signature after join: //(all relation table columns)(all filtered relation table columns)(unfiltered rel idx non-func from 'filtered') // (unfiltered rel idx func from 'rtable')(filtered rel idx) unsigned_vector removed_cols; unsigned filtered_nonfunc_ofs = rtable_sig_sz-1; add_sequence(filtered_nonfunc_ofs, selected_col_cnt, removed_cols); //data columns from 'filtered' unsigned idx_ofs = filtered_nonfunc_ofs+selected_col_cnt; removed_cols.push_back(idx_ofs); //unfiltered relation indexes from 'filtered' removed_cols.push_back(idx_ofs+1); //unfiltered relation indexes from rtable table_join_fn * res = tplugin.get_manager().mk_join_project_fn(relation_table, filtered_table, rtable_joined_cols, filtered_joined_cols, removed_cols); SASSERT(res); return res; } // ----------------------------------- // // finite_product_relation // // ----------------------------------- finite_product_relation::finite_product_relation(finite_product_relation_plugin & p, const relation_signature & s, const bool * table_columns, table_plugin & tplugin, relation_plugin & oplugin, family_id other_kind) : relation_base(p, s), m_other_plugin(oplugin), m_other_kind(other_kind), m_full_rel_idx(UINT_MAX) { const relation_signature & rel_sig = get_signature(); unsigned sz = rel_sig.size(); m_sig2table.resize(sz, UINT_MAX); m_sig2other.resize(sz, UINT_MAX); for(unsigned i=0; iclone()), m_others(r.m_others), m_available_rel_indexes(r.m_available_rel_indexes), m_full_rel_idx(r.m_full_rel_idx), m_live_rel_collection_project(), m_empty_rel_removal_filter() { //m_others is now just a shallow copy, we need use clone of the relations that in it now unsigned other_sz = m_others.size(); for(unsigned i=0; ideallocate(); relation_vector::iterator it = m_others.begin(); relation_vector::iterator end = m_others.end(); for(; it!=end; ++it) { relation_base * rel= *it; if(rel) { rel->deallocate(); } } } context & finite_product_relation::get_context() const { return get_manager().get_context(); } unsigned finite_product_relation::get_next_rel_idx() const { unsigned res; if(!m_available_rel_indexes.empty()) { res = m_available_rel_indexes.back(); m_available_rel_indexes.pop_back(); } else { res = m_others.size(); m_others.push_back(0); } SASSERT(m_others[res]==0); return res; } void finite_product_relation::recycle_rel_idx(unsigned idx) const { SASSERT(m_others[idx]==0); m_available_rel_indexes.push_back(idx); } unsigned finite_product_relation::get_full_rel_idx() { if(m_full_rel_idx==UINT_MAX) { m_full_rel_idx = get_next_rel_idx(); relation_base * full_other = mk_full_inner(0); m_others[m_full_rel_idx] = full_other; } return m_full_rel_idx; } void finite_product_relation::init(const table_base & table_vals, const relation_vector & others, bool contiguous) { SASSERT(m_table_sig.equal_up_to_fn_mark(table_vals.get_signature())); SASSERT(empty()); if(!m_others.empty()) { garbage_collect(false); } SASSERT(m_others.empty()); m_others = others; scoped_ptr table_union = get_manager().mk_union_fn(get_table(), table_vals); (*table_union)(get_table(), table_vals); if(!contiguous) { unsigned rel_cnt = m_others.size(); for(unsigned i=0; isuggest_fact(t_f)) { SASSERT(t_f.back()==new_rel_idx); new_rel = mk_empty_inner(); } else { new_rel = get_inner_rel(t_f.back()).clone(); t_f[t_f.size()-1]=new_rel_idx; m_table->ensure_fact(t_f); } new_rel->add_fact(o_f); set_inner_rel(new_rel_idx, new_rel); } bool finite_product_relation::contains_fact(const relation_fact & f) const { table_fact t_f; extract_table_fact(f, t_f); if(!m_table->fetch_fact(t_f)) { return false; } relation_fact o_f(get_context()); extract_other_fact(f, o_f); const relation_base & other = get_inner_rel(t_f.back()); return other.contains_fact(o_f); } bool finite_product_relation::empty() const { garbage_collect(true); return m_table->empty(); } finite_product_relation * finite_product_relation::clone() const { return alloc(finite_product_relation, *this); } void finite_product_relation::complement_self(func_decl* p) { unsigned other_sz = m_others.size(); for(unsigned i=0; icomplement(p); std::swap(m_others[i],r); r->deallocate(); } table_element full_rel_idx = get_full_rel_idx(); scoped_rel complement_table = m_table->complement(p, &full_rel_idx); scoped_ptr u_fn = get_manager().mk_union_fn(*m_table, *complement_table, 0); SASSERT(u_fn); (*u_fn)(*m_table, *complement_table, 0); } finite_product_relation * finite_product_relation::complement(func_decl* p) const { finite_product_relation * res = clone(); res->complement_self(p); return res; } class finite_product_relation::live_rel_collection_reducer : public table_row_pair_reduce_fn { idx_set & m_accumulator; public: live_rel_collection_reducer(idx_set & accumulator) : m_accumulator(accumulator) {} virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { m_accumulator.insert(static_cast(merged_func_columns[0])); } }; void finite_product_relation::collect_live_relation_indexes(idx_set & res) const { SASSERT(res.empty()); unsigned table_data_col_cnt = m_table_sig.size()-1; if(table_data_col_cnt==0) { if(!get_table().empty()) { table_base::iterator iit = get_table().begin(); table_base::iterator iend = get_table().end(); SASSERT(iit!=iend); res.insert(static_cast((*iit)[0])); SASSERT((++iit)==iend); } return; } if(!m_live_rel_collection_project) { buffer removed_cols; removed_cols.resize(table_data_col_cnt); for(unsigned i=0; i live_indexes_table = (*m_live_rel_collection_project)(get_table()); res.swap(m_live_rel_collection_acc); SASSERT(live_indexes_table->get_signature().size()==1); SASSERT(live_indexes_table->get_signature().functional_columns()==1); if(!live_indexes_table->empty()) { table_base::iterator iit = live_indexes_table->begin(); table_base::iterator iend = live_indexes_table->end(); SASSERT(iit!=iend); res.insert(static_cast((*iit)[0])); SASSERT((++iit)==iend); } } void finite_product_relation::garbage_collect(bool remove_empty) const { idx_set live_indexes; collect_live_relation_indexes(live_indexes); scoped_rel empty_rel_indexes; //populated only if \c remove_empty==true table_fact empty_rel_fact; unsigned rel_cnt = m_others.size(); #if Z3DEBUG unsigned encountered_live_indexes = 0; #endif for(unsigned rel_idx=0; rel_idxempty()) { continue; } if(empty_rel_indexes==0) { table_signature empty_rel_indexes_sig; empty_rel_indexes_sig.push_back(s_rel_idx_sort); empty_rel_indexes = get_table_plugin().mk_empty(empty_rel_indexes_sig); } empty_rel_fact.reset(); empty_rel_fact.push_back(rel_idx); empty_rel_indexes->add_fact(empty_rel_fact); } m_others[rel_idx]->deallocate(); m_others[rel_idx] = 0; if(rel_idx==m_full_rel_idx) { m_full_rel_idx = UINT_MAX; } recycle_rel_idx(rel_idx); } SASSERT(encountered_live_indexes==live_indexes.num_elems()); if(m_available_rel_indexes.size()==m_others.size()) { m_available_rel_indexes.reset(); m_others.reset(); } if(empty_rel_indexes) { SASSERT(remove_empty); if(!m_empty_rel_removal_filter) { unsigned t_joined_cols[] = { m_table_sig.size()-1 }; unsigned ei_joined_cols[] = { 0 }; m_empty_rel_removal_filter = get_manager().mk_filter_by_negation_fn(get_table(), *empty_rel_indexes, 1, t_joined_cols, ei_joined_cols); } (*m_empty_rel_removal_filter)(*m_table, *empty_rel_indexes); } } bool finite_product_relation::try_unify_specifications(ptr_vector & rels) { if(rels.empty()) { return true; } unsigned sig_sz = rels.back()->get_signature().size(); svector table_cols(sig_sz, true); ptr_vector::iterator it = rels.begin(); ptr_vector::iterator end = rels.end(); for(; it!=end; ++it) { finite_product_relation & rel = **it; for(unsigned i=0; i pr_fun = get_manager().mk_project_fn(get_table(), to_project_away); table_base * moved_cols_table = (*pr_fun)(get_table()); //gets destroyed inside moved_cols_trel scoped_rel moved_cols_trel = rmgr.get_table_relation_plugin(moved_cols_table->get_plugin()).mk_from_table(moved_cols_sig, moved_cols_table); svector moved_cols_table_flags(moved_cols_sig.size(), false); scoped_rel moved_cols_rel = get_plugin().mk_empty(moved_cols_sig, moved_cols_table_flags.c_ptr()); scoped_ptr union_fun = get_manager().mk_union_fn(*moved_cols_rel, *moved_cols_trel); SASSERT(union_fun); //the table_relation should be able to be 'unioned into' any relation (*union_fun)(*moved_cols_rel, *moved_cols_trel); unsigned_vector all_moved_cols_indexes; add_sequence(0, moved_cols_sig.size(), all_moved_cols_indexes); scoped_ptr join_fun = get_manager().mk_join_project_fn(*this, *moved_cols_rel, new_rel_columns, all_moved_cols_indexes, new_rel_columns, false); scoped_rel unordered_rel = (*join_fun)(*this, *moved_cols_rel); SASSERT(unordered_rel->get_signature().size()==sig_sz); //the signature size should be the same as original //now we need to reorder the columns in the \c new_rel to match the original table unsigned_vector permutation; unsigned moved_cols_cnt = new_rel_columns.size(); unsigned next_replaced_idx = 0; unsigned next_orig_idx = 0; for(unsigned i=0; i perm_fun = get_manager().mk_rename_fn(*unordered_rel, cycle); //the scoped_rel wrapper does the destruction of the old object unordered_rel = (*perm_fun)(*unordered_rel); cycle.reset(); } finite_product_relation & new_rel = finite_product_relation_plugin::get(*unordered_rel); //Swap the content of the current object and new_rel. On exitting the function new_rel will be destroyed //since it points to the content of scoped_rel unordered_rel. swap(new_rel); return true; } void finite_product_relation::display(std::ostream & out) const { garbage_collect(true); out << "finite_product_relation:\n"; out << " table:\n"; get_table().display(out); unsigned other_sz = m_others.size(); for(unsigned i=0; iget_fact(tfact); const table_relation & orel = static_cast(get_inner_rel(tfact[rel_idx_col])); const table_base & otable = orel.get_table(); table_base::iterator oit = otable.begin(); table_base::iterator oend = otable.end(); for(; oit!=oend; ++oit) { oit->get_fact(ofact); out << "\t("; for(unsigned i=0; iget_fact(fact); conjs.reset(); SASSERT(fact.size() == fact_sz); unsigned rel_idx = static_cast(fact[fact_sz-1]); m_others[rel_idx]->to_formula(tmp); for (unsigned i = 0; i + 1 < fact_sz; ++i) { conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), util.mk_numeral(fact[i], sig[i]))); } sh(tmp, fact_sz-1, tmp); conjs.push_back(tmp); disjs.push_back(m.mk_and(conjs.size(), conjs.c_ptr())); } bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), fml); } }; z3-4.4.0/src/muz/rel/dl_relation_manager.cpp0000644000175000017500000020761712540347414020540 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_relation_manager.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #include #include"ast_pp.h" #include"dl_check_table.h" #include"dl_context.h" #include"dl_finite_product_relation.h" #include"dl_product_relation.h" #include"dl_sieve_relation.h" #include"dl_table_relation.h" #include"dl_relation_manager.h" namespace datalog { relation_manager::~relation_manager() { reset(); } void relation_manager::reset_relations() { relation_map::iterator it=m_relations.begin(); relation_map::iterator end=m_relations.end(); for(;it!=end;++it) { func_decl * pred = it->m_key; get_context().get_manager().dec_ref(pred); //inc_ref in get_relation relation_base * r=(*it).m_value; r->deallocate(); } m_relations.reset(); } void relation_manager::reset() { reset_relations(); m_favourite_table_plugin = static_cast(0); m_favourite_relation_plugin = static_cast(0); dealloc_ptr_vector_content(m_table_plugins); m_table_plugins.reset(); dealloc_ptr_vector_content(m_relation_plugins); m_relation_plugins.reset(); m_next_table_fid = 0; m_next_relation_fid = 0; } dl_decl_util & relation_manager::get_decl_util() const { return get_context().get_decl_util(); } family_id relation_manager::get_next_relation_fid(relation_plugin & claimer) { unsigned res = m_next_relation_fid++; m_kind2plugin.insert(res, &claimer); return res; } void relation_manager::set_predicate_kind(func_decl * pred, family_id kind) { SASSERT(!m_relations.contains(pred)); m_pred_kinds.insert(pred, kind); } family_id relation_manager::get_requested_predicate_kind(func_decl * pred) { family_id res; if(m_pred_kinds.find(pred, res)) { return res; } else { return null_family_id; } } relation_base & relation_manager::get_relation(func_decl * pred) { relation_base * res = try_get_relation(pred); if(!res) { relation_signature sig; from_predicate(pred, sig); family_id rel_kind = get_requested_predicate_kind(pred); res = mk_empty_relation(sig, rel_kind); store_relation(pred, res); } return *res; } relation_base * relation_manager::try_get_relation(func_decl * pred) const { relation_base * res = 0; if(!m_relations.find(pred, res)) { return 0; } SASSERT(res); return res; } void relation_manager::store_relation(func_decl * pred, relation_base * rel) { SASSERT(rel); relation_map::obj_map_entry * e = m_relations.insert_if_not_there2(pred, 0); if (e->get_data().m_value) { e->get_data().m_value->deallocate(); } else { get_context().get_manager().inc_ref(pred); //dec_ref in reset } e->get_data().m_value = rel; } void relation_manager::collect_non_empty_predicates(decl_set & res) const { relation_map::iterator it = m_relations.begin(); relation_map::iterator end = m_relations.end(); for(; it!=end; ++it) { if(!it->m_value->fast_empty()) { res.insert(it->m_key); } } } void relation_manager::restrict_predicates(const decl_set & preds) { typedef ptr_vector fd_vector; fd_vector to_remove; relation_map::iterator rit = m_relations.begin(); relation_map::iterator rend = m_relations.end(); for(; rit!=rend; ++rit) { func_decl * pred = rit->m_key; if (!preds.contains(pred)) { to_remove.insert(pred); } } fd_vector::iterator pit = to_remove.begin(); fd_vector::iterator pend = to_remove.end(); for(; pit!=pend; ++pit) { func_decl * pred = *pit; relation_base * rel; VERIFY( m_relations.find(pred, rel) ); rel->deallocate(); m_relations.remove(pred); get_context().get_manager().dec_ref(pred); } set_intersection(m_saturated_rels, preds); } void relation_manager::register_plugin(table_plugin * plugin) { plugin->initialize(get_next_table_fid()); m_table_plugins.push_back(plugin); if(plugin->get_name()==get_context().default_table()) { m_favourite_table_plugin = plugin; } table_relation_plugin * tr_plugin = alloc(table_relation_plugin, *plugin, *this); register_relation_plugin_impl(tr_plugin); m_table_relation_plugins.insert(plugin, tr_plugin); if (plugin->get_name()==get_context().default_table()) { m_favourite_table_plugin = plugin; m_favourite_relation_plugin = tr_plugin; } symbol checker_name = get_context().default_table_checker(); if (get_context().default_table_checked() && get_table_plugin(checker_name)) { if( m_favourite_table_plugin && (plugin==m_favourite_table_plugin || plugin->get_name()==checker_name) ) { symbol checked_name = get_context().default_table(); //the plugins we need to create the checking plugin were just added SASSERT(m_favourite_table_plugin->get_name()==get_context().default_table()); table_plugin * checking_plugin = alloc(check_table_plugin, *this, checker_name, checked_name); register_plugin(checking_plugin); m_favourite_table_plugin = checking_plugin; } if (m_favourite_relation_plugin && m_favourite_relation_plugin->from_table()) { table_relation_plugin * fav_rel_plugin = static_cast(m_favourite_relation_plugin); if(&fav_rel_plugin->get_table_plugin()==plugin || plugin->get_name()==checker_name) { //the plugins we need to create the checking table_relation_plugin were just added SASSERT(m_favourite_relation_plugin->get_name() == get_context().default_relation()); symbol checked_name = fav_rel_plugin->get_table_plugin().get_name(); table_plugin * checking_plugin = alloc(check_table_plugin, *this, checker_name, checked_name); register_plugin(checking_plugin); table_relation_plugin * checking_tr_plugin = alloc(table_relation_plugin, *checking_plugin, *this); register_relation_plugin_impl(checking_tr_plugin); m_table_relation_plugins.insert(checking_plugin, checking_tr_plugin); m_favourite_relation_plugin = checking_tr_plugin; } } } } void relation_manager::register_relation_plugin_impl(relation_plugin * plugin) { TRACE("dl", tout << "register: " << plugin->get_name() << "\n";); m_relation_plugins.push_back(plugin); plugin->initialize(get_next_relation_fid(*plugin)); if (plugin->get_name() == get_context().default_relation()) { m_favourite_relation_plugin = plugin; } if(plugin->is_finite_product_relation()) { finite_product_relation_plugin * fprp = static_cast(plugin); relation_plugin * inner = &fprp->get_inner_plugin(); m_finite_product_relation_plugins.insert(inner, fprp); } } relation_plugin * relation_manager::try_get_appropriate_plugin(const relation_signature & s) { if(m_favourite_relation_plugin && m_favourite_relation_plugin->can_handle_signature(s)) { return m_favourite_relation_plugin; } relation_plugin_vector::iterator rpit = m_relation_plugins.begin(); relation_plugin_vector::iterator rpend = m_relation_plugins.end(); for(; rpit!=rpend; ++rpit) { if((*rpit)->can_handle_signature(s)) { return *rpit; } } return 0; } relation_plugin & relation_manager::get_appropriate_plugin(const relation_signature & s) { relation_plugin * res = try_get_appropriate_plugin(s); if (!res) { throw default_exception("no suitable plugin found for given relation signature"); } return *res; } table_plugin * relation_manager::try_get_appropriate_plugin(const table_signature & t) { if (m_favourite_table_plugin && m_favourite_table_plugin->can_handle_signature(t)) { return m_favourite_table_plugin; } table_plugin_vector::iterator tpit = m_table_plugins.begin(); table_plugin_vector::iterator tpend = m_table_plugins.end(); for(; tpit!=tpend; ++tpit) { if((*tpit)->can_handle_signature(t)) { return *tpit; } } return 0; } table_plugin & relation_manager::get_appropriate_plugin(const table_signature & t) { table_plugin * res = try_get_appropriate_plugin(t); if(!res) { throw default_exception("no suitable plugin found for given table signature"); } return *res; } relation_plugin * relation_manager::get_relation_plugin(symbol const& s) { relation_plugin_vector::iterator rpit = m_relation_plugins.begin(); relation_plugin_vector::iterator rpend = m_relation_plugins.end(); for(; rpit!=rpend; ++rpit) { if((*rpit)->get_name()==s) { return *rpit; } } return 0; } relation_plugin & relation_manager::get_relation_plugin(family_id kind) { SASSERT(kind>=0); SASSERT(kindget_name()==k) { return *tpit; } } return 0; } table_relation_plugin & relation_manager::get_table_relation_plugin(table_plugin & tp) { table_relation_plugin * res; VERIFY( m_table_relation_plugins.find(&tp, res) ); return *res; } bool relation_manager::try_get_finite_product_relation_plugin(const relation_plugin & inner, finite_product_relation_plugin * & res) { return m_finite_product_relation_plugins.find(&inner, res); } table_base * relation_manager::mk_empty_table(const table_signature & s) { return get_appropriate_plugin(s).mk_empty(s); } bool relation_manager::is_non_explanation(relation_signature const& s) const { dl_decl_util & decl_util = get_context().get_decl_util(); unsigned n = s.size(); for(unsigned i = 0; i < n; i++) { if(decl_util.is_rule_sort(s[i])) { return false; } } return true; } relation_base * relation_manager::mk_empty_relation(const relation_signature & s, func_decl* pred) { return mk_empty_relation(s, get_requested_predicate_kind(pred)); } relation_base * relation_manager::mk_empty_relation(const relation_signature & s, family_id kind) { if (kind != null_family_id) { relation_plugin & plugin = get_relation_plugin(kind); if (plugin.can_handle_signature(s, kind)) return plugin.mk_empty(s, kind); } relation_base * res; relation_plugin* p = m_favourite_relation_plugin; if (p && p->can_handle_signature(s)) { return p->mk_empty(s); } if (mk_empty_table_relation(s, res)) { return res; } for (unsigned i = 0; i < m_relation_plugins.size(); ++i) { p = m_relation_plugins[i]; if (p->can_handle_signature(s)) { return p->mk_empty(s); } } //If there is no plugin to handle the signature, we just create an empty product relation and //stuff will be added to it by later operations. TRACE("dl", s.output(get_context().get_manager(), tout << "empty product relation"); tout << "\n";); return product_relation_plugin::get_plugin(*this).mk_empty(s); } /** The newly created object takes ownership of the \c table object. */ relation_base * relation_manager::mk_table_relation(const relation_signature & s, table_base * table) { SASSERT(s.size()==table->get_signature().size()); return get_table_relation_plugin(table->get_plugin()).mk_from_table(s, table); } bool relation_manager::mk_empty_table_relation(const relation_signature & s, relation_base * & result) { table_signature tsig; if(!relation_signature_to_table(s, tsig)) { return false; } table_base * table = mk_empty_table(tsig); result = mk_table_relation(s, table); return true; } relation_base * relation_manager::mk_full_relation(const relation_signature & s, func_decl* p, family_id kind) { if (kind != null_family_id) { relation_plugin & plugin = get_relation_plugin(kind); if (plugin.can_handle_signature(s, kind)) { return plugin.mk_full(p, s, kind); } } return get_appropriate_plugin(s).mk_full(p, s, null_family_id); } relation_base * relation_manager::mk_full_relation(const relation_signature & s, func_decl* pred) { family_id kind = get_requested_predicate_kind(pred); return mk_full_relation(s, pred, kind); } void relation_manager::relation_to_table(const relation_sort & sort, const relation_element & from, table_element & to) { SASSERT(from->get_num_args()==0); VERIFY(get_context().get_decl_util().is_numeral_ext(from, to)); } void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, relation_element & to) { to = get_decl_util().mk_numeral(from, sort); } void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, relation_element_ref & to) { relation_element rel_el; table_to_relation(sort, from, rel_el); to = rel_el; } void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, const relation_fact::el_proxy & to) { relation_element rel_el; table_to_relation(sort, from, rel_el); to = rel_el; } bool relation_manager::relation_sort_to_table(const relation_sort & from, table_sort & to) { return get_context().get_decl_util().try_get_size(from, to); } void relation_manager::from_predicate(func_decl * pred, unsigned arg_index, relation_sort & result) { result = pred->get_domain(arg_index); } void relation_manager::from_predicate(func_decl * pred, relation_signature & result) { result.reset(); unsigned arg_num=pred->get_arity(); for(unsigned i=0;iset_cancel(f); } } std::string relation_manager::to_nice_string(const relation_element & el) const { uint64 val; std::stringstream stm; if(get_context().get_decl_util().is_numeral_ext(el, val)) { stm << val; } else { stm << mk_pp(el, get_context().get_manager()); } return stm.str(); } std::string relation_manager::to_nice_string(const relation_sort & s, const relation_element & el) const { std::stringstream stm; uint64 val; if(get_context().get_decl_util().is_numeral_ext(el, val)) { get_context().print_constant_name(s, val, stm); } else { stm << mk_pp(el, get_context().get_manager()); } return stm.str(); } std::string relation_manager::to_nice_string(const relation_sort & s) const { return std::string(s->get_name().bare_str()); } std::string relation_manager::to_nice_string(const relation_signature & s) const { std::string res("["); bool first = true; relation_signature::const_iterator it = s.begin(); relation_signature::const_iterator end = s.end(); for(; it!=end; ++it) { if(first) { first = false; } else { res+=','; } res+=to_nice_string(*it); } res+=']'; return res; } void relation_manager::display(std::ostream & out) const { relation_map::iterator it=m_relations.begin(); relation_map::iterator end=m_relations.end(); for(;it!=end;++it) { out << "Table " << it->m_key->get_name() << "\n"; it->m_value->display(out); } } void relation_manager::display_relation_sizes(std::ostream & out) const { relation_map::iterator it=m_relations.begin(); relation_map::iterator end=m_relations.end(); for(;it!=end;++it) { out << "Relation " << it->m_key->get_name() << " has size " << it->m_value->get_size_estimate_rows() << "\n"; } } void relation_manager::display_output_tables(rule_set const& rules, std::ostream & out) const { const decl_set & output_preds = rules.get_output_predicates(); decl_set::iterator it=output_preds.begin(); decl_set::iterator end=output_preds.end(); for(; it!=end; ++it) { func_decl * pred = *it; relation_base * rel = try_get_relation(pred); if (!rel) { out << "Tuples in " << pred->get_name() << ": \n"; continue; } rel->display_tuples(*pred, out); } } // ----------------------------------- // // relation operations // // ----------------------------------- class relation_manager::empty_signature_relation_join_fn : public relation_join_fn { public: virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { TRACE("dl", tout << r1.get_plugin().get_name() << " " << r2.get_plugin().get_name() << "\n";); if(r1.get_signature().empty()) { if(r1.empty()) { return r2.get_manager().mk_empty_relation(r2.get_signature(), r2.get_kind()); } else { return r2.clone(); } } else { SASSERT(r2.get_signature().empty()); if(r2.empty()) { return r1.get_manager().mk_empty_relation(r1.get_signature(), r1.get_kind()); } else { return r1.clone(); } } } }; relation_join_fn * relation_manager::mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, bool allow_product_relation) { relation_plugin * p1 = &t1.get_plugin(); relation_plugin * p2 = &t2.get_plugin(); relation_join_fn * res = p1->mk_join_fn(t1, t2, col_cnt, cols1, cols2); if(!res && p1!=p2) { res = p2->mk_join_fn(t1, t2, col_cnt, cols1, cols2); } if(!res && (t1.get_signature().empty() || t2.get_signature().empty())) { res = alloc(empty_signature_relation_join_fn); } finite_product_relation_plugin * fprp; if(!res && p1->from_table() && try_get_finite_product_relation_plugin(*p2, fprp)) { //we downcast here to relation_plugin so that we don't have to declare //relation_manager as a friend class of finite_product_relation_plugin res = static_cast(fprp)->mk_join_fn(t1, t2, col_cnt, cols1, cols2); } if(!res && p2->from_table() && try_get_finite_product_relation_plugin(*p1, fprp)) { res = static_cast(fprp)->mk_join_fn(t1, t2, col_cnt, cols1, cols2); } if(!res && allow_product_relation) { relation_plugin & product_plugin = product_relation_plugin::get_plugin(*this); res = product_plugin.mk_join_fn(t1, t2, col_cnt, cols1, cols2); } return res; } relation_transformer_fn * relation_manager::mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) { return t.get_plugin().mk_project_fn(t, col_cnt, removed_cols); } class relation_manager::default_relation_filter_interpreted_and_project_fn : public relation_transformer_fn { scoped_ptr m_filter; scoped_ptr m_project; unsigned_vector m_removed_cols; public: /** This constructor should be used only if we know that the projection operation exists for the result of the join. */ default_relation_filter_interpreted_and_project_fn( relation_mutator_fn* filter, unsigned removed_col_cnt, const unsigned * removed_cols) : m_filter(filter), m_project(0), m_removed_cols(removed_col_cnt, removed_cols) {} virtual relation_base * operator()(const relation_base & t) { scoped_rel t1 = t.clone(); (*m_filter)(*t1); if( !m_project) { relation_manager & rmgr = t1->get_plugin().get_manager(); m_project = rmgr.mk_project_fn(*t1, m_removed_cols.size(), m_removed_cols.c_ptr()); if (!m_project) { throw default_exception("projection does not exist"); } } return (*m_project)(*t1); } }; relation_transformer_fn * relation_manager::mk_filter_interpreted_and_project_fn( const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { relation_transformer_fn* res = t.get_plugin().mk_filter_interpreted_and_project_fn( t, condition, removed_col_cnt, removed_cols); if (!res) { relation_mutator_fn* filter_fn = mk_filter_interpreted_fn(t, condition); if (filter_fn) { res = alloc(default_relation_filter_interpreted_and_project_fn, filter_fn, removed_col_cnt, removed_cols); } } return res; } class relation_manager::default_relation_apply_sequential_fn : public relation_mutator_fn { ptr_vector m_mutators; public: default_relation_apply_sequential_fn(unsigned n, relation_mutator_fn ** mutators): m_mutators(n, mutators) { } virtual ~default_relation_apply_sequential_fn() { std::for_each(m_mutators.begin(), m_mutators.end(), delete_proc()); } virtual void operator()(relation_base& t) { for (unsigned i = 0; i < m_mutators.size(); ++i) { if (t.empty()) return; (*(m_mutators[i]))(t); } } }; relation_mutator_fn * relation_manager::mk_apply_sequential_fn(unsigned n, relation_mutator_fn ** mutators) { return alloc(default_relation_apply_sequential_fn, n, mutators); } class relation_manager::default_relation_join_project_fn : public relation_join_fn { scoped_ptr m_join; scoped_ptr m_project; unsigned_vector m_removed_cols; public: /** This constructor should be used only if we know that the projection operation exists for the result of the join. */ default_relation_join_project_fn(join_fn * join, unsigned removed_col_cnt, const unsigned * removed_cols) : m_join(join), m_project(0), m_removed_cols(removed_col_cnt, removed_cols) {} virtual relation_base * operator()(const relation_base & t1, const relation_base & t2) { scoped_rel aux = (*m_join)(t1, t2); if(!m_project) { relation_manager & rmgr = aux->get_plugin().get_manager(); m_project = rmgr.mk_project_fn(*aux, m_removed_cols.size(), m_removed_cols.c_ptr()); if(!m_project) { throw default_exception("projection does not exist"); } } relation_base * res = (*m_project)(*aux); return res; } }; relation_join_fn * relation_manager::mk_join_project_fn(const relation_base & t1, const relation_base & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, bool allow_product_relation_join) { relation_join_fn * res = t1.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); if(!res && &t1.get_plugin()!=&t2.get_plugin()) { res = t2.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } if(!res) { relation_join_fn * join = mk_join_fn(t1, t2, joined_col_cnt, cols1, cols2, allow_product_relation_join); if(join) { res = alloc(default_relation_join_project_fn, join, removed_col_cnt, removed_cols); } } return res; } relation_transformer_fn * relation_manager::mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { return t.get_plugin().mk_rename_fn(t, permutation_cycle_len, permutation_cycle); } relation_transformer_fn * relation_manager::mk_permutation_rename_fn(const relation_base & t, const unsigned * permutation) { relation_transformer_fn * res = t.get_plugin().mk_permutation_rename_fn(t, permutation); if(!res) { res = alloc(default_relation_permutation_rename_fn, t, permutation); } return res; } relation_union_fn * relation_manager::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { relation_union_fn * res = tgt.get_plugin().mk_union_fn(tgt, src, delta); if(!res && &tgt.get_plugin()!=&src.get_plugin()) { res = src.get_plugin().mk_union_fn(tgt, src, delta); } if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { res = delta->get_plugin().mk_union_fn(tgt, src, delta); } // TRACE("dl", tout << src.get_plugin().get_name() << " " << tgt.get_plugin().get_name() << " " << (res?"created":"not created") << "\n";); return res; } relation_union_fn * relation_manager::mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { relation_union_fn * res = tgt.get_plugin().mk_widen_fn(tgt, src, delta); if(!res && &tgt.get_plugin()!=&src.get_plugin()) { res = src.get_plugin().mk_widen_fn(tgt, src, delta); } if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { res = delta->get_plugin().mk_widen_fn(tgt, src, delta); } if(!res) { res = mk_union_fn(tgt, src, delta); } return res; } relation_mutator_fn * relation_manager::mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { return t.get_plugin().mk_filter_identical_fn(t, col_cnt, identical_cols); } relation_mutator_fn * relation_manager::mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col) { return t.get_plugin().mk_filter_equal_fn(t, value, col); } relation_mutator_fn * relation_manager::mk_filter_interpreted_fn(const relation_base & t, app * condition) { return t.get_plugin().mk_filter_interpreted_fn(t, condition); } class relation_manager::default_relation_select_equal_and_project_fn : public relation_transformer_fn { scoped_ptr m_filter; scoped_ptr m_project; public: default_relation_select_equal_and_project_fn(relation_mutator_fn * filter, relation_transformer_fn * project) : m_filter(filter), m_project(project) {} virtual relation_base * operator()(const relation_base & t1) { TRACE("dl", tout << t1.get_plugin().get_name() << "\n";); scoped_rel aux = t1.clone(); (*m_filter)(*aux); relation_base * res = (*m_project)(*aux); return res; } }; relation_transformer_fn * relation_manager::mk_select_equal_and_project_fn(const relation_base & t, const relation_element & value, unsigned col) { relation_transformer_fn * res = t.get_plugin().mk_select_equal_and_project_fn(t, value, col); if(!res) { relation_mutator_fn * selector = mk_filter_equal_fn(t, value, col); if(selector) { relation_transformer_fn * projector = mk_project_fn(t, 1, &col); if(projector) { res = alloc(default_relation_select_equal_and_project_fn, selector, projector); } else { dealloc(selector); } } } return res; } class relation_manager::default_relation_intersection_filter_fn : public relation_intersection_filter_fn { scoped_ptr m_join_fun; scoped_ptr m_union_fun; public: default_relation_intersection_filter_fn(relation_join_fn * join_fun, relation_union_fn * union_fun) : m_join_fun(join_fun), m_union_fun(union_fun) {} virtual void operator()(relation_base & tgt, const relation_base & intersected_obj) { scoped_rel filtered_rel = (*m_join_fun)(tgt, intersected_obj); TRACE("dl", tgt.display(tout << "tgt:\n"); intersected_obj.display(tout << "intersected:\n"); filtered_rel->display(tout << "filtered:\n"); ); if(!m_union_fun) { SASSERT(tgt.can_swap(*filtered_rel)); tgt.swap(*filtered_rel); } tgt.reset(); TRACE("dl", tgt.display(tout << "target reset:\n"); ); (*m_union_fun)(tgt, *filtered_rel); TRACE("dl", tgt.display(tout << "intersected target:\n"); ); } }; relation_intersection_filter_fn * relation_manager::try_mk_default_filter_by_intersection_fn( const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt, const unsigned * tgt_cols, const unsigned * src_cols) { TRACE("dl_verbose", tout << tgt.get_plugin().get_name() << "\n";); unsigned_vector join_removed_cols; add_sequence(tgt.get_signature().size(), src.get_signature().size(), join_removed_cols); scoped_rel join_fun = mk_join_project_fn(tgt, src, joined_col_cnt, tgt_cols, src_cols, join_removed_cols.size(), join_removed_cols.c_ptr(), false); if(!join_fun) { return 0; } //we perform the join operation here to see what the result is scoped_rel join_res = (*join_fun)(tgt, src); if(tgt.can_swap(*join_res)) { return alloc(default_relation_intersection_filter_fn, join_fun.release(), 0); } if(join_res->get_plugin().is_product_relation()) { //we cannot have the product relation here, since it uses the intersection operation //for unions and therefore we would get into an infinite recursion return 0; } scoped_rel union_fun = mk_union_fn(tgt, *join_res); if(!union_fun) { return 0; } return alloc(default_relation_intersection_filter_fn, join_fun.release(), union_fun.release()); } relation_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const relation_base & t, const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) { TRACE("dl_verbose", tout << t.get_plugin().get_name() << "\n";); relation_intersection_filter_fn * res = t.get_plugin().mk_filter_by_intersection_fn(t, src, joined_col_cnt, t_cols, src_cols); if(!res && &t.get_plugin()!=&src.get_plugin()) { res = src.get_plugin().mk_filter_by_intersection_fn(t, src, joined_col_cnt, t_cols, src_cols); } if(!res) { res = try_mk_default_filter_by_intersection_fn(t, src, joined_col_cnt, t_cols, src_cols); } return res; } relation_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const relation_base & tgt, const relation_base & src) { TRACE("dl_verbose", tout << tgt.get_plugin().get_name() << "\n";); SASSERT(tgt.get_signature()==src.get_signature()); unsigned sz = tgt.get_signature().size(); unsigned_vector cols; add_sequence(0, sz, cols); return mk_filter_by_intersection_fn(tgt, src, cols, cols); } relation_intersection_filter_fn * relation_manager::mk_filter_by_negation_fn(const relation_base & t, const relation_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { TRACE("dl", tout << t.get_plugin().get_name() << "\n";); relation_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, t_cols, negated_cols); if(!res && &t.get_plugin()!=&negated_obj.get_plugin()) { res = negated_obj.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, t_cols, negated_cols); } return res; } // ----------------------------------- // // table operations // // ----------------------------------- class relation_manager::default_table_join_fn : public convenient_table_join_fn { unsigned m_col_cnt; public: default_table_join_fn(const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_table_join_fn(t1_sig, t2_sig, col_cnt, cols1, cols2), m_col_cnt(col_cnt) {} virtual table_base * operator()(const table_base & t1, const table_base & t2) { table_plugin * plugin = &t1.get_plugin(); const table_signature & res_sign = get_result_signature(); if (!plugin->can_handle_signature(res_sign)) { plugin = &t2.get_plugin(); if (!plugin->can_handle_signature(res_sign)) { plugin = &t1.get_manager().get_appropriate_plugin(res_sign); } } SASSERT(plugin->can_handle_signature(res_sign)); table_base * res = plugin->mk_empty(res_sign); unsigned t1cols = t1.get_signature().size(); unsigned t2cols = t2.get_signature().size(); unsigned t1first_func = t1.get_signature().first_functional(); unsigned t2first_func = t2.get_signature().first_functional(); table_base::iterator els1it = t1.begin(); table_base::iterator els1end = t1.end(); table_base::iterator els2end = t2.end(); table_fact acc; for(; els1it!=els1end; ++els1it) { const table_base::row_interface & row1 = *els1it; table_base::iterator els2it = t2.begin(); for(; els2it!=els2end; ++els2it) { const table_base::row_interface & row2 = *els2it; bool match=true; for(unsigned i=0; iadd_fact(acc); } } return res; } }; table_join_fn * relation_manager::mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { table_join_fn * res = t1.get_plugin().mk_join_fn(t1, t2, col_cnt, cols1, cols2); if(!res && &t1.get_plugin()!=&t2.get_plugin()) { res = t2.get_plugin().mk_join_fn(t1, t2, col_cnt, cols1, cols2); } if(!res) { table_signature sig; table_signature::from_join(t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, sig); res = alloc(default_table_join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); } return res; } table_min_fn * relation_manager::mk_min_fn(const table_base & t, unsigned_vector & group_by_cols, const unsigned col) { return t.get_plugin().mk_min_fn(t, group_by_cols, col); } class relation_manager::auxiliary_table_transformer_fn { table_fact m_row; public: virtual ~auxiliary_table_transformer_fn() {} virtual const table_signature & get_result_signature() const = 0; virtual void modify_fact(table_fact & f) const = 0; table_base * operator()(const table_base & t) { table_plugin & plugin = t.get_plugin(); const table_signature & res_sign = get_result_signature(); SASSERT(plugin.can_handle_signature(res_sign)); table_base * res = plugin.mk_empty(res_sign); table_base::iterator it = t.begin(); table_base::iterator end = t.end(); for(; it!=end; ++it) { it->get_fact(m_row); modify_fact(m_row); res->add_fact(m_row); } return res; } }; class relation_manager::default_table_project_fn : public convenient_table_project_fn, auxiliary_table_transformer_fn { public: default_table_project_fn(const table_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_table_project_fn(orig_sig, removed_col_cnt, removed_cols) { SASSERT(removed_col_cnt>0); } virtual const table_signature & get_result_signature() const { return convenient_table_project_fn::get_result_signature(); } virtual void modify_fact(table_fact & f) const { project_out_vector_columns(f, m_removed_cols); } virtual table_base * operator()(const table_base & t) { return auxiliary_table_transformer_fn::operator()(t); } }; class relation_manager::null_signature_table_project_fn : public table_transformer_fn { const table_signature m_empty_sig; public: null_signature_table_project_fn() : m_empty_sig() {} virtual table_base * operator()(const table_base & t) { relation_manager & m = t.get_plugin().get_manager(); table_base * res = m.mk_empty_table(m_empty_sig); if(!t.empty()) { table_fact el; res->add_fact(el); } return res; } }; table_transformer_fn * relation_manager::mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { table_transformer_fn * res = t.get_plugin().mk_project_fn(t, col_cnt, removed_cols); if(!res && col_cnt==t.get_signature().size()) { //all columns are projected out res = alloc(null_signature_table_project_fn); } if(!res) { res = alloc(default_table_project_fn, t.get_signature(), col_cnt, removed_cols); } return res; } class relation_manager::default_table_join_project_fn : public convenient_table_join_project_fn { scoped_ptr m_join; scoped_ptr m_project; unsigned_vector m_removed_cols; public: default_table_join_project_fn(join_fn * join, const table_base & t1, const table_base & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_table_join_project_fn(t1.get_signature(), t2.get_signature(), joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols), m_join(join), m_removed_cols(removed_col_cnt, removed_cols) {} class unreachable_reducer : public table_row_pair_reduce_fn { virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { //we do project_with_reduce only if we are sure there will be no reductions //(see code of the table_signature::from_join_project function) UNREACHABLE(); } }; virtual table_base * operator()(const table_base & t1, const table_base & t2) { table_base * aux = (*m_join)(t1, t2); if(m_project==0) { relation_manager & rmgr = aux->get_plugin().get_manager(); if(get_result_signature().functional_columns()!=0) { //to preserve functional columns we need to do the project_with_reduction unreachable_reducer * reducer = alloc(unreachable_reducer); m_project = rmgr.mk_project_with_reduce_fn(*aux, m_removed_cols.size(), m_removed_cols.c_ptr(), reducer); } else { m_project = rmgr.mk_project_fn(*aux, m_removed_cols); } if(!m_project) { throw default_exception("projection for table does not exist"); } } table_base * res = (*m_project)(*aux); aux->deallocate(); return res; } }; table_join_fn * relation_manager::mk_join_project_fn(const table_base & t1, const table_base & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { table_join_fn * res = t1.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); if(!res && &t1.get_plugin()!=&t2.get_plugin()) { res = t2.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } if(!res) { table_join_fn * join = mk_join_fn(t1, t2, joined_col_cnt, cols1, cols2); if(join) { res = alloc(default_table_join_project_fn, join, t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } } return res; } class relation_manager::default_table_rename_fn : public convenient_table_rename_fn, auxiliary_table_transformer_fn { public: default_table_rename_fn(const table_signature & orig_sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { SASSERT(permutation_cycle_len>=2); } virtual const table_signature & get_result_signature() const { return convenient_table_rename_fn::get_result_signature(); } virtual void modify_fact(table_fact & f) const { permutate_by_cycle(f, m_cycle); } virtual table_base * operator()(const table_base & t) { return auxiliary_table_transformer_fn::operator()(t); } }; table_transformer_fn * relation_manager::mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { table_transformer_fn * res = t.get_plugin().mk_rename_fn(t, permutation_cycle_len, permutation_cycle); if(!res) { res = alloc(default_table_rename_fn, t.get_signature(), permutation_cycle_len, permutation_cycle); } return res; } table_transformer_fn * relation_manager::mk_permutation_rename_fn(const table_base & t, const unsigned * permutation) { table_transformer_fn * res = t.get_plugin().mk_permutation_rename_fn(t, permutation); if(!res) { res = alloc(default_table_permutation_rename_fn, t, permutation); } return res; } class relation_manager::default_table_union_fn : public table_union_fn { table_fact m_row; public: virtual void operator()(table_base & tgt, const table_base & src, table_base * delta) { table_base::iterator it = src.begin(); table_base::iterator iend = src.end(); for(; it!=iend; ++it) { it->get_fact(m_row); if(delta) { if(!tgt.contains_fact(m_row)) { tgt.add_new_fact(m_row); delta->add_fact(m_row); } } else { //if there's no delta, we don't need to know whether we are actually adding a new fact tgt.add_fact(m_row); } } } }; table_union_fn * relation_manager::mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) { table_union_fn * res = tgt.get_plugin().mk_union_fn(tgt, src, delta); if(!res && &tgt.get_plugin()!=&src.get_plugin()) { res = src.get_plugin().mk_union_fn(tgt, src, delta); } if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { res = delta->get_plugin().mk_union_fn(tgt, src, delta); } if(!res) { res = alloc(default_table_union_fn); } return res; } table_union_fn * relation_manager::mk_widen_fn(const table_base & tgt, const table_base & src, const table_base * delta) { table_union_fn * res = tgt.get_plugin().mk_widen_fn(tgt, src, delta); if(!res && &tgt.get_plugin()!=&src.get_plugin()) { res = src.get_plugin().mk_widen_fn(tgt, src, delta); } if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { res = delta->get_plugin().mk_widen_fn(tgt, src, delta); } if(!res) { res = mk_union_fn(tgt, src, delta); } return res; } /** An auixiliary class for functors that perform filtering. It performs the table traversal and only asks for each individual row whether it should be removed. When using this class in multiple inheritance, this class should not be inherited publicly and should be mentioned as last. This should ensure that deteletion of the object will go well when initiated from a pointer to the first ancestor. */ class relation_manager::auxiliary_table_filter_fn { table_fact m_row; svector m_to_remove; public: virtual ~auxiliary_table_filter_fn() {} virtual bool should_remove(const table_fact & f) const = 0; void operator()(table_base & r) { m_to_remove.reset(); unsigned sz = 0; table_base::iterator it = r.begin(); table_base::iterator iend = r.end(); for(; it!=iend; ++it) { it->get_fact(m_row); if(should_remove(m_row)) { m_to_remove.append(m_row.size(), m_row.c_ptr()); ++sz; } } r.remove_facts(sz, m_to_remove.c_ptr()); } }; class relation_manager::default_table_filter_identical_fn : public table_mutator_fn, auxiliary_table_filter_fn { const unsigned m_col_cnt; const unsigned_vector m_identical_cols; public: default_table_filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) : m_col_cnt(col_cnt), m_identical_cols(col_cnt, identical_cols) { SASSERT(col_cnt>=2); } virtual bool should_remove(const table_fact & f) const { table_element val=f[m_identical_cols[0]]; for(unsigned i=1; iget_arg(0); if (!m.is_eq(condition)) { return 0; } expr* x = to_app(condition)->get_arg(0); expr* y = to_app(condition)->get_arg(1); if (!is_var(x)) { std::swap(x, y); } if (!is_var(x)) { return 0; } dl_decl_util decl_util(m); uint64 value = 0; if (!decl_util.is_numeral_ext(y, value)) { return 0; } return alloc(default_table_filter_not_equal_fn, ctx, to_var(x)->get_idx(), value); } }; class relation_manager::default_table_filter_interpreted_fn : public table_mutator_fn, auxiliary_table_filter_fn { ast_manager & m_ast_manager; var_subst & m_vs; dl_decl_util & m_decl_util; th_rewriter & m_simp; app_ref m_condition; expr_free_vars m_free_vars; expr_ref_vector m_args; public: default_table_filter_interpreted_fn(context & ctx, unsigned col_cnt, app* condition) : m_ast_manager(ctx.get_manager()), m_vs(ctx.get_var_subst()), m_decl_util(ctx.get_decl_util()), m_simp(ctx.get_rewriter()), m_condition(condition, ctx.get_manager()), m_args(ctx.get_manager()) { m_free_vars(m_condition); } virtual bool should_remove(const table_fact & f) const { expr_ref_vector& args = const_cast(m_args); args.reset(); //arguments need to be in reverse order for the substitution unsigned col_cnt = f.size(); for(int i=col_cnt-1;i>=0;i--) { if(!m_free_vars.contains(i)) { args.push_back(0); continue; //this variable does not occur in the condition; } table_element el = f[i]; args.push_back(m_decl_util.mk_numeral(el, m_free_vars[i])); } expr_ref ground(m_ast_manager); m_vs(m_condition.get(), args.size(), args.c_ptr(), ground); m_simp(ground); return m_ast_manager.is_false(ground); } virtual void operator()(table_base & t) { auxiliary_table_filter_fn::operator()(t); } }; table_mutator_fn * relation_manager::mk_filter_interpreted_fn(const table_base & t, app * condition) { context & ctx = get_context(); table_mutator_fn * res = t.get_plugin().mk_filter_interpreted_fn(t, condition); if (!res) { res = default_table_filter_not_equal_fn::mk(ctx, condition); } if(!res) { res = alloc(default_table_filter_interpreted_fn, ctx, t.get_signature().size(), condition); } return res; } class relation_manager::default_table_filter_interpreted_and_project_fn : public table_transformer_fn { scoped_ptr m_filter; scoped_ptr m_project; app_ref m_condition; unsigned_vector m_removed_cols; public: default_table_filter_interpreted_and_project_fn(context & ctx, table_mutator_fn * filter, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) : m_filter(filter), m_condition(condition, ctx.get_manager()), m_removed_cols(removed_col_cnt, removed_cols) {} virtual table_base* operator()(const table_base & tb) { table_base *t2 = tb.clone(); (*m_filter)(*t2); if (!m_project) { relation_manager & rmgr = t2->get_plugin().get_manager(); m_project = rmgr.mk_project_fn(*t2, m_removed_cols.size(), m_removed_cols.c_ptr()); if (!m_project) { throw default_exception("projection does not exist"); } } return (*m_project)(*t2); } }; table_transformer_fn * relation_manager::mk_filter_interpreted_and_project_fn(const table_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { table_transformer_fn * res = t.get_plugin().mk_filter_interpreted_and_project_fn(t, condition, removed_col_cnt, removed_cols); if (res) return res; table_mutator_fn * filter = mk_filter_interpreted_fn(t, condition); SASSERT(filter); res = alloc(default_table_filter_interpreted_and_project_fn, get_context(), filter, condition, removed_col_cnt, removed_cols); return res; } table_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const table_base & t, const table_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) { table_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, src, joined_col_cnt, t_cols, src_cols); if(!res && &t.get_plugin()!=&src.get_plugin()) { res = src.get_plugin().mk_filter_by_negation_fn(t, src, joined_col_cnt, t_cols, src_cols); } return res; } class relation_manager::default_table_negation_filter_fn : public convenient_table_negation_filter_fn, auxiliary_table_filter_fn { const table_base * m_negated_table; mutable table_fact m_aux_fact; public: default_table_negation_filter_fn(const table_base & tgt, const table_base & neg_t, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) : convenient_table_negation_filter_fn(tgt, neg_t, joined_col_cnt, t_cols, negated_cols), m_negated_table(0) { m_aux_fact.resize(neg_t.get_signature().size()); } virtual bool should_remove(const table_fact & f) const { if(!m_all_neg_bound || m_overlap) { table_base::iterator nit = m_negated_table->begin(); table_base::iterator nend = m_negated_table->end(); for(; nit!=nend; ++nit) { const table_base::row_interface & nrow = *nit; if(bindings_match(nrow, f)) { return true; } } return false; } else { make_neg_bindings(m_aux_fact, f); return m_negated_table->contains_fact(m_aux_fact); } } virtual void operator()(table_base & tgt, const table_base & negated_table) { SASSERT(m_negated_table==0); flet flet_neg_table(m_negated_table, &negated_table); auxiliary_table_filter_fn::operator()(tgt); } }; table_intersection_filter_fn * relation_manager::mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { table_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, t_cols, negated_cols); if(!res && &t.get_plugin()!=&negated_obj.get_plugin()) { res = negated_obj.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, t_cols, negated_cols); } if(!res) { res = alloc(default_table_negation_filter_fn, t, negated_obj, joined_col_cnt, t_cols, negated_cols); } return res; } table_intersection_join_filter_fn* relation_manager::mk_filter_by_negated_join_fn( const table_base & t, const table_base & src1, const table_base & src2, unsigned_vector const& t_cols, unsigned_vector const& src_cols, unsigned_vector const& src1_cols, unsigned_vector const& src2_cols) { return t.get_plugin().mk_filter_by_negated_join_fn(t, src1, src2, t_cols, src_cols, src1_cols, src2_cols); } class relation_manager::default_table_select_equal_and_project_fn : public table_transformer_fn { scoped_ptr m_filter; scoped_ptr m_project; public: default_table_select_equal_and_project_fn(table_mutator_fn * filter, table_transformer_fn * project) : m_filter(filter), m_project(project) {} virtual table_base * operator()(const table_base & t1) { TRACE("dl", tout << t1.get_plugin().get_name() << "\n";); scoped_rel aux = t1.clone(); (*m_filter)(*aux); table_base * res = (*m_project)(*aux); return res; } }; table_transformer_fn * relation_manager::mk_select_equal_and_project_fn(const table_base & t, const table_element & value, unsigned col) { table_transformer_fn * res = t.get_plugin().mk_select_equal_and_project_fn(t, value, col); if(!res) { table_mutator_fn * selector = mk_filter_equal_fn(t, value, col); SASSERT(selector); table_transformer_fn * projector = mk_project_fn(t, 1, &col); SASSERT(projector); res = alloc(default_table_select_equal_and_project_fn, selector, projector); } return res; } class relation_manager::default_table_map_fn : public table_mutator_fn { scoped_ptr m_mapper; unsigned m_first_functional; scoped_rel m_aux_table; scoped_ptr m_union_fn; table_fact m_curr_fact; public: default_table_map_fn(const table_base & t, table_row_mutator_fn * mapper) : m_mapper(mapper), m_first_functional(t.get_signature().first_functional()) { SASSERT(t.get_signature().functional_columns()>0); table_plugin & plugin = t.get_plugin(); m_aux_table = plugin.mk_empty(t.get_signature()); m_union_fn = plugin.mk_union_fn(t, *m_aux_table, static_cast(0)); } virtual void operator()(table_base & t) { SASSERT(t.get_signature()==m_aux_table->get_signature()); if(!m_aux_table->empty()) { m_aux_table->reset(); } table_base::iterator it = t.begin(); table_base::iterator iend = t.end(); for(; it!=iend; ++it) { it->get_fact(m_curr_fact); if((*m_mapper)(m_curr_fact.c_ptr()+m_first_functional)) { m_aux_table->add_fact(m_curr_fact); } } t.reset(); (*m_union_fn)(t, *m_aux_table, static_cast(0)); } }; table_mutator_fn * relation_manager::mk_map_fn(const table_base & t, table_row_mutator_fn * mapper) { SASSERT(t.get_signature().functional_columns()>0); table_mutator_fn * res = t.get_plugin().mk_map_fn(t, mapper); if(!res) { res = alloc(default_table_map_fn, t, mapper); } return res; } class relation_manager::default_table_project_with_reduce_fn : public convenient_table_transformer_fn { unsigned_vector m_removed_cols; const unsigned m_inp_col_cnt; const unsigned m_removed_col_cnt; const unsigned m_result_col_cnt; scoped_ptr m_reducer; unsigned m_res_first_functional; table_fact m_row; table_fact m_former_row; public: default_table_project_with_reduce_fn(const table_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) : m_removed_cols(removed_col_cnt, removed_cols), m_inp_col_cnt(orig_sig.size()), m_removed_col_cnt(removed_col_cnt), m_result_col_cnt(orig_sig.size()-removed_col_cnt), m_reducer(reducer) { SASSERT(removed_col_cnt>0); table_signature::from_project_with_reduce(orig_sig, removed_col_cnt, removed_cols, get_result_signature()); m_res_first_functional = get_result_signature().first_functional(); m_row.resize(get_result_signature().size()); m_former_row.resize(get_result_signature().size()); } virtual void modify_fact(table_fact & f) const { unsigned ofs=1; unsigned r_i=1; for(unsigned i=m_removed_cols[0]+1; isuggest_fact(m_former_row)) { (*m_reducer)(m_former_row.c_ptr()+m_res_first_functional, m_row.c_ptr()+m_res_first_functional); res->ensure_fact(m_former_row); } } return res; } }; table_transformer_fn * relation_manager::mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) { SASSERT(t.get_signature().functional_columns()>0); table_transformer_fn * res = t.get_plugin().mk_project_with_reduce_fn(t, col_cnt, removed_cols, reducer); if(!res) { res = alloc(default_table_project_with_reduce_fn, t.get_signature(), col_cnt, removed_cols, reducer); } return res; } }; z3-4.4.0/src/muz/rel/dl_sparse_table.cpp0000644000175000017500000015331612540347414017671 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_sparse_table.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-24. Revision History: --*/ #include #include"dl_context.h" #include"dl_util.h" #include"dl_sparse_table.h" namespace datalog { // ----------------------------------- // // entry_storage // // ----------------------------------- entry_storage::store_offset entry_storage::insert_or_get_reserve_content() { SASSERT(has_reserve()); store_offset entry_ofs = m_data_indexer.insert_if_not_there(m_reserve); if (m_reserve == entry_ofs) { //entry inserted, so reserve is no longer a reserve m_reserve = NO_RESERVE; } return entry_ofs; } bool entry_storage::insert_reserve_content() { SASSERT(has_reserve()); store_offset entry_ofs = m_data_indexer.insert_if_not_there(m_reserve); if (m_reserve == entry_ofs) { //entry inserted, so reserve is no longer a reserve m_reserve = NO_RESERVE; return true; } return false; } bool entry_storage::remove_reserve_content() { SASSERT(has_reserve()); store_offset entry_ofs; if (!find_reserve_content(entry_ofs)) { //the fact was not in the table return false; } remove_offset(entry_ofs); return true; } void entry_storage::remove_offset(store_offset ofs) { m_data_indexer.remove(ofs); store_offset last_ofs = after_last_offset() - m_entry_size; if (ofs!=last_ofs) { SASSERT(ofs + m_entry_size <= last_ofs); //we don't want any holes, so we put the last element at the place //of the removed one m_data_indexer.remove(last_ofs); char * base = &m_data.get(0); memcpy(base+ofs, base+last_ofs, m_entry_size); m_data_indexer.insert(ofs); } if (has_reserve()) { //we already have a reserve, so we need to shrink a little to keep having just one resize_data(m_data_size-m_entry_size); } m_reserve=last_ofs; } unsigned entry_storage::get_size_estimate_bytes() const { size_t sz = m_data.capacity(); sz += m_data_indexer.capacity()*sizeof(storage_indexer::entry); return static_cast(sz); } // ----------------------------------- // // sparse_table::column_layout // // ----------------------------------- unsigned get_domain_length(uint64 dom_size) { SASSERT(dom_size>0); unsigned length = 0; unsigned dom_size_sm; if (dom_size>UINT_MAX) { dom_size_sm = static_cast(dom_size>>32); length += 32; if ( (dom_size&UINT_MAX)!=0 && dom_size_sm!=UINT_MAX ) { dom_size_sm++; } } else { dom_size_sm=static_cast(dom_size); } if (dom_size_sm == 1) { length += 1; //unary domains } else if (dom_size_sm > 0x80000000u) { length += 32; } else { length += get_num_1bits(next_power_of_two(dom_size_sm)-1); //ceil(log2(dom_size)) } return length; } sparse_table::column_layout::column_layout(const table_signature & sig) : m_functional_col_cnt(sig.functional_columns()) { SASSERT(sig.size() > 0); unsigned ofs = 0; unsigned sig_sz = sig.size(); unsigned first_functional = sig_sz-m_functional_col_cnt; for (unsigned i=0; i0); SASSERT(length<=64); if (size() > 0 && (length > 54 || i == first_functional)) { //large domains must start byte-aligned, as well as functional columns make_byte_aligned_end(size()-1); ofs = back().next_ofs(); } push_back(column_info(ofs, length)); ofs += length; } make_byte_aligned_end(size()-1); SASSERT(back().next_ofs()%8 == 0);//the entries must be aligned to whole bytes m_entry_size = back().next_ofs()/8; if (m_functional_col_cnt) { SASSERT((*this)[first_functional].m_offset%8 == 0); m_functional_part_size = m_entry_size - (*this)[first_functional].m_offset/8; } else { m_functional_part_size = 0; } } void sparse_table::column_layout::make_byte_aligned_end(unsigned col_index0) { unsigned ofs = (*this)[col_index0].next_ofs(); unsigned ofs_bit_part = ofs%8; unsigned rounded_ofs = (ofs_bit_part == 0) ? ofs : (ofs+8-ofs_bit_part); if (rounded_ofs!=ofs) { SASSERT(rounded_ofs>ofs); int diff = rounded_ofs-ofs; unsigned col_idx = col_index0+1; while(diff!=0) { //we should always be able to fix the alignment by the time we reach zero SASSERT(col_idx>0); col_idx--; column_info & ci = (*this)[col_idx]; unsigned new_length = ci.m_length; if (ci.m_length < 64) { unsigned swallowed = std::min(64-static_cast(ci.m_length), diff); diff -= swallowed; new_length += swallowed; } unsigned new_ofs = ci.m_offset+diff; ci = column_info(new_ofs, new_length); } } SASSERT(rounded_ofs%8 == 0); SASSERT((*this)[col_index0].next_ofs()%8 == 0); } // ----------------------------------- // // sparse_table // // ----------------------------------- class sparse_table::our_iterator_core : public iterator_core { class our_row : public row_interface { const our_iterator_core & m_parent; public: our_row(const sparse_table & t, const our_iterator_core & parent) : row_interface(t), m_parent(parent) {} virtual table_element operator[](unsigned col) const { return m_parent.m_layout.get(m_parent.m_ptr, col); } }; const char * m_end; const char * m_ptr; unsigned m_fact_size; our_row m_row_obj; const column_layout & m_layout; public: our_iterator_core(const sparse_table & t, bool finished) : m_end(t.m_data.after_last()), m_ptr(finished ? m_end : t.m_data.begin()), m_fact_size(t.m_fact_size), m_row_obj(t, *this), m_layout(t.m_column_layout) {} virtual bool is_finished() const { return m_ptr == m_end; } virtual row_interface & operator*() { SASSERT(!is_finished()); return m_row_obj; } virtual void operator++() { SASSERT(!is_finished()); m_ptr+=m_fact_size; } }; class sparse_table::key_indexer { protected: unsigned_vector m_key_cols; public: typedef const store_offset * offset_iterator; /** Iterators returned by \c begin() and \c end() are valid only as long as the \c query_result object that returned them exists. */ struct query_result { private: bool m_singleton; union { store_offset m_single_result; struct { offset_iterator begin; offset_iterator end; } m_many; }; public: /** \brief Empty result. */ query_result() : m_singleton(false) { m_many.begin = 0; m_many.end = 0; } query_result(offset_iterator begin, offset_iterator end) : m_singleton(false) { m_many.begin = begin; m_many.end = end; } query_result(store_offset single_result) : m_singleton(true), m_single_result(single_result) {} offset_iterator begin() const { return m_singleton ? &m_single_result : m_many.begin; } offset_iterator end() const { return m_singleton ? (&m_single_result+1) : m_many.end; } bool empty() const { return begin() == end(); } }; key_indexer(unsigned key_len, const unsigned * key_cols) : m_key_cols(key_len, key_cols) {} virtual ~key_indexer() {} virtual void update(const sparse_table & t) {} virtual query_result get_matching_offsets(const key_value & key) const = 0; }; class sparse_table::general_key_indexer : public key_indexer { typedef svector offset_vector; typedef size_t_map index_map; index_map m_map; mutable entry_storage m_keys; store_offset m_first_nonindexed; void key_to_reserve(const key_value & key) const { m_keys.ensure_reserve(); m_keys.write_into_reserve(reinterpret_cast(key.c_ptr())); } offset_vector & get_matching_offset_vector(const key_value & key) { key_to_reserve(key); store_offset ofs = m_keys.insert_or_get_reserve_content(); index_map::entry * e = m_map.find_core(ofs); if (!e) { TRACE("dl_table_relation", tout << "inserting\n";); e = m_map.insert_if_not_there2(ofs, offset_vector()); } return e->get_data().m_value; } public: general_key_indexer(unsigned key_len, const unsigned * key_cols) : key_indexer(key_len, key_cols), m_keys(key_len*sizeof(table_element)), m_first_nonindexed(0) {} virtual void update(const sparse_table & t) { if (m_first_nonindexed == t.m_data.after_last_offset()) { return; } SASSERT(m_first_nonindexedinsert(ofs); } m_first_nonindexed = t.m_data.after_last_offset(); } virtual query_result get_matching_offsets(const key_value & key) const { key_to_reserve(key); store_offset ofs; if (!m_keys.find_reserve_content(ofs)) { return query_result(); } index_map::entry * e = m_map.find_core(ofs); if (!e) { return query_result(); } const offset_vector & res = e->get_data().m_value; return query_result(res.begin(), res.end()); } }; /** When doing lookup using this index, the content of the reserve in sparse_table::m_data changes. */ class sparse_table::full_signature_key_indexer : public key_indexer { const sparse_table & m_table; /** Permutation of key columns to make it into table facts. If empty, no permutation is necessary. */ unsigned_vector m_permutation; mutable table_fact m_key_fact; public: static bool can_handle(unsigned key_len, const unsigned * key_cols, const sparse_table & t) { unsigned non_func_cols = t.get_signature().first_functional(); if (key_len!=non_func_cols) { return false; } counter ctr; ctr.count(key_len, key_cols); if (ctr.get_max_counter_value()!=1 || ctr.get_max_positive()!=non_func_cols-1) { return false; } SASSERT(ctr.get_positive_count() == non_func_cols); return true; } full_signature_key_indexer(unsigned key_len, const unsigned * key_cols, const sparse_table & t) : key_indexer(key_len, key_cols), m_table(t) { SASSERT(can_handle(key_len, key_cols, t)); m_permutation.resize(key_len); for (unsigned i=0; i(m_table); t.write_into_reserve(m_key_fact.c_ptr()); store_offset res; if (!t.m_data.find_reserve_content(res)) { return query_result(); } return query_result(res); } }; sparse_table::sparse_table(sparse_table_plugin & p, const table_signature & sig, unsigned init_capacity) : table_base(p, sig), m_column_layout(sig), m_fact_size(m_column_layout.m_entry_size), m_data(m_fact_size, m_column_layout.m_functional_part_size, init_capacity) {} sparse_table::sparse_table(const sparse_table & t) : table_base(t.get_plugin(), t.get_signature()), m_column_layout(t.m_column_layout), m_fact_size(t.m_fact_size), m_data(t.m_data) {} table_base * sparse_table::clone() const { return get_plugin().mk_clone(*this); } sparse_table::~sparse_table() { reset_indexes(); } void sparse_table::reset() { reset_indexes(); m_data.reset(); } table_base::iterator sparse_table::begin() const { return mk_iterator(alloc(our_iterator_core, *this, false)); } table_base::iterator sparse_table::end() const { return mk_iterator(alloc(our_iterator_core, *this, true)); } sparse_table::key_indexer& sparse_table::get_key_indexer(unsigned key_len, const unsigned * key_cols) const { verbose_action _va("get_key_indexer"); #if Z3DEBUG //We allow indexes only on non-functional columns because we want to be able to modify them //without having to worry about updating indexes. //Maybe we might keep a list of indexes that contain functional columns and on an update reset //only those. SASSERT(key_len == 0 || counter().count(key_len, key_cols).get_max_positive()get_data().m_value) { if (full_signature_key_indexer::can_handle(key_len, key_cols, *this)) { key_map_entry->get_data().m_value = alloc(full_signature_key_indexer, key_len, key_cols, *this); } else { key_map_entry->get_data().m_value = alloc(general_key_indexer, key_len, key_cols); } } key_indexer & indexer = *key_map_entry->get_data().m_value; indexer.update(*this); return indexer; } void sparse_table::reset_indexes() { key_index_map::iterator kmit = m_key_indexes.begin(); key_index_map::iterator kmend = m_key_indexes.end(); for (; kmit!=kmend; ++kmit) { dealloc((*kmit).m_value); } m_key_indexes.reset(); } void sparse_table::write_into_reserve(const table_element* f) { TRACE("dl_table_relation", tout << "\n";); m_data.ensure_reserve(); char * reserve = m_data.get_reserve_ptr(); unsigned col_cnt = m_column_layout.size(); for (unsigned i = 0; i < col_cnt; ++i) { SASSERT(f[i] < get_signature()[i]); //the value fits into the table signature m_column_layout.set(reserve, i, f[i]); } } bool sparse_table::add_fact(const char * data) { verbose_action _va("add_fact", 10); m_data.write_into_reserve(data); return add_reserve_content(); } void sparse_table::add_fact(const table_fact & f) { write_into_reserve(f.c_ptr()); add_reserve_content(); } bool sparse_table::add_reserve_content() { return m_data.insert_reserve_content(); } bool sparse_table::contains_fact(const table_fact & f) const { verbose_action _va("contains_fact", 2); sparse_table & t = const_cast(*this); t.write_into_reserve(f.c_ptr()); unsigned func_col_cnt = get_signature().functional_columns(); if (func_col_cnt == 0) { return t.m_data.reserve_content_already_present(); } else { store_offset ofs; if (!t.m_data.find_reserve_content(ofs)) { return false; } unsigned sz = get_signature().size(); for (unsigned i=func_col_cnt; i(*this); t.write_into_reserve(f.c_ptr()); store_offset ofs; if (!t.m_data.find_reserve_content(ofs)) { return false; } unsigned sz = sig.size(); for (unsigned i=sig.first_functional(); ipre_projection_idx); dest_layout.set(dest, dest_idx++, src_layout.get(src, i)); } } void sparse_table::concatenate_rows(const column_layout & layout1, const column_layout & layout2, const column_layout & layout_res, const char * ptr1, const char * ptr2, char * res, const unsigned * removed_cols) { unsigned t1non_func = layout1.size()-layout1.m_functional_col_cnt; unsigned t2non_func = layout2.size()-layout2.m_functional_col_cnt; unsigned t1cols = layout1.size(); unsigned t2cols = layout2.size(); unsigned orig_i = 0; unsigned res_i = 0; const unsigned * next_removed = removed_cols; copy_columns(layout1, layout_res, 0, t1non_func, ptr1, res, res_i, orig_i, next_removed); copy_columns(layout2, layout_res, 0, t2non_func, ptr2, res, res_i, orig_i, next_removed); copy_columns(layout1, layout_res, t1non_func, t1cols, ptr1, res, res_i, orig_i, next_removed); copy_columns(layout2, layout_res, t2non_func, t2cols, ptr2, res, res_i, orig_i, next_removed); } void sparse_table::garbage_collect() { if (memory::above_high_watermark()) { get_plugin().garbage_collect(); } if (memory::above_high_watermark()) { IF_VERBOSE(1, verbose_stream() << "Ran out of memory while filling table of size: " << get_size_estimate_rows() << " rows " << get_size_estimate_bytes() << " bytes\n";); throw out_of_memory_error(); } } void sparse_table::self_agnostic_join_project(const sparse_table & t1, const sparse_table & t2, unsigned joined_col_cnt, const unsigned * t1_joined_cols, const unsigned * t2_joined_cols, const unsigned * removed_cols, bool tables_swapped, sparse_table & result) { verbose_action _va("join_project", 1); unsigned t1_entry_size = t1.m_fact_size; unsigned t2_entry_size = t2.m_fact_size; size_t t1idx = 0; size_t t1end = t1.m_data.after_last_offset(); TRACE("dl_table_relation", tout << "joined_col_cnt: " << joined_col_cnt << "\n"; tout << "t1_entry_size: " << t1_entry_size << "\n"; tout << "t2_entry_size: " << t2_entry_size << "\n"; t1.display(tout); t2.display(tout); tout << (&t1) << " " << (&t2) << " " << (&result) << "\n"; ); if (joined_col_cnt == 0) { size_t t2idx = 0; size_t t2end = t2.m_data.after_last_offset(); for (; t1idx!=t1end; t1idx+=t1_entry_size) { for (t2idx = 0; t2idx != t2end; t2idx += t2_entry_size) { result.m_data.ensure_reserve(); result.garbage_collect(); char * res_reserve = result.m_data.get_reserve_ptr(); char const* t1ptr = t1.get_at_offset(t1idx); char const* t2ptr = t2.get_at_offset(t2idx); if (tables_swapped) { concatenate_rows(t2.m_column_layout, t1.m_column_layout, result.m_column_layout, t2ptr, t1ptr, res_reserve, removed_cols); } else { concatenate_rows(t1.m_column_layout, t2.m_column_layout, result.m_column_layout, t1ptr, t2ptr, res_reserve, removed_cols); } result.add_reserve_content(); } } return; } key_value t1_key; t1_key.resize(joined_col_cnt); key_indexer& t2_indexer = t2.get_key_indexer(joined_col_cnt, t2_joined_cols); bool key_modified = true; key_indexer::query_result t2_offsets; for (; t1idx != t1end; t1idx += t1_entry_size) { for (unsigned i = 0; i < joined_col_cnt; i++) { table_element val = t1.m_column_layout.get(t1.get_at_offset(t1idx), t1_joined_cols[i]); TRACE("dl_table_relation", tout << "val: " << val << " " << t1idx << " " << t1_joined_cols[i] << "\n";); if (t1_key[i] != val) { t1_key[i] = val; key_modified = true; } } if (key_modified) { t2_offsets = t2_indexer.get_matching_offsets(t1_key); key_modified = false; } if (t2_offsets.empty()) { continue; } key_indexer::offset_iterator t2ofs_it = t2_offsets.begin(); key_indexer::offset_iterator t2ofs_end = t2_offsets.end(); for (; t2ofs_it != t2ofs_end; ++t2ofs_it) { store_offset t2ofs = *t2ofs_it; result.m_data.ensure_reserve(); result.garbage_collect(); char * res_reserve = result.m_data.get_reserve_ptr(); char const * t1ptr = t1.get_at_offset(t1idx); char const * t2ptr = t2.get_at_offset(t2ofs); if (tables_swapped) { concatenate_rows(t2.m_column_layout, t1.m_column_layout, result.m_column_layout, t2ptr, t1ptr, res_reserve, removed_cols); } else { concatenate_rows(t1.m_column_layout, t2.m_column_layout, result.m_column_layout, t1ptr, t2ptr, res_reserve, removed_cols); } result.add_reserve_content(); } } } // ----------------------------------- // // sparse_table_plugin // // ----------------------------------- sparse_table_plugin::sparse_table_plugin(relation_manager & manager) : table_plugin(symbol("sparse"), manager) {} sparse_table_plugin::~sparse_table_plugin() { reset(); } sparse_table const& sparse_table_plugin::get(table_base const& t) { return dynamic_cast(t); } sparse_table& sparse_table_plugin::get(table_base& t) { return dynamic_cast(t); } sparse_table const* sparse_table_plugin::get(table_base const* t) { return dynamic_cast(t); } sparse_table* sparse_table_plugin::get(table_base* t) { return dynamic_cast(t); } void sparse_table_plugin::reset() { table_pool::iterator it = m_pool.begin(); table_pool::iterator end = m_pool.end(); for (; it!=end; ++it) { sp_table_vector * vect = it->m_value; sp_table_vector::iterator vit = vect->begin(); sp_table_vector::iterator vend = vect->end(); for (; vit!=vend; ++vit) { (*vit)->destroy(); //calling deallocate() would only put the table back into the pool } dealloc(vect); } m_pool.reset(); } void sparse_table_plugin::garbage_collect() { IF_VERBOSE(2, verbose_stream() << "garbage collecting "<< memory::get_allocation_size() << " bytes down to ";); reset(); IF_VERBOSE(2, verbose_stream() << memory::get_allocation_size() << " bytes\n";); } void sparse_table_plugin::recycle(sparse_table * t) { verbose_action _va("recycle", 2); const table_signature & sig = t->get_signature(); t->reset(); table_pool::entry * e = m_pool.insert_if_not_there2(sig, 0); sp_table_vector * & vect = e->get_data().m_value; if (vect == 0) { vect = alloc(sp_table_vector); } IF_VERBOSE(12, verbose_stream() << "Recycle: " << t->get_size_estimate_bytes() << "\n";); vect->push_back(t); } table_base * sparse_table_plugin::mk_empty(const table_signature & s) { SASSERT(can_handle_signature(s)); sp_table_vector * vect; if (!m_pool.find(s, vect) || vect->empty()) { return alloc(sparse_table, *this, s); } sparse_table * res = vect->back(); vect->pop_back(); return res; } sparse_table * sparse_table_plugin::mk_clone(const sparse_table & t) { sparse_table * res = get(mk_empty(t.get_signature())); res->m_data = t.m_data; return res; } bool sparse_table_plugin::join_involves_functional(const table_signature & s1, const table_signature & s2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (col_cnt == 0) { return false; } return counter().count(col_cnt, cols1).get_max_positive()>=s1.first_functional() || counter().count(col_cnt, cols2).get_max_positive()>=s2.first_functional(); } class sparse_table_plugin::join_project_fn : public convenient_table_join_project_fn { public: join_project_fn(const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_table_join_project_fn(t1_sig, t2_sig, col_cnt, cols1, cols2, removed_col_cnt, removed_cols) { m_removed_cols.push_back(UINT_MAX); } virtual table_base * operator()(const table_base & tb1, const table_base & tb2) { const sparse_table & t1 = get(tb1); const sparse_table & t2 = get(tb2); sparse_table_plugin & plugin = t1.get_plugin(); sparse_table * res = get(plugin.mk_empty(get_result_signature())); //If we join with some intersection, want to iterate over the smaller table and //do indexing into the bigger one. If we simply do a product, we want the bigger //one to be at the outer iteration (then the small one will hopefully fit into //the cache) if ( (t1.row_count() > t2.row_count()) == (!m_cols1.empty()) ) { sparse_table::self_agnostic_join_project(t2, t1, m_cols1.size(), m_cols2.c_ptr(), m_cols1.c_ptr(), m_removed_cols.c_ptr(), true, *res); } else { sparse_table::self_agnostic_join_project(t1, t2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr(), m_removed_cols.c_ptr(), false, *res); } TRACE("dl_table_relation", tb1.display(tout); tb2.display(tout); res->display(tout); ); return res; } }; table_join_fn * sparse_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { const table_signature & sig1 = t1.get_signature(); const table_signature & sig2 = t2.get_signature(); if (t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind() || join_involves_functional(sig1, sig2, col_cnt, cols1, cols2)) { //We also don't allow indexes on functional columns (and they are needed for joins) return 0; } return mk_join_project_fn(t1, t2, col_cnt, cols1, cols2, 0, static_cast(0)); } table_join_fn * sparse_table_plugin::mk_join_project_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { const table_signature & sig1 = t1.get_signature(); const table_signature & sig2 = t2.get_signature(); if (t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind() || removed_col_cnt == t1.get_signature().size()+t2.get_signature().size() || join_involves_functional(sig1, sig2, col_cnt, cols1, cols2)) { //We don't allow sparse tables with zero signatures (and project on all columns leads to such) //We also don't allow indexes on functional columns. return 0; } return alloc(join_project_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } class sparse_table_plugin::union_fn : public table_union_fn { public: virtual void operator()(table_base & tgt0, const table_base & src0, table_base * delta0) { verbose_action _va("union"); sparse_table & tgt = get(tgt0); const sparse_table & src = get(src0); sparse_table * delta = get(delta0); unsigned fact_size = tgt.m_fact_size; const char* ptr = src.m_data.begin(); const char* after_last=src.m_data.after_last(); for (; ptradd_fact(ptr); } } } }; table_union_fn * sparse_table_plugin::mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) { if (tgt.get_kind()!=get_kind() || src.get_kind()!=get_kind() || (delta && delta->get_kind()!=get_kind()) || tgt.get_signature()!=src.get_signature() || (delta && delta->get_signature()!=tgt.get_signature())) { return 0; } return alloc(union_fn); } class sparse_table_plugin::project_fn : public convenient_table_project_fn { const unsigned m_inp_col_cnt; const unsigned m_removed_col_cnt; const unsigned m_result_col_cnt; public: project_fn(const table_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_table_project_fn(orig_sig, removed_col_cnt, removed_cols), m_inp_col_cnt(orig_sig.size()), m_removed_col_cnt(removed_col_cnt), m_result_col_cnt(orig_sig.size()-removed_col_cnt) { SASSERT(removed_col_cnt>0); } virtual void transform_row(const char * src, char * tgt, const sparse_table::column_layout & src_layout, const sparse_table::column_layout & tgt_layout) { unsigned r_idx=0; unsigned tgt_i=0; for (unsigned i=0; im_column_layout; const char* t_ptr = t.m_data.begin(); const char* t_end = t.m_data.after_last(); for (; t_ptr!=t_end; t_ptr+=t_fact_size) { SASSERT(t_ptrm_data.ensure_reserve(); char * res_ptr = res->m_data.get_reserve_ptr(); transform_row(t_ptr, res_ptr, src_layout, tgt_layout); res->m_data.insert_reserve_content(); } return res; } }; table_transformer_fn * sparse_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { if (col_cnt == t.get_signature().size()) { return 0; } return alloc(project_fn, t.get_signature(), col_cnt, removed_cols); } class sparse_table_plugin::select_equal_and_project_fn : public convenient_table_transformer_fn { const unsigned m_col; sparse_table::key_value m_key; public: select_equal_and_project_fn(const table_signature & orig_sig, table_element val, unsigned col) : m_col(col) { table_signature::from_project(orig_sig, 1, &col, get_result_signature()); m_key.push_back(val); } virtual table_base * operator()(const table_base & tb) { verbose_action _va("select_equal_and_project"); const sparse_table & t = get(tb); sparse_table_plugin & plugin = t.get_plugin(); sparse_table * res = get(plugin.mk_empty(get_result_signature())); const sparse_table::column_layout & t_layout = t.m_column_layout; const sparse_table::column_layout & res_layout = res->m_column_layout; unsigned t_cols = t_layout.size(); sparse_table::key_indexer & indexer = t.get_key_indexer(1, &m_col); sparse_table::key_indexer::query_result t_offsets = indexer.get_matching_offsets(m_key); if (t_offsets.empty()) { //no matches return res; } sparse_table::key_indexer::offset_iterator ofs_it=t_offsets.begin(); sparse_table::key_indexer::offset_iterator ofs_end=t_offsets.end(); for (; ofs_it!=ofs_end; ++ofs_it) { sparse_table::store_offset t_ofs = *ofs_it; const char * t_ptr = t.get_at_offset(t_ofs); res->m_data.ensure_reserve(); char * res_reserve = res->m_data.get_reserve_ptr(); unsigned res_i = 0; for (unsigned i=0; iadd_reserve_content(); } return res; } }; table_transformer_fn * sparse_table_plugin::mk_select_equal_and_project_fn(const table_base & t, const table_element & value, unsigned col) { if (t.get_kind()!=get_kind() || t.get_signature().size() == 1 || col>=t.get_signature().first_functional()) { //We don't allow sparse tables with zero signatures (and project on a single //column table produces one). //We also don't allow indexes on functional columns. And our implementation of //select_equal_and_project uses index on \c col. return 0; } return alloc(select_equal_and_project_fn, t.get_signature(), value, col); } class sparse_table_plugin::rename_fn : public convenient_table_rename_fn { unsigned_vector m_out_of_cycle; public: rename_fn(const table_signature & orig_sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { SASSERT(permutation_cycle_len>=2); idx_set cycle_cols; for (unsigned i=0; i < permutation_cycle_len; ++i) { cycle_cols.insert(permutation_cycle[i]); } for (unsigned i=0; i < orig_sig.size(); ++i) { if (!cycle_cols.contains(i)) { m_out_of_cycle.push_back(i); } } } void transform_row(const char * src, char * tgt, const sparse_table::column_layout & src_layout, const sparse_table::column_layout & tgt_layout) { for (unsigned i=1; i < m_cycle.size(); ++i) { tgt_layout.set(tgt, m_cycle[i-1], src_layout.get(src, m_cycle[i])); } tgt_layout.set(tgt, m_cycle[m_cycle.size()-1], src_layout.get(src, m_cycle[0])); unsigned_vector::const_iterator it = m_out_of_cycle.begin(); unsigned_vector::const_iterator end = m_out_of_cycle.end(); for (; it!=end; ++it) { unsigned col = *it; tgt_layout.set(tgt, col, src_layout.get(src, col)); } } virtual table_base * operator()(const table_base & tb) { verbose_action _va("rename"); const sparse_table & t = get(tb); unsigned t_fact_size = t.m_fact_size; sparse_table_plugin & plugin = t.get_plugin(); sparse_table * res = get(plugin.mk_empty(get_result_signature())); size_t res_fact_size = res->m_fact_size; size_t res_data_size = res_fact_size*t.row_count(); if (res_fact_size != 0 && (res_data_size / res_fact_size) != t.row_count()) { throw default_exception("multiplication overflow"); } res->m_data.resize_data(res_data_size); //here we can separate data creating and insertion into hashmap, since we know //that no row will become duplicate //create the data const char* t_ptr = t.m_data.begin(); char* res_ptr = res->m_data.begin(); char* res_end = res_ptr+res_data_size; for (; res_ptr!=res_end; t_ptr+=t_fact_size, res_ptr+=res_fact_size) { transform_row(t_ptr, res_ptr, t.m_column_layout, res->m_column_layout); } //and insert them into the hash-map for (size_t i = 0; i != res_data_size; i += res_fact_size) { TRUSTME(res->m_data.insert_offset(i)); } return res; } }; table_transformer_fn * sparse_table_plugin::mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { if (t.get_kind()!=get_kind()) { return 0; } return alloc(rename_fn, t.get_signature(), permutation_cycle_len, permutation_cycle); } class sparse_table_plugin::negation_filter_fn : public convenient_table_negation_filter_fn { typedef sparse_table::store_offset store_offset; typedef sparse_table::key_value key_value; typedef sparse_table::key_indexer key_indexer; bool m_joining_neg_non_functional; /** Used by \c collect_intersection_offsets function. If tgt_is_first is false, contains the same items as \c res. */ idx_set m_intersection_content; public: negation_filter_fn(const table_base & tgt, const table_base & neg, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) : convenient_table_negation_filter_fn(tgt, neg, joined_col_cnt, t_cols, negated_cols) { unsigned neg_first_func = neg.get_signature().first_functional(); counter ctr; ctr.count(m_cols2); m_joining_neg_non_functional = ctr.get_max_counter_value() == 1 && ctr.get_positive_count() == neg_first_func && (neg_first_func == 0 || ctr.get_max_positive() == neg_first_func-1); } /** Collect offsets of rows in \c t1 or \c t2 (depends on whether \c tgt_is_first is true or false) that have a match in the other table into \c res. Offsets in \c res are in ascending order. */ void collect_intersection_offsets(const sparse_table & t1, const sparse_table & t2, bool tgt_is_first, svector & res) { SASSERT(res.empty()); m_intersection_content.reset(); unsigned joined_col_cnt = m_cols1.size(); unsigned t1_entry_size = t1.m_data.entry_size(); const unsigned * cols1 = tgt_is_first ? m_cols1.c_ptr() : m_cols2.c_ptr(); const unsigned * cols2 = tgt_is_first ? m_cols2.c_ptr() : m_cols1.c_ptr(); key_value t1_key; t1_key.resize(joined_col_cnt); key_indexer & t2_indexer = t2.get_key_indexer(joined_col_cnt, cols2); bool key_modified=true; key_indexer::query_result t2_offsets; store_offset t1_after_last = t1.m_data.after_last_offset(); for (store_offset t1_ofs=0; t1_ofs(ofs); if (ofs != offs2) { throw default_exception("Z3 cannot perform negation with excessively large tables"); } if (!m_intersection_content.contains(offs2)) { m_intersection_content.insert(offs2); res.push_back(ofs); } } } } if (!tgt_is_first) { //in this case \c res now may be in arbitrary order std::sort(res.begin(), res.end()); } } virtual void operator()(table_base & tgt0, const table_base & neg0) { sparse_table & tgt = get(tgt0); const sparse_table & neg = get(neg0); verbose_action _va("filter_by_negation"); if (m_cols1.size() == 0) { if (!neg.empty()) { tgt.reset(); } return; } svector to_remove; //offsets here are in increasing order //We don't do just the simple tgt.row_count()>neg.row_count() because the swapped case is //more expensive. The constant 4 is, however, just my guess what the ratio might be. if (tgt.row_count()/4>neg.row_count()) { collect_intersection_offsets(neg, tgt, false, to_remove); } else { collect_intersection_offsets(tgt, neg, true, to_remove); } //the largest offsets are at the end, so we can remove them one by one while (!to_remove.empty()) { store_offset removed_ofs = to_remove.back(); to_remove.pop_back(); tgt.m_data.remove_offset(removed_ofs); } tgt.reset_indexes(); } }; table_intersection_filter_fn * sparse_table_plugin::mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { if (!check_kind(t) || !check_kind(negated_obj) || join_involves_functional(t.get_signature(), negated_obj.get_signature(), joined_col_cnt, t_cols, negated_cols) ) { return 0; } return alloc(negation_filter_fn, t, negated_obj, joined_col_cnt, t_cols, negated_cols); } /** T \ (S1 Join S2) t_cols - columns from T s_cols - columns from (S1 Join S2) that are equated src1_cols - columns from S1 equated with columns from S2 src2_cols - columns from S2 equated with columns from S1 t1_cols - columns from T that map into S1 s1_cols - matching columns from s_cols for t1_cols t2s1_cols - columns from T that map into S2, and columns from src1 that join src2 s2_cols - matching columns from t2s1_cols columns from s2 that are equal to a column from s1 that is in s_cols: - ... */ class sparse_table_plugin::negated_join_fn : public table_intersection_join_filter_fn { typedef sparse_table::store_offset store_offset; typedef sparse_table::key_value key_value; typedef sparse_table::key_indexer key_indexer; unsigned_vector m_t1_cols; unsigned_vector m_s1_cols; unsigned_vector m_t2_cols; unsigned_vector m_s2_cols; unsigned_vector m_src1_cols; public: negated_join_fn( table_base const& src1, unsigned_vector const& t_cols, unsigned_vector const& src_cols, unsigned_vector const& src1_cols, unsigned_vector const& src2_cols): m_src1_cols(src1_cols) { // split t_cols and src_cols according to src1, and src2 unsigned src1_size = src1.get_signature().size(); for (unsigned i = 0; i < t_cols.size(); ++i) { if (src_cols[i] < src1_size) { m_t1_cols.push_back(t_cols[i]); m_s1_cols.push_back(src_cols[i]); } else { m_t2_cols.push_back(t_cols[i]); m_s2_cols.push_back(src_cols[i]); } } m_s2_cols.append(src2_cols); } virtual void operator()(table_base & _t, const table_base & _s1, const table_base& _s2) { verbose_action _va("negated_join"); sparse_table& t = get(_t); svector to_remove; collect_to_remove(t, get(_s1), get(_s2), to_remove); for (unsigned i = 0; i < to_remove.size(); ++i) { t.m_data.remove_offset(to_remove[i]); } t.reset_indexes(); } private: void collect_to_remove(sparse_table& t, sparse_table const& s1, sparse_table const& s2, svector& to_remove) { key_value s1_key, s2_key; SASSERT(&s1 != &s2); SASSERT(m_s1_cols.size() == m_t1_cols.size()); SASSERT(m_s2_cols.size() == m_t2_cols.size() + m_src1_cols.size()); s1_key.resize(m_s1_cols.size()); s2_key.resize(m_s2_cols.size()); key_indexer & s1_indexer = s1.get_key_indexer(m_s1_cols.size(), m_s1_cols.c_ptr()); key_indexer & s2_indexer = s2.get_key_indexer(m_s2_cols.size(), m_s2_cols.c_ptr()); store_offset t_after_last = t.m_data.after_last_offset(); key_indexer::query_result s1_offsets, s2_offsets; unsigned t_entry_size = t.m_data.entry_size(); for (store_offset t_ofs = 0; t_ofs < t_after_last; t_ofs += t_entry_size) { if (update_key(s1_key, 0, t, t_ofs, m_t1_cols)) { s1_offsets = s1_indexer.get_matching_offsets(s1_key); } key_indexer::offset_iterator it = s1_offsets.begin(); key_indexer::offset_iterator end = s1_offsets.end(); for (; it != end; ++it) { store_offset s1_ofs = *it; bool upd1 = update_key(s2_key, 0, t, t_ofs, m_t2_cols); bool upd2 = update_key(s2_key, m_t2_cols.size(), s1, s1_ofs, m_src1_cols); if (upd1 || upd2) { s2_offsets = s2_indexer.get_matching_offsets(s2_key); } if (!s2_offsets.empty()) { to_remove.push_back(t_ofs); break; } } } } inline bool update_key(key_value& key, unsigned key_offset, sparse_table const& t, store_offset ofs, unsigned_vector const& cols) { bool modified = false; unsigned sz = cols.size(); for (unsigned i = 0; i < sz; ++i) { table_element val = t.get_cell(ofs, cols[i]); modified = update_key(key[i+key_offset], val) || modified; } return modified; } inline bool update_key(table_element& tgt, table_element src) { if (tgt == src) { return false; } else { tgt = src; return true; } } }; table_intersection_join_filter_fn* sparse_table_plugin::mk_filter_by_negated_join_fn( const table_base & t, const table_base & src1, const table_base & src2, unsigned_vector const& t_cols, unsigned_vector const& src_cols, unsigned_vector const& src1_cols, unsigned_vector const& src2_cols) { if (check_kind(t) && check_kind(src1) && check_kind(src2)) { return alloc(negated_join_fn, src1, t_cols, src_cols, src1_cols, src2_cols); } else { return 0; } } unsigned sparse_table::get_size_estimate_bytes() const { unsigned sz = 0; sz += m_data.get_size_estimate_bytes(); sz += m_key_indexes.capacity()*8; // TBD return sz; } }; z3-4.4.0/src/muz/rel/dl_finite_product_relation.h0000644000175000017500000003532012540347414021577 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_finite_product_relation.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-24. Revision History: --*/ #ifndef _DL_FINITE_PRODUCT_RELATION_H_ #define _DL_FINITE_PRODUCT_RELATION_H_ #include "dl_base.h" #include "dl_relation_manager.h" #include "dl_table_relation.h" namespace datalog { class finite_product_relation; void universal_delete(finite_product_relation* ptr); class finite_product_relation_plugin : public relation_plugin { friend class finite_product_relation; public: struct rel_spec { family_id m_inner_kind; //null_family_id means we don't care about the kind svector m_table_cols; rel_spec() : m_inner_kind(null_family_id) {} rel_spec(const svector& table_cols) : m_inner_kind(null_family_id), m_table_cols(table_cols) {} bool operator==(const rel_spec & o) const { return m_inner_kind==o.m_inner_kind && vectors_equal(m_table_cols, o.m_table_cols); } struct hash { unsigned operator()(const rel_spec & o) const { return o.m_inner_kind^svector_hash()(o.m_table_cols); } }; }; private: class join_fn; class converting_join_fn; class project_fn; class rename_fn; class union_fn; class inner_singleton_union_fn; class converting_union_fn; class filter_identical_fn; class filter_equal_fn; class filter_interpreted_fn; class negation_filter_fn; class filter_identical_pairs_fn; relation_plugin & m_inner_plugin; rel_spec_store > m_spec_store; static symbol get_name(relation_plugin & inner_plugin); family_id get_relation_kind(finite_product_relation & r, const bool * table_columns); static void get_all_possible_table_columns(relation_manager & rmgr, const relation_signature & s, svector & table_columns); void get_all_possible_table_columns(const relation_signature & s, svector & table_columns) { get_all_possible_table_columns(get_manager(), s, table_columns); } void split_signatures(const relation_signature & s, table_signature & table_sig, relation_signature & remaining_sig); void split_signatures(const relation_signature & s, const bool * table_columns, table_signature & table_sig, relation_signature & remaining_sig); public: static finite_product_relation & get(relation_base & r); static const finite_product_relation & get(const relation_base & r); static finite_product_relation * get(relation_base * r); static const finite_product_relation * get(const relation_base * r); static finite_product_relation_plugin & get_plugin(relation_manager & rmgr, relation_plugin & inner); finite_product_relation_plugin(relation_plugin & inner_plugin, relation_manager & manager); virtual void initialize(family_id fid); relation_plugin & get_inner_plugin() const { return m_inner_plugin; } virtual bool can_handle_signature(const relation_signature & s); virtual relation_base * mk_empty(const relation_signature & s); /** \c inner_kind==null_family_id means we don't care about the kind of the inner relation */ finite_product_relation * mk_empty(const relation_signature & s, const bool * table_columns, family_id inner_kind=null_family_id); finite_product_relation * mk_empty(const finite_product_relation & original); virtual relation_base * mk_empty(const relation_base & original); virtual relation_base * mk_empty(const relation_signature & s, family_id kind); virtual relation_base * mk_full(func_decl* p, const relation_signature & s); /** \brief Return true if \c r can be converted to \c finite_product_relation_plugin either by \c mk_from_table_relation or by \c mk_from_inner_relation. */ bool can_be_converted(const relation_base & r); /** If the conversion cannot be performed, 0 is returned. */ finite_product_relation * mk_from_table_relation(const table_relation & r); finite_product_relation * mk_from_inner_relation(const relation_base & r); bool can_convert_to_table_relation(const finite_product_relation & r); table_relation * to_table_relation(const finite_product_relation & r); protected: virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols); virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols); virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col); virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, const relation_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols); private: /** \brief Create a filter that enforces equality between pairs of table and relation columns The column numbers in arrays \c table_cols and \c rel_cols must be local to the table/inner relation. */ relation_mutator_fn * mk_filter_identical_pairs(const finite_product_relation & r, unsigned col_cnt, const unsigned * table_cols, const unsigned * rel_cols); /** \brief Create a join-project operation that creates a table according to \c relation_table but with references to relations updated and removed according to the content of \c filtered_table. \c selected_columns contains sorted indexes of data columns in \c relation_table that are also in the \c filtered_table (so that the first column in \c filtered_table corresponds to \c selected_columns[0] -th column in \c relation_table etc...) Signature of \c relation_table: (data columns)(functional column with indexes of relation objects) Signature of \c filtered_table: (selected data columns)(non-functional column with original relation object indexes) (functional column with indexes of filtered relation objects) */ static table_join_fn * mk_assembler_of_filter_result(const table_base & relation_table, const table_base & filtered_table, const unsigned_vector & selected_columns); }; class finite_product_relation : public relation_base { friend class finite_product_relation_plugin; friend class finite_product_relation_plugin::join_fn; friend class finite_product_relation_plugin::project_fn; friend class finite_product_relation_plugin::union_fn; friend class finite_product_relation_plugin::rename_fn; friend class finite_product_relation_plugin::inner_singleton_union_fn; friend class finite_product_relation_plugin::filter_equal_fn; friend class finite_product_relation_plugin::filter_identical_pairs_fn; class live_rel_collection_reducer; public: /** Size of this sort determines how many different relation objects can we refer to. */ static const table_sort s_rel_idx_sort; /** \brief The last column in the signature is a functional column with index of the associated inner relation. The other columns correspond to the relation signature according to \c m_table2sig. It holds that \c m_table_sig.size()-1==m_table2sig.size() */ table_signature m_table_sig; unsigned_vector m_table2sig; // (ordered list) unsigned_vector m_sig2table; //index of corresponding table column or UINT_MAX private: relation_signature m_other_sig; unsigned_vector m_other2sig; // (ordered list) public: unsigned_vector m_sig2other; //index of corresponding other relation column or UINT_MAX private: relation_plugin & m_other_plugin; family_id m_other_kind; mutable table_base * m_table; public: mutable relation_vector m_others; private: mutable unsigned_vector m_available_rel_indexes; /** \c UINT_MAX means uninitialized. If we can get away with it, we want to have a single full relation to refer to. */ mutable unsigned m_full_rel_idx; mutable idx_set m_live_rel_collection_acc; mutable scoped_ptr m_live_rel_collection_project; mutable scoped_ptr m_empty_rel_removal_filter; void recycle_rel_idx(unsigned idx) const; // creates a full relation if it does not exist. unsigned get_full_rel_idx(); public: relation_base & get_inner_rel(table_element idx) { SASSERT(idx(idx)); } relation_base & get_inner_rel(unsigned idx) { SASSERT(m_others[idx]); return *m_others[idx]; } const relation_base & get_inner_rel(unsigned idx) const { return const_cast(*this).get_inner_rel(idx); } unsigned get_next_rel_idx() const; /** The relation takes ownership of the \c inner object. */ void set_inner_rel(table_element idx, relation_base * inner) { SASSERT(idx(idx), inner); } /** The relation takes ownership of the \c inner object. */ void set_inner_rel(unsigned idx, relation_base * inner) { SASSERT(!m_others[idx]); SASSERT(inner); m_others[idx] = inner; } table_base & get_table() { return *m_table; } table_plugin & get_table_plugin() const { return get_table().get_plugin(); } void garbage_collect(bool remove_empty) const; /** \brief Initialize an empty relation with table \c table_vals and relations in \c others. The relation object takes ownership of relations inside the \c others vector. If \c contiguous is true, it can be assumed that there are no zero elements in the \c others array. */ void init(const table_base & table_vals, const relation_vector & others, bool contiguous); private: /** \brief Extract the values of table non-functional columns from the relation fact. The value of the functional column which determines index of the inner relation is undefined. */ void extract_table_fact(const relation_fact rf, table_fact & tf) const; /** \brief Extract the values of the inner relation columns from the relation fact. */ void extract_other_fact(const relation_fact rf, relation_fact & of) const; relation_base * mk_empty_inner(); relation_base * mk_full_inner(func_decl* pred); void complement_self(func_decl* pred); void collect_live_relation_indexes(idx_set & res) const; /** \brief Try to modify relations in \c rels so that they have the same columns corresponding to the table and the inner relation (so that the union can be perofrmed on theim in a straightforward way). Relations in \c rels must all have equal signature. Even if the function fails and false is returned, some relations may already be modified. They are in a valid state, but with different specification. */ static bool try_unify_specifications(ptr_vector & rels); bool try_modify_specification(const bool * table_cols); virtual bool can_swap(const relation_base & r) const { return &get_plugin()==&r.get_plugin(); } /** \brief Swap content of the current relation with the content of \c r. Both relations must come from the same plugin and be of the same signature. */ virtual void swap(relation_base & r); /** \brief Create a \c finite_product_relation object. */ finite_product_relation(finite_product_relation_plugin & p, const relation_signature & s, const bool * table_columns, table_plugin & tplugin, relation_plugin & oplugin, family_id other_kind); finite_product_relation(const finite_product_relation & r); virtual ~finite_product_relation(); public: context & get_context() const; finite_product_relation_plugin & get_plugin() const { return static_cast(relation_base::get_plugin()); } bool is_table_column(unsigned col_idx) const { return m_sig2table[col_idx]!=UINT_MAX; } const table_base & get_table() const { return *m_table; } const relation_base & get_inner_rel(table_element idx) const { SASSERT(idx(idx)); } /** The function calls garbage_collect, so the internal state may change when it is called. */ virtual bool empty() const; void reset() { m_table->reset(); garbage_collect(false); } virtual void add_fact(const relation_fact & f); virtual bool contains_fact(const relation_fact & f) const; virtual finite_product_relation * clone() const; virtual finite_product_relation * complement(func_decl* p) const; virtual void display(std::ostream & out) const; virtual void display_tuples(func_decl & pred, std::ostream & out) const; virtual unsigned get_size_estimate_rows() const { return m_table->get_size_estimate_rows(); } virtual unsigned get_size_estimate_bytes() const { return m_table->get_size_estimate_bytes(); } virtual void to_formula(expr_ref& fml) const; }; }; #endif /* _DL_FINITE_PRODUCT_RELATION_H_ */ z3-4.4.0/src/muz/rel/dl_sparse_table.h0000644000175000017500000004523212540347414017333 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_table.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-01. Revision History: --*/ #ifndef _DL_SPARSE_TABLE_H_ #define _DL_SPARSE_TABLE_H_ #include #include #include #include "ast.h" #include "bit_vector.h" #include "buffer.h" #include "hashtable.h" #include "map.h" #include "ref_vector.h" #include "vector.h" #include "dl_base.h" namespace datalog { class sparse_table; class sparse_table_plugin : public table_plugin { friend class sparse_table; protected: class join_project_fn; class union_fn; class transformer_fn; class rename_fn; class project_fn; class negation_filter_fn; class select_equal_and_project_fn; class negated_join_fn; typedef ptr_vector sp_table_vector; typedef map table_pool; table_pool m_pool; void recycle(sparse_table * t); void garbage_collect(); void reset(); static bool join_involves_functional(const table_signature & s1, const table_signature & s2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); public: typedef sparse_table table; sparse_table_plugin(relation_manager & manager); ~sparse_table_plugin(); virtual bool can_handle_signature(const table_signature & s) { return s.size()>0; } virtual table_base * mk_empty(const table_signature & s); sparse_table * mk_clone(const sparse_table & t); protected: virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols); virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta); virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols); virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); virtual table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, const table_element & value, unsigned col); virtual table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols); virtual table_intersection_join_filter_fn* mk_filter_by_negated_join_fn( const table_base & t, const table_base & src1, const table_base & src2, unsigned_vector const& t_cols, unsigned_vector const& src_cols, unsigned_vector const& src1_cols, unsigned_vector const& src2_cols); static sparse_table const& get(table_base const&); static sparse_table& get(table_base&); static sparse_table const* get(table_base const*); static sparse_table* get(table_base*); }; class entry_storage { public: typedef size_t store_offset; private: typedef svector storage; class offset_hash_proc { storage & m_storage; unsigned m_unique_entry_size; public: offset_hash_proc(storage & s, unsigned unique_entry_sz) : m_storage(s), m_unique_entry_size(unique_entry_sz) {} unsigned operator()(store_offset ofs) const { return string_hash(m_storage.c_ptr()+ofs, m_unique_entry_size, 0); } }; class offset_eq_proc { storage & m_storage; unsigned m_unique_entry_size; public: offset_eq_proc(storage & s, unsigned unique_entry_sz) : m_storage(s), m_unique_entry_size(unique_entry_sz) {} bool operator()(store_offset o1, store_offset o2) const { const char * base = m_storage.c_ptr(); return memcmp(base+o1, base+o2, m_unique_entry_size)==0; } }; typedef hashtable storage_indexer; static const store_offset NO_RESERVE = UINT_MAX; unsigned m_entry_size; unsigned m_unique_part_size; size_t m_data_size; /** Invariant: Every or all but one blocks of length \c m_entry_size in the \c m_data vector are unique sequences of bytes and have their offset stored in the \c m_data_indexer hashtable. If the offset of the last block is not stored in the hashtable, it is stored in the \c m_reserve variable. Otherwise \c m_reserve==NO_RESERVE. The size of m_data is actually 8 bytes larger than stated in m_data_size, so that we may deref an uint64 pointer at the end of the array. */ storage m_data; storage_indexer m_data_indexer; store_offset m_reserve; public: entry_storage(unsigned entry_size, unsigned functional_size = 0, unsigned init_size = 0) : m_entry_size(entry_size), m_unique_part_size(entry_size-functional_size), m_data_indexer(next_power_of_two(std::max(8u,init_size)), offset_hash_proc(m_data, m_unique_part_size), offset_eq_proc(m_data, m_unique_part_size)), m_reserve(NO_RESERVE) { SASSERT(entry_size>0); SASSERT(functional_size<=entry_size); resize_data(init_size); resize_data(0); } entry_storage(const entry_storage &s) : m_entry_size(s.m_entry_size), m_unique_part_size(s.m_unique_part_size), m_data_size(s.m_data_size), m_data(s.m_data), m_data_indexer(next_power_of_two(std::max(8u,s.entry_count())), offset_hash_proc(m_data, m_unique_part_size), offset_eq_proc(m_data, m_unique_part_size)), m_reserve(s.m_reserve) { store_offset after_last=after_last_offset(); for(store_offset i=0; i(this)->get(ofs); } unsigned entry_count() const { return m_data_indexer.size(); } store_offset after_last_offset() const { return (m_reserve==NO_RESERVE) ? m_data_size : m_reserve; } char * begin() { return get(0); } const char * begin() const { return get(0); } const char * after_last() const { return get(after_last_offset()); } bool has_reserve() const { return m_reserve!=NO_RESERVE; } store_offset reserve() const { SASSERT(has_reserve()); return m_reserve; } void ensure_reserve() { if(has_reserve()) { SASSERT(m_reserve==m_data_size-m_entry_size); return; } m_reserve = m_data_size; resize_data(m_data_size+m_entry_size); } /** \brief Return pointer to the reserve. The reserve must exist when the function is called. */ char * get_reserve_ptr() { SASSERT(has_reserve()); return &m_data.get(reserve()); } bool reserve_content_already_present() const { SASSERT(has_reserve()); return m_data_indexer.contains(reserve()); } bool find_reserve_content(store_offset & result) const { SASSERT(has_reserve()); storage_indexer::entry * indexer_entry = m_data_indexer.find_core(reserve()); if(!indexer_entry) { return false; } result = indexer_entry->get_data(); return true; } /** \brief Write fact \c f into the reserve at the end of the \c m_data storage. If the reserve does not exist, this function creates it. */ void write_into_reserve(const char * data) { ensure_reserve(); memcpy(get_reserve_ptr(), data, m_entry_size); } /** \brief If the fact in reserve is not in the table, insert it there and return true; otherwise return false. When a fact is inserted into the table, the reserve becomes part of the table and is no longer a reserve. */ bool insert_reserve_content(); store_offset insert_or_get_reserve_content(); bool remove_reserve_content(); /** Remove data at the offset \c ofs. Data with offset lower than \c ofs are not be modified by this function, data with higher offset may be moved. */ void remove_offset(store_offset ofs); //the following two operations allow breaking of the object invariant! void resize_data(size_t sz) { m_data_size = sz; if (sz + sizeof(uint64) < sz) { throw default_exception("overflow resizing data section for sparse table"); } m_data.resize(sz + sizeof(uint64)); } bool insert_offset(store_offset ofs) { return m_data_indexer.insert_if_not_there(ofs)==ofs; } }; class sparse_table : public table_base { friend class sparse_table_plugin; friend class sparse_table_plugin::join_project_fn; friend class sparse_table_plugin::union_fn; friend class sparse_table_plugin::transformer_fn; friend class sparse_table_plugin::rename_fn; friend class sparse_table_plugin::project_fn; friend class sparse_table_plugin::negation_filter_fn; friend class sparse_table_plugin::select_equal_and_project_fn; class our_iterator_core; class key_indexer; class general_key_indexer; class full_signature_key_indexer; typedef entry_storage::store_offset store_offset; class column_info { unsigned m_big_offset; unsigned m_small_offset; uint64 m_mask; uint64 m_write_mask; public: unsigned m_offset; //!< in bits unsigned m_length; //!< in bits column_info(unsigned offset, unsigned length) \ : m_big_offset(offset/8), m_small_offset(offset%8), m_mask( length==64 ? ULLONG_MAX : (static_cast(1)<(rec+m_big_offset); uint64 res = *ptr; res>>=m_small_offset; res&=m_mask; return res; } void set(char * rec, table_element val) const { SASSERT( (val&~m_mask)==0 ); //the value fits into the column uint64 * ptr = reinterpret_cast(rec+m_big_offset); *ptr&=m_write_mask; *ptr|=val< { void make_byte_aligned_end(unsigned col_index); public: unsigned m_entry_size; /** Number of last bytes which correspond to functional columns in the signature. */ unsigned m_functional_part_size; unsigned m_functional_col_cnt; column_layout(const table_signature & sig); table_element get(const char * rec, unsigned col) const { return (*this)[col].get(rec); } void set(char * rec, unsigned col, table_element val) const { return (*this)[col].set(rec, val); } }; typedef svector key_spec; //sequence of columns in a key typedef svector key_value; //values of key columns typedef map, vector_eq_proc > key_index_map; static const store_offset NO_RESERVE = UINT_MAX; column_layout m_column_layout; unsigned m_fact_size; entry_storage m_data; mutable key_index_map m_key_indexes; const char * get_at_offset(store_offset i) const { return m_data.get(i); } table_element get_cell(store_offset ofs, unsigned column) const { return m_column_layout.get(m_data.get(ofs), column); } void set_cell(store_offset ofs, unsigned column, table_element val) { m_column_layout.set(m_data.get(ofs), column, val); } void write_into_reserve(const table_element* f); /** \brief Return reference to an indexer over columns in \c key_cols. An indexer can retrieve a sequence of offsets that with \c key_cols columns equal to the specified key. Indexers are populated lazily -- they remember the position of the last fact they contain, and when an indexer is retrieved by the \c get_key_indexer function, all the new facts are added into the indexer. When a fact is removed from the table, all indexers are destroyed. This is not an extra expense in the current use scenario, because we first perform all fact removals and do the joins only after that (joins are the only operations that lead to index construction). */ key_indexer& get_key_indexer(unsigned key_len, const unsigned * key_cols) const; void reset_indexes(); static void copy_columns(const column_layout & src_layout, const column_layout & dest_layout, unsigned start_index, unsigned after_last, const char * src, char * dest, unsigned & dest_idx, unsigned & pre_projection_idx, const unsigned * & next_removed); /** \c array \c removed_cols contains column indexes to be removed in ascending order and is terminated by a number greated than the highest column index of a join the the two tables. This is to simplify the traversal of the array when building facts. */ static void concatenate_rows(const column_layout & layout1, const column_layout & layout2, const column_layout & layout_res, const char * ptr1, const char * ptr2, char * res, const unsigned * removed_cols); /** \brief Perform join-project between t1 and t2 iterating through t1 and retrieving relevant columns from t2 using indexing. \c array \c removed_cols contains column indexes to be removed in ascending order and is terminated by a number greated than the highest column index of a join the the two tables. This is to simplify the traversal of the array when building facts. \c tables_swapped value means that the resulting facts should contain facts from t2 first, instead of the default behavior that would concatenate the two facts as \c (t1,t2). \remark The function is called \c self_agnostic_join since, unlike the virtual method \c join, it is static and therefore allows to easily swap the roles of the two joined tables (the indexed and iterated one) in a way that is expected to give better performance. */ static void self_agnostic_join_project(const sparse_table & t1, const sparse_table & t2, unsigned joined_col_cnt, const unsigned * t1_joined_cols, const unsigned * t2_joined_cols, const unsigned * removed_cols, bool tables_swapped, sparse_table & result); /** If the fact at \c data (in table's native representation) is not in the table, add it and return true. Otherwise return false. */ bool add_fact(const char * data); bool add_reserve_content(); void garbage_collect(); sparse_table(sparse_table_plugin & p, const table_signature & sig, unsigned init_capacity=0); sparse_table(const sparse_table & t); virtual ~sparse_table(); public: virtual void deallocate() { get_plugin().recycle(this); } unsigned row_count() const { return m_data.entry_count(); } sparse_table_plugin & get_plugin() const { return static_cast(table_base::get_plugin()); } virtual bool empty() const { return row_count()==0; } virtual void add_fact(const table_fact & f); virtual bool contains_fact(const table_fact & f) const; virtual bool fetch_fact(table_fact & f) const; virtual void ensure_fact(const table_fact & f); virtual void remove_fact(const table_element* fact); virtual void reset(); virtual table_base * clone() const; virtual table_base::iterator begin() const; virtual table_base::iterator end() const; virtual unsigned get_size_estimate_rows() const { return row_count(); } virtual unsigned get_size_estimate_bytes() const; virtual bool knows_exact_size() const { return true; } }; }; #endif /* _DL_SPARSE_TABLE_H_ */ z3-4.4.0/src/muz/rel/dl_base.cpp0000644000175000017500000004647312540347414016144 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_base.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #include"ast_pp.h" #include"union_find.h" #include"vector.h" #include"dl_context.h" #include"dl_base.h" #include"bool_rewriter.h" #include"dl_relation_manager.h" #include namespace datalog { void universal_delete(relation_base* ptr) { ptr->deallocate(); } void universal_delete(table_base* ptr) { ptr->deallocate(); } void dealloc_ptr_vector_content(ptr_vector & v) { ptr_vector::iterator it = v.begin(); ptr_vector::iterator end = v.end(); for(; it!=end; ++it) { (*it)->deallocate(); } } void get_renaming_args(const unsigned_vector & map, const relation_signature & orig_sig, expr_ref_vector & renaming_arg) { ast_manager & m = renaming_arg.get_manager(); unsigned sz = map.size(); unsigned ofs = sz-1; renaming_arg.resize(sz, static_cast(0)); for(unsigned i=0; i reset_fn = get_manager().mk_filter_interpreted_fn(*this, bottom_ref); if(!reset_fn) { NOT_IMPLEMENTED_YET(); } (*reset_fn)(*this); } void table_signature::from_join(const table_signature & s1, const table_signature & s2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, table_signature & result) { result.reset(); unsigned s1sz=s1.size(); unsigned s2sz=s2.size(); unsigned s1first_func=s1sz-s1.functional_columns(); unsigned s2first_func=s2sz-s2.functional_columns(); for(unsigned i=0; i=col_cnt); result.set_functional_columns(func_cnt-col_cnt); } } void table_signature::from_project_with_reduce(const table_signature & src, unsigned col_cnt, const unsigned * removed_cols, table_signature & result) { signature_base::from_project(src, col_cnt, removed_cols, result); unsigned remaining_fun = src.functional_columns(); unsigned first_src_fun = src.first_functional(); for(int i=col_cnt-1; i>=0; i--) { if(removed_cols[i] remaining_in_equivalence_class; remaining_in_equivalence_class.resize(join_sig_sz, 0); bool merging_rows_can_happen = false; union_find_default_ctx uf_ctx; union_find<> uf(uf_ctx); //the numbers in uf correspond to column indexes after the join for(unsigned i=0; icols1[i]) ? cols1[i] : (first_func_ofs+cols1[i]-s1_first_func); unsigned idx2 = (s2_first_func>cols2[i]) ? (second_ofs+cols2[i]) : (second_func_ofs+cols2[i]-s2_first_func); uf.merge(idx1, idx2); } for(unsigned i=0; i=first_func_ofs) { //removing functional columns won't make us merge rows continue; } unsigned eq_class_idx = uf.find(rc); if(remaining_in_equivalence_class[eq_class_idx]>1) { remaining_in_equivalence_class[eq_class_idx]--; } else { merging_rows_can_happen = true; break; } } if(merging_rows_can_happen) { //this one marks all columns as non-functional from_project(aux, removed_col_cnt, removed_cols, result); SASSERT(result.functional_columns()==0); } else { //this one preserves columns to be functional from_project_with_reduce(aux, removed_col_cnt, removed_cols, result); } } // ----------------------------------- // // table_base // // ----------------------------------- //here we give generic implementation of table operations using iterators bool table_base::empty() const { return begin()==end(); } void table_base::remove_facts(unsigned fact_cnt, const table_fact * facts) { for(unsigned i=0; i to_remove; table_base::iterator it = begin(); table_base::iterator iend = end(); table_fact row; for(; it!=iend; ++it) { it->get_fact(row); to_remove.push_back(row); } remove_facts(to_remove.size(), to_remove.c_ptr()); } bool table_base::contains_fact(const table_fact & f) const { iterator it = begin(); iterator iend = end(); table_fact row; for(; it!=iend; ++it) { it->get_fact(row); if(vectors_equal(row, f)) { return true; } } return false; } bool table_base::fetch_fact(table_fact & f) const { if(get_signature().functional_columns()==0) { return contains_fact(f); } else { unsigned sig_sz = get_signature().size(); unsigned non_func_cnt = sig_sz-get_signature().functional_columns(); table_base::iterator it = begin(); table_base::iterator iend = end(); table_fact row; for(; it!=iend; ++it) { it->get_fact(row); bool differs = false; for(unsigned i=0; iget_fact(row); res->add_new_fact(row); } return res; } /** \brief Default method for complementation. It assumes that the compiler creates only tables with at most one column (0 or 1 columns). Complementation of tables with more than one columns is transformed into a cross product of complements and/or difference. */ table_base * table_base::complement(func_decl* p, const table_element * func_columns) const { const table_signature & sig = get_signature(); SASSERT(sig.functional_columns()==0 || func_columns!=0); SASSERT(sig.first_functional() <= 1); table_base * res = get_plugin().mk_empty(sig); table_fact fact; fact.resize(sig.first_functional()); fact.append(sig.functional_columns(), func_columns); if (sig.first_functional() == 0) { if (empty()) { res->add_fact(fact); } return res; } VERIFY(sig.first_functional() == 1); uint64 upper_bound = get_signature()[0]; bool empty_table = empty(); if (upper_bound > (1 << 18)) { std::ostringstream buffer; buffer << "creating large table of size " << upper_bound; if (p) buffer << " for relation " << p->get_name(); warning_msg(buffer.str().c_str()); } for(table_element i = 0; i < upper_bound; i++) { fact[0] = i; if(empty_table || !contains_fact(fact)) { res->add_fact(fact); } } return res; } void table_base::display(std::ostream & out) const { out << "table with signature "; print_container(get_signature(), out); out << ":\n"; iterator it = begin(); iterator iend = end(); for(; it!=iend; ++it) { const row_interface & r = *it; r.display(out); } out << "\n"; } class table_base::row_interface::fact_row_iterator : public table_base::row_iterator_core { const row_interface & m_parent; unsigned m_index; protected: virtual bool is_finished() const { return m_index==m_parent.size(); } public: fact_row_iterator(const row_interface & row, bool finished) : m_parent(row), m_index(finished ? row.size() : 0) {} virtual table_element operator*() { SASSERT(!is_finished()); return m_parent[m_index]; } virtual void operator++() { m_index++; SASSERT(m_index<=m_parent.size()); } }; table_base::row_iterator table_base::row_interface::begin() const { return row_iterator(alloc(fact_row_iterator, *this, false)); } table_base::row_iterator table_base::row_interface::end() const { return row_iterator(alloc(fact_row_iterator, *this, true)); } void table_base::row_interface::get_fact(table_fact & result) const { result.reset(); unsigned n=size(); for(unsigned i=0; i t2[col] } T3 = { t1 | (t1,t2) in T2 } T4 = T \ T3 The point of this reference implementation is to show that the minimum requires negation (set difference). This is relevant for fixed point computations. */ virtual table_base * reference_implementation(const table_base & t) { relation_manager & manager = t.get_manager(); scoped_ptr join_fn = manager.mk_join_fn(t, t, m_group_by_cols, m_group_by_cols); scoped_rel join_table = (*join_fn)(t, t); table_base::iterator join_table_it = join_table->begin(); table_base::iterator join_table_end = join_table->end(); table_fact row; table_element i, j; for (; join_table_it != join_table_end; ++join_table_it) { join_table_it->get_fact(row); i = row[m_col]; j = row[t.num_columns() + m_col]; if (i > j) { continue; } join_table->remove_fact(row); } unsigned_vector cols(t.num_columns()); for (unsigned k = 0; k < cols.size(); ++k) { cols[k] = cols.size() + k; SASSERT(cols[k] < join_table->num_columns()); } scoped_ptr project_fn = manager.mk_project_fn(*join_table, cols); scoped_rel gt_table = (*project_fn)(*join_table); for (unsigned k = 0; k < cols.size(); ++k) { cols[k] = k; SASSERT(cols[k] < t.num_columns()); SASSERT(cols[k] < gt_table->num_columns()); } table_base * result = t.clone(); scoped_ptr diff_fn = manager.mk_filter_by_negation_fn(*result, *gt_table, cols, cols); (*diff_fn)(*result, *gt_table); return result; } typedef map < table_fact, table_element, svector_hash_proc, vector_eq_proc > group_map; // Thanks to Nikolaj who kindly helped with the second reference implementation! virtual table_base * reference_implementation_with_hash(const table_base & t) { group_map group; table_base::iterator it = t.begin(); table_base::iterator end = t.end(); table_fact row, row2; table_element current_value, min_value; for (; it != end; ++it) { it->get_fact(row); current_value = row[m_col]; group_by(row, row2); group_map::entry* entry = group.find_core(row2); if (!entry) { group.insert(row2, current_value); } else if (entry->get_data().m_value > current_value) { entry->get_data().m_value = current_value; } } table_base* result = t.get_plugin().mk_empty(m_sig); table_base::iterator it2 = t.begin(); for (; it2 != end; ++it2) { it2->get_fact(row); current_value = row[m_col]; group_by(row, row2); VERIFY(group.find(row2, min_value)); if (min_value == current_value) { result->add_fact(row); } } return result; } void group_by(table_fact const& in, table_fact& out) { out.reset(); for (unsigned i = 0; i < m_group_by_cols.size(); ++i) { out.push_back(in[m_group_by_cols[i]]); } } }; table_min_fn * table_plugin::mk_min_fn(const table_base & t, unsigned_vector & group_by_cols, const unsigned col) { return alloc(table_plugin::min_fn, t.get_signature(), group_by_cols, col); } } z3-4.4.0/src/muz/rel/dl_compiler.cpp0000644000175000017500000015672012540347414017041 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_compiler.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #include #include"ref_vector.h" #include"dl_context.h" #include"rel_context.h" #include"dl_rule.h" #include"dl_util.h" #include"dl_compiler.h" #include"ast_pp.h" #include"ast_smt2_pp.h" namespace datalog { void compiler::reset() { m_pred_regs.reset(); } void compiler::ensure_predicate_loaded(func_decl * pred, instruction_block & acc) { pred2idx::obj_map_entry * e = m_pred_regs.insert_if_not_there2(pred, UINT_MAX); if(e->get_data().m_value!=UINT_MAX) { //predicate is already loaded return; } relation_signature sig; m_context.get_rel_context()->get_rmanager().from_predicate(pred, sig); reg_idx reg = get_fresh_register(sig); e->get_data().m_value=reg; acc.push_back(instruction::mk_load(m_context.get_manager(), pred, reg)); } void compiler::make_join(reg_idx t1, reg_idx t2, const variable_intersection & vars, reg_idx & result, bool reuse_t1, instruction_block & acc) { relation_signature res_sig; relation_signature::from_join(m_reg_signatures[t1], m_reg_signatures[t2], vars.size(), vars.get_cols1(), vars.get_cols2(), res_sig); result = get_register(res_sig, reuse_t1, t1); acc.push_back(instruction::mk_join(t1, t2, vars.size(), vars.get_cols1(), vars.get_cols2(), result)); } void compiler::make_join_project(reg_idx t1, reg_idx t2, const variable_intersection & vars, const unsigned_vector & removed_cols, reg_idx & result, bool reuse_t1, instruction_block & acc) { relation_signature aux_sig; relation_signature sig1 = m_reg_signatures[t1]; relation_signature sig2 = m_reg_signatures[t2]; relation_signature::from_join(sig1, sig2, vars.size(), vars.get_cols1(), vars.get_cols2(), aux_sig); relation_signature res_sig; relation_signature::from_project(aux_sig, removed_cols.size(), removed_cols.c_ptr(), res_sig); result = get_register(res_sig, reuse_t1, t1); acc.push_back(instruction::mk_join_project(t1, t2, vars.size(), vars.get_cols1(), vars.get_cols2(), removed_cols.size(), removed_cols.c_ptr(), result)); } void compiler::make_min(reg_idx source, reg_idx & target, const unsigned_vector & group_by_cols, const unsigned min_col, instruction_block & acc) { target = get_register(m_reg_signatures[source], true, source); acc.push_back(instruction::mk_min(source, target, group_by_cols, min_col)); } void compiler::make_filter_interpreted_and_project(reg_idx src, app_ref & cond, const unsigned_vector & removed_cols, reg_idx & result, bool reuse, instruction_block & acc) { SASSERT(!removed_cols.empty()); relation_signature res_sig; relation_signature::from_project(m_reg_signatures[src], removed_cols.size(), removed_cols.c_ptr(), res_sig); result = get_register(res_sig, reuse, src); acc.push_back(instruction::mk_filter_interpreted_and_project(src, cond, removed_cols.size(), removed_cols.c_ptr(), result)); } void compiler::make_select_equal_and_project(reg_idx src, const relation_element val, unsigned col, reg_idx & result, bool reuse, instruction_block & acc) { relation_signature res_sig; relation_signature::from_project(m_reg_signatures[src], 1, &col, res_sig); result = get_register(res_sig, reuse, src); acc.push_back(instruction::mk_select_equal_and_project(m_context.get_manager(), src, val, col, result)); } void compiler::make_clone(reg_idx src, reg_idx & result, instruction_block & acc) { relation_signature sig = m_reg_signatures[src]; result = get_fresh_register(sig); acc.push_back(instruction::mk_clone(src, result)); } void compiler::make_union(reg_idx src, reg_idx tgt, reg_idx delta, bool use_widening, instruction_block & acc) { SASSERT(m_reg_signatures[src]==m_reg_signatures[tgt]); SASSERT(delta==execution_context::void_register || m_reg_signatures[src]==m_reg_signatures[delta]); if (use_widening) { acc.push_back(instruction::mk_widen(src, tgt, delta)); } else { acc.push_back(instruction::mk_union(src, tgt, delta)); } } void compiler::make_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols, reg_idx & result, bool reuse, instruction_block & acc) { SASSERT(col_cnt>0); relation_signature res_sig; relation_signature::from_project(m_reg_signatures[src], col_cnt, removed_cols, res_sig); result = get_register(res_sig, reuse, src); acc.push_back(instruction::mk_projection(src, col_cnt, removed_cols, result)); } compiler::reg_idx compiler::get_fresh_register(const relation_signature & sig) { //since we might be resizing the m_reg_signatures vector, the argument must not point inside it SASSERT((&sig>=m_reg_signatures.end()) || (&sig & acis0, reg_idx & result, bool & dealloc, instruction_block & acc) { TRACE("dl", tout << mk_pp(head_pred, m_context.get_manager()) << "\n";); unsigned col_cnt = acis0.size(); reg_idx curr = src; relation_signature empty_signature; relation_signature * curr_sig; if(curr!=execution_context::void_register) { curr_sig = & m_reg_signatures[curr]; } else { curr_sig = & empty_signature; } unsigned src_col_cnt=curr_sig->size(); svector acis(acis0); int2int handled_unbound; //first remove unused source columns int_set referenced_src_cols; for(unsigned i=0; isize(); if(acis[i].kind==ACK_CONSTANT) { make_add_constant_column(head_pred, curr, acis[i].domain, acis[i].constant, curr, dealloc, acc); } else { SASSERT(acis[i].kind==ACK_UNBOUND_VAR); make_add_unbound_column(compiled_rule, i, head_pred, curr, acis[i].domain, curr, dealloc, acc); handled_unbound.insert(acis[i].var_index,bound_column_index); } curr_sig = & m_reg_signatures[curr]; SASSERT(bound_column_index==curr_sig->size()-1); } SASSERT((*curr_sig)[bound_column_index]==acis[i].domain); acis[i].kind=ACK_BOUND_VAR; acis[i].source_column=bound_column_index; } //duplicate needed source columns int_set used_cols; for(unsigned i=0; isize()-1; SASSERT((*curr_sig)[bound_column_index]==acis[i].domain); acis[i].source_column=bound_column_index; } //reorder source columns to match target SASSERT(curr_sig->size()==col_cnt); //now the intermediate table is a permutation for(unsigned i=0; i=i); //columns below i are already reordered SASSERT(nextget_num_args(); for(unsigned i = 0; iget_arg(i); if (is_var(e) && globals.get(to_var(e)->get_idx()) > 0) { globals.update(to_var(e)->get_idx(), -1); res.push_back(i + ofs); } } } void compiler::get_local_indexes_for_projection(rule * r, unsigned_vector & res) { SASSERT(r->get_positive_tail_size()==2); rule_counter counter; // leave one column copy per var in the head (avoids later duplication) counter.count_vars(r->get_head(), -1); // take interp & neg preds into account (at least 1 column copy if referenced) unsigned n = r->get_tail_size(); if (n > 2) { rule_counter counter_tail; for (unsigned i = 2; i < n; ++i) { counter_tail.count_vars(r->get_tail(i)); } rule_counter::iterator I = counter_tail.begin(), E = counter_tail.end(); for (; I != E; ++I) { int& n = counter.get(I->m_key); if (n == 0) n = -1; } } app * t1 = r->get_tail(0); app * t2 = r->get_tail(1); counter.count_vars(t1); counter.count_vars(t2); get_local_indexes_for_projection(t1, counter, 0, res); get_local_indexes_for_projection(t2, counter, t1->get_num_args(), res); } void compiler::find_min_aggregates(const rule * r, ptr_vector& min_aggregates) { unsigned ut_len = r->get_uninterpreted_tail_size(); unsigned ft_len = r->get_tail_size(); // full tail func_decl * aggregate; for (unsigned tail_index = ut_len; tail_index < ft_len; ++tail_index) { aggregate = r->get_tail(tail_index)->get_decl(); if (dl_decl_plugin::is_aggregate(aggregate)) { min_aggregates.push_back(aggregate); } } } bool compiler::prepare_min_aggregate(const func_decl * decl, const ptr_vector& min_aggregates, unsigned_vector & group_by_cols, unsigned & min_col) { for (unsigned i = 0; i < min_aggregates.size(); ++i) { if (dl_decl_plugin::min_func_decl(min_aggregates[i]) == decl) { group_by_cols = dl_decl_plugin::group_by_cols(min_aggregates[i]); min_col = dl_decl_plugin::min_col(min_aggregates[i]); return true; } } return false; } void compiler::compile_rule_evaluation_run(rule * r, reg_idx head_reg, const reg_idx * tail_regs, reg_idx delta_reg, bool use_widening, instruction_block & acc) { ast_manager & m = m_context.get_manager(); m_instruction_observer.start_rule(r); const app * h = r->get_head(); unsigned head_len = h->get_num_args(); func_decl * head_pred = h->get_decl(); TRACE("dl", r->display(m_context, tout); ); unsigned pt_len = r->get_positive_tail_size(); SASSERT(pt_len<=2); //we require rules to be processed by the mk_simple_joins rule transformer plugin reg_idx single_res; expr_ref_vector single_res_expr(m); //used to save on filter_identical instructions where the check is already done //by the join operation unsigned second_tail_arg_ofs; // whether to dealloc the previous result bool dealloc = true; // setup information for min aggregation ptr_vector min_aggregates; find_min_aggregates(r, min_aggregates); unsigned_vector group_by_cols; unsigned min_col; if(pt_len == 2) { reg_idx t1_reg=tail_regs[0]; reg_idx t2_reg=tail_regs[1]; app * a1 = r->get_tail(0); app * a2 = r->get_tail(1); SASSERT(m_reg_signatures[t1_reg].size()==a1->get_num_args()); SASSERT(m_reg_signatures[t2_reg].size()==a2->get_num_args()); if (prepare_min_aggregate(a1->get_decl(), min_aggregates, group_by_cols, min_col)) { make_min(t1_reg, single_res, group_by_cols, min_col, acc); } if (prepare_min_aggregate(a2->get_decl(), min_aggregates, group_by_cols, min_col)) { make_min(t2_reg, single_res, group_by_cols, min_col, acc); } variable_intersection a1a2(m_context.get_manager()); a1a2.populate(a1,a2); unsigned_vector removed_cols; get_local_indexes_for_projection(r, removed_cols); if(removed_cols.empty()) { make_join(t1_reg, t2_reg, a1a2, single_res, false, acc); } else { make_join_project(t1_reg, t2_reg, a1a2, removed_cols, single_res, false, acc); } unsigned rem_index = 0; unsigned rem_sz = removed_cols.size(); unsigned a1len=a1->get_num_args(); for(unsigned i=0; i=i); if(rem_indexget_arg(i)); } second_tail_arg_ofs = single_res_expr.size(); unsigned a2len=a2->get_num_args(); for(unsigned i=0; i=i+a1len); if(rem_indexget_arg(i)); } SASSERT(rem_index==rem_sz); } else if(pt_len==1) { app * a = r->get_tail(0); single_res = tail_regs[0]; dealloc = false; if (prepare_min_aggregate(a->get_decl(), min_aggregates, group_by_cols, min_col)) { make_min(single_res, single_res, group_by_cols, min_col, acc); } SASSERT(m_reg_signatures[single_res].size() == a->get_num_args()); unsigned n=a->get_num_args(); for(unsigned i=0; iget_arg(i); if(is_app(arg)) { app * c = to_app(arg); //argument is a constant SASSERT(m.is_value(c)); make_select_equal_and_project(single_res, c, single_res_expr.size(), single_res, dealloc, acc); dealloc = true; } else { SASSERT(is_var(arg)); single_res_expr.push_back(arg); } } } else { SASSERT(pt_len==0); //single_res register should never be used in this case single_res=execution_context::void_register; dealloc = false; } add_unbound_columns_for_negation(r, head_pred, single_res, single_res_expr, dealloc, acc); int2ints var_indexes; reg_idx filtered_res = single_res; { //enforce equality to constants unsigned srlen=single_res_expr.size(); SASSERT((single_res==execution_context::void_register) ? (srlen==0) : (srlen==m_reg_signatures[single_res].size())); for(unsigned i=0; iget_idx(); int2ints::entry * e = var_indexes.insert_if_not_there2(var_num, unsigned_vector()); e->get_data().m_value.push_back(i); } } } //enforce equality of columns int2ints::iterator vit=var_indexes.begin(); int2ints::iterator vend=var_indexes.end(); for(; vit!=vend; ++vit) { int2ints::key_data & k = *vit; unsigned_vector & indexes = k.m_value; if(indexes.size()==1) { continue; } SASSERT(indexes.size()>1); if(pt_len==2 && indexes[0]=second_tail_arg_ofs) { //If variable appears in multiple tails, the identicity will already be enforced by join. //(If behavior the join changes so that it is not enforced anymore, remove this //condition!) continue; } if (!dealloc) make_clone(filtered_res, filtered_res, acc); acc.push_back(instruction::mk_filter_identical(filtered_res, indexes.size(), indexes.c_ptr())); dealloc = true; } // add unbounded columns for interpreted filter unsigned ut_len = r->get_uninterpreted_tail_size(); unsigned ft_len = r->get_tail_size(); // full tail ptr_vector tail; for (unsigned tail_index = ut_len; tail_index < ft_len; ++tail_index) { if (!r->is_min_tail(tail_index)) tail.push_back(r->get_tail(tail_index)); } expr_ref_vector binding(m); if (!tail.empty()) { app_ref filter_cond(tail.size() == 1 ? to_app(tail.back()) : m.mk_and(tail.size(), tail.c_ptr()), m); m_free_vars(filter_cond); // create binding binding.resize(m_free_vars.size()+1); for (unsigned v = 0; v < m_free_vars.size(); ++v) { if (!m_free_vars[v]) continue; int2ints::entry * entry = var_indexes.find_core(v); unsigned src_col; if (entry) { src_col = entry->get_data().m_value.back(); } else { // we have an unbound variable, so we add an unbound column for it relation_sort unbound_sort = m_free_vars[v]; make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, filtered_res, dealloc, acc); src_col = single_res_expr.size(); single_res_expr.push_back(m.mk_var(v, unbound_sort)); entry = var_indexes.insert_if_not_there2(v, unsigned_vector()); entry->get_data().m_value.push_back(src_col); } relation_sort var_sort = m_reg_signatures[filtered_res][src_col]; binding[m_free_vars.size()-v] = m.mk_var(src_col, var_sort); } } // add at least one column for the negative filter if (pt_len != ut_len && filtered_res == execution_context::void_register) { relation_signature empty_signature; make_full_relation(head_pred, empty_signature, filtered_res, acc); } //enforce negative predicates for (unsigned i = pt_len; iget_tail(i); func_decl * neg_pred = neg_tail->get_decl(); variable_intersection neg_intersection(m_context.get_manager()); neg_intersection.populate(single_res_expr, neg_tail); unsigned_vector t_cols(neg_intersection.size(), neg_intersection.get_cols1()); unsigned_vector neg_cols(neg_intersection.size(), neg_intersection.get_cols2()); unsigned neg_len = neg_tail->get_num_args(); for (unsigned i = 0; iget_arg(i); if (is_var(e)) { continue; } SASSERT(is_app(e)); relation_sort arg_sort; m_context.get_rel_context()->get_rmanager().from_predicate(neg_pred, i, arg_sort); make_add_constant_column(head_pred, filtered_res, arg_sort, to_app(e), filtered_res, dealloc, acc); t_cols.push_back(single_res_expr.size()); neg_cols.push_back(i); single_res_expr.push_back(e); } SASSERT(t_cols.size() == neg_cols.size()); reg_idx neg_reg = m_pred_regs.find(neg_pred); if (!dealloc) make_clone(filtered_res, filtered_res, acc); acc.push_back(instruction::mk_filter_by_negation(filtered_res, neg_reg, t_cols.size(), t_cols.c_ptr(), neg_cols.c_ptr())); dealloc = true; } // enforce interpreted tail predicates if (!tail.empty()) { app_ref filter_cond(tail.size() == 1 ? to_app(tail.back()) : m.mk_and(tail.size(), tail.c_ptr()), m); // check if there are any columns to remove unsigned_vector remove_columns; { unsigned_vector var_idx_to_remove; m_free_vars(r->get_head()); for (int2ints::iterator I = var_indexes.begin(), E = var_indexes.end(); I != E; ++I) { unsigned var_idx = I->m_key; if (!m_free_vars.contains(var_idx)) { unsigned_vector & cols = I->m_value; for (unsigned i = 0; i < cols.size(); ++i) { remove_columns.push_back(cols[i]); } var_idx_to_remove.push_back(var_idx); } } for (unsigned i = 0; i < var_idx_to_remove.size(); ++i) { var_indexes.remove(var_idx_to_remove[i]); } // update column idx for after projection state if (!remove_columns.empty()) { unsigned_vector offsets; offsets.resize(single_res_expr.size(), 0); for (unsigned i = 0; i < remove_columns.size(); ++i) { for (unsigned col = remove_columns[i]; col < offsets.size(); ++col) { ++offsets[col]; } } for (int2ints::iterator I = var_indexes.begin(), E = var_indexes.end(); I != E; ++I) { unsigned_vector & cols = I->m_value; for (unsigned i = 0; i < cols.size(); ++i) { cols[i] -= offsets[cols[i]]; } } } } expr_ref renamed(m); m_context.get_var_subst()(filter_cond, binding.size(), binding.c_ptr(), renamed); app_ref app_renamed(to_app(renamed), m); if (remove_columns.empty()) { if (!dealloc) make_clone(filtered_res, filtered_res, acc); acc.push_back(instruction::mk_filter_interpreted(filtered_res, app_renamed)); } else { std::sort(remove_columns.begin(), remove_columns.end()); make_filter_interpreted_and_project(filtered_res, app_renamed, remove_columns, filtered_res, dealloc, acc); } dealloc = true; } #if 0 // this version is potentially better for non-symbolic tables, // since it constraints each unbound column at a time (reducing the // size of intermediate results). unsigned ft_len=r->get_tail_size(); //full tail for(unsigned tail_index=ut_len; tail_indexget_tail(tail_index); m_free_vars(t); if (m_free_vars.empty()) { expr_ref simplified(m); m_context.get_rewriter()(t, simplified); if(m.is_true(simplified)) { //this tail element is always true continue; } //the tail of this rule is never satisfied SASSERT(m.is_false(simplified)); goto finish; } //determine binding size unsigned max_var = m_free_vars.size(); while (max_var > 0 && !m_free_vars[max_var-1]) --max_var; //create binding expr_ref_vector binding(m); binding.resize(max_var); for(unsigned v = 0; v < max_var; ++v) { if (!m_free_vars[v]) { continue; } int2ints::entry * e = var_indexes.find_core(v); if(!e) { //we have an unbound variable, so we add an unbound column for it relation_sort unbound_sort = m_free_vars[v]; reg_idx new_reg; TRACE("dl", tout << mk_pp(head_pred, m_context.get_manager()) << "\n";); bool new_dealloc; make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, new_reg, new_dealloc, acc); if (dealloc) make_dealloc_non_void(filtered_res, acc); dealloc = new_dealloc; filtered_res = new_reg; // here filtered_res value gets changed !! unsigned unbound_column_index = single_res_expr.size(); single_res_expr.push_back(m.mk_var(v, unbound_sort)); e = var_indexes.insert_if_not_there2(v, unsigned_vector()); e->get_data().m_value.push_back(unbound_column_index); } unsigned src_col=e->get_data().m_value.back(); relation_sort var_sort = m_reg_signatures[filtered_res][src_col]; binding[max_var-v]=m.mk_var(src_col, var_sort); } expr_ref renamed(m); m_context.get_var_subst()(t, binding.size(), binding.c_ptr(), renamed); app_ref app_renamed(to_app(renamed), m); if (!dealloc) make_clone(filtered_res, filtered_res, acc); acc.push_back(instruction::mk_filter_interpreted(filtered_res, app_renamed)); dealloc = true; } #endif { //put together the columns of head relation relation_signature & head_sig = m_reg_signatures[head_reg]; svector head_acis; unsigned_vector head_src_cols; for(unsigned i=0; iget_arg(i); if(is_var(exp)) { unsigned var_num=to_var(exp)->get_idx(); int2ints::entry * e = var_indexes.find_core(var_num); if(e) { unsigned_vector & binding_indexes = e->get_data().m_value; aci.kind=ACK_BOUND_VAR; aci.source_column=binding_indexes.back(); SASSERT(aci.source_column1) { //if possible, we do not want multiple head columns //point to a single column in the intermediate table, //since then we would have to duplicate the column //(and remove columns we did not point to at all) binding_indexes.pop_back(); } } else { aci.kind=ACK_UNBOUND_VAR; aci.var_index=var_num; } } else { SASSERT(is_app(exp)); SASSERT(m_context.get_decl_util().is_numeral_ext(exp)); aci.kind=ACK_CONSTANT; aci.constant=to_app(exp); } head_acis.push_back(aci); } SASSERT(head_acis.size()==head_len); reg_idx new_head_reg; make_assembling_code(r, head_pred, filtered_res, head_acis, new_head_reg, dealloc, acc); //update the head relation make_union(new_head_reg, head_reg, delta_reg, use_widening, acc); if (dealloc) make_dealloc_non_void(new_head_reg, acc); } // finish: m_instruction_observer.finish_rule(); } void compiler::add_unbound_columns_for_negation(rule* r, func_decl* pred, reg_idx& single_res, expr_ref_vector& single_res_expr, bool & dealloc, instruction_block & acc) { uint_set pos_vars; u_map neg_vars; ast_manager& m = m_context.get_manager(); unsigned pt_len = r->get_positive_tail_size(); unsigned ut_len = r->get_uninterpreted_tail_size(); // no negated predicates if (pt_len == ut_len) return; // populate negative variables: for (unsigned i = pt_len; i < ut_len; ++i) { app * neg_tail = r->get_tail(i); unsigned neg_len = neg_tail->get_num_args(); for (unsigned j = 0; j < neg_len; ++j) { expr * e = neg_tail->get_arg(j); if (is_var(e)) { unsigned idx = to_var(e)->get_idx(); neg_vars.insert(idx, e); } } } // populate positive variables: for (unsigned i = 0; i < single_res_expr.size(); ++i) { expr* e = single_res_expr[i].get(); if (is_var(e)) { pos_vars.insert(to_var(e)->get_idx()); } } // add negative variables that are not in positive u_map::iterator it = neg_vars.begin(), end = neg_vars.end(); for (; it != end; ++it) { unsigned v = it->m_key; expr* e = it->m_value; if (!pos_vars.contains(v)) { single_res_expr.push_back(e); make_add_unbound_column(r, v, pred, single_res, m.get_sort(e), single_res, dealloc, acc); TRACE("dl", tout << "Adding unbound column: " << mk_pp(e, m) << "\n";); } } } void compiler::compile_rule_evaluation(rule * r, const pred2idx * input_deltas, reg_idx output_delta, bool use_widening, instruction_block & acc) { typedef std::pair tail_delta_info; //(delta register, tail index) typedef svector tail_delta_infos; unsigned rule_len = r->get_uninterpreted_tail_size(); reg_idx head_reg = m_pred_regs.find(r->get_decl()); svector tail_regs; tail_delta_infos tail_deltas; for(unsigned j=0;jget_tail(j)->get_decl(); reg_idx tail_reg = m_pred_regs.find(tail_pred); tail_regs.push_back(tail_reg); if(input_deltas && !all_or_nothing_deltas()) { reg_idx tail_delta_idx; if(input_deltas->find(tail_pred, tail_delta_idx)) { tail_deltas.push_back(tail_delta_info(tail_delta_idx, j)); } } } if(!input_deltas || all_or_nothing_deltas()) { compile_rule_evaluation_run(r, head_reg, tail_regs.c_ptr(), output_delta, use_widening, acc); } else { tail_delta_infos::iterator tdit = tail_deltas.begin(); tail_delta_infos::iterator tdend = tail_deltas.end(); for(; tdit!=tdend; ++tdit) { tail_delta_info tdinfo = *tdit; flet flet_tail_reg(tail_regs[tdinfo.second], tdinfo.first); compile_rule_evaluation_run(r, head_reg, tail_regs.c_ptr(), output_delta, use_widening, acc); } } } class cycle_breaker { typedef func_decl * T; typedef rule_dependencies::item_set item_set; //set of T rule_dependencies & m_deps; item_set & m_removed; svector m_stack; ast_mark m_stack_content; ast_mark m_visited; void traverse(T v) { SASSERT(!m_stack_content.is_marked(v)); if(m_visited.is_marked(v) || m_removed.contains(v)) { return; } m_stack.push_back(v); m_stack_content.mark(v, true); m_visited.mark(v, true); const item_set & deps = m_deps.get_deps(v); item_set::iterator it = deps.begin(); item_set::iterator end = deps.end(); for(; it!=end; ++it) { T d = *it; if(m_stack_content.is_marked(d)) { //TODO: find the best vertex to remove in the cycle m_removed.insert(v); break; } traverse(d); } SASSERT(m_stack.back()==v); m_stack.pop_back(); m_stack_content.mark(v, false); } public: cycle_breaker(rule_dependencies & deps, item_set & removed) : m_deps(deps), m_removed(removed) { SASSERT(removed.empty()); } void operator()() { rule_dependencies::iterator it = m_deps.begin(); rule_dependencies::iterator end = m_deps.end(); for(; it!=end; ++it) { T v = it->m_key; traverse(v); } m_deps.remove(m_removed); } }; void compiler::detect_chains(const func_decl_set & preds, func_decl_vector & ordered_preds, func_decl_set & global_deltas) { SASSERT(ordered_preds.empty()); SASSERT(global_deltas.empty()); rule_dependencies deps(m_rule_set.get_dependencies()); deps.restrict(preds); cycle_breaker(deps, global_deltas)(); VERIFY( deps.sort_deps(ordered_preds) ); //the predicates that were removed to get acyclic induced subgraph are put last //so that all their local input deltas are already populated func_decl_set::iterator gdit = global_deltas.begin(); func_decl_set::iterator gend = global_deltas.end(); for(; gdit!=gend; ++gdit) { ordered_preds.push_back(*gdit); } } void compiler::compile_preds(const func_decl_vector & head_preds, const func_decl_set & widened_preds, const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc) { func_decl_vector::const_iterator hpit = head_preds.begin(); func_decl_vector::const_iterator hpend = head_preds.end(); for(; hpit!=hpend; ++hpit) { func_decl * head_pred = *hpit; bool widen_predicate_in_loop = widened_preds.contains(head_pred); reg_idx d_head_reg; //output delta for the initial rule execution if(!output_deltas.find(head_pred, d_head_reg)) { d_head_reg = execution_context::void_register; } const rule_vector & pred_rules = m_rule_set.get_predicate_rules(head_pred); rule_vector::const_iterator rit = pred_rules.begin(); rule_vector::const_iterator rend = pred_rules.end(); for(; rit!=rend; ++rit) { rule * r = *rit; SASSERT(head_pred==r->get_decl()); compile_rule_evaluation(r, input_deltas, d_head_reg, widen_predicate_in_loop, acc); } } } void compiler::compile_preds_init(const func_decl_vector & head_preds, const func_decl_set & widened_preds, const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc) { func_decl_vector::const_iterator hpit = head_preds.begin(); func_decl_vector::const_iterator hpend = head_preds.end(); reg_idx void_reg = execution_context::void_register; for(; hpit!=hpend; ++hpit) { func_decl * head_pred = *hpit; const rule_vector & pred_rules = m_rule_set.get_predicate_rules(head_pred); rule_vector::const_iterator rit = pred_rules.begin(); rule_vector::const_iterator rend = pred_rules.end(); unsigned stratum = m_rule_set.get_predicate_strat(head_pred); for(; rit != rend; ++rit) { rule * r = *rit; SASSERT(head_pred==r->get_decl()); for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { unsigned stratum1 = m_rule_set.get_predicate_strat(r->get_decl(i)); if (stratum1 >= stratum) { goto next_loop; } } compile_rule_evaluation(r, input_deltas, void_reg, false, acc); next_loop: ; } reg_idx d_head_reg; if (output_deltas.find(head_pred, d_head_reg)) { acc.push_back(instruction::mk_clone(m_pred_regs.find(head_pred), d_head_reg)); } } } void compiler::make_inloop_delta_transition(const pred2idx & global_head_deltas, const pred2idx & global_tail_deltas, const pred2idx & local_deltas, instruction_block & acc) { //move global head deltas into tail ones pred2idx::iterator gdit = global_head_deltas.begin(); pred2idx::iterator gend = global_head_deltas.end(); for(; gdit!=gend; ++gdit) { func_decl * pred = gdit->m_key; reg_idx head_reg = gdit->m_value; reg_idx tail_reg = global_tail_deltas.find(pred); acc.push_back(instruction::mk_move(head_reg, tail_reg)); } //empty local deltas pred2idx::iterator lit = local_deltas.begin(); pred2idx::iterator lend = local_deltas.end(); for(; lit!=lend; ++lit) { acc.push_back(instruction::mk_dealloc(lit->m_value)); } } void compiler::compile_loop(const func_decl_vector & head_preds, const func_decl_set & widened_preds, const pred2idx & global_head_deltas, const pred2idx & global_tail_deltas, const pred2idx & local_deltas, instruction_block & acc) { instruction_block * loop_body = alloc(instruction_block); loop_body->set_observer(&m_instruction_observer); pred2idx all_head_deltas(global_head_deltas); unite_disjoint_maps(all_head_deltas, local_deltas); pred2idx all_tail_deltas(global_tail_deltas); unite_disjoint_maps(all_tail_deltas, local_deltas); //generate code for the iterative fixpoint search //The order in which we iterate the preds_vector matters, since rules can depend on //deltas generated earlier in the same iteration. compile_preds(head_preds, widened_preds, &all_tail_deltas, all_head_deltas, *loop_body); svector loop_control_regs; //loop is controlled by global src regs collect_map_range(loop_control_regs, global_tail_deltas); //move target deltas into source deltas at the end of the loop //and clear local deltas make_inloop_delta_transition(global_head_deltas, global_tail_deltas, local_deltas, *loop_body); loop_body->set_observer(0); acc.push_back(instruction::mk_while_loop(loop_control_regs.size(), loop_control_regs.c_ptr(), loop_body)); } void compiler::compile_dependent_rules(const func_decl_set & head_preds, const pred2idx * input_deltas, const pred2idx & output_deltas, bool add_saturation_marks, instruction_block & acc) { if (!output_deltas.empty()) { func_decl_set::iterator hpit = head_preds.begin(); func_decl_set::iterator hpend = head_preds.end(); for(; hpit!=hpend; ++hpit) { if(output_deltas.contains(*hpit)) { //we do not support retrieving deltas for rules that are inside a recursive //stratum, since we would have to maintain this 'global' delta through the loop //iterations NOT_IMPLEMENTED_YET(); } } } func_decl_vector preds_vector; func_decl_set global_deltas_dummy; detect_chains(head_preds, preds_vector, global_deltas_dummy); /* FIXME: right now we use all preds as global deltas for correctness purposes func_decl_set local_deltas(head_preds); set_difference(local_deltas, global_deltas); */ func_decl_set local_deltas; func_decl_set global_deltas(head_preds); pred2idx d_global_src; //these deltas serve as sources of tuples for rule evaluation inside the loop get_fresh_registers(global_deltas, d_global_src); pred2idx d_global_tgt; //these deltas are targets for new tuples in rule evaluation inside the loop get_fresh_registers(global_deltas, d_global_tgt); pred2idx d_local; get_fresh_registers(local_deltas, d_local); pred2idx d_all_src(d_global_src); //src together with local deltas unite_disjoint_maps(d_all_src, d_local); pred2idx d_all_tgt(d_global_tgt); //tgt together with local deltas unite_disjoint_maps(d_all_tgt, d_local); func_decl_set empty_func_decl_set; //generate code for the initial run // compile_preds(preds_vector, empty_func_decl_set, input_deltas, d_global_src, acc); compile_preds_init(preds_vector, empty_func_decl_set, input_deltas, d_global_src, acc); if (compile_with_widening()) { compile_loop(preds_vector, global_deltas, d_global_tgt, d_global_src, d_local, acc); } else { compile_loop(preds_vector, empty_func_decl_set, d_global_tgt, d_global_src, d_local, acc); } if(add_saturation_marks) { //after the loop finishes, all predicates in the group are saturated, //so we may mark them as such func_decl_set::iterator fdit = head_preds.begin(); func_decl_set::iterator fdend = head_preds.end(); for(; fdit!=fdend; ++fdit) { acc.push_back(instruction::mk_mark_saturated(m_context.get_manager(), *fdit)); } } } bool compiler::is_nonrecursive_stratum(const func_decl_set & preds) const { SASSERT(preds.size()>0); if(preds.size()>1) { return false; } func_decl * head_pred = *preds.begin(); const rule_vector & rules = m_rule_set.get_predicate_rules(head_pred); rule_vector::const_iterator it = rules.begin(); rule_vector::const_iterator end = rules.end(); for(; it!=end; ++it) { //it is sufficient to check just for presence of the first head predicate, //since if the rules are recursive and their heads are strongly connected by dependence, //this predicate must appear in some tail if((*it)->is_in_tail(head_pred)) { return false; } } return true; } void compiler::compile_nonrecursive_stratum(const func_decl_set & preds, const pred2idx * input_deltas, const pred2idx & output_deltas, bool add_saturation_marks, instruction_block & acc) { //non-recursive stratum always has just one head predicate SASSERT(preds.size()==1); SASSERT(is_nonrecursive_stratum(preds)); func_decl * head_pred = *preds.begin(); const rule_vector & rules = m_rule_set.get_predicate_rules(head_pred); reg_idx output_delta; if (!output_deltas.find(head_pred, output_delta)) { output_delta = execution_context::void_register; } rule_vector::const_iterator it = rules.begin(); rule_vector::const_iterator end = rules.end(); for (; it != end; ++it) { rule * r = *it; SASSERT(r->get_decl()==head_pred); compile_rule_evaluation(r, input_deltas, output_delta, false, acc); } if (add_saturation_marks) { //now the predicate is saturated, so we may mark it as such acc.push_back(instruction::mk_mark_saturated(m_context.get_manager(), head_pred)); } } bool compiler::all_saturated(const func_decl_set & preds) const { func_decl_set::iterator fdit = preds.begin(); func_decl_set::iterator fdend = preds.end(); for(; fdit!=fdend; ++fdit) { if(!m_context.get_rel_context()->get_rmanager().is_saturated(*fdit)) { return false; } } return true; } void compiler::compile_strats(const rule_stratifier & stratifier, const pred2idx * input_deltas, const pred2idx & output_deltas, bool add_saturation_marks, instruction_block & acc) { rule_set::pred_set_vector strats = stratifier.get_strats(); rule_set::pred_set_vector::const_iterator sit = strats.begin(); rule_set::pred_set_vector::const_iterator send = strats.end(); for(; sit!=send; ++sit) { func_decl_set & strat_preds = **sit; if (all_saturated(strat_preds)) { //all predicates in stratum are saturated, so no need to compile rules for them continue; } TRACE("dl", tout << "Stratum: "; func_decl_set::iterator pit = strat_preds.begin(); func_decl_set::iterator pend = strat_preds.end(); for(; pit!=pend; ++pit) { func_decl * pred = *pit; tout << pred->get_name() << " "; } tout << "\n"; ); if (is_nonrecursive_stratum(strat_preds)) { //this stratum contains just a single non-recursive rule compile_nonrecursive_stratum(strat_preds, input_deltas, output_deltas, add_saturation_marks, acc); } else { compile_dependent_rules(strat_preds, input_deltas, output_deltas, add_saturation_marks, acc); } } } void compiler::do_compilation(instruction_block & execution_code, instruction_block & termination_code) { unsigned rule_cnt=m_rule_set.get_num_rules(); if(rule_cnt==0) { return; } instruction_block & acc = execution_code; acc.set_observer(&m_instruction_observer); //load predicate data for(unsigned i=0;iget_decl(), acc); unsigned rule_len = r->get_uninterpreted_tail_size(); for(unsigned j=0;jget_tail(j)->get_decl(), acc); } } pred2idx empty_pred2idx_map; compile_strats(m_rule_set.get_stratifier(), static_cast(0), empty_pred2idx_map, true, execution_code); //store predicate data pred2idx::iterator pit = m_pred_regs.begin(); pred2idx::iterator pend = m_pred_regs.end(); for(; pit!=pend; ++pit) { pred2idx::key_data & e = *pit; func_decl * pred = e.m_key; reg_idx reg = e.m_value; termination_code.push_back(instruction::mk_store(m_context.get_manager(), pred, reg)); } acc.set_observer(0); TRACE("dl", execution_code.display(execution_context(m_context), tout);); } } z3-4.4.0/src/muz/rel/dl_vector_relation.h0000644000175000017500000003214212540347414020062 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_vector_relation.h Abstract: Basic relation with equivalences. Author: Nikolaj Bjorner (nbjorner) 2010-2-11 Revision History: --*/ #ifndef _DL_VECTOR_RELATION_H_ #define _DL_VECTOR_RELATION_H_ #include "ast_pp.h" #include "dl_context.h" #include "union_find.h" namespace datalog { typedef std::pair u_pair; template class vector_relation_helper { public: static void mk_project_t(T& t, unsigned_vector const& renaming) {} }; template > class vector_relation : public relation_base { protected: T m_default; vector* m_elems; bool m_empty; union_find_default_ctx m_ctx; union_find<>* m_eqs; public: vector_relation(relation_plugin& p, relation_signature const& s, bool is_empty, T const& t = T()): relation_base(p, s), m_default(t), m_elems(alloc(vector)), m_empty(is_empty), m_eqs(alloc(union_find<>, m_ctx)) { m_elems->resize(s.size(), t); for (unsigned i = 0; i < s.size(); ++i) { m_eqs->mk_var(); } } virtual ~vector_relation() { dealloc(m_eqs); dealloc(m_elems); } virtual void swap(relation_base& other) { vector_relation& o = dynamic_cast(other); if (&o == this) return; std::swap(o.m_eqs, m_eqs); std::swap(o.m_empty, m_empty); std::swap(o.m_elems, m_elems); } void copy(vector_relation const& other) { SASSERT(get_signature() == other.get_signature()); if (other.empty()) { set_empty(); return; } m_empty = false; for (unsigned i = 0; i < m_elems->size(); ++i) { (*this)[i] = other[i]; SASSERT(find(i) == i); } for (unsigned i = 0; i < m_elems->size(); ++i) { merge(i, find(i)); } } virtual bool empty() const { return m_empty; } T& operator[](unsigned i) { return (*m_elems)[find(i)]; } T const& operator[](unsigned i) const { return (*m_elems)[find(i)]; } virtual void display_index(unsigned i, T const& t, std::ostream& out) const = 0; virtual void display(std::ostream & out) const { if (empty()) { out << "empty\n"; return; } for (unsigned i = 0; i < m_elems->size(); ++i) { if (i == find(i)) { display_index(i, (*m_elems)[i], out); } else { out << i << " = " << find(i) << " "; } } out << "\n"; } bool is_subset_of(vector_relation const& other) const { if (empty()) return true; if (other.empty()) return false; for (unsigned i = 0; i < get_signature().size(); ++i) { if (!is_subset_of((*this)[i], other[i])) { return false; } } return true; } void set_empty() { unsigned sz = m_elems->size(); m_empty = true; m_elems->reset(); m_elems->resize(sz, m_default); dealloc(m_eqs); m_eqs = alloc(union_find<>,m_ctx); for (unsigned i = 0; i < sz; ++i) { m_eqs->mk_var(); } } virtual T mk_intersect(T const& t1, T const& t2, bool& is_empty) const = 0; virtual T mk_widen(T const& t1, T const& t2) const = 0; virtual T mk_unite(T const& t1, T const& t2) const = 0; virtual bool is_subset_of(T const& t1, T const& t2) const = 0; virtual bool is_full(T const& t) const = 0; virtual bool is_empty(unsigned i, T const& t) const = 0; virtual void mk_rename_elem(T& t, unsigned col_cnt, unsigned const* cycle) = 0; virtual T mk_eq(union_find<> const& old_eqs, union_find<> const& neq_eqs, T const& t) const { return t; } void equate(unsigned i, unsigned j) { SASSERT(i < get_signature().size()); SASSERT(j < get_signature().size()); if (!empty() && find(i) != find(j)) { bool isempty; T r = mk_intersect((*this)[i], (*this)[j], isempty); if (isempty || is_empty(find(i),r)) { m_empty = true; } else { merge(i, j); (*this)[i] = r; } } } bool is_full() const { for (unsigned i = 0; i < m_elems->size(); ++i) { if (!is_full((*this)[i])) { return false; } } return true; } void mk_join(vector_relation const& r1, vector_relation const& r2, unsigned num_cols, unsigned const* cols1, unsigned const* cols2) { SASSERT(is_full()); bool is_empty = r1.empty() || r2.empty(); if (is_empty) { m_empty = true; return; } unsigned sz1 = r1.get_signature().size(); unsigned sz2 = r2.get_signature().size(); for (unsigned i = 0; i < sz1; ++i) { (*this)[i] = r1[i]; } for (unsigned i = 0; i < sz2; ++i) { (*this)[sz1+i] = r2[i]; } for (unsigned i = 0; i < num_cols; ++i) { unsigned col1 = cols1[i]; unsigned col2 = cols2[i]; equate(col1, sz1 + col2); } TRACE("dl_relation", r1.display(tout << "r1:\n"); r2.display(tout << "r2:\n"); display(tout << "dst:\n"); ); } void mk_project(vector_relation const& r, unsigned col_cnt, unsigned const* removed_cols) { SASSERT(is_full()); unsigned_vector classRep, repNode; unsigned result_size = get_signature().size(); unsigned input_size = r.get_signature().size(); repNode.resize(input_size, UINT_MAX); // initialize vector entries and set class representatives. for (unsigned i = 0, j = 0, c = 0; i < input_size; ++i) { if (c < col_cnt && removed_cols[c] == i) { ++c; } else { (*this)[j] = r[i]; classRep.push_back(r.find(i)); ++j; } } // merge remaining equivalence classes. for (unsigned i = 0; i < result_size; ++i) { unsigned rep = classRep[i]; if (repNode[rep] == UINT_MAX) { repNode[rep] = i; } else { merge(repNode[rep], i); } } // rename columns in image of vector relation. unsigned_vector renaming; for (unsigned i = 0, j = 0, c = 0; i < input_size; ++i) { if (c < col_cnt && removed_cols[c] == i) { renaming.push_back(UINT_MAX); ++c; } else { renaming.push_back(find(j)); ++j; } } for (unsigned k = 0; k < result_size; ++k) { Helper::mk_project_t((*this)[k], renaming); } TRACE("dl_relation", ast_manager& m = r.get_plugin().get_ast_manager(); tout << "Signature: "; for (unsigned i = 0; i < r.get_signature().size(); ++i) { tout << mk_pp(r.get_signature()[i], m) << " "; } tout << "Remove: "; for (unsigned i = 0; i < col_cnt; ++i) { tout << removed_cols[i] << " "; } tout << "\n"; r.display(tout); tout << " --> \n"; display(tout);); } void mk_rename(vector_relation const& r, unsigned col_cnt, unsigned const* cycle) { unsigned col1, col2; SASSERT(is_full()); // roundabout way of creating permuted relation. unsigned_vector classRep, repNode; for (unsigned i = 0; i < r.m_elems->size(); ++i) { classRep.push_back(r.find(i)); repNode.push_back(UINT_MAX); (*this)[i] = r[i]; } for (unsigned i = 0; i + 1 < col_cnt; ++i) { col1 = cycle[i]; col2 = cycle[i+1]; (*this)[col2] = (*r.m_elems)[col1]; classRep[col2] = r.find(col1); } col1 = cycle[col_cnt-1]; col2 = cycle[0]; (*this)[col2] = (*r.m_elems)[col1]; classRep[col2] = r.find(col1); for (unsigned i = 0; i < r.m_elems->size(); ++i) { unsigned rep = classRep[i]; if (repNode[rep] == UINT_MAX) { repNode[rep] = i; } else { merge(repNode[rep], i); } } for (unsigned i = 0; i < r.m_elems->size(); ++i) { mk_rename_elem((*m_elems)[i], col_cnt, cycle); } TRACE("dl_relation", ast_manager& m = r.get_plugin().get_ast_manager(); tout << "cycle: "; for (unsigned i = 0; i < col_cnt; ++i) { tout << cycle[i] << " "; } tout << "\nold_sig: "; for (unsigned i = 0; i < r.get_signature().size(); ++i) { tout << mk_pp(r.get_signature()[i], m) << " "; } tout << "\nnew_sig: "; for (unsigned i = 0; i < get_signature().size(); ++i) { tout << mk_pp(get_signature()[i], m) << " "; } tout << "\n"; r.display(tout << "src:\n"); ); } void mk_union(vector_relation const& src, vector_relation* delta, bool is_widen) { TRACE("dl_relation", display(tout << "dst:\n"); src.display(tout << "src:\n");); if (src.empty()) { if (delta) { delta->copy(src); } return; } if (empty()) { copy(src); if (delta) { delta->copy(src); } return; } // find coarsest equivalence class containing joint equalities union_find<>* uf = alloc(union_find<>, m_ctx); unsigned size = get_signature().size(); map, default_eq > mp; bool change = false; bit_vector finds; finds.resize(size, false); for (unsigned i = 0; i < size; ++i) { uf->mk_var(); unsigned w; u_pair p(std::make_pair(find(i), src.find(i))); if (mp.find(p, w)) { uf->merge(i, w); } else { mp.insert(p, i); // detect change if (finds.get(find(i))) { change = true; } else { finds.set(find(i), true); } } } vector* elems = alloc(vector); for (unsigned i = 0; i < size; ++i) { T t1 = mk_eq(*m_eqs, *uf, (*this)[i]); T t2 = mk_eq(*src.m_eqs, *uf, src[i]); if (is_widen) { elems->push_back(mk_widen(t1, t2)); } else { elems->push_back(mk_unite(t1, t2)); } TRACE("dl_relation", tout << t1 << " u " << t2 << " = " << elems->back() << "\n";); change = delta && (change || !((*elems)[i] == (*this)[i])); } dealloc(m_eqs); dealloc(m_elems); m_eqs = uf; m_elems = elems; if (delta && change) { delta->copy(*this); } TRACE("dl_relation", display(tout << "dst':\n");); } unsigned find(unsigned i) const { return m_eqs->find(i); } void merge(unsigned i, unsigned j) { m_eqs->merge(i, j); } }; }; #endif z3-4.4.0/src/muz/rel/dl_sieve_relation.cpp0000644000175000017500000006660312540347414020237 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_explanations.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-11-08. Revision History: --*/ #include #include"ast_pp.h" #include"dl_sieve_relation.h" namespace datalog { // ----------------------------------- // // sieve_relation // // ----------------------------------- sieve_relation::sieve_relation(sieve_relation_plugin & p, const relation_signature & s, const bool * inner_columns, relation_base * inner) : relation_base(p, s), m_inner_cols(s.size(), inner_columns), m_inner(inner) { unsigned n = s.size(); for(unsigned i=0; i 0; ) { --i; unsigned idx = m_inner2sig[i]; s.push_back(m.mk_var(idx, sig[i])); } get_inner().to_formula(tmp); get_plugin().get_context().get_var_subst()(tmp, sz, s.c_ptr(), fml); } void sieve_relation::display(std::ostream & out) const { out << "Sieve relation "; print_container(m_inner_cols, out); out <<"\n"; get_inner().display(out); } // ----------------------------------- // // sieve_relation_plugin // // ----------------------------------- sieve_relation_plugin & sieve_relation_plugin::get_plugin(relation_manager & rmgr) { sieve_relation_plugin * res = static_cast(rmgr.get_relation_plugin(get_name())); if(!res) { res = alloc(sieve_relation_plugin, rmgr); rmgr.register_plugin(res); } return *res; } sieve_relation& sieve_relation_plugin::get(relation_base& r) { return dynamic_cast(r); } sieve_relation const & sieve_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); } sieve_relation* sieve_relation_plugin::get(relation_base* r) { return dynamic_cast(r); } sieve_relation const* sieve_relation_plugin::get(relation_base const* r) { return dynamic_cast(r); } sieve_relation_plugin::sieve_relation_plugin(relation_manager & manager) : relation_plugin(get_name(), manager, ST_SIEVE_RELATION), m_spec_store(*this) {} void sieve_relation_plugin::initialize(family_id fid) { relation_plugin::initialize(fid); m_spec_store.add_available_kind(get_kind()); } family_id sieve_relation_plugin::get_relation_kind(const relation_signature & sig, const bool * inner_columns, family_id inner_kind) { rel_spec spec(sig.size(), inner_columns, inner_kind); return m_spec_store.get_relation_kind(sig, spec); } family_id sieve_relation_plugin::get_relation_kind(sieve_relation & r, const bool * inner_columns) { const relation_signature & sig = r.get_signature(); return get_relation_kind(sig, inner_columns, r.get_inner().get_kind()); } void sieve_relation_plugin::extract_inner_columns(const relation_signature & s, relation_plugin & inner, svector & inner_columns) { SASSERT(inner_columns.size()==s.size()); unsigned n = s.size(); relation_signature inner_sig_singleton; for(unsigned i=0; i & inner_columns, relation_signature & inner_sig) { SASSERT(inner_columns.size()==s.size()); inner_sig.reset(); unsigned n = s.size(); for(unsigned i=0; i inner_cols(s.size()); extract_inner_columns(s, inner_cols.c_ptr()); collect_inner_signature(s, inner_cols, inner_sig); #endif } bool sieve_relation_plugin::can_handle_signature(const relation_signature & s) { //we do not want this plugin to handle anything by default return false; #if 0 relation_signature inner_sig; extract_inner_signature(s, inner_sig); SASSERT(inner_sig.size()<=s.size()); return !inner_sig.empty() && inner_sig.size()!=s.size(); #endif } sieve_relation * sieve_relation_plugin::mk_from_inner(const relation_signature & s, const bool * inner_columns, relation_base * inner_rel) { SASSERT(!inner_rel->get_plugin().is_sieve_relation()); //it does not make sense to make a sieve of a sieve return alloc(sieve_relation, *this, s, inner_columns, inner_rel); } sieve_relation * sieve_relation_plugin::mk_empty(const sieve_relation & original) { return static_cast(mk_empty(original.get_signature(), original.get_kind())); } relation_base * sieve_relation_plugin::mk_empty(const relation_base & original) { return mk_empty(static_cast(original)); } relation_base * sieve_relation_plugin::mk_empty(const relation_signature & s, family_id kind) { rel_spec spec; m_spec_store.get_relation_spec(s, kind, spec); relation_signature inner_sig; collect_inner_signature(s, spec.m_inner_cols, inner_sig); relation_base * inner = get_manager().mk_empty_relation(inner_sig, spec.m_inner_kind); return mk_from_inner(s, spec.m_inner_cols.c_ptr(), inner); } relation_base * sieve_relation_plugin::mk_empty(const relation_signature & s) { UNREACHABLE(); return 0; #if 0 svector inner_cols(s.size()); extract_inner_columns(s, inner_cols.c_ptr()); return mk_empty(s, inner_cols.c_ptr()); #endif } sieve_relation * sieve_relation_plugin::mk_empty(const relation_signature & s, relation_plugin & inner_plugin) { SASSERT(!inner_plugin.is_sieve_relation()); //it does not make sense to make a sieve of a sieve svector inner_cols(s.size()); extract_inner_columns(s, inner_plugin, inner_cols); relation_signature inner_sig; collect_inner_signature(s, inner_cols, inner_sig); relation_base * inner_rel = inner_plugin.mk_empty(inner_sig); return mk_from_inner(s, inner_cols, inner_rel); } relation_base * sieve_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { relation_signature empty_sig; relation_plugin& plugin = get_manager().get_appropriate_plugin(s); relation_base * inner = plugin.mk_full(p, empty_sig, null_family_id); svector inner_cols; inner_cols.resize(s.size(), false); return mk_from_inner(s, inner_cols, inner); } sieve_relation * sieve_relation_plugin::mk_full(func_decl* p, const relation_signature & s, relation_plugin & inner_plugin) { SASSERT(!inner_plugin.is_sieve_relation()); //it does not make sense to make a sieve of a sieve svector inner_cols(s.size()); extract_inner_columns(s, inner_plugin, inner_cols); relation_signature inner_sig; collect_inner_signature(s, inner_cols, inner_sig); relation_base * inner_rel = inner_plugin.mk_full(p, inner_sig, null_family_id); return mk_from_inner(s, inner_cols, inner_rel); } class sieve_relation_plugin::join_fn : public convenient_relation_join_fn { sieve_relation_plugin & m_plugin; unsigned_vector m_inner_cols_1; unsigned_vector m_inner_cols_2; svector m_result_inner_cols; scoped_ptr m_inner_join_fun; public: join_fn(sieve_relation_plugin & p, const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, relation_join_fn * inner_join_fun) : convenient_relation_join_fn(r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2), m_plugin(p), m_inner_join_fun(inner_join_fun) { bool r1_sieved = r1.get_plugin().is_sieve_relation(); bool r2_sieved = r2.get_plugin().is_sieve_relation(); const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : 0; const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : 0; if(r1_sieved) { m_result_inner_cols.append(sr1->m_inner_cols); } else { m_result_inner_cols.resize(r1.get_signature().size(), true); } if(r2_sieved) { m_result_inner_cols.append(sr2->m_inner_cols); } else { m_result_inner_cols.resize(m_result_inner_cols.size() + r2.get_signature().size(), true); } } virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { bool r1_sieved = r1.get_plugin().is_sieve_relation(); bool r2_sieved = r2.get_plugin().is_sieve_relation(); SASSERT(r1_sieved || r2_sieved); const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : 0; const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : 0; const relation_base & inner1 = r1_sieved ? sr1->get_inner() : r1; const relation_base & inner2 = r2_sieved ? sr2->get_inner() : r2; relation_base * inner_res = (*m_inner_join_fun)(inner1, inner2); return m_plugin.mk_from_inner(get_result_signature(), m_result_inner_cols.c_ptr(), inner_res); } }; relation_join_fn * sieve_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if( &r1.get_plugin()!=this && &r2.get_plugin()!=this ) { //we create just operations that involve the current plugin return 0; } bool r1_sieved = r1.get_plugin().is_sieve_relation(); bool r2_sieved = r2.get_plugin().is_sieve_relation(); const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : 0; const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : 0; const relation_base & inner1 = r1_sieved ? sr1->get_inner() : r1; const relation_base & inner2 = r2_sieved ? sr2->get_inner() : r2; unsigned_vector inner_cols1; unsigned_vector inner_cols2; for(unsigned i=0; iis_inner_col(cols1[i])) { continue; } if(r2_sieved && !sr2->is_inner_col(cols2[i])) { continue; } inner_cols1.push_back( r1_sieved ? sr1->get_inner_col(cols1[i]) : cols1[i] ); inner_cols2.push_back( r2_sieved ? sr2->get_inner_col(cols2[i]) : cols2[i] ); } relation_join_fn * inner_join_fun = get_manager().mk_join_fn(inner1, inner2, inner_cols1, inner_cols2, false); if(!inner_join_fun) { return 0; } return alloc(join_fn, *this, r1, r2, col_cnt, cols1, cols2, inner_join_fun); } class sieve_relation_plugin::transformer_fn : public convenient_relation_transformer_fn { svector m_result_inner_cols; scoped_ptr m_inner_fun; public: transformer_fn(relation_transformer_fn * inner_fun, const relation_signature & result_sig, const bool * result_inner_cols) : m_result_inner_cols(result_sig.size(), result_inner_cols), m_inner_fun(inner_fun) { get_result_signature() = result_sig; } virtual relation_base * operator()(const relation_base & r0) { SASSERT(r0.get_plugin().is_sieve_relation()); const sieve_relation & r = static_cast(r0); sieve_relation_plugin & plugin = r.get_plugin(); relation_base * inner_res = (*m_inner_fun)(r.get_inner()); return plugin.mk_from_inner(get_result_signature(), m_result_inner_cols.c_ptr(), inner_res); } }; relation_transformer_fn * sieve_relation_plugin::mk_project_fn(const relation_base & r0, unsigned col_cnt, const unsigned * removed_cols) { if(&r0.get_plugin()!=this) { return 0; } const sieve_relation & r = static_cast(r0); unsigned_vector inner_removed_cols; for(unsigned i=0; i result_inner_cols = r.m_inner_cols; project_out_vector_columns(result_inner_cols, col_cnt, removed_cols); relation_signature result_sig; relation_signature::from_project(r.get_signature(), col_cnt, removed_cols, result_sig); relation_transformer_fn * inner_fun; if(inner_removed_cols.empty()) { inner_fun = alloc(identity_relation_transformer_fn); } else { inner_fun = get_manager().mk_project_fn(r.get_inner(), inner_removed_cols); } if(!inner_fun) { return 0; } return alloc(transformer_fn, inner_fun, result_sig, result_inner_cols.c_ptr()); } relation_transformer_fn * sieve_relation_plugin::mk_rename_fn(const relation_base & r0, unsigned cycle_len, const unsigned * permutation_cycle) { if(&r0.get_plugin()!=this) { return 0; } const sieve_relation & r = static_cast(r0); unsigned sig_sz = r.get_signature().size(); unsigned_vector permutation; add_sequence(0, sig_sz, permutation); permutate_by_cycle(permutation, cycle_len, permutation_cycle); bool inner_identity; unsigned_vector inner_permutation; collect_sub_permutation(permutation, r.m_sig2inner, inner_permutation, inner_identity); svector result_inner_cols = r.m_inner_cols; permutate_by_cycle(result_inner_cols, cycle_len, permutation_cycle); relation_signature result_sig; relation_signature::from_rename(r.get_signature(), cycle_len, permutation_cycle, result_sig); relation_transformer_fn * inner_fun = get_manager().mk_permutation_rename_fn(r.get_inner(), inner_permutation); if(!inner_fun) { return 0; } return alloc(transformer_fn, inner_fun, result_sig, result_inner_cols.c_ptr()); } class sieve_relation_plugin::union_fn : public relation_union_fn { scoped_ptr m_union_fun; public: union_fn(relation_union_fn * union_fun) : m_union_fun(union_fun) {} virtual void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) { bool tgt_sieved = tgt.get_plugin().is_sieve_relation(); bool src_sieved = src.get_plugin().is_sieve_relation(); bool delta_sieved = delta && delta->get_plugin().is_sieve_relation(); sieve_relation * stgt = tgt_sieved ? static_cast(&tgt) : 0; const sieve_relation * ssrc = src_sieved ? static_cast(&src) : 0; sieve_relation * sdelta = delta_sieved ? static_cast(delta) : 0; relation_base & itgt = tgt_sieved ? stgt->get_inner() : tgt; const relation_base & isrc = src_sieved ? ssrc->get_inner() : src; relation_base * idelta = delta_sieved ? &sdelta->get_inner() : delta; (*m_union_fun)(itgt, isrc, idelta); } }; relation_union_fn * sieve_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if(&tgt.get_plugin()!=this && &src.get_plugin()!=this && (delta && &delta->get_plugin()!=this)) { //we create the operation only if it involves this plugin return 0; } bool tgt_sieved = tgt.get_plugin().is_sieve_relation(); bool src_sieved = src.get_plugin().is_sieve_relation(); bool delta_sieved = delta && delta->get_plugin().is_sieve_relation(); const sieve_relation * stgt = tgt_sieved ? static_cast(&tgt) : 0; const sieve_relation * ssrc = src_sieved ? static_cast(&src) : 0; const sieve_relation * sdelta = delta_sieved ? static_cast(delta) : 0; const relation_base & itgt = tgt_sieved ? stgt->get_inner() : tgt; const relation_base & isrc = src_sieved ? ssrc->get_inner() : src; const relation_base * idelta = delta_sieved ? &sdelta->get_inner() : delta; //Now we require that the sieved and inner columns must match on all relations. //We may want to allow for some cases of misalignment even though it could introcude imprecision if( tgt_sieved && src_sieved && (!delta || delta_sieved) ) { if( !vectors_equal(stgt->m_inner_cols, ssrc->m_inner_cols) || (delta && !vectors_equal(stgt->m_inner_cols, sdelta->m_inner_cols)) ) { return 0; } } else { if( (stgt && !stgt->no_sieved_columns()) || (ssrc && !ssrc->no_sieved_columns()) || (sdelta && !sdelta->no_sieved_columns()) ) { //We have an unsieved relation and then some relation with some sieved columns, //which means there is an misalignment. return 0; } } relation_union_fn * union_fun = get_manager().mk_union_fn(itgt, isrc, idelta); if(!union_fun) { return 0; } return alloc(union_fn, union_fun); } class sieve_relation_plugin::filter_fn : public relation_mutator_fn { scoped_ptr m_inner_fun; public: filter_fn(relation_mutator_fn * inner_fun) : m_inner_fun(inner_fun) {} virtual void operator()(relation_base & r0) { SASSERT(r0.get_plugin().is_sieve_relation()); sieve_relation & r = static_cast(r0); (*m_inner_fun)(r.get_inner()); } }; relation_mutator_fn * sieve_relation_plugin::mk_filter_identical_fn(const relation_base & r0, unsigned col_cnt, const unsigned * identical_cols) { if(&r0.get_plugin()!=this) { return 0; } const sieve_relation & r = static_cast(r0); unsigned_vector inner_icols; //we ignore the columns which do not belong to the inner relation (which introduces imprecision) for(unsigned i=0; i(r0); if(!r.is_inner_col(col)) { //if the column which do not belong to the inner relation, we do nothing (which introduces imprecision) return alloc(identity_relation_mutator_fn); } unsigned inner_col = r.get_inner_col(col); relation_mutator_fn * inner_fun = get_manager().mk_filter_equal_fn(r.get_inner(), value, inner_col); if(!inner_fun) { return 0; } return alloc(filter_fn, inner_fun); } relation_mutator_fn * sieve_relation_plugin::mk_filter_interpreted_fn(const relation_base & rb, app * condition) { if(&rb.get_plugin()!=this) { return 0; } ast_manager & m = get_ast_manager(); const sieve_relation & r = static_cast(rb); const relation_signature sig = r.get_signature(); unsigned sz = sig.size(); var_idx_set& cond_vars = get_context().get_rule_manager().collect_vars(condition); expr_ref_vector subst_vect(m); subst_vect.resize(sz); unsigned subst_ofs = sz-1; for(unsigned i=0; i m_inner_fun; public: negation_filter_fn(relation_intersection_filter_fn * inner_fun) : m_inner_fun(inner_fun) {} virtual void operator()(relation_base & r, const relation_base & neg) { bool r_sieved = r.get_plugin().is_sieve_relation(); bool neg_sieved = neg.get_plugin().is_sieve_relation(); SASSERT(r_sieved || neg_sieved); sieve_relation * sr = r_sieved ? static_cast(&r) : 0; const sieve_relation * sneg = neg_sieved ? static_cast(&neg) : 0; relation_base & inner_r = r_sieved ? sr->get_inner() : r; const relation_base & inner_neg = neg_sieved ? sneg->get_inner() : neg; (*m_inner_fun)(inner_r, inner_neg); } }; relation_intersection_filter_fn * sieve_relation_plugin::mk_filter_by_negation_fn(const relation_base & r, const relation_base & neg, unsigned col_cnt, const unsigned * r_cols, const unsigned * neg_cols) { if(&r.get_plugin()!=this && &neg.get_plugin()!=this) { //we create just operations that involve the current plugin return 0; } bool r_sieved = r.get_plugin().is_sieve_relation(); bool neg_sieved = neg.get_plugin().is_sieve_relation(); SASSERT(r_sieved || neg_sieved); const sieve_relation * sr = r_sieved ? static_cast(&r) : 0; const sieve_relation * sneg = neg_sieved ? static_cast(&neg) : 0; const relation_base & inner_r = r_sieved ? sr->get_inner() : r; const relation_base & inner_neg = neg_sieved ? sneg->get_inner() : neg; unsigned_vector ir_cols; unsigned_vector ineg_cols; for(unsigned i=0; iis_inner_col(r_cols[i]); bool neg_col_inner = neg_sieved && !sneg->is_inner_col(neg_cols[i]); if(r_col_inner && neg_col_inner) { ir_cols.push_back( r_sieved ? sr->get_inner_col(i) : i ); ineg_cols.push_back( neg_sieved ? sneg->get_inner_col(i) : i ); } else if(!r_col_inner && neg_col_inner) { //Sieved (i.e. full) column in r is matched on an inner column in neg. //If we assume the column in neg is not full, no rows from the inner relation of //r would be removed. So in this case we perform no operation at cost of a little //impresicion. return alloc(identity_relation_intersection_filter_fn); } else { //Inner or sieved column in r must match a sieved column in neg. //Since sieved columns are full, this is always true so we can skip the equality. continue; } } relation_intersection_filter_fn * inner_fun = get_manager().mk_filter_by_negation_fn(inner_r, inner_neg, ir_cols, ineg_cols); if(!inner_fun) { return 0; } return alloc(negation_filter_fn, inner_fun); } }; z3-4.4.0/src/muz/rel/dl_table_plugin.h0000644000175000017500000001232612540347414017332 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_table_plugin.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-23. Revision History: --*/ #ifndef _DL_TABLE_PLUGIN_H_ #define _DL_TABLE_PLUGIN_H_ #include"ast.h" #include"map.h" #include"vector.h" #include"dl_table_ops.h" namespace datalog { /** Termplate class containing common infrastructure for relations and tables */ template struct tr_infrastructure { typedef typename Traits::base_object base_object; typedef typename Traits::signature signature; typedef typename Traits::element element; typedef typename Traits::fact fact; typedef typename Traits::kind kind; class base_fn { public: virtual ~base_fn() {} }; class join_fn : public base_fn { public: virtual base_object * operator()(const base_object & t1, const base_object & t2); }; class transformer_fn : public base_fn { public: virtual base_object * operator()(const base_object & t); }; class union_fn : public base_fn { public: virtual void operator()(base_object & tgt, const base_object & src, base_object * delta); }; class mutator_fn : public base_fn { public: virtual void operator()(base_object & t); }; class negation_filter_fn : public base_fn { public: virtual void operator()(base_object & t, const base_object & negated_obj); }; class plugin_object { const kind m_kind; protected: plugin_object(kind k) : m_kind(k) {} public: kind get_kind(); virtual base_object * mk_empty(const signature & s) = 0; virtual join_fn * mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { NOT_IMPLEMENTED_YET(); } virtual transformer_fn * mk_project_fn(const base_object & t, unsigned col_cnt, const unsigned * removed_cols) = 0 virtual transformer_fn * mk_rename_fn(const base_object & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) = 0; virtual union_fn * mk_union_fn(base_object & tgt, const base_object & src, base_object * delta) = 0; virtual mutator_fn * mk_filter_identical_fn(base_object & t, unsigned col_cnt, const unsigned * identical_cols) = 0; virtual mutator_fn * mk_filter_equal_fn(base_object & t, const element & value, unsigned col) = 0; virtual mutator_fn * mk_filter_interpreted_fn(base_object & t, app * condition) = 0; virtual negation_filter_fn * mk_filter_interpreted_fn(base_object & t, const base_object & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) = 0; }; class base_ancestor { const kind m_kind; protected: relation_manager & m_manager; signature m_signature; base_ancestor(kind k, relation_manager & m, const signature & s) : m_kind(k), m_manager(m), m_signature(s) {} public: virtual ~base_ancestor() {} kind get_kind() const { return m_kind; } relation_manager & get_manager() const { return m_manager; } const signature & get_signature() const { return m_signature; } virtual bool empty() const = 0; virtual void add_fact(const fact & f) = 0; virtual bool contains_fact(const fact & f) const = 0; /** \brief Return table that contains the same data as the current one. */ virtual base_object * clone() const; }; }; // ----------------------------------- // // relation_base // // ----------------------------------- class relation_base1; enum relation_kind { RK_UNKNOWN, RK_TABLE }; struct relation_traits { typedef relation_base1 base_object; typedef relation_signature signature; typedef app * element; typedef ptr_vector fact; typedef relation_kind kind; }; typedef tr_infrastructure relation_infrastructure; typedef relation_infrastructure::plugin_object relation_plugin_base; class relation_base1 : public relation_infrastructure::base_ancestor { }; // ----------------------------------- // // table_base // // ----------------------------------- class table_base1; struct table_traits { typedef table_base1 base_object; typedef table_signature signature; typedef unsigned element; typedef unsigned_vector fact; typedef table_kind kind; }; typedef tr_infrastructure table_infrastructure; typedef table_infrastructure::plugin_object table_plugin_base; class table_base1 : public table_infrastructure::base_ancestor { }; }; #endif /* _DL_TABLE_PLUGIN_H_ */ z3-4.4.0/src/muz/rel/karr_relation.cpp0000644000175000017500000006575312540347414017411 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "karr_relation.h" #include "bool_rewriter.h" namespace datalog { class karr_relation : public relation_base { friend class karr_relation_plugin; friend class karr_relation_plugin::filter_equal_fn; karr_relation_plugin& m_plugin; ast_manager& m; mutable arith_util a; func_decl_ref m_fn; mutable bool m_empty; mutable matrix m_ineqs; mutable bool m_ineqs_valid; mutable matrix m_basis; mutable bool m_basis_valid; public: karr_relation(karr_relation_plugin& p, func_decl* f, relation_signature const& s, bool is_empty): relation_base(p, s), m_plugin(p), m(p.get_ast_manager()), a(m), m_fn(f, m), m_empty(is_empty), m_ineqs_valid(!is_empty), m_basis_valid(false) { } virtual bool empty() const { return m_empty; } virtual bool is_precise() const { return false; } virtual void add_fact(const relation_fact & f) { SASSERT(m_empty); SASSERT(!m_basis_valid); m_empty = false; m_ineqs_valid = true; for (unsigned i = 0; i < f.size(); ++i) { rational n; if (a.is_numeral(f[i], n) && n.is_int()) { vector row; row.resize(f.size()); row[i] = rational(1); m_ineqs.A.push_back(row); m_ineqs.b.push_back(-n); m_ineqs.eq.push_back(true); } } } virtual bool contains_fact(const relation_fact & f) const { UNREACHABLE(); return false; } virtual void display(std::ostream & out) const { if (m_fn) { out << m_fn->get_name() << "\n"; } if (empty()) { out << "empty\n"; } else { if (m_ineqs_valid) { m_ineqs.display(out << "ineqs:\n"); } if (m_basis_valid) { m_basis.display(out << "basis:\n"); } } } virtual karr_relation * clone() const { karr_relation* result = alloc(karr_relation, m_plugin, m_fn, get_signature(), m_empty); result->copy(*this); return result; } virtual karr_relation * complement(func_decl*) const { UNREACHABLE(); return 0; } virtual void to_formula(expr_ref& fml) const { if (empty()) { fml = m.mk_false(); } else { matrix const& M = get_ineqs(); expr_ref_vector conj(m); for (unsigned i = 0; i < M.size(); ++i) { to_formula(M.A[i], M.b[i], M.eq[i], conj); } bool_rewriter(m).mk_and(conj.size(), conj.c_ptr(), fml); } } karr_relation_plugin& get_plugin() const { return m_plugin; } void filter_interpreted(app* cond) { rational one(1), mone(-1); expr* e1, *e2, *en; var* v, *w; rational n1, n2; expr_ref_vector conjs(m); qe::flatten_and(cond, conjs); matrix& M = get_ineqs(); unsigned num_columns = get_signature().size(); for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); rational b(0); vector row; row.resize(num_columns, rational(0)); bool processed = true; if (m.is_eq(e, e1, e2) && is_linear(e1, row, b, one) && is_linear(e2, row, b, mone)) { M.A.push_back(row); M.b.push_back(b); M.eq.push_back(true); } else if ((a.is_le(e, e1, e2) || a.is_ge(e, e2, e1)) && is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { M.A.push_back(row); M.b.push_back(b); M.eq.push_back(false); } else if ((a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) && is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { M.A.push_back(row); M.b.push_back(b - rational(1)); M.eq.push_back(false); } else if (m.is_not(e, en) && (a.is_lt(en, e2, e1) || a.is_gt(en, e1, e2)) && is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { M.A.push_back(row); M.b.push_back(b); M.eq.push_back(false); } else if (m.is_not(e, en) && (a.is_le(en, e2, e1) || a.is_ge(en, e1, e2)) && is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { M.A.push_back(row); M.b.push_back(b - rational(1)); M.eq.push_back(false); } else if (m.is_or(e, e1, e2) && is_eq(e1, v, n1) && is_eq(e2, w, n2) && v == w) { if (n1 > n2) { std::swap(n1, n2); } SASSERT(n1 <= n2); row[v->get_idx()] = rational(1); // v - n1 >= 0 M.A.push_back(row); M.b.push_back(-n1); M.eq.push_back(false); // -v + n2 >= 0 row[v->get_idx()] = rational(-1); M.A.push_back(row); M.b.push_back(n2); M.eq.push_back(false); } else { processed = false; } TRACE("dl", tout << (processed?"+ ":"- ") << mk_pp(e, m) << "\n"; if (processed) matrix::display_ineq(tout, row, M.b.back(), M.eq.back()); ); } TRACE("dl", display(tout);); } void mk_join(karr_relation const& r1, karr_relation const& r2, unsigned col_cnt, unsigned const* cols1, unsigned const* cols2) { if (r1.empty() || r2.empty()) { m_empty = true; return; } matrix const& M1 = r1.get_ineqs(); matrix const& M2 = r2.get_ineqs(); unsigned sig1_size = r1.get_signature().size(); unsigned sig_size = get_signature().size(); m_ineqs.reset(); for (unsigned i = 0; i < M1.size(); ++i) { vector row; row.append(M1.A[i]); row.resize(sig_size); m_ineqs.A.push_back(row); m_ineqs.b.push_back(M1.b[i]); m_ineqs.eq.push_back(M1.eq[i]); } for (unsigned i = 0; i < M2.size(); ++i) { vector row; row.resize(sig_size); for (unsigned j = 0; j < M2.A[i].size(); ++j) { row[sig1_size + j] = M2.A[i][j]; } m_ineqs.A.push_back(row); m_ineqs.b.push_back(M2.b[i]); m_ineqs.eq.push_back(M2.eq[i]); } for (unsigned i = 0; i < col_cnt; ++i) { vector row; row.resize(sig_size); row[cols1[i]] = rational(1); row[sig1_size + cols2[i]] = rational(-1); m_ineqs.A.push_back(row); m_ineqs.b.push_back(rational(0)); m_ineqs.eq.push_back(true); } m_ineqs_valid = true; m_basis_valid = false; m_empty = false; if (r1.m_fn) { m_fn = r1.m_fn; } if (r2.m_fn) { m_fn = r2.m_fn; } } void mk_project(karr_relation const& r, unsigned cnt, unsigned const* cols) { if (r.m_empty) { m_empty = true; return; } matrix const& M = r.get_basis(); m_basis.reset(); for (unsigned i = 0; i < M.size(); ++i) { vector row; unsigned k = 0; for (unsigned j = 0; j < M.A[i].size(); ++j) { if (k < cnt && j == cols[k]) { ++k; } else { row.push_back(M.A[i][j]); } } SASSERT(row.size() + cnt == M.A[i].size()); SASSERT(M.eq[i]); m_basis.A.push_back(row); m_basis.b.push_back(M.b[i]); m_basis.eq.push_back(true); } m_basis_valid = true; m_ineqs_valid = false; m_empty = false; m_fn = r.m_fn; TRACE("dl", for (unsigned i = 0; i < cnt; ++i) { tout << cols[i] << " "; } tout << "\n"; r.display(tout); display(tout);); } void mk_rename(const karr_relation & r, unsigned col_cnt, const unsigned * cols) { if (r.empty()) { m_empty = true; return; } m_ineqs.reset(); m_basis.reset(); m_ineqs_valid = r.m_ineqs_valid; m_basis_valid = r.m_basis_valid; if (m_ineqs_valid) { m_ineqs.append(r.m_ineqs); mk_rename(m_ineqs, col_cnt, cols); } if (m_basis_valid) { m_basis.append(r.m_basis); mk_rename(m_basis, col_cnt, cols); } m_fn = r.m_fn; TRACE("dl", r.display(tout); display(tout);); } void mk_union(karr_relation const& src, karr_relation* delta) { if (src.empty()) { if (delta) { delta->m_empty = true; } return; } matrix const& M = src.get_basis(); if (empty()) { m_basis = M; m_basis_valid = true; m_empty = false; m_ineqs_valid = false; if (delta) { delta->copy(*this); } return; } matrix& N = get_basis(); unsigned N_size = N.size(); for (unsigned i = 0; i < M.size(); ++i) { bool found = false; for (unsigned j = 0; !found && j < N_size; ++j) { found = same_row(M.A[i], N.A[j]) && M.b[i] == N.b[j] && M.eq[i] == N.eq[j]; } if (!found) { N.A.push_back(M.A[i]); N.b.push_back(M.b[i]); N.eq.push_back(M.eq[i]); } } m_ineqs_valid = false; if (N_size != N.size()) { if (delta) { delta->copy(*this); } } } matrix const& get_basis() const { init_basis(); return m_basis; } matrix& get_basis() { init_basis(); return m_basis; } matrix const& get_ineqs() const { init_ineqs(); return m_ineqs; } matrix & get_ineqs() { init_ineqs(); return m_ineqs; } private: void copy(karr_relation const& other) { m_ineqs = other.m_ineqs; m_basis = other.m_basis; m_basis_valid = other.m_basis_valid; m_ineqs_valid = other.m_ineqs_valid; m_empty = other.m_empty; } bool same_row(vector const& r1, vector const& r2) const { SASSERT(r1.size() == r2.size()); for (unsigned i = 0; i < r1.size(); ++i) { if (r1[i] != r2[i]) { return false; } } return true; } void mk_rename(matrix& M, unsigned col_cnt, unsigned const* cols) { for (unsigned j = 0; j < M.size(); ++j) { vector & row = M.A[j]; rational tmp = row[cols[0]]; for (unsigned i = 0; i + 1 < col_cnt; ++i) { row[cols[i]] = row[cols[i+1]]; } row[cols[col_cnt-1]] = tmp; } } bool is_eq(expr* e, var*& v, rational& n) { expr* e1, *e2; if (!m.is_eq(e, e1, e2)) { return false; } if (!is_var(e1)) { std::swap(e1, e2); } if (!is_var(e1)) { return false; } v = to_var(e1); if (!a.is_numeral(e2, n)) { return false; } return true; } bool is_linear(expr* e, vector& row, rational& b, rational const& mul) { if (!a.is_int(e)) { return false; } if (is_var(e)) { row[to_var(e)->get_idx()] += mul; return true; } if (!is_app(e)) { return false; } rational n; if (a.is_numeral(e, n)) { b += mul*n; return true; } if (a.is_add(e)) { for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { if (!is_linear(to_app(e)->get_arg(i), row, b, mul)) { return false; } } return true; } expr* e1, *e2; if (a.is_sub(e, e1, e2)) { return is_linear(e1, row, b, mul) && is_linear(e2, row, b, -mul); } if (a.is_mul(e, e1, e2) && a.is_numeral(e1, n)) { return is_linear(e2, row, b, mul*n); } if (a.is_mul(e, e1, e2) && a.is_numeral(e2, n)) { return is_linear(e1, row, b, mul*n); } if (a.is_uminus(e, e1)) { return is_linear(e1, row, b, -mul); } return false; } void init_ineqs() const { if (!m_ineqs_valid) { SASSERT(m_basis_valid); m_plugin.dualizeH(m_ineqs, m_basis); m_ineqs_valid = true; } } void init_basis() const { if (!m_basis_valid) { SASSERT(m_ineqs_valid); if (m_plugin.dualizeI(m_basis, m_ineqs)) { m_basis_valid = true; } else { m_empty = true; } } } void to_formula(vector const& row, rational const& b, bool is_eq, expr_ref_vector& conj) const { expr_ref_vector sum(m); expr_ref zero(m), lhs(m); zero = a.mk_numeral(rational(0), true); for (unsigned i = 0; i < row.size(); ++i) { if (row[i].is_zero()) { continue; } var* var = m.mk_var(i, a.mk_int()); if (row[i].is_one()) { sum.push_back(var); } else { sum.push_back(a.mk_mul(a.mk_numeral(row[i], true), var)); } } if (!b.is_zero()) { sum.push_back(a.mk_numeral(b, true)); } lhs = a.mk_add(sum.size(), sum.c_ptr()); if (is_eq) { conj.push_back(m.mk_eq(lhs, zero)); } else { conj.push_back(a.mk_ge(lhs, zero)); } } }; karr_relation& karr_relation_plugin::get(relation_base& r) { return dynamic_cast(r); } karr_relation const & karr_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); } void karr_relation_plugin::set_cancel(bool f) { m_hb.set_cancel(f); } relation_base * karr_relation_plugin::mk_empty(const relation_signature & s) { return alloc(karr_relation, *this, 0, s, true); } relation_base * karr_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { return alloc(karr_relation, *this, p, s, false); } class karr_relation_plugin::join_fn : public convenient_relation_join_fn { public: join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2){ } virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { karr_relation const& r1 = get(_r1); karr_relation const& r2 = get(_r2); karr_relation_plugin& p = r1.get_plugin(); karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); return result; } }; relation_join_fn * karr_relation_plugin::mk_join_fn( const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(t1) || !check_kind(t2)) { return 0; } return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); } class karr_relation_plugin::project_fn : public convenient_relation_project_fn { public: project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { } virtual relation_base * operator()(const relation_base & _r) { karr_relation const& r = get(_r); karr_relation_plugin& p = r.get_plugin(); karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); return result; } }; relation_transformer_fn * karr_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt, const unsigned * removed_cols) { return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); } class karr_relation_plugin::rename_fn : public convenient_relation_rename_fn { public: rename_fn(karr_relation_plugin& p, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) {} virtual relation_base * operator()(const relation_base & _r) { karr_relation const& r = get(_r); karr_relation_plugin& p = r.get_plugin(); karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); return result; } }; relation_transformer_fn * karr_relation_plugin::mk_rename_fn(const relation_base & r, unsigned cycle_len, const unsigned * permutation_cycle) { if (!check_kind(r)) { return 0; } return alloc(rename_fn, *this, r.get_signature(), cycle_len, permutation_cycle); } bool karr_relation_plugin::dualizeI(matrix& dst, matrix const& src) { dst.reset(); m_hb.reset(); for (unsigned i = 0; i < src.size(); ++i) { if (src.eq[i]) { m_hb.add_eq(src.A[i], -src.b[i]); } else { m_hb.add_ge(src.A[i], -src.b[i]); } } for (unsigned i = 0; !src.A.empty() && i < src.A[0].size(); ++i) { m_hb.set_is_int(i); } lbool is_sat = l_undef; try { is_sat = m_hb.saturate(); } catch (...) { is_sat = l_undef; } TRACE("dl_verbose", m_hb.display(tout);); if (is_sat == l_false) { return false; } if (is_sat == l_undef) { return true; } unsigned basis_size = m_hb.get_basis_size(); bool first_initial = true; for (unsigned i = 0; i < basis_size; ++i) { bool is_initial; vector soln; m_hb.get_basis_solution(i, soln, is_initial); if (is_initial && first_initial) { dst.A.push_back(soln); dst.b.push_back(rational(1)); dst.eq.push_back(true); first_initial = false; } else if (!is_initial) { dst.A.push_back(soln); dst.b.push_back(rational(0)); dst.eq.push_back(true); } } return true; } void karr_relation_plugin::dualizeH(matrix& dst, matrix const& src) { dst.reset(); if (src.size() == 0) { return; } m_hb.reset(); for (unsigned i = 0; i < src.size(); ++i) { vector v(src.A[i]); v.push_back(src.b[i]); if (src.eq[i]) { m_hb.add_eq(v, rational(0)); } else { m_hb.add_ge(v, rational(0)); } } for (unsigned i = 0; i < 1 + src.A[0].size(); ++i) { m_hb.set_is_int(i); } lbool is_sat = l_undef; try { is_sat = m_hb.saturate(); } catch (...) { is_sat = l_undef; } if (is_sat != l_true) { return; } TRACE("dl_verbose", m_hb.display(tout);); SASSERT(is_sat == l_true); unsigned basis_size = m_hb.get_basis_size(); for (unsigned i = 0; i < basis_size; ++i) { bool is_initial; vector soln; m_hb.get_basis_solution(i, soln, is_initial); if (!is_initial) { dst.b.push_back(soln.back()); dst.eq.push_back(true); soln.pop_back(); dst.A.push_back(soln); } } } class karr_relation_plugin::union_fn : public relation_union_fn { public: union_fn() {} virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { karr_relation& r = get(_r); karr_relation const& src = get(_src); TRACE("dl", r.display(tout << "dst:\n"); src.display(tout << "src:\n");); if (_delta) { karr_relation& d = get(*_delta); r.mk_union(src, &d); } else { r.mk_union(src, 0); } TRACE("dl", r.display(tout << "result:\n");); } }; relation_union_fn * karr_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return 0; } return alloc(union_fn); } class karr_relation_plugin::filter_identical_fn : public relation_mutator_fn { unsigned_vector m_identical_cols; public: filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) : m_identical_cols(col_cnt, identical_cols) {} virtual void operator()(relation_base & _r) { karr_relation & r = get(_r); TRACE("dl", r.display(tout << "src:\n");); r.get_ineqs(); for (unsigned i = 1; i < m_identical_cols.size(); ++i) { unsigned c1 = m_identical_cols[0]; unsigned c2 = m_identical_cols[i]; vector row; row.resize(r.get_signature().size()); row[c1] = rational(1); row[c2] = rational(-1); r.m_ineqs.A.push_back(row); r.m_ineqs.b.push_back(rational(0)); r.m_ineqs.eq.push_back(true); r.m_basis_valid = false; } TRACE("dl", r.display(tout << "result:\n");); } }; relation_mutator_fn * karr_relation_plugin::mk_filter_identical_fn( const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { if(!check_kind(t)) { return 0; } return alloc(filter_identical_fn, col_cnt, identical_cols); } class karr_relation_plugin::filter_equal_fn : public relation_mutator_fn { unsigned m_col; rational m_value; bool m_valid; public: filter_equal_fn(relation_manager & m, const relation_element & value, unsigned col) : m_col(col) { arith_util arith(m.get_context().get_manager()); m_valid = arith.is_numeral(value, m_value) && m_value.is_int(); } virtual void operator()(relation_base & _r) { karr_relation & r = get(_r); if (m_valid) { r.get_ineqs(); vector row; row.resize(r.get_signature().size()); row[m_col] = rational(1); r.m_ineqs.A.push_back(row); r.m_ineqs.b.push_back(rational(-1)); r.m_ineqs.eq.push_back(true); r.m_basis_valid = false; } TRACE("dl", tout << m_value << "\n"; r.display(tout);); } }; relation_mutator_fn * karr_relation_plugin::mk_filter_equal_fn(const relation_base & r, const relation_element & value, unsigned col) { if (check_kind(r)) { return alloc(filter_equal_fn, get_manager(), value, col); } return 0; } class karr_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { app_ref m_cond; public: filter_interpreted_fn(karr_relation const& t, app* cond): m_cond(cond, t.get_plugin().get_ast_manager()) { } void operator()(relation_base& t) { get(t).filter_interpreted(m_cond); TRACE("dl", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); } }; relation_mutator_fn * karr_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { if (check_kind(t)) { return alloc(filter_interpreted_fn, get(t), condition); } return 0; } }; z3-4.4.0/src/muz/rel/rel_context.cpp0000644000175000017500000005324012540347414017067 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: rel_context.cpp Abstract: context for relational datalog engine. Author: Nikolaj Bjorner (nbjorner) 2012-12-3. Revision History: Extracted from dl_context --*/ #include"rel_context.h" #include"stopwatch.h" #include"dl_context.h" #include"dl_compiler.h" #include"dl_instruction.h" #include"dl_mk_explanations.h" #include"dl_mk_magic_sets.h" #include"dl_product_relation.h" #include"dl_bound_relation.h" #include"dl_interval_relation.h" #include"karr_relation.h" #include"dl_finite_product_relation.h" #include"udoc_relation.h" #include"check_relation.h" #include"dl_lazy_table.h" #include"dl_sparse_table.h" #include"dl_table.h" #include"dl_table_relation.h" #include"aig_exporter.h" #include"dl_mk_simple_joins.h" #include"dl_mk_similarity_compressor.h" #include"dl_mk_unbound_compressor.h" #include"dl_mk_subsumption_checker.h" #include"dl_mk_partial_equiv.h" #include"dl_mk_coi_filter.h" #include"dl_mk_filter_rules.h" #include"dl_mk_rule_inliner.h" #include"dl_mk_interp_tail_simplifier.h" #include"dl_mk_bit_blast.h" #include"dl_mk_separate_negated_tails.h" #include"ast_util.h" namespace datalog { class rel_context::scoped_query { context& m_ctx; rule_set m_rules; decl_set m_preds; bool m_was_closed; public: scoped_query(context& ctx): m_ctx(ctx), m_rules(ctx.get_rules()), m_preds(ctx.get_predicates()), m_was_closed(ctx.is_closed()) { if (m_was_closed) { ctx.reopen(); } } ~scoped_query() { m_ctx.reopen(); m_ctx.restrict_predicates(m_preds); m_ctx.replace_rules(m_rules); if (m_was_closed) { m_ctx.close(); } } void reset() { m_ctx.reopen(); m_ctx.restrict_predicates(m_preds); m_ctx.replace_rules(m_rules); m_ctx.close(); } }; rel_context::rel_context(context& ctx) : rel_context_base(ctx.get_manager(), "datalog"), m_context(ctx), m(ctx.get_manager()), m_rmanager(ctx), m_answer(m), m_last_result_relation(0), m_ectx(ctx), m_sw(0) { // register plugins for builtin tables relation_manager& rm = get_rmanager(); rm.register_plugin(alloc(sparse_table_plugin, rm)); rm.register_plugin(alloc(hashtable_table_plugin, rm)); rm.register_plugin(alloc(bitvector_table_plugin, rm)); rm.register_plugin(alloc(equivalence_table_plugin, rm)); rm.register_plugin(lazy_table_plugin::mk_sparse(rm)); // register plugins for builtin relations rm.register_plugin(alloc(bound_relation_plugin, rm)); rm.register_plugin(alloc(interval_relation_plugin, rm)); if (m_context.karr()) rm.register_plugin(alloc(karr_relation_plugin, rm)); rm.register_plugin(alloc(udoc_plugin, rm)); rm.register_plugin(alloc(check_relation_plugin, rm)); } rel_context::~rel_context() { if (m_last_result_relation) { m_last_result_relation->deallocate(); m_last_result_relation = 0; } } lbool rel_context::saturate() { scoped_query sq(m_context); return saturate(sq); } lbool rel_context::saturate(scoped_query& sq) { m_context.ensure_closed(); unsigned remaining_time_limit = m_context.soft_timeout(); unsigned restart_time = m_context.initial_restart_timeout(); bool time_limit = remaining_time_limit != 0; instruction_block termination_code; lbool result; TRACE("dl", m_context.display(tout);); while (true) { m_ectx.reset(); m_code.reset(); termination_code.reset(); m_context.ensure_closed(); transform_rules(); if (m_context.canceled()) { result = l_undef; break; } TRACE("dl", m_context.display(tout);); //IF_VERBOSE(3, m_context.display_smt2(0,0,verbose_stream());); if (m_context.print_aig().size()) { const char *filename = static_cast(m_context.print_aig().c_ptr()); aig_exporter aig(m_context.get_rules(), get_context(), &m_table_facts); std::ofstream strm(filename, std::ios_base::binary); aig(strm); exit(0); } ::stopwatch sw; sw.start(); compiler::compile(m_context, m_context.get_rules(), m_code, termination_code); bool timeout_after_this_round = time_limit && (restart_time==0 || remaining_time_limit<=restart_time); if (time_limit || restart_time!=0) { unsigned timeout = time_limit ? (restart_time!=0) ? std::min(remaining_time_limit, restart_time) : remaining_time_limit : restart_time; m_ectx.set_timelimit(timeout); } bool early_termination = !m_code.perform(m_ectx); m_ectx.reset_timelimit(); VERIFY( termination_code.perform(m_ectx) || m_context.canceled()); m_code.process_all_costs(); sw.stop(); m_sw += sw.get_seconds(); IF_VERBOSE(10, m_ectx.report_big_relations(1000, verbose_stream());); if (m_context.canceled()) { result = l_undef; break; } if (!early_termination) { m_context.set_status(OK); result = l_true; break; } if (memory::above_high_watermark()) { m_context.set_status(MEMOUT); result = l_undef; break; } if (timeout_after_this_round) { m_context.set_status(TIMEOUT); result = l_undef; break; } SASSERT(restart_time != 0); if (time_limit) { SASSERT(remaining_time_limit>restart_time); remaining_time_limit -= restart_time; } uint64 new_restart_time = static_cast(restart_time)*m_context.initial_restart_timeout(); if (new_restart_time > UINT_MAX) { restart_time = UINT_MAX; } else { restart_time = static_cast(new_restart_time); } sq.reset(); } m_context.record_transformed_rules(); TRACE("dl", display_profile(tout);); return result; } lbool rel_context::query(unsigned num_rels, func_decl * const* rels) { setup_default_relation(); get_rmanager().reset_saturated_marks(); scoped_query _scoped_query(m_context); for (unsigned i = 0; i < num_rels; ++i) { m_context.set_output_predicate(rels[i]); } m_context.close(); reset_negated_tables(); lbool res = saturate(_scoped_query); switch(res) { case l_true: { expr_ref_vector ans(m); expr_ref e(m); bool some_non_empty = num_rels == 0; bool is_approx = false; for (unsigned i = 0; i < num_rels; ++i) { func_decl* q = m_context.get_rules().get_pred(rels[i]); relation_base& rel = get_relation(q); if (!rel.empty()) { some_non_empty = true; } if (!rel.is_precise()) { is_approx = true; } rel.to_formula(e); #if 0 // Alternative format: // List the signature of the relation as // part of the answer. expr_ref_vector args(m); for (unsigned j = 0; j < q->get_arity(); ++j) { args.push_back(m.mk_var(j, q->get_domain(j))); } e = m.mk_implies(m.mk_app(q, args.size(), args.c_ptr()), e); #endif ans.push_back(e); } SASSERT(!m_last_result_relation); if (some_non_empty) { m_answer = mk_and(m, ans.size(), ans.c_ptr()); if (is_approx) { res = l_undef; m_context.set_status(APPROX); } } else { m_answer = m.mk_false(); res = l_false; } break; } case l_false: m_answer = m.mk_false(); break; case l_undef: break; } return res; } #define _MIN_DONE_ 1 void rel_context::transform_rules() { rule_transformer transf(m_context); #ifdef _MIN_DONE_ transf.register_plugin(alloc(mk_coi_filter, m_context)); #endif transf.register_plugin(alloc(mk_filter_rules, m_context)); transf.register_plugin(alloc(mk_simple_joins, m_context)); if (m_context.unbound_compressor()) { transf.register_plugin(alloc(mk_unbound_compressor, m_context)); } #ifdef _MIN_DONE_ if (m_context.similarity_compressor()) { transf.register_plugin(alloc(mk_similarity_compressor, m_context)); } #endif transf.register_plugin(alloc(mk_partial_equivalence_transformer, m_context)); #ifdef _MIN_DONE_ transf.register_plugin(alloc(mk_rule_inliner, m_context)); #endif transf.register_plugin(alloc(mk_interp_tail_simplifier, m_context)); transf.register_plugin(alloc(mk_separate_negated_tails, m_context)); if (m_context.xform_bit_blast()) { transf.register_plugin(alloc(mk_bit_blast, m_context, 22000)); transf.register_plugin(alloc(mk_interp_tail_simplifier, m_context, 21000)); } m_context.transform_rules(transf); } bool rel_context::try_get_size(func_decl* p, unsigned& rel_size) const { relation_base* rb = try_get_relation(p); if (rb && rb->knows_exact_size()) { rel_size = rb->get_size_estimate_rows(); return true; } else { return false; } } lbool rel_context::query(expr* query) { setup_default_relation(); get_rmanager().reset_saturated_marks(); scoped_query _scoped_query(m_context); rule_manager& rm = m_context.get_rule_manager(); func_decl_ref query_pred(m); try { query_pred = rm.mk_query(query, m_context.get_rules()); } catch (default_exception& exn) { m_context.set_status(INPUT_ERROR); throw exn; } m_context.close(); reset_negated_tables(); if (m_context.generate_explanations()) { m_context.transform_rules(alloc(mk_explanations, m_context)); } query_pred = m_context.get_rules().get_pred(query_pred); if (m_context.magic_sets_for_queries()) { m_context.transform_rules(alloc(mk_magic_sets, m_context, query_pred)); query_pred = m_context.get_rules().get_pred(query_pred); } lbool res = saturate(_scoped_query); query_pred = m_context.get_rules().get_pred(query_pred); if (res != l_undef) { m_last_result_relation = get_relation(query_pred).clone(); if (m_last_result_relation->empty()) { res = l_false; m_answer = m.mk_false(); } else { m_last_result_relation->to_formula(m_answer); if (!m_last_result_relation->is_precise()) { m_context.set_status(APPROX); res = l_undef; } } } return res; } void rel_context::reset_negated_tables() { rule_set::pred_set_vector const & pred_sets = m_context.get_rules().get_strats(); bool non_empty = false; for (unsigned i = 1; i < pred_sets.size(); ++i) { func_decl_set::iterator it = pred_sets[i]->begin(), end = pred_sets[i]->end(); for (; it != end; ++it) { func_decl* pred = *it; relation_base & rel = get_relation(pred); if (!rel.fast_empty()) { non_empty = true; break; } } } if (!non_empty) { return; } // collect predicates that depend on negation. func_decl_set depends_on_negation; for (unsigned i = 1; i < pred_sets.size(); ++i) { bool change = true; while (change) { change = false; func_decl_set::iterator it = pred_sets[i]->begin(), end = pred_sets[i]->end(); for (; it != end; ++it) { func_decl* pred = *it; if (depends_on_negation.contains(pred)) { continue; } rule_vector const& rules = m_context.get_rules().get_predicate_rules(pred); bool inserted = false; for (unsigned j = 0; !inserted && j < rules.size(); ++j) { rule* r = rules[j]; unsigned psz = r->get_positive_tail_size(); unsigned tsz = r->get_uninterpreted_tail_size(); if (psz < tsz) { depends_on_negation.insert(pred); change = true; inserted = true; } for (unsigned k = 0; !inserted && k < tsz; ++k) { func_decl* tail_decl = r->get_tail(k)->get_decl(); if (depends_on_negation.contains(tail_decl)) { depends_on_negation.insert(pred); change = true; inserted = true; } } } } } } func_decl_set::iterator it = depends_on_negation.begin(), end = depends_on_negation.end(); for (; it != end; ++it) { func_decl* pred = *it; relation_base & rel = get_relation(pred); if (!rel.empty()) { TRACE("dl", tout << "Resetting: " << mk_ismt2_pp(pred, m) << "\n";); rel.reset(); } } } void rel_context::restrict_predicates(func_decl_set const& predicates) { get_rmanager().restrict_predicates(predicates); } relation_base & rel_context::get_relation(func_decl * pred) { return get_rmanager().get_relation(pred); } relation_base * rel_context::try_get_relation(func_decl * pred) const { return get_rmanager().try_get_relation(pred); } expr_ref rel_context::try_get_formula(func_decl* p) const { expr_ref result(m); relation_base* rb = try_get_relation(p); if (rb) { rb->to_formula(result); } return result; } bool rel_context::is_empty_relation(func_decl* pred) const { relation_base* rb = try_get_relation(pred); return !rb || rb->fast_empty(); } relation_manager & rel_context::get_rmanager() { return m_rmanager; } const relation_manager & rel_context::get_rmanager() const { return m_rmanager; } bool rel_context::output_profile() const { return m_context.output_profile(); } void rel_context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) { TRACE("dl", tout << pred->get_name() << ": "; for (unsigned i = 0; i < relation_name_cnt; ++i) { tout << relation_names[i] << " "; } tout << "\n"; ); relation_manager & rmgr = get_rmanager(); family_id target_kind = null_family_id; switch (relation_name_cnt) { case 0: return; case 1: target_kind = get_ordinary_relation_plugin(relation_names[0]).get_kind(); break; default: { rel_spec rel_kinds; // kinds of plugins that are not table plugins family_id rel_kind; // the aggregate kind of non-table plugins for (unsigned i = 0; i < relation_name_cnt; i++) { relation_plugin & p = get_ordinary_relation_plugin(relation_names[i]); rel_kinds.push_back(p.get_kind()); } if (rel_kinds.size() == 1) { rel_kind = rel_kinds[0]; } else { relation_signature rel_sig; rmgr.from_predicate(pred, rel_sig); product_relation_plugin & prod_plugin = product_relation_plugin::get_plugin(rmgr); rel_kind = prod_plugin.get_relation_kind(rel_sig, rel_kinds); } target_kind = rel_kind; break; } } SASSERT(target_kind != null_family_id); get_rmanager().set_predicate_kind(pred, target_kind); } void rel_context::set_cancel(bool f) { get_rmanager().set_cancel(f); } void rel_context::setup_default_relation() { if (m_context.default_relation() == symbol("doc")) { m_context.set_unbound_compressor(false); } } relation_plugin & rel_context::get_ordinary_relation_plugin(symbol relation_name) { relation_plugin * plugin = get_rmanager().get_relation_plugin(relation_name); if (!plugin) { std::stringstream sstm; sstm << "relation plugin " << relation_name << " does not exist"; throw default_exception(sstm.str()); } if (plugin->is_product_relation()) { throw default_exception("cannot request product relation directly"); } if (plugin->is_sieve_relation()) { throw default_exception("cannot request sieve relation directly"); } if (plugin->is_finite_product_relation()) { throw default_exception("cannot request finite product relation directly"); } return *plugin; } bool rel_context::result_contains_fact(relation_fact const& f) { SASSERT(m_last_result_relation); return m_last_result_relation->contains_fact(f); } void rel_context::add_fact(func_decl* pred, relation_fact const& fact) { get_rmanager().reset_saturated_marks(); get_relation(pred).add_fact(fact); if (m_context.print_aig().size()) { m_table_facts.push_back(std::make_pair(pred, fact)); } } void rel_context::add_fact(func_decl* pred, table_fact const& fact) { get_rmanager().reset_saturated_marks(); relation_base & rel0 = get_relation(pred); if (rel0.from_table()) { table_relation & rel = static_cast(rel0); rel.add_table_fact(fact); // TODO: table facts? } else { relation_fact rfact(m); for (unsigned i = 0; i < fact.size(); ++i) { rfact.push_back(m_context.get_decl_util().mk_numeral(fact[i], pred->get_domain()[i])); } add_fact(pred, rfact); } } bool rel_context::has_facts(func_decl * pred) const { relation_base* r = try_get_relation(pred); return r && !r->empty(); } void rel_context::store_relation(func_decl * pred, relation_base * rel) { get_rmanager().store_relation(pred, rel); } void rel_context::collect_statistics(statistics& st) const { st.update("saturation time", m_sw); m_code.collect_statistics(st); m_ectx.collect_statistics(st); } void rel_context::updt_params() { if (m_context.check_relation() != symbol::null && m_context.check_relation() != symbol("null")) { symbol cr("check_relation"); m_context.set_default_relation(cr); relation_plugin* p = get_rmanager().get_relation_plugin(cr); SASSERT(p); check_relation_plugin* p1 = dynamic_cast(p); relation_plugin* p2 = get_rmanager().get_relation_plugin(m_context.check_relation()); SASSERT(p2); SASSERT(p1 != p2); p1->set_plugin(p2); get_rmanager().set_favourite_plugin(p1); if (m_context.check_relation() == symbol("doc")) { m_context.set_unbound_compressor(false); } } } void rel_context::inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred) { if (orig_pred) { family_id target_kind = get_rmanager().get_requested_predicate_kind(orig_pred); if (target_kind != null_family_id) { get_rmanager().set_predicate_kind(new_pred, target_kind); } } } void rel_context::display_output_facts(rule_set const& rules, std::ostream & out) const { get_rmanager().display_output_tables(rules, out); } void rel_context::display_facts(std::ostream& out) const { get_rmanager().display(out); } void rel_context::display_profile(std::ostream& out) { m_code.make_annotations(m_ectx); m_code.process_all_costs(); out << "Big relations\n"; m_ectx.report_big_relations(1000, out); get_rmanager().display_relation_sizes(out); } }; z3-4.4.0/src/muz/rel/doc.h0000644000175000017500000002772612540347414014765 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: doc.h Abstract: difference of cubes. Author: Nuno Lopes (a-nlopes) 2013-03-01 Nikolaj Bjorner (nbjorner) 2014-09-15 Revision History: Based on ternary_diff_bitvector by Nuno Lopes. --*/ #ifndef _DOC_H_ #define _DOC_H_ #include "tbv.h" #include "union_find.h" #include "buffer.h" class doc; template class union_bvec; typedef union_find<> subset_ints; typedef union_bvec utbv; typedef buffer tbv_vector; typedef buffer doc_vector; class doc_manager { tbv_manager m; tbv* m_full; small_object_allocator m_alloc; enum project_action_t { project_is_empty, project_done, project_monolithic, project_neg, project_pos, project_resolve }; project_action_t pick_resolvent( tbv const& pos, tbv_vector const& neg, bit_vector const& to_delete, unsigned& idx); public: doc_manager(unsigned num_bits); ~doc_manager(); tbv_manager& tbvm() { return m; } tbv_manager const& tbvm() const { return m; } doc* allocate(); doc* allocate1(); doc* allocate0(); doc* allocateX(); doc* allocate(doc const& src); doc* allocate(tbv const& src); doc* allocate(tbv * src); doc* allocate(uint64 n); doc* allocate(rational const& r); doc* allocate(uint64 n, unsigned hi, unsigned lo); doc* allocate(doc const& src, unsigned const* permutation); void deallocate(doc* src); void copy(doc& dst, doc const& src); doc& reset(doc& src) { return fill0(src); } doc& fill0(doc& src); doc& fill1(doc& src); doc& fillX(doc& src); bool is_full(doc const& src) const; bool is_empty_complete(ast_manager& m, doc const& src); bool set_and(doc& dst, doc const& src); bool set_and(doc& dst, tbv const& src); bool fold_neg(doc& dst); bool intersect(doc const& A, doc const& B, doc& result); void complement(doc const& src, doc_vector& result); void subtract(doc const& A, doc const& B, doc_vector& result); bool equals(doc const& a, doc const& b) const; unsigned hash(doc const& src) const; bool contains(doc const& a, doc const& b) const; bool contains(doc const& a, unsigned_vector const& colsa, doc const& b, unsigned_vector const& colsb) const; std::ostream& display(std::ostream& out, doc const& b) const; std::ostream& display(std::ostream& out, doc const& b, unsigned hi, unsigned lo) const; unsigned num_tbits() const { return m.num_tbits(); } unsigned get_size_estimate_bytes(const doc& d) const; doc* project(doc_manager& dstm, bit_vector const& to_delete, doc const& src); bool well_formed(doc const& d) const; bool merge(doc& d, unsigned lo, unsigned length, subset_ints const& equalities, bit_vector const& discard_cols); void set(doc& d, unsigned idx, tbit value); doc* join(const doc& a, const doc& b, doc_manager& dm1, const unsigned_vector& cols1, const unsigned_vector& cols2); void verify_project(ast_manager& m, doc_manager& dstm, bit_vector const& to_delete, doc const& src, doc const& dst); private: unsigned diff_by_012(tbv const& pos, tbv const& neg, unsigned& index); bool merge(doc& d, unsigned idx, subset_ints const& equalities, bit_vector const& discard_cols); void project_rename(expr_ref& fml, bit_vector const& to_delete); void project_expand(expr_ref& fml, bit_vector const& to_delete); expr_ref to_formula(ast_manager& m, doc const& src); void check_equiv(ast_manager& m, expr* fml1, expr* fml2); }; // union of tbv*, union of doc* template class union_bvec { buffer m_elems; // TBD: reuse allocator of M enum fix_bit_result_t { e_row_removed, // = 1 e_duplicate_row, // = 2 e_fixed }; public: unsigned size() const { return m_elems.size(); } T& operator[](unsigned idx) const { return *m_elems[idx]; } bool is_empty() const { return m_elems.empty(); } bool is_empty_complete(ast_manager& m, doc_manager& dm) const { for (unsigned i = 0; i < size(); ++i) { if (!dm.is_empty_complete(m, *m_elems[i])) return false; } return true; } bool is_full(M& m) const { return size() == 1 && m.is_full(*m_elems[0]); } bool contains(M& m, T& t) const { for (unsigned i = 0; i < size(); ++i) { if (m.contains(*m_elems[i], t)) return true; } return false; } std::ostream& display(M const& m, std::ostream& out) const { if (m.num_tbits() == 0) return out << "[]"; return display(m, out, m.num_tbits()-1, 0); } std::ostream& display(M const& m, std::ostream& out, unsigned hi, unsigned lo) const { out << "{"; if (size() + m.num_tbits() > 10) out << "\n "; for (unsigned i = 0; i < size(); ++i) { m.display(out, *m_elems[i], hi, lo); if (i + 1 < size()) out << ", "; if (i + 1 < size() && m.num_tbits() > 10) out << "\n "; } return out << "}"; } void push_back(T* t) { SASSERT(t); m_elems.push_back(t); } void erase(M& m, unsigned idx) { m.deallocate(m_elems[idx]); unsigned sz = m_elems.size(); for (unsigned i = idx+1; i < sz; ++i) { m_elems[i-1] = m_elems[i]; } m_elems.resize(sz-1); } void reset(M& m) { for (unsigned i = 0; i < m_elems.size(); ++i) { m.deallocate(m_elems[i]); } m_elems.reset(); } bool insert(M& m, T* t) { SASSERT(t); unsigned sz = size(), j = 0; bool found = false; unsigned i = 0; for ( ; i < sz; ++i, ++j) { if (m.contains(*m_elems[i], *t)) { found = true; } else if (m.contains(*t, *m_elems[i])) { m.deallocate(m_elems[i]); --j; continue; } if (i != j) { m_elems[j] = m_elems[i]; } } if (j != sz) m_elems.resize(j); if (found) { m.deallocate(t); } else { m_elems.push_back(t); } return !found; } void intersect(M& m, T const& t) { unsigned sz = size(); unsigned j = 0; for (unsigned i = 0; i < sz; ++i, ++j) { if (!m.set_and(*m_elems[i], t)) { m.deallocate(m_elems[i]); --j; } else if (i != j) { m_elems[j] = m_elems[i]; } } if (j != sz) m_elems.resize(j); } void insert(M& m, union_bvec const& other) { for (unsigned i = 0; i < other.size(); ++i) { insert(m, &other[i]); } } void intersect(M& m, union_bvec const& other) { unsigned sz = other.size(); union_bvec tmp, result; for (unsigned i = 0; i < sz; ++i) { tmp.copy(m, *this); tmp.intersect(m, other[i]); for (unsigned j = 0; j < tmp.size(); ++j) { result.push_back(tmp.m_elems[j]); } tmp.m_elems.reset(); } std::swap(*this, result); result.reset(m); } void subtract(M& m, union_bvec const& other) { unsigned sz = other.size(); for (unsigned i = 0; !is_empty() && i < sz; ++i) { subtract(m, other[i]); } // TBD compress? } void subtract(M& m, T& t) { unsigned sz = size(); union_bvec result; for (unsigned i = 0; i < sz; ++i) { m.subtract(*m_elems[i], t, result.m_elems); } std::swap(m_elems, result.m_elems); result.reset(m); } void complement(M& m, union_bvec& result) const { union_bvec negated; result.reset(m); result.push_back(m.allocateX()); unsigned sz = size(); for (unsigned i = 0; !is_empty() && i < sz; ++i) { m.complement(*m_elems[i], negated.m_elems); result.intersect(m, negated); negated.reset(m); } } void copy(M& m, union_bvec const& other) { reset(m); for (unsigned i = 0; i < other.size(); ++i) { push_back(m.allocate(other[i])); } } void simplify(M& m) { union_bvec result; for (unsigned i = 0; i < size(); ++i) { if (m.fold_neg(*m_elems[i])) { result.insert(m, m_elems[i]); } else { m.deallocate(m_elems[i]); } } std::swap(*this, result); } bool well_formed(M& m) const { for (unsigned i = 0; i < size(); ++i) { if (!m.well_formed(*m_elems[i])) return false; } return true; } void merge(M& m, unsigned lo, unsigned length, subset_ints const& equalities, bit_vector const& discard_cols) { unsigned j = 0; unsigned sz = size(); for (unsigned i = 0; i < sz; ++i, ++j) { if (!m.merge(*m_elems[i], lo, length, equalities, discard_cols)) { --j; m.deallocate(m_elems[i]); } else if (i != j) { m_elems[j] = m_elems[i]; } } if (j != sz) m_elems.resize(j); } void merge(M& m, unsigned lo1, unsigned lo2, unsigned length, bit_vector const& discard_cols) { union_find_default_ctx union_ctx; subset_ints equalities(union_ctx); for (unsigned i = 0; i < discard_cols.size(); ++i) { equalities.mk_var(); } for (unsigned j = 0; j < length; ++j) { equalities.merge(lo1 + j, lo2 + j); } merge(m, lo1, length, equalities, discard_cols); } void merge(M& m, unsigned_vector const& roots, subset_ints const& equalities, bit_vector const& discard_cols) { for (unsigned i = 0; i < roots.size(); ++i) { merge(m, roots[i], 1, equalities, discard_cols); } } void join(const union_bvec& d1, const union_bvec& d2, M& dm, M& dm1, const unsigned_vector& cols1, const unsigned_vector& cols2) { for (unsigned i = 0; i < d1.size(); ++i) { for (unsigned j = 0; j < d2.size(); ++j) { if (T *d = dm.join(d1[i], d2[j], dm1, cols1, cols2)) insert(dm, d); } } } unsigned get_size_estimate_bytes(const M& m) const { unsigned sz = sizeof(T*) * size(); for (unsigned i = 0; i < size(); ++i) { sz += m.get_size_estimate_bytes(*m_elems[i]); } return sz; } }; class doc { // pos \ (neg_0 \/ ... \/ neg_n) friend class doc_manager; tbv* m_pos; utbv m_neg; public: struct eq { doc_manager& m; eq(doc_manager& m):m(m) {} bool operator()(doc const& d1, doc const& d2) const { return m.equals(d1, d2); } }; struct hash { doc_manager& m; hash(doc_manager& m):m(m) {} unsigned operator()(doc const& d) const { return m.hash(d); } }; doc(tbv* t): m_pos(t) {} tbv& pos() { return *m_pos; } utbv& neg() { return m_neg; } tbv const& pos() const { return *m_pos; } utbv const& neg() const { return m_neg; } tbit operator[](unsigned idx) const { return pos()[idx]; } }; typedef union_bvec udoc; class doc_ref { doc_manager& dm; doc* d; public: doc_ref(doc_manager& dm):dm(dm),d(0) {} doc_ref(doc_manager& dm, doc* d):dm(dm),d(d) {} ~doc_ref() { if (d) dm.deallocate(d); } doc_ref& operator=(doc* d2) { if (d) dm.deallocate(d); d = d2; return *this; } doc& operator*() { return *d; } doc* operator->() { return d; } doc* detach() { doc* r = d; d = 0; return r; } operator bool() const { return d != 0; } }; #endif /* _DOC_H_ */ z3-4.4.0/src/muz/rel/rel_context.h0000644000175000017500000000770312540347414016537 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: rel_context.h Abstract: context for relational datalog engine. Author: Nikolaj Bjorner (nbjorner) 2012-12-3. Revision History: Extracted from dl_context --*/ #ifndef _REL_CONTEXT_H_ #define _REL_CONTEXT_H_ #include "ast.h" #include "dl_relation_manager.h" #include "dl_instruction.h" #include "dl_engine_base.h" #include "dl_context.h" #include "lbool.h" namespace datalog { class context; typedef vector > fact_vector; class rel_context : public rel_context_base { context& m_context; ast_manager& m; relation_manager m_rmanager; expr_ref m_answer; relation_base * m_last_result_relation; fact_vector m_table_facts; execution_context m_ectx; instruction_block m_code; double m_sw; class scoped_query; void reset_negated_tables(); relation_plugin & get_ordinary_relation_plugin(symbol relation_name); lbool saturate(scoped_query& sq); void set_cancel(bool f); void setup_default_relation(); public: rel_context(context& ctx); virtual ~rel_context(); virtual relation_manager & get_rmanager(); virtual const relation_manager & get_rmanager() const; ast_manager& get_manager() const { return m; } context& get_context() const { return m_context; } virtual relation_base & get_relation(func_decl * pred); virtual relation_base * try_get_relation(func_decl * pred) const; virtual bool is_empty_relation(func_decl* pred) const; virtual expr_ref try_get_formula(func_decl * pred) const; virtual expr_ref get_answer() { return m_answer; } virtual bool output_profile() const; virtual lbool query(expr* q); virtual lbool query(unsigned num_rels, func_decl * const* rels); virtual void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names); virtual void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred); virtual void collect_statistics(statistics& st) const; virtual void cancel() { set_cancel(true); } virtual void cleanup() { set_cancel(false);} virtual void updt_params(); /** \brief Restrict the set of used predicates to \c res. The function deallocates unsused relations, it does not deal with rules. */ virtual void restrict_predicates(func_decl_set const& predicates); virtual void transform_rules(); virtual bool try_get_size(func_decl* pred, unsigned& rel_size) const; /** \brief query result if it contains fact. */ virtual bool result_contains_fact(relation_fact const& f); virtual void collect_non_empty_predicates(func_decl_set& ps) { return get_rmanager().collect_non_empty_predicates(ps); } /** \brief add facts to relation */ virtual void add_fact(func_decl* pred, relation_fact const& fact); virtual void add_fact(func_decl* pred, table_fact const& fact); /** \brief check if facts were added to relation */ virtual bool has_facts(func_decl * pred) const; /** \brief Store the relation \c rel under the predicate \c pred. The \c context object takes over the ownership of the relation object. */ virtual void store_relation(func_decl * pred, relation_base * rel); virtual void display_output_facts(rule_set const& rules, std::ostream & out) const; virtual void display_facts(std::ostream & out) const; virtual void display_profile(std::ostream& out); virtual lbool saturate(); }; }; #endif /* _REL_CONTEXT_H_ */ z3-4.4.0/src/muz/rel/dl_product_relation.h0000644000175000017500000001566712540347414020255 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_product_relation.h Abstract: A Relation relation combinator. Author: Nikolaj Bjorner (nbjorner) 2010-4-11 Revision History: --*/ #ifndef _DL_PRODUCT_RELATION_H_ #define _DL_PRODUCT_RELATION_H_ #include "dl_context.h" #include "dl_relation_manager.h" namespace datalog { class product_relation; struct rel_spec : public svector { bool well_formed() const { return true; } }; class product_relation_plugin : public relation_plugin { friend class product_relation; private: class join_fn; class transform_fn; class mutator_fn; class aligned_union_fn; class unaligned_union_fn; class single_non_transparent_src_union_fn; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; struct fid_hash { typedef family_id data; unsigned operator()(data x) const { return static_cast(x); } }; rel_spec_store > m_spec_store; family_id get_relation_kind(const product_relation & r); bool is_product_relation(relation_base * r) { return r->get_plugin().is_product_relation(); } public: static product_relation_plugin& get_plugin(relation_manager & rmgr); product_relation_plugin(relation_manager& m); virtual void initialize(family_id fid); virtual bool can_handle_signature(const relation_signature & s); virtual bool can_handle_signature(const relation_signature & s, family_id kind); static symbol get_name() { return symbol("product_relation"); } family_id get_relation_kind(const relation_signature & sig, const rel_spec & spec); virtual relation_base * mk_empty(const relation_signature & s); virtual relation_base * mk_empty(const relation_signature & s, family_id kind); virtual relation_base * mk_full(func_decl* p, const relation_signature & s); virtual relation_base * mk_full(func_decl* p, const relation_signature & s, family_id kind); protected: virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols); virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols); virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col); virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); static bool is_product_relation(relation_base const& r); private: static product_relation& get(relation_base& r); static product_relation const & get(relation_base const& r); static product_relation* get(relation_base* r); static product_relation const* get(relation_base const* r); relation_union_fn * mk_union_w_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta, bool is_widen); bool are_aligned(const product_relation& r1, const product_relation& r2); static void get_common_spec(const ptr_vector & rels, rel_spec & res); }; class product_relation : public relation_base { friend class product_relation_plugin; friend class product_relation_plugin::join_fn; friend class product_relation_plugin::transform_fn; friend class product_relation_plugin::mutator_fn; friend class product_relation_plugin::aligned_union_fn; friend class product_relation_plugin::unaligned_union_fn; friend class product_relation_plugin::single_non_transparent_src_union_fn; friend class product_relation_plugin::filter_equal_fn; friend class product_relation_plugin::filter_identical_fn; friend class product_relation_plugin::filter_interpreted_fn; /** If m_relations is empty, value of this determines whether the relation is empty or full. */ bool m_default_empty; /** There must not be two relations of the same kind */ ptr_vector m_relations; /** Array of kinds of inner relations. If two product relations have equal signature and specification, their m_relations arrays contain corresponding relations at the same indexes. The value returned by get_kind() depends uniquely on the specification. */ rel_spec m_spec; /** \brief Ensure the kind assigned to this relation reflects the types of inner relations. */ void ensure_correct_kind(); /** The current specification must be a subset of the new one. */ void convert_spec(const rel_spec & spec); public: product_relation(product_relation_plugin& p, relation_signature const& s); product_relation(product_relation_plugin& p, relation_signature const& s, unsigned num_relations, relation_base** relations); ~product_relation(); virtual bool empty() const; virtual void add_fact(const relation_fact & f); virtual bool contains_fact(const relation_fact & f) const; virtual product_relation * clone() const; virtual product_relation * complement(func_decl* p) const; virtual void display(std::ostream & out) const; virtual void to_formula(expr_ref& fml) const; product_relation_plugin& get_plugin() const; unsigned size() const { return m_relations.size(); } relation_base& operator[](unsigned i) const { return *m_relations[i]; } /** If all relations except one are sieve_relations with no inner columns, return true and into \c idx assign index of that relation. Otherwise return false. */ bool try_get_single_non_transparent(unsigned & idx) const; virtual bool is_precise() const { for (unsigned i = 0; i < m_relations.size(); ++i) { if (!m_relations[i]->is_precise()) { return false; } } return true; } }; }; #endif z3-4.4.0/src/muz/rel/dl_instruction.cpp0000644000175000017500000013733012540347414017604 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_instruction.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #include"ast_pp.h" #include"stopwatch.h" #include"dl_context.h" #include"dl_util.h" #include"dl_instruction.h" #include"rel_context.h" #include"debug.h" #include"warning.h" #include"dl_table_relation.h" namespace datalog { // ----------------------------------- // // execution_context // // ----------------------------------- execution_context::execution_context(context & context) : m_context(context), m_stopwatch(0), m_timelimit_ms(0) {} execution_context::~execution_context() { reset(); dealloc(m_stopwatch); } void execution_context::reset() { reg_vector::iterator it=m_registers.begin(); reg_vector::iterator end=m_registers.end(); for(; it != end; ++it) { relation_base * rel = *it; if (rel) { rel->deallocate(); } } m_registers.reset(); m_reg_annotation.reset(); reset_timelimit(); } rel_context& execution_context::get_rel_context() { return dynamic_cast(*m_context.get_rel_context()); } rel_context const& execution_context::get_rel_context() const { return dynamic_cast(*m_context.get_rel_context()); } struct compare_size_proc { typedef std::pair pr; bool operator()(pr const& a, pr const& b) const { return a.second > b.second; } }; void execution_context::report_big_relations(unsigned threshold, std::ostream & out) const { unsigned n = register_count(); svector > sizes; size_t total_bytes = 0; for(unsigned i = 0; i < n; i++) { unsigned sz = reg(i) ? reg(i)->get_size_estimate_bytes() : 0; total_bytes += sz; sizes.push_back(std::make_pair(i, sz)); } std::sort(sizes.begin(), sizes.end(), compare_size_proc()); out << "bytes " << total_bytes << "\n"; out << "bytes\trows\tannotation\n"; for(unsigned i = 0; i < n; i++) { unsigned sz = sizes[i].second; unsigned rg = sizes[i].first; unsigned rows = reg(rg) ? reg(rg)->get_size_estimate_rows() : 0; if (sz < threshold) { continue; } std::string annotation; get_register_annotation(i, annotation); out << sz << "\t" << rows << "\t" << annotation << "\n"; } } void execution_context::set_timelimit(unsigned time_in_ms) { SASSERT(time_in_ms > 0); m_timelimit_ms = time_in_ms; if (!m_stopwatch) { m_stopwatch = alloc(stopwatch); } m_stopwatch->stop(); m_stopwatch->reset(); m_stopwatch->start(); } void execution_context::reset_timelimit() { if (m_stopwatch) { m_stopwatch->stop(); } m_timelimit_ms = 0; } bool execution_context::should_terminate() { return m_context.canceled() || memory::above_high_watermark() || (m_stopwatch && m_timelimit_ms != 0 && m_timelimit_ms < static_cast(1000*m_stopwatch->get_current_seconds())); } void execution_context::collect_statistics(statistics& st) const { st.update("dl.joins", m_stats.m_join); st.update("dl.project", m_stats.m_project); st.update("dl.filter", m_stats.m_filter); st.update("dl.total", m_stats.m_total); st.update("dl.unary_singleton", m_stats.m_unary_singleton); st.update("dl.filter_by_negation", m_stats.m_filter_by_negation); st.update("dl.select_equal_project", m_stats.m_select_equal_project); st.update("dl.join_project", m_stats.m_join_project); st.update("dl.project_rename", m_stats.m_project_rename); st.update("dl.union", m_stats.m_union); st.update("dl.filter_interpreted_project", m_stats.m_filter_interp_project); st.update("dl.filter_id", m_stats.m_filter_id); st.update("dl.filter_eq", m_stats.m_filter_eq); } // ----------------------------------- // // instruction // // ----------------------------------- instruction::~instruction() { fn_cache::iterator it = m_fn_cache.begin(); fn_cache::iterator end = m_fn_cache.end(); for(; it != end; ++it) { dealloc(it->m_value); } } void instruction::process_all_costs() { process_costs(); } void instruction::collect_statistics(statistics& st) const { costs c; get_total_cost(c); st.update("instruction", c.instructions); st.update("instruction-time", c.milliseconds); } void instruction::display_indented(execution_context const & _ctx, std::ostream & out, std::string indentation) const { out << indentation; rel_context const& ctx = _ctx.get_rel_context(); display_head_impl(_ctx, out); if (ctx.output_profile()) { out << " {"; output_profile(out); out << '}'; } out << "\n"; display_body_impl(_ctx, out, indentation); } void instruction::log_verbose(execution_context& ctx) { IF_VERBOSE(2, display(ctx, verbose_stream());); } class instr_io : public instruction { bool m_store; func_decl_ref m_pred; reg_idx m_reg; public: instr_io(bool store, func_decl_ref pred, reg_idx reg) : m_store(store), m_pred(pred), m_reg(reg) {} virtual bool perform(execution_context & ctx) { log_verbose(ctx); if (m_store) { if (ctx.reg(m_reg)) { ctx.get_rel_context().store_relation(m_pred, ctx.release_reg(m_reg)); } else { rel_context & dctx = ctx.get_rel_context(); relation_base * empty_rel; //the object referenced by sig is valid only until we call dctx.store_relation() const relation_signature & sig = dctx.get_relation(m_pred).get_signature(); empty_rel = dctx.get_rmanager().mk_empty_relation(sig, m_pred.get()); dctx.store_relation(m_pred, empty_rel); } } else { relation_base& rel = ctx.get_rel_context().get_relation(m_pred); if (!rel.fast_empty()) { ctx.set_reg(m_reg, rel.clone()); } else { ctx.make_empty(m_reg); } } return true; } virtual void make_annotations(execution_context & ctx) { ctx.set_register_annotation(m_reg, m_pred->get_name().bare_str()); } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { const char * rel_name = m_pred->get_name().bare_str(); if (m_store) { out << "store " << m_reg << " into " << rel_name; } else { out << "load " << rel_name << " into " << m_reg; } } }; instruction * instruction::mk_load(ast_manager & m, func_decl * pred, reg_idx tgt) { return alloc(instr_io, false, func_decl_ref(pred, m), tgt); } instruction * instruction::mk_store(ast_manager & m, func_decl * pred, reg_idx src) { return alloc(instr_io, true, func_decl_ref(pred, m), src); } class instr_dealloc : public instruction { reg_idx m_reg; public: instr_dealloc(reg_idx reg) : m_reg(reg) {} virtual bool perform(execution_context & ctx) { ctx.make_empty(m_reg); return true; } virtual void make_annotations(execution_context & ctx) { ctx.set_register_annotation(m_reg, "alloc"); } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "dealloc " << m_reg; } }; instruction * instruction::mk_dealloc(reg_idx reg) { return alloc(instr_dealloc, reg); } class instr_clone_move : public instruction { bool m_clone; reg_idx m_src; reg_idx m_tgt; public: instr_clone_move(bool clone, reg_idx src, reg_idx tgt) : m_clone(clone), m_src(src), m_tgt(tgt) {} virtual bool perform(execution_context & ctx) { if (ctx.reg(m_src)) log_verbose(ctx); if (m_clone) { ctx.set_reg(m_tgt, ctx.reg(m_src) ? ctx.reg(m_src)->clone() : 0); } else { ctx.set_reg(m_tgt, ctx.reg(m_src) ? ctx.release_reg(m_src) : 0); } return true; } virtual void make_annotations(execution_context & ctx) { std::string str; if (ctx.get_register_annotation(m_src, str)) { ctx.set_register_annotation(m_tgt, str); } else if (ctx.get_register_annotation(m_tgt, str)) { ctx.set_register_annotation(m_src, str); } } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << (m_clone ? "clone " : "move ") << m_src << " into " << m_tgt; } }; instruction * instruction::mk_clone(reg_idx from, reg_idx to) { return alloc(instr_clone_move, true, from, to); } instruction * instruction::mk_move(reg_idx from, reg_idx to) { return alloc(instr_clone_move, false, from, to); } class instr_while_loop : public instruction { typedef const vector idx_vector; idx_vector m_controls; instruction_block * m_body; bool control_is_empty(execution_context & ctx) { idx_vector::const_iterator it=m_controls.begin(); idx_vector::const_iterator end=m_controls.end(); for(; it != end; ++it) { reg_idx r = *it; if (ctx.reg(r) && !ctx.reg(r)->fast_empty()) { return false; } } return true; } protected: virtual void process_all_costs() { instruction::process_all_costs(); m_body->process_all_costs(); } public: instr_while_loop(unsigned control_reg_cnt, const reg_idx * control_regs, instruction_block * body) : m_controls(control_reg_cnt, control_regs), m_body(body) {} virtual ~instr_while_loop() { dealloc(m_body); } virtual bool perform(execution_context & ctx) { log_verbose(ctx); TRACE("dl", tout << "loop entered\n";); unsigned count = 0; while (!control_is_empty(ctx)) { IF_VERBOSE(10, verbose_stream() << "looping ... " << count++ << "\n";); if (!m_body->perform(ctx)) { TRACE("dl", tout << "while loop terminated before completion\n";); return false; } } TRACE("dl", tout << "while loop exited\n";); return true; } virtual void make_annotations(execution_context & ctx) { m_body->make_annotations(ctx); } virtual void display_head_impl(execution_context const & ctx, std::ostream & out) const { out << "while"; print_container(m_controls, out); } virtual void display_body_impl(execution_context const & ctx, std::ostream & out, std::string indentation) const { m_body->display_indented(ctx, out, indentation+" "); } }; instruction * instruction::mk_while_loop(unsigned control_reg_cnt, const reg_idx * control_regs, instruction_block * body) { return alloc(instr_while_loop, control_reg_cnt, control_regs, body); } class instr_join : public instruction { typedef unsigned_vector column_vector; reg_idx m_rel1; reg_idx m_rel2; column_vector m_cols1; column_vector m_cols2; reg_idx m_res; public: instr_join(reg_idx rel1, reg_idx rel2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, reg_idx result) : m_rel1(rel1), m_rel2(rel2), m_cols1(col_cnt, cols1), m_cols2(col_cnt, cols2), m_res(result) {} virtual bool perform(execution_context & ctx) { log_verbose(ctx); ++ctx.m_stats.m_join; if (!ctx.reg(m_rel1) || !ctx.reg(m_rel2)) { ctx.make_empty(m_res); return true; } relation_join_fn * fn; const relation_base & r1 = *ctx.reg(m_rel1); const relation_base & r2 = *ctx.reg(m_rel2); if (!find_fn(r1, r2, fn)) { fn = r1.get_manager().mk_join_fn(r1, r2, m_cols1, m_cols2); if (!fn) { throw default_exception("trying to perform unsupported join operation on relations of kinds %s and %s", r1.get_plugin().get_name().bare_str(), r2.get_plugin().get_name().bare_str()); } store_fn(r1, r2, fn); } TRACE("dl", r1.get_signature().output(ctx.get_rel_context().get_manager(), tout); tout<<":"<\n";); ctx.set_reg(m_res, (*fn)(r1, r2)); TRACE("dl", ctx.reg(m_res)->get_signature().output(ctx.get_rel_context().get_manager(), tout); tout<<":"<get_size_estimate_rows()<<"\n";); if (ctx.reg(m_res)->fast_empty()) { ctx.make_empty(m_res); } return true; } virtual void make_annotations(execution_context & ctx) { std::string a1 = "rel1", a2 = "rel2"; ctx.get_register_annotation(m_rel1, a1); ctx.get_register_annotation(m_rel1, a1); ctx.set_register_annotation(m_res, "join " + a1 + " " + a2); } virtual void display_head_impl(execution_context const & ctx, std::ostream & out) const { out << "join " << m_rel1; print_container(m_cols1, out); out << " and " << m_rel2; print_container(m_cols2, out); out << " into " << m_res; } }; instruction * instruction::mk_join(reg_idx rel1, reg_idx rel2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, reg_idx result) { return alloc(instr_join, rel1, rel2, col_cnt, cols1, cols2, result); } class instr_filter_equal : public instruction { reg_idx m_reg; app_ref m_value; unsigned m_col; public: instr_filter_equal(ast_manager & m, reg_idx reg, const relation_element & value, unsigned col) : m_reg(reg), m_value(value, m), m_col(col) {} virtual bool perform(execution_context & ctx) { log_verbose(ctx); ++ctx.m_stats.m_filter_eq; if (!ctx.reg(m_reg)) { return true; } relation_mutator_fn * fn; relation_base & r = *ctx.reg(m_reg); if (!find_fn(r, fn)) { fn = r.get_manager().mk_filter_equal_fn(r, m_value, m_col); if (!fn) { throw default_exception( "trying to perform unsupported filter_equal operation on a relation of kind %s", r.get_plugin().get_name().bare_str()); } store_fn(r, fn); } (*fn)(r); if (r.fast_empty()) { ctx.make_empty(m_reg); } return true; } virtual void make_annotations(execution_context & ctx) { std::stringstream a; a << "filter_equal " << m_col << " val: " << ctx.get_rel_context().get_rmanager().to_nice_string(m_value); ctx.set_register_annotation(m_reg, a.str()); } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "filter_equal " << m_reg << " col: " << m_col << " val: " << ctx.get_rel_context().get_rmanager().to_nice_string(m_value); } }; instruction * instruction::mk_filter_equal(ast_manager & m, reg_idx reg, const relation_element & value, unsigned col) { return alloc(instr_filter_equal, m, reg, value, col); } class instr_filter_identical : public instruction { typedef unsigned_vector column_vector; reg_idx m_reg; column_vector m_cols; public: instr_filter_identical(reg_idx reg, unsigned col_cnt, const unsigned * identical_cols) : m_reg(reg), m_cols(col_cnt, identical_cols) {} virtual bool perform(execution_context & ctx) { log_verbose(ctx); ++ctx.m_stats.m_filter_id; if (!ctx.reg(m_reg)) { return true; } relation_mutator_fn * fn; relation_base & r = *ctx.reg(m_reg); if (!find_fn(r, fn)) { fn = r.get_manager().mk_filter_identical_fn(r, m_cols.size(), m_cols.c_ptr()); if (!fn) { throw default_exception( "trying to perform unsupported filter_identical operation on a relation of kind %s", r.get_plugin().get_name().bare_str()); } store_fn(r, fn); } (*fn)(r); if (r.fast_empty()) { ctx.make_empty(m_reg); } return true; } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "filter_identical " << m_reg << " "; print_container(m_cols, out); } virtual void make_annotations(execution_context & ctx) { ctx.set_register_annotation(m_reg, "filter_identical"); } }; instruction * instruction::mk_filter_identical(reg_idx reg, unsigned col_cnt, const unsigned * identical_cols) { return alloc(instr_filter_identical, reg, col_cnt, identical_cols); } class instr_filter_interpreted : public instruction { reg_idx m_reg; app_ref m_cond; public: instr_filter_interpreted(reg_idx reg, app_ref & condition) : m_reg(reg), m_cond(condition) {} virtual bool perform(execution_context & ctx) { if (!ctx.reg(m_reg)) { return true; } log_verbose(ctx); ++ctx.m_stats.m_filter; relation_mutator_fn * fn; relation_base & r = *ctx.reg(m_reg); TRACE("dl_verbose", r.display(tout <<"pre-filter-interpreted:\n");); if (!find_fn(r, fn)) { fn = r.get_manager().mk_filter_interpreted_fn(r, m_cond); if (!fn) { throw default_exception( "trying to perform unsupported filter_interpreted operation on a relation of kind %s", r.get_plugin().get_name().bare_str()); } store_fn(r, fn); } (*fn)(r); if (r.fast_empty()) { ctx.make_empty(m_reg); } //TRACE("dl_verbose", r.display(tout <<"post-filter-interpreted:\n");); return true; } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "filter_interpreted " << m_reg << " using " << mk_pp(m_cond, m_cond.get_manager()); } virtual void make_annotations(execution_context & ctx) { std::stringstream a; a << "filter_interpreted " << mk_pp(m_cond, m_cond.get_manager()); ctx.set_register_annotation(m_reg, a.str()); } }; instruction * instruction::mk_filter_interpreted(reg_idx reg, app_ref & condition) { return alloc(instr_filter_interpreted, reg, condition); } class instr_filter_interpreted_and_project : public instruction { reg_idx m_src; app_ref m_cond; unsigned_vector m_cols; reg_idx m_res; public: instr_filter_interpreted_and_project(reg_idx src, app_ref & condition, unsigned col_cnt, const unsigned * removed_cols, reg_idx result) : m_src(src), m_cond(condition), m_cols(col_cnt, removed_cols), m_res(result) {} virtual bool perform(execution_context & ctx) { log_verbose(ctx); if (!ctx.reg(m_src)) { ctx.make_empty(m_res); return true; } ++ctx.m_stats.m_filter_interp_project; relation_transformer_fn * fn; relation_base & reg = *ctx.reg(m_src); TRACE("dl_verbose", reg.display(tout <<"pre-filter-interpreted-and-project:\n");); if (!find_fn(reg, fn)) { fn = reg.get_manager().mk_filter_interpreted_and_project_fn(reg, m_cond, m_cols.size(), m_cols.c_ptr()); if (!fn) { throw default_exception( "trying to perform unsupported filter_interpreted_and_project operation on a relation of kind %s", reg.get_plugin().get_name().bare_str()); } store_fn(reg, fn); } ctx.set_reg(m_res, (*fn)(reg)); if (ctx.reg(m_res)->fast_empty()) { ctx.make_empty(m_res); } //TRACE("dl_verbose", reg.display(tout << "post-filter-interpreted-and-project:\n");); return true; } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "filter_interpreted_and_project " << m_src << " into " << m_res; out << " using " << mk_pp(m_cond, m_cond.get_manager()); out << " deleting columns "; print_container(m_cols, out); } virtual void make_annotations(execution_context & ctx) { std::stringstream s; std::string a = "rel_src"; ctx.get_register_annotation(m_src, a); s << "filter_interpreted_and_project " << mk_pp(m_cond, m_cond.get_manager()); ctx.set_register_annotation(m_res, s.str()); } }; instruction * instruction::mk_filter_interpreted_and_project(reg_idx reg, app_ref & condition, unsigned col_cnt, const unsigned * removed_cols, reg_idx result) { return alloc(instr_filter_interpreted_and_project, reg, condition, col_cnt, removed_cols, result); } class instr_union : public instruction { reg_idx m_src; reg_idx m_tgt; reg_idx m_delta; bool m_widen; //if true, widening is performed intead of an union public: instr_union(reg_idx src, reg_idx tgt, reg_idx delta, bool widen) : m_src(src), m_tgt(tgt), m_delta(delta), m_widen(widen) {} virtual bool perform(execution_context & ctx) { TRACE("dl", tout << "union " << m_src << " into " << m_tgt << " " << ctx.reg(m_src) << " " << ctx.reg(m_tgt) << "\n";); if (!ctx.reg(m_src)) { return true; } log_verbose(ctx); ++ctx.m_stats.m_union; relation_base & r_src = *ctx.reg(m_src); if (!ctx.reg(m_tgt)) { relation_base * new_tgt = r_src.get_plugin().mk_empty(r_src); ctx.set_reg(m_tgt, new_tgt); } relation_base & r_tgt = *ctx.reg(m_tgt); if (m_delta!=execution_context::void_register && !ctx.reg(m_delta)) { relation_base * new_delta = r_tgt.get_plugin().mk_empty(r_tgt); ctx.set_reg(m_delta, new_delta); } relation_base * r_delta = (m_delta!=execution_context::void_register) ? ctx.reg(m_delta) : 0; relation_union_fn * fn; if (r_delta) { if (!find_fn(r_tgt, r_src, *r_delta, fn)) { if (m_widen) { fn = r_src.get_manager().mk_widen_fn(r_tgt, r_src, r_delta); } else { fn = r_src.get_manager().mk_union_fn(r_tgt, r_src, r_delta); } if (!fn) { std::stringstream sstm; sstm << "trying to perform unsupported union operation on relations of kinds "; sstm << r_tgt.get_plugin().get_name() << ", " << r_src.get_plugin().get_name() << " and "; sstm << r_delta->get_plugin().get_name(); throw default_exception(sstm.str()); } store_fn(r_tgt, r_src, *r_delta, fn); } } else { if (!find_fn(r_tgt, r_src, fn)) { if (m_widen) { fn = r_src.get_manager().mk_widen_fn(r_tgt, r_src, 0); } else { fn = r_src.get_manager().mk_union_fn(r_tgt, r_src, 0); } if (!fn) { std::stringstream sstm; sstm << "trying to perform unsupported union operation on relations of kinds " << r_tgt.get_plugin().get_name() << " and " << r_src.get_plugin().get_name(); throw default_exception(sstm.str()); } store_fn(r_tgt, r_src, fn); } } SASSERT(r_src.get_signature().size() == r_tgt.get_signature().size()); TRACE("dl_verbose", r_tgt.display(tout <<"pre-union:");); (*fn)(r_tgt, r_src, r_delta); TRACE("dl_verbose", r_src.display(tout <<"src:"); r_tgt.display(tout <<"post-union:"); if (r_delta) { r_delta->display(tout <<"delta:"); }); if (r_delta && r_delta->fast_empty()) { ctx.make_empty(m_delta); } return true; } virtual void make_annotations(execution_context & ctx) { std::string str = "union"; if (!ctx.get_register_annotation(m_tgt, str)) { ctx.set_register_annotation(m_tgt, "union"); } if (m_delta != execution_context::void_register) { str = "delta of " + str; } ctx.set_register_annotation(m_delta, str); } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << (m_widen ? "widen " : "union ") << m_src << " into " << m_tgt; if (m_delta!=execution_context::void_register) { out << " with delta " << m_delta; } } }; instruction * instruction::mk_union(reg_idx src, reg_idx tgt, reg_idx delta) { return alloc(instr_union, src, tgt, delta, false); } instruction * instruction::mk_widen(reg_idx src, reg_idx tgt, reg_idx delta) { return alloc(instr_union, src, tgt, delta, true); } class instr_project_rename : public instruction { typedef unsigned_vector column_vector; bool m_projection; reg_idx m_src; column_vector m_cols; reg_idx m_tgt; public: instr_project_rename(bool projection, reg_idx src, unsigned col_cnt, const unsigned * cols, reg_idx tgt) : m_projection(projection), m_src(src), m_cols(col_cnt, cols), m_tgt(tgt) {} virtual bool perform(execution_context & ctx) { if (!ctx.reg(m_src)) { ctx.make_empty(m_tgt); return true; } log_verbose(ctx); ++ctx.m_stats.m_project_rename; relation_transformer_fn * fn; relation_base & r_src = *ctx.reg(m_src); if (!find_fn(r_src, fn)) { if (m_projection) { fn = r_src.get_manager().mk_project_fn(r_src, m_cols.size(), m_cols.c_ptr()); } else { fn = r_src.get_manager().mk_rename_fn(r_src, m_cols.size(), m_cols.c_ptr()); } if (!fn) { std::stringstream sstm; sstm << "trying to perform unsupported " << (m_projection ? "project" : "rename"); sstm << " operation on a relation of kind " << r_src.get_plugin().get_name(); throw default_exception(sstm.str()); } store_fn(r_src, fn); } ctx.set_reg(m_tgt, (*fn)(r_src)); return true; } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << (m_projection ? "project " : "rename ") << m_src << " into " << m_tgt; out << (m_projection ? " deleting columns " : " with cycle "); print_container(m_cols, out); } virtual void make_annotations(execution_context & ctx) { std::stringstream s; std::string a = "rel_src"; ctx.get_register_annotation(m_src, a); s << (m_projection ? "project " : "rename ") << a; ctx.set_register_annotation(m_tgt, s.str()); } }; instruction * instruction::mk_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols, reg_idx tgt) { return alloc(instr_project_rename, true, src, col_cnt, removed_cols, tgt); } instruction * instruction::mk_rename(reg_idx src, unsigned cycle_len, const unsigned * permutation_cycle, reg_idx tgt) { return alloc(instr_project_rename, false, src, cycle_len, permutation_cycle, tgt); } class instr_join_project : public instruction { typedef unsigned_vector column_vector; reg_idx m_rel1; reg_idx m_rel2; column_vector m_cols1; column_vector m_cols2; column_vector m_removed_cols; reg_idx m_res; public: instr_join_project(reg_idx rel1, reg_idx rel2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, reg_idx result) : m_rel1(rel1), m_rel2(rel2), m_cols1(joined_col_cnt, cols1), m_cols2(joined_col_cnt, cols2), m_removed_cols(removed_col_cnt, removed_cols), m_res(result) { } virtual bool perform(execution_context & ctx) { log_verbose(ctx); if (!ctx.reg(m_rel1) || !ctx.reg(m_rel2)) { ctx.make_empty(m_res); return true; } ++ctx.m_stats.m_join_project; relation_join_fn * fn; const relation_base & r1 = *ctx.reg(m_rel1); const relation_base & r2 = *ctx.reg(m_rel2); if (!find_fn(r1, r2, fn)) { fn = r1.get_manager().mk_join_project_fn(r1, r2, m_cols1, m_cols2, m_removed_cols); if (!fn) { throw default_exception("trying to perform unsupported join-project operation on relations of kinds %s and %s", r1.get_plugin().get_name().bare_str(), r2.get_plugin().get_name().bare_str()); } store_fn(r1, r2, fn); } TRACE("dl", tout<\n";); ctx.set_reg(m_res, (*fn)(r1, r2)); TRACE("dl", tout<get_size_estimate_rows()<<"\n";); if (ctx.reg(m_res)->fast_empty()) { ctx.make_empty(m_res); } return true; } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { relation_base const* r1 = ctx.reg(m_rel1); relation_base const* r2 = ctx.reg(m_rel2); out << "join_project " << m_rel1; if (r1) { out << ":" << r1->num_columns(); out << "-" << r1->get_size_estimate_rows(); } print_container(m_cols1, out); out << " and " << m_rel2; if (r2) { out << ":" << r2->num_columns(); out << "-" << r2->get_size_estimate_rows(); } print_container(m_cols2, out); out << " into " << m_res << " removing columns "; print_container(m_removed_cols, out); } virtual void make_annotations(execution_context & ctx) { std::string s1 = "rel1", s2 = "rel2"; ctx.get_register_annotation(m_rel1, s1); ctx.get_register_annotation(m_rel2, s2); ctx.set_register_annotation(m_res, "join project " + s1 + " " + s2); } }; instruction * instruction::mk_join_project(reg_idx rel1, reg_idx rel2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, reg_idx result) { return alloc(instr_join_project, rel1, rel2, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols, result); } class instr_min : public instruction { reg_idx m_source_reg; reg_idx m_target_reg; unsigned_vector m_group_by_cols; unsigned m_min_col; public: instr_min(reg_idx source_reg, reg_idx target_reg, const unsigned_vector & group_by_cols, unsigned min_col) : m_source_reg(source_reg), m_target_reg(target_reg), m_group_by_cols(group_by_cols), m_min_col(min_col) { } virtual bool perform(execution_context & ctx) { log_verbose(ctx); if (!ctx.reg(m_source_reg)) { ctx.make_empty(m_target_reg); return true; } const relation_base & s = *ctx.reg(m_source_reg); if (!s.from_table()) { throw default_exception("relation is not a table %s", s.get_plugin().get_name().bare_str()); } ++ctx.m_stats.m_min; const table_relation & tr = static_cast(s); const table_base & source_t = tr.get_table(); relation_manager & r_manager = s.get_manager(); const relation_signature & r_sig = s.get_signature(); scoped_ptr fn = r_manager.mk_min_fn(source_t, m_group_by_cols, m_min_col); table_base * target_t = (*fn)(source_t); TRACE("dl", tout << "% "; target_t->display(tout); tout << "\n";); relation_base * target_r = r_manager.mk_table_relation(r_sig, target_t); ctx.set_reg(m_target_reg, target_r); return true; } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << " MIN AGGR "; } virtual void make_annotations(execution_context & ctx) { } }; instruction * instruction::mk_min(reg_idx source, reg_idx target, const unsigned_vector & group_by_cols, const unsigned min_col) { return alloc(instr_min, source, target, group_by_cols, min_col); } class instr_select_equal_and_project : public instruction { reg_idx m_src; reg_idx m_result; app_ref m_value; unsigned m_col; public: instr_select_equal_and_project(ast_manager & m, reg_idx src, const relation_element & value, unsigned col, reg_idx result) : m_src(src), m_result(result), m_value(value, m), m_col(col) { // [Leo]: does not compile on gcc // TRACE("dl", tout << "src:" << m_src << " result: " << m_result << " value:" << m_value << " column:" << m_col << "\n";); } virtual bool perform(execution_context & ctx) { if (!ctx.reg(m_src)) { ctx.make_empty(m_result); return true; } log_verbose(ctx); ++ctx.m_stats.m_select_equal_project; relation_transformer_fn * fn; relation_base & r = *ctx.reg(m_src); if (!find_fn(r, fn)) { fn = r.get_manager().mk_select_equal_and_project_fn(r, m_value, m_col); if (!fn) { throw default_exception( "trying to perform unsupported select_equal_and_project operation on a relation of kind %s", r.get_plugin().get_name().bare_str()); } store_fn(r, fn); } ctx.set_reg(m_result, (*fn)(r)); if (ctx.reg(m_result)->fast_empty()) { ctx.make_empty(m_result); } return true; } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "select_equal_and_project " << m_src <<" into " << m_result << " col: " << m_col << " val: " << ctx.get_rel_context().get_rmanager().to_nice_string(m_value); } virtual void make_annotations(execution_context & ctx) { std::stringstream s; std::string s1 = "src"; ctx.get_register_annotation(m_src, s1); s << "select equal project col " << m_col << " val: " << ctx.get_rel_context().get_rmanager().to_nice_string(m_value) << " " << s1; ctx.set_register_annotation(m_result, s.str()); } }; instruction * instruction::mk_select_equal_and_project(ast_manager & m, reg_idx src, const relation_element & value, unsigned col, reg_idx result) { return alloc(instr_select_equal_and_project, m, src, value, col, result); } class instr_filter_by_negation : public instruction { typedef unsigned_vector column_vector; reg_idx m_tgt; reg_idx m_neg_rel; column_vector m_cols1; column_vector m_cols2; public: instr_filter_by_negation(reg_idx tgt, reg_idx neg_rel, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : m_tgt(tgt), m_neg_rel(neg_rel), m_cols1(col_cnt, cols1), m_cols2(col_cnt, cols2) {} virtual bool perform(execution_context & ctx) { log_verbose(ctx); if (!ctx.reg(m_tgt) || !ctx.reg(m_neg_rel)) { return true; } ++ctx.m_stats.m_filter_by_negation; relation_intersection_filter_fn * fn; relation_base & r1 = *ctx.reg(m_tgt); const relation_base & r2 = *ctx.reg(m_neg_rel); if (!find_fn(r1, r2, fn)) { fn = r1.get_manager().mk_filter_by_negation_fn(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); if (!fn) { std::stringstream sstm; sstm << "trying to perform unsupported filter_by_negation on relations of kinds "; sstm << r1.get_plugin().get_name() << " and " << r2.get_plugin().get_name(); throw default_exception(sstm.str()); } store_fn(r1, r2, fn); } (*fn)(r1, r2); if (r1.fast_empty()) { ctx.make_empty(m_tgt); } return true; } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "filter_by_negation on " << m_tgt; print_container(m_cols1, out); out << " with " << m_neg_rel; print_container(m_cols2, out); out << " as the negated table"; } virtual void make_annotations(execution_context & ctx) { std::string s = "negated relation"; ctx.get_register_annotation(m_neg_rel, s); ctx.set_register_annotation(m_tgt, "filter by negation " + s); } }; instruction * instruction::mk_filter_by_negation(reg_idx tgt, reg_idx neg_rel, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { return alloc(instr_filter_by_negation, tgt, neg_rel, col_cnt, cols1, cols2); } class instr_mk_unary_singleton : public instruction { relation_signature m_sig; func_decl* m_pred; reg_idx m_tgt; relation_fact m_fact; public: instr_mk_unary_singleton(ast_manager & m, func_decl* head_pred, const relation_sort & s, const relation_element & val, reg_idx tgt) : m_pred(head_pred), m_tgt(tgt), m_fact(m) { m_sig.push_back(s); m_fact.push_back(val); } virtual bool perform(execution_context & ctx) { log_verbose(ctx); ++ctx.m_stats.m_unary_singleton; relation_base * rel = ctx.get_rel_context().get_rmanager().mk_empty_relation(m_sig, m_pred); rel->add_fact(m_fact); ctx.set_reg(m_tgt, rel); return true; } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "mk_unary_singleton into " << m_tgt << " sort:" << ctx.get_rel_context().get_rmanager().to_nice_string(m_sig[0]) << " val:" << ctx.get_rel_context().get_rmanager().to_nice_string(m_sig[0], m_fact[0]); } virtual void make_annotations(execution_context & ctx) { std::string s; if (!ctx.get_register_annotation(m_tgt, s)) { ctx.set_register_annotation(m_tgt, "mk unary singleton"); } } }; instruction * instruction::mk_unary_singleton(ast_manager & m, func_decl* head_pred, const relation_sort & s, const relation_element & val, reg_idx tgt) { return alloc(instr_mk_unary_singleton, m, head_pred, s, val, tgt); } class instr_mk_total : public instruction { relation_signature m_sig; func_decl* m_pred; reg_idx m_tgt; public: instr_mk_total(const relation_signature & sig, func_decl* p, reg_idx tgt) : m_sig(sig), m_pred(p), m_tgt(tgt) {} virtual bool perform(execution_context & ctx) { log_verbose(ctx); ++ctx.m_stats.m_total; ctx.set_reg(m_tgt, ctx.get_rel_context().get_rmanager().mk_full_relation(m_sig, m_pred)); return true; } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "mk_total into " << m_tgt << " sort:" << ctx.get_rel_context().get_rmanager().to_nice_string(m_sig); } virtual void make_annotations(execution_context & ctx) { std::string s; if (!ctx.get_register_annotation(m_tgt, s)) { ctx.set_register_annotation(m_tgt, "mk_total"); } } }; instruction * instruction::mk_total(const relation_signature & sig, func_decl* pred, reg_idx tgt) { return alloc(instr_mk_total, sig, pred, tgt); } class instr_mark_saturated : public instruction { func_decl_ref m_pred; public: instr_mark_saturated(ast_manager & m, func_decl * pred) : m_pred(pred, m) {} virtual bool perform(execution_context & ctx) { log_verbose(ctx); ctx.get_rel_context().get_rmanager().mark_saturated(m_pred); return true; } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "mark_saturated " << m_pred->get_name().bare_str(); } virtual void make_annotations(execution_context & ctx) { } }; instruction * instruction::mk_mark_saturated(ast_manager & m, func_decl * pred) { return alloc(instr_mark_saturated, m, pred); } class instr_assert_signature : public instruction { relation_signature m_sig; reg_idx m_tgt; public: instr_assert_signature(const relation_signature & s, reg_idx tgt) : m_sig(s), m_tgt(tgt) {} virtual bool perform(execution_context & ctx) { log_verbose(ctx); if (ctx.reg(m_tgt)) { SASSERT(ctx.reg(m_tgt)->get_signature()==m_sig); } return true; } virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "instr_assert_signature of " << m_tgt << " signature:"; print_container(m_sig, out); } virtual void make_annotations(execution_context & ctx) { std::string s; if (!ctx.get_register_annotation(m_tgt, s)) { ctx.set_register_annotation(m_tgt, "assert signature"); } } }; instruction * instruction::mk_assert_signature(const relation_signature & s, reg_idx tgt) { return alloc(instr_assert_signature, s, tgt); } // ----------------------------------- // // instruction_block // // ----------------------------------- instruction_block::~instruction_block() { reset(); } void instruction_block::reset() { instr_seq_type::iterator it = m_data.begin(); instr_seq_type::iterator end = m_data.end(); for(; it!=end; ++it) { dealloc(*it); } m_data.reset(); m_observer = 0; } bool instruction_block::perform(execution_context & ctx) const { cost_recorder crec; instr_seq_type::const_iterator it = m_data.begin(); instr_seq_type::const_iterator end = m_data.end(); bool success = true; for(; it!=end && success; ++it) { instruction * instr=(*it); crec.start(instr); //finish is performed by the next start() or by the destructor of crec TRACE("dl", tout <<"% "; instr->display_head_impl(ctx, tout); tout <<"\n";); success = !ctx.should_terminate() && instr->perform(ctx); } return success; } void instruction_block::process_all_costs() { instr_seq_type::iterator it = m_data.begin(); instr_seq_type::iterator end = m_data.end(); for(; it!=end; ++it) { (*it)->process_all_costs(); } } void instruction_block::collect_statistics(statistics& st) const { instr_seq_type::const_iterator it = m_data.begin(); instr_seq_type::const_iterator end = m_data.end(); for(; it!=end; ++it) { (*it)->collect_statistics(st); } } void instruction_block::make_annotations(execution_context & ctx) { instr_seq_type::iterator it = m_data.begin(); instr_seq_type::iterator end = m_data.end(); for(; it!=end; ++it) { (*it)->make_annotations(ctx); } } void instruction_block::display_indented(execution_context const& _ctx, std::ostream & out, std::string indentation) const { rel_context const& ctx = _ctx.get_rel_context(); instr_seq_type::const_iterator it = m_data.begin(); instr_seq_type::const_iterator end = m_data.end(); for(; it!=end; ++it) { instruction * i = (*it); if (i->passes_output_thresholds(ctx.get_context()) || i->being_recorded()) { i->display_indented(_ctx, out, indentation); } } } } z3-4.4.0/src/muz/rel/dl_table_relation.h0000644000175000017500000001263112540347414017650 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_table_relation.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-24. Revision History: --*/ #ifndef _DL_TABLE_RELATION_H_ #define _DL_TABLE_RELATION_H_ #include "dl_base.h" #include "dl_util.h" namespace datalog { class table_relation; class table_relation_plugin : public relation_plugin { friend class table_relation; class tr_join_project_fn; class tr_transformer_fn; class universal_target_union_fn; class tr_union_fn; class tr_mutator_fn; class tr_intersection_filter_fn; table_plugin & m_table_plugin; static symbol create_plugin_name(const table_plugin & p); public: table_relation_plugin(table_plugin & tp, relation_manager & manager) : relation_plugin(create_plugin_name(tp), manager, ST_TABLE_RELATION), m_table_plugin(tp) {} table_plugin & get_table_plugin() { return m_table_plugin; } virtual bool can_handle_signature(const relation_signature & s); virtual relation_base * mk_empty(const relation_signature & s); virtual relation_base * mk_full(const relation_signature & s, func_decl* p, family_id kind); relation_base * mk_from_table(const relation_signature & s, table_base * t); protected: virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); virtual relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols); virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols); virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); virtual relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, const unsigned * permutation); virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols); virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col); virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); virtual relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); virtual relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & t, const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols); virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, const relation_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols); virtual relation_transformer_fn * mk_select_equal_and_project_fn(const relation_base & t, const relation_element & value, unsigned col); }; class table_relation : public relation_base { friend class table_relation_plugin; friend class table_relation_plugin::tr_join_project_fn; friend class table_relation_plugin::tr_transformer_fn; scoped_rel m_table; /** \brief Create a \c table_relation object. The newly created object takes ownership of the \c table object. */ table_relation(table_relation_plugin & p, const relation_signature & s, table_base * table) : relation_base(p, s), m_table(table) { SASSERT(s.size()==table->get_signature().size()); } public: table_relation_plugin & get_plugin() const { return static_cast(relation_base::get_plugin()); } table_base & get_table() { return *m_table; } const table_base & get_table() const { return *m_table; } virtual bool empty() const { return m_table->empty(); } void add_table_fact(const table_fact & f); virtual void add_fact(const relation_fact & f); virtual bool contains_fact(const relation_fact & f) const; virtual relation_base * clone() const; virtual relation_base * complement(func_decl* p) const; virtual void to_formula(expr_ref& fml) const { get_table().to_formula(get_signature(), fml); } virtual void display(std::ostream & out) const { get_table().display(out); } virtual void display_tuples(func_decl & pred, std::ostream & out) const; virtual unsigned get_size_estimate_rows() const { return m_table->get_size_estimate_rows(); } virtual unsigned get_size_estimate_bytes() const { return m_table->get_size_estimate_bytes(); } virtual bool knows_exact_size() const { return m_table->knows_exact_size(); } }; }; #endif /* _DL_TABLE_RELATION_H_ */ z3-4.4.0/src/muz/rel/dl_external_relation.cpp0000644000175000017500000004414312540347414020741 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_external_relation.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2010-05-10 Revision History: --*/ #include "debug.h" #include "ast_pp.h" #include "dl_context.h" #include "dl_external_relation.h" #include "dl_decl_plugin.h" namespace datalog { external_relation::external_relation(external_relation_plugin & p, const relation_signature & s, expr* r) : relation_base(p, s), m_rel(r, p.get_ast_manager()), m_select_fn(p.get_ast_manager()), m_store_fn(p.get_ast_manager()), m_is_empty_fn(p.get_ast_manager()) { } external_relation::~external_relation() { } void external_relation::mk_accessor(decl_kind k, func_decl_ref& fn, const relation_fact& f, bool destructive, expr_ref& res) const { ast_manager& m = m_rel.get_manager(); family_id fid = get_plugin().get_family_id(); ptr_vector args; args.push_back(m_rel); for (unsigned i = 0; i < f.size(); ++i) { args.push_back(f[i]); } if (!fn.get()) { fn = m.mk_func_decl(fid, k, 0, 0, args.size(), args.c_ptr()); } if (destructive) { get_plugin().reduce_assign(fn, args.size(), args.c_ptr(), 1, args.c_ptr()); res = m_rel; } else { get_plugin().reduce(fn, args.size(), args.c_ptr(), res); } } bool external_relation::empty() const { ast_manager& m = m_rel.get_manager(); expr* r = m_rel.get(); expr_ref res(m); if (!m_is_empty_fn.get()) { family_id fid = get_plugin().get_family_id(); const_cast(m_is_empty_fn) = m.mk_func_decl(fid, OP_RA_IS_EMPTY, 0, 0, 1, &r); } get_plugin().reduce(m_is_empty_fn, 1, &r, res); return m.is_true(res); } void external_relation::add_fact(const relation_fact & f) { mk_accessor(OP_RA_STORE, m_store_fn, f, true, m_rel); } bool external_relation::contains_fact(const relation_fact & f) const { ast_manager& m = get_plugin().get_ast_manager(); expr_ref res(m); mk_accessor(OP_RA_SELECT, const_cast(m_select_fn), f, false, res); return !m.is_false(res); } external_relation * external_relation::clone() const { ast_manager& m = m_rel.get_manager(); family_id fid = get_plugin().get_family_id(); expr* rel = m_rel.get(); expr_ref res(m.mk_fresh_const("T", m.get_sort(rel)), m); expr* rel_out = res.get(); func_decl_ref fn(m.mk_func_decl(fid, OP_RA_CLONE,0,0, 1, &rel), m); get_plugin().reduce_assign(fn, 1, &rel, 1, &rel_out); return alloc(external_relation, get_plugin(), get_signature(), res); } external_relation * external_relation::complement(func_decl* p) const { ast_manager& m = m_rel.get_manager(); family_id fid = get_plugin().get_family_id(); expr_ref res(m); expr* rel = m_rel; func_decl_ref fn(m.mk_func_decl(fid, OP_RA_COMPLEMENT,0,0, 1, &rel), m); get_plugin().reduce(fn, 1, &rel, res); return alloc(external_relation, get_plugin(), get_signature(), res); } void external_relation::display(std::ostream & out) const { out << mk_pp(m_rel, m_rel.get_manager()) << "\n"; } void external_relation::display_tuples(func_decl & pred, std::ostream & out) const { display(out); } external_relation_plugin & external_relation::get_plugin() const { return static_cast(relation_base::get_plugin()); } // ----------------------------------- // // external_relation_plugin // // ----------------------------------- external_relation_plugin::external_relation_plugin(external_relation_context& ctx, relation_manager & m) : relation_plugin(external_relation_plugin::get_name(), m), m_ext(ctx) {} external_relation const & external_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); } external_relation & external_relation_plugin::get(relation_base & r) { return dynamic_cast(r); } relation_base * external_relation_plugin::mk_empty(const relation_signature & s) { ast_manager& m = get_ast_manager(); sort* r_sort = get_relation_sort(s); parameter param(r_sort); family_id fid = get_family_id(); expr_ref e(m.mk_fresh_const("T", r_sort), m); expr* args[1] = { e.get() }; func_decl_ref empty_decl(m.mk_func_decl(fid, OP_RA_EMPTY, 1, ¶m, 0, (sort*const*)0), m); reduce_assign(empty_decl, 0, 0, 1, args); return alloc(external_relation, *this, s, e); } sort* external_relation_plugin::get_relation_sort(relation_signature const& sig) { vector sorts; ast_manager& m = get_ast_manager(); family_id fid = get_family_id(); for (unsigned i = 0; i < sig.size(); ++i) { sorts.push_back(parameter(sig[i])); } return m.mk_sort(fid, DL_RELATION_SORT, sorts.size(), sorts.c_ptr()); } sort* external_relation_plugin::get_column_sort(unsigned col, sort* s) { SASSERT(s->get_num_parameters() > col); SASSERT(s->get_parameter(col).is_ast()); SASSERT(is_sort(s->get_parameter(col).get_ast())); return to_sort(s->get_parameter(col).get_ast()); } family_id external_relation_plugin::get_family_id() { return m_ext.get_family_id(); } void external_relation_plugin::mk_filter_fn(sort* s, app* condition, func_decl_ref& f) { ast_manager& m = get_ast_manager(); family_id fid = get_family_id(); parameter param(condition); f = m.mk_func_decl(fid, OP_RA_FILTER, 1, ¶m, 1, &s); } class external_relation_plugin::join_fn : public convenient_relation_join_fn { external_relation_plugin& m_plugin; func_decl_ref m_join_fn; expr* m_args[2]; public: join_fn(external_relation_plugin& p, const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2), m_plugin(p), m_join_fn(p.get_ast_manager()) { ast_manager& m = p.get_ast_manager(); family_id fid = p.get_family_id(); vector params; for (unsigned i = 0; i < col_cnt; ++i) { params.push_back(parameter(cols1[i])); params.push_back(parameter(cols2[i])); } sort* domain[2] = { p.get_relation_sort(o1_sig), p.get_relation_sort(o2_sig) }; m_join_fn = m.mk_func_decl(fid, OP_RA_JOIN, params.size(), params.c_ptr(), 2, domain); } virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { expr_ref res(m_plugin.get_ast_manager()); m_args[0] = get(r1).get_relation(); m_args[1] = get(r2).get_relation(); m_plugin.reduce(m_join_fn, 2, m_args, res); return alloc(external_relation, m_plugin, get_result_signature(), res); } }; relation_join_fn * external_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(r1) || !check_kind(r2)) { return 0; } return alloc(join_fn, *this, r1.get_signature(), r2.get_signature() , col_cnt, cols1, cols2); } class external_relation_plugin::project_fn : public convenient_relation_project_fn { external_relation_plugin& m_plugin; func_decl_ref m_project_fn; public: project_fn(external_relation_plugin& p, sort* relation_sort, const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols), m_plugin(p), m_project_fn(p.get_ast_manager()) { vector params; ast_manager& m = p.get_ast_manager(); family_id fid = p.get_family_id(); for (unsigned i = 0; i < removed_col_cnt; ++i) { params.push_back(parameter(removed_cols[i])); } m_project_fn = m.mk_func_decl(fid, OP_RA_PROJECT, params.size(), params.c_ptr(), 1, &relation_sort); } virtual relation_base * operator()(const relation_base & r) { expr_ref res(m_plugin.get_ast_manager()); expr* rel = get(r).get_relation(); m_plugin.reduce(m_project_fn, 1, &rel, res); return alloc(external_relation, m_plugin, get_result_signature(), to_app(res)); } }; relation_transformer_fn * external_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt, const unsigned * removed_cols) { return alloc(project_fn, *this, get(r).get_sort(), r.get_signature(), col_cnt, removed_cols); } class external_relation_plugin::rename_fn : public convenient_relation_rename_fn { external_relation_plugin& m_plugin; func_decl_ref m_rename_fn; expr* m_args[2]; public: rename_fn(external_relation_plugin& p, sort* relation_sort, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) : convenient_relation_rename_fn(orig_sig, cycle_len, cycle), m_plugin(p), m_rename_fn(p.get_ast_manager()) { ast_manager& m = p.get_ast_manager(); family_id fid = p.get_family_id(); vector params; for (unsigned i = 0; i < cycle_len; ++i) { SASSERT(cycle[i] < orig_sig.size()); params.push_back(parameter(cycle[i])); } m_rename_fn = m.mk_func_decl(fid, OP_RA_RENAME, params.size(), params.c_ptr(), 1, &relation_sort); } virtual relation_base * operator()(const relation_base & r) { expr* rel = get(r).get_relation(); expr_ref res(m_plugin.get_ast_manager()); m_args[0] = rel; m_plugin.reduce(m_rename_fn, 1, &rel, res); return alloc(external_relation, m_plugin, get_result_signature(), res); } }; relation_transformer_fn * external_relation_plugin::mk_rename_fn(const relation_base & r, unsigned cycle_len, const unsigned * permutation_cycle) { if(!check_kind(r)) { return 0; } return alloc(rename_fn, *this, get(r).get_sort(), r.get_signature(), cycle_len, permutation_cycle); } class external_relation_plugin::union_fn : public relation_union_fn { external_relation_plugin& m_plugin; func_decl_ref m_union_fn; expr* m_args[2]; expr* m_outs[2]; public: union_fn(external_relation_plugin& p, decl_kind k, sort* relation_sort): m_plugin(p), m_union_fn(p.get_ast_manager()) { ast_manager& m = p.get_ast_manager(); sort* domain[2] = { relation_sort, relation_sort }; m_union_fn = m.mk_func_decl(p.get_family_id(), k, 0, 0, 2, domain); } virtual void operator()(relation_base & r, const relation_base & src, relation_base * delta) { ast_manager& m = m_plugin.get_ast_manager(); expr_ref_vector res(m); m_args[0] = get(r).get_relation(); m_args[1] = get(src).get_relation(); m_outs[0] = m_args[0]; unsigned num_out = 1; if (delta) { m_outs[1] = get(*delta).get_relation(); ++num_out; } m_plugin.reduce_assign(m_union_fn, 2, m_args, num_out, m_outs); } }; relation_union_fn * external_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return 0; } return alloc(union_fn, *this, OP_RA_UNION, get(src).get_sort()); } relation_union_fn * external_relation_plugin::mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return 0; } return alloc(union_fn, *this, OP_RA_WIDEN, get(src).get_sort()); } class external_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { external_relation_plugin& m_plugin; app_ref m_condition; func_decl_ref m_filter_fn; public: filter_interpreted_fn(external_relation_plugin& p, sort* relation_sort, app * condition) : m_plugin(p), m_condition(condition, p.get_ast_manager()), m_filter_fn(p.get_ast_manager()) { ast_manager& m = p.get_ast_manager(); p.mk_filter_fn(relation_sort, condition, m_filter_fn); SASSERT(m.is_bool(condition)); } virtual void operator()(relation_base & r) { SASSERT(m_plugin.check_kind(r)); expr* arg = get(r).get_relation(); m_plugin.reduce_assign(m_filter_fn, 1, &arg, 1, &arg); } }; relation_mutator_fn * external_relation_plugin::mk_filter_interpreted_fn(const relation_base & r, app * condition) { if(!check_kind(r)) { return 0; } return alloc(filter_interpreted_fn, *this, get(r).get_sort(), condition); } relation_mutator_fn * external_relation_plugin::mk_filter_equal_fn(const relation_base & r, const relation_element & value, unsigned col) { if(!check_kind(r)) { return 0; } ast_manager& m = get_ast_manager(); app_ref condition(m); expr_ref var(m); sort* relation_sort = get(r).get_sort(); sort* column_sort = get_column_sort(col, relation_sort); var = m.mk_var(col, column_sort); condition = m.mk_eq(var, value); return mk_filter_interpreted_fn(r, condition); } class external_relation_plugin::filter_identical_fn : public relation_mutator_fn { external_relation_plugin& m_plugin; func_decl_ref_vector m_filter_fn; public: filter_identical_fn(external_relation_plugin& p, sort* relation_sort, unsigned col_cnt, const unsigned * identical_cols) : m_plugin(p), m_filter_fn(p.get_ast_manager()) { ast_manager& m = p.get_ast_manager(); func_decl_ref fn(m); app_ref eq(m); if (col_cnt <= 1) { return; } unsigned col = identical_cols[0]; sort* s = p.get_column_sort(col, relation_sort); var* v0 = m.mk_var(col, s); for (unsigned i = 1; i < col_cnt; ++i) { col = identical_cols[i]; s = p.get_column_sort(col, relation_sort); eq = m.mk_eq(v0, m.mk_var(col, s)); p.mk_filter_fn(relation_sort, eq.get(), fn); m_filter_fn.push_back(fn); } } virtual void operator()(relation_base & r) { expr* r0 = get(r).get_relation(); for (unsigned i = 0; i < m_filter_fn.size(); ++i) { m_plugin.reduce_assign(m_filter_fn[i].get(), 1, &r0, 1, &r0); } } }; relation_mutator_fn * external_relation_plugin::mk_filter_identical_fn(const relation_base & r, unsigned col_cnt, const unsigned * identical_cols) { if (!check_kind(r)) { return 0; } return alloc(filter_identical_fn, *this, get(r).get_sort(), col_cnt, identical_cols); } class external_relation_plugin::negation_filter_fn : public convenient_relation_negation_filter_fn { external_relation_plugin& m_plugin; func_decl_ref m_negated_filter_fn; expr* m_args[2]; public: negation_filter_fn(external_relation_plugin& p, const relation_base & tgt, const relation_base & neg_t, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) : convenient_negation_filter_fn(tgt, neg_t, joined_col_cnt, t_cols, negated_cols), m_plugin(p), m_negated_filter_fn(p.get_ast_manager()) { ast_manager& m = p.get_ast_manager(); family_id fid = p.get_family_id(); vector params; for (unsigned i = 0; i < joined_col_cnt; ++i) { params.push_back(parameter(t_cols[i])); params.push_back(parameter(negated_cols[i])); } sort* domain[2] = { get(tgt).get_sort(), get(neg_t).get_sort() }; m_negated_filter_fn = m.mk_func_decl(fid, OP_RA_NEGATION_FILTER, params.size(), params.c_ptr(), 2, domain); } void operator()(relation_base & t, const relation_base & negated_obj) { m_args[0] = get(t).get_relation(); m_args[1] = get(negated_obj).get_relation(); m_plugin.reduce_assign(m_negated_filter_fn.get(), 2, m_args, 1, m_args); } }; relation_intersection_filter_fn * external_relation_plugin::mk_filter_by_negation_fn(const relation_base & t, const relation_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { if (!check_kind(t) || !check_kind(negated_obj)) { return 0; } return alloc(negation_filter_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols); } }; z3-4.4.0/src/muz/rel/dl_product_relation.cpp0000644000175000017500000013300212540347414020570 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_product_relation.cpp Abstract: A Relation combinator. Author: Nikolaj Bjorner (nbjorner) 2010-4-11 Revision History: Notes: join = more refined version lets augment the product relation as a consequence of join. join Q = join = u = more refined version: < (R u R') n (R u S') n (R' u S), (S u S') n (S u R') n (S' u R)> proj = < proj R, proj S> & phi = attach S to [R & phi] whenever R & phi can propagate to S [rename] = --*/ #include "dl_sieve_relation.h" #include "dl_table_relation.h" #include "dl_product_relation.h" #include "bool_rewriter.h" #include "ast_pp.h" namespace datalog { // ----------------------------------- // // product_relation_plugin // // ----------------------------------- product_relation_plugin & product_relation_plugin::get_plugin(relation_manager & rmgr) { product_relation_plugin * res = static_cast(rmgr.get_relation_plugin(get_name())); if(!res) { res = alloc(product_relation_plugin, rmgr); rmgr.register_plugin(res); } return *res; } product_relation_plugin::product_relation_plugin(relation_manager& m): relation_plugin(product_relation_plugin::get_name(), m, ST_PRODUCT_RELATION), m_spec_store(*this) { } void product_relation_plugin::initialize(family_id fid) { relation_plugin::initialize(fid); m_spec_store.add_available_kind(get_kind()); } family_id product_relation_plugin::get_relation_kind(const relation_signature & sig, const rel_spec & spec) { SASSERT(spec.well_formed()); return m_spec_store.get_relation_kind(sig, spec); } family_id product_relation_plugin::get_relation_kind(const product_relation & r) { return get_relation_kind(r.get_signature(), r.m_spec); } bool product_relation_plugin::can_handle_signature(const relation_signature & s) { return m_spec_store.contains_signature(s); } bool product_relation_plugin::can_handle_signature(const relation_signature & s, family_id k) { return true; } product_relation& product_relation_plugin::get(relation_base& r) { return dynamic_cast(r); } product_relation const & product_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); } product_relation* product_relation_plugin::get(relation_base* r) { return dynamic_cast(r); } product_relation const* product_relation_plugin::get(relation_base const* r) { return dynamic_cast(r); } bool product_relation_plugin::is_product_relation(relation_base const& r) { return r.get_plugin().get_name() == product_relation_plugin::get_name(); } bool product_relation_plugin::are_aligned(const product_relation& r1, const product_relation& r2) { unsigned sz = r1.size(); if(sz!=r2.size()) { return false; } for(unsigned i=0; i & rels, rel_spec & res) { vector specs; ptr_vector::const_iterator rit = rels.begin(); ptr_vector::const_iterator rend = rels.end(); for(; rit!=rend; ++rit) { specs.push_back((*rit)->m_spec); SASSERT(specs.back().well_formed()); std::sort(specs.back().begin(), specs.back().end()); } vector::iterator sit = specs.begin(), send = specs.end(); res.reset(); for(;;) { family_id next = -1; sit = specs.begin(); for(; sit!=send; ++sit) { rel_spec & s = *sit; if(!s.empty() && s.back()>next) { next = s.back(); } } if(next==-1) { //we're done break; } res.push_back(next); sit = specs.begin(); for(; sit!=send; ++sit) { rel_spec & s = *sit; while (!s.empty() && s.back()==next) { s.pop_back(); } } } } relation_base * product_relation_plugin::mk_empty(const relation_signature & s) { return alloc(product_relation,*this, s); } relation_base * product_relation_plugin::mk_empty(const relation_signature & s, family_id kind) { rel_spec spec; m_spec_store.get_relation_spec(s, kind, spec); relation_vector inner_rels; unsigned rel_cnt = spec.size(); for(unsigned i=0; im_default_empty = false; return result; } rel_spec spec; m_spec_store.get_relation_spec(s, kind, spec); SASSERT(spec.well_formed()); relation_vector inner_rels; unsigned rel_cnt = spec.size(); for(unsigned i=0; i m_joins; ptr_vector m_full; unsigned_vector m_offset1; svector m_kind1; unsigned_vector m_offset2; svector m_kind2; const relation_base & get_nonsieve_relation(const relation_base & r) { relation_plugin & rp = r.get_plugin(); if(rp.is_sieve_relation()) { return static_cast(r).get_inner(); } else { return r; } } relation_plugin & get_nonsieve_plugin(const relation_base & r) { return get_nonsieve_relation(r).get_plugin(); } family_id get_nonsieve_kind(const relation_base & r) { return get_nonsieve_relation(r).get_kind(); } /** A tableish relatio is either a table_relation or a sieve_relation with a table_relation inside. */ bool is_tableish_relation(const relation_base & r) { return get_nonsieve_plugin(r).from_table(); } relation_base * get_full_tableish_relation(const relation_signature & sig, func_decl* p, family_id kind) { relation_manager& rmgr = m_plugin.get_manager(); table_signature tsig; if(rmgr.relation_signature_to_table(sig, tsig)) { return rmgr.mk_table_relation(sig, rmgr.get_appropriate_plugin(tsig).mk_full(p, tsig, kind)); } unsigned sz = sig.size(); tsig.reset(); for(unsigned i=0; i relations; unsigned sz = m_joins.size(); relation_base* result = 0; for (unsigned i = 0; i < sz; ++i) { relation_base const& r1 = (m_kind1[i] == T_FULL)?(*m_full[m_offset1[i]]):access(m_offset1[i], _r1); relation_base const& r2 = (m_kind2[i] == T_FULL)?(*m_full[m_offset2[i]]):access(m_offset2[i], _r2); relations.push_back((*m_joins[i])(r1, r2)); } result = alloc(product_relation, m_plugin, get_result_signature(), sz, relations.c_ptr()); TRACE("dl",result->display(tout);); return result; } }; relation_join_fn * product_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (is_product_relation(r1) && is_product_relation(r2)) { return alloc(join_fn, *this, get(r1), get(r2), col_cnt, cols1, cols2); } if (is_product_relation(r1)) { return alloc(join_fn, *this, get(r1), r2, col_cnt, cols1, cols2); } if (is_product_relation(r2)) { return alloc(join_fn, *this, r1, get(r2), col_cnt, cols1, cols2); } if (r1.get_kind() != r2.get_kind()) { return alloc(join_fn, *this, r1, r2, col_cnt, cols1, cols2); } return 0; } class product_relation_plugin::transform_fn : public relation_transformer_fn { relation_signature m_sig; ptr_vector m_transforms; public: transform_fn(relation_signature s, unsigned num_trans, relation_transformer_fn** trans): m_sig(s), m_transforms(num_trans, trans) {} ~transform_fn() { dealloc_ptr_vector_content(m_transforms); } virtual relation_base * operator()(const relation_base & _r) { product_relation const& r = get(_r); product_relation_plugin& p = r.get_plugin(); SASSERT(m_transforms.size() == r.size()); ptr_vector relations; for (unsigned i = 0; i < r.size(); ++i) { relations.push_back((*m_transforms[i])(r[i])); } relation_base* result = alloc(product_relation, p, m_sig, relations.size(), relations.c_ptr()); TRACE("dl", _r.display(tout); result->display(tout);); return result; } }; relation_transformer_fn * product_relation_plugin::mk_project_fn(const relation_base & _r, unsigned col_cnt, const unsigned * removed_cols) { if (is_product_relation(_r)) { product_relation const& r = get(_r); ptr_vector projs; for (unsigned i = 0; i < r.size(); ++i) { projs.push_back(get_manager().mk_project_fn(r[i], col_cnt, removed_cols)); } relation_signature s; relation_signature::from_project(r.get_signature(), col_cnt, removed_cols, s); return alloc(transform_fn, s, projs.size(), projs.c_ptr()); } return 0; } relation_transformer_fn * product_relation_plugin::mk_rename_fn(const relation_base & _r, unsigned cycle_len, const unsigned * permutation_cycle) { if(is_product_relation(_r)) { ptr_vector trans; product_relation const& r = get(_r); for (unsigned i = 0; i < r.size(); ++i) { trans.push_back(get_manager().mk_rename_fn(r[i], cycle_len, permutation_cycle)); } relation_signature s; relation_signature::from_rename(r.get_signature(), cycle_len, permutation_cycle, s); return alloc(transform_fn, s, trans.size(), trans.c_ptr()); } return 0; } class product_relation_plugin::aligned_union_fn : public relation_union_fn { relation_manager & m_rmgr; product_relation_plugin& m_plugin; bool m_is_widen; //m_union[i][j] is union between i-th and j-th relation. //It can be zero which means that particular union should be skipped. vector > m_unions; void mk_union_fn(unsigned i, unsigned j, relation_base const& r1, relation_base const& r2, const relation_base* delta) { relation_manager& rmgr = r1.get_manager(); relation_union_fn* u = 0; if (m_is_widen) { u = rmgr.mk_widen_fn(r1, r2, delta); } else { u = rmgr.mk_union_fn(r1, r2, delta); } TRACE("dl_verbose", tout << r1.get_plugin().get_name() << " " << r2.get_plugin().get_name() << " " << (u?"found":"not found") << "\n";); m_unions.back().push_back(u); } void init(const relation_vector & tgts, const relation_vector & srcs, const relation_vector * deltas) { SASSERT(tgts.size()==srcs.size()); unsigned num = tgts.size(); for (unsigned i = 0; i < num; ++i) { relation_base& r1 = *tgts[i]; relation_base* delta = deltas ? (*deltas)[i] : 0; m_unions.push_back(ptr_vector()); for (unsigned j = 0; j < num; ++j) { relation_base& r2 = *srcs[j]; mk_union_fn(i, j, r1, r2, delta); } } } bool can_do_inner_union(unsigned tgt_idx, unsigned src_idx) { return m_unions[tgt_idx][src_idx] != 0; } void do_inner_union(unsigned tgt_idx, unsigned src_idx, relation_base& tgt, relation_base& src, relation_base * delta) { SASSERT(m_unions[tgt_idx][src_idx]); (*m_unions[tgt_idx][src_idx])(tgt, src, delta); } /** If tgt is zero, it is assumed to be a full relation. */ void do_destructive_intersection(scoped_rel& tgt, scoped_rel& src) { if(!src) { return; } if(!tgt) { tgt=src.release(); return; } do_intersection(*tgt, *src); src = 0; } void do_intersection(relation_base& tgt, relation_base& src) { scoped_ptr intersect_fun = m_rmgr.mk_filter_by_intersection_fn(tgt, src); if (!intersect_fun) { TRACE("dl", tgt.display(tout << "tgt\n"); src.display(tout << "src\n");); warning_msg("intersection does not exist"); return; } (*intersect_fun)(tgt, src); } void do_delta_union(unsigned rel_idx, relation_base& tgt, relation_base& src) { scoped_ptr union_fun = m_rmgr.mk_union_fn(tgt, src); SASSERT(union_fun); (*union_fun)(tgt, src); } public: aligned_union_fn( product_relation const& tgt, product_relation const& src, product_relation const* delta, bool is_widen) : m_rmgr(tgt.get_manager()), m_plugin(tgt.get_plugin()), m_is_widen(is_widen) { SASSERT(vectors_equal(tgt.m_spec, src.m_spec)); SASSERT(!delta || vectors_equal(tgt.m_spec, delta->m_spec)); init(tgt.m_relations, src.m_relations, delta ? &delta->m_relations : 0); } ~aligned_union_fn() { unsigned sz = m_unions.size(); for(unsigned i=0; i side_results; ptr_vector side_deltas; for (unsigned i = 0; i < num; ++i) { relation_base& itgt = tgt[i]; relation_base* idelta = delta ? &(*delta)[i] : 0; scoped_rel fresh_delta = idelta ? idelta->get_plugin().mk_empty(*idelta) : 0; scoped_rel side_result; scoped_rel side_delta; //compute the side unions with which we will intersect the result of the basic one for (unsigned j = 0; j < num; ++j) { if (i == j) { continue; //this is the basic union which we will perform later } if (can_do_inner_union(i, j) && can_do_inner_union(j, i)) { TRACE("dl", itgt.display(tout << "tgt:\n"); src[j].display(tout << "src:\n");); // union[i][j] scoped_rel one_side_union = itgt.clone(); scoped_rel one_side_delta = fresh_delta ? fresh_delta->clone() : 0; TRACE("dl", one_side_union->display(tout << "union 1:\n"); src[j].display(tout);); do_inner_union(i, j, *one_side_union, src[j], one_side_delta.get()); TRACE("dl", one_side_union->display(tout << "union:\n");); do_destructive_intersection(side_result, one_side_union); TRACE("dl", side_result->display(tout << "inner-union: " << i << " " << j << "\n"); itgt.display(tout << "tgt:\n");); if (one_side_delta) { do_destructive_intersection(side_delta, one_side_delta); } // union[j][i] one_side_union = src[i].clone(); one_side_delta = fresh_delta ? fresh_delta->clone() : 0; TRACE("dl", one_side_union->display(tout << "union 2:\n"); tgt[j].display(tout);); do_inner_union(i, j, *one_side_union, tgt[j], one_side_delta.get()); TRACE("dl", one_side_union->display(tout << "union:\n");); do_destructive_intersection(side_result, one_side_union); TRACE("dl", side_result->display(tout << "inner-union: " << i << " " << j << "\n"); itgt.display(tout << "tgt:\n");); if (one_side_delta) { do_destructive_intersection(side_delta, one_side_delta); } } } side_results.push_back(side_result.release()); side_deltas.push_back(side_delta.release()); } for (unsigned i = 0; i < num; ++i) { relation_base& itgt = tgt[i]; relation_base* idelta = delta ? &(*delta)[i] : 0; scoped_rel fresh_delta = idelta ? idelta->get_plugin().mk_empty(*idelta) : 0; scoped_rel side_result(side_results[i]); scoped_rel side_delta(side_deltas[i]); // perform the basic union // assume a relation can always perform union with the relation of the same type VERIFY(can_do_inner_union(i,i)); do_inner_union(i, i, itgt, src[i], fresh_delta.get()); if (side_result) { do_intersection(itgt, *side_result); TRACE("dl", side_result->display(tout << "inner-union-end: " << i << "\n");); } if (fresh_delta) { do_destructive_intersection(fresh_delta,side_delta); SASSERT(idelta); do_delta_union(i, *idelta, *fresh_delta); } } if (num == 0) { //we need to handle product relation of no relations separately if (!src.m_default_empty && tgt.m_default_empty) { tgt.m_default_empty = false; if (delta) { delta->m_default_empty = false; } } } TRACE("dl", _tgt.display(tout << "dst':\n"); if (_delta) _delta->display(tout << "delta:\n"); ;); } }; class product_relation_plugin::unaligned_union_fn : public relation_union_fn { bool m_is_widen; rel_spec m_common_spec; scoped_ptr m_aligned_union_fun; public: unaligned_union_fn(product_relation const& tgt, product_relation const& src, product_relation const* delta, bool is_widen) : m_is_widen(is_widen) { ptr_vector rels; rels.push_back(&tgt); rels.push_back(&src); if(delta) { rels.push_back(delta); } get_common_spec(rels, m_common_spec); } virtual void operator()(relation_base& _tgt, const relation_base& _src, relation_base* _delta) { TRACE("dl_verbose", _tgt.display(tout << "dst:\n"); _src.display(tout << "src:\n");); product_relation& tgt = get(_tgt); product_relation const& src0 = get(_src); product_relation* delta = _delta ? get(_delta) : 0; tgt.convert_spec(m_common_spec); if(delta) { delta->convert_spec(m_common_spec); } scoped_rel src_scoped; if(src0.get_kind()!=tgt.get_kind()) { src_scoped = src0.clone(); src_scoped->convert_spec(m_common_spec); } product_relation const& src = src_scoped ? *src_scoped : src0; if(!m_aligned_union_fun) { m_aligned_union_fun = alloc(aligned_union_fn, tgt, src, delta, m_is_widen); SASSERT(m_aligned_union_fun); } (*m_aligned_union_fun)(tgt, src, delta); TRACE("dl", _tgt.display(tout << "dst':\n"); if (_delta) _delta->display(tout << "delta:\n");); } }; class product_relation_plugin::single_non_transparent_src_union_fn : public relation_union_fn { unsigned m_single_rel_idx; scoped_ptr m_inner_union_fun; public: single_non_transparent_src_union_fn(unsigned single_rel_idx, relation_union_fn* inner_union_fun) : m_single_rel_idx(single_rel_idx), m_inner_union_fun(inner_union_fun) {} virtual void operator()(relation_base& tgt, const relation_base& _src, relation_base* delta) { TRACE("dl", tgt.display(tout); _src.display(tout); ); product_relation const& src = get(_src); (*m_inner_union_fun)(tgt, src[m_single_rel_idx], delta); } }; relation_union_fn * product_relation_plugin::mk_union_w_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta, bool is_widen) { if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { if(are_aligned(get(tgt), get(src)) && (!delta || are_aligned(get(tgt), *get(delta)))) { return alloc(aligned_union_fn, get(tgt), get(src), get(delta), is_widen); } return alloc(unaligned_union_fn, get(tgt), get(src), get(delta), is_widen); } if (check_kind(src)) { TRACE("dl", tgt.display(tout << "different kinds"); src.display(tout);); const product_relation & p_src = get(src); unsigned single_idx; if(p_src.try_get_single_non_transparent(single_idx)) { relation_union_fn * inner; if(is_widen) { inner = get_manager().mk_widen_fn(tgt, p_src[single_idx], delta); } else { inner = get_manager().mk_union_fn(tgt, p_src[single_idx], delta); } if (inner) { return alloc(single_non_transparent_src_union_fn, single_idx, inner); } } } return 0; } relation_union_fn * product_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { return mk_union_w_fn(tgt, src, delta, false); } relation_union_fn * product_relation_plugin::mk_widen_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { return mk_union_w_fn(tgt, src, delta, true); } class product_relation_plugin::mutator_fn : public relation_mutator_fn { ptr_vector m_mutators; public: mutator_fn(unsigned sz, relation_mutator_fn** muts): m_mutators(sz, muts) {} ~mutator_fn() { dealloc_ptr_vector_content(m_mutators); } virtual void operator()(relation_base & _r) { TRACE("dl", _r.display(tout);); product_relation& r = get(_r); SASSERT(m_mutators.size() == r.size()); for (unsigned i = 0; i < r.size(); ++i) { relation_mutator_fn* m = m_mutators[i]; if (m) { (*m)(r[i]); } } TRACE("dl", _r.display(tout);); } }; relation_mutator_fn * product_relation_plugin::mk_filter_identical_fn( const relation_base & _t, unsigned col_cnt, const unsigned * identical_cols) { if(is_product_relation(_t)) { bool found = false; product_relation const& r = get(_t); ptr_vector mutators; for (unsigned i = 0; i < r.size(); ++i) { relation_mutator_fn* m = get_manager().mk_filter_identical_fn(r[i], col_cnt, identical_cols); mutators.push_back(m); if (m) found = true; } if (found) { return alloc(mutator_fn, mutators.size(), mutators.c_ptr()); } } return 0; } relation_mutator_fn * product_relation_plugin::mk_filter_equal_fn(const relation_base & _t, const relation_element & value, unsigned col) { if(is_product_relation(_t)) { product_relation const& r = get(_t); ptr_vector mutators; bool found = false; for (unsigned i = 0; i < r.size(); ++i) { relation_mutator_fn* m = get_manager().mk_filter_equal_fn(r[i], value, col); mutators.push_back(m); if (m) found = true; } if (found) { return alloc(mutator_fn, mutators.size(), mutators.c_ptr()); } } return 0; } class product_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { ptr_vector m_mutators; svector > m_attach; public: filter_interpreted_fn(product_relation const& r, app* cond) { for (unsigned i = 0; i < r.size(); ++i) { m_mutators.push_back(r.get_manager().mk_filter_interpreted_fn(r[i], cond)); } for (unsigned i = 0; i < r.size(); ++i) { relation_mutator_fn& m1 = *(m_mutators[i]); for (unsigned j = i + 1; j < r.size(); ++j) { relation_mutator_fn& m2 = *(m_mutators[j]); if (m1.supports_attachment(r[j])) { m_attach.push_back(std::make_pair(i,j)); } if (m2.supports_attachment(r[i])) { m_attach.push_back(std::make_pair(j,i)); } } } } ~filter_interpreted_fn() { dealloc_ptr_vector_content(m_mutators); } void operator()(relation_base& _r) { TRACE("dl", _r.display(tout);); product_relation const& r = get(_r); for (unsigned i = 0; i < m_attach.size(); ++i) { m_mutators[m_attach[i].first]->attach(r[m_attach[i].second]); } for (unsigned i = 0; i < m_mutators.size(); ++i) { (*m_mutators[i])(r[i]); } TRACE("dl", _r.display(tout);); } }; relation_mutator_fn * product_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { return alloc(filter_interpreted_fn, get(t), condition); } // ----------------------------------- // // product_relation // // ----------------------------------- product_relation::product_relation(product_relation_plugin& p, relation_signature const& s): relation_base(p, s), m_default_empty(true) { ensure_correct_kind(); } product_relation::product_relation(product_relation_plugin& p, relation_signature const& s, unsigned num_relations, relation_base** relations) : relation_base(p, s), m_default_empty(true) { for (unsigned i = 0; i < num_relations; ++i) { SASSERT(relations[i]->get_signature()==s); m_relations.push_back(relations[i]); } ensure_correct_kind(); } product_relation::~product_relation() { unsigned num_relations = m_relations.size(); for (unsigned i = 0; i < num_relations; ++i) { m_relations[i]->deallocate(); } } product_relation_plugin& product_relation::get_plugin() const { return dynamic_cast(relation_base::get_plugin()); } void product_relation::ensure_correct_kind() { unsigned rel_cnt = m_relations.size(); //the rel_cnt==0 part makes us to update the kind also when the relation is newly created bool spec_changed = rel_cnt != m_spec.size() || rel_cnt==0; if (spec_changed) { m_spec.resize(rel_cnt); } for (unsigned i = 0; i < rel_cnt; i++) { family_id rkind = m_relations[i]->get_kind(); spec_changed |= (m_spec[i] != rkind); m_spec[i] = rkind; } if (spec_changed) { set_kind(get_plugin().get_relation_kind(*this)); } } void product_relation::convert_spec(const rel_spec & spec) { func_decl* p = 0; const relation_signature & sig = get_signature(); family_id new_kind = get_plugin().get_relation_kind(sig, spec); if (new_kind == get_kind()) { return; } TRACE("dl", { ast_manager& m = get_ast_manager_from_rel_manager(get_manager()); sig.output(m, tout); tout << "\n"; for (unsigned i = 0; i < spec.size(); ++i) { tout << spec[i] << " "; } tout << "\n"; } ); unsigned old_sz = size(); unsigned new_sz = spec.size(); unsigned old_remain = old_sz; relation_vector new_rels; //the loop is quadratic with the number of relations, maybe we want to fix it for(unsigned i=0; iget_kind()==ikind) { irel = m_relations[j]; m_relations[j] = 0; old_remain--; break; } } if(!irel) { if(old_sz == 0 && m_default_empty) { //The relation didn't contain any inner relations but it was empty, //so we make the newly added relations empty as well. irel = get_manager().mk_empty_relation(sig, ikind); } else { irel = get_manager().mk_full_relation(sig, p, ikind); } } new_rels.push_back(irel); } SASSERT(old_remain==0); //the new specification must be a superset of the old one m_relations = new_rels; set_kind(new_kind); m_spec = spec; SASSERT(get_kind() == new_kind); } bool product_relation::try_get_single_non_transparent(unsigned & idx) const { unsigned sz = size(); bool found = false; unsigned candidate; for(unsigned i=0; i relations; for (unsigned i = 0; i < size(); ++i) { relations.push_back((*this)[i].clone()); } product_relation_plugin& p = get_plugin(); return alloc(product_relation, p, get_signature(), relations.size(), relations.c_ptr()); } product_relation * product_relation::complement(func_decl*) const { if(m_relations.empty()) { product_relation * res = clone(); res->m_default_empty = !m_default_empty; return res; } UNREACHABLE(); return 0; } bool product_relation::empty() const { if(m_relations.empty()) { return m_default_empty; } for (unsigned i = 0; i < m_relations.size(); ++i) { if (m_relations[i]->empty()) { return true; } } return false; } void product_relation::to_formula(expr_ref& fml) const { ast_manager& m = fml.get_manager(); expr_ref_vector conjs(m); expr_ref tmp(m); for (unsigned i = 0; i < m_relations.size(); ++i) { m_relations[i]->to_formula(tmp); conjs.push_back(tmp); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), fml); } void product_relation::display(std::ostream & out) const { if (m_relations.empty()) { out << "{}\n"; return; } out << "Product of the following relations:\n"; for (unsigned i = 0; i < m_relations.size(); ++i) { m_relations[i]->display(out); } } }; z3-4.4.0/src/muz/rel/dl_interval_relation.cpp0000644000175000017500000005617712540347414020755 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_interval_relation.cpp Abstract: Basic interval reatlion. Author: Nikolaj Bjorner (nbjorner) 2010-2-11 Revision History: --*/ #include "debug.h" #include "optional.h" #include "ast_pp.h" #include "dl_interval_relation.h" #include "dl_relation_manager.h" #include "bool_rewriter.h" namespace datalog { // ------------------------- // interval_relation_plugin interval_relation_plugin::interval_relation_plugin(relation_manager& m): relation_plugin(interval_relation_plugin::get_name(), m), m_empty(m_dep), m_arith(get_ast_manager()) { } bool interval_relation_plugin::can_handle_signature(const relation_signature & sig) { for (unsigned i = 0; i < sig.size(); ++i) { if (!m_arith.is_int(sig[i]) && !m_arith.is_real(sig[i])) { return false; } } return true; } relation_base * interval_relation_plugin::mk_empty(const relation_signature & s) { return alloc(interval_relation, *this, s, true); } relation_base * interval_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { return alloc(interval_relation, *this, s, false); } class interval_relation_plugin::join_fn : public convenient_relation_join_fn { public: join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2){ } virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { interval_relation const& r1 = get(_r1); interval_relation const& r2 = get(_r2); interval_relation_plugin& p = r1.get_plugin(); interval_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); return result; } }; relation_join_fn * interval_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(r1) || !check_kind(r2)) { return 0; } return alloc(join_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2); } class interval_relation_plugin::project_fn : public convenient_relation_project_fn { public: project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { } virtual relation_base * operator()(const relation_base & _r) { interval_relation const& r = get(_r); interval_relation_plugin& p = r.get_plugin(); interval_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); return result; } }; relation_transformer_fn * interval_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt, const unsigned * removed_cols) { return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); } class interval_relation_plugin::rename_fn : public convenient_relation_rename_fn { public: rename_fn(const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) { } virtual relation_base * operator()(const relation_base & _r) { interval_relation const& r = get(_r); interval_relation_plugin& p = r.get_plugin(); interval_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); return result; } }; relation_transformer_fn * interval_relation_plugin::mk_rename_fn(const relation_base & r, unsigned cycle_len, const unsigned * permutation_cycle) { if(!check_kind(r)) { return 0; } return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle); } interval interval_relation_plugin::unite(interval const& src1, interval const& src2) { bool l_open = src1.is_lower_open(); bool r_open = src1.is_upper_open(); ext_numeral low = src1.inf(); ext_numeral high = src1.sup(); if (src2.inf() < low || (src2.inf() == low && l_open)) { low = src2.inf(); l_open = src2.is_lower_open(); } if (src2.sup() > high || (src2.sup() == high && r_open)) { high = src2.sup(); r_open = src2.is_upper_open(); } return interval(dep(), low, l_open, 0, high, r_open, 0); } interval interval_relation_plugin::widen(interval const& src1, interval const& src2) { bool l_open = src1.is_lower_open(); bool r_open = src1.is_upper_open(); ext_numeral low = src1.inf(); ext_numeral high = src1.sup(); if (src2.inf() < low || (low == src2.inf() && l_open && !src2.is_lower_open())) { low = ext_numeral(false); l_open = true; } if (high < src2.sup() || (src2.sup() == high && !r_open && src2.is_upper_open())) { high = ext_numeral(true); r_open = true; } return interval(dep(), low, l_open, 0, high, r_open, 0); } interval interval_relation_plugin::meet(interval const& src1, interval const& src2, bool& isempty) { isempty = false; if (is_empty(0, src1) || is_infinite(src2)) { return src1; } if (is_empty(0, src2) || is_infinite(src1)) { return src2; } bool l_open = src1.is_lower_open(); bool r_open = src1.is_upper_open(); ext_numeral low = src1.inf(); ext_numeral high = src1.sup(); if (src2.inf() > low || (src2.inf() == low && !l_open)) { low = src2.inf(); l_open = src2.is_lower_open(); } if (src2.sup() < high || (src2.sup() == high && !r_open)) { high = src2.sup(); r_open = src2.is_upper_open(); } if (low > high || (low == high && (l_open || r_open))) { isempty = true; return interval(dep()); } else { return interval(dep(), low, l_open, 0, high, r_open, 0); } } bool interval_relation_plugin::is_infinite(interval const& i) { return i.plus_infinity() && i.minus_infinity(); } bool interval_relation_plugin::is_empty(unsigned, interval const& i) { return i.sup() < i.inf(); } class interval_relation_plugin::union_fn : public relation_union_fn { bool m_is_widen; public: union_fn(bool is_widen) : m_is_widen(is_widen) { } virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { TRACE("interval_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); interval_relation& r = get(_r); interval_relation const& src = get(_src); if (_delta) { interval_relation& d = get(*_delta); r.mk_union(src, &d, m_is_widen); } else { r.mk_union(src, 0, m_is_widen); } } }; relation_union_fn * interval_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return 0; } return alloc(union_fn, false); } relation_union_fn * interval_relation_plugin::mk_widen_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return 0; } return alloc(union_fn, true); } class interval_relation_plugin::filter_identical_fn : public relation_mutator_fn { unsigned_vector m_identical_cols; public: filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) : m_identical_cols(col_cnt, identical_cols) {} virtual void operator()(relation_base & r) { interval_relation & pr = get(r); for (unsigned i = 1; i < m_identical_cols.size(); ++i) { unsigned c1 = m_identical_cols[0]; unsigned c2 = m_identical_cols[i]; pr.equate(c1, c2); } } }; relation_mutator_fn * interval_relation_plugin::mk_filter_identical_fn( const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { if(!check_kind(t)) { return 0; } return alloc(filter_identical_fn, col_cnt, identical_cols); } class interval_relation_plugin::filter_equal_fn : public relation_mutator_fn { unsigned m_col; rational m_value; public: filter_equal_fn(relation_manager & m, const relation_element & value, unsigned col) : m_col(col) { arith_util arith(m.get_context().get_manager()); VERIFY(arith.is_numeral(value, m_value)); } virtual void operator()(relation_base & _r) { interval_relation & r = get(_r); interval_relation_plugin & p = r.get_plugin(); r.mk_intersect(m_col, interval(p.dep(), m_value)); TRACE("interval_relation", tout << m_value << "\n"; r.display(tout);); } }; relation_mutator_fn * interval_relation_plugin::mk_filter_equal_fn(const relation_base & r, const relation_element & value, unsigned col) { if(check_kind(r)) { return alloc(filter_equal_fn, get_manager(), value, col); } return 0; } class interval_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { app_ref m_cond; public: filter_interpreted_fn(interval_relation const& t, app* cond): m_cond(cond, t.get_plugin().get_ast_manager()) { } void operator()(relation_base& t) { get(t).filter_interpreted(m_cond); TRACE("interval_relation", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); } }; relation_mutator_fn * interval_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { if (check_kind(t)) { return alloc(filter_interpreted_fn, get(t), condition); } return 0; } interval_relation& interval_relation_plugin::get(relation_base& r) { return dynamic_cast(r); } interval_relation const & interval_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); } // ----------------------- // interval_relation interval_relation::interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty): vector_relation(p, s, is_empty, interval(p.dep())) { TRACE("interval_relation", tout << s.size() << "\n";); } void interval_relation::add_fact(const relation_fact & f) { interval_relation r(get_plugin(), get_signature(), false); ast_manager& m = get_plugin().get_ast_manager(); for (unsigned i = 0; i < f.size(); ++i) { app_ref eq(m); expr* e = f[i]; eq = m.mk_eq(m.mk_var(i, m.get_sort(e)), e); r.filter_interpreted(eq.get()); } mk_union(r, 0, false); } bool interval_relation::contains_fact(const relation_fact & f) const { SASSERT(f.size() == get_signature().size()); interval_relation_plugin& p = get_plugin(); for (unsigned i = 0; i < f.size(); ++i) { if (f[i] != f[find(i)]) { return false; } interval const& iv = (*this)[i]; if (p.is_infinite(iv)) { continue; } rational v; if (p.m_arith.is_numeral(f[i], v)) { if (!iv.contains(v)) { return false; } } else { // TBD: may or must? } } return true; } interval_relation * interval_relation::clone() const { interval_relation* result = alloc(interval_relation, get_plugin(), get_signature(), empty()); result->copy(*this); return result; } interval_relation * interval_relation::complement(func_decl*) const { UNREACHABLE(); return 0; } void interval_relation::to_formula(expr_ref& fml) const { ast_manager& m = get_plugin().get_ast_manager(); arith_util& arith = get_plugin().m_arith; expr_ref_vector conjs(m); relation_signature const& sig = get_signature(); for (unsigned i = 0; i < sig.size(); ++i) { if (i != find(i)) { conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), m.mk_var(find(i), sig[find(i)]))); continue; } interval const& iv = (*this)[i]; sort* ty = sig[i]; expr_ref var(m.mk_var(i, ty), m); if (!iv.minus_infinity()) { expr* lo = arith.mk_numeral(iv.get_lower_value(), ty); if (iv.is_lower_open()) { conjs.push_back(arith.mk_lt(lo, var)); } else { conjs.push_back(arith.mk_le(lo, var)); } } if (!iv.plus_infinity()) { expr* hi = arith.mk_numeral(iv.get_upper_value(), ty); if (iv.is_upper_open()) { conjs.push_back(arith.mk_lt(var, hi)); } else { conjs.push_back(arith.mk_le(var, hi)); } } } bool_rewriter br(m); br.mk_and(conjs.size(), conjs.c_ptr(), fml); } void interval_relation::display_index(unsigned i, interval const& j, std::ostream & out) const { out << i << " in " << j << "\n"; } interval_relation_plugin& interval_relation::get_plugin() const { return static_cast(relation_base::get_plugin()); } void interval_relation::mk_intersect(unsigned idx, interval const& i) { bool isempty; (*this)[idx] = mk_intersect((*this)[idx], i, isempty); if (isempty || is_empty(idx, (*this)[idx])) { set_empty(); } } void interval_relation::mk_rename_elem(interval& i, unsigned, unsigned const* ) { } void interval_relation::filter_interpreted(app* cond) { interval_relation_plugin& p = get_plugin(); rational k; unsigned x, y; if (p.is_lt(cond, x, k, y)) { // 0 < x - y + k if (x == UINT_MAX) { // y < k mk_intersect(y, interval(p.dep(), k, true, false, 0)); return; } if (y == UINT_MAX) { // -k < x mk_intersect(x, interval(p.dep(), -k, true, true, 0)); return; } // y < x + k ext_numeral x_hi = (*this)[x].sup(); ext_numeral y_lo = (*this)[y].inf(); if (!x_hi.is_infinite()) { mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), true, false, 0)); } if (!y_lo.is_infinite()) { mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, true, true, 0)); } return; } bool is_int = false; if (p.is_le(cond, x, k, y, is_int)) { // 0 <= x - y + k if (x == UINT_MAX) { // y <= k mk_intersect(y, interval(p.dep(), k, false, false, 0)); return; } if (y == UINT_MAX) { // -k <= x mk_intersect(x, interval(p.dep(), -k, false, true, 0)); return; } ext_numeral x_hi = (*this)[x].sup(); ext_numeral y_lo = (*this)[y].inf(); if (!x_hi.is_infinite()) { mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), false, false, 0)); } if (!y_lo.is_infinite()) { mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, false, true, 0)); } return; } if (p.is_eq(cond, x, k, y)) { // y = x + k if (x == UINT_MAX) { SASSERT(y != UINT_MAX); mk_intersect(y, interval(p.dep(), k)); return; } if (y == UINT_MAX) { // x = - k SASSERT(x != UINT_MAX); mk_intersect(x, interval(p.dep(), -k)); return; } interval x_i = (*this)[x]; interval y_i = (*this)[y]; x_i += interval(p.dep(), k); y_i -= interval(p.dep(), k); mk_intersect(x, y_i); mk_intersect(y, x_i); } if (get_plugin().get_ast_manager().is_false(cond)) { set_empty(); } } bool interval_relation_plugin::is_linear(expr* e, unsigned& neg, unsigned& pos, rational& k, bool is_pos) const { #define SET_VAR(_idx_) \ if (is_pos &&pos == UINT_MAX) { \ pos = _idx_; \ return true; \ } \ if (!is_pos && neg == UINT_MAX) { \ neg = _idx_; \ return true; \ } \ else { \ return false; \ } if (is_var(e)) { SET_VAR(to_var(e)->get_idx()); } if (!is_app(e)) { return false; } app* a = to_app(e); if (m_arith.is_add(e)) { for (unsigned i = 0; i < a->get_num_args(); ++i) { if (!is_linear(a->get_arg(i), neg, pos, k, is_pos)) return false; } return true; } if (m_arith.is_sub(e)) { SASSERT(a->get_num_args() == 2); return is_linear(a->get_arg(0), neg, pos, k, is_pos) && is_linear(a->get_arg(1), neg, pos, k, !is_pos); } rational k1; SASSERT(!m_arith.is_mul(e) || a->get_num_args() == 2); if (m_arith.is_mul(e) && m_arith.is_numeral(a->get_arg(0), k1) && k1.is_minus_one() && is_var(a->get_arg(1))) { SET_VAR(to_var(a->get_arg(1))->get_idx()); } if (m_arith.is_numeral(e, k1)) { if (is_pos) { k += k1; } else { k -= k1; } return true; } return false; } // 0 <= x - y + k bool interval_relation_plugin::is_le(app* cond, unsigned& x, rational& k, unsigned& y, bool& is_int) const { ast_manager& m = get_ast_manager(); k.reset(); x = UINT_MAX; y = UINT_MAX; if (m_arith.is_le(cond)) { is_int = m_arith.is_int(cond->get_arg(0)); if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; return (x != UINT_MAX || y != UINT_MAX); } if (m_arith.is_ge(cond)) { is_int = m_arith.is_int(cond->get_arg(0)); if (!is_linear(cond->get_arg(0), y, x, k, true)) return false; if (!is_linear(cond->get_arg(1), y, x, k, false)) return false; return (x != UINT_MAX || y != UINT_MAX); } if (m_arith.is_lt(cond) && m_arith.is_int(cond->get_arg(0))) { is_int = true; if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; k -= rational::one(); return (x != UINT_MAX || y != UINT_MAX); } if (m_arith.is_gt(cond) && m_arith.is_int(cond->get_arg(0))) { is_int = true; if (!is_linear(cond->get_arg(0), y, x, k, true)) return false; if (!is_linear(cond->get_arg(1), y, x, k, false)) return false; k += rational::one(); return (x != UINT_MAX || y != UINT_MAX); } if (m.is_not(cond) && is_app(cond->get_arg(0))) { // not (0 <= x - y + k) // <=> // 0 > x - y + k // <=> // 0 <= y - x - k - 1 if (is_le(to_app(cond->get_arg(0)), x, k, y, is_int) && is_int) { k.neg(); k -= rational::one(); std::swap(x, y); return true; } // not (0 < x - y + k) // <=> // 0 >= x - y + k // <=> // 0 <= y - x - k if (is_lt(to_app(cond->get_arg(0)), x, k, y)) { is_int = false; k.neg(); std::swap(x, y); return true; } } return false; } // 0 < x - y + k bool interval_relation_plugin::is_lt(app* cond, unsigned& x, rational& k, unsigned& y) const { k.reset(); x = UINT_MAX; y = UINT_MAX; if (m_arith.is_lt(cond) && m_arith.is_real(cond->get_arg(0))) { if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; return (x != UINT_MAX || y != UINT_MAX); } if (m_arith.is_gt(cond) && m_arith.is_real(cond->get_arg(0))) { if (!is_linear(cond->get_arg(0), y, x, k, true)) return false; if (!is_linear(cond->get_arg(1), y, x, k, false)) return false; return (x != UINT_MAX || y != UINT_MAX); } return false; } // 0 = x - y + k bool interval_relation_plugin::is_eq(app* cond, unsigned& x, rational& k, unsigned& y) const { ast_manager& m = get_ast_manager(); k.reset(); x = UINT_MAX; y = UINT_MAX; if (m.is_eq(cond)) { if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; return (x != UINT_MAX || y != UINT_MAX); } return false; } }; z3-4.4.0/src/muz/rel/check_relation.h0000644000175000017500000001561412540347414017163 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: check_relation.h Abstract: Checked relation. Each operation on an underlying relation is checked for correctness. Author: Nikolaj Bjorner (nbjorner) 2014-09-23 Revision History: --*/ #ifndef _CHECK_RELATION_H_ #define _CHECK_RELATION_H_ #include "doc.h" #include "dl_base.h" namespace datalog { class check_relation_plugin; class check_relation; class check_relation : public relation_base { friend class check_relation_plugin; ast_manager& m; relation_base* m_relation; expr_ref m_fml; void check_equiv(char const* objective, expr* f1, expr* f2) const; expr_ref mk_eq(relation_fact const& f) const; public: check_relation(check_relation_plugin& p, relation_signature const& s, relation_base* r); virtual ~check_relation(); virtual void reset(); virtual void add_fact(const relation_fact & f); virtual void add_new_fact(const relation_fact & f); virtual bool contains_fact(const relation_fact & f) const; virtual check_relation * clone() const; virtual check_relation * complement(func_decl*) const; virtual void to_formula(expr_ref& fml) const; check_relation_plugin& get_plugin() const; virtual bool fast_empty() const; virtual bool empty() const; virtual void display(std::ostream& out) const; virtual bool is_precise() const { return m_relation->is_precise(); } virtual unsigned get_size_estimate_rows() const { return m_relation->get_size_estimate_rows(); } relation_base& rb() { return *m_relation; } relation_base const& rb() const { return *m_relation; } expr_ref ground(expr* fml) const; void consistent_formula(); }; class check_relation_plugin : public relation_plugin { friend class check_relation; class join_fn; class join_project_fn; class project_fn; class union_fn; class rename_fn; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; class filter_by_negation_fn; class filter_by_union_fn; class filter_proj_fn; class negation_filter_fn; ast_manager& m; relation_plugin* m_base; static check_relation& get(relation_base& r); static check_relation* get(relation_base* r); static check_relation const & get(relation_base const& r); expr_ref ground(relation_base const& rb, expr* fml) const; expr_ref ground(relation_base const& rb) const; expr_ref mk_project( relation_signature const& sig, expr* fml, unsigned_vector const& removed_cols); expr_ref mk_join( relation_base const& t1, relation_base const& t2, unsigned_vector const& cols1, unsigned_vector const& cols2); public: check_relation_plugin(relation_manager& rm); ~check_relation_plugin(); void set_plugin(relation_plugin* p) { m_base = p; } virtual bool can_handle_signature(const relation_signature & s); static symbol get_name() { return symbol("check_relation"); } virtual relation_base * mk_empty(const relation_signature & s); virtual relation_base * mk_full(func_decl* p, const relation_signature & s); virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); virtual relation_join_fn * mk_join_project_fn( const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols); virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols); virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols); virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col); virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); virtual relation_intersection_filter_fn * mk_filter_by_negation_fn( const relation_base& t, const relation_base& neg, unsigned joined_col_cnt, const unsigned *t_cols, const unsigned *negated_cols); virtual relation_transformer_fn * mk_filter_interpreted_and_project_fn( const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); void verify_join(relation_base const& t1, relation_base const& t2, relation_base const& t, unsigned_vector const& cols1, unsigned_vector const& cols2); void verify_filter(expr* fml0, relation_base const& t, expr* cond); void verify_union(expr* fml0, relation_base const& src, relation_base const& dst, expr* delta0, relation_base const* delta); void verify_permutation( relation_base const& src, relation_base const& dst, unsigned_vector const& cycle); void verify_project( relation_base const& src, expr* f1, relation_base const& dst, expr* f2, unsigned_vector const& removed_cols); void verify_project( relation_base const& src, relation_base const& dst, unsigned_vector const& removed_cols); void verify_filter_project( relation_base const& src, relation_base const& dst, app* cond, unsigned_vector const& removed_cols); void verify_join_project( relation_base const& t1, relation_base const& t2, relation_base const& t, unsigned_vector const& cols1, unsigned_vector const& cols2, unsigned_vector const& rm_cols); void check_equiv(char const* objective, expr* f1, expr* f2); void check_contains(char const* objective, expr* f1, expr* f2); void verify_filter_by_negation( expr* dst0, relation_base const& dst, relation_base const& neg, unsigned_vector const& dst_eq, unsigned_vector const& neg_eq); }; }; #endif /* _CHECK_RELATION_H_ */ z3-4.4.0/src/solver/0000755000175000017500000000000012540347414013746 5ustar michaelmichaelz3-4.4.0/src/solver/tactic2solver.h0000644000175000017500000000171312540347414016705 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: tactic2solver.h Abstract: Wrapper for implementing the external solver interface using a tactic. This is a light version of the strategic solver. Author: Leonardo (leonardo) 2012-01-23 Notes: --*/ #ifndef _TACTIC2SOLVER_H_ #define _TACTIC2SOLVER_H_ #include"params.h" class ast_manager; class tactic; class tactic_factory; class solver; class solver_factory; solver * mk_tactic2solver(ast_manager & m, tactic * t = 0, params_ref const & p = params_ref(), bool produce_proofs = false, bool produce_models = true, bool produce_unsat_cores = false, symbol const & logic = symbol::null); solver_factory * mk_tactic2solver_factory(tactic * t); solver_factory * mk_tactic_factory2solver_factory(tactic_factory * f); #endif z3-4.4.0/src/solver/solver.h0000644000175000017500000001003412540347414015427 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: solver.h Abstract: abstract solver interface Author: Leonardo (leonardo) 2011-03-19 Notes: --*/ #ifndef _SOLVER_H_ #define _SOLVER_H_ #include"check_sat_result.h" #include"progress_callback.h" #include"params.h" class solver; class solver_factory { public: virtual ~solver_factory() {} virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) = 0; }; /** \brief Abstract interface for making solvers available in the Z3 API and front-ends such as SMT 2.0 and (legacy) SMT 1.0. It provides the basic functionality for incremental solvers. - assertions - push/pop - parameter setting (updt_params) - statistics - results based on check_sat_result API - interruption (set_cancel) */ class solver : public check_sat_result { public: virtual ~solver() {} /** \brief Update the solver internal settings. */ virtual void updt_params(params_ref const & p) {} /** \brief Store in \c r a description of the configuration parameters available in this solver. */ virtual void collect_param_descrs(param_descrs & r) {} /** \brief Enable/Disable model generation for this solver object. It is invoked before init(m, logic). The user may optionally invoke it after init(m, logic). */ virtual void set_produce_models(bool f) {} /** \brief Add a new formula to the assertion stack. */ virtual void assert_expr(expr * t) = 0; /** \brief Add a new formula \c t to the assertion stack, and "tag" it with \c a. The propositional variable \c a is used to track the use of \c t in a proof of unsatisfiability. */ virtual void assert_expr(expr * t, expr * a) = 0; /** \brief Create a backtracking point. */ virtual void push() = 0; /** \brief Remove \c n backtracking points. All assertions between the pop and matching push are removed. */ virtual void pop(unsigned n) = 0; /** \brief Return the number of backtracking points. */ virtual unsigned get_scope_level() const = 0; /** \brief Check if the set of assertions in the assertion stack is satisfiable modulo the given assumptions. If it is unsatisfiable, and unsat-core generation is enabled. Then, the unsat-core is a subset of these assumptions. */ virtual lbool check_sat(unsigned num_assumptions, expr * const * assumptions) = 0; /** \brief Interrupt this solver. */ void cancel() { set_cancel(true); } /** \brief Reset the interruption. */ void reset_cancel() { set_cancel(false); } /** \brief Set a progress callback procedure that is invoked by this solver during check_sat. This is essentially for backward compatibility and integration with VCC tools. */ virtual void set_progress_callback(progress_callback * callback) = 0; /** \brief Return the number of assertions in the assertion stack. */ virtual unsigned get_num_assertions() const; /** \brief Return the assertion at position idx in the assertion stack. */ virtual expr * get_assertion(unsigned idx) const; /** \brief The number of tracked assumptions (see assert_expr(t, a)). */ virtual unsigned get_num_assumptions() const = 0; /** \brief Retrieves the idx'th tracked assumption (see assert_expr(t, a)). */ virtual expr * get_assumption(unsigned idx) const = 0; /** \brief Display the content of this solver. */ virtual void display(std::ostream & out) const; class scoped_push { solver& s; bool m_nopop; public: scoped_push(solver& s):s(s), m_nopop(false) { s.push(); } ~scoped_push() { if (!m_nopop) s.pop(1); } void disable_pop() { m_nopop = true; } }; protected: virtual void set_cancel(bool f) = 0; }; #endif z3-4.4.0/src/solver/solver.cpp0000644000175000017500000000070712540347414015770 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: solver.h Abstract: abstract solver interface Author: Leonardo (leonardo) 2011-03-19 Notes: --*/ #include"solver.h" unsigned solver::get_num_assertions() const { NOT_IMPLEMENTED_YET(); return 0; } expr * solver::get_assertion(unsigned idx) const { NOT_IMPLEMENTED_YET(); return 0; } void solver::display(std::ostream & out) const { out << "(solver)"; } z3-4.4.0/src/solver/check_sat_result.cpp0000644000175000017500000000210212540347414017767 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: check_sat_result.cpp Abstract: Abstract interface for storing the result produced by a check_sat like command Author: Leonardo (leonardo) 2012-11-01 Notes: --*/ #include"check_sat_result.h" simple_check_sat_result::simple_check_sat_result(ast_manager & m): m_core(m), m_proof(m) { } simple_check_sat_result::~simple_check_sat_result() { } void simple_check_sat_result::collect_statistics(statistics & st) const { st.copy(m_stats); } void simple_check_sat_result::get_unsat_core(ptr_vector & r) { if (m_status == l_false) r.append(m_core.size(), m_core.c_ptr()); } void simple_check_sat_result::get_model(model_ref & m) { if (m_status != l_false) m = m_model; else m = 0; } proof * simple_check_sat_result::get_proof() { return m_status == l_false ? m_proof.get() : 0; } std::string simple_check_sat_result::reason_unknown() const { return m_unknown; } void simple_check_sat_result::get_labels(svector & r) { } z3-4.4.0/src/solver/tactic2solver.cpp0000644000175000017500000002007212540347414017237 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: tactic2solver.cpp Abstract: Wrapper for implementing the solver interface using a tactic. This is a light version of the strategic solver. Author: Leonardo (leonardo) 2012-01-23 Notes: --*/ #include"solver_na2as.h" #include"tactic.h" #include"ast_smt2_pp.h" /** \brief Simulates the incremental solver interface using a tactic. Every query will be solved from scratch. So, this is not a good option for applications trying to solve many easy queries that a similar to each other. */ class tactic2solver : public solver_na2as { expr_ref_vector m_assertions; unsigned_vector m_scopes; ref m_result; tactic_ref m_tactic; symbol m_logic; params_ref m_params; bool m_produce_models; bool m_produce_proofs; bool m_produce_unsat_cores; statistics m_stats; public: tactic2solver(ast_manager & m, tactic * t, params_ref const & p, bool produce_proofs, bool produce_models, bool produce_unsat_cores, symbol const & logic); virtual ~tactic2solver(); virtual void updt_params(params_ref const & p); virtual void collect_param_descrs(param_descrs & r); virtual void set_produce_models(bool f) { m_produce_models = f; } virtual void assert_expr(expr * t); virtual void push_core(); virtual void pop_core(unsigned n); virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions); virtual void set_cancel(bool f); virtual void collect_statistics(statistics & st) const; virtual void get_unsat_core(ptr_vector & r); virtual void get_model(model_ref & m); virtual proof * get_proof(); virtual std::string reason_unknown() const; virtual void get_labels(svector & r) {} virtual void set_progress_callback(progress_callback * callback) {} virtual unsigned get_num_assertions() const; virtual expr * get_assertion(unsigned idx) const; virtual void display(std::ostream & out) const; }; tactic2solver::tactic2solver(ast_manager & m, tactic * t, params_ref const & p, bool produce_proofs, bool produce_models, bool produce_unsat_cores, symbol const & logic): solver_na2as(m), m_assertions(m) { m_tactic = t; m_logic = logic; m_params = p; m_produce_models = produce_models; m_produce_proofs = produce_proofs; m_produce_unsat_cores = produce_unsat_cores; } tactic2solver::~tactic2solver() { } void tactic2solver::updt_params(params_ref const & p) { m_params = p; } void tactic2solver::collect_param_descrs(param_descrs & r) { if (m_tactic.get()) m_tactic->collect_param_descrs(r); } void tactic2solver::assert_expr(expr * t) { m_assertions.push_back(t); m_result = 0; } void tactic2solver::push_core() { m_scopes.push_back(m_assertions.size()); m_result = 0; } void tactic2solver::pop_core(unsigned n) { unsigned new_lvl = m_scopes.size() - n; unsigned old_sz = m_scopes[new_lvl]; m_assertions.shrink(old_sz); m_scopes.shrink(new_lvl); m_result = 0; } lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * assumptions) { if (m_tactic.get() == 0) return l_false; ast_manager & m = m_assertions.m(); m_result = alloc(simple_check_sat_result, m); m_tactic->cleanup(); m_tactic->updt_params(m_params); m_tactic->set_logic(m_logic); goal_ref g = alloc(goal, m, m_produce_proofs, m_produce_models, m_produce_unsat_cores); unsigned sz = m_assertions.size(); for (unsigned i = 0; i < sz; i++) { g->assert_expr(m_assertions.get(i)); } for (unsigned i = 0; i < num_assumptions; i++) { g->assert_expr(assumptions[i], m.mk_asserted(assumptions[i]), m.mk_leaf(assumptions[i])); } model_ref md; proof_ref pr(m); expr_dependency_ref core(m); std::string reason_unknown = "unknown"; try { switch (::check_sat(*m_tactic, g, md, pr, core, reason_unknown)) { case l_true: m_result->set_status(l_true); break; case l_false: m_result->set_status(l_false); break; default: m_result->set_status(l_undef); if (reason_unknown != "") m_result->m_unknown = reason_unknown; break; } } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { TRACE("tactic2solver", tout << "exception: " << ex.msg() << "\n";); m_result->set_status(l_undef); m_result->m_unknown = ex.msg(); } m_tactic->collect_statistics(m_result->m_stats); m_tactic->collect_statistics(m_stats); m_result->m_model = md; m_result->m_proof = pr; if (m_produce_unsat_cores) { ptr_vector core_elems; m.linearize(core, core_elems); m_result->m_core.append(core_elems.size(), core_elems.c_ptr()); } m_tactic->cleanup(); return m_result->status(); } void tactic2solver::set_cancel(bool f) { if (m_tactic.get()) { if (f) m_tactic->cancel(); else m_tactic->reset_cancel(); } } void tactic2solver::collect_statistics(statistics & st) const { st.copy(m_stats); //SASSERT(m_stats.size() > 0); } void tactic2solver::get_unsat_core(ptr_vector & r) { if (m_result.get()) m_result->get_unsat_core(r); } void tactic2solver::get_model(model_ref & m) { if (m_result.get()) m_result->get_model(m); } proof * tactic2solver::get_proof() { if (m_result.get()) return m_result->get_proof(); else return 0; } std::string tactic2solver::reason_unknown() const { if (m_result.get()) return m_result->reason_unknown(); else return std::string("unknown"); } unsigned tactic2solver::get_num_assertions() const { return m_assertions.size(); } expr * tactic2solver::get_assertion(unsigned idx) const { return m_assertions.get(idx); } void tactic2solver::display(std::ostream & out) const { ast_manager & m = m_assertions.m(); unsigned num = m_assertions.size(); out << "(solver"; for (unsigned i = 0; i < num; i++) { out << "\n " << mk_ismt2_pp(m_assertions.get(i), m, 2); } out << ")"; } solver * mk_tactic2solver(ast_manager & m, tactic * t, params_ref const & p, bool produce_proofs, bool produce_models, bool produce_unsat_cores, symbol const & logic) { return alloc(tactic2solver, m, t, p, produce_proofs, produce_models, produce_unsat_cores, logic); } class tactic2solver_factory : public solver_factory { ref m_tactic; public: tactic2solver_factory(tactic * t):m_tactic(t) { } virtual ~tactic2solver_factory() {} virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { return mk_tactic2solver(m, m_tactic.get(), p, proofs_enabled, models_enabled, unsat_core_enabled, logic); } }; class tactic_factory2solver_factory : public solver_factory { scoped_ptr m_factory; public: tactic_factory2solver_factory(tactic_factory * f):m_factory(f) { } virtual ~tactic_factory2solver_factory() {} virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { tactic * t = (*m_factory)(m, p); return mk_tactic2solver(m, t, p, proofs_enabled, models_enabled, unsat_core_enabled, logic); } }; solver_factory * mk_tactic2solver_factory(tactic * t) { return alloc(tactic2solver_factory, t); } solver_factory * mk_tactic_factory2solver_factory(tactic_factory * f) { return alloc(tactic_factory2solver_factory, f); } z3-4.4.0/src/solver/check_sat_result.h0000644000175000017500000000442412540347414017445 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: check_sat_result.h Abstract: Abstract interface for storing the result produced by a check_sat like command Author: Leonardo (leonardo) 2012-01-23 Notes: --*/ #ifndef _CHECK_SAT_RESULT_H_ #define _CHECK_SAT_RESULT_H_ #include"model.h" #include"lbool.h" #include"statistics.h" /** \brief Abstract interface for the result of a (check-sat) like command. It encapsulates information such as: - the actual result: l_true (satisfiable), l_false (unsatisfiable), l_undef (unknown) - statistics - model (if the result is satisfiable) - proof (if the result is unsatisfiable) - unsat-core (if the result is unsatisfiable) - reason-unknown (if the result is unknown, i.e., the solver failed to solve the problem) - label (if the result is satisfiable) this is legacy for Boogie */ class check_sat_result { protected: unsigned m_ref_count; lbool m_status; public: check_sat_result():m_ref_count(0), m_status(l_undef) {} virtual ~check_sat_result() {} void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) dealloc(this); } void set_status(lbool r) { m_status = r; } lbool status() const { return m_status; } virtual void collect_statistics(statistics & st) const = 0; virtual void get_unsat_core(ptr_vector & r) = 0; virtual void get_model(model_ref & m) = 0; virtual proof * get_proof() = 0; virtual std::string reason_unknown() const = 0; virtual void get_labels(svector & r) = 0; }; /** \brief Very simple implementation of the check_sat_result object. */ struct simple_check_sat_result : public check_sat_result { statistics m_stats; model_ref m_model; expr_ref_vector m_core; proof_ref m_proof; std::string m_unknown; simple_check_sat_result(ast_manager & m); virtual ~simple_check_sat_result(); virtual void collect_statistics(statistics & st) const; virtual void get_unsat_core(ptr_vector & r); virtual void get_model(model_ref & m); virtual proof * get_proof(); virtual std::string reason_unknown() const; virtual void get_labels(svector & r); }; #endif z3-4.4.0/src/solver/combined_solver.h0000644000175000017500000000105512540347414017272 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: combined_solver.cpp Abstract: Implements the solver API by combining two solvers. This is a replacement for the strategic_solver class. Author: Leonardo (leonardo) 2012-12-11 Notes: --*/ #ifndef _COMBINED_SOLVER_H_ #define _COMBINED_SOLVER_H_ #include"params.h" class solver; class solver_factory; solver * mk_combined_solver(solver * s1, solver * s2, params_ref const & p); solver_factory * mk_combined_solver_factory(solver_factory * f1, solver_factory * f2); #endif z3-4.4.0/src/solver/combined_solver_params.pyg0000644000175000017500000000121612540347414021204 0ustar michaelmichaeldef_module_params('combined_solver', description='combines two solvers: non-incremental (solver1) and incremental (solver2)', export=True, params=(('solver2_timeout', UINT, UINT_MAX, "fallback to solver 1 after timeout even when in incremental model"), ('ignore_solver1', BOOL, False, "if true, solver 2 is always used"), ('solver2_unknown', UINT, 1, "what should be done when solver 2 returns unknown: 0 - just return unknown, 1 - execute solver 1 if quantifier free problem, 2 - execute solver 1") )) z3-4.4.0/src/solver/solver_na2as.h0000644000175000017500000000267412540347414016526 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: solver_na2as.h Abstract: Solver that implements "named" assertions using assumptions (aka answer literals). That is, a named assertion assert_expr(t, a) is mapped into a implies t and 'a' is used as an extra assumption for check_sat. Author: Leonardo (leonardo) 2012-11-02 Notes: --*/ #ifndef _SOLVER_NA2AS_H_ #define _SOLVER_NA2AS_H_ #include"solver.h" class solver_na2as : public solver { ast_manager & m_manager; ptr_vector m_assumptions; unsigned_vector m_scopes; void restore_assumptions(unsigned old_sz); public: solver_na2as(ast_manager & m); virtual ~solver_na2as(); virtual void assert_expr(expr * t, expr * a); virtual void assert_expr(expr * t) = 0; // Subclasses of solver_na2as should redefine the following *_core methods instead of these ones. virtual lbool check_sat(unsigned num_assumptions, expr * const * assumptions); virtual void push(); virtual void pop(unsigned n); virtual unsigned get_scope_level() const; virtual unsigned get_num_assumptions() const { return m_assumptions.size(); } virtual expr * get_assumption(unsigned idx) const { return m_assumptions[idx]; } protected: virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) = 0; virtual void push_core() = 0; virtual void pop_core(unsigned n) = 0; }; #endif z3-4.4.0/src/solver/progress_callback.h0000644000175000017500000000105612540347414017601 0ustar michaelmichael/*++ Copyright (c) 2008 Microsoft Corporation Module Name: progress_callback.h Abstract: Virtual callback for reporting progress. Author: Michal Moskal (micmo) 2009-02-17. Revision History: --*/ #ifndef _PROGRESS_CALLBACK_H_ #define _PROGRESS_CALLBACK_H_ class progress_callback { public: virtual ~progress_callback() {} // Called on every check for resource limit exceeded (much more frequent). virtual void fast_progress_sample() {} // Less frequent invoked. virtual void slow_progress_sample() {} }; #endif z3-4.4.0/src/solver/solver_na2as.cpp0000644000175000017500000000456412540347414017061 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: solver_na2as.cpp Abstract: Solver that implements "named" assertions using assumptions (aka answer literals). That is, a named assertion assert_expr(t, a) is mapped into a implies t and 'a' is used as an extra assumption for check_sat. Author: Leonardo (leonardo) 2012-11-02 Notes: --*/ #include"solver_na2as.h" #include"ast_smt2_pp.h" solver_na2as::solver_na2as(ast_manager & m): m_manager(m) { } solver_na2as::~solver_na2as() { restore_assumptions(0); } void solver_na2as::assert_expr(expr * t, expr * a) { if (a == 0) { assert_expr(t); } else { SASSERT(is_uninterp_const(a)); SASSERT(m_manager.is_bool(a)); TRACE("solver_na2as", tout << "asserting\n" << mk_ismt2_pp(t, m_manager) << "\n" << mk_ismt2_pp(a, m_manager) << "\n";); m_manager.inc_ref(a); m_assumptions.push_back(a); expr_ref new_t(m_manager); new_t = m_manager.mk_implies(a, t); assert_expr(new_t); } } struct append_assumptions { ptr_vector & m_assumptions; unsigned m_old_sz; append_assumptions(ptr_vector & _m_assumptions, unsigned num_assumptions, expr * const * assumptions): m_assumptions(_m_assumptions) { m_old_sz = m_assumptions.size(); m_assumptions.append(num_assumptions, assumptions); } ~append_assumptions() { m_assumptions.shrink(m_old_sz); } }; lbool solver_na2as::check_sat(unsigned num_assumptions, expr * const * assumptions) { append_assumptions app(m_assumptions, num_assumptions, assumptions); return check_sat_core(m_assumptions.size(), m_assumptions.c_ptr()); } void solver_na2as::push() { m_scopes.push_back(m_assumptions.size()); push_core(); } void solver_na2as::pop(unsigned n) { pop_core(n); unsigned lvl = m_scopes.size(); SASSERT(n <= lvl); unsigned new_lvl = lvl - n; restore_assumptions(m_scopes[new_lvl]); m_scopes.shrink(new_lvl); } void solver_na2as::restore_assumptions(unsigned old_sz) { // SASSERT(old_sz == 0); for (unsigned i = old_sz; i < m_assumptions.size(); i++) { m_manager.dec_ref(m_assumptions[i]); } m_assumptions.shrink(old_sz); } unsigned solver_na2as::get_scope_level() const { return m_scopes.size(); } z3-4.4.0/src/solver/combined_solver.cpp0000644000175000017500000002352312540347414017631 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: combined_solver.cpp Abstract: Implements the solver API by combining two solvers. This is a replacement for the strategic_solver class. Author: Leonardo (leonardo) 2012-12-11 Notes: --*/ #include"solver.h" #include"scoped_timer.h" #include"combined_solver_params.hpp" #define PS_VB_LVL 15 /** \brief Implementation of the solver API that combines two given solvers. The combined solver has two modes: - non-incremental - incremental In non-incremental mode, the first solver is used. In incremental mode, the second one is used. A timeout for the second solver can be specified. If the timeout is reached, then the first solver is executed. The object switches to incremental when: - push is used - assertions are peformed after a check_sat - parameter ignore_solver1==false */ class combined_solver : public solver { public: // Behavior when the incremental solver returns unknown. enum inc_unknown_behavior { IUB_RETURN_UNDEF, // just return unknown IUB_USE_TACTIC_IF_QF, // invoke tactic if problem is quantifier free IUB_USE_TACTIC // invoke tactic }; private: bool m_inc_mode; bool m_check_sat_executed; bool m_use_solver1_results; ref m_solver1; ref m_solver2; // We delay sending assertions to solver 2 // This is relevant for big benchmarks that are meant to be solved // by a non-incremental solver. bool m_solver2_initialized; bool m_ignore_solver1; inc_unknown_behavior m_inc_unknown_behavior; unsigned m_inc_timeout; void init_solver2_assertions() { if (m_solver2_initialized) return; unsigned sz = m_solver1->get_num_assertions(); for (unsigned i = 0; i < sz; i++) { m_solver2->assert_expr(m_solver1->get_assertion(i)); } m_solver2_initialized = true; } void switch_inc_mode() { m_inc_mode = true; init_solver2_assertions(); } struct aux_timeout_eh : public event_handler { solver * m_solver; volatile bool m_canceled; aux_timeout_eh(solver * s):m_solver(s), m_canceled(false) {} virtual void operator()() { m_solver->cancel(); m_canceled = true; } }; void updt_local_params(params_ref const & _p) { combined_solver_params p(_p); m_inc_timeout = p.solver2_timeout(); m_ignore_solver1 = p.ignore_solver1(); m_inc_unknown_behavior = static_cast(p.solver2_unknown()); } bool has_quantifiers() const { unsigned sz = get_num_assertions(); for (unsigned i = 0; i < sz; i++) { if (::has_quantifiers(get_assertion(i))) return true; } return false; } bool use_solver1_when_undef() const { switch (m_inc_unknown_behavior) { case IUB_RETURN_UNDEF: return false; case IUB_USE_TACTIC_IF_QF: return !has_quantifiers(); case IUB_USE_TACTIC: return true; default: UNREACHABLE(); return false; } } public: combined_solver(solver * s1, solver * s2, params_ref const & p) { m_solver1 = s1; m_solver2 = s2; updt_local_params(p); m_solver2_initialized = false; m_inc_mode = false; m_check_sat_executed = false; m_use_solver1_results = true; } virtual void updt_params(params_ref const & p) { m_solver1->updt_params(p); m_solver2->updt_params(p); updt_local_params(p); } virtual void collect_param_descrs(param_descrs & r) { m_solver1->collect_param_descrs(r); m_solver2->collect_param_descrs(r); combined_solver_params::collect_param_descrs(r); } virtual void set_produce_models(bool f) { m_solver1->set_produce_models(f); m_solver2->set_produce_models(f); } virtual void assert_expr(expr * t) { if (m_check_sat_executed) switch_inc_mode(); m_solver1->assert_expr(t); if (m_solver2_initialized) m_solver2->assert_expr(t); } virtual void assert_expr(expr * t, expr * a) { if (m_check_sat_executed) switch_inc_mode(); m_solver1->assert_expr(t, a); init_solver2_assertions(); m_solver2->assert_expr(t, a); } virtual void push() { switch_inc_mode(); m_solver1->push(); m_solver2->push(); } virtual void pop(unsigned n) { switch_inc_mode(); m_solver1->pop(n); m_solver2->pop(n); } virtual unsigned get_scope_level() const { return m_solver1->get_scope_level(); } virtual lbool check_sat(unsigned num_assumptions, expr * const * assumptions) { m_check_sat_executed = true; if (get_num_assumptions() != 0 || num_assumptions > 0 || // assumptions were provided m_ignore_solver1) { // must use incremental solver switch_inc_mode(); m_use_solver1_results = false; return m_solver2->check_sat(num_assumptions, assumptions); } if (m_inc_mode) { if (m_inc_timeout == UINT_MAX) { IF_VERBOSE(PS_VB_LVL, verbose_stream() << "(combined-solver \"using solver 2 (without a timeout)\")\n";); lbool r = m_solver2->check_sat(0, 0); if (r != l_undef || !use_solver1_when_undef()) { m_use_solver1_results = false; return r; } } else { IF_VERBOSE(PS_VB_LVL, verbose_stream() << "(combined-solver \"using solver 2 (with timeout)\")\n";); aux_timeout_eh eh(m_solver2.get()); lbool r; { scoped_timer timer(m_inc_timeout, &eh); r = m_solver2->check_sat(0, 0); } if ((r != l_undef || !use_solver1_when_undef()) && !eh.m_canceled) { m_use_solver1_results = false; return r; } } IF_VERBOSE(PS_VB_LVL, verbose_stream() << "(combined-solver \"solver 2 failed, trying solver1\")\n";); } IF_VERBOSE(PS_VB_LVL, verbose_stream() << "(combined-solver \"using solver 1\")\n";); m_use_solver1_results = true; return m_solver1->check_sat(0, 0); } virtual void set_cancel(bool f) { if (f) { m_solver1->cancel(); m_solver2->cancel(); } else { m_solver1->reset_cancel(); m_solver2->reset_cancel(); } } virtual void set_progress_callback(progress_callback * callback) { m_solver1->set_progress_callback(callback); m_solver2->set_progress_callback(callback); } virtual unsigned get_num_assertions() const { return m_solver1->get_num_assertions(); } virtual expr * get_assertion(unsigned idx) const { return m_solver1->get_assertion(idx); } virtual unsigned get_num_assumptions() const { return m_solver1->get_num_assumptions() + m_solver2->get_num_assumptions(); } virtual expr * get_assumption(unsigned idx) const { unsigned c1 = m_solver1->get_num_assumptions(); if (idx < c1) return m_solver1->get_assumption(idx); return m_solver2->get_assumption(idx - c1); } virtual void display(std::ostream & out) const { m_solver1->display(out); } virtual void collect_statistics(statistics & st) const { if (m_use_solver1_results) m_solver1->collect_statistics(st); else m_solver2->collect_statistics(st); } virtual void get_unsat_core(ptr_vector & r) { if (m_use_solver1_results) m_solver1->get_unsat_core(r); else m_solver2->get_unsat_core(r); } virtual void get_model(model_ref & m) { if (m_use_solver1_results) m_solver1->get_model(m); else m_solver2->get_model(m); } virtual proof * get_proof() { if (m_use_solver1_results) return m_solver1->get_proof(); else return m_solver2->get_proof(); } virtual std::string reason_unknown() const { if (m_use_solver1_results) return m_solver1->reason_unknown(); else return m_solver2->reason_unknown(); } virtual void get_labels(svector & r) { if (m_use_solver1_results) return m_solver1->get_labels(r); else return m_solver2->get_labels(r); } }; solver * mk_combined_solver(solver * s1, solver * s2, params_ref const & p) { return alloc(combined_solver, s1, s2, p); } class combined_solver_factory : public solver_factory { scoped_ptr m_f1; scoped_ptr m_f2; public: combined_solver_factory(solver_factory * f1, solver_factory * f2):m_f1(f1), m_f2(f2) {} virtual ~combined_solver_factory() {} virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { return mk_combined_solver((*m_f1)(m, p, proofs_enabled, models_enabled, unsat_core_enabled, logic), (*m_f2)(m, p, proofs_enabled, models_enabled, unsat_core_enabled, logic), p); } }; solver_factory * mk_combined_solver_factory(solver_factory * f1, solver_factory * f2) { return alloc(combined_solver_factory, f1, f2); } z3-4.4.0/src/duality/0000755000175000017500000000000012540347414014107 5ustar michaelmichaelz3-4.4.0/src/duality/duality_profiling.cpp0000755000175000017500000000662212540347414020350 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: duality_profiling.cpp Abstract: collection performance information for duality Author: Ken McMillan (kenmcmil) Revision History: --*/ #include #include #include #include #include #ifdef _WINDOWS #pragma warning(disable:4996) #pragma warning(disable:4800) #pragma warning(disable:4267) #endif #include "duality_wrapper.h" #include "iz3profiling.h" namespace Duality { void show_time(){ output_time(std::cout,current_time()); std::cout << "\n"; } typedef std::map nmap; struct node { std::string name; clock_t time; clock_t start_time; nmap sub; struct node *parent; node(); } top; node::node(){ time = 0; parent = 0; } struct node *current; struct init { init(){ top.name = "TOTAL"; current = ⊤ } } initializer; struct time_entry { clock_t t; time_entry(){t = 0;}; void add(clock_t incr){t += incr;} }; struct ltstr { bool operator()(const char* s1, const char* s2) const { return strcmp(s1, s2) < 0; } }; typedef std::map tmap; static std::ostream *pfs; void print_node(node &top, int indent, tmap &totals){ for(int i = 0; i < indent; i++) (*pfs) << " "; (*pfs) << top.name; int dots = 70 - 2 * indent - top.name.size(); for(int i = 0; i second,indent+1,totals); } void print_profile(std::ostream &os) { pfs = &os; top.time = 0; for(nmap::iterator it = top.sub.begin(); it != top.sub.end(); it++) top.time += it->second.time; tmap totals; print_node(top,0,totals); (*pfs) << "TOTALS:" << std::endl; for(tmap::iterator it = totals.begin(); it != totals.end(); it++){ (*pfs) << (it->first) << " "; output_time(*pfs, it->second.t); (*pfs) << std::endl; } profiling::print(os); // print the interpolation stats } void timer_start(const char *name){ node &child = current->sub[name]; if(child.name.empty()){ // a new node child.parent = current; child.name = name; } child.start_time = current_time(); current = &child; } void timer_stop(const char *name){ if (current->name != name || !current->parent) { #if 0 std::cerr << "imbalanced timer_start and timer_stop"; exit(1); #endif // in case we lost a timer stop due to an exception while (current->name != name && current->parent) current = current->parent; if (current->parent) { current->time += (current_time() - current->start_time); current = current->parent; } return; } current->time += (current_time() - current->start_time); current = current->parent; } } z3-4.4.0/src/duality/duality_wrapper.cpp0000755000175000017500000006115012540347414020034 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: wrapper.cpp Abstract: wrap various objects in the style expected by duality Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifdef _WINDOWS #pragma warning(disable:4996) #pragma warning(disable:4800) #pragma warning(disable:4267) #pragma warning(disable:4101) #endif #include "duality_wrapper.h" #include #include "smt_solver.h" #include "iz3interp.h" #include "statistics.h" #include "expr_abstract.h" #include "stopwatch.h" #include "model_smt2_pp.h" #include "qe_lite.h" namespace Duality { solver::solver(Duality::context& c, bool _extensional, bool models) : object(c), the_model(c) { params_ref p; p.set_bool("proof", true); // this is currently useless if(models) p.set_bool("model", true); p.set_bool("unsat_core", true); bool mbqi = c.get_config().get().get_bool("mbqi",true); p.set_bool("mbqi",mbqi); // just to test p.set_str("mbqi.id","itp"); // use mbqi for quantifiers in interpolants p.set_uint("mbqi.max_iterations",1); // use mbqi for quantifiers in interpolants extensional = mbqi && (true || _extensional); if(extensional) p.set_bool("array.extensional",true); scoped_ptr sf = mk_smt_solver_factory(); m_solver = (*sf)(m(), p, true, true, true, ::symbol::null); m_solver->updt_params(p); // why do we have to do this? canceled = false; m_mode = m().proof_mode(); } expr context::constant(const std::string &name, const sort &ty){ symbol s = str_symbol(name.c_str()); return cook(m().mk_const(m().mk_const_decl(s, ty))); } expr context::make(decl_kind op, int n, ::expr **args){ switch(op) { case True: return mki(m_basic_fid,OP_TRUE,n,args); case False: return mki(m_basic_fid,OP_FALSE,n,args); case Equal: return mki(m_basic_fid,OP_EQ,n,args); case Distinct: return mki(m_basic_fid,OP_DISTINCT,n,args); case Ite: return mki(m_basic_fid,OP_ITE,n,args); case And: return mki(m_basic_fid,OP_AND,n,args); case Or: return mki(m_basic_fid,OP_OR,n,args); case Iff: return mki(m_basic_fid,OP_IFF,n,args); case Xor: return mki(m_basic_fid,OP_XOR,n,args); case Not: return mki(m_basic_fid,OP_NOT,n,args); case Implies: return mki(m_basic_fid,OP_IMPLIES,n,args); case Oeq: return mki(m_basic_fid,OP_OEQ,n,args); case Interp: return mki(m_basic_fid,OP_INTERP,n,args); case Leq: return mki(m_arith_fid,OP_LE,n,args); case Geq: return mki(m_arith_fid,OP_GE,n,args); case Lt: return mki(m_arith_fid,OP_LT,n,args); case Gt: return mki(m_arith_fid,OP_GT,n,args); case Plus: return mki(m_arith_fid,OP_ADD,n,args); case Sub: return mki(m_arith_fid,OP_SUB,n,args); case Uminus: return mki(m_arith_fid,OP_UMINUS,n,args); case Times: return mki(m_arith_fid,OP_MUL,n,args); case Div: return mki(m_arith_fid,OP_DIV,n,args); case Idiv: return mki(m_arith_fid,OP_IDIV,n,args); case Rem: return mki(m_arith_fid,OP_REM,n,args); case Mod: return mki(m_arith_fid,OP_MOD,n,args); case Power: return mki(m_arith_fid,OP_POWER,n,args); case ToReal: return mki(m_arith_fid,OP_TO_REAL,n,args); case ToInt: return mki(m_arith_fid,OP_TO_INT,n,args); case IsInt: return mki(m_arith_fid,OP_IS_INT,n,args); case Store: return mki(m_array_fid,OP_STORE,n,args); case Select: return mki(m_array_fid,OP_SELECT,n,args); case ConstArray: return mki(m_array_fid,OP_CONST_ARRAY,n,args); case ArrayDefault: return mki(m_array_fid,OP_ARRAY_DEFAULT,n,args); case ArrayMap: return mki(m_array_fid,OP_ARRAY_MAP,n,args); case SetUnion: return mki(m_array_fid,OP_SET_UNION,n,args); case SetIntersect: return mki(m_array_fid,OP_SET_INTERSECT,n,args); case SetDifference: return mki(m_array_fid,OP_SET_DIFFERENCE,n,args); case SetComplement: return mki(m_array_fid,OP_SET_COMPLEMENT,n,args); case SetSubSet: return mki(m_array_fid,OP_SET_SUBSET,n,args); case AsArray: return mki(m_array_fid,OP_AS_ARRAY,n,args); default: assert(0); return expr(*this); } } expr context::mki(family_id fid, ::decl_kind dk, int n, ::expr **args){ return cook(m().mk_app(fid, dk, 0, 0, n, (::expr **)args)); } expr context::make(decl_kind op, const std::vector &args){ static std::vector< ::expr*> a(10); if(a.size() < args.size()) a.resize(args.size()); for(unsigned i = 0; i < args.size(); i++) a[i] = to_expr(args[i].raw()); return make(op,args.size(), args.size() ? &a[0] : 0); } expr context::make(decl_kind op){ return make(op,0,0); } expr context::make(decl_kind op, const expr &arg0){ ::expr *a = to_expr(arg0.raw()); return make(op,1,&a); } expr context::make(decl_kind op, const expr &arg0, const expr &arg1){ ::expr *args[2]; args[0] = to_expr(arg0.raw()); args[1] = to_expr(arg1.raw()); return make(op,2,args); } expr context::make(decl_kind op, const expr &arg0, const expr &arg1, const expr &arg2){ ::expr *args[3]; args[0] = to_expr(arg0.raw()); args[1] = to_expr(arg1.raw()); args[2] = to_expr(arg2.raw()); return make(op,3,args); } expr context::make_quant(decl_kind op, const std::vector &bvs, const expr &body){ if(bvs.size() == 0) return body; std::vector< ::expr *> foo(bvs.size()); std::vector< ::symbol> names; std::vector< ::sort *> types; std::vector< ::expr *> bound_asts; unsigned num_bound = bvs.size(); for (unsigned i = 0; i < num_bound; ++i) { app* a = to_app(bvs[i].raw()); ::symbol s(to_app(a)->get_decl()->get_name()); names.push_back(s); types.push_back(m().get_sort(a)); bound_asts.push_back(a); } expr_ref abs_body(m()); expr_abstract(m(), 0, num_bound, &bound_asts[0], to_expr(body.raw()), abs_body); expr_ref result(m()); result = m().mk_quantifier( op == Forall, names.size(), &types[0], &names[0], abs_body.get(), 0, ::symbol(), ::symbol(), 0, 0, 0, 0 ); return cook(result.get()); } expr context::make_quant(decl_kind op, const std::vector &_sorts, const std::vector &_names, const expr &body){ if(_sorts.size() == 0) return body; std::vector< ::symbol> names; std::vector< ::sort *> types; std::vector< ::expr *> bound_asts; unsigned num_bound = _sorts.size(); for (unsigned i = 0; i < num_bound; ++i) { names.push_back(_names[i]); types.push_back(to_sort(_sorts[i].raw())); } expr_ref result(m()); result = m().mk_quantifier( op == Forall, names.size(), &types[0], &names[0], to_expr(body.raw()), 0, ::symbol(), ::symbol(), 0, 0, 0, 0 ); return cook(result.get()); } decl_kind func_decl::get_decl_kind() const { return ctx().get_decl_kind(*this); } decl_kind context::get_decl_kind(const func_decl &t){ ::func_decl *d = to_func_decl(t.raw()); if (null_family_id == d->get_family_id()) return Uninterpreted; // return (opr)d->get_decl_kind(); if (m_basic_fid == d->get_family_id()) { switch(d->get_decl_kind()) { case OP_TRUE: return True; case OP_FALSE: return False; case OP_EQ: return Equal; case OP_DISTINCT: return Distinct; case OP_ITE: return Ite; case OP_AND: return And; case OP_OR: return Or; case OP_IFF: return Iff; case OP_XOR: return Xor; case OP_NOT: return Not; case OP_IMPLIES: return Implies; case OP_OEQ: return Oeq; case OP_INTERP: return Interp; default: return OtherBasic; } } if (m_arith_fid == d->get_family_id()) { switch(d->get_decl_kind()) { case OP_LE: return Leq; case OP_GE: return Geq; case OP_LT: return Lt; case OP_GT: return Gt; case OP_ADD: return Plus; case OP_SUB: return Sub; case OP_UMINUS: return Uminus; case OP_MUL: return Times; case OP_DIV: return Div; case OP_IDIV: return Idiv; case OP_REM: return Rem; case OP_MOD: return Mod; case OP_POWER: return Power; case OP_TO_REAL: return ToReal; case OP_TO_INT: return ToInt; case OP_IS_INT: return IsInt; default: return OtherArith; } } if (m_array_fid == d->get_family_id()) { switch(d->get_decl_kind()) { case OP_STORE: return Store; case OP_SELECT: return Select; case OP_CONST_ARRAY: return ConstArray; case OP_ARRAY_DEFAULT: return ArrayDefault; case OP_ARRAY_MAP: return ArrayMap; case OP_SET_UNION: return SetUnion; case OP_SET_INTERSECT: return SetIntersect; case OP_SET_DIFFERENCE: return SetDifference; case OP_SET_COMPLEMENT: return SetComplement; case OP_SET_SUBSET: return SetSubSet; case OP_AS_ARRAY: return AsArray; default: return OtherArray; } } return Other; } sort_kind context::get_sort_kind(const sort &s){ family_id fid = to_sort(s.raw())->get_family_id(); ::decl_kind k = to_sort(s.raw())->get_decl_kind(); if (m().is_uninterp(to_sort(s.raw()))) { return UninterpretedSort; } else if (fid == m_basic_fid && k == BOOL_SORT) { return BoolSort; } else if (fid == m_arith_fid && k == INT_SORT) { return IntSort; } else if (fid == m_arith_fid && k == REAL_SORT) { return RealSort; } else if (fid == m_array_fid && k == ARRAY_SORT) { return ArraySort; } else { return UnknownSort; } } expr func_decl::operator()(unsigned n, expr const * args) const { std::vector< ::expr *> _args(n); for(unsigned i = 0; i < n; i++) _args[i] = to_expr(args[i].raw()); return ctx().cook(m().mk_app(to_func_decl(raw()),n,&_args[0])); } int solver::get_num_decisions(){ ::statistics st; m_solver->collect_statistics(st); std::ostringstream ss; st.display(ss); std::string stats = ss.str(); int pos = stats.find("decisions:"); if(pos < 0) return 0; // for some reason, decisions are not reported if there are none pos += 10; int end = stats.find('\n',pos); std::string val = stats.substr(pos,end-pos); return atoi(val.c_str()); } void context::print_expr(std::ostream &s, const ast &e){ s << mk_pp(e.raw(), m()); } expr expr::simplify(const params &_p) const { ::expr * a = to_expr(raw()); params_ref p = _p.get(); th_rewriter m_rw(m(), p); expr_ref result(m()); m_rw(a, result); return ctx().cook(result); } expr expr::simplify() const { params p; return simplify(p); } expr context::make_var(int idx, const sort &s){ ::sort * a = to_sort(s.raw()); return cook(m().mk_var(idx,a)); } expr expr::qe_lite() const { ::qe_lite qe(m()); expr_ref result(to_expr(raw()),m()); proof_ref pf(m()); qe(result,pf); return ctx().cook(result); } expr expr::qe_lite(const std::set &idxs, bool index_of_bound) const { ::qe_lite qe(m()); expr_ref result(to_expr(raw()),m()); proof_ref pf(m()); uint_set uis; for(std::set::const_iterator it=idxs.begin(), en = idxs.end(); it != en; ++it) uis.insert(*it); qe(uis,index_of_bound,result); return ctx().cook(result); } expr clone_quantifier(const expr &q, const expr &b){ return q.ctx().cook(q.m().update_quantifier(to_quantifier(q.raw()), to_expr(b.raw()))); } expr clone_quantifier(const expr &q, const expr &b, const std::vector &patterns){ quantifier *thing = to_quantifier(q.raw()); bool is_forall = thing->is_forall(); unsigned num_patterns = patterns.size(); std::vector< ::expr *> _patterns(num_patterns); for(unsigned i = 0; i < num_patterns; i++) _patterns[i] = to_expr(patterns[i].raw()); return q.ctx().cook(q.m().update_quantifier(thing, is_forall, num_patterns, &_patterns[0], to_expr(b.raw()))); } expr clone_quantifier(decl_kind dk, const expr &q, const expr &b){ quantifier *thing = to_quantifier(q.raw()); bool is_forall = dk == Forall; return q.ctx().cook(q.m().update_quantifier(thing, is_forall, to_expr(b.raw()))); } void expr::get_patterns(std::vector &pats) const { quantifier *thing = to_quantifier(raw()); unsigned num_patterns = thing->get_num_patterns(); :: expr * const *it = thing->get_patterns(); pats.resize(num_patterns); for(unsigned i = 0; i < num_patterns; i++) pats[i] = expr(ctx(),it[i]); } unsigned func_decl::arity() const { return (to_func_decl(raw())->get_arity()); } sort func_decl::domain(unsigned i) const { return sort(ctx(),(to_func_decl(raw())->get_domain(i))); } sort func_decl::range() const { return sort(ctx(),(to_func_decl(raw())->get_range())); } func_decl context::fresh_func_decl(char const * prefix, const std::vector &domain, sort const & range){ std::vector < ::sort * > _domain(domain.size()); for(unsigned i = 0; i < domain.size(); i++) _domain[i] = to_sort(domain[i].raw()); ::func_decl* d = m().mk_fresh_func_decl(prefix, _domain.size(), &_domain[0], to_sort(range.raw())); return func_decl(*this,d); } func_decl context::fresh_func_decl(char const * prefix, sort const & range){ ::func_decl* d = m().mk_fresh_func_decl(prefix, 0, 0, to_sort(range.raw())); return func_decl(*this,d); } #if 0 lbool interpolating_solver::interpolate( const std::vector &assumptions, std::vector &interpolants, model &model, Z3_literals &labels, bool incremental) { Z3_model _model = 0; Z3_literals _labels = 0; Z3_lbool lb; std::vector _assumptions(assumptions.size()); std::vector _interpolants(assumptions.size()-1); for(unsigned i = 0; i < assumptions.size(); i++) _assumptions[i] = assumptions[i]; std::vector _theory(theory.size()); for(unsigned i = 0; i < theory.size(); i++) _theory[i] = theory[i]; lb = Z3_interpolate( ctx(), _assumptions.size(), &_assumptions[0], 0, 0, &_interpolants[0], &_model, &_labels, incremental, _theory.size(), &_theory[0] ); if(lb == Z3_L_FALSE){ interpolants.resize(_interpolants.size()); for (unsigned i = 0; i < _interpolants.size(); ++i) { interpolants[i] = expr(ctx(),_interpolants[i]); } } if (_model) { model = iz3wrapper::model(ctx(), _model); } if(_labels){ labels = _labels; } return lb; } #endif static int linearize_assumptions(int num, TermTree *assumptions, std::vector > &linear_assumptions, std::vector &parents){ for(unsigned i = 0; i < assumptions->getChildren().size(); i++) num = linearize_assumptions(num, assumptions->getChildren()[i], linear_assumptions, parents); // linear_assumptions[num].push_back(assumptions->getTerm()); for(unsigned i = 0; i < assumptions->getChildren().size(); i++) parents[assumptions->getChildren()[i]->getNumber()] = num; parents[num] = SHRT_MAX; // in case we have no parent linear_assumptions[num].push_back(assumptions->getTerm()); std::vector &ts = assumptions->getTerms(); for(unsigned i = 0; i < ts.size(); i++) linear_assumptions[num].push_back(ts[i]); return num + 1; } static int unlinearize_interpolants(int num, TermTree* assumptions, const std::vector &interpolant, TermTree * &tree_interpolant) { std::vector chs(assumptions->getChildren().size()); for(unsigned i = 0; i < assumptions->getChildren().size(); i++) num = unlinearize_interpolants(num, assumptions->getChildren()[i], interpolant,chs[i]); expr f; if(num < (int)interpolant.size()) // last interpolant is missing, presumed false f = interpolant[num]; tree_interpolant = new TermTree(f,chs); return num + 1; } lbool interpolating_solver::interpolate_tree(TermTree *assumptions, TermTree *&interpolant, model &model, literals &labels, bool incremental ) { int size = assumptions->number(0); std::vector > linear_assumptions(size); std::vector parents(size); linearize_assumptions(0,assumptions,linear_assumptions,parents); ptr_vector< ::ast> _interpolants(size-1); vector >_assumptions(size); for(int i = 0; i < size; i++) for(unsigned j = 0; j < linear_assumptions[i].size(); j++) _assumptions[i].push_back(linear_assumptions[i][j]); ::vector _parents; _parents.resize(parents.size()); for(unsigned i = 0; i < parents.size(); i++) _parents[i] = parents[i]; ptr_vector< ::ast> _theory(theory.size()); for(unsigned i = 0; i < theory.size(); i++) _theory[i] = theory[i]; if(!incremental){ push(); for(unsigned i = 0; i < linear_assumptions.size(); i++) for(unsigned j = 0; j < linear_assumptions[i].size(); j++) add(linear_assumptions[i][j]); } check_result res = unsat; if(!m_solver->get_proof()) res = check(); if(res == unsat){ interpolation_options_struct opts; if(weak_mode) opts.set("weak","1"); ::ast *proof = m_solver->get_proof(); iz3interpolate(m(),proof,_assumptions,_parents,_interpolants,_theory,&opts); std::vector linearized_interpolants(_interpolants.size()); for(unsigned i = 0; i < _interpolants.size(); i++) linearized_interpolants[i] = expr(ctx(),_interpolants[i]); // since iz3interpolant returns interpolants with one ref count, we decrement here for(unsigned i = 0; i < _interpolants.size(); i++) m().dec_ref(_interpolants[i]); unlinearize_interpolants(0,assumptions,linearized_interpolants,interpolant); interpolant->setTerm(ctx().bool_val(false)); } model_ref _m; m_solver->get_model(_m); model = Duality::model(ctx(),_m.get()); #if 0 if(_labels){ labels = _labels; } #endif if(!incremental) pop(); return (res == unsat) ? l_false : ((res == sat) ? l_true : l_undef); } void interpolating_solver::SetWeakInterpolants(bool weak){ weak_mode = weak; } void interpolating_solver::SetPrintToFile(const std::string &filename){ print_filename = filename; } void interpolating_solver::AssertInterpolationAxiom(const expr & t){ add(t); theory.push_back(t); } void interpolating_solver::RemoveInterpolationAxiom(const expr & t){ // theory.remove(t); } const char *interpolating_solver::profile(){ // return Z3_interpolation_profile(ctx()); return ""; } static void get_assumptions_rec(stl_ext::hash_set &memo, const proof &pf, std::vector &assumps){ if(memo.find(pf) != memo.end())return; memo.insert(pf); pfrule dk = pf.rule(); if(dk == PR_ASSERTED){ expr con = pf.conc(); assumps.push_back(con); } else { unsigned nprems = pf.num_prems(); for(unsigned i = 0; i < nprems; i++){ proof arg = pf.prem(i); get_assumptions_rec(memo,arg,assumps); } } } void proof::get_assumptions(std::vector &assumps){ stl_ext::hash_set memo; get_assumptions_rec(memo,*this,assumps); } void ast::show() const{ std::cout << mk_pp(raw(), m()) << std::endl; } void model::show() const { model_smt2_pp(std::cout, m(), *m_model, 0); std::cout << std::endl; } void model::show_hash() const { std::ostringstream ss; model_smt2_pp(ss, m(), *m_model, 0); hash_space::hash hasher; unsigned h = hasher(ss.str()); std::cout << "model hash: " << h << "\n"; } void solver::show() { unsigned n = m_solver->get_num_assertions(); if(!n) return; ast_smt_pp pp(m()); for (unsigned i = 0; i < n-1; ++i) pp.add_assumption(m_solver->get_assertion(i)); pp.display_smt2(std::cout, m_solver->get_assertion(n-1)); } void solver::print(const char *filename) { std::ofstream f(filename); unsigned n = m_solver->get_num_assertions(); if(!n) return; ast_smt_pp pp(m()); for (unsigned i = 0; i < n-1; ++i) pp.add_assumption(m_solver->get_assertion(i)); pp.display_smt2(f, m_solver->get_assertion(n-1)); } void solver::show_assertion_ids() { #if 0 unsigned n = m_solver->get_num_assertions(); std::cerr << "assertion ids: "; for (unsigned i = 0; i < n-1; ++i) std::cerr << " " << m_solver->get_assertion(i)->get_id(); std::cerr << "\n"; #else unsigned n = m_solver->get_num_assertions(); std::cerr << "assertion ids hash: "; unsigned h = 0; for (unsigned i = 0; i < n-1; ++i) h += m_solver->get_assertion(i)->get_id(); std::cerr << h << "\n"; #endif } void include_ast_show(ast &a){ a.show(); } void include_model_show(model &a){ a.show(); } void show_ast(::ast *a, ast_manager &m) { std::cout << mk_pp(a, m) << std::endl; } bool expr::is_label (bool &pos,std::vector &names) const { buffer< ::symbol> _names; bool res = m().is_label(to_expr(raw()),pos,_names); if(res) for(unsigned i = 0; i < _names.size(); i++) names.push_back(symbol(ctx(),_names[i])); return res; } double current_time() { static stopwatch sw; static bool started = false; if(!started){ sw.start(); started = true; } return sw.get_current_seconds(); } } z3-4.4.0/src/duality/duality.h0000644000175000017500000013501112540347414015734 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: duality.h Abstract: main header for duality Author: Ken McMillan (kenmcmil) Revision History: --*/ #pragma once #include "duality_wrapper.h" #include #include // make hash_map and hash_set available using namespace stl_ext; namespace Duality { struct implicant_solver; /* Generic operations on Z3 formulas */ struct Z3User { context &ctx; typedef func_decl FuncDecl; typedef expr Term; Z3User(context &_ctx) : ctx(_ctx){} const char *string_of_int(int n); Term conjoin(const std::vector &args); Term sum(const std::vector &args); Term CloneQuantifier(const Term &t, const Term &new_body); Term SubstRec(hash_map &memo, const Term &t); Term SubstRec(hash_map &memo, hash_map &map, const Term &t); void Strengthen(Term &x, const Term &y); // return the func_del of an app if it is uninterpreted bool get_relation(const Term &t, func_decl &R); // return true if term is an individual variable // TODO: have to check that it is not a background symbol bool is_variable(const Term &t); FuncDecl SuffixFuncDecl(Term t, int n); Term SubstRecHide(hash_map &memo, const Term &t, int number); void CollectConjuncts(const Term &f, std::vector &lits, bool pos = true); void SortTerms(std::vector &terms); Term SortSum(const Term &t); void Summarize(const Term &t); int CountOperators(const Term &t); Term SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val); Term CloneQuantAndSimp(const expr &t, const expr &body); Term RemoveRedundancy(const Term &t); Term IneqToEq(const Term &t); bool IsLiteral(const expr &lit, expr &atom, expr &val); expr Negate(const expr &f); expr SimplifyAndOr(const std::vector &args, bool is_and); expr ReallySimplifyAndOr(const std::vector &args, bool is_and); int MaxIndex(hash_map &memo, const Term &t); bool IsClosedFormula(const Term &t); Term AdjustQuantifiers(const Term &t); FuncDecl RenumberPred(const FuncDecl &f, int n); FuncDecl NumberPred(const FuncDecl &f, int n); Term ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming); protected: void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); int CountOperatorsRec(hash_set &memo, const Term &t); void RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo); Term RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t); Term SubstAtomTriv(const expr &foo, const expr &atom, const expr &val); expr ReduceAndOr(const std::vector &args, bool is_and, std::vector &res); expr FinishAndOr(const std::vector &args, bool is_and); expr PullCommonFactors(std::vector &args, bool is_and); Term IneqToEqRec(hash_map &memo, const Term &t); Term CloneQuantAndSimp(const expr &t, const expr &body, bool is_forall); Term PushQuantifier(const expr &t, const expr &body, bool is_forall); void CollectJuncts(const Term &f, std::vector &lits, decl_kind op, bool negate); Term DeleteBoundRec(hash_map > &memo, int level, int num, const Term &t); Term DeleteBound(int level, int num, const Term &t); }; /** This class represents a relation post-fixed point (RPFP) problem as * a "problem graph". The graph consists of Nodes and hyper-edges. * * A node consists of * - Annotation, a symbolic relation * - Bound, a symbolic relation giving an upper bound on Annotation * * * A hyper-edge consists of: * - Children, a sequence of children Nodes, * - F, a symbolic relational transformer, * - Parent, a single parent Node. * * The graph is "solved" when: * - For every Node n, n.Annotation subseteq n.Bound * - For every hyperedge e, e.F(e.Children.Annotation) subseteq e.Parent.Annotation * * where, if x is a sequence of Nodes, x.Annotation is the sequences * of Annotations of the nodes in the sequence. * * A symbolic Transformer consists of * - RelParams, a sequence of relational symbols * - IndParams, a sequence of individual symbols * - Formula, a formula over RelParams and IndParams * * A Transformer t represents a function that takes sequence R of relations * and yields the relation lambda (t.Indparams). Formula(R/RelParams). * * As a special case, a nullary Transformer (where RelParams is the empty sequence) * represents a fixed relation. * * An RPFP consists of * - Nodes, a set of Nodes * - Edges, a set of hyper-edges * - Context, a prover context that contains formula AST's * * Multiple RPFP's can use the same Context, but you should be careful * that only one RPFP asserts constraints in the context at any time. * * */ class RPFP : public Z3User { public: class Edge; class Node; bool HornClauses; /** Interface class for interpolating solver. */ class LogicSolver { public: context *ctx; /** Z3 context for formulas */ solver *slvr; /** Z3 solver */ bool need_goals; /** Can the solver use the goal tree to optimize interpolants? */ solver aux_solver; /** For temporary use -- don't leave assertions here. */ /** Tree interpolation. This method assumes the formulas in TermTree "assumptions" are currently asserted in the solver. The return value indicates whether the assertions are satisfiable. In the UNSAT case, a tree interpolant is returned in "interpolants". In the SAT case, a model is returned. */ virtual lbool interpolate_tree(TermTree *assumptions, TermTree *&interpolants, model &_model, TermTree *goals = 0, bool weak = false ) = 0; /** Declare a constant in the background theory. */ virtual void declare_constant(const func_decl &f) = 0; /** Is this a background constant? */ virtual bool is_constant(const func_decl &f) = 0; /** Get the constants in the background vocabulary */ virtual hash_set &get_constants() = 0; /** Assert a background axiom. */ virtual void assert_axiom(const expr &axiom) = 0; /** Get the background axioms. */ virtual const std::vector &get_axioms() = 0; /** Return a string describing performance. */ virtual std::string profile() = 0; virtual void write_interpolation_problem(const std::string &file_name, const std::vector &assumptions, const std::vector &theory ){} /** Cancel, throw Canceled object if possible. */ virtual void cancel(){ } /* Note: aux solver uses extensional array theory, since it needs to be able to produce counter-models for interpolants the have array equalities in them. */ LogicSolver(context &c) : aux_solver(c,true){} virtual ~LogicSolver(){} }; /** This solver uses iZ3. */ class iZ3LogicSolver : public LogicSolver { public: interpolating_context *ictx; /** iZ3 context for formulas */ interpolating_solver *islvr; /** iZ3 solver */ lbool interpolate_tree(TermTree *assumptions, TermTree *&interpolants, model &_model, TermTree *goals = 0, bool weak = false) { literals _labels; islvr->SetWeakInterpolants(weak); return islvr->interpolate_tree(assumptions,interpolants,_model,_labels,true); } void assert_axiom(const expr &axiom){ #if 1 // HACK: large "distict" predicates can kill the legacy SMT solver. // encode them with a UIF if(axiom.is_app() && axiom.decl().get_decl_kind() == Distinct) if(axiom.num_args() > 10){ sort s = axiom.arg(0).get_sort(); std::vector sv; sv.push_back(s); int nargs = axiom.num_args(); std::vector args(nargs); func_decl f = ctx->fresh_func_decl("@distinct",sv,ctx->int_sort()); for(int i = 0; i < nargs; i++){ expr a = axiom.arg(i); expr new_cnstr = f(a) == ctx->int_val(i); args[i] = new_cnstr; } expr cnstr = ctx->make(And,args); islvr->AssertInterpolationAxiom(cnstr); return; } #endif islvr->AssertInterpolationAxiom(axiom); } const std::vector &get_axioms() { return islvr->GetInterpolationAxioms(); } std::string profile(){ return islvr->profile(); } #if 0 iZ3LogicSolver(config &_config){ ctx = ictx = new interpolating_context(_config); slvr = islvr = new interpolating_solver(*ictx); need_goals = false; islvr->SetWeakInterpolants(true); } #endif iZ3LogicSolver(context &c, bool models = true) : LogicSolver(c) { ctx = ictx = &c; slvr = islvr = new interpolating_solver(*ictx, models); need_goals = false; islvr->SetWeakInterpolants(true); } void write_interpolation_problem(const std::string &file_name, const std::vector &assumptions, const std::vector &theory ){ #if 0 islvr->write_interpolation_problem(file_name,assumptions,theory); #endif } void cancel(){islvr->cancel();} /** Declare a constant in the background theory. */ virtual void declare_constant(const func_decl &f){ bckg.insert(f); } /** Is this a background constant? */ virtual bool is_constant(const func_decl &f){ return bckg.find(f) != bckg.end(); } /** Get the constants in the background vocabulary */ virtual hash_set &get_constants(){ return bckg; } ~iZ3LogicSolver(){ // delete ictx; delete islvr; } private: hash_set bckg; }; #if 0 /** Create a logic solver from a Z3 configuration. */ static iZ3LogicSolver *CreateLogicSolver(config &_config){ return new iZ3LogicSolver(_config); } #endif /** Create a logic solver from a low-level Z3 context. Only use this if you know what you're doing. */ static iZ3LogicSolver *CreateLogicSolver(context c){ return new iZ3LogicSolver(c); } LogicSolver *ls; protected: int nodeCount; int edgeCount; class stack_entry { public: std::list edges; std::list nodes; std::list > constraints; }; public: model dualModel; protected: literals dualLabels; std::list stack; std::vector axioms; // only saved here for printing purposes solver &aux_solver; hash_set *proof_core; public: /** Construct an RPFP graph with a given interpolating prover context. It is allowed to have multiple RPFP's use the same context, but you should never have teo RPFP's with the same conext asserting nodes or edges at the same time. Note, if you create axioms in one RPFP, them create a second RPFP with the same context, the second will inherit the axioms. */ RPFP(LogicSolver *_ls) : Z3User(*(_ls->ctx)), dualModel(*(_ls->ctx)), aux_solver(_ls->aux_solver) { ls = _ls; nodeCount = 0; edgeCount = 0; stack.push_back(stack_entry()); HornClauses = false; proof_core = 0; } virtual ~RPFP(); /** Symbolic representation of a relational transformer */ class Transformer { public: std::vector RelParams; std::vector IndParams; Term Formula; RPFP *owner; hash_map labels; Transformer *Clone() { return new Transformer(*this); } void SetEmpty(){ Formula = owner->ctx.bool_val(false); } void SetFull(){ Formula = owner->ctx.bool_val(true); } bool IsEmpty(){ return eq(Formula,owner->ctx.bool_val(false)); } bool IsFull(){ return eq(Formula,owner->ctx.bool_val(true)); } void UnionWith(const Transformer &other){ Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); Formula = Formula || t; } void IntersectWith(const Transformer &other){ Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); Formula = Formula && t; } bool SubsetEq(const Transformer &other){ Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); expr test = Formula && !t; owner->aux_solver.push(); owner->aux_solver.add(test); check_result res = owner->aux_solver.check(); owner->aux_solver.pop(1); return res == unsat; } void Complement(){ Formula = !Formula; } void Simplify(){ Formula = Formula.simplify(); } Transformer(const std::vector &_RelParams, const std::vector &_IndParams, const Term &_Formula, RPFP *_owner) : RelParams(_RelParams), IndParams(_IndParams), Formula(_Formula) {owner = _owner;} }; /** Create a symbolic transformer. */ Transformer CreateTransformer(const std::vector &_RelParams, const std::vector &_IndParams, const Term &_Formula) { // var ops = new Util.ContextOps(ctx); // var foo = ops.simplify_lhs(_Formula); // t.Formula = foo.Item1; // t.labels = foo.Item2; return Transformer(_RelParams,_IndParams,_Formula,this); } /** Create a relation (nullary relational transformer) */ Transformer CreateRelation(const std::vector &_IndParams, const Term &_Formula) { return CreateTransformer(std::vector(), _IndParams, _Formula); } /** A node in the RPFP graph */ class Node { public: FuncDecl Name; Transformer Annotation; Transformer Bound; Transformer Underapprox; RPFP *owner; int number; Edge *Outgoing; std::vector Incoming; Term dual; Node *map; unsigned recursion_bound; Node(const FuncDecl &_Name, const Transformer &_Annotation, const Transformer &_Bound, const Transformer &_Underapprox, const Term &_dual, RPFP *_owner, int _number) : Name(_Name), Annotation(_Annotation), Bound(_Bound), Underapprox(_Underapprox), dual(_dual) {owner = _owner; number = _number; Outgoing = 0; recursion_bound = UINT_MAX;} }; /** Create a node in the graph. The input is a term R(v_1...v_n) * where R is an arbitrary relational symbol and v_1...v_n are * arbitary distinct variables. The names are only of mnemonic value, * however, the number and type of arguments determine the type * of the relation at this node. */ Node *CreateNode(const Term &t) { std::vector _IndParams; int nargs = t.num_args(); for(int i = 0; i < nargs; i++) _IndParams.push_back(t.arg(i)); Node *n = new Node(t.decl(), CreateRelation(_IndParams,ctx.bool_val(true)), CreateRelation(_IndParams,ctx.bool_val(true)), CreateRelation(_IndParams,ctx.bool_val(false)), expr(ctx), this, ++nodeCount ); nodes.push_back(n); return n; } /** Clone a node (can be from another graph). */ Node *CloneNode(Node *old) { Node *n = new Node(old->Name, old->Annotation, old->Bound, old->Underapprox, expr(ctx), this, ++nodeCount ); nodes.push_back(n); n->map = old; return n; } /** Delete a node. You can only do this if not connected to any edges.*/ void DeleteNode(Node *node){ if(node->Outgoing || !node->Incoming.empty()) throw "cannot delete RPFP node"; for(std::vector::iterator it = nodes.end(), en = nodes.begin(); it != en;){ if(*(--it) == node){ nodes.erase(it); break; } } delete node; } /** This class represents a hyper-edge in the RPFP graph */ class Edge { public: Transformer F; Node *Parent; std::vector Children; RPFP *owner; int number; // these should be internal... Term dual; hash_map relMap; hash_map varMap; Edge *map; Term labeled; std::vector constraints; Edge(Node *_Parent, const Transformer &_F, const std::vector &_Children, RPFP *_owner, int _number) : F(_F), Parent(_Parent), Children(_Children), dual(expr(_owner->ctx)) { owner = _owner; number = _number; } }; /** Create a hyper-edge. */ Edge *CreateEdge(Node *_Parent, const Transformer &_F, const std::vector &_Children) { Edge *e = new Edge(_Parent,_F,_Children,this,++edgeCount); _Parent->Outgoing = e; for(unsigned i = 0; i < _Children.size(); i++) _Children[i]->Incoming.push_back(e); edges.push_back(e); return e; } /** Delete a hyper-edge and unlink it from any nodes. */ void DeleteEdge(Edge *edge){ if(edge->Parent) edge->Parent->Outgoing = 0; for(unsigned int i = 0; i < edge->Children.size(); i++){ std::vector &ic = edge->Children[i]->Incoming; for(std::vector::iterator it = ic.begin(), en = ic.end(); it != en; ++it){ if(*it == edge){ ic.erase(it); break; } } } for(std::vector::iterator it = edges.end(), en = edges.begin(); it != en;){ if(*(--it) == edge){ edges.erase(it); break; } } delete edge; } /** Create an edge that lower-bounds its parent. */ Edge *CreateLowerBoundEdge(Node *_Parent) { return CreateEdge(_Parent, _Parent->Annotation, std::vector()); } /** For incremental solving, asserts the constraint associated * with this edge in the SMT context. If this edge is removed, * you must pop the context accordingly. The second argument is * the number of pushes we are inside. */ virtual void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); /* Constrain an edge by the annotation of one of its children. */ void ConstrainParent(Edge *parent, Node *child); /** For incremental solving, asserts the negation of the upper bound associated * with a node. * */ void AssertNode(Node *n); /** Assert a constraint on an edge in the SMT context. */ void ConstrainEdge(Edge *e, const Term &t); /** Fix the truth values of atomic propositions in the given edge to their values in the current assignment. */ void FixCurrentState(Edge *root); void FixCurrentStateFull(Edge *edge, const expr &extra); void FixCurrentStateFull(Edge *edge, const std::vector &assumps, const hash_map &renaming); /** Declare a constant in the background theory. */ void DeclareConstant(const FuncDecl &f); /** Assert a background axiom. Background axioms can be used to provide the * theory of auxilliary functions or relations. All symbols appearing in * background axioms are considered global, and may appear in both transformer * and relational solutions. Semantically, a solution to the RPFP gives * an interpretation of the unknown relations for each interpretation of the * auxilliary symbols that is consistent with the axioms. Axioms should be * asserted before any calls to Push. They cannot be de-asserted by Pop. */ void AssertAxiom(const Term &t); #if 0 /** Do not call this. */ void RemoveAxiom(const Term &t); #endif /** Solve an RPFP graph. This means either strengthen the annotation * so that the bound at the given root node is satisfied, or * show that this cannot be done by giving a dual solution * (i.e., a counterexample). * * In the current implementation, this only works for graphs that * are: * - tree-like * * - closed. * * In a tree-like graph, every nod has out most one incoming and one out-going edge, * and there are no cycles. In a closed graph, every node has exactly one out-going * edge. This means that the leaves of the tree are all hyper-edges with no * children. Such an edge represents a relation (nullary transformer) and thus * a lower bound on its parent. The parameter root must be the root of this tree. * * If Solve returns LBool.False, this indicates success. The annotation of the tree * has been updated to satisfy the upper bound at the root. * * If Solve returns LBool.True, this indicates a counterexample. For each edge, * you can then call Eval to determine the values of symbols in the transformer formula. * You can also call Empty on a node to determine if its value in the counterexample * is the empty relation. * * \param root The root of the tree * \param persist Number of context pops through which result should persist * * */ lbool Solve(Node *root, int persist); /** Same as Solve, but annotates only a single node. */ lbool SolveSingleNode(Node *root, Node *node); /** Get the constraint tree (but don't solve it) */ TermTree *GetConstraintTree(Node *root, Node *skip_descendant = 0); /** Dispose of the dual model (counterexample) if there is one. */ void DisposeDualModel(); /** Check satisfiability of asserted edges and nodes. Same functionality as * Solve, except no primal solution (interpolant) is generated in the unsat case. */ check_result Check(Node *root, std::vector underapproxes = std::vector(), std::vector *underapprox_core = 0); /** Update the model, attempting to make the propositional literals in assumps true. If possible, return sat, else return unsat and keep the old model. */ check_result CheckUpdateModel(Node *root, std::vector assumps); /** Determines the value in the counterexample of a symbol occuring in the transformer formula of * a given edge. */ Term Eval(Edge *e, Term t); /** Return the fact derived at node p in a counterexample. */ Term EvalNode(Node *p); /** Returns true if the given node is empty in the primal solution. For proecudure summaries, this means that the procedure is not called in the current counter-model. */ bool Empty(Node *p); /** Compute an underapproximation of every node in a tree rooted at "root", based on a previously computed counterexample. */ Term ComputeUnderapprox(Node *root, int persist); /** Try to strengthen the annotation of a node by removing disjuncts. */ void Generalize(Node *root, Node *node); /** Compute disjunctive interpolant for node by case splitting */ void InterpolateByCases(Node *root, Node *node); /** Push a scope. Assertions made after Push can be undone by Pop. */ void Push(); /** Exception thrown when bad clause is encountered */ struct bad_clause { std::string msg; int i; bad_clause(const std::string &_msg, int _i){ msg = _msg; i = _i; } }; struct parse_error { std::string msg; parse_error(const std::string &_msg){ msg = _msg; } }; struct file_not_found { }; struct bad_format { }; // thrown on internal error struct Bad { }; // thrown on more serious internal error struct ReallyBad { }; /** Pop a scope (see Push). Note, you cannot pop axioms. */ void Pop(int num_scopes); /** Erase the proof by performing a Pop, Push and re-assertion of all the popped constraints */ void PopPush(); /** Return true if the given edge is used in the proof of unsat. Can be called only after Solve or Check returns an unsat result. */ bool EdgeUsedInProof(Edge *edge); /** Convert a collection of clauses to Nodes and Edges in the RPFP. Predicate unknowns are uninterpreted predicates not occurring in the background theory. Clauses are of the form B => P(t_1,...,t_k) where P is a predicate unknown and predicate unknowns occur only positivey in H and only under existential quantifiers in prenex form. Each predicate unknown maps to a node. Each clause maps to an edge. Let C be a clause B => P(t_1,...,t_k) where the sequence of predicate unknowns occurring in B (in order of occurrence) is P_1..P_n. The clause maps to a transformer T where: T.Relparams = P_1..P_n T.Indparams = x_1...x+k T.Formula = B /\ t_1 = x_1 /\ ... /\ t_k = x_k Throws exception bad_clause(msg,i) if a clause i is in the wrong form. */ struct label_struct { symbol name; expr value; bool pos; label_struct(const symbol &s, const expr &e, bool b) : name(s), value(e), pos(b) {} }; #ifdef _WINDOWS __declspec(dllexport) #endif void FromClauses(const std::vector &clauses, const std::vector *bounds = 0); void FromFixpointContext(fixedpoint fp, std::vector &queries); void WriteSolution(std::ostream &s); void WriteCounterexample(std::ostream &s, Node *node); enum FileFormat {DualityFormat, SMT2Format, HornFormat}; /** Write the RPFP to a file (currently in SMTLIB 1.2 format) */ void WriteProblemToFile(std::string filename, FileFormat format = DualityFormat); /** Read the RPFP from a file (in specificed format) */ void ReadProblemFromFile(std::string filename, FileFormat format = DualityFormat); /** Translate problem to Horn clause form */ void ToClauses(std::vector &cnsts, FileFormat format = DualityFormat); /** Translate the RPFP to a fixed point context, with queries */ fixedpoint ToFixedPointProblem(std::vector &queries); /** Nodes of the graph. */ std::vector nodes; /** Edges of the graph. */ std::vector edges; /** Fuse a vector of transformers. If the total number of inputs of the transformers is N, then the result is an N-ary transfomer whose output is the union of the outputs of the given transformers. The is, suppose we have a vetor of transfoermers {T_i(r_i1,...,r_iN(i) : i=1..M}. The the result is a transformer F(r_11,...,r_iN(1),...,r_M1,...,r_MN(M)) = T_1(r_11,...,r_iN(1)) U ... U T_M(r_M1,...,r_MN(M)) */ Transformer Fuse(const std::vector &trs); /** Fuse edges so that each node is the output of at most one edge. This transformation is solution-preserving, but changes the numbering of edges in counterexamples. */ void FuseEdges(); void RemoveDeadNodes(); Term SubstParams(const std::vector &from, const std::vector &to, const Term &t); Term SubstParamsNoCapture(const std::vector &from, const std::vector &to, const Term &t); Term Localize(Edge *e, const Term &t); void EvalNodeAsConstraint(Node *p, Transformer &res); TermTree *GetGoalTree(Node *root); int EvalTruth(hash_map &memo, Edge *e, const Term &f); void GetLabels(Edge *e, std::vector &labels); // int GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos); /** Compute and save the proof core for future calls to EdgeUsedInProof. You only need to call this if you will pop the solver before calling EdgeUsedInProof. */ void ComputeProofCore(); int CumulativeDecisions(); void GreedyReduceNodes(std::vector &nodes); check_result CheckWithConstrainedNodes(std::vector &posnodes,std::vector &negnodes); solver &slvr(){ return *ls->slvr; } protected: void ClearProofCore(){ if(proof_core) delete proof_core; proof_core = 0; } Term SuffixVariable(const Term &t, int n); Term HideVariable(const Term &t, int n); void RedVars(Node *node, Term &b, std::vector &v); Term RedDualRela(Edge *e, std::vector &args, int idx); Term LocalizeRec(Edge *e, hash_map &memo, const Term &t); void SetEdgeMaps(Edge *e); Term ReducedDualEdge(Edge *e); TermTree *ToTermTree(Node *root, Node *skip_descendant = 0); TermTree *ToGoalTree(Node *root); void CollapseTermTreeRec(TermTree *root, TermTree *node); TermTree *CollapseTermTree(TermTree *node); void DecodeTree(Node *root, TermTree *interp, int persist); Term GetUpperBound(Node *n); TermTree *AddUpperBound(Node *root, TermTree *t); #if 0 void WriteInterps(System.IO.StreamWriter f, TermTree t); #endif void WriteEdgeVars(Edge *e, hash_map &memo, const Term &t, std::ostream &s); void WriteEdgeAssignment(std::ostream &s, Edge *e); // Scan the clause body for occurrences of the predicate unknowns Term ScanBody(hash_map &memo, const Term &t, hash_map &pmap, std::vector &res, std::vector &nodes); Term RemoveLabelsRec(hash_map &memo, const Term &t, std::vector &lbls); Term RemoveLabels(const Term &t, std::vector &lbls); Term GetAnnotation(Node *n); Term GetUnderapprox(Node *n); Term UnderapproxFlag(Node *n); hash_map underapprox_flag_rev; Node *UnderapproxFlagRev(const Term &flag); Term ProjectFormula(std::vector &keep_vec, const Term &f); int SubtermTruth(hash_map &memo, const Term &); void ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, hash_set *done, bool truth, hash_set &dont_cares); void Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares); Term UnderapproxFormula(const Term &f, hash_set &dont_cares); void ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, hash_set &done, hash_set &dont_cares, bool extensional = true); public: Term UnderapproxFullFormula(const Term &f, bool extensional = true); protected: Term ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants); hash_map resolve_ite_memo; Term ResolveIte(hash_map &memo, const Term &t, std::vector &lits, hash_set *done, hash_set &dont_cares); struct ArrayValue { bool defined; std::map entries; expr def_val; }; void EvalArrayTerm(const Term &t, ArrayValue &res); Term EvalArrayEquality(const Term &f); Term ModelValueAsConstraint(const Term &t); void GetLabelsRec(hash_map &memo, const Term &f, std::vector &labels, hash_set *done, bool truth); Term SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t); Term SubstBound(hash_map &subst, const Term &t); void ConstrainEdgeLocalized(Edge *e, const Term &t); void GreedyReduce(solver &s, std::vector &conjuncts); void NegateLits(std::vector &lits); expr SimplifyOr(std::vector &lits); expr SimplifyAnd(std::vector &lits); void SetAnnotation(Node *root, const expr &t); void AddEdgeToSolver(Edge *edge); void AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge); void AddToProofCore(hash_set &core); void GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under); Term StrengthenFormulaByCaseSplitting(const Term &f, std::vector &case_lits); expr NegateLit(const expr &f); expr GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox); bool IsVar(const expr &t); void GetVarsRec(hash_set &memo, const expr &cnst, std::vector &vars); expr UnhoistPullRec(hash_map & memo, const expr &w, hash_map & init_defs, hash_map & const_params, hash_map &const_params_inv, std::vector &new_params); void AddParamsToTransformer(Transformer &trans, const std::vector ¶ms); expr AddParamsToApp(const expr &app, const func_decl &new_decl, const std::vector ¶ms); expr GetRelRec(hash_set &memo, const expr &t, const func_decl &rel); expr GetRel(Edge *edge, int child_idx); void GetDefs(const expr &cnst, hash_map &defs); void GetDefsRec(const expr &cnst, hash_map &defs); void AddParamsToNode(Node *node, const std::vector ¶ms); void UnhoistLoop(Edge *loop_edge, Edge *init_edge); void Unhoist(); Term ElimIteRec(hash_map &memo, const Term &t, std::vector &cnsts); Term ElimIte(const Term &t); void MarkLiveNodes(hash_map > &outgoing, hash_set &live_nodes, Node *node); virtual void slvr_add(const expr &e); virtual void slvr_pop(int i); virtual void slvr_push(); virtual check_result slvr_check(unsigned n = 0, expr * const assumptions = 0, unsigned *core_size = 0, expr *core = 0); virtual lbool ls_interpolate_tree(TermTree *assumptions, TermTree *&interpolants, model &_model, TermTree *goals = 0, bool weak = false); virtual bool proof_core_contains(const expr &e); }; /** RPFP solver base class. */ class Solver { public: class Counterexample { private: RPFP *tree; RPFP::Node *root; public: Counterexample(){ tree = 0; root = 0; } Counterexample(RPFP *_tree, RPFP::Node *_root){ tree = _tree; root = _root; } ~Counterexample(){ if(tree) delete tree; } void swap(Counterexample &other){ std::swap(tree,other.tree); std::swap(root,other.root); } void set(RPFP *_tree, RPFP::Node *_root){ if(tree) delete tree; tree = _tree; root = _root; } void clear(){ if(tree) delete tree; tree = 0; } RPFP *get_tree() const {return tree;} RPFP::Node *get_root() const {return root;} private: Counterexample &operator=(const Counterexample &); Counterexample(const Counterexample &); }; /** Solve the problem. You can optionally give an old counterexample to use as a guide. This is chiefly useful for abstraction refinement metholdologies, and is only used as a heuristic. */ virtual bool Solve() = 0; virtual Counterexample &GetCounterexample() = 0; virtual bool SetOption(const std::string &option, const std::string &value) = 0; /** Learn heuristic information from another solver. This is chiefly useful for abstraction refinement, when we want to solve a series of similar problems. */ virtual void LearnFrom(Solver *old_solver) = 0; /** Return true if the solution be incorrect due to recursion bounding. That is, the returned "solution" might contain all derivable facts up to the given recursion bound, but not be actually a fixed point. */ virtual bool IsResultRecursionBounded() = 0; virtual ~Solver(){} static Solver *Create(const std::string &solver_class, RPFP *rpfp); /** This can be called asynchrnously to cause Solve to throw a Canceled exception at some time in the future. */ virtual void Cancel() = 0; /** Object thrown on cancellation */ struct Canceled {}; /** Object thrown on incompleteness */ struct Incompleteness {}; }; } // Allow to hash on nodes and edges in deterministic way namespace hash_space { template <> class hash { public: size_t operator()(const Duality::RPFP::Node *p) const { return p->number; } }; } namespace hash_space { template <> class hash { public: size_t operator()(const Duality::RPFP::Edge *p) const { return p->number; } }; } // allow to walk sets of nodes without address dependency namespace std { template <> class less { public: bool operator()(Duality::RPFP::Node * const &s, Duality::RPFP::Node * const &t) const { return s->number < t->number; // s.raw()->get_id() < t.raw()->get_id(); } }; } // #define LIMIT_STACK_WEIGHT 5 namespace Duality { /** Caching version of RPFP. Instead of asserting constraints, returns assumption literals */ class RPFP_caching : public RPFP { public: /** appends assumption literals for edge to lits. if with_children is true, includes that annotation of the edge's children. */ void AssertEdgeCache(Edge *e, std::vector &lits, bool with_children = false); /** appends assumption literals for node to lits */ void AssertNodeCache(Node *, std::vector lits); /** check assumption lits, and return core */ check_result CheckCore(const std::vector &assumps, std::vector &core); /** Clone another RPFP into this one, keeping a map */ void Clone(RPFP *other); /** Get the clone of a node */ Node *GetNodeClone(Node *other_node); /** Get the clone of an edge */ Edge *GetEdgeClone(Edge *other_edge); /** Try to strengthen the parent of an edge */ void GeneralizeCache(Edge *edge); /** Try to propagate some facts from children to parents of edge. Return true if success. */ bool PropagateCache(Edge *edge); /** Construct a caching RPFP using a LogicSolver */ RPFP_caching(LogicSolver *_ls) : RPFP(_ls) {} /** Constraint an edge by its child's annotation. Return assumption lits. */ void ConstrainParentCache(Edge *parent, Node *child, std::vector &lits); #ifdef LIMIT_STACK_WEIGHT virtual void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); #endif virtual ~RPFP_caching(){} protected: hash_map AssumptionLits; hash_map NodeCloneMap; hash_map EdgeCloneMap; std::vector alit_stack; std::vector alit_stack_sizes; // to let us use one solver per edge struct edge_solver { hash_map AssumptionLits; uptr slvr; }; hash_map edge_solvers; #ifdef LIMIT_STACK_WEIGHT struct weight_counter { int val; weight_counter(){val = 0;} void swap(weight_counter &other){ std::swap(val,other.val); } }; struct big_stack_entry { weight_counter weight_added; std::vector new_alits; std::vector alit_stack; std::vector alit_stack_sizes; }; std::vector new_alits; weight_counter weight_added; std::vector big_stack; #endif void GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map = 0); void GreedyReduceCache(std::vector &assumps, std::vector &core); void FilterCore(std::vector &core, std::vector &full_core); void ConstrainEdgeLocalizedCache(Edge *e, const Term &tl, std::vector &lits); virtual void slvr_add(const expr &e); virtual void slvr_pop(int i); virtual void slvr_push(); virtual check_result slvr_check(unsigned n = 0, expr * const assumptions = 0, unsigned *core_size = 0, expr *core = 0); virtual lbool ls_interpolate_tree(TermTree *assumptions, TermTree *&interpolants, model &_model, TermTree *goals = 0, bool weak = false); virtual bool proof_core_contains(const expr &e); void GetTermTreeAssertionLiterals(TermTree *assumptions); void GetTermTreeAssertionLiteralsRec(TermTree *assumptions); edge_solver &SolverForEdge(Edge *edge, bool models, bool axioms); public: struct scoped_solver_for_edge { solver *orig_slvr; RPFP_caching *rpfp; edge_solver *es; scoped_solver_for_edge(RPFP_caching *_rpfp, Edge *edge, bool models = false, bool axioms = false){ rpfp = _rpfp; orig_slvr = rpfp->ls->slvr; es = &(rpfp->SolverForEdge(edge,models,axioms)); rpfp->ls->slvr = es->slvr.get(); rpfp->AssumptionLits.swap(es->AssumptionLits); } ~scoped_solver_for_edge(){ rpfp->ls->slvr = orig_slvr; rpfp->AssumptionLits.swap(es->AssumptionLits); } }; }; } z3-4.4.0/src/duality/duality_solver.cpp0000644000175000017500000044122712540347414017672 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: duality_solver.h Abstract: implements relational post-fixedpoint problem (RPFP) solver Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifdef _WINDOWS #pragma warning(disable:4996) #pragma warning(disable:4800) #pragma warning(disable:4267) #endif #include "duality.h" #include "duality_profiling.h" #include #include #include #include #include #include // TODO: make these official options or get rid of them #define NEW_CAND_SEL // #define LOCALIZE_CONJECTURES // #define CANDS_FROM_UPDATES #define CANDS_FROM_COVER_FAIL #define DEPTH_FIRST_EXPAND #define MINIMIZE_CANDIDATES // #define MINIMIZE_CANDIDATES_HARDER #define BOUNDED // #define CHECK_CANDS_FROM_IND_SET #define UNDERAPPROX_NODES #define NEW_EXPAND #define EARLY_EXPAND // #define TOP_DOWN // #define EFFORT_BOUNDED_STRAT #define SKIP_UNDERAPPROX_NODES // #define KEEP_EXPANSIONS // #define USE_CACHING_RPFP // #define PROPAGATE_BEFORE_CHECK #define NEW_STRATIFIED_INLINING #define USE_RPFP_CLONE #define USE_NEW_GEN_CANDS //#define NO_PROPAGATE //#define NO_GENERALIZE //#define NO_DECISIONS namespace Duality { // TODO: must be a better place for this... static char string_of_int_buffer[20]; static const char *string_of_int(int n){ sprintf(string_of_int_buffer,"%d",n); return string_of_int_buffer; } /** Generic object for producing diagnostic output. */ class Reporter { protected: RPFP *rpfp; public: Reporter(RPFP *_rpfp){ rpfp = _rpfp; } virtual void Extend(RPFP::Node *node){} virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){} virtual void Bound(RPFP::Node *node){} virtual void Expand(RPFP::Edge *edge){} virtual void AddCover(RPFP::Node *covered, std::vector &covering){} virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){} virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){} virtual void Forcing(RPFP::Node *covered, RPFP::Node *covering){} virtual void Dominates(RPFP::Node *node, RPFP::Node *other){} virtual void InductionFailure(RPFP::Edge *edge, const std::vector &children){} virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){} virtual void Reject(RPFP::Edge *edge, const std::vector &Children){} virtual void Message(const std::string &msg){} virtual void Depth(int){} virtual ~Reporter(){} }; Reporter *CreateStdoutReporter(RPFP *rpfp); Reporter *CreateConjectureFileReporter(RPFP *rpfp, const std::string &fname); /** Object we throw in case of catastrophe. */ struct InternalError { std::string msg; InternalError(const std::string _msg) : msg(_msg) {} }; /** This is the main solver. It takes anarbitrary (possibly cyclic) RPFP and either annotates it with a solution, or returns a counterexample derivation in the form of an embedd RPFP tree. */ class Duality : public Solver { public: Duality(RPFP *_rpfp) : ctx(_rpfp->ctx), slvr(_rpfp->slvr()), nodes(_rpfp->nodes), edges(_rpfp->edges) { rpfp = _rpfp; reporter = 0; conj_reporter = 0; heuristic = 0; unwinding = 0; FullExpand = false; NoConj = false; FeasibleEdges = true; UseUnderapprox = true; Report = false; StratifiedInlining = false; RecursionBound = -1; BatchExpand = false; { scoped_no_proof no_proofs_please(ctx.m()); #ifdef USE_RPFP_CLONE clone_rpfp = new RPFP_caching(rpfp->ls); clone_rpfp->Clone(rpfp); #endif #ifdef USE_NEW_GEN_CANDS gen_cands_rpfp = new RPFP_caching(rpfp->ls); gen_cands_rpfp->Clone(rpfp); #endif } } ~Duality(){ #ifdef USE_RPFP_CLONE delete clone_rpfp; #endif #ifdef USE_NEW_GEN_CANDS delete gen_cands_rpfp; #endif if(unwinding) delete unwinding; } #ifdef USE_RPFP_CLONE RPFP_caching *clone_rpfp; #endif #ifdef USE_NEW_GEN_CANDS RPFP_caching *gen_cands_rpfp; #endif typedef RPFP::Node Node; typedef RPFP::Edge Edge; /** This struct represents a candidate for extending the unwinding. It consists of an edge to instantiate and a vector of children for the new instance. */ struct Candidate { Edge *edge; std::vector Children; }; /** Comparison operator, allowing us to sort Nodes by their number field. */ struct lnode { bool operator()(const Node* s1, const Node* s2) const { return s1->number < s2->number; } }; typedef std::set Unexpanded; // sorted set of Nodes /** This class provides a heuristic for expanding a derivation tree. */ class Heuristic { RPFP *rpfp; /** Heuristic score for unwinding nodes. Currently this counts the number of updates. */ struct score { int updates; score() : updates(0) {} }; hash_map scores; public: Heuristic(RPFP *_rpfp){ rpfp = _rpfp; } virtual ~Heuristic(){} virtual void Update(RPFP::Node *node){ scores[node].updates++; } /** Heuristic choice of nodes to expand. Takes a set "choices" and returns a subset "best". We currently choose the nodes with the fewest updates. */ #if 0 virtual void ChooseExpand(const std::set &choices, std::set &best){ int best_score = INT_MAX; for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ Node *node = (*it)->map; int score = scores[node].updates; best_score = std::min(best_score,score); } for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) if(scores[(*it)->map].updates == best_score) best.insert(*it); } #else virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority=false, bool best_only=false){ if(high_priority) return; int best_score = INT_MAX; int worst_score = 0; for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ Node *node = (*it)->map; int score = scores[node].updates; best_score = std::min(best_score,score); worst_score = std::max(worst_score,score); } int cutoff = best_only ? best_score : (best_score + (worst_score-best_score)/2); for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) if(scores[(*it)->map].updates <= cutoff) best.insert(*it); } #endif /** Called when done expanding a tree */ virtual void Done() {} /** Ask whether a node should be used/unused in the tree. Returns, 1 if yes, -1 if no, and 0 if don't care. */ virtual int UseNode(Node *node){ return 0; } }; /** The Proposer class proposes conjectures eagerly. These can come from any source, including predicate abstraction, templates, or previous solver runs. The proposed conjectures are checked with low effort when the unwinding is expanded. */ class Proposer { public: /** Given a node in the unwinding, propose some conjectures */ virtual std::vector &Propose(Node *node) = 0; virtual ~Proposer(){}; }; class Covering; // see below // These members represent the state of the algorithm. RPFP *rpfp; // the input RPFP Reporter *reporter; // object for logging Reporter *conj_reporter; // object for logging conjectures Heuristic *heuristic; // expansion heuristic context &ctx; // Z3 context solver &slvr; // Z3 solver std::vector &nodes; // Nodes of input RPFP std::vector &edges; // Edges of input RPFP std::vector leaves; // leaf nodes of unwinding (unused) Unexpanded unexpanded; // unexpanded nodes std::list candidates; // candidates for expansion // maps children to edges in input RPFP hash_map > edges_by_child; // maps each node in input RPFP to its expanded instances hash_map > insts_of_node; // maps each node in input RPFP to all its instances hash_map > all_of_node; RPFP *unwinding; // the unwinding Covering *indset; // proposed inductive subset Counterexample cex; // counterexample std::list to_expand; hash_set updated_nodes; hash_map underapprox_map; // maps underapprox nodes to the nodes they approximate int last_decisions; hash_set overapproxes; std::vector proposers; std::string ConjectureFile; bool stratified_inlining_done; #ifdef BOUNDED struct Counter { unsigned val; Counter(){val = 0;} }; typedef std::map NodeToCounter; hash_map back_edges; // counts of back edges #endif /** Solve the problem. */ virtual bool Solve(){ PreSolve(); bool res = SolveMain(); // does the actual work PostSolve(); return res; } void PreSolve(){ reporter = Report ? CreateStdoutReporter(rpfp) : new Reporter(rpfp); conj_reporter = ConjectureFile.empty() ? 0 : CreateConjectureFileReporter(rpfp,ConjectureFile); #ifndef LOCALIZE_CONJECTURES heuristic = !cex.get_tree() ? new Heuristic(rpfp) : new ReplayHeuristic(rpfp,cex); #else heuristic = !cex.get_tree() ? (Heuristic *)(new LocalHeuristic(rpfp)) : (Heuristic *)(new ReplayHeuristic(rpfp,cex)); #endif // determine if we are recursion bounded for(unsigned i = 0; i < rpfp->nodes.size(); i++) if(rpfp->nodes[i]->recursion_bound < UINT_MAX) RecursionBound = 0; cex.clear(); // in case we didn't use it for heuristic if(unwinding) delete unwinding; unwinding = new RPFP(rpfp->ls); unwinding->HornClauses = rpfp->HornClauses; indset = new Covering(this); last_decisions = 0; CreateEdgesByChildMap(); #ifndef TOP_DOWN CreateInitialUnwinding(); #else CreateLeaves(); for(unsigned i = 0; i < leaves.size(); i++) if(!SatisfyUpperBound(leaves[i])) return false; #endif StratifiedLeafCount = -1; stratified_inlining_done = false; } void PostSolve(){ // print_profile(std::cout); delete indset; delete heuristic; // delete unwinding; // keep the unwinding for future mining of predicates delete reporter; if(conj_reporter) delete conj_reporter; for(unsigned i = 0; i < proposers.size(); i++) delete proposers[i]; } bool RecheckBounds(){ for(unsigned i = 0; i < unwinding->nodes.size(); i++){ Node *node = unwinding->nodes[i]; node->Bound = node->map->Bound; if(!SatisfyUpperBound(node)) return false; } return true; } void CreateInitialUnwinding(){ if(!StratifiedInlining){ CreateLeaves(); if(FeasibleEdges)NullaryCandidates(); else InstantiateAllEdges(); } else { #ifdef NEW_STRATIFIED_INLINING #else CreateLeaves(); #endif } } void Cancel(){ // TODO } #if 0 virtual void Restart(RPFP *_rpfp){ rpfp = _rpfp; delete unwinding; nodes = _rpfp->nodes; edges = _rpfp->edges; leaves.clear(); unexpanded.clear(); // unexpanded nodes candidates.clear(); // candidates for expansion edges_by_child.clear(); insts_of_node.clear(); all_of_node.clear(); to_expand.clear(); } #endif virtual void LearnFrom(Solver *other_solver){ // get the counterexample as a guide cex.swap(other_solver->GetCounterexample()); // propose conjectures based on old unwinding Duality *old_duality = dynamic_cast(other_solver); if(old_duality) proposers.push_back(new HistoryProposer(old_duality,this)); } /** Return a reference to the counterexample */ virtual Counterexample &GetCounterexample(){ return cex; } // options bool FullExpand; // do not use partial expansion of derivation tree bool NoConj; // do not use conjectures (no forced covering) bool FeasibleEdges; // use only feasible edges in unwinding bool UseUnderapprox; // use underapproximations bool Report; // spew on stdout bool StratifiedInlining; // Do stratified inlining as preprocessing step int RecursionBound; // Recursion bound for bounded verification bool BatchExpand; bool EnableRestarts; bool SetBoolOption(bool &opt, const std::string &value){ if(value == "0") { opt = false; return true; } if(value == "1") { opt = true; return true; } return false; } bool SetIntOption(int &opt, const std::string &value){ opt = atoi(value.c_str()); return true; } /** Set options (not currently used) */ virtual bool SetOption(const std::string &option, const std::string &value){ if(option == "full_expand"){ return SetBoolOption(FullExpand,value); } if(option == "no_conj"){ return SetBoolOption(NoConj,value); } if(option == "feasible_edges"){ return SetBoolOption(FeasibleEdges,value); } if(option == "use_underapprox"){ return SetBoolOption(UseUnderapprox,value); } if(option == "report"){ return SetBoolOption(Report,value); } if(option == "stratified_inlining"){ return SetBoolOption(StratifiedInlining,value); } if(option == "batch_expand"){ return SetBoolOption(BatchExpand,value); } if(option == "recursion_bound"){ return SetIntOption(RecursionBound,value); } if(option == "conjecture_file"){ ConjectureFile = value; } if(option == "enable_restarts"){ return SetBoolOption(EnableRestarts,value); } return false; } /** Create an instance of a node in the unwinding. Set its annotation to true, and mark it unexpanded. */ Node* CreateNodeInstance(Node *node, int number = 0){ RPFP::Node *inst = unwinding->CloneNode(node); inst->Annotation.SetFull(); if(number < 0) inst->number = number; unexpanded.insert(inst); all_of_node[node].push_back(inst); return inst; } /** Create an instance of an edge in the unwinding, with given parent and children. */ void CreateEdgeInstance(Edge *edge, Node *parent, const std::vector &children){ RPFP::Edge *inst = unwinding->CreateEdge(parent,edge->F,children); inst->map = edge; } void MakeLeaf(Node *node, bool do_not_expand = false){ node->Annotation.SetEmpty(); Edge *e = unwinding->CreateLowerBoundEdge(node); #ifdef TOP_DOWN node->Annotation.SetFull(); // allow this node to cover others #endif if(StratifiedInlining) node->Annotation.SetFull(); // allow this node to cover others else updated_nodes.insert(node); e->map = 0; reporter->Extend(node); #ifdef EARLY_EXPAND if(!do_not_expand) TryExpandNode(node); #endif // e->F.SetEmpty(); } void MakeOverapprox(Node *node){ node->Annotation.SetFull(); Edge *e = unwinding->CreateLowerBoundEdge(node); overapproxes.insert(node); e->map = 0; } /** We start the unwinding with leaves that under-approximate each relation with false. */ void CreateLeaves(){ unexpanded.clear(); leaves.clear(); for(unsigned i = 0; i < nodes.size(); i++){ RPFP::Node *node = CreateNodeInstance(nodes[i]); if(0 && nodes[i]->Outgoing->Children.size() == 0) CreateEdgeInstance(nodes[i]->Outgoing,node,std::vector()); else { if(!StratifiedInlining) MakeLeaf(node); else { MakeOverapprox(node); LeafMap[nodes[i]] = node; } } leaves.push_back(node); } } /** Create the map from children to edges in the input RPFP. This is used to generate candidates for expansion. */ void CreateEdgesByChildMap(){ edges_by_child.clear(); for(unsigned i = 0; i < edges.size(); i++){ Edge *e = edges[i]; std::set done; for(unsigned j = 0; j < e->Children.size(); j++){ Node *c = e->Children[j]; if(done.find(c) == done.end()) // avoid duplicates edges_by_child[c].push_back(e); done.insert(c); } } } void NullaryCandidates(){ for(unsigned i = 0; i < edges.size(); i++){ RPFP::Edge *edge = edges[i]; if(edge->Children.size() == 0){ Candidate cand; cand.edge = edge; candidates.push_back(cand); } } } void InstantiateAllEdges(){ hash_map leaf_map; for(unsigned i = 0; i < leaves.size(); i++){ leaf_map[leaves[i]->map] = leaves[i]; insts_of_node[leaves[i]->map].push_back(leaves[i]); } unexpanded.clear(); for(unsigned i = 0; i < edges.size(); i++){ Edge *edge = edges[i]; Candidate c; c.edge = edge; c.Children.resize(edge->Children.size()); for(unsigned j = 0; j < c.Children.size(); j++) c.Children[j] = leaf_map[edge->Children[j]]; Node *new_node; Extend(c,new_node); #ifdef EARLY_EXPAND TryExpandNode(new_node); #endif } for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it) indset->Add(*it); for(unsigned i = 0; i < leaves.size(); i++){ std::vector &foo = insts_of_node[leaves[i]->map]; foo.erase(foo.begin()); } } bool ProducedBySI(Edge *edge, std::vector &children){ if(LeafMap.find(edge->Parent) == LeafMap.end()) return false; Node *other = LeafMap[edge->Parent]; if(other->Outgoing->map != edge) return false; std::vector &ochs = other->Outgoing->Children; for(unsigned i = 0; i < children.size(); i++) if(ochs[i] != children[i]) return false; return true; } /** Add a candidate for expansion, but not if Stratified inlining has already produced it */ void AddCandidate(Edge *edge, std::vector &children){ if(StratifiedInlining && ProducedBySI(edge,children)) return; candidates.push_back(Candidate()); candidates.back().edge = edge; candidates.back().Children = children; } /** Generate candidates for expansion, given a vector of candidate sets for each argument position. This recursively produces the cross product. */ void GenCandidatesRec(int pos, Edge *edge, const std::vector > &vec, std::vector &children){ if(pos == (int)vec.size()){ AddCandidate(edge,children); } else { for(unsigned i = 0; i < vec[pos].size(); i++){ children[pos] = vec[pos][i]; GenCandidatesRec(pos+1,edge,vec,children); } } } /** Setup for above recursion. */ void GenCandidates(int pos, Edge *edge, const std::vector > &vec){ std::vector children(vec.size()); GenCandidatesRec(0,edge,vec,children); } /** Expand a node. We find all the candidates for expansion using this node and other already expanded nodes. This is a little tricky, since a node may be used for multiple argument positions of an edge, and we don't want to produce duplicates. */ #ifndef NEW_EXPAND void ExpandNode(Node *node){ std::vector &nedges = edges_by_child[node->map]; for(unsigned i = 0; i < nedges.size(); i++){ Edge *edge = nedges[i]; for(unsigned npos = 0; npos < edge->Children.size(); ++npos){ if(edge->Children[npos] == node->map){ std::vector > vec(edge->Children.size()); vec[npos].push_back(node); for(unsigned j = 0; j < edge->Children.size(); j++){ if(j != npos){ std::vector &insts = insts_of_node[edge->Children[j]]; for(unsigned k = 0; k < insts.size(); k++) if(indset->Candidate(insts[k])) vec[j].push_back(insts[k]); } if(j < npos && edge->Children[j] == node->map) vec[j].push_back(node); } GenCandidates(0,edge,vec); } } } unexpanded.erase(node); insts_of_node[node->map].push_back(node); } #else /** If the current proposed solution is not inductive, use the induction failure to generate candidates for extension. */ void ExpandNode(Node *node){ unexpanded.erase(node); insts_of_node[node->map].push_back(node); timer_start("GenCandIndFailUsing"); std::vector &nedges = edges_by_child[node->map]; for(unsigned i = 0; i < nedges.size(); i++){ Edge *edge = nedges[i]; slvr.push(); RPFP *checker = new RPFP(rpfp->ls); Node *root = CheckerJustForEdge(edge,checker,true); if(root){ expr using_cond = ctx.bool_val(false); for(unsigned npos = 0; npos < edge->Children.size(); ++npos) if(edge->Children[npos] == node->map) using_cond = using_cond || checker->Localize(root->Outgoing->Children[npos]->Outgoing,NodeMarker(node)); slvr.add(using_cond); if(checker->Check(root) != unsat){ Candidate candidate; ExtractCandidateFromCex(edge,checker,root,candidate); reporter->InductionFailure(edge,candidate.Children); candidates.push_back(candidate); } } slvr.pop(1); delete checker; } timer_stop("GenCandIndFailUsing"); } #endif void ExpandNodeFromOther(Node *node, Node *other){ std::vector &in = other->Incoming; for(unsigned i = 0; i < in.size(); i++){ Edge *edge = in[i]; Candidate cand; cand.edge = edge->map; cand.Children = edge->Children; for(unsigned j = 0; j < cand.Children.size(); j++) if(cand.Children[j] == other) cand.Children[j] = node; candidates.push_front(cand); } // unexpanded.erase(node); // insts_of_node[node->map].push_back(node); } /** Expand a node based on some uncovered node it dominates. This pushes cahdidates onto the *front* of the candidate queue, so these expansions are done depth-first. */ bool ExpandNodeFromCoverFail(Node *node){ if(!node->Outgoing || node->Outgoing->Children.size() == 0) return false; Node *other = indset->GetSimilarNode(node); if(!other) return false; #ifdef UNDERAPPROX_NODES Node *under_node = CreateUnderapproxNode(node); underapprox_map[under_node] = node; indset->CoverByNode(node,under_node); ExpandNodeFromOther(under_node,other); ExpandNode(under_node); #else ExpandNodeFromOther(node,other); unexpanded.erase(node); insts_of_node[node->map].push_back(node); #endif return true; } /** Make a boolean variable to act as a "marker" for a node. */ expr NodeMarker(Node *node){ std::string name = std::string("@m_") + string_of_int(node->number); return ctx.constant(name.c_str(),ctx.bool_sort()); } /** Make a boolean variable to act as a "marker" for a pair of nodes. */ expr NodeMarker(Node *node1, Node *node2){ std::string name = std::string("@m_") + string_of_int(node1->number); name += std::string("_") + string_of_int(node2->number); return ctx.constant(name.c_str(),ctx.bool_sort()); } /** Union the annotation of dst into src. If with_markers is true, we conjoin the annotation formula of dst with its marker. This allows us to discover which disjunct is true in a satisfying assignment. */ void UnionAnnotations(RPFP::Transformer &dst, Node *src, bool with_markers = false){ if(!with_markers) dst.UnionWith(src->Annotation); else { RPFP::Transformer t = src->Annotation; t.Formula = t.Formula && NodeMarker(src); dst.UnionWith(t); } } void GenNodeSolutionFromIndSet(Node *node, RPFP::Transformer &annot, bool with_markers = false){ annot.SetEmpty(); std::vector &insts = insts_of_node[node]; for(unsigned j = 0; j < insts.size(); j++) if(indset->Contains(insts[j])) UnionAnnotations(annot,insts[j],with_markers); annot.Simplify(); } bool NodeSolutionFromIndSetFull(Node *node){ std::vector &insts = insts_of_node[node]; for(unsigned j = 0; j < insts.size(); j++) if(indset->Contains(insts[j])) if(insts[j]->Annotation.IsFull()) return true; return false; } bool recursionBounded; /** See if the solution might be bounded. */ void TestRecursionBounded(){ recursionBounded = false; if(RecursionBound == -1) return; for(unsigned i = 0; i < nodes.size(); i++){ Node *node = nodes[i]; std::vector &insts = insts_of_node[node]; for(unsigned j = 0; j < insts.size(); j++) if(indset->Contains(insts[j])) if(NodePastRecursionBound(insts[j],true)) recursionBounded = true; } } bool IsResultRecursionBounded(){ return recursionBounded; } /** Generate a proposed solution of the input RPFP from the unwinding, by unioning the instances of each node. */ void GenSolutionFromIndSet(bool with_markers = false){ for(unsigned i = 0; i < nodes.size(); i++){ Node *node = nodes[i]; GenNodeSolutionFromIndSet(node,node->Annotation,with_markers); } } #ifdef BOUNDED bool NodePastRecursionBound(Node *node, bool report = false){ if(RecursionBound < 0) return false; NodeToCounter &backs = back_edges[node]; for(NodeToCounter::iterator it = backs.begin(), en = backs.end(); it != en; ++it){ if(it->second.val > it->first->recursion_bound){ if(report){ std::ostringstream os; os << "cut off " << it->first->Name.name() << " at depth " << it->second.val; reporter->Message(os.str()); } return true; } } return false; } #endif /** Test whether a given extension candidate actually represents an induction failure. Right now we approximate this: if the resulting node in the unwinding could be labeled false, it clearly is not an induction failure. */ bool CandidateFeasible(const Candidate &cand){ if(!FeasibleEdges) return true; timer_start("CandidateFeasible"); RPFP *checker = new RPFP(rpfp->ls); // std::cout << "Checking feasibility of extension " << cand.edge->Parent->number << std::endl; checker->Push(); std::vector chs(cand.Children.size()); Node *root = checker->CloneNode(cand.edge->Parent); #ifdef BOUNDED for(unsigned i = 0; i < cand.Children.size(); i++) if(NodePastRecursionBound(cand.Children[i])){ timer_stop("CandidateFeasible"); return false; } #endif #ifdef NEW_CAND_SEL GenNodeSolutionFromIndSet(cand.edge->Parent,root->Bound); #else root->Bound.SetEmpty(); #endif checker->AssertNode(root); for(unsigned i = 0; i < cand.Children.size(); i++) chs[i] = checker->CloneNode(cand.Children[i]); Edge *e = checker->CreateEdge(root,cand.edge->F,chs); checker->AssertEdge(e,0,true); // std::cout << "Checking SAT: " << e->dual << std::endl; bool res = checker->Check(root) != unsat; // std::cout << "Result: " << res << std::endl; if(!res)reporter->Reject(cand.edge,cand.Children); checker->Pop(1); delete checker; timer_stop("CandidateFeasible"); return res; } hash_map TopoSort; int TopoSortCounter; std::vector SortedEdges; void DoTopoSortRec(Node *node){ if(TopoSort.find(node) != TopoSort.end()) return; TopoSort[node] = INT_MAX; // just to break cycles Edge *edge = node->Outgoing; // note, this is just *one* outgoing edge if(edge){ std::vector &chs = edge->Children; for(unsigned i = 0; i < chs.size(); i++) DoTopoSortRec(chs[i]); } TopoSort[node] = TopoSortCounter++; SortedEdges.push_back(edge); } void DoTopoSort(){ TopoSort.clear(); SortedEdges.clear(); TopoSortCounter = 0; for(unsigned i = 0; i < nodes.size(); i++) DoTopoSortRec(nodes[i]); } int StratifiedLeafCount; #ifdef NEW_STRATIFIED_INLINING /** Stratified inlining builds an initial layered unwinding before switching to the LAWI strategy. Currently the number of layers is one. Only nodes that are the targets of back edges are consider to be leaves. This assumes we have already computed a topological sort. */ bool DoStratifiedInlining(){ if(stratified_inlining_done) return true; stratified_inlining_done = true; DoTopoSort(); int depth = 1; // TODO: make this an option std::vector > unfolding_levels(depth+1); for(int level = 1; level <= depth; level++) for(unsigned i = 0; i < SortedEdges.size(); i++){ Edge *edge = SortedEdges[i]; Node *parent = edge->Parent; std::vector &chs = edge->Children; std::vector my_chs(chs.size()); for(unsigned j = 0; j < chs.size(); j++){ Node *child = chs[j]; int ch_level = TopoSort[child] >= TopoSort[parent] ? level-1 : level; if(unfolding_levels[ch_level].find(child) == unfolding_levels[ch_level].end()){ if(ch_level == 0) unfolding_levels[0][child] = CreateLeaf(child); else throw InternalError("in levelized unwinding"); } my_chs[j] = unfolding_levels[ch_level][child]; } Candidate cand; cand.edge = edge; cand.Children = my_chs; Node *new_node; bool ok = Extend(cand,new_node); MarkExpanded(new_node); // we don't expand here -- just mark it done if(!ok) return false; // got a counterexample unfolding_levels[level][parent] = new_node; } return true; } Node *CreateLeaf(Node *node){ RPFP::Node *nchild = CreateNodeInstance(node); MakeLeaf(nchild, /* do_not_expand = */ true); nchild->Annotation.SetEmpty(); return nchild; } void MarkExpanded(Node *node){ if(unexpanded.find(node) != unexpanded.end()){ unexpanded.erase(node); insts_of_node[node->map].push_back(node); } } #else /** In stratified inlining, we build the unwinding from the bottom down, trying to satisfy the node bounds. We do this as a pre-pass, limiting the expansion. If we get a counterexample, we are done, else we continue as usual expanding the unwinding upward. */ bool DoStratifiedInlining(){ timer_start("StratifiedInlining"); DoTopoSort(); for(unsigned i = 0; i < leaves.size(); i++){ Node *node = leaves[i]; bool res = SatisfyUpperBound(node); if(!res){ timer_stop("StratifiedInlining"); return false; } } // don't leave any dangling nodes! #ifndef EFFORT_BOUNDED_STRAT for(unsigned i = 0; i < leaves.size(); i++) if(!leaves[i]->Outgoing) MakeLeaf(leaves[i],true); #endif timer_stop("StratifiedInlining"); return true; } #endif /** Here, we do the downward expansion for stratified inlining */ hash_map LeafMap, StratifiedLeafMap; Edge *GetNodeOutgoing(Node *node, int last_decs = 0){ if(overapproxes.find(node) == overapproxes.end()) return node->Outgoing; /* already expanded */ overapproxes.erase(node); #ifdef EFFORT_BOUNDED_STRAT if(last_decs > 5000){ // RPFP::Transformer save = node->Annotation; node->Annotation.SetEmpty(); Edge *e = unwinding->CreateLowerBoundEdge(node); // node->Annotation = save; insts_of_node[node->map].push_back(node); // std::cout << "made leaf: " << node->number << std::endl; return e; } #endif Edge *edge = node->map->Outgoing; std::vector &chs = edge->Children; // make sure we don't create a covered node in this process! for(unsigned i = 0; i < chs.size(); i++){ Node *child = chs[i]; if(TopoSort[child] < TopoSort[node->map]){ Node *leaf = LeafMap[child]; if(!indset->Contains(leaf)){ node->Outgoing->F.Formula = ctx.bool_val(false); // make this a proper leaf, else bogus cex return node->Outgoing; } } } std::vector nchs(chs.size()); for(unsigned i = 0; i < chs.size(); i++){ Node *child = chs[i]; if(TopoSort[child] < TopoSort[node->map]){ Node *leaf = LeafMap[child]; nchs[i] = leaf; if(unexpanded.find(leaf) != unexpanded.end()){ unexpanded.erase(leaf); insts_of_node[child].push_back(leaf); } } else { if(StratifiedLeafMap.find(child) == StratifiedLeafMap.end()){ RPFP::Node *nchild = CreateNodeInstance(child,StratifiedLeafCount--); MakeLeaf(nchild); nchild->Annotation.SetEmpty(); StratifiedLeafMap[child] = nchild; indset->SetDominated(nchild); } nchs[i] = StratifiedLeafMap[child]; } } CreateEdgeInstance(edge,node,nchs); reporter->Extend(node); return node->Outgoing; } void SetHeuristicOldNode(Node *node){ LocalHeuristic *h = dynamic_cast(heuristic); if(h) h->SetOldNode(node); } bool SolveMain(){ timer_start("SolveMain"); bool res = SolveMainInt(); // does the actual work timer_stop("SolveMain"); return res; } /** This does the actual solving work. We try to generate candidates for extension. If we succed, we extend the unwinding. If we fail, we have a solution. */ bool SolveMainInt(){ if(StratifiedInlining && !DoStratifiedInlining()) return false; #ifdef BOUNDED DoTopoSort(); #endif while(true){ timer_start("ProduceCandidatesForExtension"); ProduceCandidatesForExtension(); timer_stop("ProduceCandidatesForExtension"); if(candidates.empty()){ GenSolutionFromIndSet(); TestRecursionBounded(); return true; } Candidate cand = candidates.front(); candidates.pop_front(); if(CandidateFeasible(cand)){ Node *new_node; if(!Extend(cand,new_node)) return false; #ifdef EARLY_EXPAND TryExpandNode(new_node); #endif } } } // hack: put something local into the underapproximation formula // without this, interpolants can be pretty bad void AddThing(expr &conj){ std::string name = "@thing"; expr thing = ctx.constant(name.c_str(),ctx.bool_sort()); if(conj.is_app() && conj.decl().get_decl_kind() == And){ std::vector conjs(conj.num_args()+1); for(unsigned i = 0; i+1 < conjs.size(); i++) conjs[i] = conj.arg(i); conjs[conjs.size()-1] = thing; conj = rpfp->conjoin(conjs); } } Node *CreateUnderapproxNode(Node *node){ // cex.get_tree()->ComputeUnderapprox(cex.get_root(),0); RPFP::Node *under_node = CreateNodeInstance(node->map /* ,StratifiedLeafCount-- */); under_node->Annotation.IntersectWith(cex.get_root()->Underapprox); AddThing(under_node->Annotation.Formula); Edge *e = unwinding->CreateLowerBoundEdge(under_node); under_node->Annotation.SetFull(); // allow this node to cover others back_edges[under_node] = back_edges[node]; e->map = 0; reporter->Extend(under_node); return under_node; } /** Try to prove a conjecture about a node. If successful update the unwinding annotation appropriately. */ bool ProveConjecture(Node *node, const RPFP::Transformer &t,Node *other = 0, Counterexample *_cex = 0){ reporter->Conjecture(node,t); timer_start("ProveConjecture"); RPFP::Transformer save = node->Bound; node->Bound.IntersectWith(t); #ifndef LOCALIZE_CONJECTURES bool ok = SatisfyUpperBound(node); #else SetHeuristicOldNode(other); bool ok = SatisfyUpperBound(node); SetHeuristicOldNode(0); #endif if(ok){ timer_stop("ProveConjecture"); return true; } #ifdef UNDERAPPROX_NODES if(UseUnderapprox && last_decisions > 500){ std::cout << "making an underapprox\n"; ExpandNodeFromCoverFail(node); } #endif if(_cex) (*_cex).swap(cex); // return the cex if asked cex.clear(); // throw away the useless cex node->Bound = save; // put back original bound timer_stop("ProveConjecture"); return false; } /** If a node is part of the inductive subset, expand it. We ask the inductive subset to exclude the node if possible. */ void TryExpandNode(RPFP::Node *node){ if(indset->Close(node)) return; if(!NoConj && indset->Conjecture(node)){ #ifdef UNDERAPPROX_NODES /* TODO: temporary fix. this prevents an infinite loop in case the node is covered by multiple others. This should be removed when covering by a set is implemented. */ if(indset->Contains(node)){ unexpanded.erase(node); insts_of_node[node->map].push_back(node); } #endif return; } #ifdef UNDERAPPROX_NODES if(!indset->Contains(node)) return; // could be covered by an underapprox node #endif indset->Add(node); #if defined(CANDS_FROM_COVER_FAIL) && !defined(UNDERAPPROX_NODES) if(ExpandNodeFromCoverFail(node)) return; #endif ExpandNode(node); } /** Make the conjunction of markers for all (expanded) instances of a node in the input RPFP. */ expr AllNodeMarkers(Node *node){ expr res = ctx.bool_val(true); std::vector &insts = insts_of_node[node]; for(int k = insts.size()-1; k >= 0; k--) res = res && NodeMarker(insts[k]); return res; } void RuleOutNodesPastBound(Node *node, RPFP::Transformer &t){ #ifdef BOUNDED if(RecursionBound < 0)return; std::vector &insts = insts_of_node[node]; for(unsigned i = 0; i < insts.size(); i++) if(NodePastRecursionBound(insts[i])) t.Formula = t.Formula && !NodeMarker(insts[i]); #endif } void GenNodeSolutionWithMarkersAux(Node *node, RPFP::Transformer &annot, expr &marker_disjunction, Node *other_node){ #ifdef BOUNDED if(RecursionBound >= 0 && NodePastRecursionBound(node)) return; #endif RPFP::Transformer temp = node->Annotation; expr marker = (!other_node) ? NodeMarker(node) : NodeMarker(node, other_node); temp.Formula = (!marker || temp.Formula); annot.IntersectWith(temp); marker_disjunction = marker_disjunction || marker; } bool GenNodeSolutionWithMarkers(Node *node, RPFP::Transformer &annot, bool expanded_only = false, Node *other_node = 0){ bool res = false; annot.SetFull(); expr marker_disjunction = ctx.bool_val(false); std::vector &insts = expanded_only ? insts_of_node[node] : all_of_node[node]; for(unsigned j = 0; j < insts.size(); j++){ Node *node = insts[j]; if(indset->Contains(insts[j])){ GenNodeSolutionWithMarkersAux(node, annot, marker_disjunction, other_node); res = true; } } annot.Formula = annot.Formula && marker_disjunction; annot.Simplify(); return res; } /** Make a checker to determine if an edge in the input RPFP is satisfied. */ Node *CheckerJustForEdge(Edge *edge, RPFP *checker, bool expanded_only = false){ Node *root = checker->CloneNode(edge->Parent); GenNodeSolutionFromIndSet(edge->Parent, root->Bound); if(root->Bound.IsFull()) return 0; checker->AssertNode(root); std::vector cs; for(unsigned j = 0; j < edge->Children.size(); j++){ Node *oc = edge->Children[j]; Node *nc = checker->CloneNode(oc); if(!GenNodeSolutionWithMarkers(oc,nc->Annotation,expanded_only)) return 0; Edge *e = checker->CreateLowerBoundEdge(nc); checker->AssertEdge(e); cs.push_back(nc); } checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); return root; } #ifndef MINIMIZE_CANDIDATES_HARDER #if 0 /** Make a checker to detheermine if an edge in the input RPFP is satisfied. */ Node *CheckerForEdge(Edge *edge, RPFP *checker){ Node *root = checker->CloneNode(edge->Parent); root->Bound = edge->Parent->Annotation; root->Bound.Formula = (!AllNodeMarkers(edge->Parent)) || root->Bound.Formula; checker->AssertNode(root); std::vector cs; for(unsigned j = 0; j < edge->Children.size(); j++){ Node *oc = edge->Children[j]; Node *nc = checker->CloneNode(oc); nc->Annotation = oc->Annotation; RuleOutNodesPastBound(oc,nc->Annotation); Edge *e = checker->CreateLowerBoundEdge(nc); checker->AssertEdge(e); cs.push_back(nc); } checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); return root; } #else /** Make a checker to determine if an edge in the input RPFP is satisfied. */ Node *CheckerForEdge(Edge *edge, RPFP *checker){ Node *root = checker->CloneNode(edge->Parent); GenNodeSolutionFromIndSet(edge->Parent, root->Bound); #if 0 if(root->Bound.IsFull()) return = 0; #endif checker->AssertNode(root); std::vector cs; for(unsigned j = 0; j < edge->Children.size(); j++){ Node *oc = edge->Children[j]; Node *nc = checker->CloneNode(oc); GenNodeSolutionWithMarkers(oc,nc->Annotation,true); Edge *e = checker->CreateLowerBoundEdge(nc); checker->AssertEdge(e); cs.push_back(nc); } checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); return root; } #endif /** If an edge is not satisfied, produce an extension candidate using instances of its children that violate the parent annotation. We find these using the marker predicates. */ void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ candidate.edge = edge; for(unsigned j = 0; j < edge->Children.size(); j++){ Node *node = root->Outgoing->Children[j]; Edge *lb = node->Outgoing; std::vector &insts = insts_of_node[edge->Children[j]]; #ifndef MINIMIZE_CANDIDATES for(int k = insts.size()-1; k >= 0; k--) #else for(unsigned k = 0; k < insts.size(); k++) #endif { Node *inst = insts[k]; if(indset->Contains(inst)){ if(checker->Empty(node) || eq(lb ? checker->Eval(lb,NodeMarker(inst)) : checker->dualModel.eval(NodeMarker(inst,node)),ctx.bool_val(true))){ candidate.Children.push_back(inst); goto next_child; } } } throw InternalError("No candidate from induction failure"); next_child:; } } #else /** Make a checker to determine if an edge in the input RPFP is satisfied. */ Node *CheckerForEdge(Edge *edge, RPFP *checker){ Node *root = checker->CloneNode(edge->Parent); GenNodeSolutionFromIndSet(edge->Parent, root->Bound); if(root->Bound.IsFull()) return = 0; checker->AssertNode(root); std::vector cs; for(unsigned j = 0; j < edge->Children.size(); j++){ Node *oc = edge->Children[j]; Node *nc = checker->CloneNode(oc); GenNodeSolutionWithMarkers(oc,nc->Annotation,true); Edge *e = checker->CreateLowerBoundEdge(nc); checker->AssertEdge(e); cs.push_back(nc); } checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); return root; } /** If an edge is not satisfied, produce an extension candidate using instances of its children that violate the parent annotation. We find these using the marker predicates. */ void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ candidate.edge = edge; std::vector assumps; for(unsigned j = 0; j < edge->Children.size(); j++){ Edge *lb = root->Outgoing->Children[j]->Outgoing; std::vector &insts = insts_of_node[edge->Children[j]]; for(unsigned k = 0; k < insts.size(); k++) { Node *inst = insts[k]; expr marker = NodeMarker(inst); if(indset->Contains(inst)){ if(checker->Empty(lb->Parent) || eq(checker->Eval(lb,marker),ctx.bool_val(true))){ candidate.Children.push_back(inst); assumps.push_back(checker->Localize(lb,marker)); goto next_child; } assumps.push_back(checker->Localize(lb,marker)); if(checker->CheckUpdateModel(root,assumps) != unsat){ candidate.Children.push_back(inst); goto next_child; } assumps.pop_back(); } } throw InternalError("No candidate from induction failure"); next_child:; } } #endif Node *CheckerForEdgeClone(Edge *edge, RPFP_caching *checker){ Edge *gen_cands_edge = checker->GetEdgeClone(edge); Node *root = gen_cands_edge->Parent; root->Outgoing = gen_cands_edge; GenNodeSolutionFromIndSet(edge->Parent, root->Bound); #if 0 if(root->Bound.IsFull()) return = 0; #endif checker->AssertNode(root); for(unsigned j = 0; j < edge->Children.size(); j++){ Node *oc = edge->Children[j]; Node *nc = gen_cands_edge->Children[j]; GenNodeSolutionWithMarkers(oc,nc->Annotation,true,nc); } checker->AssertEdge(gen_cands_edge,1,true); return root; } /** If the current proposed solution is not inductive, use the induction failure to generate candidates for extension. */ void GenCandidatesFromInductionFailure(bool full_scan = false){ timer_start("GenCandIndFail"); GenSolutionFromIndSet(true /* add markers */); for(unsigned i = 0; i < edges.size(); i++){ Edge *edge = edges[i]; if(!full_scan && updated_nodes.find(edge->Parent) == updated_nodes.end()) continue; #ifndef USE_NEW_GEN_CANDS slvr.push(); RPFP *checker = new RPFP(rpfp->ls); Node *root = CheckerForEdge(edge,checker); if(checker->Check(root) != unsat){ Candidate candidate; ExtractCandidateFromCex(edge,checker,root,candidate); reporter->InductionFailure(edge,candidate.Children); candidates.push_back(candidate); } slvr.pop(1); delete checker; #else if(!NodeSolutionFromIndSetFull(edge->Parent)){ RPFP_caching::scoped_solver_for_edge ssfe(gen_cands_rpfp,edge,true /* models */, true /*axioms*/); gen_cands_rpfp->Push(); Node *root = CheckerForEdgeClone(edge,gen_cands_rpfp); if(gen_cands_rpfp->Check(root) != unsat){ Candidate candidate; ExtractCandidateFromCex(edge,gen_cands_rpfp,root,candidate); reporter->InductionFailure(edge,candidate.Children); candidates.push_back(candidate); } gen_cands_rpfp->Pop(1); } #endif } updated_nodes.clear(); timer_stop("GenCandIndFail"); #ifdef CHECK_CANDS_FROM_IND_SET for(std::list::iterator it = candidates.begin(), en = candidates.end(); it != en; ++it){ if(!CandidateFeasible(*it)) throw "produced infeasible candidate"; } #endif if(!full_scan && candidates.empty()){ reporter->Message("No candidates from updates. Trying full scan."); GenCandidatesFromInductionFailure(true); } } #ifdef CANDS_FROM_UPDATES /** If the given edge is not inductive in the current proposed solution, use the induction failure to generate candidates for extension. */ void GenCandidatesFromEdgeInductionFailure(RPFP::Edge *edge){ GenSolutionFromIndSet(true /* add markers */); for(unsigned i = 0; i < edges.size(); i++){ slvr.push(); Edge *edge = edges[i]; RPFP *checker = new RPFP(rpfp->ls); Node *root = CheckerForEdge(edge,checker); if(checker->Check(root) != unsat){ Candidate candidate; ExtractCandidateFromCex(edge,checker,root,candidate); reporter->InductionFailure(edge,candidate.Children); candidates.push_back(candidate); } slvr.pop(1); delete checker; } } #endif /** Find the unexpanded nodes in the inductive subset. */ void FindNodesToExpand(){ for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it){ Node *node = *it; if(indset->Candidate(node)) to_expand.push_back(node); } } /** Try to create some extension candidates from the unexpanded nodes. */ void ProduceSomeCandidates(){ while(candidates.empty() && !to_expand.empty()){ Node *node = to_expand.front(); to_expand.pop_front(); TryExpandNode(node); } } std::list postponed_candidates; /** Try to produce some extension candidates, first from unexpanded nides, and if this fails, from induction failure. */ void ProduceCandidatesForExtension(){ if(candidates.empty()) ProduceSomeCandidates(); while(candidates.empty()){ FindNodesToExpand(); if(to_expand.empty()) break; ProduceSomeCandidates(); } if(candidates.empty()){ #ifdef DEPTH_FIRST_EXPAND if(postponed_candidates.empty()){ GenCandidatesFromInductionFailure(); postponed_candidates.swap(candidates); } if(!postponed_candidates.empty()){ candidates.push_back(postponed_candidates.front()); postponed_candidates.pop_front(); } #else GenCandidatesFromInductionFailure(); #endif } } bool Update(Node *node, const RPFP::Transformer &fact, bool eager=false){ if(!node->Annotation.SubsetEq(fact)){ reporter->Update(node,fact,eager); if(conj_reporter) conj_reporter->Update(node,fact,eager); indset->Update(node,fact); updated_nodes.insert(node->map); node->Annotation.IntersectWith(fact); return true; } return false; } bool UpdateNodeToNode(Node *node, Node *top){ return Update(node,top->Annotation); } /** Update the unwinding solution, using an interpolant for the derivation tree. */ void UpdateWithInterpolant(Node *node, RPFP *tree, Node *top){ if(top->Outgoing) for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) UpdateWithInterpolant(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); UpdateNodeToNode(node, top); heuristic->Update(node); } /** Update unwinding lower bounds, using a counterexample. */ void UpdateWithCounterexample(Node *node, RPFP *tree, Node *top){ if(top->Outgoing) for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) UpdateWithCounterexample(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); if(!top->Underapprox.SubsetEq(node->Underapprox)){ reporter->UpdateUnderapprox(node,top->Underapprox); // indset->Update(node,top->Annotation); node->Underapprox.UnionWith(top->Underapprox); heuristic->Update(node); } } /** Try to update the unwinding to satisfy the upper bound of a node. */ bool SatisfyUpperBound(Node *node){ if(node->Bound.IsFull()) return true; #ifdef PROPAGATE_BEFORE_CHECK Propagate(); #endif reporter->Bound(node); int start_decs = rpfp->CumulativeDecisions(); DerivationTree *dtp = new DerivationTreeSlow(this,unwinding,reporter,heuristic,FullExpand); DerivationTree &dt = *dtp; bool res = dt.Derive(unwinding,node,UseUnderapprox); int end_decs = rpfp->CumulativeDecisions(); // std::cout << "decisions: " << (end_decs - start_decs) << std::endl; last_decisions = end_decs - start_decs; if(res){ cex.set(dt.tree,dt.top); // note tree is now owned by cex if(UseUnderapprox){ UpdateWithCounterexample(node,dt.tree,dt.top); } } else { UpdateWithInterpolant(node,dt.tree,dt.top); delete dt.tree; } delete dtp; return !res; } /* For a given nod in the unwinding, get conjectures from the proposers and check them locally. Update the node with any true conjectures. */ void DoEagerDeduction(Node *node){ for(unsigned i = 0; i < proposers.size(); i++){ const std::vector &conjectures = proposers[i]->Propose(node); for(unsigned j = 0; j < conjectures.size(); j++){ const RPFP::Transformer &conjecture = conjectures[j]; RPFP::Transformer bound(conjecture); std::vector conj_vec; unwinding->CollectConjuncts(bound.Formula,conj_vec); for(unsigned k = 0; k < conj_vec.size(); k++){ bound.Formula = conj_vec[k]; if(CheckEdgeCaching(node->Outgoing,bound) == unsat) Update(node,bound, /* eager = */ true); //else //std::cout << "conjecture failed\n"; } } } } check_result CheckEdge(RPFP *checker, Edge *edge){ Node *root = edge->Parent; checker->Push(); checker->AssertNode(root); checker->AssertEdge(edge,1,true); check_result res = checker->Check(root); checker->Pop(1); return res; } check_result CheckEdgeCaching(Edge *unwinding_edge, const RPFP::Transformer &bound){ // use a dedicated solver for this edge // TODO: can this mess be hidden somehow? RPFP_caching *checker = gen_cands_rpfp; // TODO: a good choice? Edge *edge = unwinding_edge->map; // get the edge in the original RPFP RPFP_caching::scoped_solver_for_edge ssfe(checker,edge,true /* models */, true /*axioms*/); Edge *checker_edge = checker->GetEdgeClone(edge); // copy the annotations and bound to the clone Node *root = checker_edge->Parent; root->Bound = bound; for(unsigned j = 0; j < checker_edge->Children.size(); j++){ Node *oc = unwinding_edge->Children[j]; Node *nc = checker_edge->Children[j]; nc->Annotation = oc->Annotation; } return CheckEdge(checker,checker_edge); } /* If the counterexample derivation is partial due to use of underapproximations, complete it. */ void BuildFullCex(Node *node){ DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); bool res = dt.Derive(unwinding,node,UseUnderapprox,true); // build full tree if(!res) throw "Duality internal error in BuildFullCex"; cex.set(dt.tree,dt.top); } void UpdateBackEdges(Node *node){ #ifdef BOUNDED std::vector &chs = node->Outgoing->Children; for(unsigned i = 0; i < chs.size(); i++){ Node *child = chs[i]; bool is_back = TopoSort[child->map] >= TopoSort[node->map]; NodeToCounter &nov = back_edges[node]; NodeToCounter chv = back_edges[child]; if(is_back) chv[child->map].val++; for(NodeToCounter::iterator it = chv.begin(), en = chv.end(); it != en; ++it){ Node *back = it->first; Counter &c = nov[back]; c.val = std::max(c.val,it->second.val); } } #endif } /** Extend the unwinding, keeping it solved. */ bool Extend(Candidate &cand, Node *&node){ timer_start("Extend"); node = CreateNodeInstance(cand.edge->Parent); CreateEdgeInstance(cand.edge,node,cand.Children); UpdateBackEdges(node); reporter->Extend(node); DoEagerDeduction(node); // first be eager... bool res = SatisfyUpperBound(node); // then be lazy if(res) indset->CloseDescendants(node); else { #ifdef UNDERAPPROX_NODES ExpandUnderapproxNodes(cex.get_tree(), cex.get_root()); #endif if(UseUnderapprox) BuildFullCex(node); timer_stop("Extend"); return res; } timer_stop("Extend"); return res; } void ExpandUnderapproxNodes(RPFP *tree, Node *root){ Node *node = root->map; if(underapprox_map.find(node) != underapprox_map.end()){ RPFP::Transformer cnst = root->Annotation; tree->EvalNodeAsConstraint(root, cnst); cnst.Complement(); Node *orig = underapprox_map[node]; RPFP::Transformer save = orig->Bound; orig->Bound = cnst; DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); bool res = dt.Derive(unwinding,orig,UseUnderapprox,true,tree); if(!res){ UpdateWithInterpolant(orig,dt.tree,dt.top); throw "bogus underapprox!"; } ExpandUnderapproxNodes(tree,dt.top); } else if(root->Outgoing){ std::vector &chs = root->Outgoing->Children; for(unsigned i = 0; i < chs.size(); i++) ExpandUnderapproxNodes(tree,chs[i]); } } // Propagate conjuncts up the unwinding void Propagate(){ reporter->Message("beginning propagation"); timer_start("Propagate"); std::vector sorted_nodes = unwinding->nodes; std::sort(sorted_nodes.begin(),sorted_nodes.end(),std::less()); // sorts by sequence number hash_map > facts; for(unsigned i = 0; i < sorted_nodes.size(); i++){ Node *node = sorted_nodes[i]; std::set &node_facts = facts[node->map]; if(!(node->Outgoing && indset->Contains(node))) continue; std::vector conj_vec; unwinding->CollectConjuncts(node->Annotation.Formula,conj_vec); std::set conjs; std::copy(conj_vec.begin(),conj_vec.end(),std::inserter(conjs,conjs.begin())); if(!node_facts.empty()){ RPFP *checker = new RPFP(rpfp->ls); slvr.push(); Node *root = checker->CloneNode(node); Edge *edge = node->Outgoing; // checker->AssertNode(root); std::vector cs; for(unsigned j = 0; j < edge->Children.size(); j++){ Node *oc = edge->Children[j]; Node *nc = checker->CloneNode(oc); nc->Annotation = oc->Annotation; // is this needed? cs.push_back(nc); } Edge *checker_edge = checker->CreateEdge(root,edge->F,cs); checker->AssertEdge(checker_edge, 0, true, false); std::vector propagated; for(std::set ::iterator it = node_facts.begin(), en = node_facts.end(); it != en;){ const expr &fact = *it; if(conjs.find(fact) == conjs.end()){ root->Bound.Formula = fact; slvr.push(); checker->AssertNode(root); check_result res = checker->Check(root); slvr.pop(); if(res != unsat){ std::set ::iterator victim = it; ++it; node_facts.erase(victim); // if it ain't true, nix it continue; } propagated.push_back(fact); } ++it; } slvr.pop(); for(unsigned i = 0; i < propagated.size(); i++){ root->Annotation.Formula = propagated[i]; UpdateNodeToNode(node,root); } delete checker; } for(std::set ::iterator it = conjs.begin(), en = conjs.end(); it != en; ++it){ expr foo = *it; node_facts.insert(foo); } } timer_stop("Propagate"); } /** This class represents a derivation tree. */ class DerivationTree { public: virtual ~DerivationTree(){} DerivationTree(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) : slvr(rpfp->slvr()), ctx(rpfp->ctx) { duality = _duality; reporter = _reporter; heuristic = _heuristic; full_expand = _full_expand; } Duality *duality; Reporter *reporter; Heuristic *heuristic; solver &slvr; context &ctx; RPFP *tree; RPFP::Node *top; std::list leaves; bool full_expand; bool underapprox; bool constrained; bool false_approx; std::vector underapprox_core; int start_decs, last_decs; /* We build derivation trees in one of three modes: 1) In normal mode, we build the full tree without considering underapproximations. 2) In underapprox mode, we use underapproximations to cut off the tree construction. THis means the resulting tree may not be complete. 3) In constrained mode, we build the full tree but use underapproximations as upper bounds. This mode is used to complete the partial derivation constructed in underapprox mode. */ bool Derive(RPFP *rpfp, RPFP::Node *root, bool _underapprox, bool _constrained = false, RPFP *_tree = 0){ underapprox = _underapprox; constrained = _constrained; false_approx = true; timer_start("Derive"); #ifndef USE_CACHING_RPFP tree = _tree ? _tree : new RPFP(rpfp->ls); #else RPFP::LogicSolver *cache_ls = new RPFP::iZ3LogicSolver(ctx); cache_ls->slvr->push(); tree = _tree ? _tree : new RPFP_caching(cache_ls); #endif tree->HornClauses = rpfp->HornClauses; tree->Push(); // so we can clear out the solver later when finished top = CreateApproximatedInstance(root); tree->AssertNode(top); // assert the negation of the top-level spec timer_start("Build"); bool res = Build(); heuristic->Done(); timer_stop("Build"); timer_start("Pop"); tree->Pop(1); timer_stop("Pop"); #ifdef USE_CACHING_RPFP cache_ls->slvr->pop(1); delete cache_ls; tree->ls = rpfp->ls; #endif timer_stop("Derive"); return res; } #define WITH_CHILDREN void InitializeApproximatedInstance(RPFP::Node *to){ to->Annotation = to->map->Annotation; #ifndef WITH_CHILDREN tree->CreateLowerBoundEdge(to); #endif leaves.push_back(to); } Node *CreateApproximatedInstance(RPFP::Node *from){ Node *to = tree->CloneNode(from); InitializeApproximatedInstance(to); return to; } bool CheckWithUnderapprox(){ timer_start("CheckWithUnderapprox"); std::vector leaves_vector(leaves.size()); std::copy(leaves.begin(),leaves.end(),leaves_vector.begin()); check_result res = tree->Check(top,leaves_vector); timer_stop("CheckWithUnderapprox"); return res != unsat; } virtual bool Build(){ #ifdef EFFORT_BOUNDED_STRAT start_decs = tree->CumulativeDecisions(); #endif while(ExpandSomeNodes(true)); // do high-priority expansions while (true) { #ifndef WITH_CHILDREN timer_start("asserting leaves"); timer_start("pushing"); tree->Push(); timer_stop("pushing"); for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) tree->AssertEdge((*it)->Outgoing,1); // assert the overapproximation, and keep it past pop timer_stop("asserting leaves"); lbool res = tree->Solve(top, 2); // incremental solve, keep interpolants for two pops timer_start("popping leaves"); tree->Pop(1); timer_stop("popping leaves"); #else lbool res; if((underapprox || false_approx) && top->Outgoing && CheckWithUnderapprox()){ if(constrained) goto expand_some_nodes; // in constrained mode, keep expanding goto we_are_sat; // else if underapprox is sat, we stop } // tree->Check(top); res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop #endif if (res == l_false) return false; expand_some_nodes: if(ExpandSomeNodes()) continue; we_are_sat: if(underapprox && !constrained){ timer_start("ComputeUnderapprox"); tree->ComputeUnderapprox(top,1); timer_stop("ComputeUnderapprox"); } else { #ifdef UNDERAPPROX_NODES #ifndef SKIP_UNDERAPPROX_NODES timer_start("ComputeUnderapprox"); tree->ComputeUnderapprox(top,1); timer_stop("ComputeUnderapprox"); #endif #endif } return true; } } virtual void ExpandNode(RPFP::Node *p){ // tree->RemoveEdge(p->Outgoing); Edge *ne = p->Outgoing; if(ne) { // reporter->Message("Recycling edge..."); std::vector &cs = ne->Children; for(unsigned i = 0; i < cs.size(); i++) InitializeApproximatedInstance(cs[i]); // ne->dual = expr(); } else { Edge *edge = duality->GetNodeOutgoing(p->map,last_decs); std::vector &cs = edge->Children; std::vector children(cs.size()); for(unsigned i = 0; i < cs.size(); i++) children[i] = CreateApproximatedInstance(cs[i]); ne = tree->CreateEdge(p, p->map->Outgoing->F, children); ne->map = p->map->Outgoing->map; } #ifndef WITH_CHILDREN tree->AssertEdge(ne); // assert the edge in the solver #else tree->AssertEdge(ne,0,!full_expand,(underapprox || false_approx)); // assert the edge in the solver #endif reporter->Expand(ne); } #define UNDERAPPROXCORE #ifndef UNDERAPPROXCORE void ExpansionChoices(std::set &best){ std::set choices; for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) if (!tree->Empty(*it)) // if used in the counter-model choices.insert(*it); heuristic->ChooseExpand(choices, best); } #else #if 0 void ExpansionChoices(std::set &best){ std::vector unused_set, used_set; std::set choices; for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it){ Node *n = *it; if (!tree->Empty(n)) used_set.push_back(n); else unused_set.push_back(n); } if(tree->Check(top,unused_set) == unsat) throw "error in ExpansionChoices"; for(unsigned i = 0; i < used_set.size(); i++){ Node *n = used_set[i]; unused_set.push_back(n); if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ unused_set.pop_back(); choices.insert(n); } else std::cout << "Using underapprox of " << n->number << std::endl; } heuristic->ChooseExpand(choices, best); } #else void ExpansionChoicesFull(std::set &best, bool high_priority, bool best_only = false){ std::set choices; for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) if (high_priority || !tree->Empty(*it)) // if used in the counter-model choices.insert(*it); heuristic->ChooseExpand(choices, best, high_priority, best_only); } void ExpansionChoicesRec(std::vector &unused_set, std::vector &used_set, std::set &choices, int from, int to){ if(from == to) return; int orig_unused = unused_set.size(); unused_set.resize(orig_unused + (to - from)); std::copy(used_set.begin()+from,used_set.begin()+to,unused_set.begin()+orig_unused); if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ unused_set.resize(orig_unused); if(to - from == 1){ #if 1 std::cout << "Not using underapprox of " << used_set[from] ->number << std::endl; #endif choices.insert(used_set[from]); } else { int mid = from + (to - from)/2; ExpansionChoicesRec(unused_set, used_set, choices, from, mid); ExpansionChoicesRec(unused_set, used_set, choices, mid, to); } } else { #if 1 std::cout << "Using underapprox of "; for(int i = from; i < to; i++){ std::cout << used_set[i]->number << " "; if(used_set[i]->map->Underapprox.IsEmpty()) std::cout << "(false!) "; } std::cout << std::endl; #endif } } std::set old_choices; void ExpansionChoices(std::set &best, bool high_priority, bool best_only = false){ if(!underapprox || constrained || high_priority){ ExpansionChoicesFull(best, high_priority,best_only); return; } std::vector unused_set, used_set; std::set choices; for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it){ Node *n = *it; if (!tree->Empty(n)){ if(old_choices.find(n) != old_choices.end() || n->map->Underapprox.IsEmpty()) choices.insert(n); else used_set.push_back(n); } else unused_set.push_back(n); } if(tree->Check(top,unused_set) == unsat) throw "error in ExpansionChoices"; ExpansionChoicesRec(unused_set, used_set, choices, 0, used_set.size()); old_choices = choices; heuristic->ChooseExpand(choices, best, high_priority); } #endif #endif bool ExpandSomeNodes(bool high_priority = false, int max = INT_MAX){ #ifdef EFFORT_BOUNDED_STRAT last_decs = tree->CumulativeDecisions() - start_decs; #endif timer_start("ExpandSomeNodes"); timer_start("ExpansionChoices"); std::set choices; ExpansionChoices(choices,high_priority,max != INT_MAX); timer_stop("ExpansionChoices"); std::list leaves_copy = leaves; // copy so can modify orig leaves.clear(); int count = 0; for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ if(choices.find(*it) != choices.end() && count < max){ count++; ExpandNode(*it); } else leaves.push_back(*it); } timer_stop("ExpandSomeNodes"); return !choices.empty(); } void RemoveExpansion(RPFP::Node *p){ Edge *edge = p->Outgoing; Node *parent = edge->Parent; #ifndef KEEP_EXPANSIONS std::vector cs = edge->Children; tree->DeleteEdge(edge); for(unsigned i = 0; i < cs.size(); i++) tree->DeleteNode(cs[i]); #endif leaves.push_back(parent); } // remove all the descendants of tree root (but not root itself) void RemoveTree(RPFP *tree, RPFP::Node *root){ Edge *edge = root->Outgoing; std::vector cs = edge->Children; tree->DeleteEdge(edge); for(unsigned i = 0; i < cs.size(); i++){ RemoveTree(tree,cs[i]); tree->DeleteNode(cs[i]); } } }; class DerivationTreeSlow : public DerivationTree { public: struct stack_entry { unsigned level; // SMT solver stack level std::vector expansions; }; std::vector stack; hash_map updates; int restart_interval; DerivationTreeSlow(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) : DerivationTree(_duality, rpfp, _reporter, _heuristic, _full_expand) { stack.push_back(stack_entry()); } struct DoRestart {}; virtual bool Build(){ restart_interval = 3; while (true) { try { return BuildMain(); } catch (const DoRestart &) { // clear the statck and try again updated_nodes.clear(); while(stack.size() > 1) PopLevel(); reporter->Message("restarted"); restart_interval += 1; } } } // When we check, try to use the same children that were used in the // previous counterexample. check_result Check(){ #if 0 std::vector posnodes, negnodes; std::vector &expansions = stack.back().expansions; for(unsigned i = 0; i < expansions.size(); i++){ Node *node = expansions[i]; std::vector &chs = node->Outgoing->Children; for(unsigned j = 0; j < chs.size(); j++){ Node *ch = chs[j]; int use = heuristic->UseNode(ch); if(use == 1) posnodes.push_back(ch); else if (use == -1) negnodes.push_back(ch); } } if(!(posnodes.empty() && negnodes.empty())){ check_result res = tree->CheckWithConstrainedNodes(posnodes,negnodes); if(res != unsat){ reporter->Message("matched previous counterexample"); return res; } } #endif return tree->Check(top); } bool BuildMain(){ stack.back().level = tree->slvr().get_scope_level(); bool was_sat = true; int update_failures = 0; int total_updates = 0; while (true) { lbool res; unsigned slvr_level = tree->slvr().get_scope_level(); if(slvr_level != stack.back().level) throw "stacks out of sync!"; reporter->Depth(stack.size()); // res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop check_result foo = Check(); res = foo == unsat ? l_false : l_true; if (res == l_false) { if (stack.empty()) // should never happen return false; { std::vector &expansions = stack.back().expansions; int update_count = 0; for(unsigned i = 0; i < expansions.size(); i++){ Node *node = expansions[i]; try { tree->SolveSingleNode(top,node); #ifdef NO_GENERALIZE node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); #else if(expansions.size() == 1 && NodeTooComplicated(node)) SimplifyNode(node); else node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); Generalize(node); #endif } catch(const RPFP::Bad &){ // bad interpolants can get us here throw DoRestart(); } catch(const RPFP::ReallyBad &){ // this could be caused by incompleteness for(unsigned i = 0; i < expansions.size(); i++){ Node *node = expansions[i]; node->map->Annotation.SetFull(); std::vector &chs = node->map->Outgoing->Children; for(unsigned j = 0; j < chs.size(); j++) chs[j]->Annotation.SetFull(); reporter->Message("incompleteness: cleared annotation and child annotations"); } throw DoRestart(); } catch(char const *msg){ // bad interpolants can get us here reporter->Message(std::string("interpolation failure:") + msg); throw DoRestart(); } if(RecordUpdate(node)){ update_count++; total_updates++; } else heuristic->Update(node->map); // make it less likely to expand this node in future } #if 1 if(duality->EnableRestarts) if(total_updates >= restart_interval) throw DoRestart(); #endif if(update_count == 0){ if(was_sat){ update_failures++; if(update_failures > 10){ for(unsigned i = 0; i < expansions.size(); i++){ Node *node = expansions[i]; node->map->Annotation.SetFull(); reporter->Message("incompleteness: cleared annotation"); } throw DoRestart(); } } reporter->Message("backtracked without learning"); } else update_failures = 0; } tree->ComputeProofCore(); // need to compute the proof core before popping solver bool propagated = false; while(1) { bool prev_level_used = LevelUsedInProof(stack.size()-2); // need to compute this before pop PopLevel(); if(stack.size() == 1)break; if(prev_level_used){ Node *node = stack.back().expansions[0]; #ifndef NO_PROPAGATE if(!Propagate(node)) break; #endif if(!RecordUpdate(node)) break; // shouldn't happen! RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list propagated = true; continue; } if(propagated) break; // propagation invalidates the proof core, so disable non-chron backtrack RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list std::vector &unused_ex = stack.back().expansions; for(unsigned i = 0; i < unused_ex.size(); i++) heuristic->Update(unused_ex[i]->map); // make it less likely to expand this node in future } HandleUpdatedNodes(); if(stack.size() == 1){ if(top->Outgoing) tree->DeleteEdge(top->Outgoing); // in case we kept the tree return false; } was_sat = false; } else { was_sat = true; tree->Push(); std::vector &expansions = stack.back().expansions; #ifndef NO_DECISIONS #if 0 if(expansions.size() > 0) tree->GreedyReduceNodes(expansions[0]->Outgoing->Children); // try to reduce number of children #endif for(unsigned i = 0; i < expansions.size(); i++){ tree->FixCurrentState(expansions[i]->Outgoing); } #endif #if 0 if(tree->slvr().check() == unsat) throw "help!"; #endif int expand_max = 1; if(0&&duality->BatchExpand){ int thing = stack.size() / 10; // * 0.1; expand_max = std::max(1,thing); if(expand_max > 1) std::cout << "foo!\n"; } if(ExpandSomeNodes(false,expand_max)) continue; tree->Pop(1); node_order.clear(); while(stack.size() > 1){ tree->Pop(1); std::vector &expansions = stack.back().expansions; for(unsigned i = 0; i < expansions.size(); i++) node_order.push_back(expansions[i]); stack.pop_back(); } #if 0 Reduce(); #endif return true; } } } std::vector node_order; void Reduce(){ tree->Push(); // tree->AssertNode(top); // assert the negation of the top-level spec for(int i = node_order.size()-1; i >= 0; --i){ Edge *edge = node_order[i]->Outgoing; if(edge){ for(unsigned j = 0; j < edge->Children.size(); j++){ Node *ch = edge->Children[j]; if(!ch->Outgoing) ch->Annotation.SetEmpty(); } tree->AssertEdge(edge,0,true); } } tree->GreedyReduceNodes(node_order); // try to reduce the counterexample size tree->Pop(1); } void PopLevel(){ std::vector &expansions = stack.back().expansions; tree->Pop(1); hash_set leaves_to_remove; for(unsigned i = 0; i < expansions.size(); i++){ Node *node = expansions[i]; // if(node != top) //tree->ConstrainParent(node->Incoming[0],node); std::vector &cs = node->Outgoing->Children; for(unsigned i = 0; i < cs.size(); i++){ leaves_to_remove.insert(cs[i]); UnmapNode(cs[i]); if(std::find(updated_nodes.begin(),updated_nodes.end(),cs[i]) != updated_nodes.end()) throw "help!"; } } RemoveLeaves(leaves_to_remove); // have to do this before actually deleting the children for(unsigned i = 0; i < expansions.size(); i++){ Node *node = expansions[i]; RemoveExpansion(node); } stack.pop_back(); } bool NodeTooComplicated(Node *node){ int ops = tree->CountOperators(node->Annotation.Formula); if(ops > 10) return true; node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); return tree->CountOperators(node->Annotation.Formula) > 3; } void SimplifyNode(Node *node){ // have to destroy the old proof to get a new interpolant timer_start("SimplifyNode"); tree->PopPush(); try { tree->InterpolateByCases(top,node); } catch(const RPFP::Bad&){ timer_stop("SimplifyNode"); throw RPFP::Bad(); } timer_stop("SimplifyNode"); } bool LevelUsedInProof(unsigned level){ std::vector &expansions = stack[level].expansions; for(unsigned i = 0; i < expansions.size(); i++) if(tree->EdgeUsedInProof(expansions[i]->Outgoing)) return true; return false; } void RemoveUpdateNodesAtCurrentLevel() { for(std::list::iterator it = updated_nodes.begin(), en = updated_nodes.end(); it != en;){ Node *node = *it; if(AtCurrentStackLevel(node->Incoming[0]->Parent)){ std::list::iterator victim = it; ++it; updated_nodes.erase(victim); } else ++it; } } void RemoveLeaves(hash_set &leaves_to_remove){ std::list leaves_copy; leaves_copy.swap(leaves); for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ if(leaves_to_remove.find(*it) == leaves_to_remove.end()) leaves.push_back(*it); } } hash_map > node_map; std::list updated_nodes; virtual void ExpandNode(RPFP::Node *p){ stack.push_back(stack_entry()); stack.back().level = tree->slvr().get_scope_level(); stack.back().expansions.push_back(p); DerivationTree::ExpandNode(p); std::vector &new_nodes = p->Outgoing->Children; for(unsigned i = 0; i < new_nodes.size(); i++){ Node *n = new_nodes[i]; node_map[n->map].push_back(n); } } bool RecordUpdate(Node *node){ bool res = duality->UpdateNodeToNode(node->map,node); if(res){ std::vector to_update = node_map[node->map]; for(unsigned i = 0; i < to_update.size(); i++){ Node *node2 = to_update[i]; // maintain invariant that no nodes on updated list are created at current stack level if(node2 == node || !(node->Incoming.size() > 0 && AtCurrentStackLevel(node2->Incoming[0]->Parent))){ updated_nodes.push_back(node2); if(node2 != node) node2->Annotation = node->Annotation; } } } return res; } void HandleUpdatedNodes(){ for(std::list::iterator it = updated_nodes.begin(), en = updated_nodes.end(); it != en;){ Node *node = *it; node->Annotation = node->map->Annotation; if(node->Incoming.size() > 0) tree->ConstrainParent(node->Incoming[0],node); if(AtCurrentStackLevel(node->Incoming[0]->Parent)){ std::list::iterator victim = it; ++it; updated_nodes.erase(victim); } else ++it; } } bool AtCurrentStackLevel(Node *node){ std::vector vec = stack.back().expansions; for(unsigned i = 0; i < vec.size(); i++) if(vec[i] == node) return true; return false; } void UnmapNode(Node *node){ std::vector &vec = node_map[node->map]; for(unsigned i = 0; i < vec.size(); i++){ if(vec[i] == node){ std::swap(vec[i],vec.back()); vec.pop_back(); return; } } throw "can't unmap node"; } void Generalize(Node *node){ #ifndef USE_RPFP_CLONE tree->Generalize(top,node); #else RPFP_caching *clone_rpfp = duality->clone_rpfp; if(!node->Outgoing->map) return; Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); Node *clone_node = clone_edge->Parent; clone_node->Annotation = node->Annotation; for(unsigned i = 0; i < clone_edge->Children.size(); i++) clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; clone_rpfp->GeneralizeCache(clone_edge); node->Annotation = clone_node->Annotation; #endif } bool Propagate(Node *node){ #ifdef USE_RPFP_CLONE RPFP_caching *clone_rpfp = duality->clone_rpfp; Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); Node *clone_node = clone_edge->Parent; clone_node->Annotation = node->map->Annotation; for(unsigned i = 0; i < clone_edge->Children.size(); i++) clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; bool res = clone_rpfp->PropagateCache(clone_edge); if(res) node->Annotation = clone_node->Annotation; return res; #else return false; #endif } }; class Covering { struct cover_info { Node *covered_by; std::list covers; bool dominated; std::set dominates; cover_info(){ covered_by = 0; dominated = false; } }; typedef hash_map cover_map; cover_map cm; Duality *parent; bool some_updates; #define NO_CONJ_ON_SIMPLE_LOOPS #ifdef NO_CONJ_ON_SIMPLE_LOOPS hash_set simple_loops; #endif Node *&covered_by(Node *node){ return cm[node].covered_by; } std::list &covers(Node *node){ return cm[node].covers; } std::vector &insts_of_node(Node *node){ return parent->insts_of_node[node]; } Reporter *reporter(){ return parent->reporter; } std::set &dominates(Node *x){ return cm[x].dominates; } bool dominates(Node *x, Node *y){ std::set &d = cm[x].dominates; return d.find(y) != d.end(); } bool &dominated(Node *x){ return cm[x].dominated; } public: Covering(Duality *_parent){ parent = _parent; some_updates = false; #ifdef NO_CONJ_ON_SIMPLE_LOOPS hash_map > outgoing; for(unsigned i = 0; i < parent->rpfp->edges.size(); i++) outgoing[parent->rpfp->edges[i]->Parent].push_back(parent->rpfp->edges[i]); for(unsigned i = 0; i < parent->rpfp->nodes.size(); i++){ Node * node = parent->rpfp->nodes[i]; std::vector &outs = outgoing[node]; if(outs.size() == 2){ for(int j = 0; j < 2; j++){ Edge *loop_edge = outs[j]; if(loop_edge->Children.size() == 1 && loop_edge->Children[0] == loop_edge->Parent) simple_loops.insert(node); } } } #endif } bool IsCoveredRec(hash_set &memo, Node *node){ if(memo.find(node) != memo.end()) return false; memo.insert(node); if(covered_by(node)) return true; for(unsigned i = 0; i < node->Outgoing->Children.size(); i++) if(IsCoveredRec(memo,node->Outgoing->Children[i])) return true; return false; } bool IsCovered(Node *node){ hash_set memo; return IsCoveredRec(memo,node); } #ifndef UNDERAPPROX_NODES void RemoveCoveringsBy(Node *node){ std::list &cs = covers(node); for(std::list::iterator it = cs.begin(), en = cs.end(); it != en; it++){ covered_by(*it) = 0; reporter()->RemoveCover(*it,node); } cs.clear(); } #else void RemoveCoveringsBy(Node *node){ std::vector &cs = parent->all_of_node[node->map]; for(std::vector::iterator it = cs.begin(), en = cs.end(); it != en; it++){ Node *other = *it; if(covered_by(other) && CoverOrder(node,other)){ covered_by(other) = 0; reporter()->RemoveCover(*it,node); } } } #endif void RemoveAscendantCoveringsRec(hash_set &memo, Node *node){ if(memo.find(node) != memo.end()) return; memo.insert(node); RemoveCoveringsBy(node); for(std::vector::iterator it = node->Incoming.begin(), en = node->Incoming.end(); it != en; ++it) RemoveAscendantCoveringsRec(memo,(*it)->Parent); } void RemoveAscendantCoverings(Node *node){ hash_set memo; RemoveAscendantCoveringsRec(memo,node); } bool CoverOrder(Node *covering, Node *covered){ #ifdef UNDERAPPROX_NODES if(parent->underapprox_map.find(covered) != parent->underapprox_map.end()) return false; if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) return covering->number < covered->number || parent->underapprox_map[covering] == covered; #endif return covering->number < covered->number; } bool CheckCover(Node *covered, Node *covering){ return CoverOrder(covering,covered) && covered->Annotation.SubsetEq(covering->Annotation) && !IsCovered(covering); } bool CoverByNode(Node *covered, Node *covering){ if(CheckCover(covered,covering)){ covered_by(covered) = covering; covers(covering).push_back(covered); std::vector others; others.push_back(covering); reporter()->AddCover(covered,others); RemoveAscendantCoverings(covered); return true; } else return false; } #ifdef UNDERAPPROX_NODES bool CoverByAll(Node *covered){ RPFP::Transformer all = covered->Annotation; all.SetEmpty(); std::vector &insts = parent->insts_of_node[covered->map]; std::vector others; for(unsigned i = 0; i < insts.size(); i++){ Node *covering = insts[i]; if(CoverOrder(covering,covered) && !IsCovered(covering)){ others.push_back(covering); all.UnionWith(covering->Annotation); } } if(others.size() && covered->Annotation.SubsetEq(all)){ covered_by(covered) = covered; // anything non-null will do reporter()->AddCover(covered,others); RemoveAscendantCoverings(covered); return true; } else return false; } #endif bool Close(Node *node){ if(covered_by(node)) return true; #ifndef UNDERAPPROX_NODES std::vector &insts = insts_of_node(node->map); for(unsigned i = 0; i < insts.size(); i++) if(CoverByNode(node,insts[i])) return true; #else if(CoverByAll(node)) return true; #endif return false; } bool CloseDescendantsRec(hash_set &memo, Node *node){ if(memo.find(node) != memo.end()) return false; for(unsigned i = 0; i < node->Outgoing->Children.size(); i++) if(CloseDescendantsRec(memo,node->Outgoing->Children[i])) return true; if(Close(node)) return true; memo.insert(node); return false; } bool CloseDescendants(Node *node){ timer_start("CloseDescendants"); hash_set memo; bool res = CloseDescendantsRec(memo,node); timer_stop("CloseDescendants"); return res; } bool Contains(Node *node){ timer_start("Contains"); bool res = !IsCovered(node); timer_stop("Contains"); return res; } bool Candidate(Node *node){ timer_start("Candidate"); bool res = !IsCovered(node) && !dominated(node); timer_stop("Candidate"); return res; } void SetDominated(Node *node){ dominated(node) = true; } bool CouldCover(Node *covered, Node *covering){ #ifdef NO_CONJ_ON_SIMPLE_LOOPS // Forsimple loops, we rely on propagation, not covering if(simple_loops.find(covered->map) != simple_loops.end()) return false; #endif #ifdef UNDERAPPROX_NODES // if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) // return parent->underapprox_map[covering] == covered; #endif if(CoverOrder(covering,covered) && !IsCovered(covering)){ RPFP::Transformer f(covering->Annotation); f.SetEmpty(); #if defined(TOP_DOWN) || defined(EFFORT_BOUNDED_STRAT) if(parent->StratifiedInlining) return true; #endif return !covering->Annotation.SubsetEq(f); } return false; } bool ContainsCex(Node *node, Counterexample &cex){ expr val = cex.get_tree()->Eval(cex.get_root()->Outgoing,node->Annotation.Formula); return eq(val,parent->ctx.bool_val(true)); } /** We conjecture that the annotations of similar nodes may be true of this one. We start with later nodes, on the principle that their annotations are likely weaker. We save a counterexample -- if annotations of other nodes are true in this counterexample, we don't need to check them. */ #ifndef UNDERAPPROX_NODES bool Conjecture(Node *node){ std::vector &insts = insts_of_node(node->map); Counterexample cex; for(int i = insts.size() - 1; i >= 0; i--){ Node *other = insts[i]; if(CouldCover(node,other)){ reporter()->Forcing(node,other); if(cex.get_tree() && !ContainsCex(other,cex)) continue; cex.clear(); if(parent->ProveConjecture(node,other->Annotation,other,&cex)) if(CloseDescendants(node)) return true; } } cex.clear(); return false; } #else bool Conjecture(Node *node){ std::vector &insts = insts_of_node(node->map); Counterexample cex; RPFP::Transformer Bound = node->Annotation; Bound.SetEmpty(); bool some_other = false; for(int i = insts.size() - 1; i >= 0; i--){ Node *other = insts[i]; if(CouldCover(node,other)){ reporter()->Forcing(node,other); Bound.UnionWith(other->Annotation); some_other = true; } } if(some_other && parent->ProveConjecture(node,Bound)){ CloseDescendants(node); return true; } return false; } #endif void Update(Node *node, const RPFP::Transformer &update){ RemoveCoveringsBy(node); some_updates = true; } #ifndef UNDERAPPROX_NODES Node *GetSimilarNode(Node *node){ if(!some_updates) return 0; std::vector &insts = insts_of_node(node->map); for(int i = insts.size()-1; i >= 0; i--){ Node *other = insts[i]; if(dominates(node,other)) if(CoverOrder(other,node) && !IsCovered(other)) return other; } return 0; } #else Node *GetSimilarNode(Node *node){ if(!some_updates) return 0; std::vector &insts = insts_of_node(node->map); for(int i = insts.size() - 1; i >= 0; i--){ Node *other = insts[i]; if(CoverOrder(other,node) && !IsCovered(other)) return other; } return 0; } #endif bool Dominates(Node * node, Node *other){ if(node == other) return false; if(other->Outgoing->map == 0) return true; if(node->Outgoing->map == other->Outgoing->map){ assert(node->Outgoing->Children.size() == other->Outgoing->Children.size()); for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ Node *nc = node->Outgoing->Children[i]; Node *oc = other->Outgoing->Children[i]; if(!(nc == oc || oc->Outgoing->map ==0 || dominates(nc,oc))) return false; } return true; } return false; } void Add(Node *node){ std::vector &insts = insts_of_node(node->map); for(unsigned i = 0; i < insts.size(); i++){ Node *other = insts[i]; if(Dominates(node,other)){ cm[node].dominates.insert(other); cm[other].dominated = true; reporter()->Dominates(node, other); } } } }; /* This expansion heuristic makes use of a previuosly obtained counterexample as a guide. This is for use in abstraction refinement schemes.*/ class ReplayHeuristic : public Heuristic { Counterexample old_cex; public: ReplayHeuristic(RPFP *_rpfp, Counterexample &_old_cex) : Heuristic(_rpfp) { old_cex.swap(_old_cex); // take ownership from caller } ~ReplayHeuristic(){ } // Maps nodes of derivation tree into old cex hash_map cex_map; void Done() { cex_map.clear(); old_cex.clear(); } void ShowNodeAndChildren(Node *n){ std::cout << n->Name.name() << ": "; std::vector &chs = n->Outgoing->Children; for(unsigned i = 0; i < chs.size(); i++) std::cout << chs[i]->Name.name() << " " ; std::cout << std::endl; } // HACK: When matching relation names, we drop suffixes used to // make the names unique between runs. For compatibility // with boggie, we drop suffixes beginning with @@ std::string BaseName(const std::string &name){ int pos = name.find("@@"); if(pos >= 1) return name.substr(0,pos); return name; } Node *MatchNode(Node *node){ if(cex_map.find(node) == cex_map.end()){ // try to match an unmatched node Node *parent = node->Incoming[0]->Parent; // assumes we are a tree! if(cex_map.find(parent) == cex_map.end()) throw "catastrophe in ReplayHeuristic::ChooseExpand"; Node *old_parent = cex_map[parent]; std::vector &chs = parent->Outgoing->Children; if(old_parent && old_parent->Outgoing){ std::vector &old_chs = old_parent->Outgoing->Children; for(unsigned i = 0, j=0; i < chs.size(); i++){ if(j < old_chs.size() && BaseName(chs[i]->Name.name().str()) == BaseName(old_chs[j]->Name.name().str())) cex_map[chs[i]] = old_chs[j++]; else { std::cerr << "WARNING: duality: unmatched child: " << chs[i]->Name.name() << std::endl; cex_map[chs[i]] = 0; } } goto matching_done; } for(unsigned i = 0; i < chs.size(); i++) cex_map[chs[i]] = 0; } matching_done: return cex_map[node]; } int UseNode(Node *node){ if (!old_cex.get_tree()) return 0; Node *old_node = MatchNode(node); if(!old_node) return 0; return old_cex.get_tree()->Empty(old_node) ? -1 : 1; } virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority, bool best_only){ if(cex_map.empty()) cex_map[*(choices.begin())] = old_cex.get_root(); // match the root nodes if(!high_priority || !old_cex.get_tree()){ Heuristic::ChooseExpand(choices,best,false); return; } // first, try to match the derivatino tree nodes to the old cex std::set matched, unmatched; for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ Node *node = (*it); Node *old_node = MatchNode(node); if(!old_node) unmatched.insert(node); else if(old_cex.get_tree()->Empty(old_node)) unmatched.insert(node); else matched.insert(node); } if (matched.empty() && !high_priority) Heuristic::ChooseExpand(unmatched,best,false); else Heuristic::ChooseExpand(matched,best,false); } }; class LocalHeuristic : public Heuristic { RPFP::Node *old_node; public: LocalHeuristic(RPFP *_rpfp) : Heuristic(_rpfp) { old_node = 0; } void SetOldNode(RPFP::Node *_old_node){ old_node = _old_node; cex_map.clear(); } // Maps nodes of derivation tree into old subtree hash_map cex_map; virtual void ChooseExpand(const std::set &choices, std::set &best){ if(old_node == 0){ Heuristic::ChooseExpand(choices,best); return; } // first, try to match the derivatino tree nodes to the old cex std::set matched, unmatched; for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ Node *node = (*it); if(cex_map.empty()) cex_map[node] = old_node; // match the root nodes if(cex_map.find(node) == cex_map.end()){ // try to match an unmatched node Node *parent = node->Incoming[0]->Parent; // assumes we are a tree! if(cex_map.find(parent) == cex_map.end()) throw "catastrophe in ReplayHeuristic::ChooseExpand"; Node *old_parent = cex_map[parent]; std::vector &chs = parent->Outgoing->Children; if(old_parent && old_parent->Outgoing){ std::vector &old_chs = old_parent->Outgoing->Children; if(chs.size() == old_chs.size()){ for(unsigned i = 0; i < chs.size(); i++) cex_map[chs[i]] = old_chs[i]; goto matching_done; } else std::cout << "derivation tree does not match old cex" << std::endl; } for(unsigned i = 0; i < chs.size(); i++) cex_map[chs[i]] = 0; } matching_done: Node *old_node = cex_map[node]; if(!old_node) unmatched.insert(node); else if(old_node != node->map) unmatched.insert(node); else matched.insert(node); } Heuristic::ChooseExpand(unmatched,best); } }; /** This proposer class generates conjectures based on the unwinding generated by a previous solver. The assumption is that the provious solver was working on a different abstraction of the same system. The trick is to adapt the annotations in the old unwinding to the new predicates. We start by generating a map from predicates and parameters in the old problem to the new. HACK: mapping is done by cheesy name comparison. */ class HistoryProposer : public Proposer { Duality *old_solver; Duality *new_solver; hash_map > conjectures; public: /** Construct a history solver. */ HistoryProposer(Duality *_old_solver, Duality *_new_solver) : old_solver(_old_solver), new_solver(_new_solver) { // tricky: names in the axioms may have changed -- map them hash_set &old_constants = old_solver->unwinding->ls->get_constants(); hash_set &new_constants = new_solver->rpfp->ls->get_constants(); hash_map cmap; for(hash_set::iterator it = new_constants.begin(), en = new_constants.end(); it != en; ++it) cmap[GetKey(*it)] = *it; hash_map bckg_map; for(hash_set::iterator it = old_constants.begin(), en = old_constants.end(); it != en; ++it){ func_decl f = new_solver->ctx.translate(*it); // move to new context if(cmap.find(GetKey(f)) != cmap.end()) bckg_map[f] = cmap[GetKey(f)]; // else // std::cout << "constant not matched\n"; } RPFP *old_unwinding = old_solver->unwinding; hash_map > pred_match; // index all the predicates in the old unwinding for(unsigned i = 0; i < old_unwinding->nodes.size(); i++){ Node *node = old_unwinding->nodes[i]; std::string key = GetKey(node); pred_match[key].push_back(node); } // match with predicates in the new RPFP RPFP *rpfp = new_solver->rpfp; for(unsigned i = 0; i < rpfp->nodes.size(); i++){ Node *node = rpfp->nodes[i]; std::string key = GetKey(node); std::vector &matches = pred_match[key]; for(unsigned j = 0; j < matches.size(); j++) MatchNodes(node,matches[j],bckg_map); } } virtual std::vector &Propose(Node *node){ return conjectures[node->map]; } virtual ~HistoryProposer(){ }; private: void MatchNodes(Node *new_node, Node *old_node, hash_map &bckg_map){ if(old_node->Annotation.IsFull()) return; // don't conjecture true! hash_map var_match; std::vector &new_params = new_node->Annotation.IndParams; // Index the new parameters by their keys for(unsigned i = 0; i < new_params.size(); i++) var_match[GetKey(new_params[i])] = new_params[i]; RPFP::Transformer &old = old_node->Annotation; std::vector from_params = old.IndParams; for(unsigned j = 0; j < from_params.size(); j++) from_params[j] = new_solver->ctx.translate(from_params[j]); // get in new context std::vector to_params = from_params; for(unsigned j = 0; j < to_params.size(); j++){ std::string key = GetKey(to_params[j]); if(var_match.find(key) == var_match.end()){ // std::cout << "unmatched parameter!\n"; return; } to_params[j] = var_match[key]; } expr fmla = new_solver->ctx.translate(old.Formula); // get in new context fmla = new_solver->rpfp->SubstParams(old.IndParams,to_params,fmla); // substitute parameters hash_map memo; fmla = new_solver->rpfp->SubstRec(memo,bckg_map,fmla); // substitute background constants RPFP::Transformer new_annot = new_node->Annotation; new_annot.Formula = fmla; conjectures[new_node].push_back(new_annot); } // We match names by removing suffixes beginning with double at sign std::string GetKey(Node *node){ return GetKey(node->Name); } std::string GetKey(const expr &var){ return GetKey(var.decl()); } std::string GetKey(const func_decl &f){ std::string name = f.name().str(); int idx = name.find("@@"); if(idx >= 0) name.erase(idx); return name; } }; }; static int stop_event = -1; class StreamReporter : public Reporter { std::ostream &s; public: StreamReporter(RPFP *_rpfp, std::ostream &_s) : Reporter(_rpfp), s(_s) {event = 0; depth = -1;} int event; int depth; void ev(){ if(stop_event == event){ std::cout << "stop!\n"; } s << "[" << event++ << "]" ; } virtual void Extend(RPFP::Node *node){ ev(); s << "node " << node->number << ": " << node->Name.name(); std::vector &rps = node->Outgoing->Children; for(unsigned i = 0; i < rps.size(); i++) s << " " << rps[i]->number; s << std::endl; } virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){ ev(); s << "update " << node->number << " " << node->Name.name() << ": "; rpfp->Summarize(update.Formula); if(depth > 0) s << " (depth=" << depth << ")"; if(eager) s << " (eager)"; s << std::endl; } virtual void Bound(RPFP::Node *node){ ev(); s << "check " << node->number << std::endl; } virtual void Expand(RPFP::Edge *edge){ RPFP::Node *node = edge->Parent; ev(); s << "expand " << node->map->number << " " << node->Name.name(); if(depth > 0) s << " (depth=" << depth << ")"; s << std::endl; } virtual void Depth(int d){ depth = d; } virtual void AddCover(RPFP::Node *covered, std::vector &covering){ ev(); s << "cover " << covered->Name.name() << ": " << covered->number << " by "; for(unsigned i = 0; i < covering.size(); i++) s << covering[i]->number << " "; s << std::endl; } virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){ ev(); s << "uncover " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; } virtual void Forcing(RPFP::Node *covered, RPFP::Node *covering){ ev(); s << "forcing " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; } virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){ ev(); s << "conjecture " << node->number << " " << node->Name.name() << ": "; rpfp->Summarize(t.Formula); s << std::endl; } virtual void Dominates(RPFP::Node *node, RPFP::Node *other){ ev(); s << "dominates " << node->Name.name() << ": " << node->number << " > " << other->number << std::endl; } virtual void InductionFailure(RPFP::Edge *edge, const std::vector &children){ ev(); s << "induction failure: " << edge->Parent->Name.name() << ", children ="; for(unsigned i = 0; i < children.size(); i++) s << " " << children[i]->number; s << std::endl; } virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){ ev(); s << "underapprox " << node->number << " " << node->Name.name() << ": " << update.Formula << std::endl; } virtual void Reject(RPFP::Edge *edge, const std::vector &children){ ev(); s << "reject " << edge->Parent->number << " " << edge->Parent->Name.name() << ": "; for(unsigned i = 0; i < children.size(); i++) s << " " << children[i]->number; s << std::endl; } virtual void Message(const std::string &msg){ ev(); s << "msg " << msg << std::endl; } }; class DualityDepthBounded : public Solver { Duality *duality; context &ctx; // Z3 context solver &slvr; // Z3 solver public: DualityDepthBounded(RPFP *_rpfp) : ctx(_rpfp->ctx), slvr(_rpfp->slvr()){ rpfp = _rpfp; DepthBoundRPFP(); duality = alloc(Duality,drpfp); } ~DualityDepthBounded(){ dealloc(duality); delete drpfp; } bool Solve(){ int depth_bound = 10; bool res; SetMaxDepthRPFP(depth_bound); duality->PreSolve(); while(true){ res = duality->SolveMain(); if(!res || GetSolution()) break; depth_bound++; SetMaxDepthRPFP(depth_bound); res = duality->RecheckBounds(); if(!res) break; } duality->PostSolve(); if(!res) ConvertCex(); return res; } Counterexample &GetCounterexample(){ return cex; } bool SetOption(const std::string &option, const std::string &value){ return duality->SetOption(option,value); } virtual void LearnFrom(Solver *old_solver){ DualityDepthBounded *old = dynamic_cast(old_solver); if(old){ duality->LearnFrom(old->duality); } } bool IsResultRecursionBounded(){ return duality->IsResultRecursionBounded(); } void Cancel(){ duality->Cancel(); } typedef RPFP::Node Node; typedef RPFP::Edge Edge; RPFP *rpfp, *drpfp; hash_map db_map, db_rev_map; hash_map db_edge_rev_map; std::vector db_saved_bounds; Counterexample cex; expr AddParamToRels(hash_map &memo, hash_map &rmap, const expr &p, const expr &t) { std::pair foo(t,expr(ctx)); std::pair::iterator, bool> bar = memo.insert(foo); expr &res = bar.first->second; if(!bar.second) return res; if (t.is_app()) { func_decl f = t.decl(); std::vector args; int nargs = t.num_args(); for(int i = 0; i < nargs; i++) args.push_back(AddParamToRels(memo, rmap, p, t.arg(i))); hash_map::iterator rit = rmap.find(f); if(rit != rmap.end()){ args.push_back(p); res = (rit->second)(args); res = ctx.make(And,res,ctx.make(Geq,p,ctx.int_val(0))); } else res = f(args); } else if (t.is_quantifier()) { expr body = AddParamToRels(memo, rmap, p, t.body()); res = clone_quantifier(t, body); } else res = t; return res; } void DepthBoundRPFP(){ drpfp = new RPFP(rpfp->ls); expr dvar = ctx.int_const("@depth"); expr dmax = ctx.int_const("@depth_max"); for(unsigned i = 0; i < rpfp->nodes.size(); i++){ Node *node = rpfp->nodes[i]; std::vector arg_sorts; const std::vector ¶ms = node->Annotation.IndParams; for(unsigned j = 0; j < params.size(); j++) arg_sorts.push_back(params[j].get_sort()); arg_sorts.push_back(ctx.int_sort()); std::string new_name = std::string("@db@") + node->Name.name().str(); func_decl f = ctx.function(new_name.c_str(),arg_sorts.size(), &arg_sorts[0],ctx.bool_sort()); std::vector args = params; args.push_back(dvar); expr pat = f(args); Node *dnode = drpfp->CreateNode(pat); db_map[node] = dnode; db_rev_map[dnode] = node; expr bound_fmla = node->Bound.Formula; if(!eq(bound_fmla,ctx.bool_val(true))){ bound_fmla = implies(dvar == dmax,bound_fmla); dnode->Bound.Formula = bound_fmla; } db_saved_bounds.push_back(bound_fmla); // dnode->Annotation.Formula = ctx.make(And,node->Annotation.Formula,ctx.make(Geq,dvar,ctx.int_val(0))); } for(unsigned i = 0; i < rpfp->edges.size(); i++){ Edge *edge = rpfp->edges[i]; std::vector new_children; std::vector new_relparams; hash_map rmap; for(unsigned j = 0; j < edge->Children.size(); j++){ Node *ch = edge->Children[j]; Node *nch = db_map[ch]; func_decl f = nch->Name; func_decl sf = drpfp->NumberPred(f,j); new_children.push_back(nch); new_relparams.push_back(sf); rmap[edge->F.RelParams[j]] = sf; } std::vector new_indparams = edge->F.IndParams; new_indparams.push_back(dvar); hash_map memo; expr new_fmla = AddParamToRels(memo,rmap,ctx.make(Sub,dvar,ctx.int_val(1)),edge->F.Formula); RPFP::Transformer new_t = drpfp->CreateTransformer(new_relparams,new_indparams,new_fmla); Node *new_parent = db_map[edge->Parent]; db_edge_rev_map[drpfp->CreateEdge(new_parent,new_t,new_children)] = edge; } } void SetMaxDepthRPFP(int depth){ hash_map subst; expr dmax = ctx.int_const("@depth_max"); subst[dmax] = ctx.int_val(depth); for(unsigned i = 0; i < drpfp->nodes.size(); i++){ Node *node = drpfp->nodes[i]; expr fmla = db_saved_bounds[i]; fmla = drpfp->SubstRec(subst,fmla); node->Bound.Formula = fmla; } } void ConvertCex(){ cex.clear(); RPFP *tree = new RPFP(rpfp->ls); Node *root; Counterexample &dctx = duality->GetCounterexample(); hash_map ctx_node_map; for(unsigned i = 0; i < dctx.get_tree()->nodes.size(); i++){ Node *dnode = dctx.get_tree()->nodes[i]; Node *onode = db_rev_map[dnode->map->map]; Node *node = tree->CloneNode(onode); node->number = dnode->number; // numbers have to match for model to make sense ctx_node_map[dnode] = node; if(dnode == dctx.get_root()) root = node; } for(unsigned i = 0; i < dctx.get_tree()->edges.size(); i++){ Edge *dedge = dctx.get_tree()->edges[i]; Edge *oedge = db_edge_rev_map[dedge->map]; Node *parent = ctx_node_map[dedge->Parent]; std::vector chs; for(unsigned j = 0; j < dedge->Children.size(); j++) chs.push_back(ctx_node_map[dedge->Children[j]]); Edge *edge = tree->CreateEdge(parent,oedge->F,chs); edge->number = dedge->number; // numbers have to match for model to make sense edge->map = oedge; } tree->dualModel = dctx.get_tree()->dualModel; cex.set(tree,root); } bool GetSolution(){ for(unsigned i = 0; i < rpfp->nodes.size(); i++) if(!drpfp->nodes[i]->Annotation.SubsetEq(rpfp->nodes[i]->Bound)) return false; expr dvar = ctx.int_const("@depth"); hash_map subst; subst[dvar] = ctx.int_val(INT_MAX); for(unsigned i = 0; i < rpfp->nodes.size(); i++){ expr fmla = drpfp->nodes[i]->Annotation.Formula; fmla = drpfp->SubstRec(subst,fmla); fmla = fmla.simplify(); rpfp->nodes[i]->Annotation.Formula = fmla; } return true; } void UndoDepthBoundRPFP(){ #if 0 if(cex.get_tree()){ // here, need to map the cex back... } // also need to map the proof back, but we don't... #endif } }; Solver *Solver::Create(const std::string &solver_class, RPFP *rpfp){ // Solver *s = alloc(DualityDepthBounded,rpfp); Solver *s = alloc(Duality,rpfp); return s; } Reporter *CreateStdoutReporter(RPFP *rpfp){ return new StreamReporter(rpfp, std::cout); } class ConjectureFileReporter : public Reporter { std::ofstream s; public: ConjectureFileReporter(RPFP *_rpfp, const std::string &fname) : Reporter(_rpfp), s(fname.c_str()) {} virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){ s << "(define-fun " << node->Name.name() << " ("; for(unsigned i = 0; i < update.IndParams.size(); i++){ if(i != 0) s << " "; s << "(" << update.IndParams[i] << " " << update.IndParams[i].get_sort() << ")"; } s << ") Bool \n"; s << update.Formula << ")\n"; s << std::endl; } }; Reporter *CreateConjectureFileReporter(RPFP *rpfp, const std::string &fname){ return new ConjectureFileReporter(rpfp, fname); } } z3-4.4.0/src/duality/duality_profiling.h0000755000175000017500000000113312540347414020005 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: duality_profiling.h Abstract: collection performance information for duality Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef DUALITYPROFILING_H #define DUALITYPROFILING_H #include namespace Duality { /** Start a timer with given name */ void timer_start(const char *); /** Stop a timer with given name */ void timer_stop(const char *); /** Print out timings */ void print_profile(std::ostream &s); /** Show the current time. */ void show_time(); } #endif z3-4.4.0/src/duality/duality_wrapper.h0000644000175000017500000015332012540347414017477 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: duality_wrapper.h Abstract: wrap various Z3 classes in the style expected by duality Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef __DUALITY_WRAPPER_H_ #define __DUALITY_WRAPPER_H_ #include #include #include #include #include #include #include #include"version.h" #include #include "iz3hash.h" #include "model.h" #include "solver.h" #include"well_sorted.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"datatype_decl_plugin.h" #include"array_decl_plugin.h" #include"ast_translation.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"ast_smt_pp.h" #include"ast_smt2_pp.h" #include"th_rewriter.h" #include"var_subst.h" #include"expr_substitution.h" #include"pp.h" #include"scoped_ctrl_c.h" #include"cancel_eh.h" #include"scoped_timer.h" #include"scoped_proof.h" namespace Duality { class exception; class config; class context; class symbol; class params; class ast; class sort; class func_decl; class expr; class solver; class goal; class tactic; class probe; class model; class func_interp; class func_entry; class statistics; class apply_result; class fixedpoint; class literals; /** Duality global configuration object. */ class config { params_ref m_params; config & operator=(config const & s); public: config(config const & s) : m_params(s.m_params) {} config(const params_ref &_params) : m_params(_params) {} config() { } // TODO: create a new params object here ~config() { } void set(char const * param, char const * value) { m_params.set_str(param,value); } void set(char const * param, bool value) { m_params.set_bool(param,value); } void set(char const * param, int value) { m_params.set_uint(param,value); } params_ref &get() {return m_params;} const params_ref &get() const {return m_params;} }; enum decl_kind { True, False, And, Or, Not, Iff, Ite, Equal, Implies, Distinct, Xor, Oeq, Interp, Leq, Geq, Lt, Gt, Plus, Sub, Uminus, Times, Div, Idiv, Rem, Mod, Power, ToReal, ToInt, IsInt, Select, Store, ConstArray, ArrayDefault, ArrayMap, SetUnion, SetIntersect, SetDifference, SetComplement, SetSubSet, AsArray, Numeral, Forall, Exists, Variable, Uninterpreted, OtherBasic, OtherArith, OtherArray, Other }; enum sort_kind {BoolSort,IntSort,RealSort,ArraySort,UninterpretedSort,UnknownSort}; /** A context has an ast manager global configuration options, etc. */ class context { protected: ast_manager &mgr; config m_config; family_id m_basic_fid; family_id m_array_fid; family_id m_arith_fid; family_id m_bv_fid; family_id m_dt_fid; family_id m_datalog_fid; arith_util m_arith_util; public: context(ast_manager &_manager, const config &_config) : mgr(_manager), m_config(_config), m_arith_util(_manager) { m_basic_fid = m().get_basic_family_id(); m_arith_fid = m().mk_family_id("arith"); m_bv_fid = m().mk_family_id("bv"); m_array_fid = m().mk_family_id("array"); m_dt_fid = m().mk_family_id("datatype"); m_datalog_fid = m().mk_family_id("datalog_relation"); } ~context() { } ast_manager &m() const {return *(ast_manager *)&mgr;} void set(char const * param, char const * value) { m_config.set(param,value); } void set(char const * param, bool value) { m_config.set(param,value); } void set(char const * param, int value) { m_config.set(param,value); } config &get_config() {return m_config;} symbol str_symbol(char const * s); symbol int_symbol(int n); sort bool_sort(); sort int_sort(); sort real_sort(); sort bv_sort(unsigned sz); sort array_sort(sort d, sort r); func_decl function(symbol const & name, unsigned arity, sort const * domain, sort const & range); func_decl function(char const * name, unsigned arity, sort const * domain, sort const & range); func_decl function(char const * name, sort const & domain, sort const & range); func_decl function(char const * name, sort const & d1, sort const & d2, sort const & range); func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range); func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range); func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range); func_decl fresh_func_decl(char const * name, const std::vector &domain, sort const & range); func_decl fresh_func_decl(char const * name, sort const & range); expr constant(symbol const & name, sort const & s); expr constant(char const * name, sort const & s); expr constant(const std::string &name, sort const & s); expr bool_const(char const * name); expr int_const(char const * name); expr real_const(char const * name); expr bv_const(char const * name, unsigned sz); expr bool_val(bool b); expr int_val(int n); expr int_val(unsigned n); expr int_val(char const * n); expr real_val(int n, int d); expr real_val(int n); expr real_val(unsigned n); expr real_val(char const * n); expr bv_val(int n, unsigned sz); expr bv_val(unsigned n, unsigned sz); expr bv_val(char const * n, unsigned sz); expr num_val(int n, sort const & s); expr mki(family_id fid, ::decl_kind dk, int n, ::expr **args); expr make(decl_kind op, int n, ::expr **args); expr make(decl_kind op, const std::vector &args); expr make(decl_kind op); expr make(decl_kind op, const expr &arg0); expr make(decl_kind op, const expr &arg0, const expr &arg1); expr make(decl_kind op, const expr &arg0, const expr &arg1, const expr &arg2); expr make_quant(decl_kind op, const std::vector &bvs, const expr &body); expr make_quant(decl_kind op, const std::vector &_sorts, const std::vector &_names, const expr &body); expr make_var(int idx, const sort &s); decl_kind get_decl_kind(const func_decl &t); sort_kind get_sort_kind(const sort &s); expr translate(const expr &e); func_decl translate(const func_decl &); void print_expr(std::ostream &s, const ast &e); fixedpoint mk_fixedpoint(); expr cook(::expr *a); std::vector cook(ptr_vector< ::expr> v); ::expr *uncook(const expr &a); }; template class z3array { T * m_array; unsigned m_size; z3array(z3array const & s); z3array & operator=(z3array const & s); public: z3array(unsigned sz):m_size(sz) { m_array = new T[sz]; } ~z3array() { delete[] m_array; } unsigned size() const { return m_size; } T & operator[](unsigned i) { assert(i < m_size); return m_array[i]; } T const & operator[](unsigned i) const { assert(i < m_size); return m_array[i]; } T const * ptr() const { return m_array; } T * ptr() { return m_array; } }; class object { protected: context * m_ctx; public: object(): m_ctx((context *)0) {} object(context & c):m_ctx(&c) {} object(object const & s):m_ctx(s.m_ctx) {} context & ctx() const { return *m_ctx; } friend void check_context(object const & a, object const & b) { assert(a.m_ctx == b.m_ctx); } ast_manager &m() const {return m_ctx->m();} }; class symbol : public object { ::symbol m_sym; public: symbol(context & c, ::symbol s):object(c), m_sym(s) {} symbol(symbol const & s):object(s), m_sym(s.m_sym) {} symbol & operator=(symbol const & s) { m_ctx = s.m_ctx; m_sym = s.m_sym; return *this; } operator ::symbol() const {return m_sym;} std::string str() const { if (m_sym.is_numerical()) { std::ostringstream buffer; buffer << m_sym.get_num(); return buffer.str(); } else { return m_sym.bare_str(); } } friend std::ostream & operator<<(std::ostream & out, symbol const & s){ return out << s.str(); } friend bool operator==(const symbol &x, const symbol &y){ return x.m_sym == y.m_sym; } }; class params : public config {}; /** Wrapper around an ast pointer */ class ast_i : public object { protected: ::ast *_ast; public: ::ast * const &raw() const {return _ast;} ast_i(context & c, ::ast *a = 0) : object(c) {_ast = a;} ast_i(){_ast = 0;} bool eq(const ast_i &other) const { return _ast == other._ast; } bool lt(const ast_i &other) const { return _ast < other._ast; } friend bool operator==(const ast_i &x, const ast_i&y){ return x.eq(y); } friend bool operator!=(const ast_i &x, const ast_i&y){ return !x.eq(y); } friend bool operator<(const ast_i &x, const ast_i&y){ return x.lt(y); } size_t hash() const {return (size_t)_ast;} bool null() const {return !_ast;} }; /** Reference counting verison of above */ class ast : public ast_i { public: operator ::ast*() const { return raw(); } friend bool eq(ast const & a, ast const & b) { return a.raw() == b.raw(); } ast(context &c, ::ast *a = 0) : ast_i(c,a) { if(_ast) m().inc_ref(a); } ast() {} ast(const ast &other) : ast_i(other) { if(_ast) m().inc_ref(_ast); } ast &operator=(const ast &other) { if(_ast) m().dec_ref(_ast); _ast = other._ast; m_ctx = other.m_ctx; if(_ast) m().inc_ref(_ast); return *this; } ~ast(){ if(_ast) m().dec_ref(_ast); } void show() const; }; class sort : public ast { public: sort(context & c):ast(c) {} sort(context & c, ::sort *s):ast(c, s) {} sort(sort const & s):ast(s) {} operator ::sort*() const { return to_sort(raw()); } sort & operator=(sort const & s) { return static_cast(ast::operator=(s)); } bool is_bool() const { return m().is_bool(*this); } bool is_int() const { return ctx().get_sort_kind(*this) == IntSort; } bool is_real() const { return ctx().get_sort_kind(*this) == RealSort; } bool is_arith() const; bool is_array() const { return ctx().get_sort_kind(*this) == ArraySort; } bool is_datatype() const; bool is_relation() const; bool is_finite_domain() const; sort array_domain() const; sort array_range() const; friend std::ostream & operator<<(std::ostream & out, sort const & m){ m.ctx().print_expr(out,m); return out; } }; class func_decl : public ast { public: func_decl() : ast() {} func_decl(context & c):ast(c) {} func_decl(context & c, ::func_decl *n):ast(c, n) {} func_decl(func_decl const & s):ast(s) {} operator ::func_decl*() const { return to_func_decl(*this); } func_decl & operator=(func_decl const & s) { return static_cast(ast::operator=(s)); } unsigned arity() const; sort domain(unsigned i) const; sort range() const; symbol name() const {return symbol(ctx(),to_func_decl(raw())->get_name());} decl_kind get_decl_kind() const; bool is_const() const { return arity() == 0; } expr operator()(unsigned n, expr const * args) const; expr operator()(const std::vector &args) const; expr operator()() const; expr operator()(expr const & a) const; expr operator()(int a) const; expr operator()(expr const & a1, expr const & a2) const; expr operator()(expr const & a1, int a2) const; expr operator()(int a1, expr const & a2) const; expr operator()(expr const & a1, expr const & a2, expr const & a3) const; expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const; expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const; func_decl get_func_decl_parameter(unsigned idx){ return func_decl(ctx(),to_func_decl(to_func_decl(raw())->get_parameters()[idx].get_ast())); } }; class expr : public ast { public: expr() : ast() {} expr(context & c):ast(c) {} expr(context & c, ::ast *n):ast(c, n) {} expr(expr const & n):ast(n) {} expr & operator=(expr const & n) { return static_cast(ast::operator=(n)); } operator ::expr*() const { return to_expr(raw()); } unsigned get_id() const {return to_expr(raw())->get_id();} sort get_sort() const { return sort(ctx(),m().get_sort(to_expr(raw()))); } bool is_bool() const { return get_sort().is_bool(); } bool is_int() const { return get_sort().is_int(); } bool is_real() const { return get_sort().is_real(); } bool is_arith() const { return get_sort().is_arith(); } bool is_array() const { return get_sort().is_array(); } bool is_datatype() const { return get_sort().is_datatype(); } bool is_relation() const { return get_sort().is_relation(); } bool is_finite_domain() const { return get_sort().is_finite_domain(); } bool is_true() const {return is_app() && decl().get_decl_kind() == True; } bool is_numeral() const { return is_app() && decl().get_decl_kind() == OtherArith && m().is_unique_value(to_expr(raw())); } bool is_app() const {return raw()->get_kind() == AST_APP;} bool is_quantifier() const {return raw()->get_kind() == AST_QUANTIFIER;} bool is_var() const {return raw()->get_kind() == AST_VAR;} bool is_label (bool &pos,std::vector &names) const ; bool is_ground() const {return to_app(raw())->is_ground();} bool has_quantifiers() const {return to_app(raw())->has_quantifiers();} bool has_free(int idx) const { used_vars proc; proc.process(to_expr(raw())); return proc.contains(idx); } unsigned get_max_var_idx_plus_1() const { used_vars proc; proc.process(to_expr(raw())); return proc.get_max_found_var_idx_plus_1(); } // operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } func_decl decl() const {return func_decl(ctx(),to_app(raw())->get_decl());} unsigned num_args() const { ast_kind dk = raw()->get_kind(); switch(dk){ case AST_APP: return to_app(raw())->get_num_args(); case AST_QUANTIFIER: return 1; case AST_VAR: return 0; default:; } SASSERT(0); return 0; } expr arg(unsigned i) const { ast_kind dk = raw()->get_kind(); switch(dk){ case AST_APP: return ctx().cook(to_app(raw())->get_arg(i)); case AST_QUANTIFIER: return ctx().cook(to_quantifier(raw())->get_expr()); default:; } assert(0); return expr(); } expr body() const { return ctx().cook(to_quantifier(raw())->get_expr()); } friend expr operator!(expr const & a) { // ::expr *e = a; return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_NOT,a)); } friend expr operator&&(expr const & a, expr const & b) { return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_AND,a,b)); } friend expr operator||(expr const & a, expr const & b) { return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_OR,a,b)); } friend expr implies(expr const & a, expr const & b) { return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_IMPLIES,a,b)); } friend expr operator==(expr const & a, expr const & b) { return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_EQ,a,b)); } friend expr operator!=(expr const & a, expr const & b) { return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DISTINCT,a,b)); } friend expr operator+(expr const & a, expr const & b) { return a.ctx().make(Plus,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_ADD,a,b)); } friend expr operator*(expr const & a, expr const & b) { return a.ctx().make(Times,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_MUL,a,b)); } friend expr operator/(expr const & a, expr const & b) { return a.ctx().make(Div,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DIV,a,b)); } friend expr operator-(expr const & a) { return a.ctx().make(Uminus,a); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_UMINUS,a)); } friend expr operator-(expr const & a, expr const & b) { return a.ctx().make(Sub,a,b); // expr(a.ctx(),a.m().mk_app(a.ctx().m_arith_fid,OP_SUB,a,b)); } friend expr operator<=(expr const & a, expr const & b) { return a.ctx().make(Leq,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LE,a,b)); } friend expr operator>=(expr const & a, expr const & b) { return a.ctx().make(Geq,a,b); //expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GE,a,b)); } friend expr operator<(expr const & a, expr const & b) { return a.ctx().make(Lt,a,b); expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LT,a,b)); } friend expr operator>(expr const & a, expr const & b) { return a.ctx().make(Gt,a,b); expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GT,a,b)); } expr simplify() const; expr simplify(params const & p) const; expr qe_lite() const; expr qe_lite(const std::set &idxs, bool index_of_bound) const; friend expr clone_quantifier(const expr &, const expr &); friend expr clone_quantifier(const expr &q, const expr &b, const std::vector &patterns); friend expr clone_quantifier(decl_kind, const expr &, const expr &); friend std::ostream & operator<<(std::ostream & out, expr const & m){ m.ctx().print_expr(out,m); return out; } void get_patterns(std::vector &pats) const ; unsigned get_quantifier_num_bound() const { return to_quantifier(raw())->get_num_decls(); } unsigned get_index_value() const { var* va = to_var(raw()); return va->get_idx(); } bool is_quantifier_forall() const { return to_quantifier(raw())->is_forall(); } sort get_quantifier_bound_sort(unsigned n) const { return sort(ctx(),to_quantifier(raw())->get_decl_sort(n)); } symbol get_quantifier_bound_name(unsigned n) const { return symbol(ctx(),to_quantifier(raw())->get_decl_names()[n]); } friend expr forall(const std::vector &quants, const expr &body); friend expr exists(const std::vector &quants, const expr &body); }; typedef ::decl_kind pfrule; class proof : public ast { public: proof(context & c):ast(c) {} proof(context & c, ::proof *s):ast(c, s) {} proof(proof const & s):ast(s) {} operator ::proof*() const { return to_app(raw()); } proof & operator=(proof const & s) { return static_cast(ast::operator=(s)); } pfrule rule() const { ::func_decl *d = to_app(raw())->get_decl(); return d->get_decl_kind(); } unsigned num_prems() const { return to_app(raw())->get_num_args() - 1; } expr conc() const { return ctx().cook(to_app(raw())->get_arg(num_prems())); } proof prem(unsigned i) const { return proof(ctx(),to_app(to_app(raw())->get_arg(i))); } void get_assumptions(std::vector &assumps); }; #if 0 #if Z3_MAJOR_VERSION > 4 || Z3_MAJOR_VERSION == 4 && Z3_MINOR_VERSION >= 3 template class ast_vector_tpl : public object { Z3_ast_vector m_vector; void init(Z3_ast_vector v) { Z3_ast_vector_inc_ref(ctx(), v); m_vector = v; } public: ast_vector_tpl(context & c):object(c) { init(Z3_mk_ast_vector(c)); } ast_vector_tpl(context & c, Z3_ast_vector v):object(c) { init(v); } ast_vector_tpl(ast_vector_tpl const & s):object(s), m_vector(s.m_vector) { Z3_ast_vector_inc_ref(ctx(), m_vector); } ~ast_vector_tpl() { /* Z3_ast_vector_dec_ref(ctx(), m_vector); */ } operator Z3_ast_vector() const { return m_vector; } unsigned size() const { return Z3_ast_vector_size(ctx(), m_vector); } T operator[](unsigned i) const { Z3_ast r = Z3_ast_vector_get(ctx(), m_vector, i); check_error(); return cast_ast()(ctx(), r); } void push_back(T const & e) { Z3_ast_vector_push(ctx(), m_vector, e); check_error(); } void resize(unsigned sz) { Z3_ast_vector_resize(ctx(), m_vector, sz); check_error(); } T back() const { return operator[](size() - 1); } void pop_back() { assert(size() > 0); resize(size() - 1); } bool empty() const { return size() == 0; } ast_vector_tpl & operator=(ast_vector_tpl const & s) { Z3_ast_vector_inc_ref(s.ctx(), s.m_vector); // Z3_ast_vector_dec_ref(ctx(), m_vector); m_ctx = s.m_ctx; m_vector = s.m_vector; return *this; } friend std::ostream & operator<<(std::ostream & out, ast_vector_tpl const & v) { out << Z3_ast_vector_to_string(v.ctx(), v); return out; } }; typedef ast_vector_tpl ast_vector; typedef ast_vector_tpl expr_vector; typedef ast_vector_tpl sort_vector; typedef ast_vector_tpl func_decl_vector; #endif #endif class func_interp : public object { ::func_interp * m_interp; void init(::func_interp * e) { m_interp = e; } public: func_interp(context & c, ::func_interp * e):object(c) { init(e); } func_interp(func_interp const & s):object(s) { init(s.m_interp); } ~func_interp() { } operator ::func_interp *() const { return m_interp; } func_interp & operator=(func_interp const & s) { m_ctx = s.m_ctx; m_interp = s.m_interp; return *this; } unsigned num_entries() const { return m_interp->num_entries(); } expr get_arg(unsigned ent, unsigned arg) const { return expr(ctx(),m_interp->get_entry(ent)->get_arg(arg)); } expr get_value(unsigned ent) const { return expr(ctx(),m_interp->get_entry(ent)->get_result()); } expr else_value() const { return expr(ctx(),m_interp->get_else()); } }; class model : public object { model_ref m_model; void init(::model *m) { m_model = m; } public: model(context & c, ::model * m = 0):object(c), m_model(m) { } model(model const & s):object(s), m_model(s.m_model) { } ~model() { } operator ::model *() const { return m_model.get(); } model & operator=(model const & s) { // ::model *_inc_ref(s.ctx(), s.m_model); // ::model *_dec_ref(ctx(), m_model); m_ctx = s.m_ctx; m_model = s.m_model.get(); return *this; } model & operator=(::model *s) { m_model = s; return *this; } bool null() const {return !m_model;} expr eval(expr const & n, bool model_completion=true) const { ::model * _m = m_model.get(); expr_ref result(ctx().m()); _m->eval(n, result, model_completion); return expr(ctx(), result); } void show() const; void show_hash() const; unsigned num_consts() const {return m_model.get()->get_num_constants();} unsigned num_funcs() const {return m_model.get()->get_num_functions();} func_decl get_const_decl(unsigned i) const {return func_decl(ctx(),m_model.get()->get_constant(i));} func_decl get_func_decl(unsigned i) const {return func_decl(ctx(),m_model.get()->get_function(i));} unsigned size() const; func_decl operator[](unsigned i) const; expr get_const_interp(func_decl f) const { return ctx().cook(m_model->get_const_interp(to_func_decl(f.raw()))); } func_interp get_func_interp(func_decl f) const { return func_interp(ctx(),m_model->get_func_interp(to_func_decl(f.raw()))); } #if 0 friend std::ostream & operator<<(std::ostream & out, model const & m) { out << Z3_model_to_string(m.ctx(), m); return out; } #endif }; #if 0 class stats : public object { Z3_stats m_stats; void init(Z3_stats e) { m_stats = e; Z3_stats_inc_ref(ctx(), m_stats); } public: stats(context & c):object(c), m_stats(0) {} stats(context & c, Z3_stats e):object(c) { init(e); } stats(stats const & s):object(s) { init(s.m_stats); } ~stats() { if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); } operator Z3_stats() const { return m_stats; } stats & operator=(stats const & s) { Z3_stats_inc_ref(s.ctx(), s.m_stats); if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); m_ctx = s.m_ctx; m_stats = s.m_stats; return *this; } unsigned size() const { return Z3_stats_size(ctx(), m_stats); } std::string key(unsigned i) const { Z3_string s = Z3_stats_get_key(ctx(), m_stats, i); check_error(); return s; } bool is_uint(unsigned i) const { Z3_bool r = Z3_stats_is_uint(ctx(), m_stats, i); check_error(); return r != 0; } bool is_double(unsigned i) const { Z3_bool r = Z3_stats_is_double(ctx(), m_stats, i); check_error(); return r != 0; } unsigned uint_value(unsigned i) const { unsigned r = Z3_stats_get_uint_value(ctx(), m_stats, i); check_error(); return r; } double double_value(unsigned i) const { double r = Z3_stats_get_double_value(ctx(), m_stats, i); check_error(); return r; } friend std::ostream & operator<<(std::ostream & out, stats const & s) { out << Z3_stats_to_string(s.ctx(), s); return out; } }; #endif enum check_result { unsat, sat, unknown }; class fixedpoint : public object { public: void get_rules(std::vector &rules); void get_assertions(std::vector &rules); void register_relation(const func_decl &rela); void add_rule(const expr &clause, const symbol &name); void assert_cnst(const expr &cnst); }; inline std::ostream & operator<<(std::ostream & out, check_result r) { if (r == unsat) out << "unsat"; else if (r == sat) out << "sat"; else out << "unknown"; return out; } inline check_result to_check_result(lbool l) { if (l == l_true) return sat; else if (l == l_false) return unsat; return unknown; } class solver : public object { protected: ::solver *m_solver; model the_model; bool canceled; proof_gen_mode m_mode; bool extensional; public: solver(context & c, bool extensional = false, bool models = true); solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; canceled = false;} solver(solver const & s):object(s), the_model(s.the_model) { m_solver = s.m_solver; canceled = false;} ~solver() { if(m_solver) dealloc(m_solver); } operator ::solver*() const { return m_solver; } solver & operator=(solver const & s) { m_ctx = s.m_ctx; m_solver = s.m_solver; the_model = s.the_model; m_mode = s.m_mode; return *this; } struct cancel_exception {}; void checkpoint(){ if(canceled) throw(cancel_exception()); } // void set(params const & p) { Z3_solver_set_params(ctx(), m_solver, p); check_error(); } void push() { scoped_proof_mode spm(m(),m_mode); m_solver->push(); } void pop(unsigned n = 1) { scoped_proof_mode spm(m(),m_mode); m_solver->pop(n); } // void reset() { Z3_solver_reset(ctx(), m_solver); check_error(); } void add(expr const & e) { scoped_proof_mode spm(m(),m_mode); m_solver->assert_expr(e); } check_result check() { scoped_proof_mode spm(m(),m_mode); checkpoint(); lbool r = m_solver->check_sat(0,0); model_ref m; m_solver->get_model(m); the_model = m.get(); return to_check_result(r); } check_result check_keep_model(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { scoped_proof_mode spm(m(),m_mode); model old_model(the_model); check_result res = check(n,assumptions,core_size,core); if(the_model == 0) the_model = old_model; return res; } check_result check(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { scoped_proof_mode spm(m(),m_mode); checkpoint(); std::vector< ::expr *> _assumptions(n); for (unsigned i = 0; i < n; i++) { _assumptions[i] = to_expr(assumptions[i]); } the_model = 0; lbool r = m_solver->check_sat(n,&_assumptions[0]); if(core_size && core){ ptr_vector< ::expr> _core; m_solver->get_unsat_core(_core); *core_size = _core.size(); for(unsigned i = 0; i < *core_size; i++) core[i] = expr(ctx(),_core[i]); } model_ref m; m_solver->get_model(m); the_model = m.get(); return to_check_result(r); } #if 0 check_result check(expr_vector assumptions) { scoped_proof_mode spm(m(),m_mode); unsigned n = assumptions.size(); z3array _assumptions(n); for (unsigned i = 0; i < n; i++) { check_context(*this, assumptions[i]); _assumptions[i] = assumptions[i]; } Z3_lbool r = Z3_check_assumptions(ctx(), m_solver, n, _assumptions.ptr()); check_error(); return to_check_result(r); } #endif model get_model() const { return model(ctx(), the_model); } // std::string reason_unknown() const { Z3_string r = Z3_solver_get_reason_unknown(ctx(), m_solver); check_error(); return r; } // stats statistics() const { Z3_stats r = Z3_solver_get_statistics(ctx(), m_solver); check_error(); return stats(ctx(), r); } #if 0 expr_vector unsat_core() const { Z3_ast_vector r = Z3_solver_get_unsat_core(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } expr_vector assertions() const { Z3_ast_vector r = Z3_solver_get_assertions(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } #endif // expr proof() const { Z3_ast r = Z3_solver_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); } // friend std::ostream & operator<<(std::ostream & out, solver const & s) { out << Z3_solver_to_string(s.ctx(), s); return out; } int get_num_decisions(); void cancel(){ scoped_proof_mode spm(m(),m_mode); canceled = true; if(m_solver) m_solver->cancel(); } unsigned get_scope_level(){ scoped_proof_mode spm(m(),m_mode); return m_solver->get_scope_level();} void show(); void print(const char *filename); void show_assertion_ids(); proof get_proof(){ scoped_proof_mode spm(m(),m_mode); return proof(ctx(),m_solver->get_proof()); } bool extensional_array_theory() {return extensional;} }; #if 0 class goal : public object { Z3_goal m_goal; void init(Z3_goal s) { m_goal = s; Z3_goal_inc_ref(ctx(), s); } public: goal(context & c, bool models=true, bool unsat_cores=false, bool proofs=false):object(c) { init(Z3_mk_goal(c, models, unsat_cores, proofs)); } goal(context & c, Z3_goal s):object(c) { init(s); } goal(goal const & s):object(s) { init(s.m_goal); } ~goal() { Z3_goal_dec_ref(ctx(), m_goal); } operator Z3_goal() const { return m_goal; } goal & operator=(goal const & s) { Z3_goal_inc_ref(s.ctx(), s.m_goal); Z3_goal_dec_ref(ctx(), m_goal); m_ctx = s.m_ctx; m_goal = s.m_goal; return *this; } void add(expr const & f) { check_context(*this, f); Z3_goal_assert(ctx(), m_goal, f); check_error(); } unsigned size() const { return Z3_goal_size(ctx(), m_goal); } expr operator[](unsigned i) const { Z3_ast r = Z3_goal_formula(ctx(), m_goal, i); check_error(); return expr(ctx(), r); } Z3_goal_prec precision() const { return Z3_goal_precision(ctx(), m_goal); } bool inconsistent() const { return Z3_goal_inconsistent(ctx(), m_goal) != 0; } unsigned depth() const { return Z3_goal_depth(ctx(), m_goal); } void reset() { Z3_goal_reset(ctx(), m_goal); } unsigned num_exprs() const { Z3_goal_num_exprs(ctx(), m_goal); } bool is_decided_sat() const { return Z3_goal_is_decided_sat(ctx(), m_goal) != 0; } bool is_decided_unsat() const { return Z3_goal_is_decided_unsat(ctx(), m_goal) != 0; } friend std::ostream & operator<<(std::ostream & out, goal const & g) { out << Z3_goal_to_string(g.ctx(), g); return out; } }; class apply_result : public object { Z3_apply_result m_apply_result; void init(Z3_apply_result s) { m_apply_result = s; Z3_apply_result_inc_ref(ctx(), s); } public: apply_result(context & c, Z3_apply_result s):object(c) { init(s); } apply_result(apply_result const & s):object(s) { init(s.m_apply_result); } ~apply_result() { Z3_apply_result_dec_ref(ctx(), m_apply_result); } operator Z3_apply_result() const { return m_apply_result; } apply_result & operator=(apply_result const & s) { Z3_apply_result_inc_ref(s.ctx(), s.m_apply_result); Z3_apply_result_dec_ref(ctx(), m_apply_result); m_ctx = s.m_ctx; m_apply_result = s.m_apply_result; return *this; } unsigned size() const { return Z3_apply_result_get_num_subgoals(ctx(), m_apply_result); } goal operator[](unsigned i) const { Z3_goal r = Z3_apply_result_get_subgoal(ctx(), m_apply_result, i); check_error(); return goal(ctx(), r); } goal operator[](int i) const { assert(i >= 0); return this->operator[](static_cast(i)); } model convert_model(model const & m, unsigned i = 0) const { check_context(*this, m); Z3_model new_m = Z3_apply_result_convert_model(ctx(), m_apply_result, i, m); check_error(); return model(ctx(), new_m); } friend std::ostream & operator<<(std::ostream & out, apply_result const & r) { out << Z3_apply_result_to_string(r.ctx(), r); return out; } }; class tactic : public object { Z3_tactic m_tactic; void init(Z3_tactic s) { m_tactic = s; Z3_tactic_inc_ref(ctx(), s); } public: tactic(context & c, char const * name):object(c) { Z3_tactic r = Z3_mk_tactic(c, name); check_error(); init(r); } tactic(context & c, Z3_tactic s):object(c) { init(s); } tactic(tactic const & s):object(s) { init(s.m_tactic); } ~tactic() { Z3_tactic_dec_ref(ctx(), m_tactic); } operator Z3_tactic() const { return m_tactic; } tactic & operator=(tactic const & s) { Z3_tactic_inc_ref(s.ctx(), s.m_tactic); Z3_tactic_dec_ref(ctx(), m_tactic); m_ctx = s.m_ctx; m_tactic = s.m_tactic; return *this; } solver mk_solver() const { Z3_solver r = Z3_mk_solver_from_tactic(ctx(), m_tactic); check_error(); return solver(ctx(), r); } apply_result apply(goal const & g) const { check_context(*this, g); Z3_apply_result r = Z3_tactic_apply(ctx(), m_tactic, g); check_error(); return apply_result(ctx(), r); } apply_result operator()(goal const & g) const { return apply(g); } std::string help() const { char const * r = Z3_tactic_get_help(ctx(), m_tactic); check_error(); return r; } friend tactic operator&(tactic const & t1, tactic const & t2) { check_context(t1, t2); Z3_tactic r = Z3_tactic_and_then(t1.ctx(), t1, t2); t1.check_error(); return tactic(t1.ctx(), r); } friend tactic operator|(tactic const & t1, tactic const & t2) { check_context(t1, t2); Z3_tactic r = Z3_tactic_or_else(t1.ctx(), t1, t2); t1.check_error(); return tactic(t1.ctx(), r); } friend tactic repeat(tactic const & t, unsigned max=UINT_MAX) { Z3_tactic r = Z3_tactic_repeat(t.ctx(), t, max); t.check_error(); return tactic(t.ctx(), r); } friend tactic with(tactic const & t, params const & p) { Z3_tactic r = Z3_tactic_using_params(t.ctx(), t, p); t.check_error(); return tactic(t.ctx(), r); } friend tactic try_for(tactic const & t, unsigned ms) { Z3_tactic r = Z3_tactic_try_for(t.ctx(), t, ms); t.check_error(); return tactic(t.ctx(), r); } }; class probe : public object { Z3_probe m_probe; void init(Z3_probe s) { m_probe = s; Z3_probe_inc_ref(ctx(), s); } public: probe(context & c, char const * name):object(c) { Z3_probe r = Z3_mk_probe(c, name); check_error(); init(r); } probe(context & c, double val):object(c) { Z3_probe r = Z3_probe_const(c, val); check_error(); init(r); } probe(context & c, Z3_probe s):object(c) { init(s); } probe(probe const & s):object(s) { init(s.m_probe); } ~probe() { Z3_probe_dec_ref(ctx(), m_probe); } operator Z3_probe() const { return m_probe; } probe & operator=(probe const & s) { Z3_probe_inc_ref(s.ctx(), s.m_probe); Z3_probe_dec_ref(ctx(), m_probe); m_ctx = s.m_ctx; m_probe = s.m_probe; return *this; } double apply(goal const & g) const { double r = Z3_probe_apply(ctx(), m_probe, g); check_error(); return r; } double operator()(goal const & g) const { return apply(g); } friend probe operator<=(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_le(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } friend probe operator<=(probe const & p1, double p2) { return p1 <= probe(p1.ctx(), p2); } friend probe operator<=(double p1, probe const & p2) { return probe(p2.ctx(), p1) <= p2; } friend probe operator>=(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_ge(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } friend probe operator>=(probe const & p1, double p2) { return p1 >= probe(p1.ctx(), p2); } friend probe operator>=(double p1, probe const & p2) { return probe(p2.ctx(), p1) >= p2; } friend probe operator<(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_lt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } friend probe operator<(probe const & p1, double p2) { return p1 < probe(p1.ctx(), p2); } friend probe operator<(double p1, probe const & p2) { return probe(p2.ctx(), p1) < p2; } friend probe operator>(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_gt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } friend probe operator>(probe const & p1, double p2) { return p1 > probe(p1.ctx(), p2); } friend probe operator>(double p1, probe const & p2) { return probe(p2.ctx(), p1) > p2; } friend probe operator==(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_eq(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } friend probe operator==(probe const & p1, double p2) { return p1 == probe(p1.ctx(), p2); } friend probe operator==(double p1, probe const & p2) { return probe(p2.ctx(), p1) == p2; } friend probe operator&&(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_and(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } friend probe operator||(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_or(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } friend probe operator!(probe const & p) { Z3_probe r = Z3_probe_not(p.ctx(), p); p.check_error(); return probe(p.ctx(), r); } }; inline tactic fail_if(probe const & p) { Z3_tactic r = Z3_tactic_fail_if(p.ctx(), p); p.check_error(); return tactic(p.ctx(), r); } inline tactic when(probe const & p, tactic const & t) { check_context(p, t); Z3_tactic r = Z3_tactic_when(t.ctx(), p, t); t.check_error(); return tactic(t.ctx(), r); } inline tactic cond(probe const & p, tactic const & t1, tactic const & t2) { check_context(p, t1); check_context(p, t2); Z3_tactic r = Z3_tactic_cond(t1.ctx(), p, t1, t2); t1.check_error(); return tactic(t1.ctx(), r); } #endif inline expr context::bool_val(bool b){return b ? make(True) : make(False);} inline symbol context::str_symbol(char const * s) { ::symbol r = ::symbol(s); return symbol(*this, r); } inline symbol context::int_symbol(int n) { ::symbol r = ::symbol(n); return symbol(*this, r); } inline sort context::bool_sort() { ::sort *s = m().mk_sort(m_basic_fid, BOOL_SORT); return sort(*this, s); } inline sort context::int_sort() { ::sort *s = m().mk_sort(m_arith_fid, INT_SORT); return sort(*this, s); } inline sort context::real_sort() { ::sort *s = m().mk_sort(m_arith_fid, REAL_SORT); return sort(*this, s); } inline sort context::array_sort(sort d, sort r) { parameter params[2] = { parameter(d), parameter(to_sort(r)) }; ::sort * s = m().mk_sort(m_array_fid, ARRAY_SORT, 2, params); return sort(*this, s); } inline func_decl context::function(symbol const & name, unsigned arity, sort const * domain, sort const & range) { std::vector< ::sort *> sv(arity); for(unsigned i = 0; i < arity; i++) sv[i] = domain[i]; ::func_decl* d = m().mk_func_decl(name,arity,&sv[0],range); return func_decl(*this,d); } inline func_decl context::function(char const * name, unsigned arity, sort const * domain, sort const & range) { return function(str_symbol(name), arity, domain, range); } inline func_decl context::function(char const * name, sort const & domain, sort const & range) { sort args[1] = { domain }; return function(name, 1, args, range); } inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & range) { sort args[2] = { d1, d2 }; return function(name, 2, args, range); } inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range) { sort args[3] = { d1, d2, d3 }; return function(name, 3, args, range); } inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range) { sort args[4] = { d1, d2, d3, d4 }; return function(name, 4, args, range); } inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range) { sort args[5] = { d1, d2, d3, d4, d5 }; return function(name, 5, args, range); } inline expr context::constant(symbol const & name, sort const & s) { ::expr *r = m().mk_const(m().mk_const_decl(name, s)); return expr(*this, r); } inline expr context::constant(char const * name, sort const & s) { return constant(str_symbol(name), s); } inline expr context::bool_const(char const * name) { return constant(name, bool_sort()); } inline expr context::int_const(char const * name) { return constant(name, int_sort()); } inline expr context::real_const(char const * name) { return constant(name, real_sort()); } inline expr context::bv_const(char const * name, unsigned sz) { return constant(name, bv_sort(sz)); } inline expr func_decl::operator()(const std::vector &args) const { return operator()(args.size(),&args[0]); } inline expr func_decl::operator()() const { return operator()(0,0); } inline expr func_decl::operator()(expr const & a) const { return operator()(1,&a); } inline expr func_decl::operator()(expr const & a1, expr const & a2) const { expr args[2] = {a1,a2}; return operator()(2,args); } inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3) const { expr args[3] = {a1,a2,a3}; return operator()(3,args); } inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const { expr args[4] = {a1,a2,a3,a4}; return operator()(4,args); } inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const { expr args[5] = {a1,a2,a3,a4,a5}; return operator()(5,args); } inline expr select(expr const & a, expr const & i) { return a.ctx().make(Select,a,i); } inline expr store(expr const & a, expr const & i, expr const & v) { return a.ctx().make(Store,a,i,v); } inline expr forall(const std::vector &quants, const expr &body){ return body.ctx().make_quant(Forall,quants,body); } inline expr exists(const std::vector &quants, const expr &body){ return body.ctx().make_quant(Exists,quants,body); } inline expr context::int_val(int n){ :: sort *r = m().mk_sort(m_arith_fid, INT_SORT); return cook(m_arith_util.mk_numeral(rational(n),r)); } class literals : public object { }; class TermTree { public: TermTree(expr _term){ term = _term; } TermTree(expr _term, const std::vector &_children){ term = _term; children = _children; } inline expr getTerm(){return term;} inline std::vector &getTerms(){return terms;} inline std::vector &getChildren(){ return children; } inline int number(int from){ for(unsigned i = 0; i < children.size(); i++) from = children[i]->number(from); num = from; return from + 1; } inline int getNumber(){ return num; } inline void setTerm(expr t){term = t;} inline void addTerm(expr t){terms.push_back(t);} inline void setChildren(const std::vector & _children){ children = _children; } inline void setNumber(int _num){ num = _num; } ~TermTree(){ for(unsigned i = 0; i < children.size(); i++) delete children[i]; } private: expr term; std::vector terms; std::vector children; int num; }; typedef context interpolating_context; class interpolating_solver : public solver { public: interpolating_solver(context &ctx, bool models = true) : solver(ctx, true, models) { weak_mode = false; } public: lbool interpolate(const std::vector &assumptions, std::vector &interpolants, model &_model, literals &lits, bool incremental ); lbool interpolate_tree(TermTree *assumptions, TermTree *&interpolants, model &_model, literals &lits, bool incremental ); bool read_interpolation_problem(const std::string &file_name, std::vector &assumptions, std::vector &theory, std::string &error_message ); void write_interpolation_problem(const std::string &file_name, const std::vector &assumptions, const std::vector &theory ); void AssertInterpolationAxiom(const expr &expr); void RemoveInterpolationAxiom(const expr &expr); void SetWeakInterpolants(bool weak); void SetPrintToFile(const std::string &file_name); const std::vector &GetInterpolationAxioms() {return theory;} const char *profile(); private: bool weak_mode; std::string print_filename; std::vector theory; }; inline expr context::cook(::expr *a) {return expr(*this,a);} inline std::vector context::cook(ptr_vector< ::expr> v) { std::vector _v(v.size()); for(unsigned i = 0; i < v.size(); i++) _v[i] = cook(v[i]); return _v; } inline ::expr *context::uncook(const expr &a) { m().inc_ref(a.raw()); return to_expr(a.raw()); } inline expr context::translate(const expr &e) { ::expr *f = to_expr(e.raw()); if(&e.ctx().m() != &m()) // same ast manager -> no translation throw "ast manager mismatch"; return cook(f); } inline func_decl context::translate(const func_decl &e) { ::func_decl *f = to_func_decl(e.raw()); if(&e.ctx().m() != &m()) // same ast manager -> no translation throw "ast manager mismatch"; return func_decl(*this,f); } typedef double clock_t; clock_t current_time(); inline void output_time(std::ostream &os, clock_t time){os << time;} template class uptr { public: X *ptr; uptr(){ptr = 0;} void set(X *_ptr){ if(ptr) delete ptr; ptr = _ptr; } X *get(){ return ptr;} ~uptr(){ if(ptr) delete ptr; } }; }; // to make Duality::ast hashable namespace hash_space { template <> class hash { public: size_t operator()(const Duality::ast &s) const { return s.raw()->get_id(); } }; } // to make Duality::ast usable in ordered collections namespace std { template <> class less { public: bool operator()(const Duality::ast &s, const Duality::ast &t) const { // return s.raw() < t.raw(); return s.raw()->get_id() < t.raw()->get_id(); } }; } // to make Duality::ast usable in ordered collections namespace std { template <> class less { public: bool operator()(const Duality::expr &s, const Duality::expr &t) const { // return s.raw() < t.raw(); return s.raw()->get_id() < t.raw()->get_id(); } }; } // to make Duality::func_decl hashable namespace hash_space { template <> class hash { public: size_t operator()(const Duality::func_decl &s) const { return s.raw()->get_id(); } }; } // to make Duality::func_decl usable in ordered collections namespace std { template <> class less { public: bool operator()(const Duality::func_decl &s, const Duality::func_decl &t) const { // return s.raw() < t.raw(); return s.raw()->get_id() < t.raw()->get_id(); } }; } #endif z3-4.4.0/src/duality/duality_rpfp.cpp0000755000175000017500000046674512540347414017346 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: duality_rpfp.h Abstract: implements relational post-fixedpoint problem (RPFP) data structure. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifdef _WINDOWS #pragma warning(disable:4996) #pragma warning(disable:4800) #pragma warning(disable:4267) #endif #include #include #include #include #include "duality.h" #include "duality_profiling.h" // TODO: do we need these? #ifdef Z3OPS class Z3_subterm_truth { public: virtual bool eval(Z3_ast f) = 0; ~Z3_subterm_truth(){} }; Z3_subterm_truth *Z3_mk_subterm_truth(Z3_context ctx, Z3_model model); #endif #include // TODO: use something official for this int debug_gauss = 0; namespace Duality { static char string_of_int_buffer[20]; const char *Z3User::string_of_int(int n){ sprintf(string_of_int_buffer,"%d",n); return string_of_int_buffer; } RPFP::Term RPFP::SuffixVariable(const Term &t, int n) { std::string name = t.decl().name().str() + "_" + string_of_int(n); return ctx.constant(name.c_str(), t.get_sort()); } RPFP::Term RPFP::HideVariable(const Term &t, int n) { std::string name = std::string("@p_") + t.decl().name().str() + "_" + string_of_int(n); return ctx.constant(name.c_str(), t.get_sort()); } void RPFP::RedVars(Node *node, Term &b, std::vector &v) { int idx = node->number; if(HornClauses) b = ctx.bool_val(true); else { std::string name = std::string("@b_") + string_of_int(idx); symbol sym = ctx.str_symbol(name.c_str()); b = ctx.constant(sym,ctx.bool_sort()); } // ctx.constant(name.c_str(), ctx.bool_sort()); v = node->Annotation.IndParams; for(unsigned i = 0; i < v.size(); i++) v[i] = SuffixVariable(v[i],idx); } void Z3User::SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t){ if(memo.find(t) != memo.end()) return; memo.insert(t); if(t.is_app()){ decl_kind k = t.decl().get_decl_kind(); if (k == And || k == Or || k == Not || k == Implies || k == Iff) { ops++; int nargs = t.num_args(); for (int i = 0; i < nargs; i++) SummarizeRec(memo, lits, ops, t.arg(i)); return; } } lits.push_back(t); } int RPFP::CumulativeDecisions(){ #if 0 std::string stats = Z3_statistics_to_string(ctx); int pos = stats.find("decisions:"); pos += 10; int end = stats.find('\n',pos); std::string val = stats.substr(pos,end-pos); return atoi(val.c_str()); #endif return slvr().get_num_decisions(); } void Z3User::Summarize(const Term &t){ hash_set memo; std::vector lits; int ops = 0; SummarizeRec(memo, lits, ops, t); std::cout << ops << ": "; for(unsigned i = 0; i < lits.size(); i++){ if(i > 0) std::cout << ", "; std::cout << lits[i]; } } int Z3User::CountOperatorsRec(hash_set &memo, const Term &t){ if(memo.find(t) != memo.end()) return 0; memo.insert(t); if(t.is_app()){ decl_kind k = t.decl().get_decl_kind(); if (k == And || k == Or) { int count = 1; int nargs = t.num_args(); for (int i = 0; i < nargs; i++) count += CountOperatorsRec(memo, t.arg(i)); return count; } return 0; } if(t.is_quantifier()){ int nbv = t.get_quantifier_num_bound(); return CountOperatorsRec(memo,t.body()) + 2 * nbv; // count 2 for each quantifier } return 0; } int Z3User::CountOperators(const Term &t){ hash_set memo; return CountOperatorsRec(memo,t); } Z3User::Term Z3User::conjoin(const std::vector &args){ return ctx.make(And,args); } Z3User::Term Z3User::sum(const std::vector &args){ return ctx.make(Plus,args); } RPFP::Term RPFP::RedDualRela(Edge *e, std::vector &args, int idx){ Node *child = e->Children[idx]; Term b(ctx); std::vector v; RedVars(child, b, v); for (unsigned i = 0; i < args.size(); i++) { if (eq(args[i].get_sort(), ctx.bool_sort())) args[i] = ctx.make(Iff, args[i], v[i]); else args[i] = args[i] == v[i]; } return args.size() > 0 ? (b && conjoin(args)) : b; } Z3User::Term Z3User::CloneQuantifier(const Term &t, const Term &new_body){ #if 0 Z3_context c = ctx; Z3_ast a = t; std::vector pats; int num_pats = Z3_get_quantifier_num_patterns(c,a); for(int i = 0; i < num_pats; i++) pats.push_back(Z3_get_quantifier_pattern_ast(c,a,i)); std::vector no_pats; int num_no_pats = Z3_get_quantifier_num_patterns(c,a); for(int i = 0; i < num_no_pats; i++) no_pats.push_back(Z3_get_quantifier_no_pattern_ast(c,a,i)); int bound = Z3_get_quantifier_num_bound(c,a); std::vector sorts; std::vector names; for(int i = 0; i < bound; i++){ sorts.push_back(Z3_get_quantifier_bound_sort(c,a,i)); names.push_back(Z3_get_quantifier_bound_name(c,a,i)); } Z3_ast foo = Z3_mk_quantifier_ex(c, Z3_is_quantifier_forall(c,a), Z3_get_quantifier_weight(c,a), 0, 0, num_pats, &pats[0], num_no_pats, &no_pats[0], bound, &sorts[0], &names[0], new_body); return expr(ctx,foo); #endif return clone_quantifier(t,new_body); } RPFP::Term RPFP::LocalizeRec(Edge *e, hash_map &memo, const Term &t) { std::pair foo(t,expr(ctx)); std::pair::iterator, bool> bar = memo.insert(foo); Term &res = bar.first->second; if(!bar.second) return res; hash_map::iterator it = e->varMap.find(t); if (it != e->varMap.end()){ res = it->second; return res; } if (t.is_app()) { func_decl f = t.decl(); std::vector args; int nargs = t.num_args(); for (int i = 0; i < nargs; i++) args.push_back(LocalizeRec(e, memo, t.arg(i))); hash_map::iterator rit = e->relMap.find(f); if (rit != e->relMap.end()) res = RedDualRela(e, args, (rit->second)); else { if (args.size() == 0 && f.get_decl_kind() == Uninterpreted && !ls->is_constant(f)) { res = HideVariable(t, e->number); } else { res = f(args.size(), &args[0]); } } } else if (t.is_quantifier()) { std::vector pats; t.get_patterns(pats); for (unsigned i = 0; i < pats.size(); i++) pats[i] = LocalizeRec(e, memo, pats[i]); Term body = LocalizeRec(e, memo, t.body()); res = clone_quantifier(t, body, pats); } else res = t; return res; } void RPFP::SetEdgeMaps(Edge *e){ timer_start("SetEdgeMaps"); e->relMap.clear(); e->varMap.clear(); for(unsigned i = 0; i < e->F.RelParams.size(); i++){ e->relMap[e->F.RelParams[i]] = i; } Term b(ctx); std::vector v; RedVars(e->Parent, b, v); for(unsigned i = 0; i < e->F.IndParams.size(); i++){ // func_decl parentParam = e->Parent.Annotation.IndParams[i]; expr oldname = e->F.IndParams[i]; expr newname = v[i]; e->varMap[oldname] = newname; } timer_stop("SetEdgeMaps"); } RPFP::Term RPFP::Localize(Edge *e, const Term &t){ timer_start("Localize"); hash_map memo; if(e->F.IndParams.size() > 0 && e->varMap.empty()) SetEdgeMaps(e); // TODO: why is this needed? Term res = LocalizeRec(e,memo,t); timer_stop("Localize"); return res; } RPFP::Term RPFP::ReducedDualEdge(Edge *e) { SetEdgeMaps(e); timer_start("RedVars"); Term b(ctx); std::vector v; RedVars(e->Parent, b, v); timer_stop("RedVars"); // ast_to_track = (ast)b; return implies(b, Localize(e, e->F.Formula)); } TermTree *RPFP::ToTermTree(Node *root, Node *skip_descendant) { if(skip_descendant && root == skip_descendant) return new TermTree(ctx.bool_val(true)); Edge *e = root->Outgoing; if(!e) return new TermTree(ctx.bool_val(true), std::vector()); std::vector children(e->Children.size()); for(unsigned i = 0; i < children.size(); i++) children[i] = ToTermTree(e->Children[i],skip_descendant); // Term top = ReducedDualEdge(e); Term top = e->dual.null() ? ctx.bool_val(true) : e->dual; TermTree *res = new TermTree(top, children); for(unsigned i = 0; i < e->constraints.size(); i++) res->addTerm(e->constraints[i]); return res; } TermTree *RPFP::GetGoalTree(Node *root){ std::vector children(1); children[0] = ToGoalTree(root); return new TermTree(ctx.bool_val(false),children); } TermTree *RPFP::ToGoalTree(Node *root) { Term b(ctx); std::vector v; RedVars(root, b, v); Term goal = root->Name(v); Edge *e = root->Outgoing; if(!e) return new TermTree(goal, std::vector()); std::vector children(e->Children.size()); for(unsigned i = 0; i < children.size(); i++) children[i] = ToGoalTree(e->Children[i]); // Term top = ReducedDualEdge(e); return new TermTree(goal, children); } Z3User::Term Z3User::SubstRec(hash_map &memo, const Term &t) { std::pair foo(t,expr(ctx)); std::pair::iterator, bool> bar = memo.insert(foo); Term &res = bar.first->second; if(!bar.second) return res; if (t.is_app()) { func_decl f = t.decl(); std::vector args; int nargs = t.num_args(); for (int i = 0; i < nargs; i++) args.push_back(SubstRec(memo, t.arg(i))); res = f(args.size(), &args[0]); } else if (t.is_quantifier()) { std::vector pats; t.get_patterns(pats); for (unsigned i = 0; i < pats.size(); i++) pats[i] = SubstRec(memo, pats[i]); Term body = SubstRec(memo, t.body()); res = clone_quantifier(t, body, pats); } // res = CloneQuantifier(t,SubstRec(memo, t.body())); else res = t; return res; } Z3User::Term Z3User::SubstRec(hash_map &memo, hash_map &map, const Term &t) { std::pair foo(t,expr(ctx)); std::pair::iterator, bool> bar = memo.insert(foo); Term &res = bar.first->second; if(!bar.second) return res; if (t.is_app()) { func_decl f = t.decl(); std::vector args; int nargs = t.num_args(); for (int i = 0; i < nargs; i++) args.push_back(SubstRec(memo, map, t.arg(i))); hash_map::iterator it = map.find(f); if (it != map.end()) f = it->second; res = f(args.size(), &args[0]); } else if (t.is_quantifier()) { std::vector pats; t.get_patterns(pats); for (unsigned i = 0; i < pats.size(); i++) pats[i] = SubstRec(memo, map, pats[i]); Term body = SubstRec(memo, map, t.body()); res = clone_quantifier(t, body, pats); } // res = CloneQuantifier(t,SubstRec(memo, t.body())); else res = t; return res; } Z3User::Term Z3User::ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming) { std::pair foo(t,expr(ctx)); std::pair::iterator, bool> bar = memo.insert(foo); Term &res = bar.first->second; if(!bar.second) return res; if (t.is_app()) { func_decl f = t.decl(); std::vector args; int nargs = t.num_args(); for (int i = 0; i < nargs; i++) args.push_back(ExtractStores(memo, t.arg(i), cnstrs, renaming)); res = f(args.size(), &args[0]); if (f.get_decl_kind() == Store) { func_decl fresh = ctx.fresh_func_decl("@arr", res.get_sort()); expr y = fresh(); expr equ = ctx.make(Equal, y, res); cnstrs.push_back(equ); renaming[y] = res; res = y; } } else res = t; return res; } bool Z3User::IsLiteral(const expr &lit, expr &atom, expr &val){ if (!(lit.is_quantifier() && IsClosedFormula(lit))) { if (!lit.is_app()) return false; decl_kind k = lit.decl().get_decl_kind(); if (k == Not) { if (IsLiteral(lit.arg(0), atom, val)) { val = eq(val, ctx.bool_val(true)) ? ctx.bool_val(false) : ctx.bool_val(true); return true; } return false; } if (k == And || k == Or || k == Iff || k == Implies) return false; } atom = lit; val = ctx.bool_val(true); return true; } expr Z3User::Negate(const expr &f){ if(f.is_app() && f.decl().get_decl_kind() == Not) return f.arg(0); else if(eq(f,ctx.bool_val(true))) return ctx.bool_val(false); else if(eq(f,ctx.bool_val(false))) return ctx.bool_val(true); return !f; } expr Z3User::ReduceAndOr(const std::vector &args, bool is_and, std::vector &res){ for(unsigned i = 0; i < args.size(); i++) if (!eq(args[i], ctx.bool_val(is_and))) { if (eq(args[i], ctx.bool_val(!is_and))) return ctx.bool_val(!is_and); res.push_back(args[i]); } return expr(); } expr Z3User::FinishAndOr(const std::vector &args, bool is_and){ if(args.size() == 0) return ctx.bool_val(is_and); if(args.size() == 1) return args[0]; return ctx.make(is_and ? And : Or,args); } expr Z3User::SimplifyAndOr(const std::vector &args, bool is_and){ std::vector sargs; expr res = ReduceAndOr(args,is_and,sargs); if(!res.null()) return res; return FinishAndOr(sargs,is_and); } expr Z3User::PullCommonFactors(std::vector &args, bool is_and){ // first check if there's anything to do... if(args.size() < 2) return FinishAndOr(args,is_and); for (unsigned i = 0; i < args.size(); i++) { const expr &a = args[i]; if (!(a.is_app() && a.decl().get_decl_kind() == (is_and ? Or : And))) return FinishAndOr(args, is_and); } std::vector common; for (unsigned i = 0; i < args.size(); i++) { unsigned n = args[i].num_args(); std::vector v(n), w; for (unsigned j = 0; j < n; j++) v[j] = args[i].arg(j); std::less comp; std::sort(v.begin(), v.end(), comp); if (i == 0) common.swap(v); else { std::set_intersection(common.begin(), common.end(), v.begin(), v.end(), std::inserter(w, w.begin()), comp); common.swap(w); } } if(common.empty()) return FinishAndOr(args,is_and); std::set common_set(common.begin(),common.end()); for(unsigned i = 0; i < args.size(); i++){ unsigned n = args[i].num_args(); std::vector lits; for (unsigned j = 0; j < n; j++) { const expr b = args[i].arg(j); if (common_set.find(b) == common_set.end()) lits.push_back(b); } args[i] = SimplifyAndOr(lits,!is_and); } common.push_back(SimplifyAndOr(args,is_and)); return SimplifyAndOr(common,!is_and); } expr Z3User::ReallySimplifyAndOr(const std::vector &args, bool is_and){ std::vector sargs; expr res = ReduceAndOr(args,is_and,sargs); if(!res.null()) return res; return PullCommonFactors(sargs,is_and); } Z3User::Term Z3User::SubstAtomTriv(const expr &foo, const expr &atom, const expr &val){ if(eq(foo,atom)) return val; else if(foo.is_app() && foo.decl().get_decl_kind() == Not && eq(foo.arg(0),atom)) return Negate(val); else return foo; } Z3User::Term Z3User::PushQuantifier(const expr &t, const expr &body, bool is_forall){ if (t.get_quantifier_num_bound() == 1) { std::vector fmlas, free, not_free; CollectJuncts(body, fmlas, is_forall ? Or : And, false); for (unsigned i = 0; i < fmlas.size(); i++) { const expr &fmla = fmlas[i]; if (fmla.has_free(0)) free.push_back(fmla); else not_free.push_back(fmla); } decl_kind op = is_forall ? Or : And; if (free.empty()) return DeleteBound(0, 1, SimplifyAndOr(not_free, op == And)); expr q = clone_quantifier(is_forall ? Forall : Exists, t, SimplifyAndOr(free, op == And)); if (!not_free.empty()) q = ctx.make(op, q, DeleteBound(0, 1, SimplifyAndOr(not_free, op == And))); return q; } return clone_quantifier(is_forall ? Forall : Exists,t,body); } Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body, bool is_forall) { if (body.is_app()) { if (body.decl().get_decl_kind() == (is_forall ? And : Or)) { // quantifier distributes int nargs = body.num_args(); std::vector args(nargs); for (int i = 0; i < nargs; i++) args[i] = CloneQuantAndSimp(t, body.arg(i), is_forall); return SimplifyAndOr(args, body.decl().get_decl_kind() == And); } else if (body.decl().get_decl_kind() == (is_forall ? Or : And)) { // quantifier distributes return PushQuantifier(t, body, is_forall); // may distribute partially } else if (body.decl().get_decl_kind() == Not) { return ctx.make(Not, CloneQuantAndSimp(t, body.arg(0), !is_forall)); } } if (t.get_quantifier_num_bound() == 1 && !body.has_free(0)) return DeleteBound(0, 1, body); // drop the quantifier return clone_quantifier(is_forall ? Forall : Exists, t, body); } Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body){ return CloneQuantAndSimp(t,body,t.is_quantifier_forall()); } Z3User::Term Z3User::SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val) { std::pair foo(t, expr(ctx)); std::pair::iterator, bool> bar = memo.insert(foo); Term &res = bar.first->second; if (!bar.second) return res; if (t.is_app()) { func_decl f = t.decl(); decl_kind k = f.get_decl_kind(); // TODO: recur here, but how much? We don't want to be quadractic in formula size if (k == And || k == Or) { int nargs = t.num_args(); std::vector args(nargs); for (int i = 0; i < nargs; i++) args[i] = SubstAtom(memo, t.arg(i), atom, val); res = ReallySimplifyAndOr(args, k == And); return res; } } else if (t.is_quantifier() && atom.is_quantifier()) { if (eq(t, atom)) res = val; else res = clone_quantifier(t, SubstAtom(memo, t.body(), atom, val)); return res; } res = SubstAtomTriv(t, atom, val); return res; } void Z3User::RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo) { for (unsigned i = 0; i < args.size(); i++) { const expr &lit = args[i]; expr atom, val; if (IsLiteral(lit, atom, val)) { if (atom.is_app() && atom.decl().get_decl_kind() == Equal) if (pol ? eq(val, ctx.bool_val(true)) : eq(val, ctx.bool_val(false))) { expr lhs = atom.arg(0), rhs = atom.arg(1); if (lhs.is_numeral()) std::swap(lhs, rhs); if (rhs.is_numeral() && lhs.is_app()) { for (unsigned j = 0; j < args.size(); j++) if (j != i) { smemo.clear(); smemo[lhs] = rhs; args[j] = SubstRec(smemo, args[j]); } } } for (unsigned j = 0; j < args.size(); j++) if (j != i) { smemo.clear(); args[j] = SubstAtom(smemo, args[j], atom, pol ? val : !val); } } } } Z3User::Term Z3User::RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t) { std::pair foo(t, expr(ctx)); std::pair::iterator, bool> bar = memo.insert(foo); Term &res = bar.first->second; if (!bar.second) return res; if (t.is_app()) { func_decl f = t.decl(); std::vector args; int nargs = t.num_args(); for (int i = 0; i < nargs; i++) args.push_back(RemoveRedundancyRec(memo, smemo, t.arg(i))); decl_kind k = f.get_decl_kind(); if (k == And) { RemoveRedundancyOp(true, args, smemo); res = ReallySimplifyAndOr(args, true); } else if (k == Or) { RemoveRedundancyOp(false, args, smemo); res = ReallySimplifyAndOr(args, false); } else { if (k == Equal && args[0].get_id() > args[1].get_id()) std::swap(args[0], args[1]); res = f(args.size(), &args[0]); } } else if (t.is_quantifier()) { Term body = RemoveRedundancyRec(memo, smemo, t.body()); res = CloneQuantAndSimp(t, body); } else res = t; return res; } Z3User::Term Z3User::RemoveRedundancy(const Term &t){ hash_map memo; hash_map smemo; return RemoveRedundancyRec(memo,smemo,t); } Z3User::Term Z3User::AdjustQuantifiers(const Term &t) { if(t.is_quantifier() || (t.is_app() && t.has_quantifiers())) return t.qe_lite(); return t; } Z3User::Term Z3User::IneqToEqRec(hash_map &memo, const Term &t) { std::pair foo(t, expr(ctx)); std::pair::iterator, bool> bar = memo.insert(foo); Term &res = bar.first->second; if (!bar.second) return res; if (t.is_app()) { func_decl f = t.decl(); std::vector args; int nargs = t.num_args(); for (int i = 0; i < nargs; i++) args.push_back(IneqToEqRec(memo, t.arg(i))); decl_kind k = f.get_decl_kind(); if (k == And) { for (int i = 0; i < nargs - 1; i++) { if ((args[i].is_app() && args[i].decl().get_decl_kind() == Geq && args[i + 1].is_app() && args[i + 1].decl().get_decl_kind() == Leq) || (args[i].is_app() && args[i].decl().get_decl_kind() == Leq && args[i + 1].is_app() && args[i + 1].decl().get_decl_kind() == Geq)) if (eq(args[i].arg(0), args[i + 1].arg(0)) && eq(args[i].arg(1), args[i + 1].arg(1))) { args[i] = ctx.make(Equal, args[i].arg(0), args[i].arg(1)); args[i + 1] = ctx.bool_val(true); } } } res = f(args.size(), &args[0]); } else if (t.is_quantifier()) { Term body = IneqToEqRec(memo, t.body()); res = clone_quantifier(t, body); } else res = t; return res; } Z3User::Term Z3User::IneqToEq(const Term &t){ hash_map memo; return IneqToEqRec(memo,t); } Z3User::Term Z3User::SubstRecHide(hash_map &memo, const Term &t, int number) { std::pair foo(t, expr(ctx)); std::pair::iterator, bool> bar = memo.insert(foo); Term &res = bar.first->second; if (!bar.second) return res; if (t.is_app()) { func_decl f = t.decl(); std::vector args; int nargs = t.num_args(); if (nargs == 0 && f.get_decl_kind() == Uninterpreted) { std::string name = std::string("@q_") + t.decl().name().str() + "_" + string_of_int(number); res = ctx.constant(name.c_str(), t.get_sort()); return res; } for (int i = 0; i < nargs; i++) args.push_back(SubstRec(memo, t.arg(i))); res = f(args.size(), &args[0]); } else if (t.is_quantifier()) res = CloneQuantifier(t, SubstRec(memo, t.body())); else res = t; return res; } RPFP::Term RPFP::SubstParams(const std::vector &from, const std::vector &to, const Term &t) { hash_map memo; bool some_diff = false; for (unsigned i = 0; i < from.size(); i++) if (i < to.size() && !eq(from[i], to[i])) { memo[from[i]] = to[i]; some_diff = true; } return some_diff ? SubstRec(memo, t) : t; } RPFP::Term RPFP::SubstParamsNoCapture(const std::vector &from, const std::vector &to, const Term &t) { hash_map memo; bool some_diff = false; for (unsigned i = 0; i < from.size(); i++) if (i < to.size() && !eq(from[i], to[i])) { memo[from[i]] = to[i]; // if the new param is not being mapped to anything else, we need to rename it to prevent capture // note, if the new param *is* mapped later in the list, it will override this substitution const expr &w = to[i]; if (memo.find(w) == memo.end()) { std::string old_name = w.decl().name().str(); func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), w.get_sort()); expr y = fresh(); memo[w] = y; } some_diff = true; } return some_diff ? SubstRec(memo, t) : t; } RPFP::Transformer RPFP::Fuse(const std::vector &trs) { assert(!trs.empty()); const std::vector ¶ms = trs[0]->IndParams; std::vector fmlas(trs.size()); fmlas[0] = trs[0]->Formula; for (unsigned i = 1; i < trs.size(); i++) fmlas[i] = SubstParamsNoCapture(trs[i]->IndParams, params, trs[i]->Formula); std::vector rel_params = trs[0]->RelParams; for (unsigned i = 1; i < trs.size(); i++) { const std::vector ¶ms2 = trs[i]->RelParams; hash_map map; for (unsigned j = 0; j < params2.size(); j++) { func_decl rel = RenumberPred(params2[j], rel_params.size()); rel_params.push_back(rel); map[params2[j]] = rel; } hash_map memo; fmlas[i] = SubstRec(memo, map, fmlas[i]); } return Transformer(rel_params, params, ctx.make(Or, fmlas), trs[0]->owner); } void Z3User::Strengthen(Term &x, const Term &y) { if (eq(x,ctx.bool_val(true))) x = y; else x = x && y; } void RPFP::SetAnnotation(Node *root, const expr &t){ hash_map memo; Term b; std::vector v; RedVars(root, b, v); memo[b] = ctx.bool_val(true); for (unsigned i = 0; i < v.size(); i++) memo[v[i]] = root->Annotation.IndParams[i]; Term annot = SubstRec(memo, t); // Strengthen(ref root.Annotation.Formula, annot); root->Annotation.Formula = annot; } void RPFP::DecodeTree(Node *root, TermTree *interp, int persist) { std::vector &ic = interp->getChildren(); if (ic.size() > 0) { std::vector &nc = root->Outgoing->Children; for (unsigned i = 0; i < nc.size(); i++) DecodeTree(nc[i], ic[i], persist); } SetAnnotation(root, interp->getTerm()); #if 0 if(persist != 0) Z3_persist_ast(ctx,root->Annotation.Formula,persist); #endif } RPFP::Term RPFP::GetUpperBound(Node *n) { // TODO: cache this Term b(ctx); std::vector v; RedVars(n, b, v); hash_map memo; for (unsigned int i = 0; i < v.size(); i++) memo[n->Bound.IndParams[i]] = v[i]; Term cnst = SubstRec(memo, n->Bound.Formula); return b && !cnst; } RPFP::Term RPFP::GetAnnotation(Node *n) { if(eq(n->Annotation.Formula,ctx.bool_val(true))) return n->Annotation.Formula; // TODO: cache this Term b(ctx); std::vector v; RedVars(n, b, v); hash_map memo; for (unsigned i = 0; i < v.size(); i++) memo[n->Annotation.IndParams[i]] = v[i]; Term cnst = SubstRec(memo, n->Annotation.Formula); return !b || cnst; } RPFP::Term RPFP::GetUnderapprox(Node *n) { // TODO: cache this Term b(ctx); std::vector v; RedVars(n, b, v); hash_map memo; for (unsigned i = 0; i < v.size(); i++) memo[n->Underapprox.IndParams[i]] = v[i]; Term cnst = SubstRecHide(memo, n->Underapprox.Formula, n->number); return !b || cnst; } TermTree *RPFP::AddUpperBound(Node *root, TermTree *t) { Term f = !((ast)(root->dual)) ? ctx.bool_val(true) : root->dual; std::vector c(1); c[0] = t; return new TermTree(f, c); } #if 0 void RPFP::WriteInterps(System.IO.StreamWriter f, TermTree t) { foreach (var c in t.getChildren()) WriteInterps(f, c); f.WriteLine(t.getTerm()); } #endif expr RPFP::GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox) { if (e->dual.null()) { timer_start("ReducedDualEdge"); e->dual = ReducedDualEdge(e); timer_stop("ReducedDualEdge"); timer_start("getting children"); if (underapprox) { std::vector cus(e->Children.size()); for (unsigned i = 0; i < e->Children.size(); i++) cus[i] = !UnderapproxFlag(e->Children[i]) || GetUnderapprox(e->Children[i]); expr cnst = conjoin(cus); e->dual = e->dual && cnst; } timer_stop("getting children"); timer_start("Persisting"); std::list::reverse_iterator it = stack.rbegin(); for (int i = 0; i < persist && it != stack.rend(); i++) it++; if (it != stack.rend()) it->edges.push_back(e); timer_stop("Persisting"); //Console.WriteLine("{0}", cnst); } return e->dual; } /** For incremental solving, asserts the constraint associated * with this edge in the SMT context. If this edge is removed, * you must pop the context accordingly. The second argument is * the number of pushes we are inside. The constraint formula * will survive "persist" pops of the context. If you plan * to reassert the edge after popping the context once, * you can save time re-constructing the formula by setting * "persist" to one. If you set "persist" too high, however, * you could have a memory leak. * * The flag "with children" causes the annotations of the children * to be asserted. The flag underapprox causes the underapproximations * of the children to be asserted *conditionally*. See Check() on * how to actually enforce these constraints. * */ void RPFP::AssertEdge(Edge *e, int persist, bool with_children, bool underapprox) { if (eq(e->F.Formula, ctx.bool_val(true)) && (!with_children || e->Children.empty())) return; expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); timer_start("solver add"); slvr_add(e->dual); timer_stop("solver add"); if (with_children) for (unsigned i = 0; i < e->Children.size(); i++) ConstrainParent(e, e->Children[i]); } #ifdef LIMIT_STACK_WEIGHT void RPFP_caching::AssertEdge(Edge *e, int persist, bool with_children, bool underapprox) { unsigned old_new_alits = new_alits.size(); if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) return; expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); timer_start("solver add"); slvr_add(e->dual); timer_stop("solver add"); if(old_new_alits < new_alits.size()) weight_added.val++; if(with_children) for(unsigned i = 0; i < e->Children.size(); i++) ConstrainParent(e,e->Children[i]); } #endif // caching verion of above void RPFP_caching::AssertEdgeCache(Edge *e, std::vector &lits, bool with_children) { if (eq(e->F.Formula, ctx.bool_val(true)) && (!with_children || e->Children.empty())) return; expr fmla = GetEdgeFormula(e, 0, with_children, false); GetAssumptionLits(fmla, lits); if (with_children) for (unsigned i = 0; i < e->Children.size(); i++) ConstrainParentCache(e, e->Children[i], lits); } void RPFP::slvr_add(const expr &e){ slvr().add(e); } void RPFP_caching::slvr_add(const expr &e){ GetAssumptionLits(e,alit_stack); } void RPFP::slvr_pop(int i){ slvr().pop(i); } void RPFP::slvr_push(){ slvr().push(); } void RPFP_caching::slvr_pop(int i){ for(int j = 0; j < i; j++){ #ifdef LIMIT_STACK_WEIGHT if(alit_stack_sizes.empty()){ if(big_stack.empty()) throw "stack underflow"; for(unsigned k = 0; k < new_alits.size(); k++){ if(AssumptionLits.find(new_alits[k]) == AssumptionLits.end()) throw "foo!"; AssumptionLits.erase(new_alits[k]); } big_stack_entry &bsb = big_stack.back(); bsb.alit_stack_sizes.swap(alit_stack_sizes); bsb.alit_stack.swap(alit_stack); bsb.new_alits.swap(new_alits); bsb.weight_added.swap(weight_added); big_stack.pop_back(); slvr().pop(1); continue; } #endif alit_stack.resize(alit_stack_sizes.back()); alit_stack_sizes.pop_back(); } } void RPFP_caching::slvr_push(){ #ifdef LIMIT_STACK_WEIGHT if(weight_added.val > LIMIT_STACK_WEIGHT){ big_stack.resize(big_stack.size()+1); big_stack_entry &bsb = big_stack.back(); bsb.alit_stack_sizes.swap(alit_stack_sizes); bsb.alit_stack.swap(alit_stack); bsb.new_alits.swap(new_alits); bsb.weight_added.swap(weight_added); slvr().push(); for(unsigned i = 0; i < bsb.alit_stack.size(); i++) slvr().add(bsb.alit_stack[i]); return; } #endif alit_stack_sizes.push_back(alit_stack.size()); } check_result RPFP::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ return slvr().check(n, assumptions, core_size, core); } check_result RPFP_caching::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ unsigned oldsiz = alit_stack.size(); if(n && assumptions) std::copy(assumptions,assumptions+n,std::inserter(alit_stack,alit_stack.end())); check_result res; if (core_size && core) { std::vector full_core(alit_stack.size()), core1(n); std::copy(assumptions, assumptions + n, core1.begin()); res = slvr().check(alit_stack.size(), &alit_stack[0], core_size, &full_core[0]); full_core.resize(*core_size); if (res == unsat) { FilterCore(core1, full_core); *core_size = core1.size(); std::copy(core1.begin(), core1.end(), core); } } else res = slvr().check(alit_stack.size(), &alit_stack[0]); alit_stack.resize(oldsiz); return res; } lbool RPFP::ls_interpolate_tree(TermTree *assumptions, TermTree *&interpolants, model &_model, TermTree *goals, bool weak) { return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); } lbool RPFP_caching::ls_interpolate_tree(TermTree *assumptions, TermTree *&interpolants, model &_model, TermTree *goals, bool weak) { GetTermTreeAssertionLiterals(assumptions); return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); } void RPFP_caching::GetTermTreeAssertionLiteralsRec(TermTree *assumptions){ std::vector alits; hash_map map; GetAssumptionLits(assumptions->getTerm(),alits,&map); std::vector &ts = assumptions->getTerms(); for(unsigned i = 0; i < ts.size(); i++) GetAssumptionLits(ts[i],alits,&map); assumptions->setTerm(ctx.bool_val(true)); ts = alits; for(unsigned i = 0; i < alits.size(); i++) ts.push_back(ctx.make(Implies,alits[i],map[alits[i]])); for(unsigned i = 0; i < assumptions->getChildren().size(); i++) GetTermTreeAssertionLiterals(assumptions->getChildren()[i]); return; } void RPFP_caching::GetTermTreeAssertionLiterals(TermTree *assumptions) { // optimize binary case if (assumptions->getChildren().size() == 1 && assumptions->getChildren()[0]->getChildren().size() == 0) { hash_map map; TermTree *child = assumptions->getChildren()[0]; std::vector dummy; GetAssumptionLits(child->getTerm(), dummy, &map); std::vector &ts = child->getTerms(); for (unsigned i = 0; i < ts.size(); i++) GetAssumptionLits(ts[i], dummy, &map); std::vector assumps; slvr().get_proof().get_assumptions(assumps); if (!proof_core) { // save the proof core for later use proof_core = new hash_set < ast > ; for (unsigned i = 0; i < assumps.size(); i++) proof_core->insert(assumps[i]); } std::vector *cnsts[2] = { &child->getTerms(), &assumptions->getTerms() }; for (unsigned i = 0; i < assumps.size(); i++) { expr &as = assumps[i]; expr alit = (as.is_app() && as.decl().get_decl_kind() == Implies) ? as.arg(0) : as; bool isA = map.find(alit) != map.end(); cnsts[isA ? 0 : 1]->push_back(as); } } else GetTermTreeAssertionLiteralsRec(assumptions); } void RPFP::AddToProofCore(hash_set &core){ std::vector assumps; slvr().get_proof().get_assumptions(assumps); for(unsigned i = 0; i < assumps.size(); i++) core.insert(assumps[i]); } void RPFP::ComputeProofCore(){ if(!proof_core){ proof_core = new hash_set; AddToProofCore(*proof_core); } } void RPFP_caching::GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map) { std::vector conjs; CollectConjuncts(fmla, conjs); for (unsigned i = 0; i < conjs.size(); i++) { const expr &conj = conjs[i]; std::pair foo(conj, expr(ctx)); std::pair::iterator, bool> bar = AssumptionLits.insert(foo); Term &res = bar.first->second; if (bar.second) { func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); res = pred(); #ifdef LIMIT_STACK_WEIGHT new_alits.push_back(conj); #endif slvr().add(ctx.make(Implies, res, conj)); // std::cout << res << ": " << conj << "\n"; } if (opt_map) (*opt_map)[res] = conj; lits.push_back(res); } } void RPFP::ConstrainParent(Edge *parent, Node *child){ ConstrainEdgeLocalized(parent,GetAnnotation(child)); } void RPFP_caching::ConstrainParentCache(Edge *parent, Node *child, std::vector &lits){ ConstrainEdgeLocalizedCache(parent,GetAnnotation(child),lits); } /** For incremental solving, asserts the negation of the upper bound associated * with a node. * */ void RPFP::AssertNode(Node *n) { if (n->dual.null()) { n->dual = GetUpperBound(n); stack.back().nodes.push_back(n); slvr_add(n->dual); } } // caching version of above void RPFP_caching::AssertNodeCache(Node *n, std::vector lits){ if (n->dual.null()) { n->dual = GetUpperBound(n); stack.back().nodes.push_back(n); GetAssumptionLits(n->dual, lits); } } /** Clone another RPFP into this one, keeping a map */ void RPFP_caching::Clone(RPFP *other) { #if 0 for(unsigned i = 0; i < other->nodes.size(); i++) NodeCloneMap[other->nodes[i]] = CloneNode(other->nodes[i]); #endif for (unsigned i = 0; i < other->edges.size(); i++) { Edge *edge = other->edges[i]; Node *parent = CloneNode(edge->Parent); std::vector cs; for (unsigned j = 0; j < edge->Children.size(); j++) // cs.push_back(NodeCloneMap[edge->Children[j]]); cs.push_back(CloneNode(edge->Children[j])); EdgeCloneMap[edge] = CreateEdge(parent, edge->F, cs); } } /** Get the clone of a node */ RPFP::Node *RPFP_caching::GetNodeClone(Node *other_node){ return NodeCloneMap[other_node]; } /** Get the clone of an edge */ RPFP::Edge *RPFP_caching::GetEdgeClone(Edge *other_edge){ return EdgeCloneMap[other_edge]; } /** check assumption lits, and return core */ check_result RPFP_caching::CheckCore(const std::vector &assumps, std::vector &core){ core.resize(assumps.size()); unsigned core_size; check_result res = slvr().check(assumps.size(),(expr *)&assumps[0],&core_size,&core[0]); if(res == unsat) core.resize(core_size); else core.clear(); return res; } /** Assert a constraint on an edge in the SMT context. */ void RPFP::ConstrainEdge(Edge *e, const Term &t) { Term tl = Localize(e, t); ConstrainEdgeLocalized(e,tl); } void RPFP::ConstrainEdgeLocalized(Edge *e, const Term &tl) { e->constraints.push_back(tl); stack.back().constraints.push_back(std::pair(e,tl)); slvr_add(tl); } void RPFP_caching::ConstrainEdgeLocalizedCache(Edge *e, const Term &tl, std::vector &lits) { e->constraints.push_back(tl); stack.back().constraints.push_back(std::pair(e,tl)); GetAssumptionLits(tl,lits); } /** Declare a constant in the background theory. */ void RPFP::DeclareConstant(const FuncDecl &f){ ls->declare_constant(f); } /** Assert a background axiom. Background axioms can be used to provide the * theory of auxilliary functions or relations. All symbols appearing in * background axioms are considered global, and may appear in both transformer * and relational solutions. Semantically, a solution to the RPFP gives * an interpretation of the unknown relations for each interpretation of the * auxilliary symbols that is consistent with the axioms. Axioms should be * asserted before any calls to Push. They cannot be de-asserted by Pop. */ void RPFP::AssertAxiom(const Term &t) { ls->assert_axiom(t); axioms.push_back(t); // for printing only } #if 0 /** Do not call this. */ void RPFP::RemoveAxiom(const Term &t) { slvr().RemoveInterpolationAxiom(t); } #endif /** Solve an RPFP graph. This means either strengthen the annotation * so that the bound at the given root node is satisfied, or * show that this cannot be done by giving a dual solution * (i.e., a counterexample). * * In the current implementation, this only works for graphs that * are: * - tree-like * * - closed. * * In a tree-like graph, every nod has out most one incoming and one out-going edge, * and there are no cycles. In a closed graph, every node has exactly one out-going * edge. This means that the leaves of the tree are all hyper-edges with no * children. Such an edge represents a relation (nullary transformer) and thus * a lower bound on its parent. The parameter root must be the root of this tree. * * If Solve returns LBool.False, this indicates success. The annotation of the tree * has been updated to satisfy the upper bound at the root. * * If Solve returns LBool.True, this indicates a counterexample. For each edge, * you can then call Eval to determine the values of symbols in the transformer formula. * You can also call Empty on a node to determine if its value in the counterexample * is the empty relation. * * \param root The root of the tree * \param persist Number of context pops through which result should persist * * */ lbool RPFP::Solve(Node *root, int persist) { timer_start("Solve"); TermTree *tree = GetConstraintTree(root); TermTree *interpolant = NULL; TermTree *goals = NULL; if(ls->need_goals) goals = GetGoalTree(root); ClearProofCore(); // if (dualModel != null) dualModel.Dispose(); // if (dualLabels != null) dualLabels.Dispose(); timer_start("interpolate_tree"); lbool res = ls_interpolate_tree(tree, interpolant, dualModel,goals,true); timer_stop("interpolate_tree"); if (res == l_false) { DecodeTree(root, interpolant->getChildren()[0], persist); delete interpolant; } delete tree; if(goals) delete goals; timer_stop("Solve"); return res; } void RPFP::CollapseTermTreeRec(TermTree *root, TermTree *node){ root->addTerm(node->getTerm()); std::vector &cnsts = node->getTerms(); for(unsigned i = 0; i < cnsts.size(); i++) root->addTerm(cnsts[i]); std::vector &chs = node->getChildren(); for(unsigned i = 0; i < chs.size(); i++){ CollapseTermTreeRec(root,chs[i]); } } TermTree *RPFP::CollapseTermTree(TermTree *node){ std::vector &chs = node->getChildren(); for(unsigned i = 0; i < chs.size(); i++) CollapseTermTreeRec(node,chs[i]); for(unsigned i = 0; i < chs.size(); i++) delete chs[i]; chs.clear(); return node; } lbool RPFP::SolveSingleNode(Node *root, Node *node) { timer_start("Solve"); TermTree *tree = CollapseTermTree(GetConstraintTree(root,node)); tree->getChildren().push_back(CollapseTermTree(ToTermTree(node))); TermTree *interpolant = NULL; ClearProofCore(); timer_start("interpolate_tree"); lbool res = ls_interpolate_tree(tree, interpolant, dualModel,0,true); timer_stop("interpolate_tree"); if (res == l_false) { DecodeTree(node, interpolant->getChildren()[0], 0); delete interpolant; } delete tree; timer_stop("Solve"); return res; } /** Get the constraint tree (but don't solve it) */ TermTree *RPFP::GetConstraintTree(Node *root, Node *skip_descendant) { return AddUpperBound(root, ToTermTree(root,skip_descendant)); } /** Dispose of the dual model (counterexample) if there is one. */ void RPFP::DisposeDualModel() { dualModel = model(ctx,NULL); } RPFP::Term RPFP::UnderapproxFlag(Node *n){ expr flag = ctx.constant((std::string("@under") + string_of_int(n->number)).c_str(), ctx.bool_sort()); underapprox_flag_rev[flag] = n; return flag; } RPFP::Node *RPFP::UnderapproxFlagRev(const Term &flag){ return underapprox_flag_rev[flag]; } /** Check satisfiability of asserted edges and nodes. Same functionality as * Solve, except no primal solution (interpolant) is generated in the unsat case. * The vector underapproxes gives the set of node underapproximations to be enforced * (assuming these were conditionally asserted by AssertEdge). * */ check_result RPFP::Check(Node *root, std::vector underapproxes, std::vector *underapprox_core) { timer_start("Check"); ClearProofCore(); // if (dualModel != null) dualModel.Dispose(); check_result res; if (!underapproxes.size()) res = slvr_check(); else { std::vector us(underapproxes.size()); for (unsigned i = 0; i < underapproxes.size(); i++) us[i] = UnderapproxFlag(underapproxes[i]); slvr_check(); // TODO: no idea why I need to do this if (underapprox_core) { std::vector unsat_core(us.size()); unsigned core_size = 0; res = slvr_check(us.size(), &us[0], &core_size, &unsat_core[0]); underapprox_core->resize(core_size); for (unsigned i = 0; i < core_size; i++) (*underapprox_core)[i] = UnderapproxFlagRev(unsat_core[i]); } else { res = slvr_check(us.size(), &us[0]); bool dump = false; if (dump) { std::vector cnsts; // cnsts.push_back(axioms[0]); cnsts.push_back(root->dual); cnsts.push_back(root->Outgoing->dual); ls->write_interpolation_problem("temp.smt", cnsts, std::vector()); } } // check_result temp = slvr_check(); } dualModel = slvr().get_model(); timer_stop("Check"); return res; } check_result RPFP::CheckUpdateModel(Node *root, std::vector assumps){ // check_result temp1 = slvr_check(); // no idea why I need to do this ClearProofCore(); check_result res = slvr_check(assumps.size(),&assumps[0]); model mod = slvr().get_model(); if(!mod.null()) dualModel = mod;; return res; } /** Determines the value in the counterexample of a symbol occuring in the transformer formula of * a given edge. */ RPFP::Term RPFP::Eval(Edge *e, Term t) { Term tl = Localize(e, t); return dualModel.eval(tl); } /** Returns true if the given node is empty in the primal solution. For proecudure summaries, this means that the procedure is not called in the current counter-model. */ bool RPFP::Empty(Node *p) { Term b; std::vector v; RedVars(p, b, v); // dualModel.show_internals(); // std::cout << "b: " << b << std::endl; expr bv = dualModel.eval(b); // std::cout << "bv: " << bv << std::endl; bool res = !eq(bv,ctx.bool_val(true)); return res; } RPFP::Term RPFP::EvalNode(Node *p) { Term b; std::vector v; RedVars(p, b, v); std::vector args; for(unsigned i = 0; i < v.size(); i++) args.push_back(dualModel.eval(v[i])); return (p->Name)(args); } void RPFP::EvalArrayTerm(const RPFP::Term &t, ArrayValue &res){ if (t.is_app()) { decl_kind k = t.decl().get_decl_kind(); if (k == AsArray) { func_decl fd = t.decl().get_func_decl_parameter(0); func_interp r = dualModel.get_func_interp(fd); int num = r.num_entries(); res.defined = true; for (int i = 0; i < num; i++) { expr arg = r.get_arg(i, 0); expr value = r.get_value(i); res.entries[arg] = value; } res.def_val = r.else_value(); return; } else if (k == Store) { EvalArrayTerm(t.arg(0), res); if (!res.defined)return; expr addr = t.arg(1); expr val = t.arg(2); if (addr.is_numeral() && val.is_numeral()) { if (eq(val, res.def_val)) res.entries.erase(addr); else res.entries[addr] = val; } else res.defined = false; return; } } res.defined = false; } int eae_count = 0; RPFP::Term RPFP::EvalArrayEquality(const RPFP::Term &f) { ArrayValue lhs, rhs; eae_count++; EvalArrayTerm(f.arg(0), lhs); EvalArrayTerm(f.arg(1), rhs); if (lhs.defined && rhs.defined) { if (eq(lhs.def_val, rhs.def_val)) if (lhs.entries == rhs.entries) return ctx.bool_val(true); return ctx.bool_val(false); } return f; } /** Compute truth values of all boolean subterms in current model. Assumes formulas has been simplified by Z3, so only boolean ops ands and, or, not. Returns result in memo. */ int RPFP::SubtermTruth(hash_map &memo, const Term &f) { if (memo.find(f) != memo.end()) { return memo[f]; } int res; if (f.is_app()) { int nargs = f.num_args(); decl_kind k = f.decl().get_decl_kind(); if (k == Implies) { res = SubtermTruth(memo, !f.arg(0) || f.arg(1)); goto done; } if (k == And) { res = 1; for (int i = 0; i < nargs; i++) { int ar = SubtermTruth(memo, f.arg(i)); if (ar == 0) { res = 0; goto done; } if (ar == 2)res = 2; } goto done; } else if (k == Or) { res = 0; for (int i = 0; i < nargs; i++) { int ar = SubtermTruth(memo, f.arg(i)); if (ar == 1) { res = 1; goto done; } if (ar == 2)res = 2; } goto done; } else if (k == Not) { int ar = SubtermTruth(memo, f.arg(0)); res = (ar == 0) ? 1 : ((ar == 1) ? 0 : 2); goto done; } } { bool pos; std::vector names; if (f.is_label(pos, names)) { res = SubtermTruth(memo, f.arg(0)); goto done; } } { expr bv = dualModel.eval(f); if (bv.is_app() && bv.decl().get_decl_kind() == Equal && bv.arg(0).is_array()) { bv = EvalArrayEquality(bv); } // Hack!!!! array equalities can occur negatively! if (bv.is_app() && bv.decl().get_decl_kind() == Not && bv.arg(0).decl().get_decl_kind() == Equal && bv.arg(0).arg(0).is_array()) { bv = dualModel.eval(!EvalArrayEquality(bv.arg(0))); } if (eq(bv, ctx.bool_val(true))) res = 1; else if (eq(bv, ctx.bool_val(false))) res = 0; else res = 2; } done: memo[f] = res; return res; } int RPFP::EvalTruth(hash_map &memo, Edge *e, const Term &f){ Term tl = Localize(e, f); return SubtermTruth(memo,tl); } /** Compute truth values of all boolean subterms in current model. Assumes formulas has been simplified by Z3, so only boolean ops ands and, or, not. Returns result in memo. */ #if 0 int RPFP::GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos){ if(memo[labpos].find(f) != memo[labpos].end()){ return memo[labpos][f]; } int res; if(f.is_app()){ int nargs = f.num_args(); decl_kind k = f.decl().get_decl_kind(); if(k == Implies){ res = GetLabelsRec(memo,f.arg(1) || !f.arg(0), labels, labpos); goto done; } if(k == And) { res = 1; for(int i = 0; i < nargs; i++){ int ar = GetLabelsRec(memo,f.arg(i), labels, labpos); if(ar == 0){ res = 0; goto done; } if(ar == 2)res = 2; } goto done; } else if(k == Or) { res = 0; for(int i = 0; i < nargs; i++){ int ar = GetLabelsRec(memo,f.arg(i), labels, labpos); if(ar == 1){ res = 1; goto done; } if(ar == 2)res = 2; } goto done; } else if(k == Not) { int ar = GetLabelsRec(memo,f.arg(0), labels, !labpos); res = (ar == 0) ? 1 : ((ar == 1) ? 0 : 2); goto done; } } { bool pos; std::vector names; if(f.is_label(pos,names)){ res = GetLabelsRec(memo,f.arg(0), labels, labpos); if(pos == labpos && res == (pos ? 1 : 0)) for(unsigned i = 0; i < names.size(); i++) labels.push_back(names[i]); goto done; } } { expr bv = dualModel.eval(f); if(bv.is_app() && bv.decl().get_decl_kind() == Equal && bv.arg(0).is_array()){ bv = EvalArrayEquality(bv); } // Hack!!!! array equalities can occur negatively! if(bv.is_app() && bv.decl().get_decl_kind() == Not && bv.arg(0).decl().get_decl_kind() == Equal && bv.arg(0).arg(0).is_array()){ bv = dualModel.eval(!EvalArrayEquality(bv.arg(0))); } if(eq(bv,ctx.bool_val(true))) res = 1; else if(eq(bv,ctx.bool_val(false))) res = 0; else res = 2; } done: memo[labpos][f] = res; return res; } #endif void RPFP::GetLabelsRec(hash_map &memo, const Term &f, std::vector &labels, hash_set *done, bool truth) { if (done[truth].find(f) != done[truth].end()) return; /* already processed */ if (f.is_app()) { int nargs = f.num_args(); decl_kind k = f.decl().get_decl_kind(); if (k == Implies) { GetLabelsRec(memo, f.arg(1) || !f.arg(0), labels, done, truth); goto done; } if (k == Iff) { int b = SubtermTruth(memo, f.arg(0)); if (b == 2) throw "disaster in GetLabelsRec"; GetLabelsRec(memo, f.arg(1), labels, done, truth ? b : !b); goto done; } if (truth ? k == And : k == Or) { for (int i = 0; i < nargs; i++) GetLabelsRec(memo, f.arg(i), labels, done, truth); goto done; } if (truth ? k == Or : k == And) { for (int i = 0; i < nargs; i++) { Term a = f.arg(i); timer_start("SubtermTruth"); int b = SubtermTruth(memo, a); timer_stop("SubtermTruth"); if (truth ? (b == 1) : (b == 0)) { GetLabelsRec(memo, a, labels, done, truth); goto done; } } /* Unreachable! */ // throw "error in RPFP::GetLabelsRec"; goto done; } else if (k == Not) { GetLabelsRec(memo, f.arg(0), labels, done, !truth); goto done; } else { bool pos; std::vector names; if (f.is_label(pos, names)) { GetLabelsRec(memo, f.arg(0), labels, done, truth); if (pos == truth) for (unsigned i = 0; i < names.size(); i++) labels.push_back(names[i]); goto done; } } } done: done[truth].insert(f); } void RPFP::GetLabels(Edge *e, std::vector &labels){ if(!e->map || e->map->labeled.null()) return; Term tl = Localize(e, e->map->labeled); hash_map memo; hash_set done[2]; GetLabelsRec(memo,tl,labels,done,true); } #ifdef Z3OPS static Z3_subterm_truth *stt; #endif int ir_count = 0; void RPFP::ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, hash_set *done, bool truth, hash_set &dont_cares) { if (done[truth].find(f) != done[truth].end()) return; /* already processed */ #if 0 int this_count = ir_count++; if(this_count == 50092) std::cout << "foo!\n"; #endif if (f.is_app()) { int nargs = f.num_args(); decl_kind k = f.decl().get_decl_kind(); if (k == Implies) { ImplicantRed(memo, f.arg(1) || !f.arg(0), lits, done, truth, dont_cares); goto done; } if (k == Iff) { int b = SubtermTruth(memo, f.arg(0)); if (b == 2) throw "disaster in ImplicantRed"; ImplicantRed(memo, f.arg(1), lits, done, truth ? b : !b, dont_cares); goto done; } if (truth ? k == And : k == Or) { for (int i = 0; i < nargs; i++) ImplicantRed(memo, f.arg(i), lits, done, truth, dont_cares); goto done; } if (truth ? k == Or : k == And) { for (int i = 0; i < nargs; i++) { Term a = f.arg(i); #if 0 if(i == nargs - 1){ // last chance! ImplicantRed(memo,a,lits,done,truth,dont_cares); goto done; } #endif timer_start("SubtermTruth"); #ifdef Z3OPS bool b = stt->eval(a); #else int b = SubtermTruth(memo, a); #endif timer_stop("SubtermTruth"); if (truth ? (b == 1) : (b == 0)) { ImplicantRed(memo, a, lits, done, truth, dont_cares); goto done; } } /* Unreachable! */ // TODO: need to indicate this failure to caller // std::cerr << "error in RPFP::ImplicantRed"; goto done; } else if (k == Not) { ImplicantRed(memo, f.arg(0), lits, done, !truth, dont_cares); goto done; } } { if (dont_cares.find(f) == dont_cares.end()) { expr rf = ResolveIte(memo, f, lits, done, dont_cares); expr bv = truth ? rf : !rf; lits.push_back(bv); } } done: done[truth].insert(f); } void RPFP::ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, hash_set &done, hash_set &dont_cares, bool extensional) { if (done.find(f) != done.end()) return; /* already processed */ if (f.is_app()) { int nargs = f.num_args(); decl_kind k = f.decl().get_decl_kind(); if (k == Implies || k == Iff || k == And || k == Or || k == Not) { for (int i = 0; i < nargs; i++) ImplicantFullRed(memo, f.arg(i), lits, done, dont_cares, extensional); goto done; } } { if (dont_cares.find(f) == dont_cares.end()) { int b = SubtermTruth(memo, f); if (b != 0 && b != 1) goto done; if (f.is_app() && f.decl().get_decl_kind() == Equal && f.arg(0).is_array()) { if (b == 1 && !extensional) { expr x = dualModel.eval(f.arg(0)); expr y = dualModel.eval(f.arg(1)); if (!eq(x, y)) b = 0; } if (b == 0) goto done; } expr bv = (b == 1) ? f : !f; lits.push_back(bv); } } done: done.insert(f); } RPFP::Term RPFP::ResolveIte(hash_map &memo, const Term &t, std::vector &lits, hash_set *done, hash_set &dont_cares) { if (resolve_ite_memo.find(t) != resolve_ite_memo.end()) return resolve_ite_memo[t]; Term res; if (t.is_app()) { func_decl f = t.decl(); std::vector args; int nargs = t.num_args(); if (f.get_decl_kind() == Ite) { timer_start("SubtermTruth"); #ifdef Z3OPS bool sel = stt->eval(t.arg(0)); #else int xval = SubtermTruth(memo, t.arg(0)); bool sel; if (xval == 0)sel = false; else if (xval == 1)sel = true; else throw "unresolved ite in model"; #endif timer_stop("SubtermTruth"); ImplicantRed(memo, t.arg(0), lits, done, sel, dont_cares); res = ResolveIte(memo, t.arg(sel ? 1 : 2), lits, done, dont_cares); } else { for (int i = 0; i < nargs; i++) args.push_back(ResolveIte(memo, t.arg(i), lits, done, dont_cares)); res = f(args.size(), &args[0]); } } else res = t; resolve_ite_memo[t] = res; return res; } RPFP::Term RPFP::ElimIteRec(hash_map &memo, const Term &t, std::vector &cnsts) { std::pair foo(t, expr(ctx)); std::pair::iterator, bool> bar = memo.insert(foo); Term &res = bar.first->second; if (bar.second) { if (t.is_app()) { int nargs = t.num_args(); std::vector args; if (t.decl().get_decl_kind() == Equal) { expr lhs = t.arg(0); expr rhs = t.arg(1); if (rhs.decl().get_decl_kind() == Ite) { expr rhs_args[3]; lhs = ElimIteRec(memo, lhs, cnsts); for (int i = 0; i < 3; i++) rhs_args[i] = ElimIteRec(memo, rhs.arg(i), cnsts); res = (rhs_args[0] && (lhs == rhs_args[1])) || ((!rhs_args[0]) && (lhs == rhs_args[2])); goto done; } } if (t.decl().get_decl_kind() == Ite) { func_decl sym = ctx.fresh_func_decl("@ite", t.get_sort()); res = sym(); cnsts.push_back(ElimIteRec(memo, ctx.make(Equal, res, t), cnsts)); } else { for (int i = 0; i < nargs; i++) args.push_back(ElimIteRec(memo, t.arg(i), cnsts)); res = t.decl()(args.size(), &args[0]); } } else if (t.is_quantifier()) res = clone_quantifier(t, ElimIteRec(memo, t.body(), cnsts)); else res = t; } done: return res; } RPFP::Term RPFP::ElimIte(const Term &t){ hash_map memo; std::vector cnsts; expr res = ElimIteRec(memo,t,cnsts); if(!cnsts.empty()){ cnsts.push_back(res); res = ctx.make(And,cnsts); } return res; } void RPFP::Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares){ hash_set done[2]; ImplicantRed(memo,f,lits,done,true, dont_cares); } /** Underapproximate a formula using current counterexample. */ RPFP::Term RPFP::UnderapproxFormula(const Term &f, hash_set &dont_cares){ /* first compute truth values of subterms */ hash_map memo; #ifdef Z3OPS stt = Z3_mk_subterm_truth(ctx,dualModel); #endif // SubtermTruth(memo,f); /* now compute an implicant */ std::vector lits; Implicant(memo,f,lits, dont_cares); #ifdef Z3OPS delete stt; stt = 0; #endif /* return conjunction of literals */ return conjoin(lits); } RPFP::Term RPFP::UnderapproxFullFormula(const Term &f, bool extensional){ hash_set dont_cares; resolve_ite_memo.clear(); timer_start("UnderapproxFormula"); /* first compute truth values of subterms */ hash_map memo; hash_set done; std::vector lits; ImplicantFullRed(memo,f,lits,done,dont_cares, extensional); timer_stop("UnderapproxFormula"); /* return conjunction of literals */ return conjoin(lits); } struct VariableProjector : Z3User { struct elim_cand { Term var; int sup; Term val; }; typedef expr Term; hash_set keep; hash_map var_ord; int num_vars; std::vector elim_cands; hash_map > sup_map; hash_map elim_map; std::vector ready_cands; hash_map cand_map; params simp_params; VariableProjector(Z3User &_user, std::vector &keep_vec) : Z3User(_user), simp_params() { num_vars = 0; for (unsigned i = 0; i < keep_vec.size(); i++) { keep.insert(keep_vec[i]); var_ord[keep_vec[i]] = num_vars++; } } int VarNum(const Term &v) { if (var_ord.find(v) == var_ord.end()) var_ord[v] = num_vars++; return var_ord[v]; } bool IsVar(const Term &t){ return t.is_app() && t.num_args() == 0 && t.decl().get_decl_kind() == Uninterpreted; } bool IsPropLit(const Term &t, Term &a) { if (IsVar(t)) { a = t; return true; } else if (t.is_app() && t.decl().get_decl_kind() == Not) return IsPropLit(t.arg(0), a); return false; } void CountOtherVarsRec(hash_map &memo, const Term &t, int id, int &count) { std::pair foo(t, 0); std::pair::iterator, bool> bar = memo.insert(foo); // int &res = bar.first->second; if (!bar.second) return; if (t.is_app()) { func_decl f = t.decl(); std::vector args; int nargs = t.num_args(); if (nargs == 0 && f.get_decl_kind() == Uninterpreted) { if (cand_map.find(t) != cand_map.end()) { count++; sup_map[t].push_back(id); } } for (int i = 0; i < nargs; i++) CountOtherVarsRec(memo, t.arg(i), id, count); } else if (t.is_quantifier()) CountOtherVarsRec(memo, t.body(), id, count); } void NewElimCand(const Term &lhs, const Term &rhs) { if (debug_gauss) { std::cout << "mapping " << lhs << " to " << rhs << std::endl; } elim_cand cand; cand.var = lhs; cand.sup = 0; cand.val = rhs; elim_cands.push_back(cand); cand_map[lhs] = elim_cands.size() - 1; } void MakeElimCand(const Term &lhs, const Term &rhs) { if (eq(lhs, rhs)) return; if (!IsVar(lhs)) { if (IsVar(rhs)) { MakeElimCand(rhs, lhs); return; } else { std::cout << "would have mapped a non-var\n"; return; } } if (IsVar(rhs) && VarNum(rhs) > VarNum(lhs)) { MakeElimCand(rhs, lhs); return; } if (keep.find(lhs) != keep.end()) return; if (cand_map.find(lhs) == cand_map.end()) NewElimCand(lhs, rhs); else { int cand_idx = cand_map[lhs]; if (IsVar(rhs) && cand_map.find(rhs) == cand_map.end() && keep.find(rhs) == keep.end()) NewElimCand(rhs, elim_cands[cand_idx].val); elim_cands[cand_idx].val = rhs; } } Term FindRep(const Term &t) { if (cand_map.find(t) == cand_map.end()) return t; Term &res = elim_cands[cand_map[t]].val; if (IsVar(res)) { assert(VarNum(res) < VarNum(t)); res = FindRep(res); return res; } return t; } void GaussElimCheap(const std::vector &lits_in, std::vector &lits_out) { for (unsigned i = 0; i < lits_in.size(); i++) { Term lit = lits_in[i]; if (lit.is_app()) { decl_kind k = lit.decl().get_decl_kind(); if (k == Equal || k == Iff) MakeElimCand(FindRep(lit.arg(0)), FindRep(lit.arg(1))); } } for (unsigned i = 0; i < elim_cands.size(); i++) { elim_cand &cand = elim_cands[i]; hash_map memo; CountOtherVarsRec(memo, cand.val, i, cand.sup); if (cand.sup == 0) ready_cands.push_back(i); } while (!ready_cands.empty()) { elim_cand &cand = elim_cands[ready_cands.back()]; ready_cands.pop_back(); Term rep = FindRep(cand.var); if (!eq(rep, cand.var)) if (cand_map.find(rep) != cand_map.end()) { int rep_pos = cand_map[rep]; cand.val = elim_cands[rep_pos].val; } Term val = SubstRec(elim_map, cand.val); if (debug_gauss) { std::cout << "subbing " << cand.var << " --> " << val << std::endl; } elim_map[cand.var] = val; std::vector &sup = sup_map[cand.var]; for (unsigned i = 0; i < sup.size(); i++) { int c = sup[i]; if ((--elim_cands[c].sup) == 0) ready_cands.push_back(c); } } for (unsigned i = 0; i < lits_in.size(); i++) { Term lit = lits_in[i]; lit = SubstRec(elim_map, lit); lit = lit.simplify(); if (eq(lit, ctx.bool_val(true))) continue; Term a; if (IsPropLit(lit, a)) if (keep.find(lit) == keep.end()) continue; lits_out.push_back(lit); } } // maps variables to constrains in which the occur pos, neg hash_map la_index[2]; hash_map la_coeffs[2]; std::vector la_pos_vars; bool fixing; void IndexLAcoeff(const Term &coeff1, const Term &coeff2, Term t, int id) { Term coeff = coeff1 * coeff2; coeff = coeff.simplify(); Term is_pos = (coeff >= ctx.int_val(0)); is_pos = is_pos.simplify(); if (eq(is_pos, ctx.bool_val(true))) IndexLA(true, coeff, t, id); else IndexLA(false, coeff, t, id); } void IndexLAremove(const Term &t) { if (IsVar(t)) { la_index[0][t] = -1; // means ineligible to be eliminated la_index[1][t] = -1; // (more that one occurrence, or occurs not in linear comb) } else if (t.is_app()) { int nargs = t.num_args(); for (int i = 0; i < nargs; i++) IndexLAremove(t.arg(i)); } // TODO: quantifiers? } void IndexLA(bool pos, const Term &coeff, const Term &t, int id) { if (t.is_numeral()) return; if (t.is_app()) { int nargs = t.num_args(); switch (t.decl().get_decl_kind()) { case Plus: for (int i = 0; i < nargs; i++) IndexLA(pos, coeff, t.arg(i), id); break; case Sub: IndexLA(pos, coeff, t.arg(0), id); IndexLA(!pos, coeff, t.arg(1), id); break; case Times: if (t.arg(0).is_numeral()) IndexLAcoeff(coeff, t.arg(0), t.arg(1), id); else if (t.arg(1).is_numeral()) IndexLAcoeff(coeff, t.arg(1), t.arg(0), id); break; default: if (IsVar(t) && (fixing || la_index[pos].find(t) == la_index[pos].end())) { la_index[pos][t] = id; la_coeffs[pos][t] = coeff; if (pos && !fixing) la_pos_vars.push_back(t); // this means we only add a var once } else IndexLAremove(t); } } } void IndexLAstart(bool pos, const Term &t, int id){ IndexLA(pos,(pos ? ctx.int_val(1) : ctx.int_val(-1)), t, id); } void IndexLApred(bool pos, const Term &p, int id) { if (p.is_app()) { switch (p.decl().get_decl_kind()) { case Not: IndexLApred(!pos, p.arg(0), id); break; case Leq: case Lt: IndexLAstart(!pos, p.arg(0), id); IndexLAstart(pos, p.arg(1), id); break; case Geq: case Gt: IndexLAstart(pos, p.arg(0), id); IndexLAstart(!pos, p.arg(1), id); break; default: IndexLAremove(p); } } } void IndexLAfix(const Term &p, int id){ fixing = true; IndexLApred(true,p,id); fixing = false; } bool IsCanonIneq(const Term &lit, Term &term, Term &bound) { // std::cout << Z3_simplify_get_help(ctx) << std::endl; bool pos = lit.decl().get_decl_kind() != Not; Term atom = pos ? lit : lit.arg(0); if (atom.decl().get_decl_kind() == Leq) { if (pos) { bound = atom.arg(0); term = atom.arg(1).simplify(simp_params); #if Z3_MAJOR_VERSION < 4 term = SortSum(term); #endif } else { bound = (atom.arg(1) + ctx.int_val(1)); term = atom.arg(0); // std::cout << "simplifying bound: " << bound << std::endl; bound = bound.simplify(); term = term.simplify(simp_params); #if Z3_MAJOR_VERSION < 4 term = SortSum(term); #endif } return true; } else if (atom.decl().get_decl_kind() == Geq) { if (pos) { bound = atom.arg(1); // integer axiom term = atom.arg(0).simplify(simp_params); #if Z3_MAJOR_VERSION < 4 term = SortSum(term); #endif return true; } else { bound = -(atom.arg(1) - ctx.int_val(1)); // integer axiom term = -atom.arg(0); bound = bound.simplify(); term = term.simplify(simp_params); #if Z3_MAJOR_VERSION < 4 term = SortSum(term); #endif } return true; } return false; } Term CanonIneqTerm(const Term &p){ Term term,bound; Term ps = p.simplify(); bool ok = IsCanonIneq(ps,term,bound); assert(ok); return term - bound; } void ElimRedundantBounds(std::vector &lits) { hash_map best_bound; for (unsigned i = 0; i < lits.size(); i++) { lits[i] = lits[i].simplify(simp_params); Term term, bound; if (IsCanonIneq(lits[i], term, bound)) { if (best_bound.find(term) == best_bound.end()) best_bound[term] = i; else { int best = best_bound[term]; Term bterm, bbound; IsCanonIneq(lits[best], bterm, bbound); Term comp = bound > bbound; comp = comp.simplify(); if (eq(comp, ctx.bool_val(true))) { lits[best] = ctx.bool_val(true); best_bound[term] = i; } else { lits[i] = ctx.bool_val(true); } } } } } void FourierMotzkinCheap(const std::vector &lits_in, std::vector &lits_out) { simp_params.set(":som", true); simp_params.set(":sort-sums", true); fixing = false; lits_out = lits_in; ElimRedundantBounds(lits_out); for (unsigned i = 0; i < lits_out.size(); i++) IndexLApred(true, lits_out[i], i); for (unsigned i = 0; i < la_pos_vars.size(); i++) { Term var = la_pos_vars[i]; if (la_index[false].find(var) != la_index[false].end()) { int pos_idx = la_index[true][var]; int neg_idx = la_index[false][var]; if (pos_idx >= 0 && neg_idx >= 0) { if (keep.find(var) != keep.end()) { std::cout << "would have eliminated keep var\n"; continue; } Term tpos = CanonIneqTerm(lits_out[pos_idx]); Term tneg = CanonIneqTerm(lits_out[neg_idx]); Term pos_coeff = la_coeffs[true][var]; Term neg_coeff = -la_coeffs[false][var]; Term comb = neg_coeff * tpos + pos_coeff * tneg; Term ineq = ctx.int_val(0) <= comb; ineq = ineq.simplify(); lits_out[pos_idx] = ineq; lits_out[neg_idx] = ctx.bool_val(true); IndexLAfix(ineq, pos_idx); } } } } Term ProjectFormula(const Term &f){ std::vector lits, new_lits1, new_lits2; CollectConjuncts(f,lits); timer_start("GaussElimCheap"); GaussElimCheap(lits,new_lits1); timer_stop("GaussElimCheap"); timer_start("FourierMotzkinCheap"); FourierMotzkinCheap(new_lits1,new_lits2); timer_stop("FourierMotzkinCheap"); return conjoin(new_lits2); } }; void Z3User::CollectConjuncts(const Term &f, std::vector &lits, bool pos) { if (f.is_app() && f.decl().get_decl_kind() == Not) CollectConjuncts(f.arg(0), lits, !pos); else if (pos && f.is_app() && f.decl().get_decl_kind() == And) { int num_args = f.num_args(); for (int i = 0; i < num_args; i++) CollectConjuncts(f.arg(i), lits, true); } else if (!pos && f.is_app() && f.decl().get_decl_kind() == Or) { int num_args = f.num_args(); for (int i = 0; i < num_args; i++) CollectConjuncts(f.arg(i), lits, false); } else if (pos) { if (!eq(f, ctx.bool_val(true))) lits.push_back(f); } else { if (!eq(f, ctx.bool_val(false))) lits.push_back(!f); } } void Z3User::CollectJuncts(const Term &f, std::vector &lits, decl_kind op, bool negate) { if (f.is_app() && f.decl().get_decl_kind() == Not) CollectJuncts(f.arg(0), lits, (op == And) ? Or : And, !negate); else if (f.is_app() && f.decl().get_decl_kind() == op) { int num_args = f.num_args(); for (int i = 0; i < num_args; i++) CollectJuncts(f.arg(i), lits, op, negate); } else { expr junct = negate ? Negate(f) : f; lits.push_back(junct); } } struct TermLt { bool operator()(const expr &x, const expr &y){ unsigned xid = x.get_id(); unsigned yid = y.get_id(); return xid < yid; } }; void Z3User::SortTerms(std::vector &terms){ TermLt foo; std::sort(terms.begin(),terms.end(),foo); } Z3User::Term Z3User::SortSum(const Term &t){ if(!(t.is_app() && t.decl().get_decl_kind() == Plus)) return t; int nargs = t.num_args(); if(nargs < 2) return t; std::vector args(nargs); for(int i = 0; i < nargs; i++) args[i] = t.arg(i); SortTerms(args); if(nargs == 2) return args[0] + args[1]; return sum(args); } RPFP::Term RPFP::ProjectFormula(std::vector &keep_vec, const Term &f){ VariableProjector vp(*this,keep_vec); return vp.ProjectFormula(f); } /** Compute an underapproximation of every node in a tree rooted at "root", based on a previously computed counterexample. The underapproximation may contain free variables that are implicitly existentially quantified. */ RPFP::Term RPFP::ComputeUnderapprox(Node *root, int persist){ /* if terminated underapprox is empty set (false) */ bool show_model = false; if(show_model) std::cout << dualModel << std::endl; if(!root->Outgoing){ root->Underapprox.SetEmpty(); return ctx.bool_val(true); } /* if not used in cex, underapprox is empty set (false) */ if(Empty(root)){ root->Underapprox.SetEmpty(); return ctx.bool_val(true); } /* compute underapprox of children first */ std::vector &chs = root->Outgoing->Children; std::vector chu(chs.size()); for(unsigned i = 0; i < chs.size(); i++) chu[i] = ComputeUnderapprox(chs[i],persist); Term b; std::vector v; RedVars(root, b, v); /* underapproximate the edge formula */ hash_set dont_cares; dont_cares.insert(b); resolve_ite_memo.clear(); timer_start("UnderapproxFormula"); Term dual = root->Outgoing->dual.null() ? ctx.bool_val(true) : root->Outgoing->dual; Term eu = UnderapproxFormula(dual,dont_cares); timer_stop("UnderapproxFormula"); /* combine with children */ chu.push_back(eu); eu = conjoin(chu); /* project onto appropriate variables */ eu = ProjectFormula(v,eu); eu = eu.simplify(); #if 0 /* check the result is consistent */ { hash_map memo; int res = SubtermTruth(memo, eu); if(res != 1) throw "inconsistent projection"; } #endif /* rename to appropriate variable names */ hash_map memo; for (unsigned i = 0; i < v.size(); i++) memo[v[i]] = root->Annotation.IndParams[i]; /* copy names from annotation */ Term funder = SubstRec(memo, eu); root->Underapprox = CreateRelation(root->Annotation.IndParams,funder); #if 0 if(persist) Z3_persist_ast(ctx,root->Underapprox.Formula,persist); #endif return eu; } void RPFP::FixCurrentState(Edge *edge){ hash_set dont_cares; resolve_ite_memo.clear(); timer_start("UnderapproxFormula"); Term dual = edge->dual.null() ? ctx.bool_val(true) : edge->dual; Term eu = UnderapproxFormula(dual,dont_cares); timer_stop("UnderapproxFormula"); ConstrainEdgeLocalized(edge,eu); } void RPFP::GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under){ if(memo[under].find(f) != memo[under].end()) return; memo[under].insert(f); if (f.is_app()) { if (!under && !f.has_quantifiers()) return; decl_kind k = f.decl().get_decl_kind(); if (k == And || k == Or || k == Implies || k == Iff) { int num_args = f.num_args(); for (int i = 0; i < num_args; i++) GetGroundLitsUnderQuants(memo, f.arg(i), res, under); return; } } else if (f.is_quantifier()){ #if 0 // treat closed quantified formula as a literal 'cause we hate nested quantifiers if(under && IsClosedFormula(f)) res.push_back(f); else #endif GetGroundLitsUnderQuants(memo,f.body(),res,1); return; } if(f.is_var()){ // std::cout << "foo!\n"; return; } if(under && f.is_ground()) res.push_back(f); } RPFP::Term RPFP::StrengthenFormulaByCaseSplitting(const Term &f, std::vector &case_lits){ hash_set memo[2]; std::vector lits; GetGroundLitsUnderQuants(memo, f, lits, 0); hash_set lits_hash; for(unsigned i = 0; i < lits.size(); i++) lits_hash.insert(lits[i]); hash_map subst; hash_map stt_memo; std::vector conjuncts; for(unsigned i = 0; i < lits.size(); i++){ const expr &lit = lits[i]; if(lits_hash.find(NegateLit(lit)) == lits_hash.end()){ case_lits.push_back(lit); bool tval = false; expr atom = lit; if(lit.is_app() && lit.decl().get_decl_kind() == Not){ tval = true; atom = lit.arg(0); } expr etval = ctx.bool_val(tval); if(atom.is_quantifier()) subst[atom] = etval; // this is a bit desperate, since we can't eval quants else { int b = SubtermTruth(stt_memo,atom); if(b == (tval ? 1 : 0)) subst[atom] = etval; else { if(b == 0 || b == 1){ etval = ctx.bool_val(b ? true : false); subst[atom] = etval; conjuncts.push_back(b ? atom : !atom); } } } } } expr g = f; if(!subst.empty()){ g = SubstRec(subst,f); if(conjuncts.size()) g = g && ctx.make(And,conjuncts); g = g.simplify(); } #if 1 expr g_old = g; g = RemoveRedundancy(g); bool changed = !eq(g,g_old); g = g.simplify(); if(changed) { // a second pass can get some more simplification g = RemoveRedundancy(g); g = g.simplify(); } #else g = RemoveRedundancy(g); g = g.simplify(); #endif g = AdjustQuantifiers(g); return g; } RPFP::Term RPFP::ModelValueAsConstraint(const Term &t) { if (t.is_array()) { ArrayValue arr; Term e = dualModel.eval(t); EvalArrayTerm(e, arr); if (arr.defined) { std::vector cs; for (std::map::iterator it = arr.entries.begin(), en = arr.entries.end(); it != en; ++it) { expr foo = select(t, expr(ctx, it->first)) == expr(ctx, it->second); cs.push_back(foo); } return conjoin(cs); } } else { expr r = dualModel.get_const_interp(t.decl()); if (!r.null()) { expr res = t == expr(ctx, r); return res; } } return ctx.bool_val(true); } void RPFP::EvalNodeAsConstraint(Node *p, Transformer &res) { Term b; std::vector v; RedVars(p, b, v); std::vector args; for(unsigned i = 0; i < v.size(); i++){ expr val = ModelValueAsConstraint(v[i]); if(!eq(val,ctx.bool_val(true))) args.push_back(val); } expr cnst = conjoin(args); hash_map memo; for (unsigned i = 0; i < v.size(); i++) memo[v[i]] = p->Annotation.IndParams[i]; /* copy names from annotation */ Term funder = SubstRec(memo, cnst); res = CreateRelation(p->Annotation.IndParams,funder); } #if 0 void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ // verify s.push(); expr conj = ctx.make(And,conjuncts); s.add(conj); check_result res = s.check(); if(res != unsat) throw "should be unsat"; s.pop(1); for(unsigned i = 0; i < conjuncts.size(); ){ std::swap(conjuncts[i],conjuncts.back()); expr save = conjuncts.back(); conjuncts.pop_back(); s.push(); expr conj = ctx.make(And,conjuncts); s.add(conj); check_result res = s.check(); s.pop(1); if(res != unsat){ conjuncts.push_back(save); std::swap(conjuncts[i],conjuncts.back()); i++; } } } #endif void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ std::vector lits(conjuncts.size()); for(unsigned i = 0; i < lits.size(); i++){ func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); lits[i] = pred(); s.add(ctx.make(Implies,lits[i],conjuncts[i])); } // verify check_result res = s.check(lits.size(),&lits[0]); if(res != unsat){ // add the axioms in the off chance they are useful const std::vector &theory = ls->get_axioms(); for(unsigned i = 0; i < theory.size(); i++) s.add(theory[i]); for(int k = 0; k < 100; k++) // keep trying, maybe MBQI will do something! if(s.check(lits.size(),&lits[0]) == unsat) goto is_unsat; throw "should be unsat"; } is_unsat: for(unsigned i = 0; i < conjuncts.size(); ){ std::swap(conjuncts[i],conjuncts.back()); std::swap(lits[i],lits.back()); check_result res = s.check(lits.size()-1,&lits[0]); if(res != unsat){ std::swap(conjuncts[i],conjuncts.back()); std::swap(lits[i],lits.back()); i++; } else { conjuncts.pop_back(); lits.pop_back(); } } } void foobar(){ } void RPFP::GreedyReduceNodes(std::vector &nodes){ std::vector lits; for(unsigned i = 0; i < nodes.size(); i++){ Term b; std::vector v; RedVars(nodes[i], b, v); lits.push_back(!b); expr bv = dualModel.eval(b); if(eq(bv,ctx.bool_val(true))){ check_result res = slvr_check(lits.size(),&lits[0]); if(res == unsat) lits.pop_back(); else foobar(); } } } check_result RPFP::CheckWithConstrainedNodes(std::vector &posnodes,std::vector &negnodes){ timer_start("Check"); std::vector lits; for(unsigned i = 0; i < posnodes.size(); i++){ Term b; std::vector v; RedVars(posnodes[i], b, v); lits.push_back(b); } for(unsigned i = 0; i < negnodes.size(); i++){ Term b; std::vector v; RedVars(negnodes[i], b, v); lits.push_back(!b); } check_result res = slvr_check(lits.size(),&lits[0]); if(res == unsat && posnodes.size()){ lits.resize(posnodes.size()); res = slvr_check(lits.size(),&lits[0]); } dualModel = slvr().get_model(); #if 0 if(!dualModel.null()){ std::cout << "posnodes called:\n"; for(unsigned i = 0; i < posnodes.size(); i++) if(!Empty(posnodes[i])) std::cout << posnodes[i]->Name.name() << "\n"; std::cout << "negnodes called:\n"; for(unsigned i = 0; i < negnodes.size(); i++) if(!Empty(negnodes[i])) std::cout << negnodes[i]->Name.name() << "\n"; } #endif timer_stop("Check"); return res; } void RPFP_caching::FilterCore(std::vector &core, std::vector &full_core){ hash_set core_set; std::copy(full_core.begin(),full_core.end(),std::inserter(core_set,core_set.begin())); std::vector new_core; for(unsigned i = 0; i < core.size(); i++) if(core_set.find(core[i]) != core_set.end()) new_core.push_back(core[i]); core.swap(new_core); } void RPFP_caching::GreedyReduceCache(std::vector &assumps, std::vector &core){ std::vector lits = assumps, full_core; std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); // verify check_result res = CheckCore(lits,full_core); if(res != unsat){ // add the axioms in the off chance they are useful const std::vector &theory = ls->get_axioms(); for(unsigned i = 0; i < theory.size(); i++) GetAssumptionLits(theory[i],assumps); lits = assumps; std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); for(int k = 0; k < 100; k++) // keep trying, maybe MBQI will do something! if((res = CheckCore(lits,full_core)) == unsat) goto is_unsat; throw "should be unsat"; } is_unsat: FilterCore(core,full_core); std::vector dummy; if(CheckCore(full_core,dummy) != unsat) throw "should be unsat"; for(unsigned i = 0; i < core.size(); ){ expr temp = core[i]; std::swap(core[i],core.back()); core.pop_back(); lits.resize(assumps.size()); std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); res = CheckCore(lits,full_core); if(res != unsat){ core.push_back(temp); std::swap(core[i],core.back()); i++; } } } expr RPFP::NegateLit(const expr &f){ if(f.is_app() && f.decl().get_decl_kind() == Not) return f.arg(0); else return !f; } void RPFP::NegateLits(std::vector &lits){ for(unsigned i = 0; i < lits.size(); i++){ expr &f = lits[i]; if(f.is_app() && f.decl().get_decl_kind() == Not) f = f.arg(0); else f = !f; } } expr RPFP::SimplifyOr(std::vector &lits){ if(lits.size() == 0) return ctx.bool_val(false); if(lits.size() == 1) return lits[0]; return ctx.make(Or,lits); } expr RPFP::SimplifyAnd(std::vector &lits){ if(lits.size() == 0) return ctx.bool_val(true); if(lits.size() == 1) return lits[0]; return ctx.make(And,lits); } /* This is a wrapper for a solver that is intended to compute implicants from models. It works around a problem in Z3 with models in the non-extensional array theory. It does this by naming all of the store terms in a formula. That is, (store ...) is replaced by "name" with an added constraint name = (store ...). This allows us to determine from the model whether an array equality is true or false (it is false if the two sides are mapped to different function symbols, even if they have the same contents). */ struct implicant_solver { RPFP *owner; solver &aux_solver; std::vector assumps, namings; std::vector assump_stack, naming_stack; hash_map renaming, renaming_memo; void add(const expr &e){ expr t = e; if(!aux_solver.extensional_array_theory()){ unsigned i = namings.size(); t = owner->ExtractStores(renaming_memo,t,namings,renaming); for(; i < namings.size(); i++) aux_solver.add(namings[i]); } assumps.push_back(t); aux_solver.add(t); } void push() { assump_stack.push_back(assumps.size()); naming_stack.push_back(namings.size()); aux_solver.push(); } // When we pop the solver, we have to re-add any namings that were lost void pop(int n) { aux_solver.pop(n); int new_assumps = assump_stack[assump_stack.size()-n]; int new_namings = naming_stack[naming_stack.size()-n]; for(unsigned i = new_namings; i < namings.size(); i++) aux_solver.add(namings[i]); assumps.resize(new_assumps); namings.resize(new_namings); assump_stack.resize(assump_stack.size()-1); naming_stack.resize(naming_stack.size()-1); } check_result check() { return aux_solver.check(); } model get_model() { return aux_solver.get_model(); } expr get_implicant() { owner->dualModel = aux_solver.get_model(); expr dual = owner->ctx.make(And,assumps); bool ext = aux_solver.extensional_array_theory(); expr eu = owner->UnderapproxFullFormula(dual,ext); // if we renamed store terms, we have to undo if(!ext) eu = owner->SubstRec(renaming,eu); return eu; } implicant_solver(RPFP *_owner, solver &_aux_solver) : owner(_owner), aux_solver(_aux_solver) {} }; // set up edge constraint in aux solver void RPFP::AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge){ if(!edge->dual.null()) aux_solver.add(edge->dual); for(unsigned i = 0; i < edge->constraints.size(); i++){ expr tl = edge->constraints[i]; aux_solver.add(tl); } } void RPFP::AddEdgeToSolver(Edge *edge){ if(!edge->dual.null()) aux_solver.add(edge->dual); for(unsigned i = 0; i < edge->constraints.size(); i++){ expr tl = edge->constraints[i]; aux_solver.add(tl); } } static int by_case_counter = 0; void RPFP::InterpolateByCases(Node *root, Node *node){ timer_start("InterpolateByCases"); bool axioms_added = false; hash_set axioms_needed; const std::vector &theory = ls->get_axioms(); for(unsigned i = 0; i < theory.size(); i++) axioms_needed.insert(theory[i]); implicant_solver is(this,aux_solver); is.push(); AddEdgeToSolver(is,node->Outgoing); node->Annotation.SetEmpty(); hash_set *core = new hash_set; core->insert(node->Outgoing->dual); expr prev_annot = ctx.bool_val(false); expr prev_impl = ctx.bool_val(false); int repeated_case_count = 0; while(1){ by_case_counter++; is.push(); expr annot = !GetAnnotation(node); Transformer old_annot = node->Annotation; is.add(annot); if(is.check() == unsat){ is.pop(1); break; } is.pop(1); Push(); expr the_impl = is.get_implicant(); if(eq(the_impl,prev_impl)){ // std::cout << "got old implicant\n"; repeated_case_count++; } prev_impl = the_impl; ConstrainEdgeLocalized(node->Outgoing,the_impl); ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); //TODO: need this? { check_result foo = Check(root); if(foo != unsat){ Pop(1); is.pop(1); delete core; timer_stop("InterpolateByCases"); throw ReallyBad(); // slvr().print("should_be_unsat.smt2"); // throw "should be unsat"; } std::vector assumps, axioms_to_add; slvr().get_proof().get_assumptions(assumps); for(unsigned i = 0; i < assumps.size(); i++){ (*core).insert(assumps[i]); if(axioms_needed.find(assumps[i]) != axioms_needed.end()){ axioms_to_add.push_back(assumps[i]); axioms_needed.erase(assumps[i]); } } // AddToProofCore(*core); SolveSingleNode(root,node); { expr itp = GetAnnotation(node); dualModel = is.get_model(); // TODO: what does this mean? std::vector case_lits; itp = StrengthenFormulaByCaseSplitting(itp, case_lits); SetAnnotation(node,itp); node->Annotation.Formula = node->Annotation.Formula.simplify(); } for(unsigned i = 0; i < axioms_to_add.size(); i++) is.add(axioms_to_add[i]); #define TEST_BAD #ifdef TEST_BAD { static int bad_count = 0, num_bads = 1; if(bad_count >= num_bads){ bad_count = 0; num_bads = num_bads * 2; Pop(1); is.pop(1); delete core; timer_stop("InterpolateByCases"); throw Bad(); } bad_count++; } #endif } if(node->Annotation.IsEmpty() || eq(node->Annotation.Formula,prev_annot) || (repeated_case_count > 0 && !axioms_added) || (repeated_case_count >= 10)){ //looks_bad: if(!axioms_added){ // add the axioms in the off chance they are useful const std::vector &theory = ls->get_axioms(); for(unsigned i = 0; i < theory.size(); i++) is.add(theory[i]); axioms_added = true; } else { //#define KILL_ON_BAD_INTERPOLANT #ifdef KILL_ON_BAD_INTERPOLANT std::cout << "bad in InterpolateByCase -- core:\n"; #if 0 std::vector assumps; slvr().get_proof().get_assumptions(assumps); for(unsigned i = 0; i < assumps.size(); i++) assumps[i].show(); #endif std::cout << "checking for inconsistency\n"; std::cout << "model:\n"; is.get_model().show(); expr impl = is.get_implicant(); std::vector conjuncts; CollectConjuncts(impl,conjuncts,true); std::cout << "impl:\n"; for(unsigned i = 0; i < conjuncts.size(); i++) conjuncts[i].show(); std::cout << "annot:\n"; annot.show(); is.add(annot); for(unsigned i = 0; i < conjuncts.size(); i++) is.add(conjuncts[i]); if(is.check() == unsat){ std::cout << "inconsistent!\n"; std::vector is_assumps; is.aux_solver.get_proof().get_assumptions(is_assumps); std::cout << "core:\n"; for(unsigned i = 0; i < is_assumps.size(); i++) is_assumps[i].show(); } else { std::cout << "consistent!\n"; is.aux_solver.print("should_be_inconsistent.smt2"); } std::cout << "by_case_counter = " << by_case_counter << "\n"; throw "ack!"; #endif Pop(1); is.pop(1); delete core; timer_stop("InterpolateByCases"); throw ReallyBad(); } } Pop(1); prev_annot = node->Annotation.Formula; node->Annotation.UnionWith(old_annot); } if(proof_core) delete proof_core; // shouldn't happen proof_core = core; is.pop(1); timer_stop("InterpolateByCases"); } void RPFP::Generalize(Node *root, Node *node){ timer_start("Generalize"); aux_solver.push(); AddEdgeToSolver(node->Outgoing); expr fmla = GetAnnotation(node); std::vector conjuncts; CollectConjuncts(fmla,conjuncts,false); GreedyReduce(aux_solver,conjuncts); // try to remove conjuncts one at a tme aux_solver.pop(1); NegateLits(conjuncts); SetAnnotation(node,SimplifyOr(conjuncts)); timer_stop("Generalize"); } RPFP_caching::edge_solver &RPFP_caching::SolverForEdge(Edge *edge, bool models, bool axioms){ edge_solver &es = edge_solvers[edge]; uptr &p = es.slvr; if(!p.get()){ scoped_no_proof no_proofs_please(ctx.m()); // no proofs p.set(new solver(ctx,true,models)); // no models if(axioms){ RPFP::LogicSolver *ls = edge->owner->ls; const std::vector &axs = ls->get_axioms(); for(unsigned i = 0; i < axs.size(); i++) p.get()->add(axs[i]); } } return es; } // caching version of above void RPFP_caching::GeneralizeCache(Edge *edge){ timer_start("Generalize"); scoped_solver_for_edge ssfe(this,edge); Node *node = edge->Parent; std::vector assumps, core, conjuncts; AssertEdgeCache(edge,assumps); for(unsigned i = 0; i < edge->Children.size(); i++){ expr as = GetAnnotation(edge->Children[i]); std::vector clauses; if(!as.is_true()){ CollectConjuncts(as.arg(1),clauses); for(unsigned j = 0; j < clauses.size(); j++) GetAssumptionLits(as.arg(0) || clauses[j],assumps); } } expr fmla = GetAnnotation(node); std::vector lits; if(fmla.is_true()){ timer_stop("Generalize"); return; } assumps.push_back(fmla.arg(0).arg(0)); CollectConjuncts(!fmla.arg(1),lits); #if 0 for(unsigned i = 0; i < lits.size(); i++){ const expr &lit = lits[i]; if(lit.is_app() && lit.decl().get_decl_kind() == Equal){ lits[i] = ctx.make(Leq,lit.arg(0),lit.arg(1)); lits.push_back(ctx.make(Leq,lit.arg(1),lit.arg(0))); } } #endif hash_map lit_map; for(unsigned i = 0; i < lits.size(); i++) GetAssumptionLits(lits[i],core,&lit_map); GreedyReduceCache(assumps,core); for(unsigned i = 0; i < core.size(); i++) conjuncts.push_back(lit_map[core[i]]); NegateLits(conjuncts); SetAnnotation(node,SimplifyOr(conjuncts)); timer_stop("Generalize"); } // caching version of above bool RPFP_caching::PropagateCache(Edge *edge){ timer_start("PropagateCache"); scoped_solver_for_edge ssfe(this,edge); bool some = false; { std::vector candidates, skip; Node *node = edge->Parent; CollectConjuncts(node->Annotation.Formula,skip); for(unsigned i = 0; i < edge->Children.size(); i++){ Node *child = edge->Children[i]; if(child->map == node->map){ CollectConjuncts(child->Annotation.Formula,candidates); break; } } if(candidates.empty()) goto done; hash_set skip_set; std::copy(skip.begin(),skip.end(),std::inserter(skip_set,skip_set.begin())); std::vector new_candidates; for(unsigned i = 0; i < candidates.size(); i++) if(skip_set.find(candidates[i]) == skip_set.end()) new_candidates.push_back(candidates[i]); candidates.swap(new_candidates); if(candidates.empty()) goto done; std::vector assumps, core, conjuncts; AssertEdgeCache(edge,assumps); for(unsigned i = 0; i < edge->Children.size(); i++){ expr ass = GetAnnotation(edge->Children[i]); if(eq(ass,ctx.bool_val(true))) continue; std::vector clauses; CollectConjuncts(ass.arg(1),clauses); for(unsigned j = 0; j < clauses.size(); j++) GetAssumptionLits(ass.arg(0) || clauses[j],assumps); } for(unsigned i = 0; i < candidates.size(); i++){ unsigned old_size = assumps.size(); node->Annotation.Formula = candidates[i]; expr fmla = GetAnnotation(node); assumps.push_back(fmla.arg(0).arg(0)); GetAssumptionLits(!fmla.arg(1),assumps); std::vector full_core; check_result res = CheckCore(assumps,full_core); if(res == unsat) conjuncts.push_back(candidates[i]); assumps.resize(old_size); } if(conjuncts.empty()) goto done; SetAnnotation(node,SimplifyAnd(conjuncts)); some = true; } done: timer_stop("PropagateCache"); return some; } /** Push a scope. Assertions made after Push can be undone by Pop. */ void RPFP::Push() { stack.push_back(stack_entry()); slvr_push(); } /** Pop a scope (see Push). Note, you cannot pop axioms. */ void RPFP::Pop(int num_scopes) { slvr_pop(num_scopes); for (int i = 0; i < num_scopes; i++) { stack_entry &back = stack.back(); for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) (*it)->dual = expr(ctx,NULL); for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) (*it)->dual = expr(ctx,NULL); for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) (*it).first->constraints.pop_back(); stack.pop_back(); } } /** Erase the proof by performing a Pop, Push and re-assertion of all the popped constraints */ void RPFP::PopPush(){ slvr_pop(1); slvr_push(); stack_entry &back = stack.back(); for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) slvr_add((*it)->dual); for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) slvr_add((*it)->dual); for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) slvr_add((*it).second); } // This returns a new FuncDel with same sort as top-level function // of term t, but with numeric suffix appended to name. Z3User::FuncDecl Z3User::SuffixFuncDecl(Term t, int n) { std::string name = t.decl().name().str() + "_" + string_of_int(n); std::vector sorts; int nargs = t.num_args(); for(int i = 0; i < nargs; i++) sorts.push_back(t.arg(i).get_sort()); return ctx.function(name.c_str(), nargs, &sorts[0], t.get_sort()); } Z3User::FuncDecl Z3User::RenumberPred(const FuncDecl &f, int n) { std::string name = f.name().str(); name = name.substr(0,name.rfind('_')) + "_" + string_of_int(n); int arity = f.arity(); std::vector domain; for(int i = 0; i < arity; i++) domain.push_back(f.domain(i)); return ctx.function(name.c_str(), arity, &domain[0], f.range()); } Z3User::FuncDecl Z3User::NumberPred(const FuncDecl &f, int n) { std::string name = f.name().str(); name = name + "_" + string_of_int(n); int arity = f.arity(); std::vector domain; for(int i = 0; i < arity; i++) domain.push_back(f.domain(i)); return ctx.function(name.c_str(), arity, &domain[0], f.range()); } // Scan the clause body for occurrences of the predicate unknowns RPFP::Term RPFP::ScanBody(hash_map &memo, const Term &t, hash_map &pmap, std::vector &parms, std::vector &nodes) { if(memo.find(t) != memo.end()) return memo[t]; Term res(ctx); if (t.is_app()) { func_decl f = t.decl(); if(pmap.find(f) != pmap.end()){ nodes.push_back(pmap[f]); f = SuffixFuncDecl(t,parms.size()); parms.push_back(f); } int nargs = t.num_args(); std::vector args; for(int i = 0; i < nargs; i++) args.push_back(ScanBody(memo,t.arg(i),pmap,parms,nodes)); res = f(nargs,&args[0]); } else if (t.is_quantifier()) res = CloneQuantifier(t,ScanBody(memo,t.body(),pmap,parms,nodes)); else res = t; memo[t] = res; return res; } // return the func_del of an app if it is uninterpreted bool Z3User::get_relation(const Term &t, func_decl &R){ if(!t.is_app()) return false; R = t.decl(); return R.get_decl_kind() == Uninterpreted; } // return true if term is an individual variable // TODO: have to check that it is not a background symbol bool Z3User::is_variable(const Term &t){ if(!t.is_app()) return t.is_var(); return t.decl().get_decl_kind() == Uninterpreted && t.num_args() == 0; } RPFP::Term RPFP::RemoveLabelsRec(hash_map &memo, const Term &t, std::vector &lbls){ if(memo.find(t) != memo.end()) return memo[t]; Term res(ctx); if (t.is_app()){ func_decl f = t.decl(); std::vector names; bool pos; if(t.is_label(pos,names)){ res = RemoveLabelsRec(memo,t.arg(0),lbls); for(unsigned i = 0; i < names.size(); i++) lbls.push_back(label_struct(names[i],res,pos)); } else { int nargs = t.num_args(); std::vector args; for(int i = 0; i < nargs; i++) args.push_back(RemoveLabelsRec(memo,t.arg(i),lbls)); res = f(nargs,&args[0]); } } else if (t.is_quantifier()) res = CloneQuantifier(t,RemoveLabelsRec(memo,t.body(),lbls)); else res = t; memo[t] = res; return res; } RPFP::Term RPFP::RemoveLabels(const Term &t, std::vector &lbls){ hash_map memo ; return RemoveLabelsRec(memo,t,lbls); } RPFP::Term RPFP::SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t) { std::pair foo(t,expr(ctx)); std::pair::iterator, bool> bar = memo[level].insert(foo); Term &res = bar.first->second; if(!bar.second) return res; if (t.is_app()) { func_decl f = t.decl(); std::vector args; int nargs = t.num_args(); if(nargs == 0 && f.get_decl_kind() == Uninterpreted) ls->declare_constant(f); // keep track of background constants for(int i = 0; i < nargs; i++) args.push_back(SubstBoundRec(memo, subst, level, t.arg(i))); res = f(args.size(),&args[0]); } else if (t.is_quantifier()){ int bound = t.get_quantifier_num_bound(); std::vector pats; t.get_patterns(pats); for(unsigned i = 0; i < pats.size(); i++) pats[i] = SubstBoundRec(memo, subst, level + bound, pats[i]); res = clone_quantifier(t, SubstBoundRec(memo, subst, level + bound, t.body()), pats); } else if (t.is_var()) { int idx = t.get_index_value(); if(idx >= level && subst.find(idx-level) != subst.end()){ res = subst[idx-level]; } else res = t; } else res = t; return res; } RPFP::Term RPFP::SubstBound(hash_map &subst, const Term &t){ hash_map > memo; return SubstBoundRec(memo, subst, 0, t); } // Eliminate the deBruijn indices from level to level+num-1 Z3User::Term Z3User::DeleteBoundRec(hash_map > &memo, int level, int num, const Term &t) { std::pair foo(t,expr(ctx)); std::pair::iterator, bool> bar = memo[level].insert(foo); Term &res = bar.first->second; if(!bar.second) return res; if (t.is_app()) { func_decl f = t.decl(); std::vector args; int nargs = t.num_args(); for(int i = 0; i < nargs; i++) args.push_back(DeleteBoundRec(memo, level, num, t.arg(i))); res = f(args.size(),&args[0]); } else if (t.is_quantifier()){ int bound = t.get_quantifier_num_bound(); std::vector pats; t.get_patterns(pats); for(unsigned i = 0; i < pats.size(); i++) pats[i] = DeleteBoundRec(memo, level + bound, num, pats[i]); res = clone_quantifier(t, DeleteBoundRec(memo, level + bound, num, t.body()), pats); } else if (t.is_var()) { int idx = t.get_index_value(); if(idx >= level){ res = ctx.make_var(idx-num,t.get_sort()); } else res = t; } else res = t; return res; } Z3User::Term Z3User::DeleteBound(int level, int num, const Term &t){ hash_map > memo; return DeleteBoundRec(memo, level, num, t); } int Z3User::MaxIndex(hash_map &memo, const Term &t) { std::pair foo(t,-1); std::pair::iterator, bool> bar = memo.insert(foo); int &res = bar.first->second; if(!bar.second) return res; if (t.is_app()){ func_decl f = t.decl(); int nargs = t.num_args(); for(int i = 0; i < nargs; i++){ int m = MaxIndex(memo, t.arg(i)); if(m > res) res = m; } } else if (t.is_quantifier()){ int bound = t.get_quantifier_num_bound(); res = MaxIndex(memo,t.body()) - bound; } else if (t.is_var()) { res = t.get_index_value(); } return res; } bool Z3User::IsClosedFormula(const Term &t){ hash_map memo; return MaxIndex(memo,t) < 0; } /** Convert a collection of clauses to Nodes and Edges in the RPFP. Predicate unknowns are uninterpreted predicates not occurring in the background theory. Clauses are of the form B => P(t_1,...,t_k) where P is a predicate unknown and predicate unknowns occur only positivey in H and only under existential quantifiers in prenex form. Each predicate unknown maps to a node. Each clause maps to an edge. Let C be a clause B => P(t_1,...,t_k) where the sequence of predicate unknowns occurring in B (in order of occurrence) is P_1..P_n. The clause maps to a transformer T where: T.Relparams = P_1..P_n T.Indparams = x_1...x+k T.Formula = B /\ t_1 = x_1 /\ ... /\ t_k = x_k Throws exception bad_clause(msg,i) if a clause i is in the wrong form. */ static bool canonical_clause(const expr &clause){ if(clause.decl().get_decl_kind() != Implies) return false; expr arg = clause.arg(1); return arg.is_app() && (arg.decl().get_decl_kind() == False || arg.decl().get_decl_kind() == Uninterpreted); } #define USE_QE_LITE void RPFP::FromClauses(const std::vector &unskolemized_clauses, const std::vector *bounds){ hash_map pmap; func_decl fail_pred = ctx.fresh_func_decl("@Fail", ctx.bool_sort()); std::vector clauses(unskolemized_clauses); // first, skolemize the clauses #ifndef USE_QE_LITE for(unsigned i = 0; i < clauses.size(); i++){ expr &t = clauses[i]; if (t.is_quantifier() && t.is_quantifier_forall()) { int bound = t.get_quantifier_num_bound(); std::vector sorts; std::vector names; hash_map subst; for(int j = 0; j < bound; j++){ sort the_sort = t.get_quantifier_bound_sort(j); symbol name = t.get_quantifier_bound_name(j); expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); subst[bound-1-j] = skolem; } t = SubstBound(subst,t.body()); } } #else std::vector > substs(clauses.size()); #endif // create the nodes from the heads of the clauses for(unsigned i = 0; i < clauses.size(); i++){ Term &clause = clauses[i]; #ifdef USE_QE_LITE Term &t = clause; if (t.is_quantifier() && t.is_quantifier_forall()) { int bound = t.get_quantifier_num_bound(); std::vector sorts; std::vector names; for(int j = 0; j < bound; j++){ sort the_sort = t.get_quantifier_bound_sort(j); symbol name = t.get_quantifier_bound_name(j); expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); substs[i][bound-1-j] = skolem; } clause = t.body(); } #endif if(clause.is_app() && clause.decl().get_decl_kind() == Uninterpreted) clause = implies(ctx.bool_val(true),clause); if(!canonical_clause(clause)) clause = implies((!clause).simplify(),ctx.bool_val(false)); Term head = clause.arg(1); func_decl R(ctx); bool is_query = false; if (eq(head,ctx.bool_val(false))){ R = fail_pred; // R = ctx.constant("@Fail", ctx.bool_sort()).decl(); is_query = true; } else if(!get_relation(head,R)) throw bad_clause("rhs must be a predicate application",i); if(pmap.find(R) == pmap.end()){ // If the node doesn't exitst, create it. The Indparams // are arbitrary, but we use the rhs arguments if they // are variables for mnomonic value. hash_set seen; std::vector Indparams; for(unsigned j = 0; j < head.num_args(); j++){ Term arg = head.arg(j); if(!is_variable(arg) || seen.find(arg) != seen.end()){ std::string name = std::string("@a_") + string_of_int(j); arg = ctx.constant(name.c_str(),arg.get_sort()); } seen.insert(arg); Indparams.push_back(arg); } #ifdef USE_QE_LITE { hash_map > sb_memo; for(unsigned j = 0; j < Indparams.size(); j++) Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); } #endif Node *node = CreateNode(R(Indparams.size(),&Indparams[0])); //nodes.push_back(node); pmap[R] = node; if (is_query) node->Bound = CreateRelation(std::vector(), ctx.bool_val(false)); node->recursion_bound = bounds ? 0 : UINT_MAX; } } bool some_labels = false; // create the edges for(unsigned i = 0; i < clauses.size(); i++){ Term clause = clauses[i]; Term body = clause.arg(0); Term head = clause.arg(1); func_decl R(ctx); if (eq(head,ctx.bool_val(false))) R = fail_pred; //R = ctx.constant("@Fail", ctx.bool_sort()).decl(); else get_relation(head,R); Node *Parent = pmap[R]; std::vector Indparams; hash_set seen; for(unsigned j = 0; j < head.num_args(); j++){ Term arg = head.arg(j); if(!is_variable(arg) || seen.find(arg) != seen.end()){ std::string name = std::string("@a_") + string_of_int(j); Term var = ctx.constant(name.c_str(),arg.get_sort()); body = body && (arg == var); arg = var; } seen.insert(arg); Indparams.push_back(arg); } // We extract the relparams positionally std::vector Relparams; hash_map scan_memo; std::vector Children; body = ScanBody(scan_memo,body,pmap,Relparams,Children); Term labeled = body; std::vector lbls; // TODO: throw this away for now body = RemoveLabels(body,lbls); if(!eq(labeled,body)) some_labels = true; // remember if there are labels, as we then can't do qe_lite // body = IneqToEq(body); // UFO converts x=y to (x<=y & x >= y). Undo this. body = body.simplify(); #ifdef USE_QE_LITE std::set idxs; if(!some_labels){ // can't do qe_lite if we have to reconstruct labels for(unsigned j = 0; j < Indparams.size(); j++) if(Indparams[j].is_var()) idxs.insert(Indparams[j].get_index_value()); body = body.qe_lite(idxs,false); } hash_map > sb_memo; body = SubstBoundRec(sb_memo,substs[i],0,body); if(some_labels) labeled = SubstBoundRec(sb_memo,substs[i],0,labeled); for(unsigned j = 0; j < Indparams.size(); j++) Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); #endif // Create the edge Transformer T = CreateTransformer(Relparams,Indparams,body); Edge *edge = CreateEdge(Parent,T,Children); edge->labeled = labeled;; // remember for label extraction if(bounds) Parent->recursion_bound = std::max(Parent->recursion_bound,(*bounds)[i]); // edges.push_back(edge); } // undo hoisting of expressions out of loops RemoveDeadNodes(); Unhoist(); // FuseEdges(); } // The following mess is used to undo hoisting of expressions outside loops by compilers expr RPFP::UnhoistPullRec(hash_map & memo, const expr &w, hash_map & init_defs, hash_map & const_params, hash_map &const_params_inv, std::vector &new_params){ if(memo.find(w) != memo.end()) return memo[w]; expr res; if(init_defs.find(w) != init_defs.end()){ expr d = init_defs[w]; std::vector vars; hash_set get_vars_memo; GetVarsRec(get_vars_memo,d,vars); hash_map map; for(unsigned j = 0; j < vars.size(); j++){ expr x = vars[j]; map[x] = UnhoistPullRec(memo,x,init_defs,const_params,const_params_inv,new_params); } expr defn = SubstRec(map,d); res = defn; } else if(const_params_inv.find(w) == const_params_inv.end()){ std::string old_name = w.decl().name().str(); func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), w.get_sort()); expr y = fresh(); const_params[y] = w; const_params_inv[w] = y; new_params.push_back(y); res = y; } else res = const_params_inv[w]; memo[w] = res; return res; } void RPFP::AddParamsToTransformer(Transformer &trans, const std::vector ¶ms){ std::copy(params.begin(),params.end(),std::inserter(trans.IndParams,trans.IndParams.end())); } expr RPFP::AddParamsToApp(const expr &app, const func_decl &new_decl, const std::vector ¶ms){ int n = app.num_args(); std::vector args(n); for (int i = 0; i < n; i++) args[i] = app.arg(i); std::copy(params.begin(),params.end(),std::inserter(args,args.end())); return new_decl(args); } expr RPFP::GetRelRec(hash_set &memo, const expr &t, const func_decl &rel){ if(memo.find(t) != memo.end()) return expr(); memo.insert(t); if (t.is_app()) { func_decl f = t.decl(); if(f == rel) return t; int nargs = t.num_args(); for(int i = 0; i < nargs; i++){ expr res = GetRelRec(memo,t.arg(i),rel); if(!res.null()) return res; } } else if (t.is_quantifier()) return GetRelRec(memo,t.body(),rel); return expr(); } expr RPFP::GetRel(Edge *edge, int child_idx){ func_decl &rel = edge->F.RelParams[child_idx]; hash_set memo; return GetRelRec(memo,edge->F.Formula,rel); } void RPFP::GetDefsRec(const expr &cnst, hash_map &defs){ if(cnst.is_app()){ switch(cnst.decl().get_decl_kind()){ case And: { int n = cnst.num_args(); for(int i = 0; i < n; i++) GetDefsRec(cnst.arg(i),defs); break; } case Equal: { expr lhs = cnst.arg(0); expr rhs = cnst.arg(1); if(IsVar(lhs)) defs[lhs] = rhs; break; } default: break; } } } void RPFP::GetDefs(const expr &cnst, hash_map &defs){ // GetDefsRec(IneqToEq(cnst),defs); GetDefsRec(cnst,defs); } bool RPFP::IsVar(const expr &t){ return t.is_app() && t.num_args() == 0 && t.decl().get_decl_kind() == Uninterpreted; } void RPFP::GetVarsRec(hash_set &memo, const expr &t, std::vector &vars){ if(memo.find(t) != memo.end()) return; memo.insert(t); if (t.is_app()) { if(IsVar(t)){ vars.push_back(t); return; } int nargs = t.num_args(); for(int i = 0; i < nargs; i++){ GetVarsRec(memo,t.arg(i),vars); } } else if (t.is_quantifier()) GetVarsRec(memo,t.body(),vars); } void RPFP::AddParamsToNode(Node *node, const std::vector ¶ms){ int arity = node->Annotation.IndParams.size(); std::vector domain; for(int i = 0; i < arity; i++) domain.push_back(node->Annotation.IndParams[i].get_sort()); for(unsigned i = 0; i < params.size(); i++) domain.push_back(params[i].get_sort()); std::string old_name = node->Name.name().str(); func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), domain, ctx.bool_sort()); node->Name = fresh; AddParamsToTransformer(node->Annotation,params); AddParamsToTransformer(node->Bound,params); AddParamsToTransformer(node->Underapprox,params); } void RPFP::UnhoistLoop(Edge *loop_edge, Edge *init_edge){ loop_edge->F.Formula = IneqToEq(loop_edge->F.Formula); init_edge->F.Formula = IneqToEq(init_edge->F.Formula); expr pre = GetRel(loop_edge,0); if(pre.null()) return; // this means the loop got simplified away int nparams = loop_edge->F.IndParams.size(); hash_map const_params, const_params_inv; std::vector work_list; // find the parameters that are constant in the loop for(int i = 0; i < nparams; i++){ if(eq(pre.arg(i),loop_edge->F.IndParams[i])){ const_params[pre.arg(i)] = init_edge->F.IndParams[i]; const_params_inv[init_edge->F.IndParams[i]] = pre.arg(i); work_list.push_back(pre.arg(i)); } } // get the definitions in the initialization hash_map defs,memo,subst; GetDefs(init_edge->F.Formula,defs); // try to pull them inside the loop std::vector new_params; for(unsigned i = 0; i < work_list.size(); i++){ expr v = work_list[i]; expr w = const_params[v]; expr def = UnhoistPullRec(memo,w,defs,const_params,const_params_inv,new_params); if(!eq(def,v)) subst[v] = def; } // do the substitutions if(subst.empty()) return; subst[pre] = pre; // don't substitute inside the precondition itself loop_edge->F.Formula = SubstRec(subst,loop_edge->F.Formula); loop_edge->F.Formula = ElimIte(loop_edge->F.Formula); init_edge->F.Formula = ElimIte(init_edge->F.Formula); // add the new parameters if(new_params.empty()) return; Node *parent = loop_edge->Parent; AddParamsToNode(parent,new_params); AddParamsToTransformer(loop_edge->F,new_params); AddParamsToTransformer(init_edge->F,new_params); std::vector &incoming = parent->Incoming; for(unsigned i = 0; i < incoming.size(); i++){ Edge *in_edge = incoming[i]; std::vector &chs = in_edge->Children; for(unsigned j = 0; j < chs.size(); j++) if(chs[j] == parent){ expr lit = GetRel(in_edge,j); expr new_lit = AddParamsToApp(lit,parent->Name,new_params); func_decl fd = SuffixFuncDecl(new_lit,j); int nargs = new_lit.num_args(); std::vector args; for(int k = 0; k < nargs; k++) args.push_back(new_lit.arg(k)); new_lit = fd(nargs,&args[0]); in_edge->F.RelParams[j] = fd; hash_map map; map[lit] = new_lit; in_edge->F.Formula = SubstRec(map,in_edge->F.Formula); } } } void RPFP::Unhoist(){ hash_map > outgoing; for(unsigned i = 0; i < edges.size(); i++) outgoing[edges[i]->Parent].push_back(edges[i]); for(unsigned i = 0; i < nodes.size(); i++){ Node *node = nodes[i]; std::vector &outs = outgoing[node]; // if we're not a simple loop with one entry, give up if(outs.size() == 2){ for(int j = 0; j < 2; j++){ Edge *loop_edge = outs[j]; Edge *init_edge = outs[1-j]; if(loop_edge->Children.size() == 1 && loop_edge->Children[0] == loop_edge->Parent){ UnhoistLoop(loop_edge,init_edge); break; } } } } } void RPFP::FuseEdges(){ hash_map > outgoing; for(unsigned i = 0; i < edges.size(); i++){ outgoing[edges[i]->Parent].push_back(edges[i]); } hash_set edges_to_delete; for(unsigned i = 0; i < nodes.size(); i++){ Node *node = nodes[i]; std::vector &outs = outgoing[node]; if(outs.size() > 1 && outs.size() <= 16){ std::vector trs(outs.size()); for(unsigned j = 0; j < outs.size(); j++) trs[j] = &outs[j]->F; Transformer tr = Fuse(trs); std::vector chs; for(unsigned j = 0; j < outs.size(); j++) for(unsigned k = 0; k < outs[j]->Children.size(); k++) chs.push_back(outs[j]->Children[k]); CreateEdge(node,tr,chs); for(unsigned j = 0; j < outs.size(); j++) edges_to_delete.insert(outs[j]); } } std::vector new_edges; hash_set all_nodes; for(unsigned j = 0; j < edges.size(); j++){ if(edges_to_delete.find(edges[j]) == edges_to_delete.end()){ #if 0 if(all_nodes.find(edges[j]->Parent) != all_nodes.end()) throw "help!"; all_nodes.insert(edges[j]->Parent); #endif new_edges.push_back(edges[j]); } else delete edges[j]; } edges.swap(new_edges); } void RPFP::MarkLiveNodes(hash_map > &outgoing, hash_set &live_nodes, Node *node){ if(live_nodes.find(node) != live_nodes.end()) return; live_nodes.insert(node); std::vector &outs = outgoing[node]; for(unsigned i = 0; i < outs.size(); i++) for(unsigned j = 0; j < outs[i]->Children.size(); j++) MarkLiveNodes(outgoing, live_nodes,outs[i]->Children[j]); } void RPFP::RemoveDeadNodes(){ hash_map > outgoing; for(unsigned i = 0; i < edges.size(); i++) outgoing[edges[i]->Parent].push_back(edges[i]); hash_set live_nodes; for(unsigned i = 0; i < nodes.size(); i++) if(!nodes[i]->Bound.IsFull()) MarkLiveNodes(outgoing,live_nodes,nodes[i]); std::vector new_edges; for(unsigned j = 0; j < edges.size(); j++){ if(live_nodes.find(edges[j]->Parent) != live_nodes.end()) new_edges.push_back(edges[j]); else { Edge *edge = edges[j]; for(unsigned int i = 0; i < edge->Children.size(); i++){ std::vector &ic = edge->Children[i]->Incoming; for(std::vector::iterator it = ic.begin(), en = ic.end(); it != en; ++it){ if(*it == edge){ ic.erase(it); break; } } } delete edge; } } edges.swap(new_edges); std::vector new_nodes; for(unsigned j = 0; j < nodes.size(); j++){ if(live_nodes.find(nodes[j]) != live_nodes.end()) new_nodes.push_back(nodes[j]); else delete nodes[j]; } nodes.swap(new_nodes); } void RPFP::WriteSolution(std::ostream &s){ for(unsigned i = 0; i < nodes.size(); i++){ Node *node = nodes[i]; Term asgn = (node->Name)(node->Annotation.IndParams) == node->Annotation.Formula; s << asgn << std::endl; } } void RPFP::WriteEdgeVars(Edge *e, hash_map &memo, const Term &t, std::ostream &s) { std::pair foo(t,0); std::pair::iterator, bool> bar = memo.insert(foo); // int &res = bar.first->second; if(!bar.second) return; hash_map::iterator it = e->varMap.find(t); if (it != e->varMap.end()){ return; } if (t.is_app()) { func_decl f = t.decl(); // int idx; int nargs = t.num_args(); for(int i = 0; i < nargs; i++) WriteEdgeVars(e, memo, t.arg(i),s); if (nargs == 0 && f.get_decl_kind() == Uninterpreted && !ls->is_constant(f)){ Term rename = HideVariable(t,e->number); Term value = dualModel.eval(rename); s << " (= " << t << " " << value << ")\n"; } } else if (t.is_quantifier()) WriteEdgeVars(e,memo,t.body(),s); return; } void RPFP::WriteEdgeAssignment(std::ostream &s, Edge *e){ s << "(\n"; hash_map memo; WriteEdgeVars(e, memo, e->F.Formula ,s); s << ")\n"; } void RPFP::WriteCounterexample(std::ostream &s, Node *node){ for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ Node *child = node->Outgoing->Children[i]; if(!Empty(child)) WriteCounterexample(s,child); } s << "(" << node->number << " : " << EvalNode(node) << " <- "; for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ Node *child = node->Outgoing->Children[i]; if(!Empty(child)) s << " " << node->Outgoing->Children[i]->number; } s << ")" << std::endl; WriteEdgeAssignment(s,node->Outgoing); } RPFP::Term RPFP::ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants) { std::pair foo(t,expr(ctx)); std::pair::iterator, bool> bar = memo.insert(foo); Term &res = bar.first->second; if(!bar.second) return res; if (t.is_app()) { func_decl f = t.decl(); // int idx; std::vector args; int nargs = t.num_args(); for(int i = 0; i < nargs; i++) args.push_back(ToRuleRec(e, memo, t.arg(i),quants)); hash_map::iterator rit = e->relMap.find(f); if(rit != e->relMap.end()){ Node* child = e->Children[rit->second]; FuncDecl op = child->Name; res = op(args.size(),&args[0]); } else { res = f(args.size(),&args[0]); if(nargs == 0 && t.decl().get_decl_kind() == Uninterpreted) quants.push_back(t); } } else if (t.is_quantifier()) { Term body = ToRuleRec(e,memo,t.body(),quants); res = CloneQuantifier(t,body); } else res = t; return res; } void RPFP::ToClauses(std::vector &cnsts, FileFormat format){ cnsts.resize(edges.size()); for(unsigned i = 0; i < edges.size(); i++){ Edge *edge = edges[i]; SetEdgeMaps(edge); std::vector quants; hash_map memo; Term lhs = ToRuleRec(edge, memo, edge->F.Formula,quants); Term rhs = (edge->Parent->Name)(edge->F.IndParams.size(),&edge->F.IndParams[0]); for(unsigned j = 0; j < edge->F.IndParams.size(); j++) ToRuleRec(edge,memo,edge->F.IndParams[j],quants); // just to get quants Term cnst = implies(lhs,rhs); #if 0 for(unsigned i = 0; i < quants.size(); i++){ std::cout << expr(ctx,(Z3_ast)quants[i]) << " : " << sort(ctx,Z3_get_sort(ctx,(Z3_ast)quants[i])) << std::endl; } #endif if(format != DualityFormat) cnst= forall(quants,cnst); cnsts[i] = cnst; } // int num_rules = cnsts.size(); for(unsigned i = 0; i < nodes.size(); i++){ Node *node = nodes[i]; if(!node->Bound.IsFull()){ Term lhs = (node->Name)(node->Bound.IndParams) && !node->Bound.Formula; Term cnst = implies(lhs,ctx.bool_val(false)); if(format != DualityFormat){ std::vector quants; for(unsigned j = 0; j < node->Bound.IndParams.size(); j++) quants.push_back(node->Bound.IndParams[j]); if(format == HornFormat) cnst= exists(quants,!cnst); else cnst= forall(quants, cnst); } cnsts.push_back(cnst); } } } bool RPFP::proof_core_contains(const expr &e){ return proof_core->find(e) != proof_core->end(); } bool RPFP_caching::proof_core_contains(const expr &e){ std::vector foo; GetAssumptionLits(e,foo); for(unsigned i = 0; i < foo.size(); i++) if(proof_core->find(foo[i]) != proof_core->end()) return true; return false; } bool RPFP::EdgeUsedInProof(Edge *edge){ ComputeProofCore(); if(!edge->dual.null() && proof_core_contains(edge->dual)) return true; for(unsigned i = 0; i < edge->constraints.size(); i++) if(proof_core_contains(edge->constraints[i])) return true; return false; } RPFP::~RPFP(){ ClearProofCore(); for(unsigned i = 0; i < nodes.size(); i++) delete nodes[i]; for(unsigned i = 0; i < edges.size(); i++) delete edges[i]; } } #if 0 void show_ast(expr *a){ std::cout << *a; } #endif z3-4.4.0/src/tactic/0000755000175000017500000000000012540347414013703 5ustar michaelmichaelz3-4.4.0/src/tactic/replace_proof_converter.cpp0000644000175000017500000000456712540347414021332 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: replace_proof_converter.cpp Abstract: Proof converter that replaces asserted by sub-proof. Author: Nikolaj Bjorner (nbjorner) 2012-9-16 Revision History: --*/ #include "replace_proof_converter.h" #include "expr_functors.h" #include "ast_pp.h" #include "for_each_expr.h" /** \brief Replace expressions by other expressions. replace_map is caching, so inserting src |-> dst has no effect if src is a sub-expression of something that has already been visited. The assumption is that proof replacements are inserted into the replace_proof_converter in the order that they are introduced, so there are no such clashes. map_proc is used as expr_replacer behaves differently when proof mode is turned on. */ class replace_map : public map_proc { public: replace_map(ast_manager& m): map_proc(m) {} void insert(expr* src, expr* dst) { m_map.insert(src, dst, 0); } void operator()(var* v) { visit(v); } void operator()(app* a) { if (!get_expr(a)) { reconstruct(a); } } void operator()(quantifier* q) { visit(q); } void apply(expr_ref& e) { for_each_expr(*this, e); e = get_expr(e); } }; void replace_proof_converter::operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) { SASSERT(num_source == 1); replace_map replace(m); proof_ref p(m); expr_ref tmp(source[0], m), e(m), f(m); // apply the substitution to the prefix before inserting it. for (unsigned i = 0; i < m_proofs.size(); ++i) { p = m_proofs[i].get(); e = p; replace.apply(e); f = m.mk_asserted(m.get_fact(p)); replace.insert(f, e); TRACE("proof_converter", tout << f->get_id() << " " << mk_pp(f, m) << "\n|-> " << mk_pp(e, m) << "\n";); } replace.apply(tmp); TRACE("proof_converter", tout << mk_pp(source[0], m) << "\n"; tout << mk_pp(tmp.get(), m) << "\n";); result = to_app(tmp); } proof_converter * replace_proof_converter::translate(ast_translation & translator) { replace_proof_converter* rp = alloc(replace_proof_converter, m); for (unsigned i = 0; i < m_proofs.size(); ++i) { rp->insert(translator(m_proofs[i].get())); } return rp; } z3-4.4.0/src/tactic/equiv_proof_converter.cpp0000644000175000017500000000133612540347414021037 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: equiv_proof_converter.cpp Abstract: Proof converter that applies equivalence rule to leaves. Author: Nikolaj Bjorner (nbjorner) 2012-11-23 Revision History: --*/ #include "equiv_proof_converter.h" #include "ast_pp.h" #include "scoped_proof.h" void equiv_proof_converter::insert(expr* fml1, expr* fml2) { if (fml1 != fml2) { scoped_proof _sp(m); proof_ref p1(m), p2(m), p3(m); p1 = m.mk_asserted(fml1); p2 = m.mk_rewrite(fml1, fml2); p3 = m.mk_modus_ponens(p1, p2); TRACE("proof_converter", tout << mk_pp(p3.get(), m) << "\n";); SASSERT(m.has_fact(p3)); m_replace.insert(p3); } } z3-4.4.0/src/tactic/horn_subsume_model_converter.cpp0000644000175000017500000001420512540347414022371 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: horn_subsume_model_converter.cpp Abstract: Model converter for redundant Horn clauses. Author: Nikolaj Bjorner (nbjorner) 2012-9-16 Revision History: --*/ #include "horn_subsume_model_converter.h" #include "var_subst.h" #include "ast_pp.h" #include "model_smt2_pp.h" #include "bool_rewriter.h" #include "th_rewriter.h" #include "for_each_expr.h" #include "well_sorted.h" void horn_subsume_model_converter::insert(app* head, expr* body) { m_delay_head.push_back(head); m_delay_body.push_back(body); } void horn_subsume_model_converter::insert(app* head, unsigned sz, expr* const* body) { expr_ref b(m); bool_rewriter(m).mk_and(sz, body, b); insert(head, b.get()); } bool horn_subsume_model_converter::mk_horn( app* head, expr* body, func_decl_ref& pred, expr_ref& body_res) { expr_ref_vector conjs(m), subst(m); ptr_vector sorts2; var_subst vs(m, false); if (!is_uninterp(head)) { return false; } pred = head->get_decl(); unsigned arity = head->get_num_args(); expr_free_vars fv; fv(head); fv.accumulate(body); if (arity == 0 && fv.empty()) { body_res = body; return true; } fv.set_default_sort(m.mk_bool_sort()); svector names; for (unsigned i = 0; i < fv.size(); ++i) { names.push_back(symbol(i)); } names.reverse(); fv.reverse(); conjs.push_back(body); for (unsigned i = 0; i < arity; ++i) { expr* arg = head->get_arg(i); var_ref v(m); v = m.mk_var(fv.size()+i, m.get_sort(arg)); if (is_var(arg)) { unsigned w = to_var(arg)->get_idx(); if (w >= subst.size()) { subst.resize(w+1); } if (subst[w].get()) { conjs.push_back(m.mk_eq(v, subst[w].get())); } else { subst[w] = v; } } else { conjs.push_back(m.mk_eq(v, arg)); } } expr_ref body_expr(m); body_expr = m.mk_and(conjs.size(), conjs.c_ptr()); // substitute variables directly. if (!subst.empty()) { expr_ref tmp(body_expr); vs(tmp, subst.size(), subst.c_ptr(), body_expr); } if (fv.empty()) { SASSERT(subst.empty()); body_res = body_expr; } else { body_res = m.mk_exists(fv.size(), fv.c_ptr(), names.c_ptr(), body_expr.get()); m_rewrite(body_res); } TRACE("mc", tout << mk_pp(head, m) << " :- " << mk_pp(body, m) << "\n"; tout << pred->get_name() << " :- " << mk_pp(body_res.get(), m) << "\n";); return true; } bool horn_subsume_model_converter::mk_horn( expr* clause, func_decl_ref& pred, expr_ref& body) { // formula is closed. DEBUG_CODE(expr_free_vars fv; fv(clause); SASSERT(fv.empty());); while (is_quantifier(clause) && to_quantifier(clause)->is_forall()) { quantifier* q = to_quantifier(clause); clause = q->get_expr(); } expr* e1, *e2; if (m.is_implies(clause, e1, e2)) { if (!is_uninterp(e2)) { return false; } return mk_horn(to_app(e2), e1, pred, body); } else if (m.is_or(clause)) { // todo? return false; } else { return false; } } void horn_subsume_model_converter::add_default_proc::operator()(app* n) { // // predicates that have not been assigned values // in the Horn model are assumed false. // if (m.is_bool(n) && !m_md->has_interpretation(n->get_decl()) && (n->get_family_id() == null_family_id)) { TRACE("mc", tout << "adding: " << n->get_decl()->get_name() << "\n";); if (n->get_decl()->get_arity() == 0) { m_md->register_decl(n->get_decl(), m.mk_false()); } else { func_interp* fi = alloc(func_interp, m, n->get_decl()->get_arity()); fi->set_else(m.mk_false()); m_md->register_decl(n->get_decl(), fi); } } } void horn_subsume_model_converter::add_default_false_interpretation(expr* e, model_ref& md) { add_default_proc proc(m, md); for_each_expr(proc, e); } void horn_subsume_model_converter::operator()(model_ref& mr) { func_decl_ref pred(m); expr_ref body_res(m); for (unsigned i = 0; i < m_delay_head.size(); ++i) { VERIFY(mk_horn(m_delay_head[i].get(), m_delay_body[i].get(), pred, body_res)); insert(pred.get(), body_res.get()); } m_delay_head.reset(); m_delay_body.reset(); TRACE("mc", tout << m_funcs.size() << "\n"; model_smt2_pp(tout, m, *mr, 0);); for (unsigned i = m_funcs.size(); i > 0; ) { --i; func_decl* h = m_funcs[i].get(); expr_ref body(m_bodies[i].get(), m); unsigned arity = h->get_arity(); add_default_false_interpretation(body, mr); SASSERT(m.is_bool(body)); TRACE("mc", tout << "eval: " << h->get_name() << "\n" << mk_pp(body, m) << "\n";); expr_ref tmp(body); mr->eval(tmp, body); TRACE("mc", tout << "to:\n" << mk_pp(body, m) << "\n";); if (arity == 0) { expr* e = mr->get_const_interp(h); if (e) { body = m.mk_or(e, body); } m_rewrite(body); mr->register_decl(h, body); } else { func_interp* f = mr->get_func_interp(h); if (f) { expr* e = f->get_else(); body = m.mk_or(e, body); } else { f = alloc(func_interp, m, arity); mr->register_decl(h, f); } m_rewrite(body); f->set_else(body); } } } model_converter* horn_subsume_model_converter::translate(ast_translation & translator) { horn_subsume_model_converter* mc = alloc(horn_subsume_model_converter, translator.to()); for (unsigned i = 0; i < m_funcs.size(); ++i) { mc->insert(translator(m_funcs[i].get()), translator(m_bodies[i].get())); } return mc; } z3-4.4.0/src/tactic/fpa/0000755000175000017500000000000012540347414014451 5ustar michaelmichaelz3-4.4.0/src/tactic/fpa/fpa2bv_rewriter_prec.h0000644000175000017500000002356712540347414020753 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_rewriter.h Abstract: Rewriter for converting FPA to BV Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #ifndef _FPA2BV_REWRITER_H_ #define _FPA2BV_REWRITER_H_ #include"cooperate.h" #include"rewriter_def.h" #include"bv_decl_plugin.h" #include"fpa2bv_converter_prec.h" #include"tactic_exception.h" #include struct fpa2bv_rewriter_prec_cfg : public default_rewriter_cfg { ast_manager & m_manager; expr_ref_vector m_out; fpa2bv_converter_prec & m_conv; obj_map * cnst2prec_map; unsigned precision; unsigned long long m_max_memory; unsigned m_max_steps; ast_manager & m() const { return m_manager; } fpa2bv_rewriter_prec_cfg(ast_manager & m, fpa2bv_converter_prec & c, params_ref const & p): m_manager(m), m_out(m), m_conv(c) { updt_params(p); // We need to make sure that the mananger has the BV plugin loaded. symbol s_bv("bv"); if (!m_manager.has_plugin(s_bv)) m_manager.register_plugin(s_bv, alloc(bv_decl_plugin)); } ~fpa2bv_rewriter_prec_cfg() { } void cleanup_buffers() { m_out.finalize(); } unsigned get_precision(func_decl * f){ if(cnst2prec_map->contains(f)) return cnst2prec_map->find(f); else return precision; } void set_precision(unsigned p) { precision=p; } void set_mappings(obj_map * o2p) { this->cnst2prec_map=o2p; } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_max_steps = p.get_uint("max_steps", UINT_MAX); } bool max_steps_exceeded(unsigned num_steps) const { cooperate("fpa2bv"); return num_steps > m_max_steps; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { TRACE("fpa2bv_rw", tout << "APP: " << f->get_name() << std::endl; ); if (num == 0 && f->get_family_id() == null_family_id && m_conv.is_float(f->get_range())) { m_conv.mk_const(f, get_precision(f), result); return BR_DONE; } if (num == 0 && f->get_family_id() == null_family_id && m_conv.is_rm(f->get_range())) { m_conv.mk_rm_const(f, result); return BR_DONE; } if (m().is_eq(f)) { SASSERT(num == 2); //SASSERT(m().get_sort(args[0]) == m().get_sort(args[1])); sort * ds = f->get_domain()[0]; if (m_conv.is_float(ds)) { m_conv.mk_eq(args[0], args[1], result); return BR_DONE; } else if (m_conv.is_rm(ds)) { result = m().mk_eq(args[0], args[1]); return BR_DONE; } return BR_FAILED; } if (m().is_ite(f)) { SASSERT(num == 3); if (m_conv.is_float(args[1])) { m_conv.mk_ite(args[0], args[1], args[2], result); return BR_DONE; } return BR_FAILED; } expr_ref newAssertion(m_manager); if (m_conv.is_float_family(f)) { switch (f->get_decl_kind()) { case OP_FPA_RM_NEAREST_TIES_TO_AWAY: case OP_FPA_RM_NEAREST_TIES_TO_EVEN: case OP_FPA_RM_TOWARD_NEGATIVE: case OP_FPA_RM_TOWARD_POSITIVE: case OP_FPA_RM_TOWARD_ZERO: m_conv.mk_rounding_mode(f, result); return BR_DONE; case OP_FPA_NUM: m_conv.mk_numeral(f, num, args, result); return BR_DONE; case OP_FPA_PLUS_INF: m_conv.mk_pinf(f, result); return BR_DONE; case OP_FPA_MINUS_INF: m_conv.mk_ninf(f, result); return BR_DONE; case OP_FPA_PLUS_ZERO: m_conv.mk_pzero(f, result); return BR_DONE; case OP_FPA_MINUS_ZERO: m_conv.mk_nzero(f, result); return BR_DONE; case OP_FPA_NAN: m_conv.mk_nan(f, result); return BR_DONE; case OP_FPA_ADD: m_conv.mk_add(f,get_precision(f), num, args, result);return BR_DONE; case OP_FPA_SUB: m_conv.mk_sub(f, get_precision(f), num, args, result); return BR_DONE; case OP_FPA_NEG: m_conv.mk_uminus(f, get_precision(f), num, args, result); return BR_DONE; case OP_FPA_MUL: m_conv.mk_mul(f, get_precision(f), num, args, result); return BR_DONE; case OP_FPA_DIV: m_conv.mk_div(f, get_precision(f), num, args, result); return BR_DONE; case OP_FPA_REM: m_conv.mk_remainder(f, get_precision(f), num, args, result); return BR_DONE; case OP_FPA_ABS: m_conv.mk_abs(f, get_precision(f), num, args, result); return BR_DONE; case OP_FPA_MIN: m_conv.mk_min(f, get_precision(f), num, args, result); return BR_DONE; case OP_FPA_MAX: m_conv.mk_max(f, get_precision(f), num, args, result); return BR_DONE; case OP_FPA_FMA: m_conv.mk_fusedma(f, get_precision(f), num, args, result); return BR_DONE; case OP_FPA_SQRT: m_conv.mk_sqrt(f, get_precision(f), num, args, result); return BR_DONE; case OP_FPA_ROUND_TO_INTEGRAL: m_conv.mk_round_to_integral(f, get_precision(f), num, args, result); return BR_DONE; case OP_FPA_EQ: m_conv.mk_float_eq(f, num, args, result); return BR_DONE; case OP_FPA_LT: m_conv.mk_float_lt(f, num, args, result); return BR_DONE; case OP_FPA_GT: m_conv.mk_float_gt(f, num, args, result); return BR_DONE; case OP_FPA_LE: m_conv.mk_float_le(f, num, args, result); return BR_DONE; case OP_FPA_GE: m_conv.mk_float_ge(f, num, args, result); return BR_DONE; case OP_FPA_IS_ZERO: m_conv.mk_is_zero(f, num, args, result); return BR_DONE; case OP_FPA_IS_NAN: m_conv.mk_is_nan(f, num, args, result); return BR_DONE; case OP_FPA_IS_INF: m_conv.mk_is_inf(f, num, args, result); return BR_DONE; case OP_FPA_IS_NORMAL: m_conv.mk_is_normal(f, num, args, result); return BR_DONE; case OP_FPA_IS_SUBNORMAL: m_conv.mk_is_subnormal(f, num, args, result); return BR_DONE; case OP_FPA_IS_POSITIVE: m_conv.mk_is_positive(f, num, args, result); return BR_DONE; case OP_FPA_IS_NEGATIVE: m_conv.mk_is_negative(f, num, args, result); return BR_DONE; case OP_FPA_TO_FP: m_conv.mk_to_fp(f, num, args, result); return BR_DONE; case OP_FPA_TO_FP_UNSIGNED: m_conv.mk_to_fp_unsigned(f, num, args, result); return BR_DONE; case OP_FPA_FP: m_conv.mk_fp(f, num, args, result); return BR_DONE; case OP_FPA_TO_UBV: m_conv.mk_to_ubv(f, num, args, result); return BR_DONE; case OP_FPA_TO_SBV: m_conv.mk_to_sbv(f, num, args, result); return BR_DONE; case OP_FPA_TO_REAL: m_conv.mk_to_real(f, num, args, result); return BR_DONE; case OP_FPA_TO_IEEE_BV: m_conv.mk_to_ieee_bv(f, num, args, result); return BR_DONE; case OP_FPA_INTERNAL_BVWRAP: case OP_FPA_INTERNAL_BVUNWRAP: case OP_FPA_INTERNAL_TO_REAL_UNSPECIFIED: case OP_FPA_INTERNAL_TO_UBV_UNSPECIFIED: case OP_FPA_INTERNAL_TO_SBV_UNSPECIFIED: return BR_FAILED; default: TRACE("fpa2bv", tout << "unsupported operator: " << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << std::endl;); NOT_IMPLEMENTED_YET(); } } if (f->get_family_id() == null_family_id) { bool is_float_uf = m_conv.is_float(f->get_range()); unsigned i = 0; while (!is_float_uf && i < num) { is_float_uf = m_conv.is_float(f->get_domain()[i]); i++; } if (is_float_uf) { m_conv.mk_uninterpreted_function(f, num, args, result); return BR_DONE; } } return BR_FAILED; } bool pre_visit(expr * t) { TRACE("pre_visit_prec", tout << mk_ismt2_pp(t, m()) << std::endl;); if(t->get_kind() == AST_APP && is_app_of(t, to_app(t)->get_family_id(), OP_EQ)) { //Equation over non-boolean expressions, it should be of form constantI = subexprI app * a = to_app(t); if (a->get_num_args() == 2) { expr * a0 = a->get_arg(0); expr * a1 = a->get_arg(1); func_decl * cnst = 0; if (a0->get_kind() == AST_APP && cnst2prec_map->contains(to_app(a0)->get_decl())) cnst = to_app(a0)->get_decl(); else if (a1->get_kind() == AST_APP && cnst2prec_map->contains(to_app(a1)->get_decl())) cnst = to_app(a1)->get_decl(); if (cnst == 0) { // For all equalities that were in the original problem, we don't // have any precision tracking, so those simply get 100% precision. set_precision(100); } else set_precision(cnst2prec_map->find(cnst)); TRACE("pre_visit_prec", tout << "Precision = " << get_precision(NULL) << std::endl;); } } return true; } bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { return false; } bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { return false; } }; template class rewriter_tpl; struct fpa2bv_rewriter_prec : public rewriter_tpl { fpa2bv_rewriter_prec_cfg m_cfg; fpa2bv_rewriter_prec(ast_manager & m, fpa2bv_converter_prec & c, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, c, p) { } }; #endif z3-4.4.0/src/tactic/fpa/qffp_tactic.h0000644000175000017500000000165012540347414017107 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qffp_tactic.h Abstract: Tactic for QF_FP benchmarks. Author: Christoph (cwinter) 2012-01-16 Notes: --*/ #ifndef _QFFP_TACTIC_H_ #define _QFFP_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_qffp_tactic(ast_manager & m, params_ref const & p = params_ref()); tactic * mk_qffpbv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qffp", "(try to) solve goal using the tactic for QF_FP.", "mk_qffp_tactic(m, p)") ADD_TACTIC("qffpbv", "(try to) solve goal using the tactic for QF_FPBV (floats+bit-vectors).", "mk_qffpbv_tactic(m, p)") */ probe * mk_is_qffp_probe(); probe * mk_is_qffpbv_probe(); /* ADD_PROBE("is-qffp", "true if the goal is in QF_FP (floats).", "mk_is_qffp_probe()") ADD_PROBE("is-qffpbv", "true if the goal is in QF_FPBV (floats+bit-vectors).", "mk_is_qffpbv_probe()") */ #endif z3-4.4.0/src/tactic/fpa/fpa2bv_approx_tactic.h0000644000175000017500000000110212540347414020714 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_lazy_tactic.h Abstract: Tactic that converts floating points to bit-vectors lazily Author: Aleksander Zeljic 2012-11-15 Notes: --*/ #ifndef _FPA2BV_APPROX_TACTIC_ #define _FPA2BV_APPROX_TACTIC_ #include"params.h" class ast_manager; class tactic; tactic * mk_fpa2bv_approx_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("fpa2bv_approx", "An iterative approximation based bit-blasting decision procedure for FPA.", "mk_fpa2bv_approx_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/fpa/fpa2bv_converter_prec.cpp0000644000175000017500000017360112540347414021445 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_converter_prec.cpp Abstract: Conversion routines for Floating Point -> Bit-Vector Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #include"ast_smt2_pp.h" #include"well_sorted.h" #include #include"fpa2bv_converter_prec.h" #define BVULT(X,Y,R) { expr_ref bvult_eq(m), bvult_not(m); m_simp.mk_eq(X, Y, bvult_eq); m_simp.mk_not(bvult_eq, bvult_not); expr_ref t(m); t = m_bv_util.mk_ule(X,Y); m_simp.mk_and(t, bvult_not, R); } #define BVSLT(X,Y,R) { expr_ref bvslt_eq(m), bvslt_not(m); m_simp.mk_eq(X, Y, bvslt_eq); m_simp.mk_not(bvslt_eq, bvslt_not); expr_ref t(m); t = m_bv_util.mk_sle(X,Y); m_simp.mk_and(t, bvslt_not, R); } #define MIN_EBITS 3 #define MIN_SBITS 3 fpa2bv_converter_prec::fpa2bv_converter_prec(ast_manager & m, fpa_approximation_mode mode) : fpa2bv_converter(m), m_mode(mode) { } void fpa2bv_converter_prec::fix_bits(unsigned prec, expr_ref rounded, unsigned sbits, unsigned ebits)//expr_ref& fixed, { //AZ: TODO: revise! minimal number of legal bits is 3!!!! Remove magic numbers unsigned szeroes = (unsigned) ((sbits-2)*(MAX_PRECISION - prec +0.0) / MAX_PRECISION);//3 bits are minimum for the significand unsigned ezeroes = (unsigned) ((ebits - 2)*(MAX_PRECISION - prec + 0.0) / MAX_PRECISION);//2 bits are minimum for the exponent expr_ref fix_sig(m), fix_exp(m); expr * sgn, *sig, *expn; split_fp( rounded.get(), sgn,sig,expn); if(ezeroes>0) { fix_exp=m.mk_eq(m_bv_util.mk_extract(ebits-2, ebits - ezeroes -1, sig), m_bv_util.mk_numeral(0,ezeroes)); m_extra_assertions.push_back(fix_exp); SASSERT(is_well_sorted(m, fix_exp)); } if(szeroes>0) { fix_sig=m.mk_eq(m_bv_util.mk_extract(sbits-2, sbits - szeroes -1, sig), m_bv_util.mk_numeral(0,szeroes)); m_extra_assertions.push_back(fix_sig); SASSERT(is_well_sorted(m, fix_sig)); } } void fpa2bv_converter_prec::mk_const(func_decl * f, unsigned prec, expr_ref & result) { switch (m_mode) { case FPAA_SMALL_FLOATS: { if (m_const2bv.contains(f)) result = m_const2bv.find(f); else { if (prec == MAX_PRECISION) fpa2bv_converter::mk_const(f, result); else { unsigned ebits = fu().get_ebits(f->get_range()); unsigned sbits = fu().get_sbits(f->get_range()); double rel = prec/(double)MAX_PRECISION; unsigned new_ebits = (unsigned) (rel * (double)ebits); unsigned new_sbits = (unsigned) (rel * (double)sbits); if (new_ebits < MIN_EBITS) new_ebits = MIN_EBITS; if (new_sbits < MIN_SBITS) new_sbits = MIN_SBITS; sort_ref ns(m), fp_srt(m); ns = fu().mk_float_sort(new_ebits, new_sbits); app_ref small_const(m); small_const = m.mk_fresh_const("small_const", ns); fp_srt = fu().mk_float_sort(ebits, sbits); symbol name("asFloat"); sort_ref rm_sort(m); rm_sort = fu().mk_rm_sort(); //sort * domain[2] = { rm_sort, ns }; //parameter parameters[2] = { parameter(ebits), parameter(sbits) }; fpa2bv_converter::mk_const(small_const->get_decl(), result); m_const2bv.insert(f, result.get()); m.inc_ref(f); m.inc_ref(result.get()); #ifdef Z3DEBUG std::cout << f->get_name() << " := " << small_const->get_decl()->get_name() << " [" << new_sbits<<","<get_name() << " := " << mk_ismt2_pp(result, m) << std::endl;); } } break; } case FPAA_PRECISE: case FPAA_FIXBITS: fpa2bv_converter::mk_const(f, result); break; default: UNREACHABLE(); break; } } void fpa2bv_converter_prec::mk_small_op(func_decl * f, unsigned new_ebits, unsigned new_sbits, unsigned num, expr * const * args, func_decl_ref & small_op, expr_ref_vector & cast_args) { if (new_ebits < MIN_EBITS) new_ebits = MIN_EBITS; if (new_sbits < MIN_SBITS) new_sbits = MIN_SBITS; sort_ref new_srt(m), rm_srt(m); new_srt = fu().mk_float_sort(new_ebits, new_sbits); rm_srt = fu().mk_rm_sort(); sort_ref_vector new_domain(m); cast_args.reset(); for (unsigned i=0; i < num; i++) // Recreate the domain by replacing the full fpa sort with the smaller one. { sort * d_i = f->get_domain(i); expr * a_i = args[i]; if (fu().is_rm(d_i)) { new_domain.push_back(rm_srt); cast_args.push_back(a_i); } else if (fu().is_float(f->get_domain(i))) { sort * a_i_srt = to_app(a_i)->get_decl()->get_range(); new_domain.push_back(new_srt); // Cast the corresponding argument to the right fpa size if (is_app(a_i)) { if (a_i_srt == new_srt) cast_args.push_back(a_i); else { app_ref rtz(m); rtz = fu().mk_round_toward_zero(); expr_ref rm(m); fpa2bv_converter::mk_rounding_mode(rtz->get_decl(), rm); sort * d[2] = { rm_srt, a_i_srt }; symbol name("asFloat"); func_decl_ref fd(m); fd = m.mk_func_decl(name, 2, d, new_srt, func_decl_info(fu().get_family_id(), OP_FPA_TO_FP, new_srt->get_num_parameters(), new_srt->get_parameters())); expr_ref t(m); expr * args[2] = { rm, a_i }; fpa2bv_converter::mk_to_fp(fd, 2, args, t); cast_args.push_back(t); SASSERT(is_well_sorted(m, t)); } } else NOT_IMPLEMENTED_YET(); } else // just keep everything else cast_args.push_back(a_i); } parameter parameters[2] = { parameter(new_ebits), parameter(new_sbits) }; // May I reuse parts of the existing declaration? CMW: Sure. small_op = m.mk_func_decl(f->get_name(), num, new_domain.c_ptr(), new_srt, func_decl_info(fu().get_family_id(), f->get_decl_kind(), 2, parameters)); //std::cout<get_name()<<"["<get_range()); unsigned sbits = fu().get_sbits(f->get_range()); double rel = prec/(double)MAX_PRECISION; unsigned new_ebits = (unsigned) (rel * (double)ebits);// (unsigned) (MIN_EBITS + rel * (double)(ebits-MIN_EBITS)) unsigned new_sbits = (unsigned) (rel * (double)sbits);// (unsigned) (MIN_SBITS + rel * (double)(sbits-MIN_SBITS)) mk_small_op(f,new_ebits,new_sbits,num,args, small_op, cast_args); } void fpa2bv_converter_prec::mk_cast_small_to_big(func_decl * f, expr * arg, expr_ref & result) { unsigned ebits = fu().get_ebits(f->get_range()); unsigned sbits = fu().get_sbits(f->get_range()); app_ref rtz(m); rtz = fu().mk_round_toward_zero(); expr_ref rm(m); fpa2bv_converter::mk_rounding_mode(rtz->get_decl(), rm); sort_ref rm_srt(m); rm_srt = fu().mk_rm_sort(); sort * d[2] = { rm_srt, to_app(arg)->get_decl()->get_range() }; parameter parameters[2] = { parameter(ebits), parameter(sbits) }; symbol name("asFloat"); func_decl_ref cast_up(m); cast_up = m.mk_func_decl(name, 2, d, f->get_range(), func_decl_info(fu().get_family_id(), OP_FPA_TO_FP, f->get_range()->get_num_parameters(), parameters)); expr * args[2] = { rm, arg }; fpa2bv_converter::mk_to_fp(cast_up, 2, args, result); } void fpa2bv_converter_prec::mk_cast_small_to_big(unsigned sbits, unsigned ebits, expr * arg, expr_ref & result) { app_ref rtz(m); rtz = fu().mk_round_toward_zero(); expr_ref rm(m); fpa2bv_converter::mk_rounding_mode(rtz->get_decl(), rm); sort_ref rm_srt(m); rm_srt = fu().mk_rm_sort(); sort * d[2] = { rm_srt, to_app(arg)->get_decl()->get_range() }; parameter parameters[2] = { parameter(ebits), parameter(sbits) }; symbol name("asFloat"); func_decl_ref cast_up(m); sort_ref ns(m); ns = fu().mk_float_sort(ebits, sbits); cast_up = m.mk_func_decl(name, 2, d, ns, func_decl_info(fu().get_family_id(), OP_FPA_TO_FP, 2, parameters)); expr * args[2] = { rm, arg }; fpa2bv_converter::mk_to_fp(cast_up, 2, args, result); } void fpa2bv_converter_prec::match_sorts(expr * a, expr * b, expr_ref & n_a, expr_ref & n_b) { //Check if the sorts of lhs and rhs match, otherwise cast them to appropriate size? SASSERT(is_app_of(a, m_plugin->get_family_id(), OP_FPA_FP)); SASSERT(is_app_of(b, m_plugin->get_family_id(), OP_FPA_FP)); func_decl * a_decl = to_app(a)->get_decl(); func_decl * b_decl = to_app(b)->get_decl(); unsigned a_ebits = fu().get_ebits(a_decl->get_range()); unsigned a_sbits = fu().get_sbits(a_decl->get_range()); unsigned b_ebits = fu().get_ebits(b_decl->get_range()); unsigned b_sbits = fu().get_sbits(b_decl->get_range()); unsigned n_ebits, n_sbits; //if( (a_ebits == b_ebits) && (a_sbits == b_sbits)) //{//No need for adjustment n_a = a; n_b = b; //} //else //{ if ((a_ebits <= b_ebits) && (a_sbits<= b_sbits)) {//sort of b is wider than sort of a, we cast a to the sort of b. mk_cast_small_to_big(b_sbits,b_ebits,a,n_a); n_b = b; } else if ((a_ebits >= b_ebits) && (a_sbits >= b_sbits)) { n_a = a; mk_cast_small_to_big(a_sbits,a_ebits,b,n_b); } else { n_ebits = (a_ebits < b_ebits)? b_ebits:a_ebits; n_sbits = (a_sbits < b_sbits)? b_sbits:a_sbits; mk_cast_small_to_big(n_sbits,n_ebits,a,n_a); mk_cast_small_to_big(n_sbits,n_ebits,b,n_b); } //} } void fpa2bv_converter_prec::mk_eq(expr * a, expr * b, expr_ref & result) { // This is structural equality, not floating point equality. expr_ref na(m),nb(m); match_sorts(a,b,na,nb); fpa2bv_converter::mk_eq(na,nb,result); } void fpa2bv_converter_prec::mk_ite(expr * c, expr * t, expr * f, expr_ref & result) { expr_ref nt(m),nf(m); match_sorts(t,f,nt,nf); fpa2bv_converter::mk_ite(c,nt,nf,result); } void fpa2bv_converter_prec::mk_add(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { // AZ: Switch can be moved just before the call to the fix_bits method, everything else should be the same switch (m_mode) { case FPAA_PRECISE: fpa2bv_converter::mk_add(f,num,args,result); break; case FPAA_SMALL_FLOATS: { func_decl_ref small_fd(m); expr_ref_vector small_args(m); mk_small_op(f, prec, num, args, small_fd, small_args); //expr_ref small_op(m); fpa2bv_converter::mk_add(small_fd, num, small_args.c_ptr(), result); //mk_cast_small_to_big(f, small_op, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_small_float_add", tout << "small_fd: " << mk_ismt2_pp(small_fd, m) << std::endl << "result = " << mk_ismt2_pp(result, m) << std::endl; ); break; } case FPAA_FIXBITS: { if (MAX_PRECISION == prec) fpa2bv_converter::mk_add(f,num,args,result); else{ //Alternative encoding /*func_decl * nf = & func_decl(f->get_name(), f->get_arity(), f->get_domain(), f->get_range(), f->get_info());*/ SASSERT(num == 3); expr_ref rm(m), x(m), y(m); rm = args[0]; x = args[1]; y = args[2]; expr_ref nan(m), nzero(m), pzero(m); mk_nan(f, nan); mk_nzero(f, nzero); mk_pzero(f, pzero); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_neg(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_neg(m), y_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_neg(x, x_is_neg); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); mk_is_neg(y, y_is_neg); mk_is_inf(y, y_is_inf); dbg_decouple("fpa2bv_add_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_add_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_add_x_is_pos", x_is_pos); dbg_decouple("fpa2bv_add_x_is_neg", x_is_neg); dbg_decouple("fpa2bv_add_x_is_inf", x_is_inf); dbg_decouple("fpa2bv_add_y_is_nan", y_is_nan); dbg_decouple("fpa2bv_add_y_is_zero", y_is_zero); dbg_decouple("fpa2bv_add_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_add_y_is_neg", y_is_neg); dbg_decouple("fpa2bv_add_y_is_inf", y_is_inf); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); m_simp.mk_or(x_is_nan, y_is_nan, c1); v1 = nan; mk_is_inf(x, c2); expr_ref nx(m), ny(m), nx_xor_ny(m), inf_xor(m); mk_is_neg(x, nx); mk_is_neg(y, ny); m_simp.mk_xor(nx, ny, nx_xor_ny); m_simp.mk_and(y_is_inf, nx_xor_ny, inf_xor); mk_ite(inf_xor, nan, x, v2); mk_is_inf(y, c3); expr_ref xy_is_neg(m), v3_and(m); m_simp.mk_xor(x_is_neg, y_is_neg, xy_is_neg); m_simp.mk_and(x_is_inf, xy_is_neg, v3_and); mk_ite(v3_and, nan, y, v3); expr_ref rm_is_to_neg(m), signs_and(m), signs_xor(m), v4_and(m), rm_and_xor(m), neg_cond(m); m_simp.mk_and(x_is_zero, y_is_zero, c4); m_simp.mk_and(x_is_neg, y_is_neg, signs_and); m_simp.mk_xor(x_is_neg, y_is_neg, signs_xor); mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); m_simp.mk_and(rm_is_to_neg, signs_xor, rm_and_xor); m_simp.mk_or(signs_and, rm_and_xor, neg_cond); mk_ite(neg_cond, nzero, pzero, v4); m_simp.mk_and(x_is_neg, y_is_neg, v4_and); mk_ite(v4_and, x, v4, v4); c5 = x_is_zero; v5 = y; c6 = y_is_zero; v6 = x; // Actual addition. unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, false); unpack(y, b_sgn, b_sig, b_exp, b_lz, false); dbg_decouple("fpa2bv_add_unpack_a_sgn", a_sgn); dbg_decouple("fpa2bv_add_unpack_a_sig", a_sig); dbg_decouple("fpa2bv_add_unpack_a_exp", a_exp); dbg_decouple("fpa2bv_add_unpack_b_sgn", b_sgn); dbg_decouple("fpa2bv_add_unpack_b_sig", b_sig); dbg_decouple("fpa2bv_add_unpack_b_exp", b_exp); expr_ref swap_cond(m); swap_cond = m_bv_util.mk_sle(a_exp, b_exp); expr_ref c_sgn(m), c_sig(m), c_exp(m), d_sgn(m), d_sig(m), d_exp(m); m_simp.mk_ite(swap_cond, b_sgn, a_sgn, c_sgn); m_simp.mk_ite(swap_cond, b_sig, a_sig, c_sig); // has sbits m_simp.mk_ite(swap_cond, b_exp, a_exp, c_exp); // has ebits m_simp.mk_ite(swap_cond, a_sgn, b_sgn, d_sgn); m_simp.mk_ite(swap_cond, a_sig, b_sig, d_sig); // has sbits m_simp.mk_ite(swap_cond, a_exp, b_exp, d_exp); // has ebits expr_ref res_sgn(m), res_sig(m), res_exp(m); add_core(sbits, ebits, rm, c_sgn, c_sig, c_exp, d_sgn, d_sig, d_exp, res_sgn, res_sig, res_exp); //Add the fixed zeroes here...? expr_ref sbits_zero(m), ebits_zero(m); // std::cout<<"res_sgn: "<1) // //Exponent = 1 bit for bias, fixed bits, actual exponent bits // res_exp=m_bv_util.mk_concat(m_bv_util.mk_extract(ebits+1, ebits-1, res_exp), // m_bv_util.mk_concat(m_bv_util.mk_numeral(0,ezeroes), // m_bv_util.mk_extract(ebits - 2 - ezeroes, 0 ,res_exp))); // if(sones>1) // res_sig=m_bv_util.mk_concat(m_bv_util.mk_extract(sbits+3,sones+4,res_sig), // m_bv_util.mk_concat(m_bv_util.mk_numeral(0,sones), // m_bv_util.mk_extract(3,0,res_sig))); // // std::cout<<"res_sgn': "<get_range(), rm, res_sgn, res_sig, res_exp, rounded); mk_ite(is_zero_sig, zero_case, rounded, v7);*/ expr_ref rounded(m);//, fixed(m); round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); fix_bits(prec, rounded, sbits,ebits); mk_ite(is_zero_sig, zero_case, rounded, v7); // signed sones=((sbits-3)*(MAX_PRECISION - prec +0.0)/MAX_PRECISION);//3 bits are minimum for the significand // unsigned ezeroes=((ebits-2)*(MAX_PRECISION - prec+0.0)/MAX_PRECISION);//2 bits are minimum for the exponent // expr_ref fix_sig(m), fix_exp(m), fix_sgn(m), rnd_sig(m), rnd_exp(m), rnd_sgn(m), rnd_lz(m); // expr * sgn, *sig, *expn; // split( rounded.get(), sgn,sig,expn); // // // if(ezeroes>1) // //Exponent = 1 bit for bias, fixed bits, actual exponent bits // fix_exp=m_bv_util.mk_concat(m_bv_util.mk_extract(ebits-1, ebits-1, expn), // m_bv_util.mk_concat(m_bv_util.mk_numeral(0,ezeroes), // m_bv_util.mk_extract(ebits - 2 - ezeroes, 0 , expn))); // if(sones>1) // fix_sig=m_bv_util.mk_concat(m_bv_util.mk_extract(sbits-2,sones,sig), // m_bv_util.mk_numeral(0,sones)); // // mk_triple(sgn, fix_sig, fix_exp, fixed); // SASSERT(is_well_sorted(m, fixed)); //mk_ite(is_zero_sig, zero_case, rounded, v7); mk_ite(c6, v6, v7, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_add", tout << "ADD = " << mk_ismt2_pp(result, m) << std::endl; ); } break; } default: UNREACHABLE(); break; } } void fpa2bv_converter_prec::mk_sub(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); expr_ref t(m); fpa2bv_converter::mk_neg(f, 1, &args[2], t); expr * nargs[3] = { args[0], args[1], t }; switch (m_mode) { case FPAA_PRECISE: fpa2bv_converter::mk_add(f, 3, nargs, result); break; case FPAA_FIXBITS: // Call the add with prec case FPAA_SMALL_FLOATS: fpa2bv_converter_prec::mk_add(f, prec, 3, nargs, result); break; default: UNREACHABLE(); break; } } void fpa2bv_converter_prec::mk_uminus(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { switch (m_mode) { case FPAA_PRECISE: case FPAA_SMALL_FLOATS: case FPAA_FIXBITS: // for now, encode fully. fpa2bv_converter::mk_neg(f,num,args,result); break; default: UNREACHABLE(); break; } } void fpa2bv_converter_prec::mk_mul(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { switch (m_mode) { case FPAA_PRECISE: fpa2bv_converter::mk_mul(f,num,args,result); break; case FPAA_SMALL_FLOATS: { func_decl_ref small_fd(m); expr_ref_vector small_args(m); mk_small_op(f, prec, num, args, small_fd, small_args); expr_ref small_op(m); fpa2bv_converter::mk_mul(small_fd, num, small_args.c_ptr(), small_op); mk_cast_small_to_big(f, small_op, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_small_float_mul", tout << mk_ismt2_pp(result, m) << std::endl; ); break; } case FPAA_FIXBITS: // for now, encode fully. { SASSERT(num == 3); expr_ref rm(m), x(m), y(m); rm = args[0]; x = args[1]; y = args[2]; expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(f, nan); mk_nzero(f, nzero); mk_pzero(f, pzero); mk_ninf(f, ninf); mk_pinf(f, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); mk_is_inf(y, y_is_inf); dbg_decouple("fpa2bv_mul_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_mul_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_mul_x_is_pos", x_is_pos); dbg_decouple("fpa2bv_mul_x_is_inf", x_is_inf); dbg_decouple("fpa2bv_mul_y_is_nan", y_is_nan); dbg_decouple("fpa2bv_mul_y_is_zero", y_is_zero); dbg_decouple("fpa2bv_mul_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_mul_y_is_inf", y_is_inf); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); // (x is NaN) || (y is NaN) -> NaN m_simp.mk_or(x_is_nan, y_is_nan, c1); v1 = nan; // (x is +oo) -> if (y is 0) then NaN else inf with y's sign. mk_is_pinf(x, c2); expr_ref y_sgn_inf(m); mk_ite(y_is_pos, pinf, ninf, y_sgn_inf); mk_ite(y_is_zero, nan, y_sgn_inf, v2); // (y is +oo) -> if (x is 0) then NaN else inf with x's sign. mk_is_pinf(y, c3); expr_ref x_sgn_inf(m); mk_ite(x_is_pos, pinf, ninf, x_sgn_inf); mk_ite(x_is_zero, nan, x_sgn_inf, v3); // (x is -oo) -> if (y is 0) then NaN else inf with -y's sign. mk_is_ninf(x, c4); expr_ref neg_y_sgn_inf(m); mk_ite(y_is_pos, ninf, pinf, neg_y_sgn_inf); mk_ite(y_is_zero, nan, neg_y_sgn_inf, v4); // (y is -oo) -> if (x is 0) then NaN else inf with -x's sign. mk_is_ninf(y, c5); expr_ref neg_x_sgn_inf(m); mk_ite(x_is_pos, ninf, pinf, neg_x_sgn_inf); mk_ite(x_is_zero, nan, neg_x_sgn_inf, v5); // (x is 0) || (y is 0) -> x but with sign = x.sign ^ y.sign m_simp.mk_or(x_is_zero, y_is_zero, c6); expr_ref sign_xor(m); m_simp.mk_xor(x_is_pos, y_is_pos, sign_xor); mk_ite(sign_xor, nzero, pzero, v6); // else comes the actual multiplication. unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); SASSERT(ebits <= sbits); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); unpack(y, b_sgn, b_sig, b_exp, b_lz, true); dbg_decouple("fpa2bv_mul_a_sig", a_sig); dbg_decouple("fpa2bv_mul_a_exp", a_exp); dbg_decouple("fpa2bv_mul_b_sig", b_sig); dbg_decouple("fpa2bv_mul_b_exp", b_exp); expr_ref a_lz_ext(m), b_lz_ext(m); a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); dbg_decouple("fpa2bv_mul_lz_a", a_lz); dbg_decouple("fpa2bv_mul_lz_b", b_lz); expr_ref a_sig_ext(m), b_sig_ext(m); a_sig_ext = m_bv_util.mk_zero_extend(sbits, a_sig); b_sig_ext = m_bv_util.mk_zero_extend(sbits, b_sig); expr_ref a_exp_ext(m), b_exp_ext(m); a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); expr_ref res_sgn(m), res_sig(m), res_exp(m); expr * signs[2] = { a_sgn, b_sgn }; res_sgn = m_bv_util.mk_bv_xor(2, signs); dbg_decouple("fpa2bv_mul_res_sgn", res_sgn); res_exp = m_bv_util.mk_bv_add( m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); expr_ref product(m); product = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); dbg_decouple("fpa2bv_mul_product", product); SASSERT(m_bv_util.get_bv_size(product) == 2*sbits); expr_ref h_p(m), l_p(m), rbits(m); h_p = m_bv_util.mk_extract(2*sbits-1, sbits, product); l_p = m_bv_util.mk_extract(sbits-1, 0, product); if (sbits >= 4) { expr_ref sticky(m); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(sbits-4, 0, product)); rbits = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits-1, sbits-3, product), sticky); } else rbits = m_bv_util.mk_concat(l_p, m_bv_util.mk_numeral(0, 4 - sbits)); SASSERT(m_bv_util.get_bv_size(rbits) == 4); res_sig = m_bv_util.mk_concat(h_p, rbits); //expr_ref rounded(m); round(f->get_range(), rm, res_sgn, res_sig, res_exp, v7); //AZ:the only difference to the original fix_bits(prec, v7, sbits, ebits); // And finally, we tie them together. mk_ite(c6, v6, v7, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_mul", tout << "MUL = " << mk_ismt2_pp(result, m) << std::endl; ); } break; default: UNREACHABLE(); break; } } void fpa2bv_converter_prec::mk_div(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { switch (m_mode) { case FPAA_SMALL_FLOATS: { func_decl_ref small_fd(m); expr_ref_vector small_args(m); mk_small_op(f, prec, num, args, small_fd, small_args); expr_ref small_op(m); fpa2bv_converter::mk_div(small_fd, num, small_args.c_ptr(), small_op); mk_cast_small_to_big(f, small_op, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_small_float_div", tout << mk_ismt2_pp(result, m) << std::endl; ); break; } case FPAA_PRECISE: case FPAA_FIXBITS: // for now, encode fully. fpa2bv_converter::mk_div(f,num,args,result); break; { SASSERT(num == 3); expr_ref rm(m), x(m), y(m); rm = args[0]; x = args[1]; y = args[2]; expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(f, nan); mk_nzero(f, nzero); mk_pzero(f, pzero); mk_ninf(f, ninf); mk_pinf(f, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); mk_is_inf(y, y_is_inf); dbg_decouple("fpa2bv_div_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_div_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_div_x_is_pos", x_is_pos); dbg_decouple("fpa2bv_div_x_is_inf", x_is_inf); dbg_decouple("fpa2bv_div_y_is_nan", y_is_nan); dbg_decouple("fpa2bv_div_y_is_zero", y_is_zero); dbg_decouple("fpa2bv_div_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_div_y_is_inf", y_is_inf); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m), c7(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m), v8(m); // (x is NaN) || (y is NaN) -> NaN m_simp.mk_or(x_is_nan, y_is_nan, c1); v1 = nan; // (x is +oo) -> if (y is oo) then NaN else inf with y's sign. mk_is_pinf(x, c2); expr_ref y_sgn_inf(m); mk_ite(y_is_pos, pinf, ninf, y_sgn_inf); mk_ite(y_is_inf, nan, y_sgn_inf, v2); // (y is +oo) -> if (x is oo) then NaN else 0 with sign x.sgn ^ y.sgn mk_is_pinf(y, c3); expr_ref xy_zero(m), signs_xor(m); m_simp.mk_xor(x_is_pos, y_is_pos, signs_xor); mk_ite(signs_xor, nzero, pzero, xy_zero); mk_ite(x_is_inf, nan, xy_zero, v3); // (x is -oo) -> if (y is oo) then NaN else inf with -y's sign. mk_is_ninf(x, c4); expr_ref neg_y_sgn_inf(m); mk_ite(y_is_pos, ninf, pinf, neg_y_sgn_inf); mk_ite(y_is_inf, nan, neg_y_sgn_inf, v4); // (y is -oo) -> if (x is oo) then NaN else 0 with sign x.sgn ^ y.sgn mk_is_ninf(y, c5); mk_ite(x_is_inf, nan, xy_zero, v5); // (y is 0) -> if (x is 0) then NaN else inf with xor sign. c6 = y_is_zero; expr_ref sgn_inf(m); mk_ite(signs_xor, ninf, pinf, sgn_inf); mk_ite(x_is_zero, nan, sgn_inf, v6); // (x is 0) -> result is zero with sgn = x.sgn^y.sgn // This is a special case to avoid problems with the unpacking of zero. c7 = x_is_zero; mk_ite(signs_xor, nzero, pzero, v7); // else comes the actual division. unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); SASSERT(ebits <= sbits); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); unpack(y, b_sgn, b_sig, b_exp, b_lz, true); unsigned extra_bits = sbits+2; expr_ref a_sig_ext(m), b_sig_ext(m); a_sig_ext = m_bv_util.mk_concat(a_sig, m_bv_util.mk_numeral(0, sbits + extra_bits)); b_sig_ext = m_bv_util.mk_zero_extend(sbits + extra_bits, b_sig); expr_ref a_exp_ext(m), b_exp_ext(m); a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); expr_ref res_sgn(m), res_sig(m), res_exp(m); expr * signs[2] = { a_sgn, b_sgn }; res_sgn = m_bv_util.mk_bv_xor(2, signs); expr_ref a_lz_ext(m), b_lz_ext(m); a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); res_exp = m_bv_util.mk_bv_sub( m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); expr_ref quotient(m); quotient = m.mk_app(m_bv_util.get_fid(), OP_BUDIV, a_sig_ext, b_sig_ext); dbg_decouple("fpa2bv_div_quotient", quotient); SASSERT(m_bv_util.get_bv_size(quotient) == (sbits + sbits + extra_bits)); expr_ref sticky(m); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(extra_bits-2, 0, quotient)); res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(extra_bits+sbits+1, extra_bits-1, quotient), sticky); SASSERT(m_bv_util.get_bv_size(res_sig) == (sbits + 4)); //expr_ref rounded(m);//AZ round(f->get_range(), rm, res_sgn, res_sig, res_exp, v8); fix_bits(prec, v8,sbits,ebits);//AZ // And finally, we tie them together. mk_ite(c7, v7, v8, result); mk_ite(c6, v6, result, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_div", tout << "DIV = " << mk_ismt2_pp(result, m) << std::endl; ); } break; default: UNREACHABLE(); break; } } void fpa2bv_converter_prec::mk_remainder(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { switch (m_mode) { case FPAA_SMALL_FLOATS: { func_decl_ref small_fd(m); expr_ref_vector small_args(m); mk_small_op(f, prec, num, args, small_fd, small_args); expr_ref small_op(m); fpa2bv_converter::mk_rem(small_fd, num, small_args.c_ptr(), small_op); mk_cast_small_to_big(f, small_op, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_small_float_remainder", tout << mk_ismt2_pp(result, m) << std::endl; ); break; } case FPAA_PRECISE: fpa2bv_converter::mk_rem(f,num,args,result); break; case FPAA_FIXBITS: // for now, encode fully. { SASSERT(num == 2); // Remainder is always exact, so there is no rounding mode. expr_ref x(m), y(m); x = args[0]; y = args[1]; expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(f, nan); mk_nzero(f, nzero); mk_pzero(f, pzero); mk_ninf(f, ninf); mk_pinf(f, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); mk_is_inf(y, y_is_inf); dbg_decouple("fpa2bv_rem_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_rem_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_rem_x_is_pos", x_is_pos); dbg_decouple("fpa2bv_rem_x_is_inf", x_is_inf); dbg_decouple("fpa2bv_rem_y_is_nan", y_is_nan); dbg_decouple("fpa2bv_rem_y_is_zero", y_is_zero); dbg_decouple("fpa2bv_rem_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_rem_y_is_inf", y_is_inf); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); // (x is NaN) || (y is NaN) -> NaN m_simp.mk_or(x_is_nan, y_is_nan, c1); v1 = nan; // (x is +-oo) -> NaN c2 = x_is_inf; v2 = nan; // (y is +-oo) -> x c3 = y_is_inf; v3 = x; // (x is 0) -> x c4 = x_is_zero; v4 = pzero; // (y is 0) -> NaN. c5 = y_is_zero; v5 = nan; // else the actual remainder. unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); expr_ref b_sgn(m), b_sig(m), b_exp(m), b_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); unpack(y, b_sgn, b_sig, b_exp, b_lz, true); BVSLT(a_exp, b_exp, c6); v6 = x; // max. exponent difference is (2^ebits) - 3 const mpz & two_to_ebits = fu().fm().m_powers2(ebits); mpz max_exp_diff; m_mpz_manager.sub(two_to_ebits, 3, max_exp_diff); SASSERT(m_mpz_manager.is_int64(max_exp_diff)); SASSERT(m_mpz_manager.get_uint64(max_exp_diff) <= UINT_MAX); unsigned int max_exp_diff_ui = (unsigned int)m_mpz_manager.get_uint64(max_exp_diff); m_mpz_manager.del(max_exp_diff); expr_ref exp_diff(m); exp_diff = m_bv_util.mk_bv_sub(a_exp, b_exp); dbg_decouple("fpa2bv_rem_exp_diff", exp_diff); // CMW: This creates _huge_ bit-vectors, which is potentially sub-optimal, // but calculating this via rem = x - y * nearest(x/y) creates huge circuits. expr_ref huge_sig(m), shifted_sig(m), huge_rem(m); huge_sig = m_bv_util.mk_zero_extend(max_exp_diff_ui, a_sig); shifted_sig = m_bv_util.mk_bv_shl(huge_sig, m_bv_util.mk_zero_extend(max_exp_diff_ui + sbits - ebits, exp_diff)); huge_rem = m_bv_util.mk_bv_urem(shifted_sig, m_bv_util.mk_zero_extend(max_exp_diff_ui, b_sig)); dbg_decouple("fpa2bv_rem_huge_rem", huge_rem); expr_ref res_sgn(m), res_sig(m), res_exp(m); res_sgn = a_sgn; res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits, 0, huge_rem), m_bv_util.mk_numeral(0, 3)); res_exp = m_bv_util.mk_sign_extend(2, b_exp); expr_ref rm(m); rm = m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3); //expr_ref rounded(m); round(f->get_range(), rm, res_sgn, res_sig, res_exp, v7); fix_bits(prec, v7,sbits, ebits); // And finally, we tie them together. mk_ite(c6, v6, v7, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_rem", tout << "REM = " << mk_ismt2_pp(result, m) << std::endl; ); } break; default: UNREACHABLE(); break; } } void fpa2bv_converter_prec::mk_abs(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { //This doesn't need approximation in it's current form switch (m_mode) { case FPAA_PRECISE: case FPAA_SMALL_FLOATS: case FPAA_FIXBITS: // for now, encode fully. fpa2bv_converter::mk_abs(f,num,args,result); break; default: UNREACHABLE(); break; } } void fpa2bv_converter_prec::mk_min(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { switch (m_mode) { case FPAA_PRECISE: case FPAA_SMALL_FLOATS: case FPAA_FIXBITS: // for now, encode fully. fpa2bv_converter::mk_min(f,num,args,result); break; default: UNREACHABLE(); break; } } void fpa2bv_converter_prec::mk_max(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { switch (m_mode) { case FPAA_PRECISE: case FPAA_SMALL_FLOATS: case FPAA_FIXBITS: // for now, encode fully. fpa2bv_converter::mk_max(f,num,args,result); break; default: UNREACHABLE(); break; } } void fpa2bv_converter_prec::mk_fusedma(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { switch (m_mode) { case FPAA_SMALL_FLOATS: { func_decl_ref small_fd(m); expr_ref_vector small_args(m); mk_small_op(f, prec, num, args, small_fd, small_args); expr_ref small_op(m); fpa2bv_converter::mk_fma(small_fd, num, small_args.c_ptr(), small_op); mk_cast_small_to_big(f, small_op, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_small_float_fma", tout << mk_ismt2_pp(result, m) << std::endl; ); break; } case FPAA_PRECISE: fpa2bv_converter::mk_fma(f,num,args,result); break; case FPAA_FIXBITS: { SASSERT(num == 4); // fusedma means (x * y) + z expr_ref rm(m), x(m), y(m), z(m); rm = args[0]; x = args[1]; y = args[2]; z = args[3]; expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(f, nan); mk_nzero(f, nzero); mk_pzero(f, pzero); mk_ninf(f, ninf); mk_pinf(f, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_neg(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_neg(m), y_is_inf(m); expr_ref z_is_nan(m), z_is_zero(m), z_is_pos(m), z_is_neg(m), z_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_neg(x, x_is_neg); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); mk_is_neg(y, y_is_neg); mk_is_inf(y, y_is_inf); mk_is_nan(z, z_is_nan); mk_is_zero(z, z_is_zero); mk_is_pos(z, z_is_pos); mk_is_neg(z, z_is_neg); mk_is_inf(z, z_is_inf); expr_ref rm_is_to_neg(m); mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); dbg_decouple("fpa2bv_fma_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_fma_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_fma_x_is_pos", x_is_pos); dbg_decouple("fpa2bv_fma_x_is_inf", x_is_inf); dbg_decouple("fpa2bv_fma_y_is_nan", y_is_nan); dbg_decouple("fpa2bv_fma_y_is_zero", y_is_zero); dbg_decouple("fpa2bv_fma_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_fma_y_is_inf", y_is_inf); dbg_decouple("fpa2bv_fma_z_is_nan", z_is_nan); dbg_decouple("fpa2bv_fma_z_is_zero", z_is_zero); dbg_decouple("fpa2bv_fma_z_is_pos", z_is_pos); dbg_decouple("fpa2bv_fma_z_is_inf", z_is_inf); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m), c7(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m), v8(m); expr_ref inf_xor(m), inf_cond(m); m_simp.mk_xor(x_is_neg, y_is_neg, inf_xor); m_simp.mk_xor(inf_xor, z_is_neg, inf_xor); m_simp.mk_and(z_is_inf, inf_xor, inf_cond); // (x is NaN) || (y is NaN) || (z is Nan) -> NaN m_simp.mk_or(x_is_nan, y_is_nan, z_is_nan, c1); v1 = nan; // (x is +oo) -> if (y is 0) then NaN else inf with y's sign. mk_is_pinf(x, c2); expr_ref y_sgn_inf(m), inf_or(m); mk_ite(y_is_pos, pinf, ninf, y_sgn_inf); m_simp.mk_or(y_is_zero, inf_cond, inf_or); mk_ite(inf_or, nan, y_sgn_inf, v2); // (y is +oo) -> if (x is 0) then NaN else inf with x's sign. mk_is_pinf(y, c3); expr_ref x_sgn_inf(m); mk_ite(x_is_pos, pinf, ninf, x_sgn_inf); m_simp.mk_or(x_is_zero, inf_cond, inf_or); mk_ite(inf_or, nan, x_sgn_inf, v3); // (x is -oo) -> if (y is 0) then NaN else inf with -y's sign. mk_is_ninf(x, c4); expr_ref neg_y_sgn_inf(m); mk_ite(y_is_pos, ninf, pinf, neg_y_sgn_inf); m_simp.mk_or(y_is_zero, inf_cond, inf_or); mk_ite(inf_or, nan, neg_y_sgn_inf, v4); // (y is -oo) -> if (x is 0) then NaN else inf with -x's sign. mk_is_ninf(y, c5); expr_ref neg_x_sgn_inf(m); mk_ite(x_is_pos, ninf, pinf, neg_x_sgn_inf); m_simp.mk_or(x_is_zero, inf_cond, inf_or); mk_ite(inf_or, nan, neg_x_sgn_inf, v5); // z is +-INF -> z. mk_is_inf(z, c6); v6 = z; // (x is 0) || (y is 0) -> z m_simp.mk_or(x_is_zero, y_is_zero, c7); expr_ref ite_c(m); m_simp.mk_and(z_is_zero, m.mk_not(rm_is_to_neg), ite_c); mk_ite(ite_c, pzero, z, v7); // else comes the fused multiplication. unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); SASSERT(ebits <= sbits); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); expr_ref b_sgn(m), b_sig(m), b_exp(m), b_lz(m); expr_ref c_sgn(m), c_sig(m), c_exp(m), c_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); unpack(y, b_sgn, b_sig, b_exp, b_lz, true); unpack(z, c_sgn, c_sig, c_exp, c_lz, true); expr_ref a_lz_ext(m), b_lz_ext(m), c_lz_ext(m); a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); c_lz_ext = m_bv_util.mk_zero_extend(2, c_lz); expr_ref a_sig_ext(m), b_sig_ext(m); a_sig_ext = m_bv_util.mk_zero_extend(sbits, a_sig); b_sig_ext = m_bv_util.mk_zero_extend(sbits, b_sig); expr_ref a_exp_ext(m), b_exp_ext(m), c_exp_ext(m); a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); c_exp_ext = m_bv_util.mk_sign_extend(2, c_exp); dbg_decouple("fpa2bv_fma_a_sig", a_sig_ext); dbg_decouple("fpa2bv_fma_b_sig", b_sig_ext); dbg_decouple("fpa2bv_fma_c_sig", c_sig); dbg_decouple("fpa2bv_fma_a_exp", a_exp_ext); dbg_decouple("fpa2bv_fma_b_exp", b_exp_ext); dbg_decouple("fpa2bv_fma_c_exp", c_exp_ext); dbg_decouple("fpa2bv_fma_a_lz", a_lz_ext); dbg_decouple("fpa2bv_fma_b_lz", b_lz_ext); dbg_decouple("fpa2bv_fma_c_lz", c_lz_ext); expr_ref mul_sgn(m), mul_sig(m), mul_exp(m); expr * signs[2] = { a_sgn, b_sgn }; mul_sgn = m_bv_util.mk_bv_xor(2, signs); dbg_decouple("fpa2bv_fma_mul_sgn", mul_sgn); mul_exp = m_bv_util.mk_bv_add(m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); dbg_decouple("fpa2bv_fma_mul_exp", mul_exp); mul_sig = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); dbg_decouple("fpa2bv_fma_mul_sig", mul_sig); SASSERT(m_bv_util.get_bv_size(mul_sig) == 2*sbits); SASSERT(m_bv_util.get_bv_size(mul_exp) == ebits + 2); // The product has the form [-1][0].[2*sbits - 2]. // Extend c c_sig = m_bv_util.mk_zero_extend(1, m_bv_util.mk_concat(c_sig, m_bv_util.mk_numeral(0, sbits-1))); c_exp_ext = m_bv_util.mk_bv_sub(c_exp_ext, c_lz_ext); SASSERT(m_bv_util.get_bv_size(mul_sig) == 2 * sbits); SASSERT(m_bv_util.get_bv_size(c_sig) == 2 * sbits); expr_ref swap_cond(m); swap_cond = m_bv_util.mk_sle(mul_exp, c_exp_ext); SASSERT(is_well_sorted(m, swap_cond)); dbg_decouple("fpa2bv_fma_swap_cond", swap_cond); expr_ref e_sgn(m), e_sig(m), e_exp(m), f_sgn(m), f_sig(m), f_exp(m); m_simp.mk_ite(swap_cond, c_sgn, mul_sgn, e_sgn); m_simp.mk_ite(swap_cond, c_sig, mul_sig, e_sig); // has 2 * sbits m_simp.mk_ite(swap_cond, c_exp_ext, mul_exp, e_exp); // has ebits + 2 m_simp.mk_ite(swap_cond, mul_sgn, c_sgn, f_sgn); m_simp.mk_ite(swap_cond, mul_sig, c_sig, f_sig); // has 2 * sbits m_simp.mk_ite(swap_cond, mul_exp, c_exp_ext, f_exp); // has ebits + 2 SASSERT(is_well_sorted(m, e_sgn)); SASSERT(is_well_sorted(m, e_sig)); SASSERT(is_well_sorted(m, e_exp)); SASSERT(is_well_sorted(m, f_sgn)); SASSERT(is_well_sorted(m, f_sig)); SASSERT(is_well_sorted(m, f_exp)); SASSERT(m_bv_util.get_bv_size(e_sig) == 2 * sbits); SASSERT(m_bv_util.get_bv_size(f_sig) == 2 * sbits); SASSERT(m_bv_util.get_bv_size(e_exp) == ebits + 2); SASSERT(m_bv_util.get_bv_size(f_exp) == ebits + 2); expr_ref res_sgn(m), res_sig(m), res_exp(m); expr_ref exp_delta(m); exp_delta = m_bv_util.mk_bv_sub(e_exp, f_exp); dbg_decouple("fpa2bv_fma_add_exp_delta", exp_delta); // cap the delta expr_ref cap(m), cap_le_delta(m); cap = m_bv_util.mk_numeral(2*sbits, ebits+2); cap_le_delta = m_bv_util.mk_ule(cap, exp_delta); m_simp.mk_ite(cap_le_delta, cap, exp_delta, exp_delta); SASSERT(m_bv_util.get_bv_size(exp_delta) == ebits+2); SASSERT(is_well_sorted(m, exp_delta)); dbg_decouple("fpa2bv_fma_add_exp_delta_capped", exp_delta); // Alignment shift with sticky bit computation. expr_ref shifted_big(m), shifted_f_sig(m), sticky_raw(m); shifted_big = m_bv_util.mk_bv_lshr( m_bv_util.mk_concat(f_sig, m_bv_util.mk_numeral(0, sbits)), m_bv_util.mk_zero_extend((3*sbits)-(ebits+2), exp_delta)); shifted_f_sig = m_bv_util.mk_extract(3*sbits-1, sbits, shifted_big); sticky_raw = m_bv_util.mk_extract(sbits-1, 0, shifted_big); SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == 2 * sbits); SASSERT(is_well_sorted(m, shifted_f_sig)); expr_ref sticky(m); sticky = m_bv_util.mk_zero_extend(2*sbits-1, m.mk_app(m_bv_util.get_fid(), OP_BREDOR, sticky_raw.get())); SASSERT(is_well_sorted(m, sticky)); dbg_decouple("fpa2bv_fma_f_sig_sticky_raw", sticky_raw); dbg_decouple("fpa2bv_fma_f_sig_sticky", sticky); expr * or_args[2] = { shifted_f_sig, sticky }; shifted_f_sig = m_bv_util.mk_bv_or(2, or_args); SASSERT(is_well_sorted(m, shifted_f_sig)); // Significant addition. // Two extra bits for catching the overflow. e_sig = m_bv_util.mk_zero_extend(2, e_sig); shifted_f_sig = m_bv_util.mk_zero_extend(2, shifted_f_sig); expr_ref eq_sgn(m); m_simp.mk_eq(e_sgn, f_sgn, eq_sgn); SASSERT(m_bv_util.get_bv_size(e_sig) == 2*sbits + 2); SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == 2*sbits + 2); dbg_decouple("fpa2bv_fma_add_e_sig", e_sig); dbg_decouple("fpa2bv_fma_add_shifted_f_sig", shifted_f_sig); expr_ref sum(m); m_simp.mk_ite(eq_sgn, m_bv_util.mk_bv_add(e_sig, shifted_f_sig), m_bv_util.mk_bv_sub(e_sig, shifted_f_sig), sum); SASSERT(is_well_sorted(m, sum)); dbg_decouple("fpa2bv_fma_add_sum", sum); expr_ref sign_bv(m), n_sum(m); sign_bv = m_bv_util.mk_extract(2*sbits+1, 2*sbits+1, sum); n_sum = m_bv_util.mk_bv_neg(sum); expr_ref res_sig_eq(m), sig_abs(m), one_1(m); one_1 = m_bv_util.mk_numeral(1, 1); m_simp.mk_eq(sign_bv, one_1, res_sig_eq); m_simp.mk_ite(res_sig_eq, n_sum, sum, sig_abs); dbg_decouple("fpa2bv_fma_add_sign_bv", sign_bv); dbg_decouple("fpa2bv_fma_add_n_sum", n_sum); dbg_decouple("fpa2bv_fma_add_sig_abs", sig_abs); res_exp = e_exp; // Result could overflow into 4.xxx ... family_id bvfid = m_bv_util.get_fid(); expr_ref res_sgn_c1(m), res_sgn_c2(m), res_sgn_c3(m); expr_ref not_e_sgn(m), not_f_sgn(m), not_sign_bv(m); not_e_sgn = m_bv_util.mk_bv_not(e_sgn); not_f_sgn = m_bv_util.mk_bv_not(f_sgn); not_sign_bv = m_bv_util.mk_bv_not(sign_bv); res_sgn_c1 = m.mk_app(bvfid, OP_BAND, not_e_sgn, e_sgn, sign_bv); res_sgn_c2 = m.mk_app(bvfid, OP_BAND, e_sgn, not_f_sgn, not_sign_bv); res_sgn_c3 = m.mk_app(bvfid, OP_BAND, e_sgn, f_sgn); expr * res_sgn_or_args[3] = { res_sgn_c1, res_sgn_c2, res_sgn_c3 }; res_sgn = m_bv_util.mk_bv_or(3, res_sgn_or_args); sticky_raw = m_bv_util.mk_extract(sbits-5, 0, sig_abs); sticky = m_bv_util.mk_zero_extend(sbits+3, m.mk_app(bvfid, OP_BREDOR, sticky_raw.get())); dbg_decouple("fpa2bv_fma_add_sum_sticky", sticky); expr * res_or_args[2] = { m_bv_util.mk_extract(2*sbits-1, sbits-4, sig_abs), sticky }; res_sig = m_bv_util.mk_bv_or(2, res_or_args); SASSERT(m_bv_util.get_bv_size(res_sig) == sbits+4); expr_ref is_zero_sig(m), nil_sbits4(m); nil_sbits4 = m_bv_util.mk_numeral(0, sbits+4); m_simp.mk_eq(res_sig, nil_sbits4, is_zero_sig); SASSERT(is_well_sorted(m, is_zero_sig)); dbg_decouple("fpa2bv_fma_is_zero_sig", is_zero_sig); expr_ref zero_case(m); mk_ite(rm_is_to_neg, nzero, pzero, zero_case); //AZ: Should the zero case be constrained in some manner? expr_ref rounded(m);//, fixed(m); round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); fix_bits(prec,rounded,sbits, ebits); mk_ite(is_zero_sig, zero_case, rounded, v8); // And finally, we tie them together. mk_ite(c7, v7, v8, result); mk_ite(c6, v6, result, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_fma_", tout << "FMA = " << mk_ismt2_pp(result, m) << std::endl; ); } break; default: UNREACHABLE(); break; } } void fpa2bv_converter_prec::mk_sqrt(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { switch (m_mode) { case FPAA_SMALL_FLOATS: { func_decl_ref small_fd(m); expr_ref_vector small_args(m); mk_small_op(f, prec, num, args, small_fd, small_args); expr_ref small_op(m); fpa2bv_converter::mk_sqrt(small_fd, num, small_args.c_ptr(), small_op); mk_cast_small_to_big(f, small_op, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_small_float_sqrt", tout << mk_ismt2_pp(result, m) << std::endl; ); break; } case FPAA_PRECISE: case FPAA_FIXBITS: // for now, encode fully. fpa2bv_converter::mk_sqrt(f,num,args,result); break; default: UNREACHABLE(); break; } } void fpa2bv_converter_prec::mk_round_to_integral(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { switch (m_mode) { case FPAA_SMALL_FLOATS: { func_decl_ref small_fd(m); expr_ref_vector small_args(m); mk_small_op(f, prec, num, args, small_fd, small_args); expr_ref small_op(m); fpa2bv_converter::mk_round_to_integral(small_fd, num, small_args.c_ptr(), small_op); mk_cast_small_to_big(f, small_op, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_small_float_r2i", tout << mk_ismt2_pp(result, m) << std::endl; ); break; } case FPAA_PRECISE: case FPAA_FIXBITS: // for now, encode fully. fpa2bv_converter::mk_sqrt(f,num,args,result); break; default: UNREACHABLE(); break; } } void fpa2bv_converter_prec::establish_sort(unsigned num, expr * const * args, unsigned & ebits, unsigned & sbits) { unsigned ne,ns; //hardcoding for now ebits = 3; sbits = 3; for(unsigned i=0;iget_decl()->get_range())) { ne = fu().get_ebits(s); ns = fu().get_sbits(s); ebits = (ne < ebits)?ebits:ne; sbits = (ns < sbits)?sbits:ns; } } } void fpa2bv_converter_prec::mk_float_eq(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { switch (m_mode) { case FPAA_PRECISE: case FPAA_FIXBITS: fpa2bv_converter::mk_float_eq(f,num,args,result); break; case FPAA_SMALL_FLOATS: { unsigned sbits, ebits; func_decl_ref small_fd(m); expr_ref_vector small_args(m); establish_sort(num, args, ebits, sbits); mk_small_op(f, ebits, sbits, num, args, small_fd, small_args); fpa2bv_converter::mk_float_eq(small_fd, num, small_args.c_ptr(), result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_small_float_add", tout << mk_ismt2_pp(result, m) << std::endl; ); break; } } } void fpa2bv_converter_prec::mk_float_lt(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { switch (m_mode){ case FPAA_PRECISE: case FPAA_FIXBITS: fpa2bv_converter::mk_float_lt(f,num,args,result); break; case FPAA_SMALL_FLOATS: { unsigned sbits, ebits; func_decl_ref small_fd(m); expr_ref_vector small_args(m); establish_sort(num, args, ebits, sbits); mk_small_op(f, ebits, sbits, num, args, small_fd, small_args); fpa2bv_converter::mk_float_lt(small_fd, num, small_args.c_ptr(), result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_small_float_add", tout << mk_ismt2_pp(result, m) << std::endl; ); break; } } } void fpa2bv_converter_prec::mk_float_gt(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { switch (m_mode){ case FPAA_PRECISE: case FPAA_FIXBITS: fpa2bv_converter::mk_float_gt(f,num,args,result); break; case FPAA_SMALL_FLOATS: { unsigned sbits, ebits; func_decl_ref small_fd(m); expr_ref_vector small_args(m); establish_sort(num, args, ebits, sbits); mk_small_op(f, ebits, sbits, num, args, small_fd, small_args); fpa2bv_converter::mk_float_gt(small_fd, num, small_args.c_ptr(), result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_small_float_add", tout << mk_ismt2_pp(result, m) << std::endl; ); break; } } } void fpa2bv_converter_prec::mk_float_le(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { switch (m_mode){ case FPAA_PRECISE: case FPAA_FIXBITS: fpa2bv_converter::mk_float_le(f,num,args,result); break; case FPAA_SMALL_FLOATS: { unsigned sbits, ebits; func_decl_ref small_fd(m); expr_ref_vector small_args(m); establish_sort(num, args, ebits, sbits); mk_small_op(f, ebits, sbits, num, args, small_fd, small_args); fpa2bv_converter::mk_float_le(small_fd, num, small_args.c_ptr(), result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_small_float_add", tout << mk_ismt2_pp(result, m) << std::endl; ); break; } } } void fpa2bv_converter_prec::mk_float_ge(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { switch (m_mode){ case FPAA_PRECISE: case FPAA_FIXBITS: fpa2bv_converter::mk_float_ge(f,num,args,result); break; case FPAA_SMALL_FLOATS: { unsigned sbits, ebits; func_decl_ref small_fd(m); expr_ref_vector small_args(m); establish_sort(num, args, ebits, sbits); mk_small_op(f, ebits, sbits, num, args, small_fd, small_args); fpa2bv_converter::mk_float_ge(small_fd, num, small_args.c_ptr(), result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_small_float_add", tout << mk_ismt2_pp(result, m) << std::endl; ); break; } } } /* void fpa2bv_converter_prec::mk_is_zero(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { switch (m_mode){ case FPAA_PRECISE: case FPAA_FIXBITS: fpa2bv_converter::mk_float_eq(f,num,args,result); break; case FPAA_SMALL_FLOATS: { unsigned sbits, ebits; func_decl_ref small_fd(m); expr_ref_vector small_args(m); establish_sort(num, args, ebits, sbits); mk_small_op(f, ebits, sbits, num, args, small_fd, small_args); fpa2bv_converter::mk_is_zero(small_fd, num, small_args.c_ptr(), result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_small_float_add", tout << mk_ismt2_pp(result, m) << std::endl; ); break; } } } void fpa2bv_converter_prec::mk_is_nzero(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void fpa2bv_converter_prec::mk_is_pzero(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void fpa2bv_converter_prec::mk_is_sign_minus(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void fpa2bv_converter_prec::mk_is_nan(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void fpa2bv_converter_prec::mk_is_inf(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void fpa2bv_converter_prec::mk_is_normal(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void fpa2bv_converter_prec::mk_is_subnormal(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); */ z3-4.4.0/src/tactic/fpa/fpa2bv_approx_tactic.cpp0000644000175000017500000014710412540347414021264 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_approx_tactic.cpp Abstract: Tactic that converts floating points to bit-vectors lazily Author: Aleksander Zeljic 2012-11-15 Notes: --*/ #include"tactical.h" #include"cooperate.h" #include"ref_util.h" #include"th_rewriter.h" #include"bit_blaster_rewriter.h" #include"bit_blaster_model_converter.h" #include"model_v2_pp.h" #include"goal2sat.h" #include"sat_solver.h" #include"fpa_decl_plugin.h" #include"fpa2bv_converter_prec.h" #include"fpa2bv_model_converter.h" #include"fpa2bv_converter.h" #include"propagate_values_tactic.h" #include"fpa2bv_rewriter_prec.h" #include"fpa2bv_approx_tactic.h" #include"const_intro_rewriter.h" #include"ctx_simplify_tactic.h" #include"filter_model_converter.h" #include #include #include #include #define K_MIN 10 #define K_PERCENTAGE 0.3 #define PREC_INCREMENT 20 #define ERR_OP 0 // struct pair { expr * exp; double quotient;// mpf * }; bool isinfinite(double x) { #ifdef _WIN32 int c = _fpclass(x); return c == _FPCLASS_PINF || c == _FPCLASS_NINF; #else return fpclassify(x) == FP_INFINITE; #endif } class fpa2bv_approx_tactic: public tactic { struct imp { ast_manager & m; goal2sat m_goal2sat; sat2goal m_sat2goal; params_ref m_params; unsigned m_num_steps; bool m_proofs_enabled; bool m_produce_models; bool m_produce_unsat_cores; bool m_cancel; fpa_approximation_mode m_mode; ast_manager * m_temp_manager; model_ref m_fpa_model; fpa_util m_float_util; imp(ast_manager & _m, params_ref const & p, fpa_approximation_mode mode) : m(_m), m_params(p), m_proofs_enabled(false), m_produce_models(false), m_produce_unsat_cores(false), m_cancel(false), m_mode(mode), m_temp_manager(0), m_float_util(_m) { } void updt_params(params_ref const & p) { m_params = p; } void set_cancel(bool f) { //If f is true stop everything m_cancel = f; } void init_precision_mapping(func_decl_ref_vector const & cnsts, obj_map & map, obj_map & const2term_map) { for (unsigned i = 0; i < cnsts.size(); i++) { if (const2term_map.contains(cnsts.get(i)) || m_mode == FPAA_SMALL_FLOATS) map.insert_if_not_there(cnsts.get(i), 0); else map.insert_if_not_there(cnsts.get(i), MAX_PRECISION); } } bool proof_guided_refinement( goal_ref const & g, func_decl_ref_vector const & cnsts, obj_map & cnst2prec_map, obj_map & new_map) { // We have no model. Let's just increase precision of everything. bool res = false; for (unsigned i = 0; i < cnsts.size(); i++) { unsigned old = cnst2prec_map.find(cnsts.get(i)); unsigned n = old + PREC_INCREMENT; if (old >= MAX_PRECISION) n = MAX_PRECISION; else { if (n > MAX_PRECISION) n = MAX_PRECISION; res = true; } new_map.insert(cnsts.get(i), n); } return res; } void boolean_comparison_of_models(goal_ref g, model_ref const & mdl, model_ref const & full_mdl, obj_map & cnst2term_map, obj_map& count) { std::queue to_traverse; app * cur; int cur_cnt; expr_ref mdl_eval(m), full_eval(m); for (unsigned i=0; i < g->size(); i++){ mdl->eval(g->form(i),mdl_eval,true); full_mdl->eval(g->form(i),full_eval,true); //Push only if the full model evaluates to false, or if the models differ? if (!m.is_true(full_eval)) // m.is_true(full_eval) != m.is_true(mdl_eval) to_traverse.push(g->form(i)); } while (to_traverse.size() > 0) { cur = to_app(to_traverse.front()); #ifdef Z3DEBUG std::cout<<"Analyze - traversing: "<get_decl())) count.insert(cur,1); if(cnst2term_map.contains(cur->get_decl())) to_traverse.push(cnst2term_map.find(cur->get_decl())); for(unsigned i=0;iget_num_args();i++) { if(m_float_util.is_rm(cur->get_arg(i)) || m_float_util.is_numeral(cur->get_arg(i))) continue; to_traverse.push(cur->get_arg(i)); } } else { //Comparing boolean values from the model and the expanded model mdl->eval(cur,mdl_eval,true); full_mdl->eval(cur,full_eval,true); if (m.is_true(full_eval) != m.is_true(mdl_eval)) { //queue arguments for(unsigned i=0; i < cur->get_num_args(); i++) to_traverse.push(cur->get_arg(i)); } } to_traverse.pop(); } #ifdef Z3DEBUG std::cout<<"Expression count"<::iterator it = count.begin(); it!= count.end(); it++) { std::cout<m_key,m)<<":"<m_value<size();i++) { eq = to_app(g->form(i)); if (eq->get_family_id() == m.get_basic_family_id() && eq->get_decl_kind() == OP_EQ){ //eq is in fact an equality app * lhs = to_app(eq->get_arg(0)); app * rhs = to_app(eq->get_arg(1)); expr * lhs_e,*rhs_e,*exp, *exp_e; app *other = NULL; if(lhs->get_num_args()==0 && rhs ->get_num_args()==0){ //over constants lhs_e = full_mdl->get_const_interp(lhs->get_decl()); rhs_e = full_mdl->get_const_interp(rhs->get_decl()); // != would work as well, to make sure they are not both NULL, //and could simplify later checks if(lhs_e != rhs_e) { //SASSERT(lhs_e || rhs_e); //and one is registered in the full model while the other is not if(!lhs_e){// && rhs_e){ other = lhs; exp_e = rhs_e; exp = rhs; } else { // if(!rhs_e && lhs_e){ other = rhs; exp_e = lhs_e; exp = lhs; } full_mdl->register_decl(other->get_decl(),exp_e); #ifdef Z3DEBUG std::cout< & cnst2term_map, obj_map & precise_op, obj_map & actual_value, obj_map & err_est, mpf_rounding_mode & rm, bool & precise_children, bool & seen_all_children, bool & children_have_finite_err, mpf * arg_val, mpf * est_arg_val //expr_ref * arg_e ){ expr_ref arg_e[] = { expr_ref(m), expr_ref(m), expr_ref(m), expr_ref(m) }; unsigned i=0; //Set rounding mode if (rhs->get_num_args() > 0 && m_float_util.is_rm(rhs->get_arg(0))) { expr_ref rm_val(m); mdl->eval(rhs->get_arg(0), rm_val, true); m_float_util.is_rm_numeral(rm_val, rm); i = 1; } //Collect argument values for (; i < rhs->get_num_args(); i++) { expr * arg = rhs->get_arg(i); if (is_app(arg) && to_app(arg)->get_num_args() == 0) { if (precise_op.contains(arg)) { precise_children &= precise_op.find(arg); } else if (!cnst2term_map.contains(to_app(arg)->get_decl())) { /* that's okay */ } else { #ifdef Z3DEBUG std::cout << "Not seen all children of " << mk_ismt2_pp(rhs, m) << " (spec. " << mk_ismt2_pp(arg, m) << ")" << std::endl; #endif precise_children = false; seen_all_children = false; break; } } // Value from small model mdl->eval(arg, arg_e[i],true); m_float_util.is_numeral(arg_e[i], arg_val[i]); if( children_have_finite_err && err_est.contains(arg) && isinfinite(err_est.find(arg))) children_have_finite_err=false; if (actual_value.contains(arg)) mpf_mngr.set(est_arg_val[i], *actual_value.find(arg)); else if (seen_all_children && is_app(arg) && to_app(arg)->get_num_args()==0) { //We have seen all children so if it is a constant and not in actual_value then //it is an input variable and its est_val is the same as actual value mpf * tmp = alloc(mpf); mpf_mngr.set(*tmp, arg_val[i]); actual_value.insert(arg, tmp); mpf_mngr.set(est_arg_val[i], *tmp); } else std::cout << "Estimated value missing: " << mk_ismt2_pp(arg,m) << std::endl; } } void full_semantics_eval( app * rhs, mpf_manager & mpf_mngr, mpf_rounding_mode & rm, mpf * arg_val, mpf * est_arg_val, mpf & rhs_value, mpf & est_rhs_value){ switch (rhs->get_decl()->get_decl_kind()) { case OP_FPA_ADD: mpf_mngr.add(rm, arg_val[1], arg_val[2], rhs_value); mpf_mngr.add(rm, est_arg_val[1], est_arg_val[2], est_rhs_value); break; case OP_FPA_SUB: mpf_mngr.sub(rm, arg_val[1], arg_val[2], rhs_value); mpf_mngr.sub(rm, est_arg_val[1], est_arg_val[2], est_rhs_value); break; case OP_FPA_NEG: mpf_mngr.neg(arg_val[0], rhs_value); mpf_mngr.neg(est_arg_val[0], est_rhs_value);//Does it even make sense to look at this? break; case OP_FPA_MUL: mpf_mngr.mul(rm, arg_val[1], arg_val[2], rhs_value); mpf_mngr.mul(rm, est_arg_val[1], est_arg_val[2], est_rhs_value); break; case OP_FPA_DIV: mpf_mngr.div(rm, arg_val[1], arg_val[2], rhs_value); mpf_mngr.div(rm, est_arg_val[1], est_arg_val[2], est_rhs_value); break; case OP_FPA_REM: mpf_mngr.rem(arg_val[0], arg_val[1], rhs_value); mpf_mngr.rem(est_arg_val[0], est_arg_val[1], est_rhs_value); break; case OP_FPA_FMA: mpf_mngr.fused_mul_add(rm, arg_val[1], arg_val[2], arg_val[3], rhs_value); mpf_mngr.fused_mul_add(rm, est_arg_val[1], est_arg_val[2], est_arg_val[3], est_rhs_value); break; case OP_FPA_SQRT: mpf_mngr.sqrt(rm, arg_val[1], rhs_value); mpf_mngr.sqrt(rm, est_arg_val[1], est_rhs_value); break; case OP_FPA_TO_FP: { unsigned ebits = rhs->get_decl()->get_parameter(0).get_int(); unsigned sbits = rhs->get_decl()->get_parameter(1).get_int(); mpf_mngr.set(rhs_value, ebits, sbits, rm, arg_val[1]); mpf_mngr.set(est_rhs_value, ebits, sbits, rm, est_arg_val[1]); break; } case OP_FPA_ABS: { mpf_mngr.abs(arg_val[0], rhs_value); mpf_mngr.abs(est_arg_val[0], est_rhs_value); break; } case OP_FPA_MIN: { mpf_mngr.minimum( arg_val[1], arg_val[2], rhs_value); mpf_mngr.minimum( est_arg_val[1], est_arg_val[2], est_rhs_value); break; } case OP_FPA_MAX: { mpf_mngr.maximum( arg_val[1], arg_val[2], rhs_value); mpf_mngr.maximum( est_arg_val[1], est_arg_val[2], est_rhs_value); break; } case OP_FPA_ROUND_TO_INTEGRAL: { mpf_mngr.round_to_integral(rm,arg_val[1],rhs_value); mpf_mngr.round_to_integral(rm,est_arg_val[1],est_rhs_value); break; } default: NOT_IMPLEMENTED_YET(); break; } } void evaluate_constant( app * rhs, model_ref const & mdl, mpf_manager & mpf_mngr, obj_map & actual_value, mpf & rhs_value, mpf & est_rhs_value){ expr_ref exp(m); mdl->eval(rhs, exp, true); m_float_util.is_numeral(exp, rhs_value); //OLD:is_value if (actual_value.contains(rhs)) mpf_mngr.set(est_rhs_value, *actual_value.find(rhs)); else { mpf * tmp = alloc(mpf); mpf_mngr.set(*tmp, rhs_value); actual_value.insert(rhs, tmp); mpf_mngr.set(est_rhs_value, rhs_value); } } void calculate_error( expr_ref & lhs, mpf_manager & mpf_mngr, obj_map & precise_op, obj_map & err_est, mpf & lhs_value, mpf & est_rhs_value, bool children_have_finite_err){ mpf err, rel_err; if (!mpf_mngr.eq(lhs_value, est_rhs_value) && !(mpf_mngr.is_nan(lhs_value) && mpf_mngr.is_nan(est_rhs_value))) { #ifdef Z3DEBUG std::cout << "Increasing precision of " << mk_ismt2_pp(lhs, m) << " because " << mk_ismt2_pp(lhs, m) << " != " << mpf_mngr.to_string(est_rhs_value) << std::endl; #endif //TODO: smarter adjustment to be implemented precise_op.insert(lhs, false); if (mpf_mngr.is_regular(lhs_value) && mpf_mngr.is_regular(est_rhs_value)) { mpf_mngr.sub(MPF_ROUND_TOWARD_ZERO, est_rhs_value, lhs_value, err); mpf_mngr.div(MPF_ROUND_TOWARD_ZERO, err, lhs_value, rel_err); mpf_mngr.abs(rel_err); } else// One of the two is a special value; in this case the relative error is +INF mpf_mngr.mk_pinf(11, 53, rel_err); if(children_have_finite_err) err_est.insert(lhs, mpf_mngr.to_double(rel_err)); #ifdef Z3DEBUG std::cout << "Error estimate: "<size(); j++) { mdl->eval(g->form(j), res, true); if (!m.is_true(res)) { std::cout << "Failed: " << mk_ismt2_pp(g->form(j), m) << std::endl; std::cout << "Evaluates to: " << mk_ismt2_pp(res, m) << std::endl; is_model=false; } } return is_model; } void evaluate_and_patch( func_decl_ref_vector const & cnsts, model_ref const & mdl, model_ref & full_mdl, goal_ref const & g, obj_map & cnst2term_map, obj_map & err_est) { mpf_manager & mpf_mngr = m_float_util.fm(); expr_ref lhs(m), lhs_eval(m); app * rhs; mpf arg_val[4]; //First argument can be rounding mode mpf est_arg_val[4]; mpf lhs_value, rhs_value, est_rhs_value; mpf_rounding_mode rm; mpf err, rel_err; obj_map precise_op; obj_map actual_value; while (precise_op.size() != cnst2term_map.size()) for(unsigned i=0;ieval(lhs, lhs_eval, true); if (m_float_util.is_numeral(lhs_eval, lhs_value)) {//OLD:is_value bool precise_children = true; bool seen_all_children = true; bool children_have_finite_err = true; obtain_values(rhs, mdl, full_mdl,mpf_mngr,cnst2term_map,precise_op,actual_value, err_est, rm, precise_children, seen_all_children, children_have_finite_err, arg_val, est_arg_val ); if (seen_all_children) {//If some arguments are not evaluated yet, skip if (rhs->get_num_args() == 0) evaluate_constant(rhs,mdl,mpf_mngr,actual_value, rhs_value, est_rhs_value); else full_semantics_eval(rhs,mpf_mngr,rm,arg_val,est_arg_val, rhs_value, est_rhs_value); if (mpf_mngr.eq(rhs_value, est_rhs_value)) { full_mdl->register_decl((to_app(lhs))->get_decl(), m_float_util.mk_value(est_rhs_value)); precise_op.insert(lhs, true); } else { full_mdl->register_decl((to_app(lhs))->get_decl(), m_float_util.mk_value(est_rhs_value)); #ifdef Z3DEBUG std::cout << "Assigning " << mk_ismt2_pp(lhs, m) << " value " << mpf_mngr.to_string(est_rhs_value) << std::endl << "Values of " << mk_ismt2_pp(lhs, m) << std::endl << "Precise children: " << ((precise_children) ? "True" : "False") << std::endl << "Lhs: " << mk_ismt2_pp(lhs_eval, m) << std::endl << "Model: " << mpf_mngr.to_string(rhs_value) << std::endl << "Estimate: " << mpf_mngr.to_string(est_rhs_value) << std::endl; #endif calculate_error(lhs,mpf_mngr,precise_op,err_est,lhs_value,est_rhs_value,children_have_finite_err); } if (!actual_value.contains(lhs)) { mpf * tmp = alloc(mpf); mpf_mngr.set(*tmp, est_rhs_value); actual_value.insert(lhs, tmp); } if (!precise_children && !precise_op.contains(lhs)) { std::cout << mk_ismt2_pp(lhs, m) << " is imprecise because some children are imprecise." << std::endl; precise_op.insert(lhs, false); } } } } for (obj_map::iterator it = actual_value.begin(); it != actual_value.end(); it++) mpf_mngr.del(*it->m_value); mpf_mngr.del(err); mpf_mngr.del(rel_err); mpf_mngr.del(lhs_value); mpf_mngr.del(rhs_value); mpf_mngr.del(est_rhs_value); for (unsigned i = 0; i < 4; i++) { mpf_mngr.del(arg_val[i]); mpf_mngr.del(est_arg_val[i]); } } bool precise_model_reconstruction( model_ref const & mdl, model_ref & full_mdl, goal_ref const & g, obj_map & err_est,//mpf* func_decl_ref_vector const & cnsts, obj_map & cnst2term_map) { #ifdef Z3DEBUG std::cout << "Attempting to patch small-float model" << std::endl; #endif expr_ref res(m); bool is_model=true; //Evaluation of the model using full fpa semantics and construction of the full model evaluate_and_patch(cnsts, mdl, full_mdl, g, cnst2term_map, err_est); #ifdef Z3DEBUG std::cout<::iterator it = err_est.begin(); it!= err_est.end(); it++) { std::cout<m_key,m)<<":"<m_value<get_num_constants(); j++) { if (!cnst2term_map.contains(mdl->get_constant(j)) && !full_mdl->get_const_interp(mdl->get_constant(j))) { mdl->eval(mdl->get_constant(j), res); full_mdl->register_decl(mdl->get_constant(j), res); } } //Evaluate the full model is_model = evaluate_model(g,full_mdl); return is_model; } void calculate_relative_error( obj_map & err_est, obj_map & expr_count, obj_map & err_ratio_map) { unsigned num_args=0; expr_ref exp(m); double out_err,cur,err_ratio, avg_err; //AZ: Currently ignoring the expr_count, since it was blocking consideration of some expressions for (obj_map::iterator it = err_est.begin(); it != err_est.end(); it++) { // if any ancestor node has an error current node will be in expr_count. /*if (!expr_count.contains(it->m_key)) continue;*/ exp = it->m_key; out_err = it->m_value; num_args = to_app(exp)->get_num_args(); // Calculate average error of input params avg_err = 0.0; if (num_args > 0) { for (unsigned i=0; iget_arg(i); if (err_est.contains(arg)) { cur = err_est.find(arg); avg_err = avg_err + cur; } } avg_err = avg_err/num_args; } // Relative error when input error exists, otherwise just output error err_ratio = fabs((avg_err != (double) 0)? out_err / avg_err : out_err); if(expr_count.contains(exp)) { if(ERR_OP) err_ratio *= 1 + expr_count.find(exp); else err_ratio += expr_count.find(exp); } err_ratio_map.insert(exp, err_ratio); } TRACE("fpa2bv_approx", tout << "ERROR RATIO MAP: " << std::endl; for (obj_map::iterator it = err_ratio_map.begin();// mpf* it != err_ratio_map.end(); it++) tout << mk_ismt2_pp(it->m_key, m) << ": " <m_value<< std::endl; ); #ifdef Z3DEBUG std::cout<<"Error ratio:"<::iterator it = err_ratio_map.begin();//mpf* it != err_ratio_map.end(); it++) std::cout<< mk_ismt2_pp(it->m_key, m) << ": " << it->m_value<< std::endl; std::cout.flush(); #endif } void rank_terms(obj_map & err_ratio_map, std::list & ranked_terms) { unsigned kth = (unsigned)(err_ratio_map.size()*K_PERCENTAGE); if (kth<10) kth=K_MIN; SASSERT(!err_ratio_map.empty()); //Insertion sort the error ratios, keeping only the k highest elements obj_map::iterator it = err_ratio_map.begin(); struct pair * p = new struct pair(); p->exp=it->m_key; p->quotient=it->m_value; ranked_terms.push_front(p); for (it++; it != err_ratio_map.end(); it++) { if (ranked_terms.size()m_value >= ranked_terms.back()->quotient) { std::list::iterator pos = ranked_terms.begin(); while (pos!=ranked_terms.end() && it->m_value <= ranked_terms.back()->quotient) pos++; struct pair * p = new struct pair(); p->exp=it->m_key; p->quotient=it->m_value; ranked_terms.insert(pos, p); if (ranked_terms.size() > kth) { delete ranked_terms.back(); ranked_terms.pop_back(); } } } } void increase_precision( std::list & ranked_terms, func_decl_ref_vector const & cnsts, obj_map & cnst2prec_map, obj_map & cnst2term_map, obj_map & new_map){ //Refine chosen terms and find the any input 'variables' which are //its immediate arguments and refine them as well #ifdef Z3DEBUG std::cout<<"Increasing precision:"<::iterator itp = ranked_terms.begin(); itp != ranked_terms.end(); itp++) { app * cur = to_app((*itp)->exp); func_decl * f = cur->get_decl(); unsigned new_prec = PREC_INCREMENT, old_prec; bool in_new_map; if (cnst2prec_map.contains(f)) new_prec += cnst2prec_map.find(f); new_prec= (new_prec > MAX_PRECISION) ? MAX_PRECISION : new_prec; new_map.insert(f, new_prec); #ifdef Z3DEBUG std::cout << f->get_name() << ":" << new_prec << std::endl; std::cout << mk_ismt2_pp(cur, m) << ":" << new_prec << std::endl; #endif if(cnst2term_map.contains(f)) cur = cnst2term_map.find(f); // Refine constants that are direct arguments of this term for(unsigned i=0; iget_num_args();i++){ func_decl * arg_decl = to_app(cur->get_arg(i))->get_decl(); if (!cnst2term_map.contains(arg_decl) && //Not a constant introduced by flattening !m_float_util.is_rm(cur->get_arg(i)) && //OLD:is_rm(...,rm) !m_float_util.is_numeral(cur->get_arg(i))) { //OLD:is_value //It is an input 'variable' if ( (in_new_map = new_map.contains(arg_decl))) old_prec=new_map.find(arg_decl); else if (cnst2prec_map.contains(arg_decl)) old_prec = cnst2prec_map.find(arg_decl); else old_prec=0; if (old_prec < new_prec) { if (in_new_map) new_map.remove(arg_decl); SASSERT(new_prec <= MAX_PRECISION); new_map.insert(arg_decl, new_prec); std::cout << " " << arg_decl->get_name() << ":" << new_prec << std::endl; #ifdef Z3DEBUG std::cout<<" "<get_arg(i),m)<<":"< & cnst2prec_map, obj_map & cnst2term_map, obj_map & err_est, obj_map & new_map) { obj_map err_ratio_map; obj_map expr_count; std::list ranked_terms; boolean_comparison_of_models(g, mdl, full_mdl, cnst2term_map, expr_count); calculate_relative_error(err_est, expr_count, err_ratio_map); if (err_ratio_map.empty()) { proof_guided_refinement(g,cnsts,cnst2prec_map,new_map); } else { rank_terms (err_ratio_map,ranked_terms); increase_precision(ranked_terms,cnsts,cnst2prec_map,cnst2term_map,new_map); } } void simplify(goal_ref mg) { ast_manager &m = mg->m(); // CMW: <--- We use the manager of the goal, so this works for any manager. expr_ref new_curr(m); proof_ref new_pr(m); th_rewriter simplifier(m, m_params); // CMW: we need to eliminate AND expressions. params_ref elim_and(m_params); elim_and.set_bool("elim_and", true); // elim_and.set_uint("max_depth", 1); // CMW: This number can have a big impact on performance, either way. simplifier.updt_params(elim_and); SASSERT(mg->is_well_sorted()); TRACE("before_simplifier", mg->display(tout);); m_num_steps = 0; if (mg->inconsistent()) return; for (unsigned idx = 0; idx < mg->size(); idx++) { if (mg->inconsistent()) break; expr * curr = mg->form(idx); simplifier(curr, new_curr, new_pr); m_num_steps += simplifier.get_num_steps(); if (mg->proofs_enabled()) { proof * pr = mg->pr(idx); new_pr = m.mk_modus_ponens(pr, new_pr); } mg->update(idx, new_curr, new_pr, mg->dep(idx)); } TRACE("after_simplifier_bug", mg->display(tout);); mg->elim_redundancies(); TRACE("after_simplifier", mg->display(tout);); TRACE("after_simplifier_detail", mg->display_with_dependencies(tout);); SASSERT(mg->is_well_sorted()); } bool fully_encoded(obj_map const & precision_map) { for (obj_map::iterator it = precision_map.begin(); it != precision_map.end(); it++) if (it->m_value < MAX_PRECISION) return false; return true; } void bitblast(goal_ref const & g, fpa2bv_converter_prec & fpa2bv, bit_blaster_rewriter & bv2bool, obj_map & const2prec_map, sat::solver & solver, atom2bool_var & map) { // CMW: This is all done using the temporary manager! expr_ref new_curr(*m_temp_manager); proof_ref new_pr(*m_temp_manager); std::cout.flush(); SASSERT(g->is_well_sorted()); simplify(g); //fpa2bv fpa2bv_rewriter_prec fpa2bv_rw(*m_temp_manager, fpa2bv, m_params); fpa2bv_rw.m_cfg.set_mappings(&const2prec_map); m_num_steps = 0; unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { if (g->inconsistent()) break; expr * curr = g->form(idx); #ifdef Z3DEBUG std::cout<pr(idx); new_pr = m_temp_manager->mk_modus_ponens(pr, new_pr); } g->update(idx, new_curr, new_pr, g->dep(idx)); SASSERT(g->is_well_sorted()); } //Adding the equalities that fix bits for(unsigned i=0;iassert_expr(fpa2bv.m_extra_assertions.get(i)); SASSERT(g->is_well_sorted()); simplify(g); //Bitblasting TRACE("before_bit_blaster", g->display(tout);); m_num_steps = 0; size = g->size(); for (unsigned idx = 0; idx < size; idx++) { if (g->inconsistent()) break; expr * curr = g->form(idx); bv2bool(curr, new_curr, new_pr); m_num_steps += bv2bool.get_num_steps(); if (m_proofs_enabled) { proof * pr = g->pr(idx); new_pr = m_temp_manager->mk_modus_ponens(pr, new_pr); } g->update(idx, new_curr, new_pr, g->dep(idx)); } g->inc_depth(); simplify(g); TRACE("before_sat_solver", g->display(tout);); g->elim_redundancies(); goal2sat::dep2asm_map d2am ; m_goal2sat(*g, m_params, solver, map, d2am , false); TRACE("sat_solver_unknown", tout << "interpreted_atoms: " << map.interpreted_atoms() << "\n"; atom2bool_var::iterator it = map.begin(); atom2bool_var::iterator end = map.end(); for (; it != end; ++it) { if (!is_uninterp_const(it->m_key)) tout << mk_ismt2_pp(it->m_key, *m_temp_manager) << "\n"; }); CASSERT("sat_solver", solver.check_invariant()); IF_VERBOSE(TACTIC_VERBOSITY_LVL, solver.display_status(verbose_stream());); TRACE("sat_dimacs", solver.display_dimacs(tout);); } model_ref get_fpa_model(goal_ref const & g, fpa2bv_converter_prec & fpa2bv, bit_blaster_rewriter & bv2bool, sat::solver & solver, atom2bool_var & map) { // CMW: This is all done using the temporary manager, until at the very end we translate the model back to this->m. model_ref md = alloc(model, *m_temp_manager); sat::model const & ll_m = solver.get_model(); TRACE("sat_tactic", for (unsigned i = 0; i < ll_m.size(); i++) tout << i << ":" << ll_m[i] << " "; tout << "\n";); atom2bool_var::iterator it = map.begin(); atom2bool_var::iterator end = map.end(); for (; it != end; ++it) { expr * n = it->m_key; sat::bool_var v = it->m_value; if (is_app(n) && to_app(n)->get_decl()->get_arity() != 0) continue; TRACE("sat_tactic", tout << "extracting value of " << mk_ismt2_pp(n, *m_temp_manager) << "\nvar: " << v << "\n";); switch (sat::value_at(v, ll_m)) { case l_true: md->register_decl(to_app(n)->get_decl(), m_temp_manager->mk_true()); break; case l_false: md->register_decl(to_app(n)->get_decl(), m_temp_manager->mk_false()); break; default: break; } } TRACE("sat_tactic", model_v2_pp(tout, *md);); model_converter_ref bb_mc = mk_bit_blaster_model_converter(*m_temp_manager, bv2bool.const2bits()); model_converter_ref bv_mc = mk_fpa2bv_model_converter(*m_temp_manager, fpa2bv.const2bv(), fpa2bv.rm_const2bv(), fpa2bv.uf2bvuf(), fpa2bv.uf23bvuf()); bb_mc->operator()(md, 0); bv_mc->operator()(md, 0); #ifdef Z3DEBUG std::cout << "Model: " << std::endl; for (unsigned i = 0 ; i < md->get_num_constants(); i++) { func_decl * d = md->get_constant(i); std::cout << d->get_name() << " = " << mk_ismt2_pp(md->get_const_interp(d), *m_temp_manager) << std::endl; } #endif // md is in terms of the temporary manager. ast_translation translator(*m_temp_manager, this->m); return md->translate(translator); } void encode_fpa_terms( goal_ref const & g, obj_map & const2term_map) { for (obj_map::iterator it = const2term_map.begin(); it!=const2term_map.end(); it++) { expr_ref q(m); #ifdef Z3DEBUG std::cout << "Adding " << it->m_key->get_name() << " = " << mk_ismt2_pp(it->m_value, m) << std::endl; #endif q = m.mk_eq(m.mk_const(it->m_key), it->m_value); g->assert_expr(q); } } lbool approximate_model_construction(goal_ref & g, obj_map & const2prec_map) { lbool r = l_undef; // CMW: The following will introduce lots of stuff that we don't need (e.g., symbols) // To save memory, we use a separate, new manager that we can throw away afterwards. m_temp_manager = alloc(ast_manager, PGM_DISABLED); { ast_translation translator(m, *m_temp_manager); goal_ref ng = g->translate(translator); obj_map const2prec_map_tm; for (obj_map::iterator it = const2prec_map.begin(); it!=const2prec_map.end(); it++) const2prec_map_tm.insert(translator(it->m_key), it->m_value); sat::solver sat_solver(m_params, 0); atom2bool_var atom_map(*m_temp_manager); { tactic_report report_i("fpa2bv_approx_before_bitblaster", *ng); } fpa2bv_converter_prec fpa2bv(*m_temp_manager, m_mode); bit_blaster_rewriter bv2bool(*m_temp_manager, m_params); bitblast(ng, fpa2bv, bv2bool, const2prec_map_tm, sat_solver, atom_map); { tactic_report report_i("fpa2bv_approx_after_bitblaster", *ng); } std::cout << "Iteration variables: " << sat_solver.num_vars() << std::endl; std::cout << "Iteration clauses: " << sat_solver.num_clauses() << std::endl; r = sat_solver.check(); if (r == l_true) { // we need to get the model and translate it back to m. m_fpa_model = get_fpa_model(ng, fpa2bv, bv2bool, sat_solver, atom_map).get(); } else m_fpa_model = 0; // CMW: translator, etc, gets destroyed here, so all references // to temporary expressions are gone. } dealloc(m_temp_manager); m_temp_manager = 0; return r; } void lift( goal_ref const & g, func_decl_ref_vector & constants, obj_map * const2term_map ) { expr_ref new_new_curr(m); expr_ref new_curr(m); proof_ref new_pr(m); simplify(g); //Renaming subexpressions using new constants const_intro_rewriter const_rewriter(m, m_params); for (unsigned idx = 0; idx < g->size(); idx++) { if (g->inconsistent()) break; expr * curr = g->form(idx); const_rewriter(curr, new_curr, new_pr); //Introduces constants that replace subexpressions m_num_steps += const_rewriter.get_num_steps(); if (m_proofs_enabled) { proof * pr = g->pr(idx); new_pr = m.mk_modus_ponens(pr, new_pr); } g->update(idx, new_curr, new_pr, g->dep(idx)); } constants.set(const_rewriter.m_cfg.m_introduced_consts); const2term_map->swap(const_rewriter.m_cfg.m_const2term_map); // Note: Ideally, we would directly encode them. For now we're lazy and just add equalities // and we rely on fpa2bv_converter_prec to `magically' recognize the equalities we added. { tactic_report report_i("fpa2bv_approx_before_fpa_terms", *(g.get())); } encode_fpa_terms(g, *const2term_map); SASSERT(g.get()->is_well_sorted()); } void verify_precise_model( goal_ref const & g, model_ref & full_mdl, func_decl_ref_vector & constants, obj_map & const2term_map, model_converter_ref & mc, goal_ref_buffer & result ){ expr_ref res(m); for (unsigned j = 0; j < g->size(); j++) { full_mdl->eval(g->form(j), res, true); if (!m.is_true(res)) { std::cout << "Failed: " << mk_ismt2_pp(g->form(j), m) << std::endl; std::cout << "Evaluates to: " << mk_ismt2_pp(res, m) << std::endl; } SASSERT(m.is_true(res)); } std::cout << "Full model: " << std::endl; for (unsigned i = 0 ; i < full_mdl->get_num_decls(); i++) { func_decl * d = full_mdl->get_decl(i); if(constants.contains(d)) std::cout << d->get_name() << " = " << mk_ismt2_pp(full_mdl->get_const_interp(d), m) << std::endl; } std::cout.flush(); result.back()->reset(); // Filter all the constants we introduced earlier from the model. filter_model_converter * fmc = alloc(filter_model_converter, m); for (obj_map::iterator it = const2term_map.begin(); it != const2term_map.end(); it++) fmc->insert(it->m_key); mc = concat(fmc, model2model_converter(m_fpa_model.get())); } void setup_options(goal_ref const & g){ SASSERT(g->is_well_sorted()); fail_if_proof_generation("fpa2bv_approx", g); fail_if_unsat_core_generation("fpa2bv_approx", g); m_proofs_enabled = g->proofs_enabled(); m_produce_models = g->models_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); m_num_steps = 0; } void print_constants(func_decl_ref_vector & constants, obj_map & const2prec_map){ #ifdef Z3DEBUG for(unsigned i=0;iget_name()<<":"< const2prec_map; obj_map next_const2prec_map; func_decl_ref_vector constants(m); obj_map const2term_map; lbool r = l_true; unsigned iteration_cnt = 0; stopwatch sw; tactic_report report("fpa2bv_approx", *g); TRACE("fpa2bv_approx", tout << "BEFORE: " << std::endl; g->display(tout);); result.reset(); result.push_back(g.get()); SASSERT(g->is_well_sorted()); if (g->inconsistent()) return; lift(g, constants, &const2term_map); init_precision_mapping(constants, const2prec_map, const2term_map); std::cout << "Simplified goal:" << std::endl; g->display(std::cout); while (!solved && !m_cancel) { std::cout << "=============== Starting iteration " << ++iteration_cnt << std::endl; sw.reset(); sw.start(); // Copy the goal goal_ref mg(alloc(goal, g->m(),g->proofs_enabled(),g->models_enabled(),g->unsat_core_enabled())); mg->copy_from(*g.get()); tactic_report report_i("fpa2bv_approx_i", *mg); print_constants(constants, const2prec_map); TRACE("fpa2bv_approx_goal_i", mg->display(tout); ); r = approximate_model_construction(mg, const2prec_map); std::cout << "Approximation is " << (r==l_true?"SAT":r==l_false?"UNSAT":"UNKNOWN") << std::endl; if (r == l_true) { model_ref full_mdl = alloc(model, m); obj_map err_est; if (fully_encoded(const2prec_map)) { full_mdl = m_fpa_model; solved = true; std::cout<<"Model is at full precision, no patching needed!"< This is unsat. solved = true; result.back()->reset(); result.back()->assert_expr(m.mk_false()); } } else { // CMW: When the sat solver comes back with `unknown', what shall we do? // AZ: Blindly refine? m_cancel = true; } const2prec_map.swap(next_const2prec_map); next_const2prec_map.reset(); std::cout << "Iteration time: " << sw.get_current_seconds() << std::endl; } std::cout << "=============== Terminating " << std::endl; dec_ref_map_key_values(m, const2term_map); std::cout << "Iteration count: " << iteration_cnt << std::endl; } }; imp * m_imp; params_ref m_params; public: fpa2bv_approx_tactic(ast_manager & m, params_ref const & p) : m_params(p){ m_imp = alloc(imp, m, p, FPAA_DEFAULT_MODE); } virtual tactic * translate(ast_manager & m) { return alloc(fpa2bv_approx_tactic, m, m_params); } virtual ~fpa2bv_approx_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } virtual void cleanup() { ast_manager & m = m_imp->m; imp * d = m_imp; #pragma omp critical (tactic_cancel) { d = m_imp; } dealloc(d); d = alloc(imp, m, m_params, FPAA_DEFAULT_MODE); #pragma omp critical (tactic_cancel) { m_imp = d; } } protected: virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_fpa2bv_approx_tactic(ast_manager & m, params_ref const & p) { return and_then(clean(alloc(fpa2bv_approx_tactic, m, p)), mk_fail_if_undecided_tactic()); } z3-4.4.0/src/tactic/fpa/fpa2bv_model_converter.h0000644000175000017500000000624412540347414021257 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_model_converter.h Abstract: Model conversion for fpa2bv_converter Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #ifndef _FPA2BV_MODEL_CONVERTER_H_ #define _FPA2BV_MODEL_CONVERTER_H_ #include"fpa2bv_converter.h" #include"model_converter.h" class fpa2bv_model_converter : public model_converter { ast_manager & m; obj_map m_const2bv; obj_map m_rm_const2bv; obj_map m_uf2bvuf; obj_map m_uf23bvuf; public: fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, obj_map const & rm_const2bv, obj_map const & uf2bvuf, obj_map const & uf23bvuf) : m(m) { // Just create a copy? for (obj_map::iterator it = const2bv.begin(); it != const2bv.end(); it++) { m_const2bv.insert(it->m_key, it->m_value); m.inc_ref(it->m_key); m.inc_ref(it->m_value); } for (obj_map::iterator it = rm_const2bv.begin(); it != rm_const2bv.end(); it++) { m_rm_const2bv.insert(it->m_key, it->m_value); m.inc_ref(it->m_key); m.inc_ref(it->m_value); } for (obj_map::iterator it = uf2bvuf.begin(); it != uf2bvuf.end(); it++) { m_uf2bvuf.insert(it->m_key, it->m_value); m.inc_ref(it->m_key); m.inc_ref(it->m_value); } for (obj_map::iterator it = uf23bvuf.begin(); it != uf23bvuf.end(); it++) { m_uf23bvuf.insert(it->m_key, it->m_value); m.inc_ref(it->m_key); } } virtual ~fpa2bv_model_converter() { dec_ref_map_key_values(m, m_const2bv); dec_ref_map_key_values(m, m_rm_const2bv); } virtual void operator()(model_ref & md, unsigned goal_idx) { SASSERT(goal_idx == 0); model * new_model = alloc(model, m); obj_hashtable bits; convert(md.get(), new_model); md = new_model; } virtual void operator()(model_ref & md) { operator()(md, 0); } void display(std::ostream & out); virtual model_converter * translate(ast_translation & translator); protected: fpa2bv_model_converter(ast_manager & m) : m(m) { } void convert(model * bv_mdl, model * float_mdl); }; model_converter * mk_fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, obj_map const & rm_const2bv, obj_map const & uf2bvuf, obj_map const & uf23bvuf); #endifz3-4.4.0/src/tactic/fpa/fpa2bv_tactic.cpp0000644000175000017500000001274312540347414017673 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_tactic.cpp Abstract: Tactic that converts floating points to bit-vectors Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #include"tactical.h" #include"fpa2bv_rewriter.h" #include"simplify_tactic.h" #include"fpa2bv_tactic.h" #include"fpa2bv_model_converter.h" class fpa2bv_tactic : public tactic { struct imp { ast_manager & m; fpa2bv_converter m_conv; fpa2bv_rewriter m_rw; unsigned m_num_steps; bool m_proofs_enabled; bool m_produce_models; bool m_produce_unsat_cores; imp(ast_manager & _m, params_ref const & p): m(_m), m_conv(m), m_rw(m, m_conv, p), m_proofs_enabled(false), m_produce_models(false), m_produce_unsat_cores(false) { } void updt_params(params_ref const & p) { m_rw.cfg().updt_params(p); } void set_cancel(bool f) { m_rw.set_cancel(f); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("fpa2bv", g); fail_if_unsat_core_generation("fpa2bv", g); m_proofs_enabled = g->proofs_enabled(); m_produce_models = g->models_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); mc = 0; pc = 0; core = 0; result.reset(); tactic_report report("fpa2bv", *g); m_rw.reset(); TRACE("fpa2bv", tout << "BEFORE: " << std::endl; g->display(tout);); if (g->inconsistent()) { result.push_back(g.get()); return; } m_num_steps = 0; expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { if (g->inconsistent()) break; expr * curr = g->form(idx); m_rw(curr, new_curr, new_pr); m_num_steps += m_rw.get_num_steps(); if (m_proofs_enabled) { proof * pr = g->pr(idx); new_pr = m.mk_modus_ponens(pr, new_pr); } g->update(idx, new_curr, new_pr, g->dep(idx)); if (is_app(new_curr)) { const app * a = to_app(new_curr.get()); if (a->get_family_id() == m_conv.fu().get_family_id() && a->get_decl_kind() == OP_FPA_IS_NAN) { // Inject auxiliary lemmas that fix e to the one and only NaN value, // that is (= e (fp #b0 #b1...1 #b0...01)), so that the value propagation // has a value to propagate. expr * sgn, *sig, *exp; expr_ref top_exp(m); m_conv.split_fp(new_curr, sgn, exp, sig); m.mk_eq(sgn, m_conv.bu().mk_numeral(0, 1)); m.mk_eq(exp, m_conv.bu().mk_numeral(-1, m_conv.bu().get_bv_size(exp))); m.mk_eq(sig, m_conv.bu().mk_numeral(1, m_conv.bu().get_bv_size(sig))); } } } if (g->models_enabled()) mc = mk_fpa2bv_model_converter(m, m_conv.const2bv(), m_conv.rm_const2bv(), m_conv.uf2bvuf(), m_conv.uf23bvuf()); g->inc_depth(); result.push_back(g.get()); for (unsigned i = 0; i < m_conv.m_extra_assertions.size(); i++) result.back()->assert_expr(m_conv.m_extra_assertions[i].get()); SASSERT(g->is_well_sorted()); TRACE("fpa2bv", tout << "AFTER: " << std::endl; g->display(tout); if (mc) mc->display(tout); tout << std::endl; ); } }; imp * m_imp; params_ref m_params; public: fpa2bv_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(fpa2bv_tactic, m, m_params); } virtual ~fpa2bv_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } virtual void cleanup() { imp * d = alloc(imp, m_imp->m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } protected: virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_fpa2bv_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(fpa2bv_tactic, m, p)); } z3-4.4.0/src/tactic/fpa/qffp_tactic.cpp0000644000175000017500000000755712540347414017456 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qffpa_tactic.cpp Abstract: Tactic for QF_FP benchmarks. Author: Christoph (cwinter) 2012-01-16 Notes: --*/ #include"tactical.h" #include"simplify_tactic.h" #include"bit_blaster_tactic.h" #include"sat_tactic.h" #include"fpa2bv_tactic.h" #include"smt_tactic.h" #include"propagate_values_tactic.h" #include"probe_arith.h" #include"qfnra_tactic.h" #include"qffp_tactic.h" struct has_fp_to_real_predicate { struct found {}; ast_manager & m; bv_util bu; fpa_util fu; arith_util au; has_fp_to_real_predicate(ast_manager & _m) : m(_m), bu(m), fu(m), au(m) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } void operator()(app * n) { sort * s = get_sort(n); if (au.is_real(s) && n->get_family_id() == fu.get_family_id() && is_app(n) && to_app(n)->get_decl_kind() == OP_FPA_TO_REAL) throw found(); } }; class has_fp_to_real_probe : public probe { public: virtual result operator()(goal const & g) { return test(g); } virtual ~has_fp_to_real_probe() {} }; probe * mk_has_fp_to_real_probe() { return alloc(has_fp_to_real_probe); } tactic * mk_qffp_tactic(ast_manager & m, params_ref const & p) { params_ref simp_p = p; simp_p.set_bool("arith_lhs", true); simp_p.set_bool("elim_and", true); tactic * st = and_then(mk_simplify_tactic(m, simp_p), mk_propagate_values_tactic(m, p), cond(mk_or(mk_produce_proofs_probe(), mk_produce_unsat_cores_probe()), mk_smt_tactic(), and_then( mk_fpa2bv_tactic(m, p), mk_propagate_values_tactic(m, p), using_params(mk_simplify_tactic(m, p), simp_p), mk_bit_blaster_tactic(m, p), using_params(mk_simplify_tactic(m, p), simp_p), cond(mk_is_propositional_probe(), mk_sat_tactic(m, p), cond(mk_has_fp_to_real_probe(), mk_qfnra_tactic(m, p), mk_smt_tactic(p)) ), mk_fail_if_undecided_tactic()))); st->updt_params(p); return st; } tactic * mk_qffpbv_tactic(ast_manager & m, params_ref const & p) { return mk_qffp_tactic(m, p); } struct is_non_qffp_predicate { struct found {}; ast_manager & m; bv_util bu; fpa_util fu; arith_util au; is_non_qffp_predicate(ast_manager & _m) : m(_m), bu(m), fu(m), au(m) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } void operator()(app * n) { sort * s = get_sort(n); if (!m.is_bool(s) && !fu.is_float(s) && !fu.is_rm(s) && !bu.is_bv_sort(s) && !au.is_real(s)) throw found(); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == fu.get_family_id() || fid == bu.get_family_id()) return; if (is_uninterp_const(n)) return; if (au.is_real(s) && au.is_numeral(n)) return; throw found(); } }; class is_qffp_probe : public probe { public: virtual result operator()(goal const & g) { return !test(g); } virtual ~is_qffp_probe() {} }; probe * mk_is_qffp_probe() { return alloc(is_qffp_probe); } probe * mk_is_qffpbv_probe() { return alloc(is_qffp_probe); } z3-4.4.0/src/tactic/fpa/fpa2bv_converter_prec.h0000644000175000017500000001111312540347414021077 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_converter_prec.h Abstract: Conversion routines for Floating Point -> Bit-Vector Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #ifndef _FPA2BV_CONVERTER_PREC #define _FPA2BV_CONVERTER_PREC #include"ast.h" #include"obj_hashtable.h" #include"ref_util.h" #include"fpa_decl_plugin.h" #include"bv_decl_plugin.h" #include"model_converter.h" #include"basic_simplifier_plugin.h" #include"fpa2bv_converter.h" #define MAX_PRECISION 100 #define MIN_PRECISION 0 class fpa2bv_prec_model_converter; enum fpa_approximation_mode { FPAA_PRECISE, // Always use precise encoding FPAA_FIXBITS, // Approximate by fixing some bits of the encoding FPAA_SMALL_FLOATS // Approximate by using smaller floats }; #define FPAA_DEFAULT_MODE FPAA_SMALL_FLOATS class fpa2bv_converter_prec : public fpa2bv_converter { fpa_approximation_mode m_mode; void fix_bits(unsigned prec, expr_ref rounded, unsigned sbits, unsigned ebits);//expr_ref& fixed, void mk_small_op(func_decl * f, unsigned prec, unsigned num, expr * const * args, func_decl_ref & small_op, expr_ref_vector & cast_args); void mk_small_op(func_decl * f, unsigned new_ebits, unsigned new_sbits, unsigned num, expr * const * args, func_decl_ref & small_op, expr_ref_vector & cast_args); void mk_cast_small_to_big(func_decl * big_fd, expr * arg, expr_ref & result); void mk_cast_small_to_big(unsigned sbits, unsigned ebits, expr * arg, expr_ref & result); void match_sorts(expr * a, expr * b, expr_ref & n_a, expr_ref & n_b); void establish_sort(unsigned num, expr * const * args, unsigned & ebits, unsigned & sbits); public: fpa2bv_converter_prec(ast_manager & m, fpa_approximation_mode mode); void mk_const(func_decl * f, unsigned prec, expr_ref & result); void mk_eq(expr * a, expr * b, expr_ref & result); void mk_ite(expr * c, expr * t, expr * f, expr_ref & result); void mk_add(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_sub(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_uminus(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_mul(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_div(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_remainder(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_abs(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_min(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_max(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_fusedma(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_sqrt(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_round_to_integral(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_float_eq(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_float_lt(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_float_gt(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_float_le(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_float_ge(func_decl * f, unsigned num, expr * const * args, expr_ref & result); /* void mk_is_zero(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_is_nzero(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_is_pzero(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_is_sign_minus(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_is_nan(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_is_inf(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_is_normal(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); void mk_is_subnormal(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); */ void reset() { dec_ref_map_key_values(m, m_const2bv); dec_ref_map_key_values(m, m_rm_const2bv); } }; #endif z3-4.4.0/src/tactic/fpa/fpa2bv_tactic.h0000644000175000017500000000077012540347414017335 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_tactic.h Abstract: Tactic that converts floating points to bit-vectors Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #ifndef _FPA2BV_TACTIC_ #define _FPA2BV_TACTIC_ #include"params.h" class ast_manager; class tactic; tactic * mk_fpa2bv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("fpa2bv", "convert floating point numbers to bit-vectors.", "mk_fpa2bv_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/fpa/const_intro_rewriter.h0000644000175000017500000001046112540347414021110 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: const_intro_rewriter.h Abstract: Rewriter for converting FPA to BV Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #ifndef _CONST_INTRO_REWRITER_H_ #define _CONST_INTRO_REWRITER_H_ #include"cooperate.h" #include"bv_decl_plugin.h" #include"tactic_exception.h" #include"fpa2bv_converter_prec.h" struct const_intro_rewriter_cfg : public default_rewriter_cfg { ast_manager & m_manager; expr * m_exp; func_decl_ref_vector m_introduced_consts; obj_map m_const2term_map; unsigned long long m_max_memory; unsigned m_max_steps; fpa_util m_float_util; ast_manager & m() const { return m_manager; } const_intro_rewriter_cfg(ast_manager & m, params_ref const & p): m_manager(m), m_introduced_consts(m), m_float_util(m) { updt_params(p); // We need to make sure that the mananger has the BV plugin loaded. symbol s_bv("bv"); if (!m_manager.has_plugin(s_bv)) m_manager.register_plugin(s_bv, alloc(bv_decl_plugin)); } ~const_intro_rewriter_cfg() { for (obj_map::iterator it = m_const2term_map.begin(); it != m_const2term_map.end(); it++) { m().dec_ref(it->m_key); m().dec_ref(it->m_value); } } void cleanup_buffers() { } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_max_steps = p.get_uint("max_steps", UINT_MAX); } bool max_steps_exceeded(unsigned num_steps) const { cooperate("fpa2bv"); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); return num_steps > m_max_steps; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { TRACE("fpa2bv_rw", tout << "APP: " << f->get_name() << std::endl; ); if (num == 0 && f->get_family_id() == null_family_id && m_float_util.is_float(f->get_range())) { app * f_cnst = m_manager.mk_const(f); if (!m_introduced_consts.contains(f)) m_introduced_consts.push_back(f); result = f_cnst; return BR_DONE; } if (f->get_family_id() == m_float_util.get_family_id()) { switch (f->get_decl_kind()) { case OP_FPA_ADD: case OP_FPA_SUB: case OP_FPA_NEG: case OP_FPA_MUL: case OP_FPA_DIV: case OP_FPA_REM: case OP_FPA_ABS: case OP_FPA_MIN: case OP_FPA_MAX: case OP_FPA_FMA: case OP_FPA_SQRT: case OP_FPA_TO_FP: case OP_FPA_ROUND_TO_INTEGRAL: { app * f_app = m_manager.mk_app(f, num, args); result = m_manager.mk_fresh_const(NULL, f->get_range()); func_decl * fd = to_app(result)->get_decl(); m_introduced_consts.push_back(fd); m_const2term_map.insert_if_not_there(fd, f_app); m().inc_ref(fd); m().inc_ref(f_app); return BR_DONE; } default: return BR_FAILED; } } return BR_FAILED; } bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { return false; } bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { return false; } bool pre_visit(expr * t){ return true; } }; template class rewriter_tpl; struct const_intro_rewriter : public rewriter_tpl { const_intro_rewriter_cfg m_cfg; const_intro_rewriter(ast_manager & m, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { } }; #endif z3-4.4.0/src/tactic/fpa/fpa2bv_model_converter.cpp0000644000175000017500000002135712540347414021614 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_model_converter.h Abstract: Model conversion for fpa2bv_converter Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #include"ast_smt2_pp.h" #include"fpa2bv_model_converter.h" void fpa2bv_model_converter::display(std::ostream & out) { out << "(fpa2bv-model-converter"; for (obj_map::iterator it = m_const2bv.begin(); it != m_const2bv.end(); it++) { const symbol & n = it->m_key->get_name(); out << "\n (" << n << " "; unsigned indent = n.size() + 4; out << mk_ismt2_pp(it->m_value, m, indent) << ")"; } for (obj_map::iterator it = m_rm_const2bv.begin(); it != m_rm_const2bv.end(); it++) { const symbol & n = it->m_key->get_name(); out << "\n (" << n << " "; unsigned indent = n.size() + 4; out << mk_ismt2_pp(it->m_value, m, indent) << ")"; } for (obj_map::iterator it = m_uf2bvuf.begin(); it != m_uf2bvuf.end(); it++) { const symbol & n = it->m_key->get_name(); out << "\n (" << n << " "; unsigned indent = n.size() + 4; out << mk_ismt2_pp(it->m_value, m, indent) << ")"; } for (obj_map::iterator it = m_uf23bvuf.begin(); it != m_uf23bvuf.end(); it++) { const symbol & n = it->m_key->get_name(); out << "\n (" << n << " "; unsigned indent = n.size() + 4; out << mk_ismt2_pp(it->m_value.f_sgn, m, indent) << " ; " << mk_ismt2_pp(it->m_value.f_sig, m, indent) << " ; " << mk_ismt2_pp(it->m_value.f_exp, m, indent) << " ; " << ")"; } out << ")" << std::endl; } model_converter * fpa2bv_model_converter::translate(ast_translation & translator) { fpa2bv_model_converter * res = alloc(fpa2bv_model_converter, translator.to()); for (obj_map::iterator it = m_const2bv.begin(); it != m_const2bv.end(); it++) { func_decl * k = translator(it->m_key); expr * v = translator(it->m_value); res->m_const2bv.insert(k, v); translator.to().inc_ref(k); translator.to().inc_ref(v); } for (obj_map::iterator it = m_rm_const2bv.begin(); it != m_rm_const2bv.end(); it++) { func_decl * k = translator(it->m_key); expr * v = translator(it->m_value); res->m_rm_const2bv.insert(k, v); translator.to().inc_ref(k); translator.to().inc_ref(v); } return res; } void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { fpa_util fu(m); bv_util bu(m); mpf fp_val; unsynch_mpz_manager & mpzm = fu.fm().mpz_manager(); unsynch_mpq_manager & mpqm = fu.fm().mpq_manager(); TRACE("fpa2bv_mc", tout << "BV Model: " << std::endl; for (unsigned i = 0; i < bv_mdl->get_num_constants(); i++) tout << bv_mdl->get_constant(i)->get_name() << " --> " << mk_ismt2_pp(bv_mdl->get_const_interp(bv_mdl->get_constant(i)), m) << std::endl; ); obj_hashtable seen; for (obj_map::iterator it = m_const2bv.begin(); it != m_const2bv.end(); it++) { func_decl * var = it->m_key; app * a = to_app(it->m_value); SASSERT(fu.is_float(var->get_range())); SASSERT(var->get_range()->get_num_parameters() == 2); unsigned ebits = fu.get_ebits(var->get_range()); unsigned sbits = fu.get_sbits(var->get_range()); expr_ref sgn(m), sig(m), exp(m); bv_mdl->eval(a->get_arg(0), sgn, true); bv_mdl->eval(a->get_arg(1), exp, true); bv_mdl->eval(a->get_arg(2), sig, true); SASSERT(a->is_app_of(fu.get_family_id(), OP_FPA_FP)); #ifdef Z3DEBUG SASSERT(to_app(a->get_arg(0))->get_decl()->get_arity() == 0); SASSERT(to_app(a->get_arg(1))->get_decl()->get_arity() == 0); SASSERT(to_app(a->get_arg(2))->get_decl()->get_arity() == 0); seen.insert(to_app(a->get_arg(0))->get_decl()); seen.insert(to_app(a->get_arg(1))->get_decl()); seen.insert(to_app(a->get_arg(2))->get_decl()); #else SASSERT(a->get_arg(0)->get_kind() == OP_EXTRACT); SASSERT(to_app(a->get_arg(0))->get_arg(0)->get_kind() == OP_EXTRACT); seen.insert(to_app(to_app(a->get_arg(0))->get_arg(0))->get_decl()); #endif if (!sgn && !sig && !exp) continue; unsigned sgn_sz = bu.get_bv_size(m.get_sort(a->get_arg(0))); unsigned exp_sz = bu.get_bv_size(m.get_sort(a->get_arg(1))); unsigned sig_sz = bu.get_bv_size(m.get_sort(a->get_arg(2))) - 1; rational sgn_q(0), sig_q(0), exp_q(0); if (sgn) bu.is_numeral(sgn, sgn_q, sgn_sz); if (sig) bu.is_numeral(sig, sig_q, sig_sz); if (exp) bu.is_numeral(exp, exp_q, exp_sz); // un-bias exponent rational exp_unbiased_q; exp_unbiased_q = exp_q - fu.fm().m_powers2.m1(ebits - 1); mpz sig_z; mpf_exp_t exp_z; mpzm.set(sig_z, sig_q.to_mpq().numerator()); exp_z = mpzm.get_int64(exp_unbiased_q.to_mpq().numerator()); TRACE("fpa2bv_mc", tout << var->get_name() << " == [" << sgn_q.to_string() << " " << mpzm.to_string(sig_z) << " " << exp_z << "(" << exp_q.to_string() << ")]" << std::endl;); fu.fm().set(fp_val, ebits, sbits, !mpqm.is_zero(sgn_q.to_mpq()), sig_z, exp_z); float_mdl->register_decl(var, fu.mk_value(fp_val)); mpzm.del(sig_z); } for (obj_map::iterator it = m_rm_const2bv.begin(); it != m_rm_const2bv.end(); it++) { func_decl * var = it->m_key; expr * v = it->m_value; expr_ref eval_v(m); SASSERT(fu.is_rm(var->get_range())); rational bv_val(0); unsigned sz = 0; if (v && bv_mdl->eval(v, eval_v, true) && bu.is_numeral(eval_v, bv_val, sz)) { TRACE("fpa2bv_mc", tout << var->get_name() << " == " << bv_val.to_string() << std::endl;); SASSERT(bv_val.is_uint64()); switch (bv_val.get_uint64()) { case BV_RM_TIES_TO_AWAY: float_mdl->register_decl(var, fu.mk_round_nearest_ties_to_away()); break; case BV_RM_TIES_TO_EVEN: float_mdl->register_decl(var, fu.mk_round_nearest_ties_to_even()); break; case BV_RM_TO_NEGATIVE: float_mdl->register_decl(var, fu.mk_round_toward_negative()); break; case BV_RM_TO_POSITIVE: float_mdl->register_decl(var, fu.mk_round_toward_positive()); break; case BV_RM_TO_ZERO: default: float_mdl->register_decl(var, fu.mk_round_toward_zero()); } SASSERT(v->get_kind() == AST_APP); seen.insert(to_app(v)->get_decl()); } } for (obj_map::iterator it = m_uf2bvuf.begin(); it != m_uf2bvuf.end(); it++) seen.insert(it->m_value); for (obj_map::iterator it = m_uf23bvuf.begin(); it != m_uf23bvuf.end(); it++) { seen.insert(it->m_value.f_sgn); seen.insert(it->m_value.f_sig); seen.insert(it->m_value.f_exp); } fu.fm().del(fp_val); // Keep all the non-float constants. unsigned sz = bv_mdl->get_num_constants(); for (unsigned i = 0; i < sz; i++) { func_decl * c = bv_mdl->get_constant(i); if (!seen.contains(c)) float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); } // And keep everything else sz = bv_mdl->get_num_functions(); for (unsigned i = 0; i < sz; i++) { func_decl * f = bv_mdl->get_function(i); if (!seen.contains(f)) { TRACE("fpa2bv_mc", tout << "Keeping: " << mk_ismt2_pp(f, m) << std::endl;); func_interp * val = bv_mdl->get_func_interp(f); float_mdl->register_decl(f, val); } } sz = bv_mdl->get_num_uninterpreted_sorts(); for (unsigned i = 0; i < sz; i++) { sort * s = bv_mdl->get_uninterpreted_sort(i); ptr_vector u = bv_mdl->get_universe(s); float_mdl->register_usort(s, u.size(), u.c_ptr()); } } model_converter * mk_fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, obj_map const & rm_const2bv, obj_map const & uf2bvuf, obj_map const & uf23bvuf) { return alloc(fpa2bv_model_converter, m, const2bv, rm_const2bv, uf2bvuf, uf23bvuf); } z3-4.4.0/src/tactic/model_converter.h0000644000175000017500000000301212540347414017237 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_converter.h Abstract: Abstract interface for converting models. Author: Leonardo (leonardo) 2011-04-21 Notes: --*/ #ifndef _MODEL_CONVERTER_H_ #define _MODEL_CONVERTER_H_ #include"model.h" #include"converter.h" #include"ref.h" class model_converter : public converter { public: virtual void operator()(model_ref & m) {} // TODO: delete virtual void operator()(model_ref & m, unsigned goal_idx) { // TODO: make it virtual after the transition to goal/tactic/tactical is complete SASSERT(goal_idx == 0); operator()(m); } virtual model_converter * translate(ast_translation & translator) = 0; }; typedef ref model_converter_ref; model_converter * concat(model_converter * mc1, model_converter * mc2); /** \brief \c mc1 is the model converter for a sequence of subgoals of size \c num. Given an i in [0, num), mc2s[i] is the model converter for subgoal i, and num_subgoals[i] is the number of subgoals of subgoals[i]. */ model_converter * concat(model_converter * mc1, unsigned num, model_converter * const * mc2s, unsigned * num_subgoals); model_converter * model2model_converter(model * m); void model_converter2model(ast_manager & mng, model_converter * mc, model_ref & m); void apply(model_converter_ref & mc, model_ref & m, unsigned gidx); typedef sref_vector model_converter_ref_vector; typedef sref_buffer model_converter_ref_buffer; #endif z3-4.4.0/src/tactic/ufbv/0000755000175000017500000000000012540347414014645 5ustar michaelmichaelz3-4.4.0/src/tactic/ufbv/ufbv_tactic.cpp0000644000175000017500000000466612540347414017656 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ufbv_tactic.cpp Abstract: General purpose tactic for UFBV benchmarks. Author: Christoph (cwinter) 2012-10-24 Notes: --*/ #include"tactical.h" #include"simplify_tactic.h" #include"propagate_values_tactic.h" #include"solve_eqs_tactic.h" #include"distribute_forall_tactic.h" #include"der_tactic.h" #include"reduce_args_tactic.h" #include"smt_tactic.h" #include"nnf_tactic.h" #include"macro_finder_tactic.h" #include"ufbv_rewriter_tactic.h" #include"quasi_macros_tactic.h" #include"ufbv_tactic.h" tactic * mk_der_fp_tactic(ast_manager & m, params_ref const & p) { return repeat(and_then(mk_der_tactic(m), mk_simplify_tactic(m, p))); } tactic * mk_ufbv_preprocessor_tactic(ast_manager & m, params_ref const & p) { params_ref no_elim_and(p); no_elim_and.set_bool("elim_and", false); return and_then( mk_trace_tactic("ufbv_pre"), and_then(mk_simplify_tactic(m, p), mk_propagate_values_tactic(m, p), and_then(using_params(mk_macro_finder_tactic(m, no_elim_and), no_elim_and), mk_simplify_tactic(m, p)), and_then(mk_snf_tactic(m, p), mk_simplify_tactic(m, p)), mk_elim_and_tactic(m, p), mk_solve_eqs_tactic(m, p), and_then(mk_der_fp_tactic(m, p), mk_simplify_tactic(m, p)), and_then(mk_distribute_forall_tactic(m, p), mk_simplify_tactic(m, p))), and_then(and_then(mk_reduce_args_tactic(m, p), mk_simplify_tactic(m, p)), and_then(mk_macro_finder_tactic(m, p), mk_simplify_tactic(m, p)), and_then(mk_ufbv_rewriter_tactic(m, p), mk_simplify_tactic(m, p)), and_then(mk_quasi_macros_tactic(m, p), mk_simplify_tactic(m, p)), and_then(mk_der_fp_tactic(m, p), mk_simplify_tactic(m, p)), mk_simplify_tactic(m, p)), mk_trace_tactic("ufbv_post")); } tactic * mk_ufbv_tactic(ast_manager & m, params_ref const & p) { params_ref main_p(p); main_p.set_bool("mbqi", true); main_p.set_uint("mbqi.max_iterations", UINT_MAX); main_p.set_bool("elim_and", true); tactic * t = and_then(repeat(mk_ufbv_preprocessor_tactic(m, main_p), 2), mk_smt_tactic_using(false, main_p)); t->updt_params(p); return t; } z3-4.4.0/src/tactic/ufbv/ufbv_rewriter_tactic.cpp0000644000175000017500000000733312540347414021573 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ufbv_rewriter_tactic.cpp Abstract: UFBV Rewriter (demodulator) Author: Christoph (cwinter) 2012-10-26 Notes: --*/ #include"tactical.h" #include"simplifier.h" #include"basic_simplifier_plugin.h" #include"ufbv_rewriter.h" #include"ufbv_rewriter_tactic.h" class ufbv_rewriter_tactic : public tactic { struct imp { ast_manager & m_manager; bool m_cancel; imp(ast_manager & m, params_ref const & p) : m_manager(m),m_cancel(false) { updt_params(p); } ast_manager & m() const { return m_manager; } void set_cancel(bool f) { m_cancel = f; } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("ufbv-rewriter", *g); fail_if_unsat_core_generation("ufbv-rewriter", g); bool produce_proofs = g->proofs_enabled(); basic_simplifier_plugin bsimp(m_manager); bsimp.set_eliminate_and(true); ufbv_rewriter dem(m_manager, bsimp); expr_ref_vector forms(m_manager), new_forms(m_manager); proof_ref_vector proofs(m_manager), new_proofs(m_manager); unsigned size = g->size(); for (unsigned i = 0; i < size; i++) { forms.push_back(g->form(i)); proofs.push_back(g->pr(i)); } dem(forms.size(), forms.c_ptr(), proofs.c_ptr(), new_forms, new_proofs); g->reset(); for (unsigned i = 0; i < new_forms.size(); i++) g->assert_expr(new_forms.get(i), produce_proofs ? new_proofs.get(i) : 0, 0); mc = 0; // CMW: Remark: The demodulator could potentially remove all references to a variable. g->inc_depth(); result.push_back(g.get()); TRACE("ufbv-rewriter", g->display(tout);); SASSERT(g->is_well_sorted()); } void updt_params(params_ref const & p) { } }; imp * m_imp; params_ref m_params; public: ufbv_rewriter_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(ufbv_rewriter_tactic, m, m_params); } virtual ~ufbv_rewriter_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { insert_max_memory(r); insert_produce_models(r); insert_produce_proofs(r); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } virtual void cleanup() { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_ufbv_rewriter_tactic(ast_manager & m, params_ref const & p) { return alloc(ufbv_rewriter_tactic, m, p); } z3-4.4.0/src/tactic/ufbv/macro_finder_tactic.cpp0000644000175000017500000001173612540347414021340 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: macro_finder_tactic.cpp Abstract: Macro finder Author: Christoph (cwinter) 2012-10-26 Notes: --*/ #include"tactical.h" #include"simplifier.h" #include"basic_simplifier_plugin.h" #include"arith_simplifier_plugin.h" #include"bv_simplifier_plugin.h" #include"macro_manager.h" #include"macro_finder.h" #include"extension_model_converter.h" #include"macro_finder_tactic.h" class macro_finder_tactic : public tactic { struct imp { ast_manager & m_manager; bool m_cancel; bool m_elim_and; imp(ast_manager & m, params_ref const & p) : m_manager(m), m_cancel(false), m_elim_and(false) { updt_params(p); } ast_manager & m() const { return m_manager; } void set_cancel(bool f) { m_cancel = f; } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("macro-finder", *g); fail_if_unsat_core_generation("macro-finder", g); bool produce_proofs = g->proofs_enabled(); simplifier simp(m_manager); basic_simplifier_plugin * bsimp = alloc(basic_simplifier_plugin, m_manager); bsimp->set_eliminate_and(m_elim_and); simp.register_plugin(bsimp); arith_simplifier_params a_params; arith_simplifier_plugin * asimp = alloc(arith_simplifier_plugin, m_manager, *bsimp, a_params); simp.register_plugin(asimp); bv_simplifier_params bv_params; bv_simplifier_plugin * bvsimp = alloc(bv_simplifier_plugin, m_manager, *bsimp, bv_params); simp.register_plugin(bvsimp); macro_manager mm(m_manager, simp); macro_finder mf(m_manager, mm); expr_ref_vector forms(m_manager), new_forms(m_manager); proof_ref_vector proofs(m_manager), new_proofs(m_manager); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { forms.push_back(g->form(idx)); proofs.push_back(g->pr(idx)); } mf(forms.size(), forms.c_ptr(), proofs.c_ptr(), new_forms, new_proofs); g->reset(); for (unsigned i = 0; i < new_forms.size(); i++) g->assert_expr(new_forms.get(i), produce_proofs ? new_proofs.get(i) : 0, 0); extension_model_converter * evmc = alloc(extension_model_converter, mm.get_manager()); unsigned num = mm.get_num_macros(); for (unsigned i = 0; i < num; i++) { expr_ref f_interp(mm.get_manager()); func_decl * f = mm.get_macro_interpretation(i, f_interp); evmc->insert(f, f_interp); } mc = evmc; g->inc_depth(); result.push_back(g.get()); TRACE("macro-finder", g->display(tout);); SASSERT(g->is_well_sorted()); } void updt_params(params_ref const & p) { m_elim_and = p.get_bool("elim_and", false); } }; imp * m_imp; params_ref m_params; public: macro_finder_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(macro_finder_tactic, m, m_params); } virtual ~macro_finder_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { insert_max_memory(r); insert_produce_models(r); insert_produce_proofs(r); r.insert("elim_and", CPK_BOOL, "(default: false) eliminate conjunctions during (internal) calls to the simplifier."); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } virtual void cleanup() { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_macro_finder_tactic(ast_manager & m, params_ref const & p) { return alloc(macro_finder_tactic, m, p); } z3-4.4.0/src/tactic/ufbv/macro_finder_tactic.h0000644000175000017500000000075312540347414021002 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: macro_finder_tactic.h Abstract: Macro finder Author: Christoph (cwinter) 2012-10-26 Notes: --*/ #ifndef _MACRO_FINDER_TACTIC_H_ #define _MACRO_FINDER_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_macro_finder_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("macro-finder", "Identifies and applies macros.", "mk_macro_finder_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/ufbv/ufbv_rewriter.h0000644000175000017500000002641012540347414017706 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: demodulator.h Abstract: Author: Leonardo de Moura (leonardo) 2010-04-12. Revision History: Christoph M. Wintersteiger (cwinter) 2012-10-24: Moved from demodulator.h to ufbv_rewriter.h --*/ #ifndef _UFBV_REWRITER_H_ #define _UFBV_REWRITER_H_ #include"ast.h" #include"substitution.h" #include"obj_hashtable.h" #include"obj_pair_hashtable.h" #include"array_map.h" #include"basic_simplifier_plugin.h" /** \brief Apply demodulators as a preprocessing technique. In first-order theorem proving (FOTP), a demodulator is a universally quantified formula of the form: Forall X1, ..., Xn. L[X1, ..., Xn] = R[X1, ..., Xn] Where L[X1, ..., Xn] contains all variables in R[X1, ..., Xn], and L[X1, ..., Xn] is "bigger" than R[X1, ...,Xn]. The idea is to replace something big L[X1, ..., Xn] with something smaller R[X1, ..., Xn]. In FOTP, they use term orderings to decide what does it mean to be smaller. We are using demodulators in a different context (pre-processing). So, I suggest we have a virtual method is_smaller for comparing expressions. The default implementation just compares the size of the expressions. Similarly, in our context, formulas using iff are also demodulators. Forall X1, ..., Xn. L[X1, ..., Xn] iff R[X1, ..., Xn] After selecting the demodulators, we traverse the rest of the formula looking for instances of L[X1, ..., Xn]. Whenever we find an instance, we replace it with the associated instance of R[X1, ..., Xn]. For example, suppose we have Forall x, y. f(x+y, y) = y and f(g(b) + h(c), h(c)) <= 0 The term f(g(b) + h(c), h(c)) is an instance of f(x+y, y) if we replace x <- g(b) and y <- h(c). So, we can replace it with "y" which is bound to h(c) in this example. So, the result of the transformation is: Forall x, y. f(x+y, y) = y and h(c) <= 0 In the first implementation, let us ignore theory matching. That is, for us the term f(a+1) is not an instance of f(1+x), because the matcher doesn't know + is commutative. Observe the demodulator is *not* copied to the macro manager in this case. Another complication is when we are looking for instances inside other universally quantified formulas. The problem is that both formulas (demodular) and target are reusing variables names (ids). To avoid renaming, we use offsets. The idea is to represent renames implicitly. In this case, each offset is a different "variable bank". A pair (expr, offset) is essentially an expression where every variable in expr is assumed to be from the "bank" offset. The class substitution (in substitution.h) manages offsets for us. The class matcher (in matcher.h) can be use to test whether an expression is an instance of another one. Finally, there is the problem when we have N demodulators (where N is big), and a big formula, and we want to traverse the formula only once looking for opportunities for applying these N demodulators. We want to efficiently find the applicable demodulars. We can start with a simple optimization that given a func_decl it returns the set of demodulators that start with this declaration. For example, suppose we have the demodulators. forall x, f(x, g(0)) = 10 forall x, f(g(h(x)), g(1)) = 20 Then, in our "index" f would map to these two demodulators. As a final optimization, we should adapt the code to use substitution-trees. The current implementation in Z3 is not efficient, and I think it is buggy. So, it would be great to replace it with a new one. The code in spc_rewriter.* does something like that. We cannot reuse this code directly since it is meant for the superposion engine in Z3, but we can adapt it for our needs in the preprocessor. */ class ufbv_rewriter { class rewrite_proc; class add_back_idx_proc; class remove_back_idx_proc; class can_rewrite_proc; typedef std::pair expr_bool_pair; class plugin { ast_manager& m_manager; public: plugin(ast_manager& m): m_manager(m) { } void ins_eh(expr* k, expr_bool_pair v) { m_manager.inc_ref(k); m_manager.inc_ref(v.first); } void del_eh(expr* k, expr_bool_pair v) { m_manager.dec_ref(k); m_manager.dec_ref(v.first); } static unsigned to_int(expr const * k) { return k->get_id(); } }; typedef array_map expr_map; typedef std::pair expr_pair; typedef obj_hashtable expr_set; typedef obj_map back_idx_map; typedef obj_hashtable quantifier_set; typedef obj_map fwd_idx_map; typedef obj_map demodulator2lhs_rhs; typedef expr_map rewrite_cache_map; /** \brief Custom matcher & substitution application */ class match_subst { typedef std::pair expr_pair; typedef obj_pair_hashtable cache; void reset(); ast_manager & m_manager; substitution m_subst; cache m_cache; svector m_todo; bool m_all_args_eq; bool match_args(app * t, expr * const * args); public: match_subst(ast_manager & m); void reserve(unsigned max_vid) { m_subst.reserve(2, max_vid+1); } /** \brief Let f be the top symbol of lhs. If (f args) is an instance of lhs, that is, there is a substitution s s.t. s[lhs] = (f args), then return true and store s[rhs] into new_rhs. Where s[t] represents the application of the substitution s into t. Assumptions, the variables in lhs and (f args) are assumed to be distinct. So, (f x y) matches (f y x). Moreover, the result should be in terms of the variables in (f args). */ bool operator()(app * lhs, expr * rhs, expr * const * args, expr_ref & new_rhs); /** \brief Return true if \c i is an instance of \c t. */ bool operator()(expr * t, expr * i); }; ast_manager & m_manager; match_subst m_match_subst; basic_simplifier_plugin & m_bsimp; fwd_idx_map m_fwd_idx; back_idx_map m_back_idx; demodulator2lhs_rhs m_demodulator2lhs_rhs; expr_ref_buffer m_todo; obj_hashtable m_processed; ptr_vector m_new_args; expr_ref_buffer m_rewrite_todo; rewrite_cache_map m_rewrite_cache; expr_ref_buffer m_new_exprs; void insert_fwd_idx(expr * large, expr * small, quantifier * demodulator); void remove_fwd_idx(func_decl * f, quantifier * demodulator); bool check_fwd_idx_consistency(void); void show_fwd_idx(std::ostream & out); bool is_demodulator(expr * e, expr_ref & large, expr_ref & small) const; bool can_rewrite(expr * n, expr * lhs); expr * rewrite(expr * n); bool rewrite1(func_decl * f, ptr_vector & m_new_args, expr_ref & np); bool rewrite_visit_children(app * a); void rewrite_cache(expr * e, expr * new_e, bool done); void reschedule_processed(func_decl * f); void reschedule_demodulators(func_decl * f, expr * np); unsigned max_var_id(expr * e); protected: // is_smaller returns -1 for e1e2. virtual int is_smaller(expr * e1, expr * e2) const; // is_subset returns -1 for e1 subset e2, +1 for e2 subset e1, 0 else. virtual int is_subset(expr * e1, expr * e2) const; public: ufbv_rewriter(ast_manager & m, basic_simplifier_plugin & p); virtual ~ufbv_rewriter(); void operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); /** Given a demodulator (aka rewrite rule) of the form Forall X. L[X] = R[X] We say the top symbol is the first symbol in L. For example: f is the top symbol in Forall x, f(h(x)) = x + 1 The rewrite engine main loop is based on the DISCOUNT loop used in first-order theorem provers. Main structures: - m_todo: The todo-stack of formulas to be processed. - m_fwd_idx: "Forward index" for finding efficiently which demodulators can be used to rewrite an expression. We organize this set as a mapping from func_decl to a set of demodulators which start with the same top symbol. - m_processed: The set of already processed formulas. We can represent it using a hashtable. - m_back_idx: "Backward index" we use it to find efficiently which already processed expressions and demodulators may be rewritten by a new demodulator. Again, we use a very simple index, for each uninterpreted function symbol (ignore constants) f, store the expressions in m_processed and the demodulators that contain f. If you prefer, you may use two m_back_idxs (one for formulas in m_processed and another for demodulators in m_fwd_idx). Initially, m_todo contains all formulas. That is, it contains the argument exprs. m_fwd_idx, m_processed, m_back_idx are empty. while (m_todo is not empty) { let n be the next formula in m_todo. rewrite n using m_fwd_idx, and let n' be the result. // at this point, it should be the case that there is no demodulator in m_fwd_idx that can rewrite n'. if (n' is not a demodulator) { insert n' into m_processed update m_back_idx (traverse n' and for each uninterpreted function declaration f in n' add the entry f->n' to m_back_idx) } else { let f be the top symbol of n' use m_back_idx to find all formulas p in m_processed that contains f { remove p from m_processed remove p from m_back_idx insert p into m_todo } use m_back_idx to find all demodulators d in m_fwd_idx that contains f { if n' can rewrite d { // this is a quick check, we just traverse d and check if there is an expression in d that is an instance of lhs of n'. // we cannot use the trick used for m_processed, since the main loop would not terminate. remove d from m_fwd_idx remode d from m_back_idx insert p into m_todo } } insert n' into m_fwd_idx update m_back_idx } } the result is the contents of m_processed + all demodulators in m_fwd_idx. Note: to remove p from m_back_idx, we need to traverse p, and for every function declartion f in p, we should remove the entry f->p from m_back_idx. Note: we can implement m_back_idx for formulas as: typedef obj_hashtable expr_set; obj_map m_back_idx; we should represent the sets as hashtables because we want to be able to efficiently remove elements from these sets. ptr_vector m_expr_set_to_delete; // same trick we used in macro_manager. we can use a similar structure for m_back_idx and m_fwd_idx for demodulators. Note: m_processed should be obj_hashtable since we want to remove elements from there efficiently. */ }; #endif /* _UFBV_REWRITER_H_ */ z3-4.4.0/src/tactic/ufbv/quasi_macros_tactic.cpp0000644000175000017500000001205712540347414021373 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: quasi_macros_tactic.cpp Abstract: Quasi-Macros Author: Christoph (cwinter) 2012-10-26 Notes: --*/ #include"tactical.h" #include"simplifier.h" #include"basic_simplifier_plugin.h" #include"arith_simplifier_plugin.h" #include"bv_simplifier_plugin.h" #include"macro_manager.h" #include"macro_finder.h" #include"extension_model_converter.h" #include"quasi_macros.h" #include"quasi_macros_tactic.h" class quasi_macros_tactic : public tactic { struct imp { ast_manager & m_manager; bool m_cancel; imp(ast_manager & m, params_ref const & p) : m_manager(m),m_cancel(false) { updt_params(p); } ast_manager & m() const { return m_manager; } void set_cancel(bool f) { m_cancel = f; } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("quasi-macros", *g); fail_if_unsat_core_generation("quasi-macros", g); bool produce_proofs = g->proofs_enabled(); simplifier simp(m_manager); basic_simplifier_plugin * bsimp = alloc(basic_simplifier_plugin, m_manager); bsimp->set_eliminate_and(true); simp.register_plugin(bsimp); arith_simplifier_params a_params; arith_simplifier_plugin * asimp = alloc(arith_simplifier_plugin, m_manager, *bsimp, a_params); simp.register_plugin(asimp); bv_simplifier_params bv_params; bv_simplifier_plugin * bvsimp = alloc(bv_simplifier_plugin, m_manager, *bsimp, bv_params); simp.register_plugin(bvsimp); macro_manager mm(m_manager, simp); quasi_macros qm(m_manager, mm, simp); bool more = true; expr_ref_vector forms(m_manager), new_forms(m_manager); proof_ref_vector proofs(m_manager), new_proofs(m_manager); unsigned size = g->size(); for (unsigned i = 0; i < size; i++) { forms.push_back(g->form(i)); proofs.push_back(g->pr(i)); } while (more) { // CMW: use repeat(...) ? if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); new_forms.reset(); new_proofs.reset(); more = qm(forms.size(), forms.c_ptr(), proofs.c_ptr(), new_forms, new_proofs); forms.swap(new_forms); proofs.swap(new_proofs); } g->reset(); for (unsigned i = 0; i < new_forms.size(); i++) g->assert_expr(new_forms.get(i), produce_proofs ? new_proofs.get(i) : 0, 0); extension_model_converter * evmc = alloc(extension_model_converter, mm.get_manager()); unsigned num = mm.get_num_macros(); for (unsigned i = 0; i < num; i++) { expr_ref f_interp(mm.get_manager()); func_decl * f = mm.get_macro_interpretation(i, f_interp); evmc->insert(f, f_interp); } mc = evmc; g->inc_depth(); result.push_back(g.get()); TRACE("quasi-macros", g->display(tout);); SASSERT(g->is_well_sorted()); } void updt_params(params_ref const & p) { } }; imp * m_imp; params_ref m_params; public: quasi_macros_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(quasi_macros_tactic, m, m_params); } virtual ~quasi_macros_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { insert_max_memory(r); insert_produce_models(r); insert_produce_proofs(r); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } virtual void cleanup() { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_quasi_macros_tactic(ast_manager & m, params_ref const & p) { return alloc(quasi_macros_tactic, m, p); } z3-4.4.0/src/tactic/ufbv/ufbv_rewriter.cpp0000644000175000017500000007606612540347414020255 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: demodulator.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-04-12. Revision History: Christoph M. Wintersteiger (cwinter) 2010-04-21: Implementation Christoph M. Wintersteiger (cwinter) 2012-10-24: Moved from demodulator.h to ufbv_rewriter.h --*/ #include"ast_pp.h" #include"ufbv_rewriter.h" #include"for_each_expr.h" #include"var_subst.h" #include"uint_set.h" ufbv_rewriter::ufbv_rewriter(ast_manager & m, basic_simplifier_plugin & p): m_manager(m), m_match_subst(m), m_bsimp(p), m_todo(m), m_rewrite_todo(m), m_rewrite_cache(m), m_new_exprs(m) { } ufbv_rewriter::~ufbv_rewriter() { reset_dealloc_values(m_fwd_idx); reset_dealloc_values(m_back_idx); for (demodulator2lhs_rhs::iterator it = m_demodulator2lhs_rhs.begin(); it != m_demodulator2lhs_rhs.end(); it++) { m_manager.dec_ref(it->m_key); m_manager.dec_ref(it->m_value.first); m_manager.dec_ref(it->m_value.second); } } bool ufbv_rewriter::is_demodulator(expr * e, expr_ref & large, expr_ref & small) const { if (e->get_kind() == AST_QUANTIFIER) { quantifier * q = to_quantifier(e); if (q->is_forall()) { expr * qe = q->get_expr(); if ((m_manager.is_eq(qe) || m_manager.is_iff(qe))) { app * eq = to_app(q->get_expr()); expr * lhs = eq->get_arg(0); expr * rhs = eq->get_arg(1); int subset = is_subset(lhs, rhs); int smaller = is_smaller(lhs, rhs); TRACE("demodulator", tout << "testing is_demodulator:\n" << mk_pp(lhs, m_manager) << "\n" << mk_pp(rhs, m_manager) << "\n" << "subset: " << subset << ", smaller: " << smaller << "\n";); // We only track uninterpreted functions, everything else is likely too expensive. if ((subset == +1 || subset == +2) && smaller == +1) { if (is_uninterp(rhs)) { large = rhs; small = lhs; return true; } #if 1 // lhs = (not rhs) --> (not lhs) = rhs expr * not_rhs; if (m_manager.is_not(rhs, not_rhs) && is_uninterp(not_rhs)) { large = not_rhs; small = m_manager.mk_not(lhs); return true; } #endif } if ((subset == -1 || subset == +2) && smaller == -1) { if (is_uninterp(lhs)) { large = lhs; small = rhs; return true; } #if 1 // (not lhs) = rhs --> lhs = (not rhs) expr * not_lhs; if (m_manager.is_not(lhs, not_lhs) && is_uninterp(not_lhs)) { large = not_lhs; small = m_manager.mk_not(rhs); return true; } #endif } } else if (m_manager.is_not(qe) && is_uninterp(to_app(qe)->get_arg(0))) { // this is like (not (f ... )) --> (= (f ...) false) large = to_app(qe)->get_arg(0); small = m_manager.mk_false(); return true; } else if (is_uninterp(qe)) { // this is like (f ... ) --> (= (f ...) true) large = to_app(qe); small = m_manager.mk_true(); return true; } } } return false; } class var_set_proc { uint_set & m_set; public: var_set_proc(uint_set &s):m_set(s) {} void operator()(var * n) { m_set.insert(n->get_idx()); } void operator()(quantifier * n) {} void operator()(app * n) {} }; int ufbv_rewriter::is_subset(expr * e1, expr * e2) const { uint_set ev1, ev2; if (m_manager.is_value(e1)) return 1; // values are always a subset! var_set_proc proc1(ev1); for_each_expr(proc1, e1); var_set_proc proc2(ev2); for_each_expr(proc2, e2); return (ev1==ev2 ) ? +2 : // We return +2 if the sets are equal. (ev1.subset_of(ev2)) ? +1 : (ev2.subset_of(ev1)) ? -1 : 0 ; } int ufbv_rewriter::is_smaller(expr * e1, expr * e2) const { unsigned sz1 = 0, sz2 = 0; // values are always smaller! if (m_manager.is_value(e1)) return +1; else if (m_manager.is_value(e2)) return -1; // interpreted stuff is always better than uninterpreted. if (!is_uninterp(e1) && is_uninterp(e2)) return +1; else if (is_uninterp(e1) && !is_uninterp(e2)) return -1; // two uninterpreted functions are ordered first by the number of // arguments, then by their id. if (is_uninterp(e1) && is_uninterp(e2)) { if (to_app(e1)->get_num_args() < to_app(e2)->get_num_args()) return +1; else if (to_app(e1)->get_num_args() > to_app(e2)->get_num_args()) return -1; else { unsigned a = to_app(e1)->get_decl()->get_id(); unsigned b = to_app(e2)->get_decl()->get_id(); if (a < b) return +1; else if (a > b) return -1; } } switch (e1->get_kind()) { case AST_VAR: sz1 = 1; break; case AST_QUANTIFIER: sz1 = to_quantifier(e1)->get_depth(); break; case AST_APP: sz1 = to_app(e1)->get_depth(); break; default: UNREACHABLE(); } switch (e2->get_kind()) { case AST_VAR: sz2 = 1; break; case AST_QUANTIFIER: sz2 = to_quantifier(e2)->get_depth(); break; case AST_APP: sz2 = to_app(e2)->get_depth(); break; default: UNREACHABLE(); } return (sz1 == sz2) ? 0 : (sz1 < sz2) ? +1 : -1 ; } class max_var_id_proc { unsigned m_max_var_id; public: max_var_id_proc(void):m_max_var_id(0) {} void operator()(var * n) { if(n->get_idx() > m_max_var_id) m_max_var_id = n->get_idx(); } void operator()(quantifier * n) {} void operator()(app * n) {} unsigned get_max(void) { return m_max_var_id; } }; unsigned ufbv_rewriter::max_var_id(expr * e) { max_var_id_proc proc; for_each_expr(proc, e); return proc.get_max(); } void ufbv_rewriter::insert_fwd_idx(expr * large, expr * small, quantifier * demodulator) { SASSERT(large->get_kind() == AST_APP); SASSERT(demodulator); SASSERT(large && small); TRACE("demodulator_fwd", tout << "INSERT: " << mk_pp(demodulator, m_manager) << std::endl; ); func_decl * fd = to_app(large)->get_decl(); fwd_idx_map::iterator it = m_fwd_idx.find_iterator(fd); if (it == m_fwd_idx.end()) { quantifier_set * qs = alloc(quantifier_set, 1); m_fwd_idx.insert(fd, qs); it = m_fwd_idx.find_iterator(fd); } SASSERT(it->m_value); it->m_value->insert(demodulator); m_manager.inc_ref(demodulator); m_manager.inc_ref(large); m_manager.inc_ref(small); m_demodulator2lhs_rhs.insert(demodulator, expr_pair(large, small)); } void ufbv_rewriter::remove_fwd_idx(func_decl * f, quantifier * demodulator) { TRACE("demodulator_fwd", tout << "REMOVE: " << std::hex << (size_t)demodulator << std::endl; ); fwd_idx_map::iterator it = m_fwd_idx.find_iterator(f); if (it != m_fwd_idx.end()) { demodulator2lhs_rhs::iterator fit = m_demodulator2lhs_rhs.find_iterator(demodulator); m_manager.dec_ref(fit->m_value.first); m_manager.dec_ref(fit->m_value.second); m_manager.dec_ref(demodulator); m_demodulator2lhs_rhs.erase(demodulator); it->m_value->erase(demodulator); } else { SASSERT(m_demodulator2lhs_rhs.contains(demodulator)); } } bool ufbv_rewriter::check_fwd_idx_consistency(void) { for (fwd_idx_map::iterator it = m_fwd_idx.begin(); it != m_fwd_idx.end() ; it++ ) { quantifier_set * set = it->m_value; SASSERT(set); for (quantifier_set::iterator sit = set->begin(); sit != set->end(); sit++) { if (!m_demodulator2lhs_rhs.contains(*sit)) return false; } } return true; } void ufbv_rewriter::show_fwd_idx(std::ostream & out) { for (fwd_idx_map::iterator it = m_fwd_idx.begin(); it != m_fwd_idx.end() ; it++ ) { quantifier_set * set = it->m_value; SASSERT(!set); out << it->m_key->get_name() << ": " << std::endl; for (quantifier_set::iterator sit = set->begin(); sit != set->end(); sit++) { out << std::hex << (size_t)*sit << std::endl; } } out << "D2LR: " << std::endl; for (demodulator2lhs_rhs::iterator it = m_demodulator2lhs_rhs.begin(); it != m_demodulator2lhs_rhs.end() ; it++) { out << (size_t) it->m_key << std::endl; } } bool ufbv_rewriter::rewrite1(func_decl * f, ptr_vector & m_new_args, expr_ref & np) { fwd_idx_map::iterator it = m_fwd_idx.find_iterator(f); if (it != m_fwd_idx.end()) { TRACE("demodulator_bug", tout << "trying to rewrite: " << f->get_name() << " args:\n"; for (unsigned i = 0; i < m_new_args.size(); i++) { tout << mk_pp(m_new_args[i], m_manager) << "\n"; }); quantifier_set::iterator dit = it->m_value->begin(); quantifier_set::iterator dend = it->m_value->end(); for ( ; dit != dend ; dit++ ) { quantifier * d = *dit; SASSERT(m_demodulator2lhs_rhs.contains(d)); expr_pair l_s; m_demodulator2lhs_rhs.find(d, l_s); app * large = to_app(l_s.first); if (large->get_num_args() != m_new_args.size()) continue; TRACE("demodulator_bug", tout << "Matching with demodulator: " << mk_pp(d, m_manager) << std::endl; ); SASSERT(large->get_decl() == f); if (m_match_subst(large, l_s.second, m_new_args.c_ptr(), np)) { TRACE("demodulator_bug", tout << "succeeded...\n" << mk_pp(l_s.second, m_manager) << "\n===>\n" << mk_pp(np, m_manager) << "\n";); return true; } } } return false; } bool ufbv_rewriter::rewrite_visit_children(app * a) { bool res=true; unsigned j = a->get_num_args(); while (j > 0) { expr * e = a->get_arg(--j); if (!m_rewrite_cache.contains(e) || !m_rewrite_cache.get(e).second) { m_rewrite_todo.push_back(e); res = false; } } return res; } void ufbv_rewriter::rewrite_cache(expr * e, expr * new_e, bool done) { m_rewrite_cache.insert(e, expr_bool_pair(new_e, done)); } expr * ufbv_rewriter::rewrite(expr * n) { if (m_fwd_idx.empty()) return n; TRACE("demodulator", tout << "rewrite: " << mk_pp(n, m_manager) << std::endl; ); app * a; SASSERT(m_rewrite_todo.empty()); m_rewrite_cache.reset(); m_rewrite_todo.push_back(n); while (!m_rewrite_todo.empty()) { TRACE("demodulator_stack", tout << "STACK: " << std::endl; for ( unsigned i = 0; iget_kind()) { case AST_VAR: rewrite_cache(e, actual, true); m_rewrite_todo.pop_back(); break; case AST_APP: a = to_app(actual); if (rewrite_visit_children(a)) { func_decl * f = a->get_decl(); m_new_args.reset(); unsigned num_args = a->get_num_args(); bool all_untouched=true; for (unsigned i = 0 ; i < num_args ; i++ ) { expr * o_child = a->get_arg(i); expr * n_child; SASSERT(m_rewrite_cache.contains(o_child) && m_rewrite_cache.get(o_child).second); expr_bool_pair const & ebp = m_rewrite_cache.get(o_child); n_child = ebp.first; if (n_child != o_child) all_untouched = false; m_new_args.push_back(n_child); } expr_ref np(m_manager); if (rewrite1(f, m_new_args, np)) { rewrite_cache(e, np, false); // No pop. } else { if(all_untouched) { rewrite_cache(e, actual, true); } else { expr_ref na(m_manager); if (f->get_family_id() != m_manager.get_basic_family_id()) na = m_manager.mk_app(f, m_new_args.size(), m_new_args.c_ptr()); else m_bsimp.reduce(f, m_new_args.size(), m_new_args.c_ptr(), na); TRACE("demodulator_bug", tout << "e:\n" << mk_pp(e, m_manager) << "\nnew_args: \n"; for (unsigned i = 0; i < m_new_args.size(); i++) { tout << mk_pp(m_new_args[i], m_manager) << "\n"; } tout << "=====>\n"; tout << "na:\n " << mk_pp(na, m_manager) << "\n";); rewrite_cache(e, na, true); } m_rewrite_todo.pop_back(); } } break; case AST_QUANTIFIER: { expr * body = to_quantifier(actual)->get_expr(); if (m_rewrite_cache.contains(body)) { const expr_bool_pair ebp = m_rewrite_cache.get(body); SASSERT(ebp.second); expr * new_body = ebp.first; quantifier_ref q(m_manager); q = m_manager.update_quantifier(to_quantifier(actual), new_body); m_new_exprs.push_back(q); expr_ref new_q(m_manager); elim_unused_vars(m_manager, q, new_q); m_new_exprs.push_back(new_q); rewrite_cache(e, new_q, true); m_rewrite_todo.pop_back(); } else { m_rewrite_todo.push_back(body); } break; } default: UNREACHABLE(); } } SASSERT(m_rewrite_cache.contains(n)); const expr_bool_pair & ebp = m_rewrite_cache.get(n); SASSERT(ebp.second); expr * r = ebp.first; TRACE("demodulator", tout << "rewrite result: " << mk_pp(r, m_manager) << std::endl; ); return r; } class ufbv_rewriter::add_back_idx_proc { back_idx_map & m_back_idx; expr * m_expr; public: add_back_idx_proc(back_idx_map & bi, expr * e):m_back_idx(bi),m_expr(e) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { // We track only uninterpreted and constant functions. if (n->get_num_args()==0) return; SASSERT(m_expr && m_expr != (expr*) 0x00000003); func_decl * d=n->get_decl(); if (d->get_family_id() == null_family_id) { back_idx_map::iterator it = m_back_idx.find_iterator(d); if (it != m_back_idx.end()) { SASSERT(it->m_value); it->m_value->insert(m_expr); } else { expr_set * e = alloc(expr_set); e->insert(m_expr); m_back_idx.insert(d, e); } } } }; class ufbv_rewriter::remove_back_idx_proc { back_idx_map & m_back_idx; expr * m_expr; public: remove_back_idx_proc(back_idx_map & bi, expr * e):m_back_idx(bi),m_expr(e) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { // We track only uninterpreted and constant functions. if (n->get_num_args()==0) return; func_decl * d=n->get_decl(); if (d->get_family_id() == null_family_id) { back_idx_map::iterator it = m_back_idx.find_iterator(d); if (it != m_back_idx.end()) { SASSERT(it->m_value); it->m_value->remove(m_expr); } } } }; void ufbv_rewriter::reschedule_processed(func_decl * f) { //use m_back_idx to find all formulas p in m_processed that contains f { back_idx_map::iterator it = m_back_idx.find_iterator(f); if (it != m_back_idx.end()) { SASSERT(it->m_value); expr_set temp; expr_set::iterator sit = it->m_value->begin(); expr_set::iterator send = it->m_value->end(); for ( ; sit != send ; sit++ ) { expr * p = *sit; if (m_processed.contains(p)) temp.insert(p); } sit = temp.begin(); send = temp.end(); for ( ; sit != send; sit++) { expr * p = *sit; // remove p from m_processed and m_back_idx m_processed.remove(p); remove_back_idx_proc proc(m_back_idx, p); // this could change it->m_value, thus we need the `temp' set. for_each_expr(proc, p); // insert p into m_todo m_todo.push_back(p); } } } bool ufbv_rewriter::can_rewrite(expr * n, expr * lhs) { // this is a quick check, we just traverse d and check if there is an expression in d that is an instance of lhs of n'. // we cannot use the trick used for m_processed, since the main loop would not terminate. ptr_vector stack; expr * curr; expr_mark visited; stack.push_back(n); while (!stack.empty()) { curr = stack.back(); if (visited.is_marked(curr)) { stack.pop_back(); continue; } switch(curr->get_kind()) { case AST_VAR: visited.mark(curr, true); stack.pop_back(); break; case AST_APP: if (for_each_expr_args(stack, visited, to_app(curr)->get_num_args(), to_app(curr)->get_args())) { if (m_match_subst(lhs, curr)) return true; visited.mark(curr, true); stack.pop_back(); } break; case AST_QUANTIFIER: if (!for_each_expr_args(stack, visited, to_quantifier(curr)->get_num_patterns(), to_quantifier(curr)->get_patterns())) { break; } if (!for_each_expr_args(stack, visited, to_quantifier(curr)->get_num_no_patterns(), to_quantifier(curr)->get_no_patterns())) { break; } if (!visited.is_marked(to_quantifier(curr)->get_expr())) { stack.push_back(to_quantifier(curr)->get_expr()); break; } stack.pop_back(); break; default: UNREACHABLE(); } } return false; } void ufbv_rewriter::reschedule_demodulators(func_decl * f, expr * lhs) { // use m_back_idx to find all demodulators d in m_fwd_idx that contains f { //ptr_vector to_remove; back_idx_map::iterator it = m_back_idx.find_iterator(f); if (it != m_back_idx.end()) { SASSERT(it->m_value); expr_set all_occurrences; expr_ref l(m_manager); expr_set::iterator esit = it->m_value->begin(); expr_set::iterator esend = it->m_value->end(); for ( ; esit != esend ; esit++) all_occurrences.insert(*esit); // Run over all f-demodulators esit = all_occurrences.begin(); esend = all_occurrences.end(); for ( ; esit != esend ; esit++ ) { expr * occ = *esit; if (!is_quantifier(occ)) continue; // Use the fwd idx to find out whether this is a demodulator. demodulator2lhs_rhs::iterator d2lr_it = m_demodulator2lhs_rhs.find_iterator(to_quantifier(occ)); if (d2lr_it != m_demodulator2lhs_rhs.end()) { l = d2lr_it->m_value.first; quantifier_ref d(m_manager); func_decl_ref df(m_manager); d = to_quantifier(occ); df = to_app(l)->get_decl(); // Now we know there is an occurrence of f in d // if n' can rewrite d { if (can_rewrite(d, lhs)) { TRACE("demodulator", tout << "Rescheduling: " << std::endl << mk_pp(d, m_manager) << std::endl; ); // remove d from m_fwd_idx remove_fwd_idx(df, d); // remove d from m_back_idx // just remember it here, because otherwise it and/or esit might become invalid? // to_remove.insert(d); remove_back_idx_proc proc(m_back_idx, d); for_each_expr(proc, d); // insert d into m_todo m_todo.push_back(d); } } } } //for (ptr_vector::iterator it = to_remove.begin(); it != to_remove.end(); it++) { // expr * d = *it; // remove_back_idx_proc proc(m_manager, m_back_idx, d); // for_each_expr(proc, d); //} } void ufbv_rewriter::operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) { if (m_manager.proofs_enabled()) { // Let us not waste time with proof production warning_msg("PRE_DEMODULATOR=true is not supported when proofs are enabled."); new_exprs.append(n, exprs); new_prs.append(n, prs); return; } TRACE("demodulator", tout << "before demodulator:\n"; for ( unsigned i = 0 ; i < n ; i++ ) tout << mk_pp(exprs[i], m_manager) << std::endl; ); // Initially, m_todo contains all formulas. That is, it contains the argument exprs. m_fwd_idx, m_processed, m_back_idx are empty. unsigned max_vid = 0; for ( unsigned i = 0 ; i < n ; i++ ) { m_todo.push_back(exprs[i]); max_vid = std::max(max_vid, max_var_id(exprs[i])); } m_match_subst.reserve(max_vid); while (!m_todo.empty()) { // let n be the next formula in m_todo. expr_ref cur(m_manager); cur = m_todo.back(); m_todo.pop_back(); // rewrite cur using m_fwd_idx, and let n' be the result. expr * np = rewrite(cur); // at this point, it should be the case that there is no demodulator in m_fwd_idx that can rewrite n'. SASSERT(rewrite(np)==np); // if (n' is not a demodulator) { expr_ref large(m_manager), small(m_manager); if (!is_demodulator(np, large, small)) { // insert n' into m_processed m_processed.insert(np); // update m_back_idx (traverse n' and for each uninterpreted function declaration f in n' add the entry f->n' to m_back_idx) add_back_idx_proc proc(m_back_idx, np); for_each_expr(proc, np); } else { // np is a demodulator that allows us to replace 'large' with 'small'. TRACE("demodulator", tout << "Found demodulator: " << std::endl; tout << mk_pp(large.get(), m_manager) << std::endl << " ---> " << std::endl << mk_pp(small.get(), m_manager) << std::endl; ); TRACE("demodulator_s", tout << "Found demodulator: " << std::endl; tout << to_app(large)->get_decl()->get_name() << "[" << to_app(large)->get_depth() << "]" << " ---> "; if (is_app(small)) tout << to_app(small)->get_decl()->get_name() << "[" << to_app(small)->get_depth() << "]" << std::endl; else tout << mk_pp(small.get(), m_manager) << std::endl; ); // let f be the top symbol of n' SASSERT(is_app(large)); func_decl * f = to_app(large)->get_decl(); reschedule_processed(f); reschedule_demodulators(f, large); // insert n' into m_fwd_idx insert_fwd_idx(large, small, to_quantifier(np)); // update m_back_idx add_back_idx_proc proc(m_back_idx, np); for_each_expr(proc, np); } } // the result is the contents of m_processed + all demodulators in m_fwd_idx. obj_hashtable::iterator pit = m_processed.begin(); obj_hashtable::iterator pend = m_processed.end(); for ( ; pit != pend ; pit++ ) { new_exprs.push_back(*pit); TRACE("demodulator", tout << mk_pp(*pit, m_manager) << std::endl; ); } fwd_idx_map::iterator fit = m_fwd_idx.begin(); fwd_idx_map::iterator fend = m_fwd_idx.end(); for ( ; fit != fend ; fit++ ) { if (fit->m_value) { quantifier_set::iterator dit = fit->m_value->begin(); quantifier_set::iterator dend = fit->m_value->end(); for ( ; dit != dend ; dit++ ) { expr * e = *dit; new_exprs.push_back(e); TRACE("demodulator", tout << mk_pp(*dit, m_manager) << std::endl; ); } } } TRACE("demodulator", tout << "after demodulator:\n"; for ( unsigned i = 0 ; i < new_exprs.size() ; i++ ) tout << mk_pp(new_exprs[i].get(), m_manager) << std::endl; ); } ufbv_rewriter::match_subst::match_subst(ast_manager & m): m_manager(m), m_subst(m) { } /** \brief Auxiliary functor used to implement optimization in match_args. See comment there. */ struct match_args_aux_proc { substitution & m_subst; struct no_match {}; match_args_aux_proc(substitution & s):m_subst(s) {} void operator()(var * n) { expr_offset r; if (m_subst.find(n, 0, r)) { if (r.get_expr() != n) { SASSERT(r.get_offset() == 1); throw no_match(); } else { m_subst.insert(n, 0, expr_offset(n, 1)); } } } void operator()(quantifier * n) { throw no_match(); } void operator()(app * n) {} }; bool ufbv_rewriter::match_subst::match_args(app * lhs, expr * const * args) { m_cache.reset(); m_todo.reset(); // fill todo-list, and perform quick success/failure tests m_all_args_eq = true; unsigned num_args = lhs->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * t_arg = lhs->get_arg(i); expr * i_arg = args[i]; if (t_arg != i_arg) m_all_args_eq = false; if (is_app(t_arg) && is_app(i_arg) && to_app(t_arg)->get_decl() != to_app(i_arg)->get_decl()) { // quick failure... return false; } m_todo.push_back(expr_pair(t_arg, i_arg)); } if (m_all_args_eq) { // quick success worked... return true; } m_subst.reset(); while (!m_todo.empty()) { expr_pair const & p = m_todo.back(); if (is_var(p.first)) { expr_offset r; if (m_subst.find(to_var(p.first), 0, r)) { if (r.get_expr() != p.second) return false; } else { m_subst.insert(to_var(p.first), 0, expr_offset(p.second, 1)); } m_todo.pop_back(); continue; } if (is_var(p.second)) return false; // we may have nested quantifiers. if (is_quantifier(p.first) || is_quantifier(p.second)) return false; SASSERT(is_app(p.first) && is_app(p.second)); if (to_app(p.first)->is_ground() && !to_app(p.second)->is_ground()) return false; if (p.first == p.second && to_app(p.first)->is_ground()) { SASSERT(to_app(p.second)->is_ground()); m_todo.pop_back(); continue; } if (m_cache.contains(p)) { m_todo.pop_back(); continue; } if (p.first == p.second) { // p.first and p.second is not ground... // Traverse p.first and check whether every variable X:0 in p.first // 1) is unbounded (then we bind X:0 -> X:1) // 2) or, is already bounded to X:1 // If that is, the case, we execute: // m_todo.pop_back(); // m_cache.insert(p); // continue; // Otherwise // return false; match_args_aux_proc proc(m_subst); try { for_each_expr(proc, p.first); // succeeded m_todo.pop_back(); m_cache.insert(p); continue; } catch (match_args_aux_proc::no_match) { return false; } } app * n1 = to_app(p.first); app * n2 = to_app(p.second); if (n1->get_decl() != n2->get_decl()) return false; unsigned num_args1 = n1->get_num_args(); if (num_args1 != n2->get_num_args()) return false; m_todo.pop_back(); if (num_args1 == 0) continue; m_cache.insert(p); unsigned j = num_args1; while (j > 0) { --j; m_todo.push_back(expr_pair(n1->get_arg(j), n2->get_arg(j))); } } return true; } bool ufbv_rewriter::match_subst::operator()(app * lhs, expr * rhs, expr * const * args, expr_ref & new_rhs) { if (match_args(lhs, args)) { if (m_all_args_eq) { // quick success... new_rhs = rhs; return true; } unsigned deltas[2] = { 0, 0 }; m_subst.apply(2, deltas, expr_offset(rhs, 0), new_rhs); return true; } return false; } bool ufbv_rewriter::match_subst::operator()(expr * t, expr * i) { m_cache.reset(); m_todo.reset(); if (is_var(t)) return true; if (is_app(t) && is_app(i) && to_app(t)->get_decl() == to_app(i)->get_decl() && to_app(t)->get_num_args() == to_app(i)->get_num_args()) { return match_args(to_app(t), to_app(i)->get_args()); } return false; } z3-4.4.0/src/tactic/ufbv/ufbv_tactic.h0000644000175000017500000000130312540347414017304 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ufbv_tactic.h Abstract: General purpose tactic for UFBV benchmarks. Author: Christoph (cwinter) 2012-10-24 Notes: --*/ #ifndef _UFBV_TACTIC_H_ #define _UFBV_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_ufbv_preprocessor_tactic(ast_manager & m, params_ref const & p = params_ref()); tactic * mk_ufbv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("bv", "builtin strategy for solving BV problems (with quantifiers).", "mk_ufbv_tactic(m, p)") ADD_TACTIC("ufbv", "builtin strategy for solving UFBV problems (with quantifiers).", "mk_ufbv_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/ufbv/quasi_macros_tactic.h0000644000175000017500000000076112540347414021037 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: quasi_macros_tactic.h Abstract: Quasi-Macros Author: Christoph (cwinter) 2012-10-26 Notes: --*/ #ifndef _QUASI_MACROS_TACTIC_H_ #define _QUASI_MACROS_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_quasi_macros_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("quasi-macros", "Identifies and applies quasi-macros.", "mk_quasi_macros_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/ufbv/ufbv_rewriter_tactic.h0000644000175000017500000000063112540347414021232 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ufbv_rewriter_tactic.cpp Abstract: UFBV Rewriter (demodulator) Author: Christoph (cwinter) 2012-10-26 Notes: --*/ #ifndef _UFBV_REWRITER_TACTIC_H_ #define _UFBV_REWRITER_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_ufbv_rewriter_tactic(ast_manager & m, params_ref const & p = params_ref()); #endif z3-4.4.0/src/tactic/extension_model_converter.cpp0000644000175000017500000000713612540347414021701 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: extension_model_converter.cpp Abstract: Model converter that introduces eliminated variables in a model. Author: Leonardo (leonardo) 2011-10-21 Notes: --*/ #include"extension_model_converter.h" #include"model_evaluator.h" #include"ast_smt2_pp.h" #include"model_v2_pp.h" #include"ast_pp.h" extension_model_converter::~extension_model_converter() { } struct extension_model_converter::set_eval { extension_model_converter * m_owner; model_evaluator * m_old; set_eval(extension_model_converter * owner, model_evaluator * ev) { m_owner = owner; m_old = owner->m_eval; #pragma omp critical (extension_model_converter) { owner->m_eval = ev; } } ~set_eval() { #pragma omp critical (extension_model_converter) { m_owner->m_eval = m_old; } } }; static void display_decls_info(std::ostream & out, model_ref & md) { ast_manager & m = md->get_manager(); unsigned sz = md->get_num_decls(); for (unsigned i = 0; i < sz; i++) { func_decl * d = md->get_decl(i); out << d->get_name(); out << " ("; for (unsigned j = 0; j < d->get_arity(); j++) out << mk_pp(d->get_domain(j), m); out << mk_pp(d->get_range(), m); out << ") "; if (d->get_info()) out << *(d->get_info()); out << " :id " << d->get_id() << "\n"; } } void extension_model_converter::operator()(model_ref & md, unsigned goal_idx) { SASSERT(goal_idx == 0); TRACE("extension_mc", model_v2_pp(tout, *md); display_decls_info(tout, md);); model_evaluator ev(*(md.get())); ev.set_model_completion(true); expr_ref val(m()); { set_eval setter(this, &ev); unsigned i = m_vars.size(); while (i > 0) { --i; expr * def = m_defs.get(i); ev(def, val); TRACE("extension_mc", tout << m_vars.get(i)->get_name() << " ->\n" << mk_ismt2_pp(def, m()) << "\n==>\n" << mk_ismt2_pp(val, m()) << "\n";); func_decl * f = m_vars.get(i); unsigned arity = f->get_arity(); if (arity == 0) { md->register_decl(f, val); } else { func_interp * new_fi = alloc(func_interp, m(), arity); new_fi->set_else(val); md->register_decl(f, new_fi); } } } TRACE("extension_mc", model_v2_pp(tout, *md); display_decls_info(tout, md);); } void extension_model_converter::cancel() { #pragma omp critical (extension_model_converter) { if (m_eval) m_eval->cancel(); } } void extension_model_converter::display(std::ostream & out) { ast_manager & m = m_vars.get_manager(); out << "(extension-model-converter"; for (unsigned i = 0; i < m_vars.size(); i++) { out << "\n (" << m_vars.get(i)->get_name() << " "; unsigned indent = m_vars.get(i)->get_name().size() + 4; out << mk_ismt2_pp(m_defs.get(i), m, indent) << ")"; } out << ")" << std::endl; } model_converter * extension_model_converter::translate(ast_translation & translator) { extension_model_converter * res = alloc(extension_model_converter, translator.to()); for (unsigned i = 0; i < m_vars.size(); i++) res->m_vars.push_back(translator(m_vars[i].get())); for (unsigned i = 0; i < m_defs.size(); i++) res->m_defs.push_back(translator(m_defs[i].get())); // m_eval is a transient object. So, it doesn't need to be translated. return res; } z3-4.4.0/src/tactic/horn_subsume_model_converter.h0000644000175000017500000000345212540347414022040 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: horn_subsume_model_converter.h Abstract: Model converter for redundant Horn clauses. Author: Nikolaj Bjorner (nbjorner) 2012-9-16 Revision History: Notes: Subsumption transformation (remove Horn clause): P(x) :- Body(x,y) Rules ---------------------------- Rules Model converter: P(x) := P(x) or (exists y. Body(x,y)) --*/ #ifndef _HORN_SUBSUME_MODEL_CONVERTER_H_ #define _HORN_SUBSUME_MODEL_CONVERTER_H_ #include "model_converter.h" #include "th_rewriter.h" class horn_subsume_model_converter : public model_converter { ast_manager& m; func_decl_ref_vector m_funcs; expr_ref_vector m_bodies; th_rewriter m_rewrite; app_ref_vector m_delay_head; expr_ref_vector m_delay_body; void add_default_false_interpretation(expr* e, model_ref& md); struct add_default_proc { ast_manager& m; model_ref& m_md; add_default_proc(ast_manager& m, model_ref& md): m(m), m_md(md) {} void operator()(app* n); void operator()(expr* n) {} }; public: horn_subsume_model_converter(ast_manager& m): m(m), m_funcs(m), m_bodies(m), m_rewrite(m), m_delay_head(m), m_delay_body(m) {} bool mk_horn(expr* clause, func_decl_ref& pred, expr_ref& body); bool mk_horn(app* head, expr* body, func_decl_ref& pred, expr_ref& body_res); void insert(app* head, expr* body); void insert(app* head, unsigned sz, expr* const* body); void insert(func_decl* p, expr* body) { m_funcs.push_back(p); m_bodies.push_back(body); } virtual void operator()(model_ref& _m); virtual model_converter * translate(ast_translation & translator); ast_manager& get_manager() { return m; } }; #endif z3-4.4.0/src/tactic/tactic.cpp0000644000175000017500000001663212540347414015666 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: tactic.h Abstract: Abstract tactic object. Author: Leonardo (leonardo) 2011-10-13 Notes: --*/ #include #include"tactic.h" #include"probe.h" #include"stopwatch.h" #include"model_v2_pp.h" void tactic::cancel() { #pragma omp critical (tactic_cancel) { set_cancel(true); } } void tactic::reset_cancel() { #pragma omp critical (tactic_cancel) { set_cancel(false); } } struct tactic_report::imp { char const * m_id; goal const & m_goal; stopwatch m_watch; double m_start_memory; imp(char const * id, goal const & g): m_id(id), m_goal(g), m_start_memory(static_cast(memory::get_allocation_size())/static_cast(1024*1024)) { m_watch.start(); } ~imp() { m_watch.stop(); double end_memory = static_cast(memory::get_allocation_size())/static_cast(1024*1024); verbose_stream() << "(" << m_id << " :num-exprs " << m_goal.num_exprs() << " :num-asts " << m_goal.m().get_num_asts() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << " :before-memory " << std::fixed << std::setprecision(2) << m_start_memory << " :after-memory " << std::fixed << std::setprecision(2) << end_memory << ")" << std::endl; } }; tactic_report::tactic_report(char const * id, goal const & g) { if (get_verbosity_level() >= TACTIC_VERBOSITY_LVL) m_imp = alloc(imp, id, g); else m_imp = 0; } tactic_report::~tactic_report() { if (m_imp) dealloc(m_imp); } void report_tactic_progress(char const * id, unsigned val) { if (val > 0) { IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(" << id << " " << val << ")" << std::endl;); } } void skip_tactic::operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { result.reset(); result.push_back(in.get()); mc = 0; pc = 0; core = 0; } tactic * mk_skip_tactic() { return alloc(skip_tactic); } class fail_tactic : public tactic { public: virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { throw tactic_exception("fail tactic"); } virtual void cleanup() {} virtual tactic * translate(ast_manager & m) { return this; } }; tactic * mk_fail_tactic() { return alloc(fail_tactic); } class report_verbose_tactic : public skip_tactic { char const * m_msg; unsigned m_lvl; public: report_verbose_tactic(char const * msg, unsigned lvl) : m_msg(msg), m_lvl(lvl) {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { IF_VERBOSE(m_lvl, verbose_stream() << m_msg << "\n";); skip_tactic::operator()(in, result, mc, pc, core); } }; tactic * mk_report_verbose_tactic(char const * msg, unsigned lvl) { return alloc(report_verbose_tactic, msg, lvl); } class trace_tactic : public skip_tactic { char const * m_tag; public: trace_tactic(char const * tag):m_tag(tag) {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { TRACE(m_tag, in->display(tout);); skip_tactic::operator()(in, result, mc, pc, core); } }; tactic * mk_trace_tactic(char const * tag) { return alloc(trace_tactic, tag); } class fail_if_undecided_tactic : public skip_tactic { public: fail_if_undecided_tactic() {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { if (!in->is_decided()) throw tactic_exception("undecided"); skip_tactic::operator()(in, result, mc, pc, core); } }; tactic * mk_fail_if_undecided_tactic() { return alloc(fail_if_undecided_tactic); } void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { t.reset_statistics(); try { t(in, result, mc, pc, core); t.cleanup(); } catch (tactic_exception & ex) { IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(tactic-exception \"" << escaped(ex.msg()) << "\")" << std::endl;); t.cleanup(); throw ex; } } lbool check_sat(tactic & t, goal_ref & g, model_ref & md, proof_ref & pr, expr_dependency_ref & core, std::string & reason_unknown) { bool models_enabled = g->models_enabled(); bool proofs_enabled = g->proofs_enabled(); bool cores_enabled = g->unsat_core_enabled(); md = 0; pr = 0; core = 0; ast_manager & m = g->m(); goal_ref_buffer r; model_converter_ref mc; proof_converter_ref pc; try { exec(t, g, r, mc, pc, core); } catch (tactic_exception & ex) { reason_unknown = ex.msg(); return l_undef; } TRACE("tactic_mc", mc->display(tout);); TRACE("tactic_check_sat", tout << "r.size(): " << r.size() << "\n"; for (unsigned i = 0; i < r.size(); i++) r[i]->display(tout);); if (is_decided_sat(r)) { if (models_enabled) { model_converter2model(m, mc.get(), md); if (!md) { // create empty model. md = alloc(model, m); } } return l_true; } else if (is_decided_unsat(r)) { goal * final = r[0]; SASSERT(m.is_false(final->form(0))); if (proofs_enabled) pr = final->pr(0); if (cores_enabled) core = final->dep(0); return l_false; } else { if (models_enabled) model_converter2model(m, mc.get(), md); reason_unknown = "incomplete"; return l_undef; } } void fail_if_proof_generation(char const * tactic_name, goal_ref const & in) { if (in->proofs_enabled()) { std::string msg = tactic_name; msg += " does not support proof production"; throw tactic_exception(msg.c_str()); } } void fail_if_unsat_core_generation(char const * tactic_name, goal_ref const & in) { if (in->unsat_core_enabled()) { std::string msg = tactic_name; msg += " does not support unsat core production"; throw tactic_exception(msg.c_str()); } } void fail_if_model_generation(char const * tactic_name, goal_ref const & in) { if (in->models_enabled()) { std::string msg = tactic_name; msg += " does not generate models"; throw tactic_exception(msg.c_str()); } } z3-4.4.0/src/tactic/goal_util.h0000644000175000017500000000043712540347414016037 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: goal_util.h Abstract: goal goodies. Author: Leonardo de Moura (leonardo) 2012-01-03. Revision History: --*/ #ifndef _GOAL_UTIL_H_ #define _GOAL_UTIL_H_ class goal; bool has_term_ite(goal const & g); #endif z3-4.4.0/src/tactic/probe.h0000644000175000017500000000736012540347414015171 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: probe.h Abstract: Evaluates/Probes a goal. A probe is used to build tactics (aka strategies) that makes decisions based on the structure of a goal. The current implementation is very simple. Author: Leonardo de Moura (leonardo) 2011-10-13. Revision History: --*/ #ifndef _PROBE_H_ #define _PROBE_H_ #include"goal.h" class probe { public: class result { double m_value; public: result(double v = 0.0):m_value(v) {} result(unsigned v):m_value(static_cast(v)) {} result(int v):m_value(static_cast(v)) {} result(bool b):m_value(b ? 1.0 : 0.0) {} bool is_true() const { return m_value != 0.0; } double get_value() const { return m_value; } }; private: unsigned m_ref_count; public: probe():m_ref_count(0) {} virtual ~probe() {} void inc_ref() { ++m_ref_count; } void dec_ref() { SASSERT(m_ref_count > 0); --m_ref_count; if (m_ref_count == 0) dealloc(this); } virtual result operator()(goal const & g) = 0; }; typedef ref probe_ref; probe * mk_const_probe(double val); probe * mk_memory_probe(); probe * mk_depth_probe(); probe * mk_size_probe(); /* ADD_PROBE("memory", "ammount of used memory in megabytes.", "mk_memory_probe()") ADD_PROBE("depth", "depth of the input goal.", "mk_depth_probe()") ADD_PROBE("size", "number of assertions in the given goal.", "mk_size_probe()") */ probe * mk_num_exprs_probe(); probe * mk_num_consts_probe(); probe * mk_num_bool_consts_probe(); probe * mk_num_arith_consts_probe(); probe * mk_num_bv_consts_probe(); /* ADD_PROBE("num-exprs", "number of expressions/terms in the given goal.", "mk_num_exprs_probe()") ADD_PROBE("num-consts", "number of non Boolean constants in the given goal.", "mk_num_consts_probe()") ADD_PROBE("num-bool-consts", "number of Boolean constants in the given goal.", "mk_num_bool_consts_probe()") ADD_PROBE("num-arith-consts", "number of arithmetic constants in the given goal.", "mk_num_arith_consts_probe()") ADD_PROBE("num-bv-consts", "number of bit-vector constants in the given goal.", "mk_num_bv_consts_probe()") */ probe * mk_produce_proofs_probe(); probe * mk_produce_models_probe(); probe * mk_produce_unsat_cores_probe(); probe * mk_has_pattern_probe(); /* ADD_PROBE("produce-proofs", "true if proof generation is enabled for the given goal.", "mk_produce_proofs_probe()") ADD_PROBE("produce-model", "true if model generation is enabled for the given goal.", "mk_produce_models_probe()") ADD_PROBE("produce-unsat-cores", "true if unsat-core generation is enabled for the given goal.", "mk_produce_unsat_cores_probe()") ADD_PROBE("has-patterns", "true if the goal contains quantifiers with patterns.", "mk_has_pattern_probe()") */ // Some basic combinators for probes probe * mk_not(probe * p1); probe * mk_and(probe * p1, probe * p2); probe * mk_or(probe * p1, probe * p2); probe * mk_implies(probe * p1, probe * p2); probe * mk_eq(probe * p1, probe * p2); probe * mk_neq(probe * p1, probe * p2); probe * mk_le(probe * p1, probe * p2); probe * mk_lt(probe * p1, probe * p2); probe * mk_ge(probe * p1, probe * p2); probe * mk_gt(probe * p1, probe * p2); probe * mk_add(probe * p1, probe * p2); probe * mk_sub(probe * p1, probe * p2); probe * mk_mul(probe * p1, probe * p2); probe * mk_div(probe * p1, probe * p2); probe * mk_is_propositional_probe(); probe * mk_is_qfbv_probe(); probe * mk_is_qfaufbv_probe(); /* ADD_PROBE("is-propositional", "true if the goal is in propositional logic.", "mk_is_propositional_probe()") ADD_PROBE("is-qfbv", "true if the goal is in QF_BV.", "mk_is_qfbv_probe()") ADD_PROBE("is-qfaufbv", "true if the goal is in QF_AUFBV.", "mk_is_qfaufbv_probe()") */ #endif z3-4.4.0/src/tactic/tactical.cpp0000644000175000017500000016525712540347414016213 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: tactical.h Abstract: Basic combinators Author: Leonardo (leonardo) 2011-10-13 Notes: --*/ #include"tactical.h" #include"scoped_timer.h" #include"cancel_eh.h" #include"cooperate.h" #include"scoped_ptr_vector.h" #include"z3_omp.h" class binary_tactical : public tactic { protected: tactic * m_t1; tactic * m_t2; volatile bool m_cancel; void checkpoint() { if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); } public: binary_tactical(tactic * t1, tactic * t2): m_t1(t1), m_t2(t2), m_cancel(false) { SASSERT(m_t1); SASSERT(m_t2); m_t1->inc_ref(); m_t2->inc_ref(); } virtual ~binary_tactical() { tactic * t1 = m_t1; tactic * t2 = m_t2; #pragma omp critical (tactic_cancel) { m_t1 = 0; m_t2 = 0; } t1->dec_ref(); t2->dec_ref(); } virtual void updt_params(params_ref const & p) { m_t1->updt_params(p); m_t2->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { m_t1->collect_param_descrs(r); m_t2->collect_param_descrs(r); } virtual void collect_statistics(statistics & st) const { m_t1->collect_statistics(st); m_t2->collect_statistics(st); } virtual void reset_statistics() { m_t1->reset_statistics(); m_t2->reset_statistics(); } virtual void cleanup() { m_t1->cleanup(); m_t2->cleanup(); } virtual void reset() { m_t1->reset(); m_t2->reset(); } virtual void set_logic(symbol const & l) { m_t1->set_logic(l); m_t2->set_logic(l); } virtual void set_progress_callback(progress_callback * callback) { m_t1->set_progress_callback(callback); m_t2->set_progress_callback(callback); } protected: /** \brief Reset cancel flag of t if this was not canceled. */ void parent_reset_cancel(tactic & t) { if (!m_cancel) { t.reset_cancel(); } } virtual void set_cancel(bool f) { m_cancel = f; m_t1->set_cancel(f); m_t2->set_cancel(f); } template tactic * translate_core(ast_manager & m) { tactic * new_t1 = m_t1->translate(m); tactic * new_t2 = m_t2->translate(m); return alloc(T, new_t1, new_t2); } }; struct false_pred { bool operator()(goal * g) { return false; } }; class and_then_tactical : public binary_tactical { public: and_then_tactical(tactic * t1, tactic * t2):binary_tactical(t1, t2) {} virtual ~and_then_tactical() {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { bool models_enabled = in->models_enabled(); bool proofs_enabled = in->proofs_enabled(); bool cores_enabled = in->unsat_core_enabled(); ast_manager & m = in->m(); goal_ref_buffer r1; model_converter_ref mc1; proof_converter_ref pc1; expr_dependency_ref core1(m); result.reset(); mc = 0; pc = 0; core = 0; m_t1->operator()(in, r1, mc1, pc1, core1); SASSERT(!is_decided(r1) || (!pc1 && !core1)); // the pc and core of decided goals is 0 unsigned r1_size = r1.size(); SASSERT(r1_size > 0); checkpoint(); if (r1_size == 1) { if (r1[0]->is_decided()) { result.push_back(r1[0]); if (models_enabled) mc = mc1; SASSERT(!pc); SASSERT(!core); return; } goal_ref r1_0 = r1[0]; m_t2->operator()(r1_0, result, mc, pc, core); if (models_enabled) mc = concat(mc1.get(), mc.get()); if (proofs_enabled) pc = concat(pc1.get(), pc.get()); if (cores_enabled) core = m.mk_join(core1.get(), core); } else { if (cores_enabled) core = core1; proof_converter_ref_buffer pc_buffer; model_converter_ref_buffer mc_buffer; sbuffer sz_buffer; goal_ref_buffer r2; for (unsigned i = 0; i < r1_size; i++) { checkpoint(); goal_ref g = r1[i]; r2.reset(); model_converter_ref mc2; proof_converter_ref pc2; expr_dependency_ref core2(m); m_t2->operator()(g, r2, mc2, pc2, core2); if (is_decided(r2)) { SASSERT(r2.size() == 1); if (is_decided_sat(r2)) { // found solution... result.push_back(r2[0]); if (models_enabled) { // mc2 contains the actual model model_ref md; md = alloc(model, m); apply(mc2, md, 0); apply(mc1, md, i); mc = model2model_converter(md.get()); } SASSERT(!pc); SASSERT(!core); return; } else { SASSERT(is_decided_unsat(r2)); // the proof and unsat core of a decided_unsat goal are stored in the node itself. // pc2 and core2 must be 0. SASSERT(!pc2); SASSERT(!core2); if (models_enabled) mc_buffer.push_back(0); if (proofs_enabled) pc_buffer.push_back(proof2proof_converter(m, r2[0]->pr(0))); if (models_enabled || proofs_enabled) sz_buffer.push_back(0); if (cores_enabled) core = m.mk_join(core.get(), r2[0]->dep(0)); } } else { result.append(r2.size(), r2.c_ptr()); if (models_enabled) mc_buffer.push_back(mc2.get()); if (proofs_enabled) pc_buffer.push_back(pc2.get()); if (models_enabled || proofs_enabled) sz_buffer.push_back(r2.size()); if (cores_enabled) core = m.mk_join(core.get(), core2.get()); } } if (result.empty()) { // all subgoals were shown to be unsat. // create an decided_unsat goal with the proof in->reset_all(); proof_ref pr(m); if (proofs_enabled) apply(m, pc1, pc_buffer, pr); SASSERT(cores_enabled || core == 0); in->assert_expr(m.mk_false(), pr, core); core = 0; result.push_back(in.get()); SASSERT(!mc); SASSERT(!pc); SASSERT(!core); } else { if (models_enabled) mc = concat(mc1.get(), mc_buffer.size(), mc_buffer.c_ptr(), sz_buffer.c_ptr()); if (proofs_enabled) pc = concat(pc1.get(), pc_buffer.size(), pc_buffer.c_ptr(), sz_buffer.c_ptr()); SASSERT(cores_enabled || core == 0); } } } virtual tactic * translate(ast_manager & m) { return translate_core(m); } }; tactic * and_then(tactic * t1, tactic * t2) { return alloc(and_then_tactical, t1, t2); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3) { return and_then(t1, and_then(t2, t3)); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4) { return and_then(t1, and_then(t2, t3, t4)); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5) { return and_then(t1, and_then(t2, t3, t4, t5)); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6) { return and_then(t1, and_then(t2, t3, t4, t5, t6)); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7) { return and_then(t1, and_then(t2, t3, t4, t5, t6, t7)); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8) { return and_then(t1, and_then(t2, t3, t4, t5, t6, t7, t8)); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9) { return and_then(t1, and_then(t2, t3, t4, t5, t6, t7, t8, t9)); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9, tactic * t10) { return and_then(t1, and_then(t2, t3, t4, t5, t6, t7, t8, t9, t10)); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9, tactic * t10, tactic * t11) { return and_then(t1, and_then(t2, t3, t4, t5, t6, t7, t8, t9, t10, t11)); } tactic * and_then(unsigned num, tactic * const * ts) { SASSERT(num > 0); unsigned i = num - 1; tactic * r = ts[i]; while (i > 0) { --i; r = and_then(ts[i], r); } return r; } class nary_tactical : public tactic { protected: ptr_vector m_ts; volatile bool m_cancel; void checkpoint() { if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); } public: nary_tactical(unsigned num, tactic * const * ts): m_cancel(false) { for (unsigned i = 0; i < num; i++) { SASSERT(ts[i]); m_ts.push_back(ts[i]); ts[i]->inc_ref(); } } virtual ~nary_tactical() { ptr_buffer old_ts; unsigned sz = m_ts.size(); old_ts.append(sz, m_ts.c_ptr()); #pragma omp critical (tactic_cancel) { for (unsigned i = 0; i < sz; i++) { m_ts[i] = 0; } } for (unsigned i = 0; i < sz; i++) { old_ts[i]->dec_ref(); } } virtual void updt_params(params_ref const & p) { TRACE("nary_tactical_updt_params", tout << "updt_params: " << p << "\n";); ptr_vector::iterator it = m_ts.begin(); ptr_vector::iterator end = m_ts.end(); for (; it != end; ++it) (*it)->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { ptr_vector::iterator it = m_ts.begin(); ptr_vector::iterator end = m_ts.end(); for (; it != end; ++it) (*it)->collect_param_descrs(r); } virtual void collect_statistics(statistics & st) const { ptr_vector::const_iterator it = m_ts.begin(); ptr_vector::const_iterator end = m_ts.end(); for (; it != end; ++it) (*it)->collect_statistics(st); } virtual void reset_statistics() { ptr_vector::const_iterator it = m_ts.begin(); ptr_vector::const_iterator end = m_ts.end(); for (; it != end; ++it) (*it)->reset_statistics(); } virtual void cleanup() { ptr_vector::iterator it = m_ts.begin(); ptr_vector::iterator end = m_ts.end(); for (; it != end; ++it) (*it)->cleanup(); } virtual void reset() { ptr_vector::iterator it = m_ts.begin(); ptr_vector::iterator end = m_ts.end(); for (; it != end; ++it) (*it)->reset(); } virtual void set_logic(symbol const & l) { ptr_vector::iterator it = m_ts.begin(); ptr_vector::iterator end = m_ts.end(); for (; it != end; ++it) (*it)->set_logic(l); } virtual void set_progress_callback(progress_callback * callback) { ptr_vector::iterator it = m_ts.begin(); ptr_vector::iterator end = m_ts.end(); for (; it != end; ++it) (*it)->set_progress_callback(callback); } protected: /** \brief Reset cancel flag of st if this was not canceled. */ void parent_reset_cancel(tactic & t) { if (!m_cancel) { t.reset_cancel(); } } virtual void set_cancel(bool f) { m_cancel = f; ptr_vector::iterator it = m_ts.begin(); ptr_vector::iterator end = m_ts.end(); for (; it != end; ++it) if (*it) (*it)->set_cancel(f); } template tactic * translate_core(ast_manager & m) { ptr_buffer new_ts; ptr_vector::iterator it = m_ts.begin(); ptr_vector::iterator end = m_ts.end(); for (; it != end; ++it) { tactic * curr = *it; tactic * new_curr = curr->translate(m); new_ts.push_back(new_curr); } return alloc(T, new_ts.size(), new_ts.c_ptr()); } }; class or_else_tactical : public nary_tactical { public: or_else_tactical(unsigned num, tactic * const * ts):nary_tactical(num, ts) { SASSERT(num > 0); } virtual ~or_else_tactical() {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { goal orig(*(in.get())); unsigned sz = m_ts.size(); unsigned i; for (i = 0; i < sz; i++) { checkpoint(); tactic * t = m_ts[i]; result.reset(); mc = 0; pc = 0; core = 0; SASSERT(sz > 0); if (i < sz - 1) { try { t->operator()(in, result, mc, pc, core); return; } catch (tactic_exception &) { } } else { t->operator()(in, result, mc, pc, core); return; } in->reset_all(); in->copy_from(orig); } } virtual tactic * translate(ast_manager & m) { return translate_core(m); } }; tactic * or_else(unsigned num, tactic * const * ts) { return alloc(or_else_tactical, num, ts); } tactic * or_else(tactic * t1, tactic * t2) { tactic * ts[2] = { t1, t2 }; return or_else(2, ts); } tactic * or_else(tactic * t1, tactic * t2, tactic * t3) { tactic * ts[3] = { t1, t2, t3 }; return or_else(3, ts); } tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4) { tactic * ts[4] = { t1, t2, t3, t4 }; return or_else(4, ts); } tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5) { tactic * ts[5] = { t1, t2, t3, t4, t5 }; return or_else(5, ts); } tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6) { tactic * ts[6] = { t1, t2, t3, t4, t5, t6 }; return or_else(6, ts); } tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7) { tactic * ts[7] = { t1, t2, t3, t4, t5, t6, t7 }; return or_else(7, ts); } tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8) { tactic * ts[8] = { t1, t2, t3, t4, t5, t6, t7, t8 }; return or_else(8, ts); } tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9) { tactic * ts[9] = { t1, t2, t3, t4, t5, t6, t7, t8, t9 }; return or_else(9, ts); } tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9, tactic * t10) { tactic * ts[10] = { t1, t2, t3, t4, t5, t6, t7, t8, t9, t10 }; return or_else(10, ts); } enum par_exception_kind { TACTIC_EX, DEFAULT_EX, ERROR_EX }; class par_tactical : public or_else_tactical { public: par_tactical(unsigned num, tactic * const * ts):or_else_tactical(num, ts) {} virtual ~par_tactical() {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { bool use_seq; #ifdef _NO_OMP_ use_seq = true; #else use_seq = 0 != omp_in_parallel(); #endif if (use_seq) { // execute tasks sequentially or_else_tactical::operator()(in, result, mc, pc, core); return; } ast_manager & m = in->m(); scoped_ptr_vector managers; goal_ref_vector in_copies; tactic_ref_vector ts; unsigned sz = m_ts.size(); for (unsigned i = 0; i < sz; i++) { ast_manager * new_m = alloc(ast_manager, m, !m.proof_mode()); managers.push_back(new_m); ast_translation translator(m, *new_m); in_copies.push_back(in->translate(translator)); ts.push_back(m_ts.get(i)->translate(*new_m)); } unsigned finished_id = UINT_MAX; par_exception_kind ex_kind = DEFAULT_EX; std::string ex_msg; unsigned error_code = 0; #pragma omp parallel for for (int i = 0; i < static_cast(sz); i++) { goal_ref_buffer _result; model_converter_ref _mc; proof_converter_ref _pc; expr_dependency_ref _core(*(managers[i])); goal_ref in_copy = in_copies[i]; tactic & t = *(ts.get(i)); try { t(in_copy, _result, _mc, _pc, _core); bool first = false; #pragma omp critical (par_tactical) { if (finished_id == UINT_MAX) { finished_id = i; first = true; } } if (first) { for (unsigned j = 0; j < sz; j++) { if (static_cast(i) != j) ts.get(j)->cancel(); } ast_translation translator(*(managers[i]), m, false); for (unsigned k = 0; k < _result.size(); k++) { result.push_back(_result[k]->translate(translator)); } mc = _mc ? _mc->translate(translator) : 0; pc = _pc ? _pc->translate(translator) : 0; expr_dependency_translation td(translator); core = td(_core); } } catch (tactic_exception & ex) { if (i == 0) { ex_kind = TACTIC_EX; ex_msg = ex.msg(); } } catch (z3_error & err) { if (i == 0) { ex_kind = ERROR_EX; error_code = err.error_code(); } } catch (z3_exception & z3_ex) { if (i == 0) { ex_kind = DEFAULT_EX; ex_msg = z3_ex.msg(); } } } if (finished_id == UINT_MAX) { mc = 0; switch (ex_kind) { case ERROR_EX: throw z3_error(error_code); case TACTIC_EX: throw tactic_exception(ex_msg.c_str()); default: throw default_exception(ex_msg.c_str()); } } } virtual tactic * translate(ast_manager & m) { return translate_core(m); } }; tactic * par(unsigned num, tactic * const * ts) { return alloc(par_tactical, num, ts); } tactic * par(tactic * t1, tactic * t2) { tactic * ts[2] = { t1, t2 }; return par(2, ts); } tactic * par(tactic * t1, tactic * t2, tactic * t3) { tactic * ts[3] = { t1, t2, t3 }; return par(3, ts); } tactic * par(tactic * t1, tactic * t2, tactic * t3, tactic * t4) { tactic * ts[4] = { t1, t2, t3, t4 }; return par(4, ts); } class par_and_then_tactical : public and_then_tactical { public: par_and_then_tactical(tactic * t1, tactic * t2):and_then_tactical(t1, t2) {} virtual ~par_and_then_tactical() {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { bool use_seq; #ifdef _NO_OMP_ use_seq = true; #else use_seq = 0 != omp_in_parallel(); #endif if (use_seq) { // execute tasks sequentially and_then_tactical::operator()(in, result, mc, pc, core); return; } bool models_enabled = in->models_enabled(); bool proofs_enabled = in->proofs_enabled(); bool cores_enabled = in->unsat_core_enabled(); ast_manager & m = in->m(); goal_ref_buffer r1; model_converter_ref mc1; proof_converter_ref pc1; expr_dependency_ref core1(m); result.reset(); mc = 0; pc = 0; core = 0; m_t1->operator()(in, r1, mc1, pc1, core1); SASSERT(!is_decided(r1) || (!pc1 && !core1)); // the pc and core of decided goals is 0 unsigned r1_size = r1.size(); SASSERT(r1_size > 0); checkpoint(); if (r1_size == 1) { // Only one subgoal created... no need for parallelism if (r1[0]->is_decided()) { result.push_back(r1[0]); if (models_enabled) mc = mc1; SASSERT(!pc); SASSERT(!core); return; } goal_ref r1_0 = r1[0]; m_t2->operator()(r1_0, result, mc, pc, core); if (models_enabled) mc = concat(mc1.get(), mc.get()); if (proofs_enabled) pc = concat(pc1.get(), pc.get()); if (cores_enabled) core = m.mk_join(core1.get(), core); } else { if (cores_enabled) core = core1; scoped_ptr_vector managers; tactic_ref_vector ts2; goal_ref_vector g_copies; for (unsigned i = 0; i < r1_size; i++) { ast_manager * new_m = alloc(ast_manager, m, !m.proof_mode()); managers.push_back(new_m); ast_translation translator(m, *new_m); g_copies.push_back(r1[i]->translate(translator)); ts2.push_back(m_t2->translate(*new_m)); } proof_converter_ref_buffer pc_buffer; model_converter_ref_buffer mc_buffer; scoped_ptr_vector core_buffer; scoped_ptr_vector goals_vect; pc_buffer.resize(r1_size); mc_buffer.resize(r1_size); core_buffer.resize(r1_size); goals_vect.resize(r1_size); bool found_solution = false; bool failed = false; par_exception_kind ex_kind = DEFAULT_EX; unsigned error_code = 0; std::string ex_msg; #pragma omp parallel for for (int i = 0; i < static_cast(r1_size); i++) { ast_manager & new_m = *(managers[i]); goal_ref new_g = g_copies[i]; goal_ref_buffer r2; model_converter_ref mc2; proof_converter_ref pc2; expr_dependency_ref core2(new_m); bool curr_failed = false; try { ts2[i]->operator()(new_g, r2, mc2, pc2, core2); } catch (tactic_exception & ex) { #pragma omp critical (par_and_then_tactical) { if (!failed && !found_solution) { curr_failed = true; failed = true; ex_kind = TACTIC_EX; ex_msg = ex.msg(); } } } catch (z3_error & err) { #pragma omp critical (par_and_then_tactical) { if (!failed && !found_solution) { curr_failed = true; failed = true; ex_kind = ERROR_EX; error_code = err.error_code(); } } } catch (z3_exception & z3_ex) { #pragma omp critical (par_and_then_tactical) { if (!failed && !found_solution) { curr_failed = true; failed = true; ex_kind = DEFAULT_EX; ex_msg = z3_ex.msg(); } } } if (curr_failed) { for (unsigned j = 0; j < r1_size; j++) { if (static_cast(i) != j) ts2.get(j)->cancel(); } } else { if (is_decided(r2)) { SASSERT(r2.size() == 1); if (is_decided_sat(r2)) { // found solution... bool first = false; #pragma omp critical (par_and_then_tactical) { if (!found_solution) { failed = false; found_solution = true; first = true; } } if (first) { for (unsigned j = 0; j < r1_size; j++) { if (static_cast(i) != j) ts2.get(j)->cancel(); } ast_translation translator(new_m, m, false); SASSERT(r2.size() == 1); result.push_back(r2[0]->translate(translator)); if (models_enabled) { // mc2 contains the actual model mc2 = mc2 ? mc2->translate(translator) : 0; model_ref md; md = alloc(model, m); apply(mc2, md, 0); apply(mc1, md, i); mc = model2model_converter(md.get()); } SASSERT(!pc); SASSERT(!core); } } else { SASSERT(is_decided_unsat(r2)); // the proof and unsat core of a decided_unsat goal are stored in the node itself. // pc2 and core2 must be 0. SASSERT(!pc2); SASSERT(!core2); if (models_enabled) mc_buffer.set(i, 0); if (proofs_enabled) { proof * pr = r2[0]->pr(0); pc_buffer.push_back(proof2proof_converter(m, pr)); } if (cores_enabled && r2[0]->dep(0) != 0) { expr_dependency_ref * new_dep = alloc(expr_dependency_ref, new_m); *new_dep = r2[0]->dep(0); core_buffer.set(i, new_dep); } } } else { goal_ref_buffer * new_r2 = alloc(goal_ref_buffer); goals_vect.set(i, new_r2); new_r2->append(r2.size(), r2.c_ptr()); mc_buffer.set(i, mc2.get()); pc_buffer.set(i, pc2.get()); if (cores_enabled && core2 != 0) { expr_dependency_ref * new_dep = alloc(expr_dependency_ref, new_m); *new_dep = core2; core_buffer.set(i, new_dep); } } } } if (failed) { switch (ex_kind) { case ERROR_EX: throw z3_error(error_code); case TACTIC_EX: throw tactic_exception(ex_msg.c_str()); default: throw default_exception(ex_msg.c_str()); } } if (found_solution) return; core = 0; sbuffer sz_buffer; for (unsigned i = 0; i < r1_size; i++) { ast_translation translator(*(managers[i]), m, false); goal_ref_buffer * r = goals_vect[i]; if (r != 0) { for (unsigned k = 0; k < r->size(); k++) { result.push_back((*r)[k]->translate(translator)); } sz_buffer.push_back(r->size()); } else { sz_buffer.push_back(0); } if (mc_buffer[i] != 0) mc_buffer.set(i, mc_buffer[i]->translate(translator)); if (pc_buffer[i] != 0) pc_buffer.set(i, pc_buffer[i]->translate(translator)); expr_dependency_translation td(translator); if (core_buffer[i] != 0) { expr_dependency_ref curr_core(m); curr_core = td(*(core_buffer[i])); core = m.mk_join(curr_core, core); } } if (result.empty()) { // all subgoals were shown to be unsat. // create an decided_unsat goal with the proof in->reset_all(); proof_ref pr(m); if (proofs_enabled) apply(m, pc1, pc_buffer, pr); SASSERT(cores_enabled || core == 0); in->assert_expr(m.mk_false(), pr, core); core = 0; result.push_back(in.get()); SASSERT(!mc); SASSERT(!pc); SASSERT(!core); } else { if (models_enabled) mc = concat(mc1.get(), mc_buffer.size(), mc_buffer.c_ptr(), sz_buffer.c_ptr()); if (proofs_enabled) pc = concat(pc1.get(), pc_buffer.size(), pc_buffer.c_ptr(), sz_buffer.c_ptr()); SASSERT(cores_enabled || core == 0); } } } virtual tactic * translate(ast_manager & m) { return translate_core(m); } }; // Similar to and_then combinator, but t2 is applied in parallel to all subgoals produced by t1 tactic * par_and_then(tactic * t1, tactic * t2) { return alloc(par_and_then_tactical, t1, t2); } tactic * par_and_then(unsigned num, tactic * const * ts) { unsigned i = num - 1; tactic * r = ts[i]; while (i > 0) { --i; r = par_and_then(ts[i], r); } return r; } class unary_tactical : public tactic { protected: tactic * m_t; volatile bool m_cancel; void checkpoint() { if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); } public: unary_tactical(tactic * t): m_t(t), m_cancel(false) { SASSERT(t); t->inc_ref(); } virtual ~unary_tactical() { tactic * t = m_t; #pragma omp critical (tactic_cancel) { m_t = 0; } t->dec_ref(); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { m_t->operator()(in, result, mc, pc, core); } virtual void cleanup(void) { m_t->cleanup(); } virtual void collect_statistics(statistics & st) const { m_t->collect_statistics(st); } virtual void reset_statistics() { m_t->reset_statistics(); } virtual void updt_params(params_ref const & p) { m_t->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { m_t->collect_param_descrs(r); } virtual void reset() { m_t->reset(); } virtual void set_logic(symbol const& l) { m_t->set_logic(l); } virtual void set_progress_callback(progress_callback * callback) { m_t->set_progress_callback(callback); } protected: virtual void set_cancel(bool f) { m_cancel = f; if (m_t) m_t->set_cancel(f); } template tactic * translate_core(ast_manager & m) { tactic * new_t = m_t->translate(m); return alloc(T, new_t); } }; class repeat_tactical : public unary_tactical { unsigned m_max_depth; void operator()(unsigned depth, goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { // TODO: implement a non-recursive version. if (depth > m_max_depth) { result.push_back(in.get()); mc = 0; pc = 0; core = 0; return; } bool models_enabled = in->models_enabled(); bool proofs_enabled = in->proofs_enabled(); bool cores_enabled = in->unsat_core_enabled(); ast_manager & m = in->m(); goal_ref_buffer r1; model_converter_ref mc1; proof_converter_ref pc1; expr_dependency_ref core1(m); result.reset(); mc = 0; pc = 0; core = 0; { goal orig_in(in->m()); orig_in.copy_from(*(in.get())); m_t->operator()(in, r1, mc1, pc1, core1); if (is_equal(orig_in, *(in.get()))) { result.push_back(r1[0]); if (models_enabled) mc = mc1; if (proofs_enabled) pc = pc1; if (cores_enabled) core = core1; return; } } unsigned r1_size = r1.size(); SASSERT(r1_size > 0); checkpoint(); if (r1_size == 1) { if (r1[0]->is_decided()) { result.push_back(r1[0]); if (models_enabled) mc = mc1; SASSERT(!pc); SASSERT(!core); return; } goal_ref r1_0 = r1[0]; operator()(depth+1, r1_0, result, mc, pc, core); if (models_enabled) mc = concat(mc.get(), mc1.get()); if (proofs_enabled) pc = concat(pc.get(), pc1.get()); if (cores_enabled) core = m.mk_join(core1.get(), core); } else { if (cores_enabled) core = core1; proof_converter_ref_buffer pc_buffer; model_converter_ref_buffer mc_buffer; sbuffer sz_buffer; goal_ref_buffer r2; for (unsigned i = 0; i < r1_size; i++) { checkpoint(); goal_ref g = r1[i]; r2.reset(); model_converter_ref mc2; proof_converter_ref pc2; expr_dependency_ref core2(m); operator()(depth+1, g, r2, mc2, pc2, core2); if (is_decided(r2)) { SASSERT(r2.size() == 1); if (is_decided_sat(r2)) { // found solution... result.push_back(r2[0]); if (models_enabled) { // mc2 contains the actual model model_ref md; if (mc2) (*mc2)(md, 0); if (mc1) (*mc1)(md, i); mc = model2model_converter(md.get()); } SASSERT(!pc); SASSERT(!core); return; } else { SASSERT(is_decided_unsat(r2)); SASSERT(!pc2); SASSERT(!core2); if (models_enabled) mc_buffer.push_back(0); if (proofs_enabled) pc_buffer.push_back(proof2proof_converter(m, r2[0]->pr(0))); if (models_enabled || proofs_enabled) sz_buffer.push_back(0); if (cores_enabled) core = m.mk_join(core.get(), r2[0]->dep(0)); } } else { result.append(r2.size(), r2.c_ptr()); if (models_enabled) mc_buffer.push_back(mc2.get()); if (proofs_enabled) pc_buffer.push_back(pc2.get()); if (models_enabled || proofs_enabled) sz_buffer.push_back(r2.size()); if (cores_enabled) core = m.mk_join(core.get(), core2.get()); } } if (result.empty()) { // all subgoals were shown to be unsat. // create an decided_unsat goal with the proof in->reset_all(); proof_ref pr(m); if (proofs_enabled) apply(m, pc1, pc_buffer, pr); SASSERT(cores_enabled || core == 0); in->assert_expr(m.mk_false(), pr, core); core = 0; result.push_back(in.get()); SASSERT(!mc); SASSERT(!pc); SASSERT(!core); } else { if (models_enabled) mc = concat(mc1.get(), mc_buffer.size(), mc_buffer.c_ptr(), sz_buffer.c_ptr()); if (proofs_enabled) pc = concat(pc1.get(), pc_buffer.size(), pc_buffer.c_ptr(), sz_buffer.c_ptr()); SASSERT(cores_enabled || core == 0); } } } public: repeat_tactical(tactic * t, unsigned max_depth): unary_tactical(t), m_max_depth(max_depth) { } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { operator()(0, in, result, mc, pc, core); } virtual tactic * translate(ast_manager & m) { tactic * new_t = m_t->translate(m); return alloc(repeat_tactical, new_t, m_max_depth); } }; tactic * repeat(tactic * t, unsigned max) { return alloc(repeat_tactical, t, max); } class fail_if_branching_tactical : public unary_tactical { unsigned m_threshold; public: fail_if_branching_tactical(tactic * t, unsigned threshold):unary_tactical(t), m_threshold(threshold) {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { m_t->operator()(in, result, mc, pc, core); if (result.size() > m_threshold) { result.reset(); mc = 0; pc = 0; core = 0; throw tactic_exception("failed-if-branching tactical"); } }; virtual tactic * translate(ast_manager & m) { tactic * new_t = m_t->translate(m); return alloc(fail_if_branching_tactical, new_t, m_threshold); } }; tactic * fail_if_branching(tactic * t, unsigned threshold) { return alloc(fail_if_branching_tactical, t, threshold); } class cleanup_tactical : public unary_tactical { public: cleanup_tactical(tactic * t):unary_tactical(t) {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { m_t->operator()(in, result, mc, pc, core); m_t->cleanup(); } virtual tactic * translate(ast_manager & m) { tactic * new_t = m_t->translate(m); return alloc(cleanup_tactical, new_t); } }; tactic * clean(tactic * t) { return alloc(cleanup_tactical, t); } class try_for_tactical : public unary_tactical { unsigned m_timeout; public: try_for_tactical(tactic * t, unsigned ts):unary_tactical(t), m_timeout(ts) {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { cancel_eh eh(*m_t); { // Warning: scoped_timer is not thread safe in Linux. scoped_timer timer(m_timeout, &eh); m_t->operator()(in, result, mc, pc, core); } } virtual tactic * translate(ast_manager & m) { tactic * new_t = m_t->translate(m); return alloc(try_for_tactical, new_t, m_timeout); } }; tactic * try_for(tactic * t, unsigned msecs) { return alloc(try_for_tactical, t, msecs); } class using_params_tactical : public unary_tactical { params_ref m_params; public: using_params_tactical(tactic * t, params_ref const & p):unary_tactical(t), m_params(p) { t->updt_params(p); } virtual void updt_params(params_ref const & p) { TRACE("using_params", tout << "before p: " << p << "\n"; tout << "m_params: " << m_params << "\n";); params_ref new_p = p; new_p.append(m_params); unary_tactical::updt_params(new_p); TRACE("using_params", tout << "after p: " << p << "\n"; tout << "m_params: " << m_params << "\n"; tout << "new_p: " << new_p << "\n";); } virtual tactic * translate(ast_manager & m) { tactic * new_t = m_t->translate(m); return alloc(using_params_tactical, new_t, m_params); } }; tactic * using_params(tactic * t, params_ref const & p) { return alloc(using_params_tactical, t, p); } class cond_tactical : public binary_tactical { probe * m_p; public: cond_tactical(probe * p, tactic * t1, tactic * t2): binary_tactical(t1, t2), m_p(p) { SASSERT(m_p); m_p->inc_ref(); } ~cond_tactical() { m_p->dec_ref(); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { if (m_p->operator()(*(in.get())).is_true()) m_t1->operator()(in, result, mc, pc, core); else m_t2->operator()(in, result, mc, pc, core); } virtual tactic * translate(ast_manager & m) { tactic * new_t1 = m_t1->translate(m); tactic * new_t2 = m_t2->translate(m); return alloc(cond_tactical, m_p, new_t1, new_t2); } }; tactic * cond(probe * p, tactic * t1, tactic * t2) { return alloc(cond_tactical, p, t1, t2); } tactic * when(probe * p, tactic * t) { return cond(p, t, mk_skip_tactic()); } class fail_if_tactic : public tactic { probe * m_p; public: fail_if_tactic(probe * p): m_p(p) { SASSERT(m_p); m_p->inc_ref(); } ~fail_if_tactic() { m_p->dec_ref(); } void cleanup() {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { mc = 0; pc = 0; core = 0; if (m_p->operator()(*(in.get())).is_true()) { throw tactic_exception("fail-if tactic"); } result.push_back(in.get()); } virtual tactic * translate(ast_manager & m) { return this; } }; tactic * fail_if(probe * p) { return alloc(fail_if_tactic, p); } tactic * fail_if_not(probe * p) { return fail_if(mk_not(p)); } class if_no_proofs_tactical : public unary_tactical { public: if_no_proofs_tactical(tactic * t):unary_tactical(t) {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { if (in->proofs_enabled()) { mc = 0; pc = 0; core = 0; result.reset(); result.push_back(in.get()); } else { m_t->operator()(in, result, mc, pc, core); } } virtual tactic * translate(ast_manager & m) { return translate_core(m); } }; class if_no_unsat_cores_tactical : public unary_tactical { public: if_no_unsat_cores_tactical(tactic * t):unary_tactical(t) {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { if (in->unsat_core_enabled()) { mc = 0; pc = 0; core = 0; result.reset(); result.push_back(in.get()); } else { m_t->operator()(in, result, mc, pc, core); } } virtual tactic * translate(ast_manager & m) { return translate_core(m); } }; class if_no_models_tactical : public unary_tactical { public: if_no_models_tactical(tactic * t):unary_tactical(t) {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { if (in->models_enabled()) { mc = 0; pc = 0; core = 0; result.reset(); result.push_back(in.get()); } else { m_t->operator()(in, result, mc, pc, core); } } virtual tactic * translate(ast_manager & m) { return translate_core(m); } }; tactic * if_no_proofs(tactic * t) { return alloc(if_no_proofs_tactical, t); } tactic * if_no_unsat_cores(tactic * t) { return alloc(if_no_unsat_cores_tactical, t); } tactic * if_no_models(tactic * t) { return alloc(if_no_models_tactical, t); } tactic * skip_if_failed(tactic * t) { return or_else(t, mk_skip_tactic()); } z3-4.4.0/src/tactic/smtlogics/0000755000175000017500000000000012540347414015707 5ustar michaelmichaelz3-4.4.0/src/tactic/smtlogics/qfidl_tactic.h0000644000175000017500000000055512540347414020513 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfidl_tactic.h Abstract: Tactic for QF_IDL Author: Leonardo (leonardo) 2012-02-21 Notes: --*/ #ifndef _QFIDL_TACTIC_H_ #define _QFIDL_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_qfidl_tactic(ast_manager & m, params_ref const & p = params_ref()); #endif z3-4.4.0/src/tactic/smtlogics/qfnia_tactic.h0000644000175000017500000000071712540347414020512 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfnia_tactic.h Abstract: Tactic for QF_NIA Author: Leonardo (leonardo) 2012-02-28 Notes: --*/ #ifndef _QFNIA_TACTIC_ #define _QFNIA_TACTIC_ #include"params.h" class ast_manager; class tactic; tactic * mk_qfnia_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qfnia", "builtin strategy for solving QF_NIA problems.", "mk_qfnia_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/smtlogics/qfufnra_tactic.cpp0000644000175000017500000000261512540347414021410 0ustar michaelmichael/*++ Copyright (c) 2015 Microsoft Corporation Module Name: qfufnra_tactic.cpp Abstract: Tactic for QF_UFNRA Author: Nikolaj (nbjorner) 2015-05-05 Notes: --*/ #include"tactical.h" #include"simplify_tactic.h" #include"propagate_values_tactic.h" #include"nl_purify_tactic.h" #include"qfufnra_tactic.h" #include"purify_arith_tactic.h" #include"solve_eqs_tactic.h" #include"elim_term_ite_tactic.h" #include"elim_uncnstr_tactic.h" #include"simplify_tactic.h" #include"nnf_tactic.h" #include"tseitin_cnf_tactic.h" tactic * mk_qfufnra_tactic(ast_manager & m, params_ref const& p) { params_ref main_p = p; main_p.set_bool("elim_and", true); main_p.set_bool("blast_distinct", true); return and_then(and_then(using_params(mk_simplify_tactic(m, p), main_p), mk_purify_arith_tactic(m, p), mk_propagate_values_tactic(m, p), mk_solve_eqs_tactic(m, p), mk_elim_uncnstr_tactic(m, p)), and_then(mk_elim_term_ite_tactic(m, p), mk_solve_eqs_tactic(m, p), using_params(mk_simplify_tactic(m, p), main_p), mk_tseitin_cnf_core_tactic(m, p), using_params(mk_simplify_tactic(m, p), main_p), mk_nl_purify_tactic(m, p))); } z3-4.4.0/src/tactic/smtlogics/qfbv_tactic.cpp0000644000175000017500000001121412540347414020677 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfbv_tactic.cpp Abstract: Tactic for QF_BV based on bit-blasting Author: Leonardo (leonardo) 2012-02-22 Notes: --*/ #include"tactical.h" #include"simplify_tactic.h" #include"propagate_values_tactic.h" #include"solve_eqs_tactic.h" #include"elim_uncnstr_tactic.h" #include"smt_tactic.h" #include"bit_blaster_tactic.h" #include"bv1_blaster_tactic.h" #include"max_bv_sharing_tactic.h" #include"bv_size_reduction_tactic.h" #include"aig_tactic.h" #include"sat_tactic.h" #define MEMLIMIT 300 tactic * mk_qfbv_preamble(ast_manager& m, params_ref const& p) { params_ref solve_eq_p; // conservative guassian elimination. solve_eq_p.set_uint("solve_eqs_max_occs", 2); params_ref simp2_p = p; simp2_p.set_bool("som", true); simp2_p.set_bool("pull_cheap_ite", true); simp2_p.set_bool("push_ite_bv", false); simp2_p.set_bool("local_ctx", true); simp2_p.set_uint("local_ctx_limit", 10000000); simp2_p.set_bool("flat", true); // required by som simp2_p.set_bool("hoist_mul", false); // required by som params_ref hoist_p; hoist_p.set_bool("hoist_mul", true); hoist_p.set_bool("som", false); return and_then( mk_simplify_tactic(m), mk_propagate_values_tactic(m), using_params(mk_solve_eqs_tactic(m), solve_eq_p), mk_elim_uncnstr_tactic(m), if_no_proofs(if_no_unsat_cores(mk_bv_size_reduction_tactic(m))), using_params(mk_simplify_tactic(m), simp2_p), // // Z3 can solve a couple of extra benchmarks by using hoist_mul // but the timeout in SMT-COMP is too small. // Moreover, it impacted negatively some easy benchmarks. // We should decide later, if we keep it or not. // using_params(mk_simplify_tactic(m), hoist_p), mk_max_bv_sharing_tactic(m)); } static tactic * main_p(tactic* t) { params_ref p; p.set_bool("elim_and", true); p.set_bool("push_ite_bv", true); p.set_bool("blast_distinct", true); return using_params(t, p); } tactic * mk_qfbv_tactic(ast_manager& m, params_ref const & p, tactic* sat, tactic* smt) { params_ref local_ctx_p = p; local_ctx_p.set_bool("local_ctx", true); params_ref solver_p; solver_p.set_bool("preprocess", false); // preprocessor of smt::context is not needed. params_ref no_flat_p; no_flat_p.set_bool("flat", false); params_ref ctx_simp_p; ctx_simp_p.set_uint("max_depth", 32); ctx_simp_p.set_uint("max_steps", 50000000); params_ref big_aig_p; big_aig_p.set_bool("aig_per_assertion", false); tactic* preamble_st = mk_qfbv_preamble(m, p); tactic * st = main_p(and_then(preamble_st, // If the user sets HI_DIV0=false, then the formula may contain uninterpreted function // symbols. In this case, we should not use cond(mk_is_qfbv_probe(), cond(mk_is_qfbv_eq_probe(), and_then(mk_bv1_blaster_tactic(m), using_params(smt, solver_p)), and_then(mk_bit_blaster_tactic(m), when(mk_lt(mk_memory_probe(), mk_const_probe(MEMLIMIT)), and_then(using_params(and_then(mk_simplify_tactic(m), mk_solve_eqs_tactic(m)), local_ctx_p), if_no_proofs(cond(mk_produce_unsat_cores_probe(), mk_aig_tactic(), using_params(mk_aig_tactic(), big_aig_p))))), sat)), smt))); st->updt_params(p); return st; } tactic * mk_qfbv_tactic(ast_manager & m, params_ref const & p) { tactic * new_sat = cond(mk_produce_proofs_probe(), and_then(mk_simplify_tactic(m), mk_smt_tactic()), mk_sat_tactic(m)); return mk_qfbv_tactic(m, p, new_sat, mk_smt_tactic()); } z3-4.4.0/src/tactic/smtlogics/quant_tactics.h0000644000175000017500000000153612540347414020727 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: quant_tactics.h Abstract: Tactics for benchmarks containing quantifiers. Author: Leonardo de Moura (leonardo) 2012-02-21. Revision History: --*/ #ifndef _QUANT_TACTICS_H_ #define _QUANT_TACTICS_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_ufnia_tactic(ast_manager & m, params_ref const & p); tactic * mk_uflra_tactic(ast_manager & m, params_ref const & p); tactic * mk_auflia_tactic(ast_manager & m, params_ref const & p); tactic * mk_auflira_tactic(ast_manager & m, params_ref const & p); tactic * mk_aufnira_tactic(ast_manager & m, params_ref const & p); tactic * mk_lra_tactic(ast_manager & m, params_ref const & p); tactic * mk_lia_tactic(ast_manager & m, params_ref const & p); tactic * mk_lira_tactic(ast_manager & m, params_ref const & p); #endif z3-4.4.0/src/tactic/smtlogics/qfauflia_tactic.cpp0000644000175000017500000000253412540347414021536 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfauflia_tactic.cpp Abstract: Tactic for QF_AUFLIA Author: Leonardo (leonardo) 2012-02-21 Notes: --*/ #include"tactical.h" #include"simplify_tactic.h" #include"propagate_values_tactic.h" #include"propagate_ineqs_tactic.h" #include"solve_eqs_tactic.h" #include"elim_uncnstr_tactic.h" #include"smt_tactic.h" tactic * mk_qfauflia_tactic(ast_manager & m, params_ref const & p) { params_ref main_p; main_p.set_bool("elim_and", true); main_p.set_bool("som", true); main_p.set_bool("sort_store", true); params_ref ctx_simp_p; ctx_simp_p.set_uint("max_depth", 30); ctx_simp_p.set_uint("max_steps", 5000000); params_ref solver_p; solver_p.set_bool("array.simplify", false); // disable array simplifications at old_simplify module tactic * preamble_st = and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), mk_solve_eqs_tactic(m), mk_elim_uncnstr_tactic(m), mk_simplify_tactic(m) ); tactic * st = and_then(using_params(preamble_st, main_p), using_params(mk_smt_tactic(), solver_p)); st->updt_params(p); return st; } z3-4.4.0/src/tactic/smtlogics/qfuf_tactic.cpp0000644000175000017500000000162212540347414020704 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfuf_tactic.cpp Abstract: Tactic for QF_QFUF benchmarks. Author: Leonardo de Moura (leonardo) 2012-02-21 Notes: --*/ #include"tactical.h" #include"simplify_tactic.h" #include"symmetry_reduce_tactic.h" #include"solve_eqs_tactic.h" #include"propagate_values_tactic.h" #include"smt_tactic.h" tactic * mk_qfuf_tactic(ast_manager & m, params_ref const & p) { params_ref s2_p; s2_p.set_bool("pull_cheap_ite", true); s2_p.set_bool("local_ctx", true); s2_p.set_uint("local_ctx_limit", 10000000); return and_then(mk_simplify_tactic(m, p), mk_propagate_values_tactic(m, p), mk_solve_eqs_tactic(m, p), using_params(mk_simplify_tactic(m, p), s2_p), mk_symmetry_reduce_tactic(m, p), mk_smt_tactic(p)); } z3-4.4.0/src/tactic/smtlogics/qfbv_tactic.h0000644000175000017500000000117312540347414020347 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfbv_tactic.h Abstract: Tactic for QF_BV based on bit-blasting Author: Leonardo (leonardo) 2012-02-22 Notes: --*/ #ifndef _QFBV_TACTIC_ #define _QFBV_TACTIC_ #include"params.h" class ast_manager; class tactic; tactic * mk_qfbv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qfbv", "builtin strategy for solving QF_BV problems.", "mk_qfbv_tactic(m, p)") */ tactic * mk_qfbv_preamble(ast_manager& m, params_ref const& p); tactic * mk_qfbv_tactic(ast_manager & m, params_ref const & p, tactic* sat, tactic* smt); #endif z3-4.4.0/src/tactic/smtlogics/qfnra_tactic.h0000644000175000017500000000071712540347414020523 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfnra_tactic.h Abstract: Tactic for QF_NRA Author: Leonardo (leonardo) 2012-02-28 Notes: --*/ #ifndef _QFNRA_TACTIC_ #define _QFNRA_TACTIC_ #include"params.h" class ast_manager; class tactic; tactic * mk_qfnra_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qfnra", "builtin strategy for solving QF_NRA problems.", "mk_qfnra_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/smtlogics/qfufbv_tactic.cpp0000644000175000017500000000271412540347414021237 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfufbv_tactic.cpp Abstract: Tactic for QF_UFBV Author: Leonardo (leonardo) 2012-02-27 Notes: --*/ #include"tactical.h" #include"simplify_tactic.h" #include"propagate_values_tactic.h" #include"solve_eqs_tactic.h" #include"elim_uncnstr_tactic.h" #include"smt_tactic.h" #include"max_bv_sharing_tactic.h" #include"bv_size_reduction_tactic.h" #include"reduce_args_tactic.h" tactic * mk_qfufbv_tactic(ast_manager & m, params_ref const & p) { params_ref main_p; main_p.set_bool("elim_and", true); main_p.set_bool("blast_distinct", true); tactic * preamble_st = and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), mk_solve_eqs_tactic(m), mk_elim_uncnstr_tactic(m), if_no_proofs(if_no_unsat_cores(mk_reduce_args_tactic(m))), if_no_proofs(if_no_unsat_cores(mk_bv_size_reduction_tactic(m))), mk_max_bv_sharing_tactic(m) ); tactic * st = using_params(and_then(preamble_st, mk_smt_tactic()), main_p); //cond(is_qfbv(), // and_then(mk_bit_blaster(m), // mk_sat_solver(m)), // mk_smt_solver()) st->updt_params(p); return st; } z3-4.4.0/src/tactic/smtlogics/quant_tactics.cpp0000644000175000017500000000725112540347414021262 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: quant_tactics.cpp Abstract: Tactics for benchmarks containing quantifiers. Author: Leonardo de Moura (leonardo) 2012-02-21. Revision History: --*/ #include"tactical.h" #include"simplify_tactic.h" #include"propagate_values_tactic.h" #include"solve_eqs_tactic.h" #include"elim_uncnstr_tactic.h" #include"qe_tactic.h" #include"qe_sat_tactic.h" #include"ctx_simplify_tactic.h" #include"smt_tactic.h" static tactic * mk_quant_preprocessor(ast_manager & m, bool disable_gaussian = false) { params_ref pull_ite_p; pull_ite_p.set_bool("pull_cheap_ite", true); pull_ite_p.set_bool("local_ctx", true); pull_ite_p.set_uint("local_ctx_limit", 10000000); params_ref ctx_simp_p; ctx_simp_p.set_uint("max_depth", 30); ctx_simp_p.set_uint("max_steps", 5000000); tactic * solve_eqs; if (disable_gaussian) solve_eqs = mk_skip_tactic(); else solve_eqs = when(mk_not(mk_has_pattern_probe()), mk_solve_eqs_tactic(m)); // remark: investigate if gaussian elimination is useful when patterns are not provided. return and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), using_params(mk_ctx_simplify_tactic(m), ctx_simp_p), using_params(mk_simplify_tactic(m), pull_ite_p), solve_eqs, mk_elim_uncnstr_tactic(m), mk_simplify_tactic(m)); } static tactic * mk_no_solve_eq_preprocessor(ast_manager & m) { return mk_quant_preprocessor(m, true); } tactic * mk_ufnia_tactic(ast_manager & m, params_ref const & p) { tactic * st = and_then(mk_no_solve_eq_preprocessor(m), mk_smt_tactic()); st->updt_params(p); return st; } tactic * mk_uflra_tactic(ast_manager & m, params_ref const & p) { tactic * st = and_then(mk_quant_preprocessor(m), mk_smt_tactic()); st->updt_params(p); return st; } tactic * mk_auflia_tactic(ast_manager & m, params_ref const & p) { params_ref qi_p; qi_p.set_str("qi.cost", "0"); TRACE("qi_cost", qi_p.display(tout); tout << "\n" << qi_p.get_str("qi.cost", "") << "\n";); tactic * st = and_then(mk_no_solve_eq_preprocessor(m), or_else(and_then(fail_if(mk_gt(mk_num_exprs_probe(), mk_const_probe(static_cast(128)))), using_params(mk_smt_tactic(), qi_p), mk_fail_if_undecided_tactic()), mk_smt_tactic())); st->updt_params(p); return st; } tactic * mk_auflira_tactic(ast_manager & m, params_ref const & p) { tactic * st = and_then(mk_quant_preprocessor(m), mk_smt_tactic()); st->updt_params(p); return st; } tactic * mk_aufnira_tactic(ast_manager & m, params_ref const & p) { tactic * st = and_then(mk_quant_preprocessor(m), mk_smt_tactic()); st->updt_params(p); return st; } tactic * mk_lra_tactic(ast_manager & m, params_ref const & p) { tactic * st = and_then(mk_quant_preprocessor(m), or_else(try_for(mk_smt_tactic(), 100), try_for(qe::mk_sat_tactic(m), 1000), try_for(mk_smt_tactic(), 1000), and_then(mk_qe_tactic(m), mk_smt_tactic()))); st->updt_params(p); return st; } tactic * mk_lia_tactic(ast_manager & m, params_ref const & p) { return mk_lra_tactic(m, p); } tactic * mk_lira_tactic(ast_manager & m, params_ref const & p) { return mk_lra_tactic(m, p); } z3-4.4.0/src/tactic/smtlogics/qfnra_tactic.cpp0000644000175000017500000000267712540347414021065 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfnra_tactic.cpp Abstract: Tactic for QF_NRA Author: Leonardo (leonardo) 2012-02-28 Notes: --*/ #include"tactical.h" #include"simplify_tactic.h" #include"propagate_values_tactic.h" #include"nla2bv_tactic.h" #include"smt_tactic.h" #include"qfnra_nlsat_tactic.h" static tactic * mk_qfnra_sat_solver(ast_manager& m, params_ref const& p, unsigned bv_size) { params_ref nra2sat_p = p; nra2sat_p.set_uint("nla2bv_max_bv_size", p.get_uint("nla2bv_max_bv_size", bv_size)); return and_then(mk_nla2bv_tactic(m, nra2sat_p), mk_smt_tactic(), mk_fail_if_undecided_tactic()); } tactic * mk_qfnra_tactic(ast_manager & m, params_ref const& p) { params_ref p1 = p; p1.set_uint("seed", 11); p1.set_bool("factor", false); params_ref p2 = p; p2.set_uint("seed", 13); p2.set_bool("factor", false); return and_then(mk_simplify_tactic(m, p), mk_propagate_values_tactic(m, p), or_else(try_for(mk_qfnra_nlsat_tactic(m, p), 5000), try_for(mk_qfnra_nlsat_tactic(m, p1), 10000), mk_qfnra_sat_solver(m, p, 4), and_then(try_for(mk_smt_tactic(), 5000), mk_fail_if_undecided_tactic()), mk_qfnra_sat_solver(m, p, 6), mk_qfnra_nlsat_tactic(m, p2))); } z3-4.4.0/src/tactic/smtlogics/qfauflia_tactic.h0000644000175000017500000000057412540347414021205 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfauflia_tactic.h Abstract: Tactic for QF_AUFLIA Author: Leonardo (leonardo) 2012-02-21 Notes: --*/ #ifndef _QFAUFLIA_TACTIC_H_ #define _QFAUFLIA_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_qfauflia_tactic(ast_manager & m, params_ref const & p = params_ref()); #endif z3-4.4.0/src/tactic/smtlogics/qfaufbv_tactic.cpp0000644000175000017500000000373312540347414021402 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfaufbv_tactic.cpp Abstract: Tactic for QF_AUFBV benchmarks. Author: Leonardo (leonardo) 2012-02-23 Notes: --*/ #include"solve_eqs_tactic.h" #include"simplify_tactic.h" #include"propagate_values_tactic.h" #include"bit_blaster_tactic.h" #include"elim_uncnstr_tactic.h" #include"max_bv_sharing_tactic.h" #include"bv_size_reduction_tactic.h" #include"ctx_simplify_tactic.h" #include"sat_tactic.h" #include"smt_tactic.h" tactic * mk_qfaufbv_tactic(ast_manager & m, params_ref const & p) { params_ref main_p; main_p.set_bool("elim_and", true); main_p.set_bool("sort_store", true); params_ref simp2_p = p; simp2_p.set_bool("som", true); simp2_p.set_bool("pull_cheap_ite", true); simp2_p.set_bool("push_ite_bv", false); simp2_p.set_bool("local_ctx", true); simp2_p.set_uint("local_ctx_limit", 10000000); params_ref ctx_simp_p; ctx_simp_p.set_uint("max_depth", 32); ctx_simp_p.set_uint("max_steps", 5000000); params_ref solver_p; solver_p.set_bool("array.simplify", false); // disable array simplifications at old_simplify module tactic * preamble_st = and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), // using_params(mk_ctx_simplify_tactic(m), ctx_simp_p), mk_solve_eqs_tactic(m), mk_elim_uncnstr_tactic(m), if_no_proofs(if_no_unsat_cores(mk_bv_size_reduction_tactic(m))), using_params(mk_simplify_tactic(m), simp2_p), mk_max_bv_sharing_tactic(m) ); tactic * st = using_params(and_then(preamble_st, using_params(mk_smt_tactic(), solver_p)), main_p); st->updt_params(p); return st; } z3-4.4.0/src/tactic/smtlogics/qfufbv_tactic.h0000644000175000017500000000055712540347414020707 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfufbv_tactic.h Abstract: Tactic for QF_UFBV Author: Leonardo (leonardo) 2012-02-27 Notes: --*/ #ifndef _QFUFBV_TACTIC_ #define _QFUFBV_TACTIC_ #include"params.h" class ast_manager; class tactic; tactic * mk_qfufbv_tactic(ast_manager & m, params_ref const & p = params_ref()); #endif z3-4.4.0/src/tactic/smtlogics/qflia_tactic.h0000644000175000017500000000111612540347414020502 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qflia_tactic.h Abstract: Tactic for QF_LRA Author: Leonardo (leonardo) 2012-02-26 Notes: --*/ #ifndef _QFLIA_TACTIC_ #define _QFLIA_TACTIC_ #include"params.h" class ast_manager; class tactic; tactic * mk_qflia_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qflia", "builtin strategy for solving QF_LIA problems.", "mk_qflia_tactic(m, p)") */ probe * mk_is_quasi_pb_probe(); /* ADD_PROBE("is-quasi-pb", "true if the goal is quasi-pb.", "mk_is_quasi_pb_probe()") */ #endif z3-4.4.0/src/tactic/smtlogics/qflra_tactic.h0000644000175000017500000000071712540347414020521 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qflra_tactic.h Abstract: Tactic for QF_LRA Author: Leonardo (leonardo) 2012-02-26 Notes: --*/ #ifndef _QFLRA_TACTIC_ #define _QFLRA_TACTIC_ #include"params.h" class ast_manager; class tactic; tactic * mk_qflra_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qflra", "builtin strategy for solving QF_LRA problems.", "mk_qflra_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/smtlogics/qflra_tactic.cpp0000644000175000017500000000452012540347414021050 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qflra_tactic.cpp Abstract: Tactic for QF_LRA Author: Leonardo (leonardo) 2012-02-26 Notes: --*/ #include"tactical.h" #include"simplify_tactic.h" #include"propagate_values_tactic.h" #include"solve_eqs_tactic.h" #include"elim_uncnstr_tactic.h" #include"smt_tactic.h" // include"mip_tactic.h" #include"recover_01_tactic.h" #include"ctx_simplify_tactic.h" #include"probe_arith.h" tactic * mk_qflra_tactic(ast_manager & m, params_ref const & p) { params_ref pivot_p; pivot_p.set_bool("arith.greatest_error_pivot", true); params_ref main_p = p; main_p.set_bool("elim_and", true); main_p.set_bool("som", true); main_p.set_bool("blast_distinct", true); params_ref ctx_simp_p; ctx_simp_p.set_uint("max_depth", 30); ctx_simp_p.set_uint("max_steps", 5000000); params_ref lhs_p; lhs_p.set_bool("arith_lhs", true); lhs_p.set_bool("eq2ineq", true); params_ref elim_to_real_p; elim_to_real_p.set_bool("elim_to_real", true); #if 0 tactic * mip = and_then(fail_if(mk_produce_proofs_probe()), fail_if(mk_produce_unsat_cores_probe()), using_params(and_then(and_then(mk_simplify_tactic(m), mk_recover_01_tactic(m), using_params(mk_simplify_tactic(m), elim_to_real_p), mk_propagate_values_tactic(m)), using_params(mk_ctx_simplify_tactic(m), ctx_simp_p), mk_elim_uncnstr_tactic(m), mk_solve_eqs_tactic(m), using_params(mk_simplify_tactic(m), lhs_p), using_params(mk_simplify_tactic(m), elim_to_real_p) ), main_p), fail_if(mk_not(mk_is_mip_probe())), try_for(mk_mip_tactic(m), 30000), mk_fail_if_undecided_tactic()); #endif // return using_params(or_else(mip, // using_params(mk_smt_tactic(), pivot_p)), // p); return using_params(using_params(mk_smt_tactic(), pivot_p), p); } z3-4.4.0/src/tactic/smtlogics/qfnia_tactic.cpp0000644000175000017500000000544112540347414021044 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qflia_tactic.cpp Abstract: Tactic for QF_NIA Author: Leonardo (leonardo) 2012-02-28 Notes: --*/ #include"tactical.h" #include"simplify_tactic.h" #include"propagate_values_tactic.h" #include"solve_eqs_tactic.h" #include"elim_uncnstr_tactic.h" #include"smt_tactic.h" #include"bit_blaster_tactic.h" #include"max_bv_sharing_tactic.h" #include"sat_tactic.h" #include"nla2bv_tactic.h" #include"ctx_simplify_tactic.h" #include"cofactor_term_ite_tactic.h" tactic * mk_qfnia_bv_solver(ast_manager & m, params_ref const & p_ref) { params_ref p = p_ref; p.set_bool("flat", false); p.set_bool("hi_div0", true); p.set_bool("elim_and", true); p.set_bool("blast_distinct", true); params_ref simp2_p = p; simp2_p.set_bool("local_ctx", true); simp2_p.set_uint("local_ctx_limit", 10000000); tactic * r = using_params(and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), using_params(mk_simplify_tactic(m), simp2_p), mk_max_bv_sharing_tactic(m), mk_bit_blaster_tactic(m), mk_sat_tactic(m)), p); return r; } tactic * mk_qfnia_premable(ast_manager & m, params_ref const & p_ref) { params_ref pull_ite_p = p_ref; pull_ite_p.set_bool("pull_cheap_ite", true); pull_ite_p.set_bool("local_ctx", true); pull_ite_p.set_uint("local_ctx_limit", 10000000); params_ref ctx_simp_p = p_ref; ctx_simp_p.set_uint("max_depth", 30); ctx_simp_p.set_uint("max_steps", 5000000); params_ref simp_p = p_ref; simp_p.set_bool("hoist_mul", true); params_ref elim_p = p_ref; elim_p.set_uint("max_memory",20); return and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), using_params(mk_ctx_simplify_tactic(m), ctx_simp_p), using_params(mk_simplify_tactic(m), pull_ite_p), mk_elim_uncnstr_tactic(m), skip_if_failed(using_params(mk_cofactor_term_ite_tactic(m), elim_p)), using_params(mk_simplify_tactic(m), simp_p)); } tactic * mk_qfnia_sat_solver(ast_manager & m, params_ref const & p) { params_ref nia2sat_p = p; nia2sat_p.set_uint("nla2bv_max_bv_size", 64); return and_then(mk_nla2bv_tactic(m, nia2sat_p), mk_qfnia_bv_solver(m, p), mk_fail_if_undecided_tactic()); } tactic * mk_qfnia_tactic(ast_manager & m, params_ref const & p) { return and_then(mk_qfnia_premable(m, p), or_else(mk_qfnia_sat_solver(m, p), mk_smt_tactic())); } z3-4.4.0/src/tactic/smtlogics/qflia_tactic.cpp0000644000175000017500000002072712540347414021046 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qflia_tactic.cpp Abstract: Tactic for QF_LIA Author: Leonardo (leonardo) 2012-02-26 Notes: --*/ #include"tactical.h" #include"simplify_tactic.h" #include"propagate_values_tactic.h" #include"propagate_ineqs_tactic.h" #include"normalize_bounds_tactic.h" #include"solve_eqs_tactic.h" #include"elim_uncnstr_tactic.h" #include"smt_tactic.h" // include"mip_tactic.h" #include"add_bounds_tactic.h" #include"pb2bv_tactic.h" #include"lia2pb_tactic.h" #include"ctx_simplify_tactic.h" #include"bit_blaster_tactic.h" #include"max_bv_sharing_tactic.h" #include"aig_tactic.h" #include"sat_tactic.h" #include"bound_manager.h" #include"probe_arith.h" struct quasi_pb_probe : public probe { virtual result operator()(goal const & g) { bool found_non_01 = false; bound_manager bm(g.m()); bm(g); rational l, u; bool st; bound_manager::iterator it = bm.begin(); bound_manager::iterator end = bm.end(); for (; it != end; ++it) { expr * t = *it; if (bm.has_lower(t, l, st) && bm.has_upper(t, u, st) && (l.is_zero() || l.is_one()) && (u.is_zero() || u.is_one())) continue; if (found_non_01) return false; found_non_01 = true; } return true; } }; probe * mk_is_quasi_pb_probe() { return mk_and(mk_not(mk_is_unbounded_probe()), alloc(quasi_pb_probe)); } // Create SMT solver that does not use cuts static tactic * mk_no_cut_smt_tactic(unsigned rs) { params_ref solver_p; solver_p.set_uint("arith.branch_cut_ratio", 10000000); solver_p.set_uint("random_seed", rs); return using_params(mk_smt_tactic_using(false), solver_p); } // Create SMT solver that does not use cuts static tactic * mk_no_cut_no_relevancy_smt_tactic(unsigned rs) { params_ref solver_p; solver_p.set_uint("arith.branch_cut_ratio", 10000000); solver_p.set_uint("random_seed", rs); solver_p.set_uint("relevancy", 0); return using_params(mk_smt_tactic_using(false), solver_p); } static tactic * mk_bv2sat_tactic(ast_manager & m) { params_ref solver_p; // The cardinality constraint encoding generates a lot of shared if-then-else's that can be flattened. // Several of them are simplified to and/or. If we flat them, we increase a lot the memory consumption. solver_p.set_bool("flat", false); solver_p.set_bool("som", false); // dynamic psm seems to work well. solver_p.set_sym("gc", symbol("dyn_psm")); return using_params(and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), mk_solve_eqs_tactic(m), mk_max_bv_sharing_tactic(m), mk_bit_blaster_tactic(m), mk_aig_tactic(), mk_sat_tactic(m)), solver_p); } #define SMALL_SIZE 80000 static tactic * mk_pb_tactic(ast_manager & m) { params_ref pb2bv_p; pb2bv_p.set_uint("pb2bv_all_clauses_limit", 8); params_ref bv2sat_p; bv2sat_p.set_bool("ite_extra", true); return and_then(fail_if_not(mk_is_pb_probe()), fail_if(mk_produce_proofs_probe()), fail_if(mk_produce_unsat_cores_probe()), or_else(and_then(fail_if(mk_ge(mk_num_exprs_probe(), mk_const_probe(SMALL_SIZE))), fail_if_not(mk_is_ilp_probe()), // try_for(mk_mip_tactic(m), 8000), mk_fail_if_undecided_tactic()), and_then(using_params(mk_pb2bv_tactic(m), pb2bv_p), fail_if_not(mk_is_qfbv_probe()), using_params(mk_bv2sat_tactic(m), bv2sat_p)))); } static tactic * mk_lia2sat_tactic(ast_manager & m) { params_ref pb2bv_p; pb2bv_p.set_uint("pb2bv_all_clauses_limit", 8); params_ref bv2sat_p; bv2sat_p.set_bool("ite_extra", true); return and_then(fail_if(mk_is_unbounded_probe()), fail_if(mk_produce_proofs_probe()), fail_if(mk_produce_unsat_cores_probe()), mk_propagate_ineqs_tactic(m), mk_normalize_bounds_tactic(m), mk_lia2pb_tactic(m), using_params(mk_pb2bv_tactic(m), pb2bv_p), fail_if_not(mk_is_qfbv_probe()), using_params(mk_bv2sat_tactic(m), bv2sat_p)); } // Try to find a model for an unbounded ILP problem. // Fails if the problem is no ILP. static tactic * mk_ilp_model_finder_tactic(ast_manager & m) { params_ref add_bounds_p1; add_bounds_p1.set_rat("add_bound_lower", rational(-16)); add_bounds_p1.set_rat("add_bound_upper", rational(15)); params_ref add_bounds_p2; add_bounds_p2.set_rat("add_bound_lower", rational(-32)); add_bounds_p2.set_rat("add_bound_upper", rational(31)); return and_then(fail_if_not(mk_and(mk_is_ilp_probe(), mk_is_unbounded_probe())), fail_if(mk_produce_proofs_probe()), fail_if(mk_produce_unsat_cores_probe()), mk_propagate_ineqs_tactic(m), or_else(// try_for(mk_mip_tactic(m), 5000), try_for(mk_no_cut_smt_tactic(100), 2000), and_then(using_params(mk_add_bounds_tactic(m), add_bounds_p1), try_for(mk_lia2sat_tactic(m), 5000)), try_for(mk_no_cut_smt_tactic(200), 5000), and_then(using_params(mk_add_bounds_tactic(m), add_bounds_p2), try_for(mk_lia2sat_tactic(m), 10000)) // , mk_mip_tactic(m) ), mk_fail_if_undecided_tactic()); } static tactic * mk_bounded_tactic(ast_manager & m) { return and_then(fail_if(mk_is_unbounded_probe()), or_else(try_for(mk_no_cut_smt_tactic(100), 5000), try_for(mk_no_cut_no_relevancy_smt_tactic(200), 5000), try_for(mk_no_cut_smt_tactic(300), 15000) ), mk_fail_if_undecided_tactic()); } tactic * mk_qflia_tactic(ast_manager & m, params_ref const & p) { params_ref main_p; main_p.set_bool("elim_and", true); main_p.set_bool("som", true); main_p.set_bool("blast_distinct", true); main_p.set_uint("blast_distinct_threshold", 128); // main_p.set_bool("push_ite_arith", true); params_ref pull_ite_p; pull_ite_p.set_bool("pull_cheap_ite", true); pull_ite_p.set_bool("push_ite_arith", false); pull_ite_p.set_bool("local_ctx", true); pull_ite_p.set_uint("local_ctx_limit", 10000000); params_ref ctx_simp_p; ctx_simp_p.set_uint("max_depth", 30); ctx_simp_p.set_uint("max_steps", 5000000); params_ref lhs_p; lhs_p.set_bool("arith_lhs", true); tactic * preamble_st = and_then(and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), using_params(mk_ctx_simplify_tactic(m), ctx_simp_p), using_params(mk_simplify_tactic(m), pull_ite_p)), mk_solve_eqs_tactic(m), mk_elim_uncnstr_tactic(m), using_params(mk_simplify_tactic(m), lhs_p) ); params_ref quasi_pb_p; quasi_pb_p.set_uint("lia2pb_max_bits", 64); params_ref no_cut_p; no_cut_p.set_uint("arith.branch_cut_ratio", 10000000); tactic * st = using_params(and_then(preamble_st, or_else(mk_ilp_model_finder_tactic(m), mk_pb_tactic(m), and_then(fail_if_not(mk_is_quasi_pb_probe()), using_params(mk_lia2sat_tactic(m), quasi_pb_p), mk_fail_if_undecided_tactic()), mk_bounded_tactic(m), mk_smt_tactic())), main_p); st->updt_params(p); return st; } z3-4.4.0/src/tactic/smtlogics/nra_tactic.h0000644000175000017500000000045512540347414020173 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nra_tactic.h Abstract: Tactic for NRA Author: Leonardo (leonardo) 2012-03-13 Notes: --*/ #ifndef _NRA_TACTIC_H_ #define _NRA_TACTIC_H_ tactic * mk_nra_tactic(ast_manager & m, params_ref const & p = params_ref()); #endif z3-4.4.0/src/tactic/smtlogics/nra_tactic.cpp0000644000175000017500000000212712540347414020524 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nra_tactic.cpp Abstract: Tactic for NRA Author: Leonardo (leonardo) 2012-03-13 Notes: --*/ #include"tactical.h" #include"simplify_tactic.h" #include"propagate_values_tactic.h" #include"smt_tactic.h" #include"nnf_tactic.h" #include"qe_tactic.h" #include"qfnra_nlsat_tactic.h" #include"probe_arith.h" tactic * mk_nra_tactic(ast_manager & m, params_ref const& p) { params_ref p1 = p; p1.set_uint("seed", 11); p1.set_bool("factor", false); params_ref p2 = p; p2.set_uint("seed", 13); p2.set_bool("factor", false); return and_then(mk_simplify_tactic(m, p), mk_nnf_tactic(m, p), mk_propagate_values_tactic(m, p), mk_qe_tactic(m, p), cond(mk_is_qfnra_probe(), or_else(try_for(mk_qfnra_nlsat_tactic(m, p), 5000), try_for(mk_qfnra_nlsat_tactic(m, p1), 10000), mk_qfnra_nlsat_tactic(m, p2)), mk_smt_tactic(p))); } z3-4.4.0/src/tactic/smtlogics/qfidl_tactic.cpp0000644000175000017500000000762512540347414021053 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfidl_tactic.cpp Abstract: Tactic for QF_IDL Author: Leonardo (leonardo) 2012-02-21 Notes: --*/ #include"tactical.h" #include"simplify_tactic.h" #include"propagate_values_tactic.h" #include"propagate_ineqs_tactic.h" #include"solve_eqs_tactic.h" #include"elim_uncnstr_tactic.h" #include"normalize_bounds_tactic.h" #include"fix_dl_var_tactic.h" #include"smt_tactic.h" #include"lia2pb_tactic.h" #include"pb2bv_tactic.h" #include"diff_neq_tactic.h" #include"bit_blaster_tactic.h" #include"max_bv_sharing_tactic.h" #include"aig_tactic.h" #include"sat_tactic.h" #define BIG_PROBLEM 5000 tactic * mk_qfidl_tactic(ast_manager & m, params_ref const & p) { params_ref main_p; main_p.set_bool("elim_and", true); main_p.set_bool("blast_distinct", true); main_p.set_bool("som", true); params_ref lhs_p; lhs_p.set_bool("arith_lhs", true); params_ref lia2pb_p; lia2pb_p.set_uint("lia2pb_max_bits", 4); params_ref pb2bv_p; pb2bv_p.set_uint("pb2bv_all_clauses_limit", 8); params_ref pull_ite_p; pull_ite_p.set_bool("pull_cheap_ite", true); pull_ite_p.set_bool("local_ctx", true); pull_ite_p.set_uint("local_ctx_limit", 10000000); tactic * preamble_st = and_then(and_then(mk_simplify_tactic(m), mk_fix_dl_var_tactic(m), mk_propagate_values_tactic(m), mk_elim_uncnstr_tactic(m) ), and_then(mk_solve_eqs_tactic(m), using_params(mk_simplify_tactic(m), lhs_p), mk_propagate_values_tactic(m), mk_normalize_bounds_tactic(m), mk_solve_eqs_tactic(m))); params_ref bv_solver_p; // The cardinality constraint encoding generates a lot of shared if-then-else's that can be flattened. // Several of them are simplified to and/or. If we flat them, we increase a lot the memory consumption. bv_solver_p.set_bool("flat", false); bv_solver_p.set_bool("som", false); // dynamic psm seems to work well. bv_solver_p.set_sym("gc", symbol("dyn_psm")); tactic * bv_solver = using_params(and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), mk_solve_eqs_tactic(m), mk_max_bv_sharing_tactic(m), mk_bit_blaster_tactic(m), mk_aig_tactic(), mk_sat_tactic(m)), bv_solver_p); tactic * try2bv = and_then(using_params(mk_lia2pb_tactic(m), lia2pb_p), mk_propagate_ineqs_tactic(m), using_params(mk_pb2bv_tactic(m), pb2bv_p), fail_if(mk_not(mk_is_qfbv_probe())), bv_solver); params_ref diff_neq_p; diff_neq_p.set_uint("diff_neq_max_k", 25); tactic * st = cond(mk_and(mk_lt(mk_num_consts_probe(), mk_const_probe(static_cast(BIG_PROBLEM))), mk_and(mk_not(mk_produce_proofs_probe()), mk_not(mk_produce_unsat_cores_probe()))), using_params(and_then(preamble_st, or_else(using_params(mk_diff_neq_tactic(m), diff_neq_p), try2bv, mk_smt_tactic())), main_p), mk_smt_tactic()); st->updt_params(p); return st; } z3-4.4.0/src/tactic/smtlogics/qfufnra_tactic.h0000644000175000017500000000073712540347414021060 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfufnra_tactic.h Abstract: Tactic for QF_UFNRA Author: Leonardo (leonardo) 2012-02-28 Notes: --*/ #ifndef _QFUFNRA_TACTIC_ #define _QFUFNRA_TACTIC_ #include"params.h" class ast_manager; class tactic; tactic * mk_qfufnra_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qfufnra", "builtin strategy for solving QF_UNFRA problems.", "mk_qfufnra_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/smtlogics/qfuf_tactic.h0000644000175000017500000000055512540347414020355 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfuf_tactic.h Abstract: Tactic for QF_QFUF benchmarks. Author: Leonardo de Moura (leonardo) 2012-02-21 Notes: --*/ #ifndef _QFUF_TACTIC_ #define _QFUF_TACTIC_ #include"params.h" class ast_manager; class tactic; tactic * mk_qfuf_tactic(ast_manager & m, params_ref const & p); #endif z3-4.4.0/src/tactic/smtlogics/qfaufbv_tactic.h0000644000175000017500000000056712540347414021051 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfaufbv_tactic.h Abstract: Tactic for QF_AUFBV Author: Leonardo (leonardo) 2012-02-23 Notes: --*/ #ifndef _QFAUFBV_TACTIC_H_ #define _QFAUFBV_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_qfaufbv_tactic(ast_manager & m, params_ref const & p = params_ref()); #endif z3-4.4.0/src/tactic/goal.cpp0000644000175000017500000004707012540347414015341 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: goal.cpp Abstract: Proof / Model finding Goals Author: Leonardo de Moura (leonardo) 2011-10-12 Revision History: --*/ #include"goal.h" #include"ast_ll_pp.h" #include"ast_smt2_pp.h" #include"for_each_expr.h" #include"well_sorted.h" goal::precision goal::mk_union(precision p1, precision p2) { if (p1 == PRECISE) return p2; if (p2 == PRECISE) return p1; if (p1 != p2) return UNDER_OVER; return p1; } std::ostream & operator<<(std::ostream & out, goal::precision p) { switch (p) { case goal::PRECISE: out << "precise"; break; case goal::UNDER: out << "under"; break; case goal::OVER: out << "over"; break; case goal::UNDER_OVER: out << "under-over"; break; } return out; } goal::goal(ast_manager & m, bool models_enabled, bool core_enabled): m_manager(m), m_ref_count(0), m_depth(0), m_models_enabled(models_enabled), m_proofs_enabled(m.proofs_enabled()), m_core_enabled(core_enabled), m_inconsistent(false), m_precision(PRECISE) { } goal::goal(ast_manager & m, bool proofs_enabled, bool models_enabled, bool core_enabled): m_manager(m), m_ref_count(0), m_depth(0), m_models_enabled(models_enabled), m_proofs_enabled(proofs_enabled), m_core_enabled(core_enabled), m_inconsistent(false), m_precision(PRECISE) { SASSERT(!proofs_enabled || m.proofs_enabled()); } goal::goal(goal const & src): m_manager(src.m()), m_ref_count(0), m_depth(0), m_models_enabled(src.models_enabled()), m_proofs_enabled(src.proofs_enabled()), m_core_enabled(src.unsat_core_enabled()), m_inconsistent(false), m_precision(PRECISE) { copy_from(src); } // Copy configuration: depth, models/proofs/cores flags, and precision from src. // The assertions are not copied goal::goal(goal const & src, bool): m_manager(src.m()), m_ref_count(0), m_depth(src.m_depth), m_models_enabled(src.models_enabled()), m_proofs_enabled(src.proofs_enabled()), m_core_enabled(src.unsat_core_enabled()), m_inconsistent(false), m_precision(src.m_precision) { } goal::~goal() { reset_core(); } void goal::copy_to(goal & target) const { SASSERT(&m_manager == &(target.m_manager)); if (this == &target) return; m().copy(m_forms, target.m_forms); m().copy(m_proofs, target.m_proofs); m().copy(m_dependencies, target.m_dependencies); target.m_depth = std::max(m_depth, target.m_depth); SASSERT(target.m_proofs_enabled == m_proofs_enabled); SASSERT(target.m_core_enabled == m_core_enabled); target.m_inconsistent = m_inconsistent; target.m_precision = mk_union(prec(), target.prec()); } void goal::push_back(expr * f, proof * pr, expr_dependency * d) { if (m().is_true(f)) return; if (m().is_false(f)) { // Make sure pr and d are not deleted by the m().del(...) statements. proof_ref saved_pr(m()); expr_dependency_ref saved_d(m()); saved_pr = pr; saved_d = d; m().del(m_forms); m().del(m_proofs); m().del(m_dependencies); m_inconsistent = true; m().push_back(m_forms, m().mk_false()); if (proofs_enabled()) m().push_back(m_proofs, saved_pr); if (unsat_core_enabled()) m().push_back(m_dependencies, saved_d); } else { SASSERT(!m_inconsistent); m().push_back(m_forms, f); if (proofs_enabled()) m().push_back(m_proofs, pr); if (unsat_core_enabled()) m().push_back(m_dependencies, d); } } void goal::quick_process(bool save_first, expr * & f, expr_dependency * d) { if (!m().is_and(f) && !(m().is_not(f) && m().is_or(to_app(f)->get_arg(0)))) { if (!save_first) { push_back(f, 0, d); } return; } typedef std::pair expr_pol; sbuffer todo; todo.push_back(expr_pol(f, true)); while (!todo.empty()) { if (m_inconsistent) return; expr_pol p = todo.back(); expr * curr = p.first; bool pol = p.second; todo.pop_back(); if (pol && m().is_and(curr)) { app * t = to_app(curr); unsigned i = t->get_num_args(); while (i > 0) { --i; todo.push_back(expr_pol(t->get_arg(i), true)); } } else if (!pol && m().is_or(curr)) { app * t = to_app(curr); unsigned i = t->get_num_args(); while (i > 0) { --i; todo.push_back(expr_pol(t->get_arg(i), false)); } } else if (m().is_not(curr)) { todo.push_back(expr_pol(to_app(curr)->get_arg(0), !pol)); } else { if (!pol) curr = m().mk_not(curr); if (save_first) { f = curr; save_first = false; } else { push_back(curr, 0, d); } } } } void goal::process_and(bool save_first, app * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr) { unsigned num = f->get_num_args(); for (unsigned i = 0; i < num; i++) { if (m_inconsistent) return; slow_process(save_first && i == 0, f->get_arg(i), m().mk_and_elim(pr, i), d, out_f, out_pr); } } void goal::process_not_or(bool save_first, app * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr) { unsigned num = f->get_num_args(); for (unsigned i = 0; i < num; i++) { if (m_inconsistent) return; expr * child = f->get_arg(i); if (m().is_not(child)) { expr * not_child = to_app(child)->get_arg(0); slow_process(save_first && i == 0, not_child, m().mk_not_or_elim(pr, i), d, out_f, out_pr); } else { expr_ref not_child(m()); not_child = m().mk_not(child); slow_process(save_first && i == 0, not_child, m().mk_not_or_elim(pr, i), d, out_f, out_pr); } } } void goal::slow_process(bool save_first, expr * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr) { if (m().is_and(f)) process_and(save_first, to_app(f), pr, d, out_f, out_pr); else if (m().is_not(f) && m().is_or(to_app(f)->get_arg(0))) process_not_or(save_first, to_app(to_app(f)->get_arg(0)), pr, d, out_f, out_pr); else if (save_first) { out_f = f; out_pr = pr; } else { push_back(f, pr, d); } } void goal::slow_process(expr * f, proof * pr, expr_dependency * d) { expr_ref out_f(m()); proof_ref out_pr(m()); slow_process(false, f, pr, d, out_f, out_pr); } void goal::assert_expr(expr * f, proof * pr, expr_dependency * d) { SASSERT(proofs_enabled() == (pr != 0 && !m().is_undef_proof(pr))); if (m_inconsistent) return; if (proofs_enabled()) slow_process(f, pr, d); else quick_process(false, f, d); } void goal::assert_expr(expr * f, expr_dependency * d) { assert_expr(f, proofs_enabled() ? m().mk_asserted(f) : 0, d); } void goal::get_formulas(ptr_vector & result) { unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { result.push_back(form(i)); } } void goal::update(unsigned i, expr * f, proof * pr, expr_dependency * d) { // KLM: don't know why this assertion is no longer true // SASSERT(proofs_enabled() == (pr != 0 && !m().is_undef_proof(pr))); if (m_inconsistent) return; if (proofs_enabled()) { expr_ref out_f(m()); proof_ref out_pr(m()); slow_process(true, f, pr, d, out_f, out_pr); if (!m_inconsistent) { if (m().is_false(out_f)) { push_back(out_f, out_pr, d); } else { m().set(m_forms, i, out_f); m().set(m_proofs, i, out_pr); if (unsat_core_enabled()) m().set(m_dependencies, i, d); } } } else { quick_process(true, f, d); if (!m_inconsistent) { if (m().is_false(f)) { push_back(f, 0, d); } else { m().set(m_forms, i, f); if (unsat_core_enabled()) m().set(m_dependencies, i, d); } } } } void goal::reset_core() { m().del(m_forms); m().del(m_proofs); m().del(m_dependencies); } void goal::reset_all() { reset_core(); m_depth = 0; m_inconsistent = false; m_precision = PRECISE; } void goal::reset() { reset_core(); m_inconsistent = false; } void goal::display(ast_printer & prn, std::ostream & out) const { out << "(goal"; unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { out << "\n "; prn.display(out, form(i), 2); } out << "\n :precision " << prec() << " :depth " << depth() << ")" << std::endl; } void goal::display_with_dependencies(ast_printer & prn, std::ostream & out) const { ptr_vector deps; obj_hashtable to_pp; out << "(goal"; unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { out << "\n |-"; deps.reset(); m().linearize(dep(i), deps); ptr_vector::iterator it = deps.begin(); ptr_vector::iterator end = deps.end(); for (; it != end; ++it) { expr * d = *it; if (is_uninterp_const(d)) { out << " " << mk_ismt2_pp(d, m()); } else { out << " #" << d->get_id(); to_pp.insert(d); } } out << "\n "; prn.display(out, form(i), 2); } if (!to_pp.empty()) { out << "\n :dependencies-definitions ("; obj_hashtable::iterator it = to_pp.begin(); obj_hashtable::iterator end = to_pp.end(); for (; it != end; ++it) { expr * d = *it; out << "\n (#" << d->get_id() << "\n "; prn.display(out, d, 2); out << ")"; } out << ")"; } out << "\n :precision " << prec() << " :depth " << depth() << ")" << std::endl; } void goal::display_with_dependencies(std::ostream & out) const { ptr_vector deps; out << "(goal"; unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { out << "\n |-"; deps.reset(); m().linearize(dep(i), deps); ptr_vector::iterator it = deps.begin(); ptr_vector::iterator end = deps.end(); for (; it != end; ++it) { expr * d = *it; if (is_uninterp_const(d)) { out << " " << mk_ismt2_pp(d, m()); } else { out << " #" << d->get_id(); } } out << "\n " << mk_ismt2_pp(form(i), m(), 2); } out << "\n :precision " << prec() << " :depth " << depth() << ")" << std::endl; } void goal::display(ast_printer_context & ctx) const { display(ctx, ctx.regular_stream()); } void goal::display_with_dependencies(ast_printer_context & ctx) const { display_with_dependencies(ctx, ctx.regular_stream()); } void goal::display(std::ostream & out) const { out << "(goal"; unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { out << "\n "; out << mk_ismt2_pp(form(i), m(), 2); } out << ")" << std::endl; } void goal::display_as_and(std::ostream & out) const { ptr_buffer args; unsigned sz = size(); for (unsigned i = 0; i < sz; i++) args.push_back(form(i)); expr_ref tmp(m()); tmp = m().mk_and(args.size(), args.c_ptr()); out << mk_ismt2_pp(tmp, m()) << "\n"; } void goal::display_ll(std::ostream & out) const { unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { out << mk_ll_pp(form(i), m()) << "\n"; } } /** \brief Assumes that the formula is already in CNF. */ void goal::display_dimacs(std::ostream & out) const { obj_map expr2var; unsigned num_vars = 0; unsigned num_cls = size(); for (unsigned i = 0; i < num_cls; i++) { expr * f = form(i); unsigned num_lits; expr * const * lits; if (m().is_or(f)) { num_lits = to_app(f)->get_num_args(); lits = to_app(f)->get_args(); } else { num_lits = 1; lits = &f; } for (unsigned j = 0; j < num_lits; j++) { expr * l = lits[j]; if (m().is_not(l)) l = to_app(l)->get_arg(0); if (expr2var.contains(l)) continue; num_vars++; expr2var.insert(l, num_vars); } } out << "p cnf " << num_vars << " " << num_cls << "\n"; for (unsigned i = 0; i < num_cls; i++) { expr * f = form(i); unsigned num_lits; expr * const * lits; if (m().is_or(f)) { num_lits = to_app(f)->get_num_args(); lits = to_app(f)->get_args(); } else { num_lits = 1; lits = &f; } for (unsigned j = 0; j < num_lits; j++) { expr * l = lits[j]; if (m().is_not(l)) { out << "-"; l = to_app(l)->get_arg(0); } unsigned id = UINT_MAX; expr2var.find(l, id); SASSERT(id != UINT_MAX); out << id << " "; } out << "0\n"; } } unsigned goal::num_exprs() const { expr_fast_mark1 visited; unsigned sz = size(); unsigned r = 0; for (unsigned i = 0; i < sz; i++) { r += get_num_exprs(form(i), visited); } return r; } void goal::shrink(unsigned j) { SASSERT(j <= size()); unsigned sz = size(); for (unsigned i = j; i < sz; i++) m().pop_back(m_forms); if (proofs_enabled()) { for (unsigned i = j; i < sz; i++) m().pop_back(m_proofs); } if (unsat_core_enabled()) { for (unsigned i = j; i < sz; i++) m().pop_back(m_dependencies); } } /** \brief Eliminate true formulas. */ void goal::elim_true() { unsigned sz = size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { expr * f = form(i); if (m().is_true(f)) continue; if (i == j) { j++; continue; } m().set(m_forms, j, f); if (proofs_enabled()) m().set(m_proofs, j, m().get(m_proofs, i)); if (unsat_core_enabled()) m().set(m_dependencies, j, m().get(m_dependencies, i)); j++; } shrink(j); } /** \brief Return the position of formula f in the goal. Return UINT_MAX if f is not in the goal */ unsigned goal::get_idx(expr * f) const { unsigned sz = size(); for (unsigned j = 0; j < sz; j++) { if (form(j) == f) return j; } return UINT_MAX; } /** \brief Return the position of formula (not f) in the goal. Return UINT_MAX if (not f) is not in the goal */ unsigned goal::get_not_idx(expr * f) const { expr * atom; unsigned sz = size(); for (unsigned j = 0; j < sz; j++) { if (m().is_not(form(j), atom) && atom == f) return j; } return UINT_MAX; } void goal::elim_redundancies() { if (inconsistent()) return; expr_ref_fast_mark1 neg_lits(m()); expr_ref_fast_mark2 pos_lits(m()); unsigned sz = size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { expr * f = form(i); if (m().is_true(f)) continue; if (m().is_not(f)) { expr * atom = to_app(f)->get_arg(0); if (neg_lits.is_marked(atom)) continue; if (pos_lits.is_marked(atom)) { proof * p = 0; if (proofs_enabled()) { proof * prs[2] = { pr(get_idx(atom)), pr(i) }; p = m().mk_unit_resolution(2, prs); } expr_dependency_ref d(m()); if (unsat_core_enabled()) d = m().mk_join(dep(get_idx(atom)), dep(i)); push_back(m().mk_false(), p, d); return; } neg_lits.mark(atom); } else { if (pos_lits.is_marked(f)) continue; if (neg_lits.is_marked(f)) { proof * p = 0; if (proofs_enabled()) { proof * prs[2] = { pr(get_not_idx(f)), pr(i) }; p = m().mk_unit_resolution(2, prs); } expr_dependency_ref d(m()); if (unsat_core_enabled()) d = m().mk_join(dep(get_not_idx(f)), dep(i)); push_back(m().mk_false(), p, d); return; } pos_lits.mark(f); } if (i == j) { j++; continue; } m().set(m_forms, j, f); if (proofs_enabled()) m().set(m_proofs, j, pr(i)); if (unsat_core_enabled()) m().set(m_dependencies, j, dep(i)); j++; } shrink(j); } bool goal::is_well_sorted() const { unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { expr * t = form(i); if (!::is_well_sorted(m(), t)) return false; } return true; } /** \brief Translate the assertion set to a new one that uses a different ast_manager. */ goal * goal::translate(ast_translation & translator) const { expr_dependency_translation dep_translator(translator); ast_manager & m_to = translator.to(); goal * res = alloc(goal, m_to, m_to.proofs_enabled() && proofs_enabled(), models_enabled(), unsat_core_enabled()); unsigned sz = m().size(m_forms); for (unsigned i = 0; i < sz; i++) { res->m().push_back(res->m_forms, translator(m().get(m_forms, i))); if (res->proofs_enabled()) res->m().push_back(res->m_proofs, translator(m().get(m_proofs, i))); if (res->unsat_core_enabled()) res->m().push_back(res->m_dependencies, dep_translator(m().get(m_dependencies, i))); } res->m_inconsistent = m_inconsistent; res->m_depth = m_depth; res->m_precision = m_precision; return res; } bool goal::sat_preserved() const { return prec() == PRECISE || prec() == UNDER; } bool goal::unsat_preserved() const { return prec() == PRECISE || prec() == OVER; } bool goal::is_decided_sat() const { return size() == 0 && sat_preserved(); } bool goal::is_decided_unsat() const { return inconsistent() && unsat_preserved(); } bool goal::is_decided() const { return is_decided_sat() || is_decided_unsat(); } bool is_equal(goal const & s1, goal const & s2) { if (s1.size() != s2.size()) return false; unsigned num1 = 0; // num unique ASTs in s1 unsigned num2 = 0; // num unique ASTs in s2 expr_fast_mark1 visited1; expr_fast_mark2 visited2; unsigned sz = s1.size(); for (unsigned i = 0; i < sz; i++) { expr * f1 = s1.form(i); if (visited1.is_marked(f1)) continue; num1++; visited1.mark(f1); } SASSERT(num1 <= sz); SASSERT(0 <= num1); for (unsigned i = 0; i < sz; i++) { expr * f2 = s2.form(i); if (visited2.is_marked(f2)) continue; num2++; visited2.mark(f2); if (!visited1.is_marked(f2)) return false; } SASSERT(num2 <= sz); SASSERT(0 <= num2); SASSERT(num1 >= num2); return num1 == num2; } z3-4.4.0/src/tactic/goal_num_occurs.h0000644000175000017500000000101412540347414017227 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: goal_num_occurs.h Abstract: Author: Leonardo de Moura (leonardo) 2012-10-20. Revision History: --*/ #ifndef _GOAL_NUM_OCCURS_H_ #define _GOAL_NUM_OCCURS_H_ #include"num_occurs.h" class goal; class goal_num_occurs : public num_occurs { public: goal_num_occurs(bool ignore_ref_count1 = false, bool ignore_quantifiers = false): num_occurs(ignore_ref_count1, ignore_quantifiers) { } void operator()(goal const & s); }; #endif z3-4.4.0/src/tactic/goal_util.cpp0000644000175000017500000000107612540347414016372 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: goal_util.cpp Abstract: goal goodies. Author: Leonardo de Moura (leonardo) 2012-01-03. Revision History: --*/ #include"goal_util.h" #include"goal.h" struct has_term_ite_functor { struct found {}; ast_manager & m; has_term_ite_functor(ast_manager & _m):m(_m) {} void operator()(var *) {} void operator()(quantifier *) {} void operator()(app * n) { if (m.is_term_ite(n)) throw found(); } }; bool has_term_ite(goal const & g) { return test(g); } z3-4.4.0/src/tactic/portfolio/0000755000175000017500000000000012540347414015720 5ustar michaelmichaelz3-4.4.0/src/tactic/portfolio/smt_strategic_solver.cpp0000644000175000017500000000656412540347414022701 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_strategic_solver.h Abstract: Create a strategic solver with tactic for all main logics used in SMT. Author: Leonardo (leonardo) 2012-02-19 Notes: --*/ #include"cmd_context.h" #include"combined_solver.h" #include"tactic2solver.h" #include"qfbv_tactic.h" #include"qflia_tactic.h" #include"qfnia_tactic.h" #include"qfnra_tactic.h" #include"qfuf_tactic.h" #include"qflra_tactic.h" #include"quant_tactics.h" #include"qfauflia_tactic.h" #include"qfaufbv_tactic.h" #include"qfufbv_tactic.h" #include"qfidl_tactic.h" #include"default_tactic.h" #include"ufbv_tactic.h" #include"qffp_tactic.h" #include"qfufnra_tactic.h" #include"horn_tactic.h" #include"smt_solver.h" tactic * mk_tactic_for_logic(ast_manager & m, params_ref const & p, symbol const & logic) { if (logic=="QF_UF") return mk_qfuf_tactic(m, p); else if (logic=="QF_BV") return mk_qfbv_tactic(m, p); else if (logic=="QF_IDL") return mk_qfidl_tactic(m, p); else if (logic=="QF_LIA") return mk_qflia_tactic(m, p); else if (logic=="QF_LRA") return mk_qflra_tactic(m, p); else if (logic=="QF_NIA") return mk_qfnia_tactic(m, p); else if (logic=="QF_NRA") return mk_qfnra_tactic(m, p); else if (logic=="QF_AUFLIA") return mk_qfauflia_tactic(m, p); else if (logic=="QF_AUFBV") return mk_qfaufbv_tactic(m, p); else if (logic=="QF_ABV") return mk_qfaufbv_tactic(m, p); else if (logic=="QF_UFBV") return mk_qfufbv_tactic(m, p); else if (logic=="AUFLIA") return mk_auflia_tactic(m, p); else if (logic=="AUFLIRA") return mk_auflira_tactic(m, p); else if (logic=="AUFNIRA") return mk_aufnira_tactic(m, p); else if (logic=="UFNIA") return mk_ufnia_tactic(m, p); else if (logic=="UFLRA") return mk_uflra_tactic(m, p); else if (logic=="LRA") return mk_lra_tactic(m, p); else if (logic=="LIA") return mk_lia_tactic(m, p); else if (logic=="UFBV") return mk_ufbv_tactic(m, p); else if (logic=="BV") return mk_ufbv_tactic(m, p); else if (logic=="QF_FP") return mk_qffp_tactic(m, p); else if (logic == "QF_FPBV" || logic == "QF_BVFP") return mk_qffpbv_tactic(m, p); else if (logic=="HORN") return mk_horn_tactic(m, p); else if (logic=="QF_UFNRA") return mk_qfufnra_tactic(m, p); else return mk_default_tactic(m, p); } class smt_strategic_solver_factory : public solver_factory { symbol m_logic; public: smt_strategic_solver_factory(symbol const & logic):m_logic(logic) {} virtual ~smt_strategic_solver_factory() {} virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { symbol l; if (m_logic != symbol::null) l = m_logic; else l = logic; tactic * t = mk_tactic_for_logic(m, p, l); return mk_combined_solver(mk_tactic2solver(m, t, p, proofs_enabled, models_enabled, unsat_core_enabled, l), mk_smt_solver(m, p, l), p); } }; solver_factory * mk_smt_strategic_solver_factory(symbol const & logic) { return alloc(smt_strategic_solver_factory, logic); } z3-4.4.0/src/tactic/portfolio/default_tactic.cpp0000644000175000017500000000352512540347414021404 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: default_tactic.cpp Abstract: General purpose tactic for the Z3 logic (when the logic is not specified). Author: Leonardo (leonardo) 2012-02-22 Notes: --*/ #include"default_tactic.h" #include"simplify_tactic.h" #include"qfbv_tactic.h" #include"smt_tactic.h" #include"qflia_tactic.h" #include"qflra_tactic.h" #include"qfnia_tactic.h" #include"qfnra_tactic.h" #include"nra_tactic.h" #include"probe_arith.h" #include"quant_tactics.h" #include"qffp_tactic.h" #include"qfaufbv_tactic.h" #include"qfauflia_tactic.h" #include"qfufnra_tactic.h" tactic * mk_default_tactic(ast_manager & m, params_ref const & p) { tactic * st = using_params(and_then(mk_simplify_tactic(m), cond(mk_is_qfbv_probe(), mk_qfbv_tactic(m), cond(mk_is_qfaufbv_probe(), mk_qfaufbv_tactic(m), cond(mk_is_qflia_probe(), mk_qflia_tactic(m), cond(mk_is_qfauflia_probe(), mk_qfauflia_tactic(m), cond(mk_is_qflra_probe(), mk_qflra_tactic(m), cond(mk_is_qfnra_probe(), mk_qfnra_tactic(m), cond(mk_is_qfnia_probe(), mk_qfnia_tactic(m), cond(mk_is_nra_probe(), mk_nra_tactic(m), cond(mk_is_lira_probe(), mk_lira_tactic(m, p), cond(mk_is_qffp_probe(), mk_qffp_tactic(m, p), cond(mk_is_qfufnra_probe(), mk_qfufnra_tactic(m, p), mk_smt_tactic())))))))))))), p); return st; } z3-4.4.0/src/tactic/portfolio/smt_strategic_solver.h0000644000175000017500000000064712540347414022342 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_strategic_solver.h Abstract: Create a strategic solver with tactic for all main logics used in SMT. Author: Leonardo (leonardo) 2012-02-19 Notes: --*/ #ifndef _SMT_STRATEGIC_SOLVER_H_ #define _SMT_STRATEGIC_SOLVER_H_ class solver_factory; solver_factory * mk_smt_strategic_solver_factory(symbol const & logic = symbol::null); #endif z3-4.4.0/src/tactic/portfolio/default_tactic.h0000644000175000017500000000065212540347414021047 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: default_tactic.h Abstract: General purpose tactic for the Z3 logic (when the logic is not specified). Author: Leonardo (leonardo) 2012-02-22 Notes: --*/ #ifndef _DEFAULT_TACTIC_ #define _DEFAULT_TACTIC_ #include"params.h" class ast_manager; class tactic; tactic * mk_default_tactic(ast_manager & m, params_ref const & p = params_ref()); #endif z3-4.4.0/src/tactic/model_converter.cpp0000644000175000017500000000757612540347414017615 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_converter.h Abstract: Abstract interface for converting models. Author: Leonardo (leonardo) 2011-04-21 Notes: --*/ #include"model_converter.h" #include"model_v2_pp.h" class concat_model_converter : public concat_converter { public: concat_model_converter(model_converter * mc1, model_converter * mc2):concat_converter(mc1, mc2) {} virtual void operator()(model_ref & m) { this->m_c2->operator()(m); this->m_c1->operator()(m); } virtual void operator()(model_ref & m, unsigned goal_idx) { this->m_c2->operator()(m, goal_idx); this->m_c1->operator()(m, 0); } virtual char const * get_name() const { return "concat-model-converter"; } virtual model_converter * translate(ast_translation & translator) { return this->translate_core(translator); } }; model_converter * concat(model_converter * mc1, model_converter * mc2) { if (mc1 == 0) return mc2; if (mc2 == 0) return mc1; return alloc(concat_model_converter, mc1, mc2); } class concat_star_model_converter : public concat_star_converter { public: concat_star_model_converter(model_converter * mc1, unsigned num, model_converter * const * mc2s, unsigned * szs): concat_star_converter(mc1, num, mc2s, szs) { } virtual void operator()(model_ref & m) { // TODO: delete method after conversion is complete UNREACHABLE(); } virtual void operator()(model_ref & m, unsigned goal_idx) { unsigned num = this->m_c2s.size(); for (unsigned i = 0; i < num; i++) { if (goal_idx < this->m_szs[i]) { // found the model converter that should be used model_converter * c2 = this->m_c2s[i]; if (c2) c2->operator()(m, goal_idx); if (m_c1) this->m_c1->operator()(m, i); return; } // invalid goal goal_idx -= this->m_szs[i]; } UNREACHABLE(); } virtual char const * get_name() const { return "concat-star-model-converter"; } virtual model_converter * translate(ast_translation & translator) { return this->translate_core(translator); } }; model_converter * concat(model_converter * mc1, unsigned num, model_converter * const * mc2s, unsigned * szs) { SASSERT(num > 0); if (num == 1) return concat(mc1, mc2s[0]); unsigned i; for (i = 0; i < num; i++) { if (mc2s[i] != 0) break; } if (i == num) { // all mc2s are 0 return mc1; } return alloc(concat_star_model_converter, mc1, num, mc2s, szs); } class model2mc : public model_converter { model_ref m_model; public: model2mc(model * m):m_model(m) {} virtual ~model2mc() {} virtual void operator()(model_ref & m) { m = m_model; } virtual void operator()(model_ref & m, unsigned goal_idx) { m = m_model; } virtual void cancel() { } virtual void display(std::ostream & out) { out << "(model->model-converter-wrapper\n"; model_v2_pp(out, *m_model); out << ")\n"; } virtual model_converter * translate(ast_translation & translator) { model * m = m_model->translate(translator); return alloc(model2mc, m); } }; model_converter * model2model_converter(model * m) { if (m == 0) return 0; return alloc(model2mc, m); } void model_converter2model(ast_manager & mng, model_converter * mc, model_ref & m) { if (mc) { m = alloc(model, mng); (*mc)(m, 0); } } void apply(model_converter_ref & mc, model_ref & m, unsigned gidx) { if (mc) { (*mc)(m, gidx); } } z3-4.4.0/src/tactic/tactical.h0000644000175000017500000000675112540347414015651 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: tactical.h Abstract: Basic combinators Author: Leonardo (leonardo) 2011-10-13 Notes: --*/ #ifndef _TACTICAL_H_ #define _TACTICAL_H_ #include"tactic.h" #include"probe.h" tactic * and_then(unsigned num, tactic * const * ts); tactic * and_then(tactic * t1, tactic * t2); tactic * and_then(tactic * t1, tactic * t2, tactic * t3); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9, tactic * t10); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9, tactic * t10, tactic * t11); tactic * or_else(unsigned num, tactic * const * ts); tactic * or_else(tactic * t1, tactic * t2); tactic * or_else(tactic * t1, tactic * t2, tactic * t3); tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4); tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5); tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6); tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7); tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8); tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9); tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9, tactic * t10); tactic * repeat(tactic * t, unsigned max = UINT_MAX); /** \brief Fails if \c t produeces more than \c threshold subgoals. Otherwise, it behabes like \c t. */ tactic * fail_if_branching(tactic * t, unsigned threshold = 1); tactic * par(unsigned num, tactic * const * ts); tactic * par(tactic * t1, tactic * t2); tactic * par(tactic * t1, tactic * t2, tactic * t3); tactic * par(tactic * t1, tactic * t2, tactic * t3, tactic * t4); tactic * par_and_then(unsigned num, tactic * const * ts); tactic * par_and_then(tactic * t1, tactic * t2); tactic * try_for(tactic * t, unsigned msecs); tactic * clean(tactic * t); tactic * using_params(tactic * t, params_ref const & p); // Create a tactic that fails if the result returned by probe p is true. tactic * fail_if(probe * p); tactic * fail_if_not(probe * p); // Execute t1 if p returns true, and t2 otherwise tactic * cond(probe * p, tactic * t1, tactic * t2); // Alias for cond(p, t, mk_skip_tactic()) tactic * when(probe * p, tactic * t); // alias for (or-else t skip) tactic * skip_if_failed(tactic * t); // Execute the given tactic only if proof production is not enabled. // If proof production is enabled it is a skip tactic * if_no_proofs(tactic * t); tactic * if_no_unsat_cores(tactic * t); tactic * if_no_models(tactic * t); #endif z3-4.4.0/src/tactic/filter_model_converter.cpp0000644000175000017500000000371612540347414021152 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: filter_model_converter.cpp Abstract: Filter decls from a model Author: Leonardo (leonardo) 2011-05-06 Notes: --*/ #include"filter_model_converter.h" #include"model_v2_pp.h" filter_model_converter::~filter_model_converter() { } void filter_model_converter::operator()(model_ref & old_model, unsigned goal_idx) { TRACE("filter_mc", tout << "before filter_model_converter\n"; model_v2_pp(tout, *old_model); display(tout);); ast_fast_mark1 fs; unsigned num = m_decls.size(); for (unsigned i = 0; i < num; i++) fs.mark(m_decls.get(i)); model * new_model = alloc(model, m()); num = old_model->get_num_constants(); for (unsigned i = 0; i < num; i++) { func_decl * f = old_model->get_constant(i); if (fs.is_marked(f)) continue; expr * fi = old_model->get_const_interp(f); new_model->register_decl(f, fi); } num = old_model->get_num_functions(); for (unsigned i = 0; i < num; i++) { func_decl * f = old_model->get_function(i); if (fs.is_marked(f)) continue; func_interp * fi = old_model->get_func_interp(f); SASSERT(fi); new_model->register_decl(f, fi->copy()); } new_model->copy_usort_interps(*old_model); old_model = new_model; TRACE("filter_mc", tout << "after filter_model_converter\n"; model_v2_pp(tout, *old_model);); } void filter_model_converter::display(std::ostream & out) { out << "(filter-model-converter"; for (unsigned i = 0; i < m_decls.size(); i++) { out << " " << m_decls.get(i)->get_name(); } out << ")" << std::endl; } model_converter * filter_model_converter::translate(ast_translation & translator) { filter_model_converter * res = alloc(filter_model_converter, translator.to()); for (unsigned i = 0; i < m_decls.size(); i++) res->m_decls.push_back(translator(m_decls[i].get())); return res; } z3-4.4.0/src/tactic/tactic_exception.h0000644000175000017500000000152212540347414017401 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: tactic_exception.h Abstract: Tactic expection object. Author: Leonardo (leonardo) 2012-08-15 Notes: --*/ #ifndef _TACTIC_EXCEPTION_H_ #define _TACTIC_EXCEPTION_H_ #include"z3_exception.h" #include"common_msgs.h" class tactic_exception : public z3_exception { protected: std::string m_msg; public: tactic_exception(char const * msg):m_msg(msg) {} virtual ~tactic_exception() {} virtual char const * msg() const { return m_msg.c_str(); } }; #define TACTIC_CANCELED_MSG Z3_CANCELED_MSG #define TACTIC_MAX_MEMORY_MSG Z3_MAX_MEMORY_MSG #define TACTIC_MAX_SCOPES_MSG Z3_MAX_SCOPES_MSG #define TACTIC_MAX_STEPS_MSG Z3_MAX_STEPS_MSG #define TACTIC_MAX_FRAMES_MSG Z3_MAX_FRAMES_MSG #define TACTIC_NO_PROOF_GEN_MSG Z3_NO_PROOF_GEN_MSG #endif z3-4.4.0/src/tactic/arith/0000755000175000017500000000000012540347414015012 5ustar michaelmichaelz3-4.4.0/src/tactic/arith/factor_tactic.h0000644000175000017500000000074712540347414020000 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: factor_tactic.h Abstract: Polynomial factorization tactic. Author: Leonardo de Moura (leonardo) 2012-02-03 Revision History: --*/ #ifndef _FACTOR_TACTIC_H_ #define _FACTOR_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_factor_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("factor", "polynomial factorization.", "mk_factor_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/arith/bound_manager.h0000644000175000017500000000474112540347414017772 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bound_manager.h Abstract: Collect bounds. Author: Leonardo (leonardo) 2011-05-16 Notes: --*/ #ifndef _BOUND_MANAGER_H_ #define _BOUND_MANAGER_H_ #include"ast.h" #include"arith_decl_plugin.h" class goal; class bound_manager { public: typedef rational numeral; private: typedef std::pair limit; arith_util m_util; obj_map m_lowers; obj_map m_uppers; obj_map m_lower_deps; obj_map m_upper_deps; ptr_vector m_bounded_vars; bool is_disjunctive_bound(expr * f, expr_dependency * d); void insert_lower(expr * v, bool strict, numeral const & n, expr_dependency * d); void insert_upper(expr * v, bool strict, numeral const & n, expr_dependency * d); public: static decl_kind neg(decl_kind k); static void norm(numeral & n, decl_kind & k); bound_manager(ast_manager & m); ~bound_manager(); ast_manager & m() const { return m_util.get_manager(); } void operator()(goal const & g); void operator()(expr * n, expr_dependency * d = 0); bool has_lower(expr * c, numeral & v, bool & strict) const { limit l; if (m_lowers.find(c, l)) { v = l.first; strict = l.second; return true; } return false; } bool has_upper(expr * c, numeral & v, bool & strict) const { limit l; if (m_uppers.find(c, l)) { v = l.first; strict = l.second; return true; } return false; } expr_dependency * lower_dep(expr * c) const { expr_dependency * d; if (m_lower_deps.find(c, d)) return d; return 0; } expr_dependency * upper_dep(expr * c) const { expr_dependency * d; if (m_upper_deps.find(c, d)) return d; return 0; } bool has_lower(expr * c) const { return m_lowers.contains(c); } bool has_upper(expr * c) const { return m_uppers.contains(c); } typedef ptr_vector::const_iterator iterator; /** \brief Iterator for all bounded constants. */ iterator begin() const { return m_bounded_vars.begin(); } iterator end() const { return m_bounded_vars.end(); } void reset(); // for debugging purposes void display(std::ostream & out) const; }; #endif z3-4.4.0/src/tactic/arith/lia2pb_tactic.cpp0000644000175000017500000003062612540347414020225 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: lia2pb_tactic.cpp Abstract: Reduce bounded LIA benchmark into 0-1 LIA benchmark. Author: Leonardo de Moura (leonardo) 2012-02-07. Revision History: --*/ #include"tactical.h" #include"bound_manager.h" #include"th_rewriter.h" #include"for_each_expr.h" #include"extension_model_converter.h" #include"filter_model_converter.h" #include"arith_decl_plugin.h" #include"expr_substitution.h" #include"ast_smt2_pp.h" class lia2pb_tactic : public tactic { struct imp { ast_manager & m; bound_manager m_bm; arith_util m_util; expr_dependency_ref_vector m_new_deps; th_rewriter m_rw; bool m_produce_models; bool m_produce_unsat_cores; bool m_partial_lia2pb; unsigned m_max_bits; unsigned m_total_bits; imp(ast_manager & _m, params_ref const & p): m(_m), m_bm(m), m_util(m), m_new_deps(m), m_rw(m, p) { updt_params(p); } void updt_params_core(params_ref const & p) { m_partial_lia2pb = p.get_bool("lia2pb_partial", false); m_max_bits = p.get_uint("lia2pb_max_bits", 32); m_total_bits = p.get_uint("lia2pb_total_bits", 2048); } void updt_params(params_ref const & p) { m_rw.updt_params(p); updt_params_core(p); } void set_cancel(bool f) { m_rw.set_cancel(f); } bool is_target_core(expr * n, rational & u) { if (!is_uninterp_const(n)) return false; rational l; bool s; if (m_bm.has_lower(n, l, s) && m_bm.has_upper(n, u, s) && l.is_zero() && !u.is_neg() && u.get_num_bits() <= m_max_bits) { return true; } return false; } bool is_bounded(expr * n) { rational u; return is_target_core(n, u); } bool is_target(expr * n) { rational u; return is_target_core(n, u) && u > rational(1); } struct failed {}; struct visitor { imp & m_owner; visitor(imp & o):m_owner(o) {} void throw_failed(expr * n) { TRACE("lia2pb", tout << "Failed at:\n" << mk_ismt2_pp(n, m_owner.m) << "\n";); throw failed(); } void operator()(var * n) { throw_failed(n); } void operator()(app * n) { family_id fid = n->get_family_id(); if (fid == m_owner.m.get_basic_family_id()) { // all basic family ops are OK } else if (fid == m_owner.m_util.get_family_id()) { // check if linear switch (n->get_decl_kind()) { case OP_LE: case OP_GE: case OP_LT: case OP_GT: case OP_ADD: case OP_NUM: return; case OP_MUL: if (n->get_num_args() != 2) throw_failed(n); if (!m_owner.m_util.is_numeral(n->get_arg(0))) throw_failed(n); return; default: throw_failed(n); } } else if (is_uninterp_const(n)) { if (m_owner.m_util.is_real(n)) { if (!m_owner.m_partial_lia2pb) throw_failed(n); } else if (m_owner.m_util.is_int(n)) { if (!m_owner.m_partial_lia2pb && !m_owner.is_bounded(n)) throw_failed(n); } } else { sort * s = m_owner.m.get_sort(n); if (s->get_family_id() == m_owner.m_util.get_family_id()) throw_failed(n); } } void operator()(quantifier * n) { throw_failed(n); } }; bool check(goal const & g) { try { expr_fast_mark1 visited; visitor proc(*this); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * f = g.form(i); for_each_expr_core(proc, visited, f); } return true; } catch (failed) { return false; } } bool has_target() { bound_manager::iterator it = m_bm.begin(); bound_manager::iterator end = m_bm.end(); for (; it != end; ++it) { if (is_target(*it)) return true; } return false; } bool check_num_bits() { unsigned num_bits = 0; rational u; bound_manager::iterator it = m_bm.begin(); bound_manager::iterator end = m_bm.end(); for (; it != end; ++it) { expr * x = *it; if (is_target_core(x, u) && u > rational(1)) { num_bits += u.get_num_bits(); if (num_bits > m_total_bits) return false; } } return true; } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("lia2pb", g); m_produce_models = g->models_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); mc = 0; pc = 0; core = 0; result.reset(); tactic_report report("lia2pb", *g); m_bm.reset(); m_rw.reset(); m_new_deps.reset(); if (g->inconsistent()) { result.push_back(g.get()); return; } m_bm(*g); TRACE("lia2pb", m_bm.display(tout);); // check if there is some variable to be converted if (!has_target()) { // nothing to be done g->inc_depth(); result.push_back(g.get()); return; } if (!check(*g)) throw tactic_exception("goal is in a fragment unsupported by lia2pb"); if (!check_num_bits()) throw tactic_exception("lia2pb failed, number of necessary bits exceeds specified threshold (use option :lia2pb-total-bits to increase threshold)"); extension_model_converter * mc1 = 0; filter_model_converter * mc2 = 0; if (m_produce_models) { mc1 = alloc(extension_model_converter, m); mc2 = alloc(filter_model_converter, m); mc = concat(mc2, mc1); } expr_ref zero(m); expr_ref one(m); zero = m_util.mk_numeral(rational(0), true); one = m_util.mk_numeral(rational(1), true); unsigned num_converted = 0; expr_substitution subst(m, m_produce_unsat_cores, false); rational u; ptr_buffer def_args; bound_manager::iterator it = m_bm.begin(); bound_manager::iterator end = m_bm.end(); for (; it != end; ++it) { expr * x = *it; if (is_target_core(x, u) && u > rational(1)) { num_converted++; def_args.reset(); rational a(1); unsigned num_bits = u.get_num_bits(); for (unsigned i = 0; i < num_bits; i++) { app * x_prime = m.mk_fresh_const(0, m_util.mk_int()); g->assert_expr(m_util.mk_le(zero, x_prime)); g->assert_expr(m_util.mk_le(x_prime, one)); if (a.is_one()) def_args.push_back(x_prime); else def_args.push_back(m_util.mk_mul(m_util.mk_numeral(a, true), x_prime)); if (m_produce_models) mc2->insert(x_prime->get_decl()); a *= rational(2); } SASSERT(def_args.size() > 1); expr * def = m_util.mk_add(def_args.size(), def_args.c_ptr()); expr_dependency * dep = 0; if (m_produce_unsat_cores) { dep = m.mk_join(m_bm.lower_dep(x), m_bm.upper_dep(x)); if (dep != 0) m_new_deps.push_back(dep); } TRACE("lia2pb", tout << mk_ismt2_pp(x, m) << " -> " << dep << "\n";); subst.insert(x, def, 0, dep); if (m_produce_models) mc1->insert(to_app(x)->get_decl(), def); } } report_tactic_progress(":converted-lia2pb", num_converted); m_rw.set_substitution(&subst); expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { expr * curr = g->form(idx); expr_dependency * dep = 0; m_rw(curr, new_curr, new_pr); if (m_produce_unsat_cores) { dep = m.mk_join(m_rw.get_used_dependencies(), g->dep(idx)); m_rw.reset_used_dependencies(); } if (m.proofs_enabled()) { new_pr = m.mk_modus_ponens(g->pr(idx), new_pr); } g->update(idx, new_curr, new_pr, dep); } g->inc_depth(); result.push_back(g.get()); TRACE("lia2pb", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: lia2pb_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(lia2pb_tactic, m, m_params); } virtual ~lia2pb_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { r.insert("lia2pb_partial", CPK_BOOL, "(default: false) partial lia2pb conversion."); r.insert("lia2pb_max_bits", CPK_UINT, "(default: 32) maximum number of bits to be used (per variable) in lia2pb."); r.insert("lia2pb_total_bits", CPK_UINT, "(default: 2048) total number of bits to be used (per problem) in lia2pb."); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { try { (*m_imp)(in, result, mc, pc, core); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } virtual void cleanup() { imp * d = alloc(imp, m_imp->m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } protected: virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_lia2pb_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(lia2pb_tactic, m, p)); } z3-4.4.0/src/tactic/arith/linear_equation.h0000644000175000017500000000534012540347414020344 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: linear_equation.h Abstract: Basic infrastructure for managing linear equations of the form: a_1 * x_1 + ... + a_n * x_n = 0 Author: Leonardo de Moura (leonardo) 2011-06-28 Revision History: --*/ #ifndef _LINEAR_EQUATION_H_ #define _LINEAR_EQUATION_H_ #include"mpq.h" #include"small_object_allocator.h" #include"numeral_buffer.h" #include"double_manager.h" class linear_equation { public: typedef unsigned var; private: static unsigned get_obj_size(unsigned sz) { return sizeof(linear_equation) + sz * (sizeof(mpz) + sizeof(double) + sizeof(var)); } friend class linear_equation_manager; unsigned m_size; mpz * m_as; // precise coefficients double * m_approx_as; // approximated coefficients var * m_xs; // var ids linear_equation() {} public: unsigned size() const { return m_size; } mpz const & a(unsigned idx) const { SASSERT(idx < m_size); return m_as[idx]; } double approx_a(unsigned idx) const { SASSERT(idx < m_size); return m_approx_as[idx]; } var x(unsigned idx) const { SASSERT(idx < m_size); return m_xs[idx]; } unsigned pos(unsigned x_i) const; void get_a(double_manager & m, unsigned idx, double & r) const { r = m_approx_as[idx]; } template void get_a(NumManager & m, unsigned idx, mpq & r) const { m.set(r, m_as[idx]); } template void get_a(NumManager & m, unsigned idx, mpz & r) const { m.set(r, m_as[idx]); } }; class linear_equation_manager { public: typedef unsynch_mpq_manager numeral_manager; typedef linear_equation::var var; typedef numeral_buffer mpz_buffer; private: typedef svector var_buffer; small_object_allocator & m_allocator; numeral_manager & m; mpz_buffer m_int_buffer; mpz_buffer m_val_buffer; char_vector m_mark; var_buffer m_var_buffer; linear_equation * mk_core(unsigned sz, mpz * as, var * xs); public: linear_equation_manager(numeral_manager & _m, small_object_allocator & a):m_allocator(a), m(_m), m_int_buffer(m), m_val_buffer(m) {} ~linear_equation_manager() {} linear_equation * mk(unsigned sz, mpq * as, var * xs, bool normalized = false); linear_equation * mk(unsigned sz, mpz * as, var * xs, bool normalized = false); void del(linear_equation * eq); // Return b1 * eq1 + b2 * eq2 // return 0 if the b1 * eq1 + b2 * eq2 == 0 linear_equation * mk(mpz const & b1, linear_equation const & eq1, mpz const & b2, linear_equation const & eq2); void display(std::ostream & out, linear_equation const & eq) const; }; #endif z3-4.4.0/src/tactic/arith/arith_bounds_tactic.h0000644000175000017500000000174212540347414021177 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: arith_bounds_tactic.h Abstract: Fast/rudimentary arithmetic subsumption tactic. Author: Nikolaj Bjorner (nbjorner) 2012-9-6 Notes: Background: The Farkas learner in PDR generates tons of inequalities that contain redundancies. It therefore needs a fast way to reduce these redundancies before passing the results to routines that are more expensive. The arith subsumption_strategy encapsulates a rudimentary routine for simplifying inequalities. Additional simplification routines can be added here or composed with this strategy. Note: The bound_manager subsumes some of the collection methods used for assembling bounds, but it does not have a way to check for subsumption of atoms. --*/ #ifndef _ARITH_BOUNDS_TACTIC_H_ #define _ARITH_BOUNDS_TACTIC_H_ #include "tactic.h" tactic * mk_arith_bounds_tactic(ast_manager & m, params_ref const & p = params_ref()); #endif z3-4.4.0/src/tactic/arith/lia2pb_tactic.h0000644000175000017500000000104712540347414017665 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: lia2pb_tactic.h Abstract: Reduce bounded LIA benchmark into 0-1 LIA benchmark. Author: Leonardo de Moura (leonardo) 2012-02-07. Revision History: --*/ #ifndef _LIA2PB_TACTIC_H_ #define _LIA2PB_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_lia2pb_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("lia2pb", "convert bounded integer variables into a sequence of 0-1 variables.", "mk_lia2pb_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/arith/fix_dl_var_tactic.h0000644000175000017500000000157612540347414020640 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: fix_dl_var_tactic.h Abstract: Fix a difference logic variable to 0. If the problem is in the difference logic fragment, that is, all arithmetic terms are of the form (x + k), and the arithmetic atoms are of the form x - y <= k or x - y = k. Then, we can set one variable to 0. This is useful because, many bounds can be exposed after this operation is performed. Author: Leonardo (leonardo) 2011-12-29 Notes: --*/ #ifndef _FIX_DL_VAR_TACTIC_H_ #define _FIX_DL_VAR_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_fix_dl_var_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("fix-dl-var", "if goal is in the difference logic fragment, then fix the variable with the most number of occurrences at 0.", "mk_fix_dl_var_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/arith/normalize_bounds_tactic.h0000644000175000017500000000123712540347414022067 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: normalize_bounds_tactic.h Abstract: Replace x with x' + l, when l <= x where x' is a fresh variable. Note that, after the transformation 0 <= x'. Author: Leonardo de Moura (leonardo) 2011-10-21. Revision History: --*/ #ifndef _NORMALIZE_BOUNDS_TACTIC_H_ #define _NORMALIZE_BOUNDS_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_normalize_bounds_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("normalize-bounds", "replace a variable x with lower bound k <= x with x' = x - k.", "mk_normalize_bounds_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/arith/lia2card_tactic.h0000644000175000017500000000124312540347414020173 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: lia2card_tactic.h Abstract: Extract 0-1 integer variables used in cardinality constraints and replace them by Booleans. Author: Nikolaj Bjorner (nbjorner) 2013-11-5 Notes: --*/ #ifndef _LIA2CARD_TACTIC_H_ #define _LIA2CARD_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_lia2card_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("lia2card", "introduce cardinality constraints from 0-1 integer.", "mk_lia2card_tactic(m, p)") */ bool get_pb_sum(expr* term, expr_ref_vector& args, vector& coeffs, rational& coeff); #endif z3-4.4.0/src/tactic/arith/pb2bv_model_converter.h0000644000175000017500000000162512540347414021451 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: pb2bv_model_converter.h Abstract: Model converter for the pb2bv tactic. Author: Christoph (cwinter) 2012-02-15 Notes: --*/ #ifndef _PB2BV_MODEL_CONVERTER_ #define _PB2BV_MODEL_CONVERTER_ #include"model_converter.h" #include"bound_manager.h" class pb2bv_model_converter : public model_converter { typedef std::pair func_decl_pair; ast_manager & m; svector m_c2bit; public: pb2bv_model_converter(ast_manager & _m, obj_map const & c2bit, bound_manager const & bm); virtual ~pb2bv_model_converter(); virtual void operator()(model_ref & md); virtual void operator()(model_ref & md, unsigned goal_idx); virtual void display(std::ostream & out); virtual model_converter * translate(ast_translation & translator); }; #endif z3-4.4.0/src/tactic/arith/bv2real_rewriter.h0000644000175000017500000002040112540347414020440 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv2real_rewriter.h Abstract: Basic rewriting rules for bv2real propagation. Author: Nikolaj (nbjorner) 2011-08-05 Notes: --*/ #ifndef _BV2REAL_REWRITER_H_ #define _BV2REAL_REWRITER_H_ #include"ast.h" #include"rewriter.h" #include"bv_decl_plugin.h" #include"arith_decl_plugin.h" // // bv2real[d,r](n,m) has interpretation: // sbv2int(n)/d + sbv2int(m)/d*sqrt(r) // where // sbv2int is signed bit-vector 2 integer. // class bv2real_util { struct bvr_sig { unsigned m_msz, m_nsz; rational m_d, m_r; }; struct bvr_eq { bool operator()(bvr_sig const& x, bvr_sig const& y) const { return x.m_msz == y.m_msz && x.m_nsz == y.m_nsz && x.m_d == y.m_d && x.m_r == y.m_r; } }; struct bvr_hash { unsigned operator()(bvr_sig const& x) const { unsigned a[3] = { x.m_msz, x.m_nsz, x.m_d.hash() }; return string_hash((char const*)a, 12, x.m_r.hash()); } }; ast_manager& m_manager; arith_util m_arith; bv_util m_bv; func_decl_ref_vector m_decls; func_decl_ref m_pos_le; func_decl_ref m_pos_lt; expr_ref_vector m_side_conditions; map m_sig2decl; obj_map m_decl2sig; rational m_default_root; rational m_default_divisor; rational m_max_divisor; unsigned m_max_num_bits; class contains_bv2real_proc; public: bv2real_util(ast_manager& m, rational const& default_root, rational const& default_divisor, unsigned max_num_bits); void reset() { m_side_conditions.reset(); } bool is_bv2real(func_decl* f) const; bool is_bv2real(func_decl* f, unsigned num_args, expr* const* args, expr*& m, expr*& n, rational& d, rational& r) const; bool is_bv2real(expr* e, expr*& n, expr*& m, rational& d, rational& r) const; bool is_bv2real(expr* e, expr*& n, expr*& m, rational& d); bool contains_bv2real(expr* e) const; bool mk_bv2real(expr* s, expr* t, rational& d, rational& r, expr_ref& result); expr* mk_bv2real_c(expr* s, expr* t, rational const& d, rational const& r); expr* mk_bv2real(expr* n, expr* m) { return mk_bv2real_c(n, m, default_divisor(), default_root()); } void mk_bv2real_reduced(expr* s, expr* t, expr_ref & result) { mk_bv2real_reduced(s, t, default_divisor(), default_root(), result); } void mk_bv2real_reduced(expr* s, expr* t, rational const& d, rational const& r, expr_ref & result); // // Positive polarity comparison operators. // Translation of positive polarity comparison requires fewer clauses. // bool is_pos_ltf(func_decl* f) const { return f == m_pos_lt; } bool is_pos_lef(func_decl* f) const { return f == m_pos_le; } bool is_pos_lt(expr const* e) const { return is_app(e) && is_pos_ltf(to_app(e)->get_decl()); } bool is_pos_le(expr const* e) const { return is_app(e) && is_pos_lef(to_app(e)->get_decl()); } MATCH_BINARY(is_pos_lt); MATCH_BINARY(is_pos_le); expr* mk_pos_lt(expr* s, expr* t) { return m().mk_app(m_pos_lt, s, t); } expr* mk_pos_le(expr* s, expr* t) { return m().mk_app(m_pos_le, s, t); } rational const& default_root() const { return m_default_root; } rational const& default_divisor() const { return m_default_divisor; } rational const& max_divisor() const { return m_max_divisor; } unsigned get_max_num_bits() const { return m_max_num_bits; } void add_side_condition(expr* e) { m_side_conditions.push_back(e); } unsigned num_side_conditions() const { return m_side_conditions.size(); } expr* const* side_conditions() const { return m_side_conditions.c_ptr(); } bool is_zero(expr* e); expr* mk_bv_add(expr* s, expr* t); expr* mk_bv_sub(expr* s, expr* t); expr* mk_bv_mul(expr* s, expr* t); expr* mk_bv_mul(rational const& n, expr* t); expr* mk_extend(unsigned sz, expr* b); expr* mk_sbv(rational const& n); void align_sizes(expr_ref& s, expr_ref& t); void align_divisors(expr_ref& s1, expr_ref& s2, expr_ref& t1, expr_ref& t2, rational& d1, rational& d2); bool is_bv2real(expr* n, expr_ref& s, expr_ref& t, rational& d, rational& r); bool align_divisor(expr_ref& s, expr_ref& t, rational& d); bool mk_is_divisible_by(expr_ref& s, rational const& _overflow); void add_aux_decl(func_decl* f) { m_decls.push_back(f); } unsigned num_aux_decls() const { return m_decls.size(); } func_decl* get_aux_decl(unsigned i) const { return m_decls[i]; } private: ast_manager & m() const { return m_manager; } arith_util & a() { return m_arith; } void mk_div(expr* e, rational const& d, expr_ref& result); void mk_sbv2real(expr* e, expr_ref& result); }; class bv2real_rewriter { ast_manager & m_manager; bv2real_util& m_util; bv_util m_bv; arith_util m_arith; public: bv2real_rewriter(ast_manager & m, bv2real_util& util); br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); private: ast_manager & m() const { return m_manager; } arith_util & a() { return m_arith; } bv2real_util& u() { return m_util; } br_status mk_eq(expr * arg1, expr * arg2, expr_ref & result); br_status mk_ite(expr* c, expr* s, expr* t, expr_ref& result); bool mk_le(expr* s, expr* t, bool is_pos, bool is_neg, expr_ref& result); br_status mk_le(expr * arg1, expr * arg2, expr_ref & result); br_status mk_lt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_le_pos(expr * arg1, expr * arg2, expr_ref & result); br_status mk_lt_pos(expr * arg1, expr * arg2, expr_ref & result); br_status mk_ge(expr * arg1, expr * arg2, expr_ref & result); br_status mk_gt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_add(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_mul(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_sub(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_div(expr* s, expr* t, expr_ref& result); br_status mk_add(expr* s, expr* t, expr_ref& result); br_status mk_mul(expr* s, expr* t, expr_ref& result); br_status mk_sub(expr* s, expr* t, expr_ref& result); br_status mk_uminus(expr* e, expr_ref & result); }; struct bv2real_rewriter_cfg : public default_rewriter_cfg { bv2real_rewriter m_r; bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = 0; return m_r.mk_app_core(f, num, args, result); } bv2real_rewriter_cfg(ast_manager & m, bv2real_util& u):m_r(m, u) {} }; class bv2real_rewriter_star : public rewriter_tpl { bv2real_rewriter_cfg m_cfg; public: bv2real_rewriter_star(ast_manager & m, bv2real_util& u): rewriter_tpl(m, false, m_cfg), m_cfg(m, u) {} }; /** \brief replace le(bv2real(a),bv2real(b)) by under-approximation. */ class bv2real_elim_rewriter { bv2real_util& m_util; public: bv2real_elim_rewriter(bv2real_util& util) : m_util(util) {} br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); }; struct bv2real_elim_rewriter_cfg : public default_rewriter_cfg { bv2real_elim_rewriter m_r; bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = 0; return m_r.mk_app_core(f, num, args, result); } bv2real_elim_rewriter_cfg(bv2real_util& u):m_r(u) {} }; class bv2real_elim_rewriter_star : public rewriter_tpl { bv2real_elim_rewriter_cfg m_cfg; public: bv2real_elim_rewriter_star(ast_manager & m, bv2real_util& u): rewriter_tpl(m, false, m_cfg), m_cfg(u) {} }; #endif z3-4.4.0/src/tactic/arith/diff_neq_tactic.h0000644000175000017500000000143612540347414020271 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: diff_neq_tactic.h Abstract: Solver for integer problems that contains literals of the form k <= x x <= k x - y != k And all variables are bounded. Author: Leonardo de Moura (leonardo) 2012-02-07. Revision History: --*/ #ifndef _DIFF_NEQ_TACTIC_H_ #define _DIFF_NEQ_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_diff_neq_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("diff-neq", "specialized solver for integer arithmetic problems that contain only atoms of the form (<= k x) (<= x k) and (not (= (- x y) k)), where x and y are constants and k is a numberal, and all constants are bounded.", "mk_diff_neq_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/arith/bv2real_rewriter.cpp0000644000175000017500000005366012540347414021010 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv2real_rewriter.cpp Abstract: Basic rewriting rules for bv2real propagation. Author: Nikolaj (nbjorner) 2011-08-05 Notes: --*/ #include"bv2real_rewriter.h" #include"rewriter_def.h" #include"ast_pp.h" #include"for_each_expr.h" bv2real_util::bv2real_util(ast_manager& m, rational const& default_root, rational const& default_divisor, unsigned max_num_bits) : m_manager(m), m_arith(m), m_bv(m), m_decls(m), m_pos_le(m), m_pos_lt(m), m_side_conditions(m), m_default_root(default_root), m_default_divisor(default_divisor), m_max_divisor(rational(2)*default_divisor), m_max_num_bits(max_num_bits) { sort* real = m_arith.mk_real(); sort* domain[2] = { real, real }; m_pos_lt = m.mk_fresh_func_decl("<","",2,domain,m.mk_bool_sort()); m_pos_le = m.mk_fresh_func_decl("<=","",2,domain,m.mk_bool_sort()); m_decls.push_back(m_pos_lt); m_decls.push_back(m_pos_le); } bool bv2real_util::is_bv2real(func_decl* f) const { return m_decl2sig.contains(f); } bool bv2real_util::is_bv2real(func_decl* f, unsigned num_args, expr* const* args, expr*& m, expr*& n, rational& d, rational& r) const { bvr_sig sig; if (!m_decl2sig.find(f, sig)) { return false; } SASSERT(num_args == 2); m = args[0]; n = args[1]; d = sig.m_d; r = sig.m_r; SASSERT(sig.m_d.is_int() && sig.m_d.is_pos()); SASSERT(sig.m_r.is_int() && sig.m_r.is_pos()); SASSERT(m_bv.get_bv_size(m) == sig.m_msz); SASSERT(m_bv.get_bv_size(n) == sig.m_nsz); return true; } bool bv2real_util::is_bv2real(expr* e, expr*& m, expr*& n, rational& d, rational& r) const { if (!is_app(e)) return false; func_decl* f = to_app(e)->get_decl(); return is_bv2real(f, to_app(e)->get_num_args(), to_app(e)->get_args(), m, n, d, r); } class bv2real_util::contains_bv2real_proc { bv2real_util const& m_util; public: class found {}; contains_bv2real_proc(bv2real_util const& u): m_util(u) {} void operator()(app* a) { if (m_util.is_bv2real(a->get_decl())) { throw found(); } } void operator()(var*) {} void operator()(quantifier*) {} }; bool bv2real_util::contains_bv2real(expr* e) const { contains_bv2real_proc p(*this); try { for_each_expr(p, e); } catch (contains_bv2real_proc::found) { return true; } return false; } bool bv2real_util::mk_bv2real(expr* _s, expr* _t, rational& d, rational& r, expr_ref& result) { expr_ref s(_s,m()), t(_t,m()); if (align_divisor(s, t, d)) { result = mk_bv2real_c(s, t, d, r); return true; } else { return false; } } expr* bv2real_util::mk_bv2real_c(expr* s, expr* t, rational const& d, rational const& r) { bvr_sig sig; sig.m_msz = m_bv.get_bv_size(s); sig.m_nsz = m_bv.get_bv_size(t); sig.m_d = d; sig.m_r = r; func_decl* f; if (!m_sig2decl.find(sig, f)) { sort* domain[2] = { m_manager.get_sort(s), m_manager.get_sort(t) }; sort* real = m_arith.mk_real(); f = m_manager.mk_fresh_func_decl("bv2real", "", 2, domain, real); m_decls.push_back(f); m_sig2decl.insert(sig, f); m_decl2sig.insert(f, sig); } return m_manager.mk_app(f, s, t); } void bv2real_util::mk_bv2real_reduced(expr* s, expr* t, rational const& d, rational const& r, expr_ref & result) { expr_ref s1(m()), t1(m()), r1(m()); rational num; mk_sbv2real(s, s1); mk_sbv2real(t, t1); mk_div(s1, d, s1); mk_div(t1, d, t1); r1 = a().mk_power(a().mk_numeral(r, false), a().mk_numeral(rational(1,2),false)); t1 = a().mk_mul(t1, r1); result = a().mk_add(s1, t1); } void bv2real_util::mk_div(expr* e, rational const& d, expr_ref& result) { result = a().mk_div(e, a().mk_numeral(rational(d), false)); } void bv2real_util::mk_sbv2real(expr* e, expr_ref& result) { rational r; unsigned bv_size = m_bv.get_bv_size(e); rational bsize = power(rational(2), bv_size); expr_ref bvr(a().mk_to_real(m_bv.mk_bv2int(e)), m()); expr_ref c(m_bv.mk_sle(m_bv.mk_numeral(rational(0), bv_size), e), m()); result = m().mk_ite(c, bvr, a().mk_sub(bvr, a().mk_numeral(bsize, false))); } expr* bv2real_util::mk_bv_mul(rational const& n, expr* t) { if (n.is_one()) return t; expr* s = mk_sbv(n); return mk_bv_mul(s, t); } void bv2real_util::align_divisors(expr_ref& s1, expr_ref& s2, expr_ref& t1, expr_ref& t2, rational& d1, rational& d2) { if (d1 == d2) { return; } // s/d1 ~ t/d2 <=> lcm*s/d1 ~ lcm*t/d2 <=> (lcm/d1)*s ~ (lcm/d2)*t // s/d1 ~ t/d2 <=> s/gcd*d1' ~ t/gcd*d2' <=> d2'*s/lcm ~ d1'*t/lcm rational g = gcd(d1,d2); rational l = lcm(d1,d2); rational d1g = d1/g; rational d2g = d2/g; s1 = mk_bv_mul(d2g, s1); s2 = mk_bv_mul(d2g, s2); t1 = mk_bv_mul(d1g, t1); t2 = mk_bv_mul(d1g, t2); d1 = l; d2 = l; } expr* bv2real_util::mk_bv_mul(expr* s, expr* t) { SASSERT(m_bv.is_bv(s)); SASSERT(m_bv.is_bv(t)); if (is_zero(s)) { return s; } if (is_zero(t)) { return t; } expr_ref s1(s, m()), t1(t, m()); align_sizes(s1, t1); unsigned n = m_bv.get_bv_size(t1); unsigned max_bits = get_max_num_bits(); bool add_side_conds = 2*n > max_bits; if (n >= max_bits) { // nothing } else if (2*n > max_bits) { s1 = mk_extend(max_bits-n, s1); t1 = mk_extend(max_bits-n, t1); } else { s1 = mk_extend(n, s1); t1 = mk_extend(n, t1); } if (add_side_conds) { add_side_condition(m_bv.mk_bvsmul_no_ovfl(s1, t1)); add_side_condition(m_bv.mk_bvsmul_no_udfl(s1, t1)); } return m_bv.mk_bv_mul(s1, t1); } bool bv2real_util::is_zero(expr* n) { rational r; unsigned sz; return m_bv.is_numeral(n, r, sz) && r.is_zero(); } expr* bv2real_util::mk_bv_add(expr* s, expr* t) { SASSERT(m_bv.is_bv(s)); SASSERT(m_bv.is_bv(t)); if (is_zero(s)) { return t; } if (is_zero(t)) { return s; } expr_ref s1(s, m()), t1(t, m()); align_sizes(s1, t1); s1 = mk_extend(1, s1); t1 = mk_extend(1, t1); return m_bv.mk_bv_add(s1, t1); } void bv2real_util::align_sizes(expr_ref& s, expr_ref& t) { unsigned sz1 = m_bv.get_bv_size(s); unsigned sz2 = m_bv.get_bv_size(t); if (sz1 > sz2) { t = mk_extend(sz1-sz2, t); } else if (sz1 < sz2) { s = mk_extend(sz2-sz1, s); } } expr* bv2real_util::mk_sbv(rational const& n) { SASSERT(n.is_int()); if (n.is_neg()) { rational m = abs(n); unsigned nb = m.get_num_bits(); return m_bv.mk_bv_neg(m_bv.mk_numeral(m, nb+1)); } else { unsigned nb = n.get_num_bits(); return m_bv.mk_numeral(n, nb+1); } } expr* bv2real_util::mk_bv_sub(expr* s, expr* t) { expr_ref s1(s, m()), t1(t, m()); align_sizes(s1, t1); s1 = mk_extend(1, s1); t1 = mk_extend(1, t1); return m_bv.mk_bv_sub(s1, t1); } expr* bv2real_util::mk_extend(unsigned sz, expr* b) { if (sz == 0) { return b; } rational r; unsigned bv_sz; if (m_bv.is_numeral(b, r, bv_sz) && power(rational(2),bv_sz-1) > r) { return m_bv.mk_numeral(r, bv_sz + sz); } return m_bv.mk_sign_extend(sz, b); } bool bv2real_util::is_bv2real(expr* n, expr_ref& s, expr_ref& t, rational& d, rational& r) { expr* _s, *_t; if (is_bv2real(n, _s, _t, d, r)) { s = _s; t = _t; return true; } rational k; bool is_int; if (m_arith.is_numeral(n, k, is_int) && !is_int) { d = denominator(k); r = default_root(); s = mk_sbv(numerator(k)); t = mk_sbv(rational(0)); return true; } return false; } bool bv2real_util::align_divisor(expr_ref& s, expr_ref& t, rational& d) { if (d > max_divisor()) { // // if divisor is over threshold, then divide s and t // add side condition that s, t are divisible. // rational overflow = d / max_divisor(); if (!overflow.is_int()) return false; if (!mk_is_divisible_by(s, overflow)) return false; if (!mk_is_divisible_by(t, overflow)) return false; d = max_divisor(); } return true; } bool bv2real_util::mk_is_divisible_by(expr_ref& s, rational const& _overflow) { rational overflow(_overflow); SASSERT(overflow.is_int()); SASSERT(overflow.is_pos()); SASSERT(!overflow.is_one()); TRACE("bv2real_rewriter", tout << mk_pp(s, m()) << " " << overflow << "\n";); unsigned power2 = 0; while ((overflow % rational(2)) == rational(0)) { power2++; overflow = div(overflow, rational(2)); } if (power2 > 0) { unsigned sz = m_bv.get_bv_size(s); if (sz <= power2) { add_side_condition(m().mk_eq(s, m_bv.mk_numeral(rational(0), sz))); s = m_bv.mk_numeral(rational(0), 1); } else { expr* s1 = m_bv.mk_extract(power2-1, 0, s); add_side_condition(m().mk_eq(s1, m_bv.mk_numeral(rational(0), power2))); s = m_bv.mk_extract(sz-1, power2, s); } } TRACE("bv2real_rewriter", tout << mk_pp(s, m()) << " " << overflow << "\n";); return overflow.is_one(); } // --------------------------------------------------------------------- // bv2real_rewriter bv2real_rewriter::bv2real_rewriter(ast_manager& m, bv2real_util& util): m_manager(m), m_util(util), m_bv(m), m_arith(m) {} br_status bv2real_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { TRACE("bv2real_rewriter", tout << mk_pp(f, m()) << " "; for (unsigned i = 0; i < num_args; ++i) { tout << mk_pp(args[i], m()) << " "; } tout << "\n";); if(f->get_family_id() == m_arith.get_family_id()) { switch (f->get_decl_kind()) { case OP_NUM: return BR_FAILED; case OP_LE: SASSERT(num_args == 2); return mk_le(args[0], args[1], result); case OP_GE: SASSERT(num_args == 2); return mk_ge(args[0], args[1], result); case OP_LT: SASSERT(num_args == 2); return mk_lt(args[0], args[1], result); case OP_GT: SASSERT(num_args == 2); return mk_gt(args[0], args[1], result); case OP_ADD: return mk_add(num_args, args, result); case OP_MUL: return mk_mul(num_args, args, result); case OP_SUB: return mk_sub(num_args, args, result); case OP_DIV: SASSERT(num_args == 2); return mk_div(args[0], args[1], result); case OP_IDIV: return BR_FAILED; case OP_MOD: return BR_FAILED; case OP_REM: return BR_FAILED; case OP_UMINUS: SASSERT(num_args == 1); return mk_uminus(args[0], result); case OP_TO_REAL: return BR_FAILED; // TBD case OP_TO_INT: return BR_FAILED; // TBD case OP_IS_INT: return BR_FAILED; // TBD default: return BR_FAILED; } } if (f->get_family_id() == m().get_basic_family_id()) { switch (f->get_decl_kind()) { case OP_EQ: SASSERT(num_args == 2); return mk_eq(args[0], args[1], result); case OP_ITE: SASSERT(num_args == 3); return mk_ite(args[0], args[1], args[2], result); default: return BR_FAILED; } } if (u().is_pos_ltf(f)) { SASSERT(num_args == 2); return mk_lt_pos(args[0], args[1], result); } if (u().is_pos_lef(f)) { SASSERT(num_args == 2); return mk_le_pos(args[0], args[1], result); } return BR_FAILED; } bool bv2real_rewriter::mk_le(expr* s, expr* t, bool is_pos, bool is_neg, expr_ref& result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; SASSERT(is_pos || is_neg); if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2 && r1 == rational(2)) { // // (s1 + s2*sqrt(2))/d1 <= (t1 + t2*sqrt(2))/d2 // <=> // // let s1 = s1*d2-t1*d1, t2 = s2*d2-t2*d1 // // // s1 + s2*sqrt(2) <= 0 // <= // s1 + s2*approx(sign(s2),sqrt(2)) <= 0 // or (s1 = 0 & s2 = 0) // // If s2 is negative use an under-approximation for sqrt(r). // If s2 is positive use an over-approximation for sqrt(r). // e.g., r = 2, then 5/4 and 3/2 are under/over approximations. // Then s1 + s2*approx(sign(s2), r) <= 0 => s1 + s2*sqrt(r) <= 0 u().align_divisors(s1, s2, t1, t2, d1, d2); s1 = u().mk_bv_sub(s1, t1); s2 = u().mk_bv_sub(s2, t2); unsigned s2_size = m_bv.get_bv_size(s2); expr_ref le_proxy(m().mk_fresh_const("le_proxy",m().mk_bool_sort()), m()); u().add_aux_decl(to_app(le_proxy)->get_decl()); expr_ref gt_proxy(m().mk_not(le_proxy), m()); expr_ref s2_is_nonpos(m_bv.mk_sle(s2, m_bv.mk_numeral(rational(0), s2_size)), m()); expr_ref under(u().mk_bv_add(u().mk_bv_mul(rational(4), s1), u().mk_bv_mul(rational(5), s2)), m()); expr_ref z1(m_bv.mk_numeral(rational(0), m_bv.get_bv_size(under)), m()); expr_ref le_under(m_bv.mk_sle(under, z1), m()); expr_ref over(u().mk_bv_add(u().mk_bv_mul(rational(2), s1), u().mk_bv_mul(rational(3), s2)), m()); expr_ref z2(m_bv.mk_numeral(rational(0), m_bv.get_bv_size(over)), m()); expr_ref le_over(m_bv.mk_sle(over, z2), m()); // predicate may occur in positive polarity. if (is_pos) { // s1 + s2*sqrt(2) <= 0 <== s2 <= 0 & s1 + s2*(5/4) <= 0; 4*s1 + 5*s2 <= 0 expr* e1 = m().mk_implies(m().mk_and(le_proxy, s2_is_nonpos), le_under); // s1 + s2*sqrt(2) <= 0 <== s2 > 0 & s1 + s2*(3/2); 0 <=> 2*s1 + 3*s2 <= 0 expr* e2 = m().mk_implies(m().mk_and(le_proxy, m().mk_not(s2_is_nonpos)), le_over); u().add_side_condition(e1); u().add_side_condition(e2); } // predicate may occur in negative polarity. if (is_neg) { // s1 + s2*sqrt(2) > 0 <== s2 > 0 & s1 + s2*(5/4) > 0; 4*s1 + 5*s2 > 0 expr* e3 = m().mk_implies(m().mk_and(gt_proxy, m().mk_not(s2_is_nonpos)), m().mk_not(le_under)); // s1 + s2*sqrt(2) > 0 <== s2 <= 0 & s1 + s2*(3/2) > 0 <=> 2*s1 + 3*s2 > 0 expr* e4 = m().mk_implies(m().mk_and(gt_proxy, s2_is_nonpos), m().mk_not(le_over)); u().add_side_condition(e3); u().add_side_condition(e4); } TRACE("bv2real_rewriter", tout << "mk_le\n";); if (is_pos) { result = le_proxy; } else { result = gt_proxy; } return true; } return false; } br_status bv2real_rewriter::mk_le_pos(expr * s, expr * t, expr_ref & result) { if (mk_le(s, t, true, false, result)) { return BR_DONE; } return BR_FAILED; } br_status bv2real_rewriter::mk_lt_pos(expr * s, expr * t, expr_ref & result) { if (mk_le(t, s, false, true, result)) { return BR_DONE; } return BR_FAILED; } br_status bv2real_rewriter::mk_le(expr * s, expr * t, expr_ref & result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; if (mk_le(s, t, true, true, result)) { return BR_DONE; } if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { // // somewhat expensive approach without having // polarity information for sound approximation. // // Convert to: // t1 + t2*sqrt(r) >= 0 // then to: // // (t1 >= 0 && t2 <= 0 => t1^2 >= t2^2*r) // (t1 <= 0 && t2 >= 0 => t1^2 <= t2^2*r) // (t1 >= 0 || t2 >= 0) // // A cheaper approach is to approximate > under the assumption // that > occurs in positive polarity. // then if t2 is negative use an over-approximation for sqrt(r) // if t2 is positive use an under-approximation for sqrt(r). // e.g., r = 2, then 5/4 and 3/2 are under/over approximations. // Then t1 + t2*approx(sign(t2), r) > 0 => t1 + t2*sqrt(r) > 0 // u().align_divisors(s1, s2, t1, t2, d1, d2); t1 = u().mk_bv_sub(t1, s1); t2 = u().mk_bv_sub(t2, s2); expr_ref z1(m()), z2(m()); z1 = m_bv.mk_numeral(rational(0), m_bv.get_bv_size(t1)); z2 = m_bv.mk_numeral(rational(0), m_bv.get_bv_size(t2)); expr* gz1 = m_bv.mk_sle(z1, t1); expr* lz1 = m_bv.mk_sle(t1, z1); expr* gz2 = m_bv.mk_sle(z2, t2); expr* lz2 = m_bv.mk_sle(t2, z2); expr_ref t12(u().mk_bv_mul(t1, t1), m()); expr_ref t22(u().mk_bv_mul(r1, u().mk_bv_mul(t2, t2)), m()); u().align_sizes(t12, t22); expr* ge = m_bv.mk_sle(t22, t12); expr* le = m_bv.mk_sle(t12, t22); expr* e1 = m().mk_or(gz1, gz2); expr* e2 = m().mk_or(m().mk_not(gz1), m().mk_not(lz2), ge); expr* e3 = m().mk_or(m().mk_not(gz2), m().mk_not(lz1), le); result = m().mk_and(e1, e2, e3); TRACE("bv2real_rewriter", tout << "\n";); return BR_DONE; } return BR_FAILED; } br_status bv2real_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) { result = m().mk_not(m_arith.mk_le(arg2, arg1)); return BR_REWRITE2; } br_status bv2real_rewriter::mk_ge(expr * arg1, expr * arg2, expr_ref & result) { return mk_le(arg2, arg1, result); } br_status bv2real_rewriter::mk_gt(expr * arg1, expr * arg2, expr_ref & result) { result = m().mk_not(m_arith.mk_le(arg1, arg2)); return BR_REWRITE2; } br_status bv2real_rewriter::mk_ite(expr* c, expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { u().align_divisors(s1, s2, t1, t2, d1, d2); u().align_sizes(s1, t1); u().align_sizes(s2, t2); if (u().mk_bv2real(m().mk_ite(c, s1, t1), m().mk_ite(c, s2, t2), d1, r1, result)) { return BR_DONE; } } return BR_FAILED; } br_status bv2real_rewriter::mk_eq(expr * s, expr * t, expr_ref & result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { u().align_divisors(s1, s2, t1, t2, d1, d2); u().align_sizes(s1, t1); u().align_sizes(s2, t2); result = m().mk_and(m().mk_eq(s1, t1), m().mk_eq(s2, t2)); return BR_DONE; } return BR_FAILED; } br_status bv2real_rewriter::mk_uminus(expr * s, expr_ref & result) { expr_ref s1(m()), s2(m()); rational d1, r1; if (u().is_bv2real(s, s1, s2, d1, r1)) { s1 = u().mk_extend(1, s1); s2 = u().mk_extend(1, s2); if (u().mk_bv2real(m_bv.mk_bv_neg(s1), m_bv.mk_bv_neg(s2), d1, r1, result)) { return BR_DONE; } } return BR_FAILED; } br_status bv2real_rewriter::mk_add(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_add(result, args[i], result); } return r; } br_status bv2real_rewriter::mk_add(expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { u().align_divisors(s1, s2, t1, t2, d1, d2); if (u().mk_bv2real(u().mk_bv_add(s1, t1), u().mk_bv_add(t2, s2), d1, r1, result)) { return BR_DONE; } } return BR_FAILED; } br_status bv2real_rewriter::mk_mul(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_mul(result, args[i], result); } return r; } br_status bv2real_rewriter::mk_div(expr* s, expr* t, expr_ref& result) { return BR_FAILED; } br_status bv2real_rewriter::mk_mul(expr* s, expr* t, expr_ref& result) { // TBD: optimize expr_ref s1(m()), t1(m()), s2(m()), t2(m()); rational d1, d2, r1, r2; if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { // s1*t1 + r1*(s2*t2) + (s1*t2 + s2*t2)*r1 expr_ref u1(m()), u2(m()); u1 = u().mk_bv_add(u().mk_bv_mul(s1, t1), u().mk_bv_mul(r1, u().mk_bv_mul(t2, s2))); u2 = u().mk_bv_add(u().mk_bv_mul(s1, t2), u().mk_bv_mul(s2, t1)); rational tmp = d1*d2; if (u().mk_bv2real(u1, u2, tmp, r1, result)) { return BR_DONE; } } return BR_FAILED; } br_status bv2real_rewriter::mk_sub(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_sub(result, args[i], result); } return r; } br_status bv2real_rewriter::mk_sub(expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { u().align_divisors(s1, s2, t1, t2, d1, d2); if (u().mk_bv2real(u().mk_bv_sub(s1, t1), u().mk_bv_sub(s2, t2), d1, r1, result)) { return BR_DONE; } } return BR_FAILED; } template class rewriter_tpl; br_status bv2real_elim_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { expr* m, *n; rational d, r; if (m_util.is_bv2real(f, num_args, args, m, n, d, r)) { m_util.mk_bv2real_reduced(m, n, d, r, result); return BR_REWRITE_FULL; } return BR_FAILED; } template class rewriter_tpl; z3-4.4.0/src/tactic/arith/recover_01_tactic.cpp0000644000175000017500000003615712540347414021026 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: recover_01_tactic.cpp Abstract: Recover 01 variables Search for clauses of the form p or q or x = 0 ~p or q or x = k1 p or ~q or x = k2 ~p or ~q or x = k1+k2 Then, replaces x with k1*y1 + k2*y2 p with y1=1 q with y2=1 where y1 and y2 are fresh 01 variables The clauses are also removed. Author: Leonardo de Moura (leonardo) 2012-02-17. Revision History: --*/ #include"tactical.h" #include"th_rewriter.h" #include"extension_model_converter.h" #include"filter_model_converter.h" #include"arith_decl_plugin.h" #include"expr_substitution.h" #include"dec_ref_util.h" #include"ast_smt2_pp.h" class recover_01_tactic : public tactic { struct imp { typedef obj_map > var2clauses; ast_manager & m; var2clauses m_var2clauses; arith_util m_util; th_rewriter m_rw; bool m_produce_models; unsigned m_cls_max_size; imp(ast_manager & _m, params_ref const & p): m(_m), m_util(m), m_rw(m, p) { updt_params_core(p); } void updt_params_core(params_ref const & p) { m_cls_max_size = p.get_uint("recover_01_max_bits", 10); } void updt_params(params_ref const & p) { m_rw.updt_params(p); updt_params_core(p); } void set_cancel(bool f) { m_rw.set_cancel(f); } bool save_clause(expr * c) { if (!m.is_or(c)) return false; func_decl * x = 0; app * cls = to_app(c); if (cls->get_num_args() <= 1 || cls->get_num_args() >= m_cls_max_size) return false; unsigned sz = cls->get_num_args(); for (unsigned i = 0; i < sz; i++) { expr * lit = cls->get_arg(i); expr * lhs, * rhs, * arg; if (is_uninterp_const(lit)) { // positive literal } else if (m.is_not(lit, arg) && is_uninterp_const(arg)) { // negative literal } else if (x == 0 && m.is_eq(lit, lhs, rhs)) { // x = k literal if (is_uninterp_const(lhs) && m_util.is_numeral(rhs)) { x = to_app(lhs)->get_decl(); } else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs)) { x = to_app(rhs)->get_decl(); } else { return false; } } else { return false; } } if (x != 0) { var2clauses::obj_map_entry * entry = m_var2clauses.insert_if_not_there2(x, ptr_vector()); if (entry->get_data().m_value.empty() || entry->get_data().m_value.back()->get_num_args() == cls->get_num_args()) { entry->get_data().m_value.push_back(cls); return true; } } return false; } // temporary fields used by operator() and process extension_model_converter * mc1; filter_model_converter * mc2; expr_substitution * subst; goal_ref new_goal; obj_map bool2int; app * find_zero_cls(func_decl * x, ptr_vector & clauses) { ptr_vector::iterator it = clauses.begin(); ptr_vector::iterator end = clauses.end(); for (; it != end; ++it) { app * cls = *it; unsigned num = cls->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * lhs, * rhs; if (m.is_eq(cls->get_arg(i), lhs, rhs)) { if (is_uninterp_const(lhs) && m_util.is_zero(rhs)) return cls; if (is_uninterp_const(rhs) && m_util.is_zero(lhs)) return cls; } } } return 0; } // Find coeff (the k of literal (x = k)) of clause cls. // Store in idx the bit-vector representing the literals. // Example: idx = 101 if cls has three boolean literals p1, p2, p3 // where p1 = ~q1, p2 = q2, p3 = ~q3 // and q1 q2 q3 are the corresponding literals in the // zero clause. // Return false, if the boolean literals of cls cannot be matched with the literals // of zero_cls bool find_coeff(app * cls, app * zero_cls, unsigned & idx, rational & k) { unsigned num = zero_cls->get_num_args(); if (cls->get_num_args() != num) return false; idx = 0; unsigned val = 1; for (unsigned i = 0; i < num; i++) { expr * lit = zero_cls->get_arg(i); if (m.is_eq(lit)) continue; // search for lit or ~lit in cls unsigned j; for (j = 0; j < num; j++) { expr * lit2 = cls->get_arg(j); if (m.is_eq(lit2)) continue; if (lit2 == lit) break; if (m.is_complement(lit2, lit)) { idx += val; break; } } if (j == num) return false; // cls does not contain literal lit val *= 2; } // find k unsigned i; for (i = 0; i < num; i++) { expr * lhs, * rhs; if (m.is_eq(cls->get_arg(i), lhs, rhs) && (m_util.is_numeral(lhs, k) || m_util.is_numeral(rhs, k))) break; } if (i == num) return false; return true; } void mk_ivar(expr * lit, expr_ref & def, bool real_ctx) { expr * atom; bool sign; if (m.is_not(lit, atom)) { sign = true; } else { atom = lit; sign = false; } SASSERT(is_uninterp_const(atom)); expr * var; if (!bool2int.find(atom, var)) { var = m.mk_fresh_const(0, m_util.mk_int()); new_goal->assert_expr(m_util.mk_le(m_util.mk_numeral(rational(0), true), var)); new_goal->assert_expr(m_util.mk_le(var, m_util.mk_numeral(rational(1), true))); expr * bool_def = m.mk_eq(var, m_util.mk_numeral(rational(1), true)); subst->insert(atom, bool_def); if (m_produce_models) { mc2->insert(to_app(var)->get_decl()); mc1->insert(to_app(atom)->get_decl(), bool_def); } m.inc_ref(atom); m.inc_ref(var); bool2int.insert(atom, var); } expr * norm_var = real_ctx ? m_util.mk_to_real(var) : var; if (sign) def = m_util.mk_sub(m_util.mk_numeral(rational(1), !real_ctx), norm_var); else def = norm_var; } bool process(func_decl * x, ptr_vector & clauses) { unsigned cls_size = clauses.back()->get_num_args(); unsigned expected_num_clauses = 1 << (cls_size - 1); if (clauses.size() < expected_num_clauses) // using < instead of != because we tolerate duplicates return false; app * zero_cls = find_zero_cls(x, clauses); if (zero_cls == 0) return false; buffer found; // marks which idx were found buffer idx2coeff; found.resize(expected_num_clauses, false); idx2coeff.resize(expected_num_clauses); ptr_vector::iterator it = clauses.begin(); ptr_vector::iterator end = clauses.end(); for (; it != end; ++it) { app * cls = *it; unsigned idx; rational k; if (!find_coeff(cls, zero_cls, idx, k)) return false; SASSERT(idx < expected_num_clauses); if (found[idx] && k != idx2coeff[idx]) return false; found[idx] = true; idx2coeff[idx] = k; } unsigned num_bits = cls_size - 1; // check if idxs are consistent for (unsigned idx = 0; idx < expected_num_clauses; idx++) { if (!found[idx]) return false; // case is missing rational expected_k; unsigned idx_aux = idx; unsigned idx_bit = 1; for (unsigned j = 0; j < num_bits; j++) { if (idx_aux % 2 == 1) { expected_k += idx2coeff[idx_bit]; } idx_aux /= 2; idx_bit *= 2; } if (idx2coeff[idx] != expected_k) return false; } expr_ref_buffer def_args(m); expr_ref def(m); bool real_ctx = m_util.is_real(x->get_range()); unsigned idx_bit = 1; for (unsigned i = 0; i < cls_size; i++) { expr * lit = zero_cls->get_arg(i); if (m.is_eq(lit)) continue; mk_ivar(lit, def, real_ctx); def_args.push_back(m_util.mk_mul(m_util.mk_numeral(idx2coeff[idx_bit], !real_ctx), def)); idx_bit *= 2; } expr * x_def; if (def_args.size() == 1) x_def = def_args[0]; else x_def = m_util.mk_add(def_args.size(), def_args.c_ptr()); TRACE("recover_01", tout << x->get_name() << " --> " << mk_ismt2_pp(x_def, m) << "\n";); subst->insert(m.mk_const(x), x_def); if (m_produce_models) { mc1->insert(x, x_def); } return true; } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("recover-01", g); fail_if_unsat_core_generation("recover-01", g); m_produce_models = g->models_enabled(); mc = 0; pc = 0; core = 0; result.reset(); tactic_report report("recover-01", *g); bool saved = false; new_goal = alloc(goal, *g, true); SASSERT(new_goal->depth() == g->depth()); SASSERT(new_goal->prec() == g->prec()); new_goal->inc_depth(); unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { expr * f = g->form(i); if (save_clause(f)) { saved = true; } else { new_goal->assert_expr(f); } } if (!saved) { result.push_back(g.get()); return; } if (m_produce_models) { mc1 = alloc(extension_model_converter, m); mc2 = alloc(filter_model_converter, m); mc = concat(mc2, mc1); } dec_ref_key_values(m, bool2int); unsigned counter = 0; bool recovered = false; expr_substitution _subst(m); subst = &_subst; var2clauses::iterator it = m_var2clauses.begin(); var2clauses::iterator end = m_var2clauses.end(); for (; it != end; ++it) { if (process(it->m_key, it->m_value)) { recovered = true; counter++; } else { ptr_vector::iterator it2 = it->m_value.begin(); ptr_vector::iterator end2 = it->m_value.end(); for (; it2 != end2; ++it2) { new_goal->assert_expr(*it2); } } } if (!recovered) { result.push_back(g.get()); mc = 0; return; } report_tactic_progress(":recovered-01-vars", counter); m_rw.set_substitution(subst); expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = new_goal->size(); for (unsigned idx = 0; idx < size; idx++) { expr * curr = new_goal->form(idx); m_rw(curr, new_curr); new_goal->update(idx, new_curr); } result.push_back(new_goal.get()); TRACE("recover_01", new_goal->display(tout);); SASSERT(new_goal->is_well_sorted()); } ~imp() { dec_ref_key_values(m, bool2int); } }; imp * m_imp; params_ref m_params; public: recover_01_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(recover_01_tactic, m, m_params); } virtual ~recover_01_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { th_rewriter::get_param_descrs(r); r.insert("recover_01_max_bits", CPK_UINT, "(default: 10) maximum number of bits to consider in a clause."); } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { try { (*m_imp)(g, result, mc, pc, core); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } virtual void cleanup() { imp * d = alloc(imp, m_imp->m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } protected: virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_recover_01_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(recover_01_tactic, m, p)); } z3-4.4.0/src/tactic/arith/normalize_bounds_tactic.cpp0000644000175000017500000001470112540347414022422 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: normalize_bounds_tactic.cpp Abstract: Replace x with x' + l, when l <= x where x' is a fresh variable. Note that, after the transformation 0 <= x'. Author: Leonardo de Moura (leonardo) 2011-10-21. Revision History: --*/ #include"tactical.h" #include"bound_manager.h" #include"th_rewriter.h" #include"extension_model_converter.h" #include"filter_model_converter.h" #include"arith_decl_plugin.h" #include"expr_substitution.h" #include"ast_smt2_pp.h" class normalize_bounds_tactic : public tactic { struct imp { ast_manager & m; bound_manager m_bm; arith_util m_util; th_rewriter m_rw; bool m_normalize_int_only; imp(ast_manager & _m, params_ref const & p): m(_m), m_bm(m), m_util(m), m_rw(m, p) { updt_params(p); } void updt_params_core(params_ref const & p) { m_normalize_int_only = p.get_bool("norm_int_only", true); } void updt_params(params_ref const & p) { m_rw.updt_params(p); updt_params_core(p); } void set_cancel(bool f) { m_rw.set_cancel(f); } bool is_target(expr * var, rational & val) { bool strict; return is_uninterp_const(var) && (!m_normalize_int_only || m_util.is_int(var)) && m_bm.has_lower(var, val, strict) && !val.is_zero(); } bool is_target(expr * var) { rational val; return is_target(var, val); } bool has_lowers() { bound_manager::iterator it = m_bm.begin(); bound_manager::iterator end = m_bm.end(); for (; it != end; ++it) { TRACE("normalize_bounds_tactic", rational val; bool strict; tout << mk_ismt2_pp(*it, m) << " has_lower: " << m_bm.has_lower(*it, val, strict) << " val: " << val << "\n";); if (is_target(*it)) return true; } return false; } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { mc = 0; pc = 0; core = 0; bool produce_models = in->models_enabled(); bool produce_proofs = in->proofs_enabled(); tactic_report report("normalize-bounds", *in); m_bm(*in); if (!has_lowers()) { result.push_back(in.get()); // did not increase depth since it didn't do anything. return; } extension_model_converter * mc1 = 0; filter_model_converter * mc2 = 0; if (produce_models) { mc1 = alloc(extension_model_converter, m); mc2 = alloc(filter_model_converter, m); mc = concat(mc2, mc1); } unsigned num_norm_bounds = 0; expr_substitution subst(m); rational val; bound_manager::iterator it = m_bm.begin(); bound_manager::iterator end = m_bm.end(); for (; it != end; ++it) { expr * x = *it; if (is_target(x, val)) { num_norm_bounds++; sort * s = m.get_sort(x); app * x_prime = m.mk_fresh_const(0, s); expr * def = m_util.mk_add(x_prime, m_util.mk_numeral(val, s)); subst.insert(x, def); if (produce_models) { mc1->insert(to_app(x)->get_decl(), def); mc2->insert(x_prime->get_decl()); } } } report_tactic_progress(":normalized-bounds", num_norm_bounds); m_rw.set_substitution(&subst); expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = in->size(); for (unsigned idx = 0; idx < size; idx++) { expr * curr = in->form(idx); m_rw(curr, new_curr, new_pr); if (produce_proofs) { proof * pr = in->pr(idx); new_pr = m.mk_modus_ponens(pr, new_pr); } in->update(idx, new_curr, new_pr, in->dep(idx)); } TRACE("normalize_bounds_tactic", in->display(tout);); in->inc_depth(); result.push_back(in.get()); } }; imp * m_imp; params_ref m_params; public: normalize_bounds_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(normalize_bounds_tactic, m, m_params); } virtual ~normalize_bounds_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { insert_produce_models(r); r.insert("norm_int_only", CPK_BOOL, "(default: true) normalize only the bounds of integer constants."); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { try { (*m_imp)(in, result, mc, pc, core); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } virtual void cleanup() { ast_manager & m = m_imp->m; imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } protected: virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_normalize_bounds_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(normalize_bounds_tactic, m, p)); } z3-4.4.0/src/tactic/arith/bv2int_rewriter.cpp0000644000175000017500000004340312540347414020651 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv2int_rewriter.cpp Abstract: Basic rewriting rules for bv2int propagation. Author: Nikolaj (nbjorner) 2011-05-05 Notes: --*/ #include "bv2int_rewriter.h" #include "rewriter_def.h" #include "ast_pp.h" void bv2int_rewriter_ctx::update_params(params_ref const& p) { m_max_size = p.get_uint("max_bv_size", UINT_MAX); } struct lt_rational { bool operator()(rational const& a, rational const& b) const { return a < b; } }; void bv2int_rewriter_ctx::collect_power2(goal const& s) { ast_manager& m = m_trail.get_manager(); arith_util arith(m); bv_util bv(m); for (unsigned j = 0; j < s.size(); ++j) { expr* f = s.form(j); if (!m.is_or(f)) continue; unsigned sz = to_app(f)->get_num_args(); expr* x, *y, *v = 0; rational n; vector bounds; bool is_int, ok = true; for (unsigned i = 0; ok && i < sz; ++i) { expr* e = to_app(f)->get_arg(i); if (!m.is_eq(e, x, y)) { ok = false; break; } if (arith.is_numeral(y, n, is_int) && is_int && (x == v || v == 0)) { v = x; bounds.push_back(n); } else if (arith.is_numeral(x, n, is_int) && is_int && (y == v || v == 0)) { v = y; bounds.push_back(n); } else { ok = false; break; } } if (!ok || !v) continue; SASSERT(!bounds.empty()); lt_rational lt; // lt is a total order on rationals. std::sort(bounds.begin(), bounds.end(), lt); rational p(1); unsigned num_bits = 0; for (unsigned i = 0; ok && i < bounds.size(); ++i) { ok = (p == bounds[i]); p *= rational(2); ++num_bits; } if (!ok) continue; unsigned log2 = 0; for (unsigned i = 1; i <= num_bits; i *= 2) ++log2; if(log2 == 0) continue; expr* logx = m.mk_fresh_const("log2_v", bv.mk_sort(log2)); logx = bv.mk_zero_extend(num_bits - log2, logx); m_trail.push_back(logx); TRACE("bv2int_rewriter", tout << mk_pp(v, m) << " |-> " << mk_pp(logx, m) << "\n";); m_power2.insert(v, logx); } } bool bv2int_rewriter_ctx::is_power2(expr* x, expr*& log_x) { return m_power2.find(x, log_x); } bv2int_rewriter::bv2int_rewriter(ast_manager & m, bv2int_rewriter_ctx& ctx) :m_manager(m), m_ctx(ctx), m_bv(m), m_arith(m) { } br_status bv2int_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if(f->get_family_id() == m_arith.get_family_id()) { switch (f->get_decl_kind()) { case OP_NUM: return BR_FAILED; case OP_LE: SASSERT(num_args == 2); return mk_le(args[0], args[1], result); case OP_GE: SASSERT(num_args == 2); return mk_ge(args[0], args[1], result); case OP_LT: SASSERT(num_args == 2); return mk_lt(args[0], args[1], result); case OP_GT: SASSERT(num_args == 2); return mk_gt(args[0], args[1], result); case OP_ADD: return mk_add(num_args, args, result); case OP_MUL: return mk_mul(num_args, args, result); case OP_SUB: return mk_sub(num_args, args, result); case OP_DIV: return BR_FAILED; case OP_IDIV: SASSERT(num_args == 2); return mk_idiv(args[0], args[1], result); case OP_MOD: SASSERT(num_args == 2); return mk_mod(args[0], args[1], result); case OP_REM: SASSERT(num_args == 2); return mk_rem(args[0], args[1], result); case OP_UMINUS: SASSERT(num_args == 1); return mk_uminus(args[0], result); case OP_TO_REAL: return BR_FAILED; case OP_TO_INT: return BR_FAILED; case OP_IS_INT: return BR_FAILED; default: return BR_FAILED; } } if (f->get_family_id() == m().get_basic_family_id()) { switch (f->get_decl_kind()) { case OP_EQ: SASSERT(num_args == 2); return mk_eq(args[0], args[1], result); case OP_ITE: SASSERT(num_args == 3); return mk_ite(args[0], args[1], args[2], result); default: return BR_FAILED; } } return BR_FAILED; } br_status bv2int_rewriter::mk_le(expr * s, expr * t, expr_ref & result) { expr_ref s1(m()), t1(m()), s2(m()), t2(m()); if (is_bv2int(s, s1) && is_bv2int(t, t1)) { align_sizes(s1, t1, false); result = m_bv.mk_ule(s1, t1); return BR_DONE; } if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) { // s1 - s2 <= t1 - t2 // <=> // s1 + t2 <= t1 + s2 // s1 = mk_bv_add(s1, t2, false); t1 = mk_bv_add(t1, s2, false); align_sizes(s1, t1, false); result = m_bv.mk_ule(s1, t1); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { align_sizes(s1, t1, true); result = m_bv.mk_sle(s1, t1); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) { result = m().mk_not(m_arith.mk_le(arg2, arg1)); return BR_REWRITE2; } br_status bv2int_rewriter::mk_ge(expr * arg1, expr * arg2, expr_ref & result) { return mk_le(arg2, arg1, result); } br_status bv2int_rewriter::mk_gt(expr * arg1, expr * arg2, expr_ref & result) { result = m().mk_not(m_arith.mk_le(arg1, arg2)); return BR_REWRITE2; } br_status bv2int_rewriter::mk_ite(expr* c, expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), t1(m()); if (is_bv2int(s, s1) && is_bv2int(t, t1)) { align_sizes(s1, t1, false); result = m_bv.mk_bv2int(m().mk_ite(c, s1, t1)); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { align_sizes(s1, t1, true); result = mk_sbv2int(m().mk_ite(c, s1, t1)); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_eq(expr * s, expr * t, expr_ref & result) { expr_ref s1(m()), t1(m()), s2(m()), t2(m()); if (is_bv2int(s, s1) && is_bv2int(t, t1)) { align_sizes(s1, t1, false); result = m().mk_eq(s1, t1); return BR_DONE; } if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) { s1 = mk_bv_add(s1, t2, false); t1 = mk_bv_add(s2, t1, false); align_sizes(s1, t1, false); result = m().mk_eq(s1, t1); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { align_sizes(s1, t1, true); result = m().mk_eq(s1, t1); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_idiv(expr * arg1, expr * arg2, expr_ref & result) { // TBD return BR_FAILED; } br_status bv2int_rewriter::mk_mod(expr * s, expr * t, expr_ref & result) { expr_ref s1(m()), s2(m()), t1(m()); if (is_bv2int(s, s1) && is_bv2int(t, t1)) { align_sizes(s1, t1, false); result = m_bv.mk_bv2int(m_bv.mk_bv_urem(s1, t1)); TRACE("bv2int_rewriter", tout << mk_pp(result,m()) << "\n";); return BR_DONE; } // // (s1 - s2) mod t1 = (s1 + (t1 - (s2 mod t1))) mod t1 // if (is_bv2int_diff(s, s1, s2) && is_bv2int(t, t1)) { expr_ref u1(m()); align_sizes(s1, t1, false); u1 = m_bv.mk_bv_urem(s1, t1); u1 = m_bv.mk_bv_sub(t1, u1); u1 = mk_bv_add(s1, u1, false); align_sizes(u1, t1, false); result = m_bv.mk_bv2int(m_bv.mk_bv_urem(u1, t1)); TRACE("bv2int_rewriter", tout << mk_pp(result,m()) << "\n";); return BR_DONE; } #if 0 // TBD: check semantics if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { align_sizes(s1, t1, true); result = mk_sbv2int(m_bv.mk_bv_srem(s1, t1)); return BR_DONE; } #endif return BR_FAILED; } br_status bv2int_rewriter::mk_rem(expr * arg1, expr * arg2, expr_ref & result) { // TBD return BR_FAILED; } br_status bv2int_rewriter::mk_uminus(expr * s, expr_ref & result) { expr_ref s1(m()), s2(m()); if (is_bv2int_diff(s, s1, s2)) { result = m_arith.mk_sub(m_bv.mk_bv2int(s2), m_bv.mk_bv2int(s1)); return BR_DONE; } if (is_sbv2int(s, s1)) { result = mk_sbv2int(m_bv.mk_bv_neg(s1)); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_add(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_add(result, args[i], result); } return r; } void bv2int_rewriter::align_sizes(expr_ref& s, expr_ref& t, bool is_signed) { unsigned sz1 = m_bv.get_bv_size(s); unsigned sz2 = m_bv.get_bv_size(t); if (sz1 > sz2 && is_signed) { t = mk_extend(sz1-sz2, t, true); } if (sz1 > sz2 && !is_signed) { t = mk_extend(sz1-sz2, t, false); } if (sz1 < sz2 && is_signed) { s = mk_extend(sz2-sz1, s, true); } if (sz1 < sz2 && !is_signed) { s = mk_extend(sz2-sz1, s, false); } } bool bv2int_rewriter::is_zero(expr* n) { rational r; unsigned sz; return m_bv.is_numeral(n, r, sz) && r.is_zero(); } expr* bv2int_rewriter::mk_bv_add(expr* s, expr* t, bool is_signed) { SASSERT(m_bv.is_bv(s)); SASSERT(m_bv.is_bv(t)); if (is_zero(s)) { return t; } if (is_zero(t)) { return s; } expr_ref s1(s, m()), t1(t, m()); align_sizes(s1, t1, is_signed); s1 = mk_extend(1, s1, is_signed); t1 = mk_extend(1, t1, is_signed); return m_bv.mk_bv_add(s1, t1); } br_status bv2int_rewriter::mk_add(expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), t1(m()), s2(m()), t2(m()); if (is_bv2int(s, s1) && is_bv2int(t, t1)) { result = m_bv.mk_bv2int(mk_bv_add(s1, t1, false)); return BR_DONE; } if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) { // s1 - s2 + t1 - t2 // = // s1 + t1 - (s2 + t2) // t1 = m_bv.mk_bv2int(mk_bv_add(s1, t1, false)); t2 = m_bv.mk_bv2int(mk_bv_add(s2, t2, false)); result = m_arith.mk_sub(t1, t2); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { result = mk_sbv2int(mk_bv_add(s1, t1, true)); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_mul(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_mul(result, args[i], result); } return r; } expr* bv2int_rewriter::mk_bv_mul(expr* s, expr* t, bool is_signed) { SASSERT(m_bv.is_bv(s)); SASSERT(m_bv.is_bv(t)); if (is_zero(s)) { return s; } if (is_zero(t)) { return t; } rational r; unsigned sz; if (m_bv.is_numeral(s, r, sz) && r.is_one()) { return t; } if (m_bv.is_numeral(t, r, sz) && r.is_one()) { return s; } expr_ref s1(s, m()), t1(t, m()); align_sizes(s1, t1, is_signed); unsigned n = m_bv.get_bv_size(t1); unsigned max_bits = m_ctx.get_max_num_bits(); bool add_side_conds = 2*n > max_bits; if (n >= max_bits) { // } else if (2*n > max_bits) { s1 = mk_extend(max_bits-n, s1, is_signed); t1 = mk_extend(max_bits-n, t1, is_signed); } else { s1 = mk_extend(n, s1, is_signed); t1 = mk_extend(n, t1, is_signed); } if (add_side_conds) { if (is_signed) { m_ctx.add_side_condition(m_bv.mk_bvsmul_no_ovfl(s1, t1)); m_ctx.add_side_condition(m_bv.mk_bvsmul_no_udfl(s1, t1)); } else { m_ctx.add_side_condition(m_bv.mk_bvumul_no_ovfl(s1, t1)); } } return m_bv.mk_bv_mul(s1, t1); } br_status bv2int_rewriter::mk_mul(expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); if ((is_shl1(s, s1) && is_bv2int(t, t1)) || (is_shl1(t, s1) && is_bv2int(s, t1))) { unsigned n = m_bv.get_bv_size(s1); unsigned m = m_bv.get_bv_size(t1); s1 = mk_extend(m, s1, false); t1 = mk_extend(n, t1, false); result = m_bv.mk_bv2int(m_bv.mk_bv_shl(t1, s1)); return BR_DONE; } if (is_bv2int(s, s1) && is_bv2int(t, t1)) { result = m_bv.mk_bv2int(mk_bv_mul(s1, t1, false)); return BR_DONE; } if ((is_bv2int(s, s1) && is_bv2int_diff(t, t1, t2)) || (is_bv2int(t, s1) && is_bv2int_diff(s, t1, t2))) { t1 = m_bv.mk_bv2int(mk_bv_mul(s1, t1, false)); t2 = m_bv.mk_bv2int(mk_bv_mul(s1, t2, false)); result = m_arith.mk_sub(t1, t2); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { result = mk_sbv2int(mk_bv_mul(s1, t1, true)); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_sub(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_sub(result, args[i], result); } return r; } br_status bv2int_rewriter::mk_sub(expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), t1(m()), s2(m()), t2(m()); if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) { // s1 - s2 - (t1 - t2) // = // s1 + t2 - (t1 + s2) // s1 = m_bv.mk_bv2int(mk_bv_add(s1, t2, false)); s2 = m_bv.mk_bv2int(mk_bv_add(s2, t1, false)); result = m_arith.mk_sub(s1, s2); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { align_sizes(s1, t1, true); s1 = m_bv.mk_sign_extend(1, s1); t1 = m_bv.mk_sign_extend(1, t1); result = mk_sbv2int(m_bv.mk_bv_sub(s1, t1)); return BR_DONE; } return BR_FAILED; } bool bv2int_rewriter::is_bv2int(expr* n, expr_ref& s) { rational k; bool is_int; if (m_bv.is_bv2int(n)) { s = to_app(n)->get_arg(0); return true; } if (m_arith.is_numeral(n, k, is_int) && is_int && !k.is_neg()) { unsigned sz = k.get_num_bits(); s = m_bv.mk_numeral(k, m_bv.mk_sort(sz)); return true; } return false; } bool bv2int_rewriter::is_shl1(expr* n, expr_ref& s) { expr* s1, *s2; rational r; unsigned bv_size; if(m_bv.is_bv2int(n, s2) && m_bv.is_bv_shl(s2, s1, s2) && m_bv.is_numeral(s1, r, bv_size) && r.is_one()) { s = s2; return true; } return false; } bool bv2int_rewriter::is_bv2int_diff(expr* n, expr_ref& s, expr_ref& t) { if (is_bv2int(n, s)) { t = m_bv.mk_numeral(0, 1); return true; } rational k; bool is_int; if (m_arith.is_numeral(n, k, is_int) && is_int) { SASSERT(k.is_neg()); k.neg(); unsigned sz = k.get_num_bits(); t = m_bv.mk_numeral(k, m_bv.mk_sort(sz)); s = m_bv.mk_numeral(0, 1); return true; } // // bv2int(a) - bv2int(b) // expr *e1, *e2; if (m_arith.is_sub(n, e1, e2) && is_bv2int(e1, s) && is_bv2int(e2, t)) { return true; } return false; } bool bv2int_rewriter::is_sbv2int(expr* n, expr_ref& s) { if (is_bv2int(n, s)) { s = m_bv.mk_zero_extend(1, s); return true; } expr_ref u1(m()), u2(m()); if (is_bv2int_diff(n, u1, u2)) { align_sizes(u1, u2, false); u1 = mk_extend(1, u1, false); u2 = mk_extend(1, u2, false); s = m_bv.mk_bv_sub(u1, u2); return true; } // ite(bv1 == b[n-1:n-1], bv2int(b[0:n-2]) - 2^{n-1}, bv2int(b[0:n-2])) expr* c, *t, *e1, *c1, *c2, *c3, *t1, *t2, *e2, *e3; rational k; bool is_int; unsigned lo, hi, lo1, hi1, sz; if (m().is_ite(n, c, t, e1) && m().is_eq(c, c1, c2) && m_bv.is_numeral(c1, k, sz) && k.is_one() && sz == 1 && m_bv.is_extract(c2, lo, hi, c3) && lo == hi && lo == m_bv.get_bv_size(c3) - 1 && m_arith.is_sub(t, t1, t2) && e1 == t1 && m_bv.is_bv2int(e1, e2) && m_bv.is_extract(e2, lo1, hi1, e3) && lo1 == 0 && hi1 == hi-1 && m_arith.is_numeral(t2, k, is_int) && is_int && k == rational::power_of_two(hi) ) { s = e3; return true; } #if 0 // bv2int(b[0:n-2]) - ite(bv1 == b[n-1:n-1], 2^{n-1}, 0) if (m().is_sub(n, e1, e2) && m_bv.is_bv2int(e1, e3) && m_bv.is_extract(e3, lo, hi, e4) && lo == 0 && hi == m_bv.get_bv_size(e4) - 2 && m().is_ite(e2, t1, t2, t3) && m().is_eq(t1, c1, c2) && m_bv.is_numeral(c1, k, sz) && k.is_one() && sz == 1 && m_bv.is_extract(c2, lo1, hi1, c3) && lo1 == h1 + 1 && hi1 == lo1 && c3 == e4 && m_arith.is_numeral(t2, )) { } #endif return false; } expr* bv2int_rewriter::mk_sbv2int(expr* b) { // // ite(bit1 = b[n-1:n-1], bv2int(b[0:n-2]) - 2^{n-1}, bv2int(b[0:n-2])) // expr* bv1 = m_bv.mk_numeral(1, 1); unsigned n = m_bv.get_bv_size(b); expr* c = m().mk_eq(bv1, m_bv.mk_extract(n-1, n-1, b)); expr* e = m_bv.mk_bv2int(m_bv.mk_extract(n-2, 0, b)); expr* t = m_arith.mk_sub(e, m_arith.mk_numeral(power(rational(2), n-1), true)); return m().mk_ite(c, t, e); } expr* bv2int_rewriter::mk_extend(unsigned sz, expr* b, bool is_signed) { if (sz == 0) { return b; } rational r; unsigned bv_sz; if (is_signed) { return m_bv.mk_sign_extend(sz, b); } else if (m_bv.is_numeral(b, r, bv_sz)) { return m_bv.mk_numeral(r, bv_sz + sz); } else { return m_bv.mk_zero_extend(sz, b); } } template class rewriter_tpl; z3-4.4.0/src/tactic/arith/probe_arith.h0000644000175000017500000000552012540347414017463 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: probe_arith.h Abstract: Some probes for arithmetic problems. Author: Leonardo de Moura (leonardo) 2012-03-01. Revision History: --*/ #ifndef _PROBE_ARITH_H_ #define _PROBE_ARITH_H_ class probe; probe * mk_arith_avg_bw_probe(); probe * mk_arith_max_bw_probe(); probe * mk_arith_avg_degree_probe(); probe * mk_arith_max_degree_probe(); /* ADD_PROBE("arith-max-deg", "max polynomial total degree of an arithmetic atom.", "mk_arith_max_degree_probe()") ADD_PROBE("arith-avg-deg", "avg polynomial total degree of an arithmetic atom.", "mk_arith_avg_degree_probe()") ADD_PROBE("arith-max-bw", "max coefficient bit width.", "mk_arith_max_bw_probe()") ADD_PROBE("arith-avg-bw", "avg coefficient bit width.", "mk_arith_avg_bw_probe()") */ probe * mk_is_qflia_probe(); probe * mk_is_qfauflia_probe(); probe * mk_is_qflra_probe(); probe * mk_is_qflira_probe(); probe * mk_is_ilp_probe(); probe * mk_is_mip_probe(); /* ADD_PROBE("is-qflia", "true if the goal is in QF_LIA.", "mk_is_qflia_probe()") ADD_PROBE("is-qfauflia", "true if the goal is in QF_AUFLIA.", "mk_is_qfauflia_probe()") ADD_PROBE("is-qflra", "true if the goal is in QF_LRA.", "mk_is_qflra_probe()") ADD_PROBE("is-qflira", "true if the goal is in QF_LIRA.", "mk_is_qflira_probe()") ADD_PROBE("is-ilp", "true if the goal is ILP.", "mk_is_ilp_probe()") */ probe * mk_is_qfnia_probe(); probe * mk_is_qfnra_probe(); probe * mk_is_nia_probe(); probe * mk_is_nra_probe(); probe * mk_is_nira_probe(); probe * mk_is_lia_probe(); probe * mk_is_lra_probe(); probe * mk_is_lira_probe(); probe * mk_is_qfufnra_probe(); /* ADD_PROBE("is-qfnia", "true if the goal is in QF_NIA (quantifier-free nonlinear integer arithmetic).", "mk_is_qfnia_probe()") ADD_PROBE("is-qfnra", "true if the goal is in QF_NRA (quantifier-free nonlinear real arithmetic).", "mk_is_qfnra_probe()") ADD_PROBE("is-nia", "true if the goal is in NIA (nonlinear integer arithmetic, formula may have quantifiers).", "mk_is_nia_probe()") ADD_PROBE("is-nra", "true if the goal is in NRA (nonlinear real arithmetic, formula may have quantifiers).", "mk_is_nra_probe()") ADD_PROBE("is-nira", "true if the goal is in NIRA (nonlinear integer and real arithmetic, formula may have quantifiers).", "mk_is_nira_probe()") ADD_PROBE("is-lia", "true if the goal is in LIA (linear integer arithmetic, formula may have quantifiers).", "mk_is_lia_probe()") ADD_PROBE("is-lra", "true if the goal is in LRA (linear real arithmetic, formula may have quantifiers).", "mk_is_lra_probe()") ADD_PROBE("is-lira", "true if the goal is in LIRA (linear integer and real arithmetic, formula may have quantifiers).", "mk_is_lira_probe()") ADD_PROBE("is-qfufnra", "true if the goal is QF_UFNRA (quantifier-free nonlinear real arithmetic with other theories).", "mk_is_qfufnra_probe()") */ #endif z3-4.4.0/src/tactic/arith/nla2bv_tactic.h0000644000175000017500000000126012540347414017675 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: nla2bv_tactic.cpp Abstract: Convert quantified NIA problems to bounded bit-vector arithmetic problems. Author: Nikolaj (nbjorner) 2011-05-3 Notes: Ported to tactic framework on 2012-02-28 --*/ #ifndef _NLA2BV_TACTIC_H_ #define _NLA2BV_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_nla2bv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("nla2bv", "convert a nonlinear arithmetic problem into a bit-vector problem, in most cases the resultant goal is an under approximation and is useul for finding models.", "mk_nla2bv_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/arith/arith_bounds_tactic.cpp0000644000175000017500000001145512540347414021534 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include"arith_bounds_tactic.h" #include"arith_decl_plugin.h" struct arith_bounds_tactic : public tactic { ast_manager& m; arith_util a; volatile bool m_cancel; arith_bounds_tactic(ast_manager& m): m(m), a(m), m_cancel(false) { } ast_manager& get_manager() { return m; } void set_cancel(bool f) { m_cancel = f; } virtual void cleanup() { m_cancel = false; } virtual void operator()(/* in */ goal_ref const & in, /* out */ goal_ref_buffer & result, /* out */ model_converter_ref & mc, /* out */ proof_converter_ref & pc, /* out */ expr_dependency_ref & core) { bounds_arith_subsumption(in, result); } virtual tactic* translate(ast_manager & mgr) { return alloc(arith_bounds_tactic, mgr); } void checkpoint() { if (m_cancel) { throw tactic_exception(TACTIC_CANCELED_MSG); } } struct info { rational r; unsigned idx; bool is_strict;}; /** \brief Basic arithmetic subsumption simplification based on bounds. */ void mk_proof(proof_ref& pr, goal_ref const& s, unsigned i, unsigned j) { if (s->proofs_enabled()) { proof* th_lemma = m.mk_th_lemma(a.get_family_id(), m.mk_implies(s->form(i), s->form(j)), 0, 0); pr = m.mk_modus_ponens(s->pr(i), th_lemma); } } bool is_le_or_lt(expr* e, expr*& e1, expr*& e2, bool& is_strict) { bool is_negated = m.is_not(e, e); if ((!is_negated && (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1))) || (is_negated && (a.is_lt(e, e2, e1) || a.is_gt(e, e1, e2)))) { is_strict = false; return true; } if ((!is_negated && (a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1))) || (is_negated && (a.is_le(e, e2, e1) || a.is_ge(e, e1, e2)))) { is_strict = true; return true; } return false; } void bounds_arith_subsumption(goal_ref const& g, goal_ref_buffer& result) { info inf; rational r; goal_ref s(g); // initialize result. obj_map lower, upper; expr* e1, *e2; TRACE("arith_subsumption", s->display(tout); ); for (unsigned i = 0; i < s->size(); ++i) { checkpoint(); expr* lemma = s->form(i); bool is_strict = false; bool is_lower = false; if (!is_le_or_lt(lemma, e1, e2, is_strict)) { continue; } // e1 <= e2 or e1 < e2 if (a.is_numeral(e2, r)) { is_lower = true; } else if (a.is_numeral(e1, r)) { is_lower = false; } else { continue; } proof_ref new_pr(m); if (is_lower && upper.find(e1, inf)) { if (inf.r > r || (inf.r == r && is_strict && !inf.is_strict)) { mk_proof(new_pr, s, i, inf.idx); s->update(inf.idx, m.mk_true(), new_pr); inf.r = r; inf.is_strict = is_strict; inf.idx = i; upper.insert(e1, inf); } else { mk_proof(new_pr, s, inf.idx, i); s->update(i, m.mk_true(), new_pr); } } else if (is_lower) { inf.r = r; inf.is_strict = is_strict; inf.idx = i; upper.insert(e1, inf); } else if (!is_lower && lower.find(e2, inf)) { if (inf.r < r || (inf.r == r && is_strict && !inf.is_strict)) { mk_proof(new_pr, s, i, inf.idx); s->update(inf.idx, m.mk_true(), new_pr); inf.r = r; inf.is_strict = is_strict; inf.idx = i; lower.insert(e2, inf); } else { mk_proof(new_pr, s, inf.idx, i); s->update(i, m.mk_true()); } } else if (!is_lower) { inf.r = r; inf.is_strict = is_strict; inf.idx = i; lower.insert(e2, inf); } } s->elim_true(); result.push_back(s.get()); TRACE("arith_subsumption", s->display(tout); ); } }; tactic * mk_arith_bounds_tactic(ast_manager & m, params_ref const & p) { return alloc(arith_bounds_tactic, m); } z3-4.4.0/src/tactic/arith/diff_neq_tactic.cpp0000644000175000017500000003302412540347414020622 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: diff_neq_tactic.cpp Abstract: Solver for integer problems that contains literals of the form k <= x x <= k x - y != k And all variables are bounded. Author: Leonardo de Moura (leonardo) 2012-02-07. Revision History: --*/ #include"tactical.h" #include"arith_decl_plugin.h" #include"ast_smt2_pp.h" #include"model.h" class diff_neq_tactic : public tactic { struct imp { ast_manager & m; arith_util u; typedef unsigned var; expr_ref_vector m_var2expr; obj_map m_expr2var; svector m_lower; svector m_upper; struct diseq { var m_y; int m_k; diseq(var y, int k):m_y(y), m_k(k) {} }; typedef svector diseqs; vector m_var_diseqs; typedef svector decision_stack; decision_stack m_stack; volatile bool m_cancel; bool m_produce_models; rational m_max_k; rational m_max_neg_k; unsigned m_num_conflicts; imp(ast_manager & _m, params_ref const & p): m(_m), u(m), m_var2expr(m) { updt_params(p); m_cancel = false; } void updt_params(params_ref const & p) { m_max_k = rational(p.get_uint("diff_neq_max_k", 1024)); m_max_neg_k = -m_max_k; if (m_max_k >= rational(INT_MAX/2)) m_max_k = rational(INT_MAX/2); } void set_cancel(bool f) { m_cancel = f; } void throw_not_supported() { throw tactic_exception("goal is not diff neq"); } unsigned num_vars() const { return m_upper.size(); } var mk_var(expr * t) { SASSERT(is_uninterp_const(t)); var x; if (m_expr2var.find(t, x)) return x; x = m_upper.size(); m_expr2var.insert(t, x); m_var2expr.push_back(t); m_lower.push_back(INT_MIN); // unknown m_upper.push_back(INT_MAX); // unknown m_var_diseqs.push_back(diseqs()); return x; } void process_le(expr * lhs, expr * rhs) { if (!u.is_int(lhs)) throw_not_supported(); rational k; if (is_uninterp_const(lhs) && u.is_numeral(rhs, k) && m_max_neg_k <= k && k <= m_max_k) { var x = mk_var(lhs); int _k = static_cast(k.get_int64()); m_upper[x] = _k; } else if (is_uninterp_const(rhs) && u.is_numeral(lhs, k) && m_max_neg_k <= k && k <= m_max_k) { var x = mk_var(rhs); int _k = static_cast(k.get_int64()); m_lower[x] = _k; } else { throw_not_supported(); } } // process t1 - t2 != k void process_neq_core(expr * t1, expr * t2, int k) { var x1 = mk_var(t1); var x2 = mk_var(t2); if (x1 == x2) throw_not_supported(); // must simplify first if (x1 < x2) { std::swap(x1, x2); k = -k; } m_var_diseqs[x1].push_back(diseq(x2, k)); } void process_neq(expr * lhs, expr * rhs) { if (!u.is_int(lhs)) throw_not_supported(); if (is_uninterp_const(lhs) && is_uninterp_const(rhs)) { process_neq_core(lhs, rhs, 0); return; } if (u.is_numeral(lhs)) std::swap(lhs, rhs); rational k; if (!u.is_numeral(rhs, k)) throw_not_supported(); if (!(m_max_neg_k <= k && k <= m_max_k)) throw_not_supported(); int _k = static_cast(k.get_int64()); expr * t1, * t2, * mt1, * mt2; if (u.is_add(lhs, t1, t2)) { if (is_uninterp_const(t1) && u.is_times_minus_one(t2, mt2) && is_uninterp_const(mt2)) process_neq_core(t1, mt2, _k); else if (is_uninterp_const(t2) && u.is_times_minus_one(t1, mt1) && is_uninterp_const(mt1)) process_neq_core(t2, mt1, _k); else throw_not_supported(); } else { throw_not_supported(); } } // throws exception if contains unbounded variable void check_unbounded() { unsigned num = num_vars(); for (var x = 0; x < num; x++) { if (m_lower[x] == INT_MIN || m_upper[x] == INT_MAX) throw_not_supported(); // possible extension: support bound normalization here if (m_lower[x] != 0) throw_not_supported(); // use bound normalizer } } void compile(goal const & g) { expr * lhs; expr * rhs; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * f = g.form(i); TRACE("diff_neq_tactic", tout << "processing: " << mk_ismt2_pp(f, m) << "\n";); if (u.is_le(f, lhs, rhs)) process_le(lhs, rhs); else if (u.is_ge(f, lhs, rhs)) process_le(rhs, lhs); else if (m.is_not(f, f) && m.is_eq(f, lhs, rhs)) process_neq(lhs, rhs); else throw_not_supported(); } check_unbounded(); } void display(std::ostream & out) { unsigned num = num_vars(); for (var x = 0; x < num; x++) { out << m_lower[x] << " <= " << mk_ismt2_pp(m_var2expr.get(x), m) << " <= " << m_upper[x] << "\n"; } for (var x = 0; x < num; x++) { diseqs::iterator it = m_var_diseqs[x].begin(); diseqs::iterator end = m_var_diseqs[x].end(); for (; it != end; ++it) { out << mk_ismt2_pp(m_var2expr.get(x), m) << " != " << mk_ismt2_pp(m_var2expr.get(it->m_y), m) << " + " << it->m_k << "\n"; } } } void display_model(std::ostream & out) { unsigned num = m_stack.size(); for (var x = 0; x < num; x++) { out << mk_ismt2_pp(m_var2expr.get(x), m) << " := " << m_stack[x] << "\n"; } } svector m_forbidden; // make sure m_forbidden.size() > max upper bound void init_forbidden() { int max = 0; unsigned num = num_vars(); for (var x = 0; x < num; x++) { if (m_upper[x] > max) max = m_upper[x]; } m_forbidden.reset(); m_forbidden.resize(max+1, false); } // Return a value v s.t. v >= starting_at and v <= m_upper[x] and all diseqs in m_var_diseqs[x] are satisfied. // Return -1 if such value does not exist. int choose_value(var x, int starting_at) { int max = starting_at-1; int v = starting_at; int upper = m_upper[x]; if (starting_at > upper) return -1; diseqs const & ds = m_var_diseqs[x]; diseqs::const_iterator it = ds.begin(); diseqs::const_iterator end = ds.end(); for (; it != end; ++it) { int bad_v = m_stack[it->m_y] + it->m_k; if (bad_v < v) continue; if (bad_v > upper) continue; if (bad_v == v) { while (true) { v++; if (v > upper) return -1; if (!m_forbidden[v]) break; m_forbidden[v] = false; } continue; } SASSERT(bad_v > v && bad_v <= upper); m_forbidden[bad_v] = true; if (bad_v > max) max = bad_v; } // reset forbidden for (int i = starting_at + 1; i <= max; i++) m_forbidden[i] = false; DEBUG_CODE({ for (unsigned i = 0; i < m_forbidden.size(); i++) { SASSERT(!m_forbidden[i]); } }); return v; } bool extend_model(var x) { int v = choose_value(x, 0); if (v == -1) return false; m_stack.push_back(v); return true; } bool resolve_conflict() { m_num_conflicts++; while (!m_stack.empty()) { int v = m_stack.back(); m_stack.pop_back(); var x = m_stack.size(); v = choose_value(x, v+1); if (v != -1) { m_stack.push_back(v); return true; } } return false; } bool search() { m_num_conflicts = 0; init_forbidden(); unsigned nvars = num_vars(); while (m_stack.size() < nvars) { if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); TRACE("diff_neq_tactic", display_model(tout);); var x = m_stack.size(); if (extend_model(x)) continue; if (!resolve_conflict()) return false; } TRACE("diff_neq_tactic", display_model(tout);); return true; } model * mk_model() { model * md = alloc(model, m); unsigned num = num_vars(); SASSERT(m_stack.size() == num); for (var x = 0; x < num; x++) { func_decl * d = to_app(m_var2expr.get(x))->get_decl(); md->register_decl(d, u.mk_numeral(rational(m_stack[x]), true)); } return md; } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); m_produce_models = g->models_enabled(); mc = 0; pc = 0; core = 0; result.reset(); tactic_report report("diff-neq", *g); fail_if_proof_generation("diff-neq", g); fail_if_unsat_core_generation("diff-neq", g); if (g->inconsistent()) { result.push_back(g.get()); return; } compile(*g); TRACE("diff_neq_tactic", g->display(tout); display(tout);); bool r = search(); report_tactic_progress(":conflicts", m_num_conflicts); if (r) { if (m_produce_models) mc = model2model_converter(mk_model()); g->reset(); } else { g->assert_expr(m.mk_false()); } g->inc_depth(); result.push_back(g.get()); TRACE("diff_neq", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: diff_neq_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(diff_neq_tactic, m, m_params); } virtual ~diff_neq_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { r.insert("diff_neq_max_k", CPK_UINT, "(default: 1024) maximum variable upper bound for diff neq solver."); } virtual void collect_statistics(statistics & st) const { st.update("conflicts", m_imp->m_num_conflicts); } virtual void reset_statistics() { m_imp->m_num_conflicts = 0; } /** \brief Fix a DL variable in s to 0. If s is not really in the difference logic fragment, then this is a NOOP. */ virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } virtual void cleanup() { imp * d = alloc(imp, m_imp->m, m_params); d->m_num_conflicts = m_imp->m_num_conflicts; #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } protected: virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_diff_neq_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(diff_neq_tactic, m, p)); } z3-4.4.0/src/tactic/arith/propagate_ineqs_tactic.h0000644000175000017500000000203612540347414021674 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: propagate_ineqs_tactic.h Abstract: This tactic performs the following tasks: - Propagate bounds using the bound_propagator. - Eliminate subsumed inequalities. For example: x - y >= 3 can be replaced with true if we know that x >= 3 and y <= 0 - Convert inequalities of the form p <= k and p >= k into p = k, where p is a polynomial and k is a constant. This strategy assumes the input is in arith LHS mode. This can be achieved by using option :arith-lhs true in the simplifier. Author: Leonardo (leonardo) 2012-02-19 Notes: --*/ #ifndef _PROPAGATE_INEQS_TACTIC_H_ #define _PROPAGATE_INEQS_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_propagate_ineqs_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("propagate-ineqs", "propagate ineqs/bounds, remove subsumed inequalities.", "mk_propagate_ineqs_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/arith/elim01_tactic.h0000644000175000017500000000101112540347414017572 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: elim01_tactic.h Abstract: Replace 0-1 integer variables by Booleans. Author: Nikolaj Bjorner (nbjorner) 2013-12-7 Notes: --*/ #ifndef _ELIM01_TACTIC_H_ #define _ELIM01_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_elim01_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("elim01", "eliminate 0-1 integer variables, replace them by Booleans.", "mk_elim01_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/arith/elim01_tactic.cpp0000644000175000017500000002142312540347414020136 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: elim01_tactic.cpp Abstract: Replace 0-1 integer variables by Booleans. Author: Nikolaj Bjorner (nbjorner) 2013-12-7 Notes: --*/ #include"tactical.h" #include"cooperate.h" #include"bound_manager.h" #include"ast_pp.h" #include"expr_safe_replace.h" // NB: should use proof-producing expr_substitute in polished version. #include"arith_decl_plugin.h" #include"elim01_tactic.h" #include"model_smt2_pp.h" #include"th_rewriter.h" class bool2int_model_converter : public model_converter { ast_manager& m; arith_util a; func_decl_ref_vector m_refs; obj_hashtable m_bools; vector > m_nums_as_bool; ptr_vector m_nums_as_int; public: bool2int_model_converter(ast_manager& m): m(m), a(m), m_refs(m) {} virtual void operator()(model_ref & old_model, unsigned goal_idx) { SASSERT(goal_idx == 0); model * new_model = alloc(model, m); unsigned num = old_model->get_num_constants(); for (unsigned i = 0; i < m_nums_as_int.size(); ++i) { func_decl* f_old = m_nums_as_int[i]; rational val(0); rational po(1); bool is_value = true; for (unsigned j = 0; is_value && j < m_nums_as_bool[i].size(); ++j) { func_decl* f = m_nums_as_bool[i][j]; expr* fi = old_model->get_const_interp(f); if (!fi) { is_value = false; } else if (m.is_true(fi)) { val += po; } else if (!m.is_false(fi)) { is_value = false; } po *= rational(2); } if (is_value) { expr* fi = a.mk_numeral(val, true); new_model->register_decl(f_old, fi); } } for (unsigned i = 0; i < num; ++i) { func_decl* f = old_model->get_constant(i); expr* fi = old_model->get_const_interp(f); if (!m_bools.contains(f)) { new_model->register_decl(f, fi); } } num = old_model->get_num_functions(); for (unsigned i = 0; i < num; i++) { func_decl * f = old_model->get_function(i); func_interp * fi = old_model->get_func_interp(f); new_model->register_decl(f, fi->copy()); } new_model->copy_usort_interps(*old_model); old_model = new_model; } void insert(func_decl* x_new, func_decl* x_old) { m_refs.push_back(x_new); m_refs.push_back(x_old); m_bools.insert(x_new); m_nums_as_int.push_back(x_old); m_nums_as_bool.push_back(ptr_vector()); m_nums_as_bool.back().push_back(x_new); } void insert(func_decl* x_old, unsigned sz, func_decl * const* x_new) { m_nums_as_int.push_back(x_old); m_nums_as_bool.push_back(ptr_vector()); m_refs.push_back(x_old); for (unsigned i = 0; i < sz; ++i) { m_refs.push_back(x_new[i]); m_nums_as_bool.back().push_back(x_new[i]); m_bools.insert(x_new[i]); } } virtual model_converter * translate(ast_translation & translator) { bool2int_model_converter* mc = alloc(bool2int_model_converter, translator.to()); for (unsigned i = 0; i < m_nums_as_int.size(); ++i) { mc->insert(m_nums_as_int[i], m_nums_as_bool[i].size(), m_nums_as_bool[i].c_ptr()); } return mc; } }; class elim01_tactic : public tactic { public: typedef obj_hashtable expr_set; ast_manager & m; arith_util a; th_rewriter m_rewriter; params_ref m_params; unsigned m_max_hi_default; rational m_max_hi; elim01_tactic(ast_manager & _m, params_ref const & p): m(_m), a(m), m_rewriter(m), m_max_hi_default(8), m_max_hi(rational(m_max_hi_default)) { } virtual ~elim01_tactic() { } void set_cancel(bool f) { } virtual void updt_params(params_ref const & p) { m_max_hi = rational(p.get_uint("max_coefficient", m_max_hi_default)); m_params = p; } virtual void collect_param_descrs(param_descrs & r) { r.insert("max_coefficient", CPK_UINT, "(default: 1) maximal upper bound for finite range -> Bool conversion"); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("elim01", *g); expr_safe_replace sub(m); bool2int_model_converter* b2i = alloc(bool2int_model_converter, m); mc = b2i; bound_manager bounds(m); expr_ref_vector axioms(m); bounds(*g); rational zero(0); bound_manager::iterator bit = bounds.begin(), bend = bounds.end(); for (; bit != bend; ++bit) { if (!is_app(*bit)) continue; app* x = to_app(*bit); bool s1, s2; rational lo, hi; if (a.is_int(x) && bounds.has_lower(x, lo, s1) && !s1 && zero <= lo && bounds.has_upper(x, hi, s2) && !s2 && hi <= m_max_hi && lo <= hi) { add_variable(b2i, sub, x, lo.get_unsigned(), hi.get_unsigned(), axioms); } else if (a.is_int(x)) { TRACE("pb", tout << "Not adding variable " << mk_pp(x, m) << " has lower: " << bounds.has_lower(x, lo, s1) << " " << lo << " has upper: " << bounds.has_upper(x, hi, s2) << " " << hi << "\n";); } } if (sub.empty()) { result.push_back(g.get()); return; } expr_ref new_curr(m), tmp_curr(m); proof_ref new_pr(m); for (unsigned i = 0; i < g->size(); i++) { expr * curr = g->form(i); sub(curr, tmp_curr); m_rewriter(tmp_curr, new_curr); if (m.proofs_enabled()) { new_pr = m.mk_rewrite(curr, new_curr); new_pr = m.mk_modus_ponens(g->pr(i), new_pr); } g->update(i, new_curr, new_pr, g->dep(i)); } for (unsigned i = 0; i < axioms.size(); ++i) { g->assert_expr(axioms[i].get()); } g->inc_depth(); result.push_back(g.get()); TRACE("pb", g->display(tout);); SASSERT(g->is_well_sorted()); // TBD: support proof conversion (or not..) } virtual tactic * translate(ast_manager & m) { return alloc(elim01_tactic, m, m_params); } virtual void cleanup() {} void add_variable(bool2int_model_converter* b2i, expr_safe_replace& sub, app* x, unsigned min_value, unsigned max_value, expr_ref_vector& axioms) { std::string name = x->get_decl()->get_name().str(); unsigned sh = 0; app_ref_vector xs(m), ites(m); func_decl_ref_vector xfs(m); app_ref zero(m), sum(m); zero = a.mk_numeral(rational(0), true); while (max_value >= (1ul << sh)) { xs.push_back(m.mk_fresh_const(name.c_str(), m.mk_bool_sort())); xfs.push_back(xs.back()->get_decl()); ites.push_back(m.mk_ite(xs.back(), a.mk_numeral(rational(1 << sh), true), zero)); ++sh; } switch (ites.size()) { case 0: sum = zero; break; case 1: sum = ites[0].get(); break; default: sum = a.mk_add(ites.size(), (expr*const*)ites.c_ptr()); break; } TRACE("pb", tout << mk_pp(x, m) << " " << sum << " max: " << max_value << "\n";); sub.insert(x, sum); b2i->insert(x->get_decl(), xfs.size(), xfs.c_ptr()); // if max_value+1 is not a power of two: if ((max_value & (max_value + 1)) != 0) { axioms.push_back(a.mk_le(sum, a.mk_numeral(rational(max_value), true))); } if (min_value > 0) { axioms.push_back(a.mk_ge(sum, a.mk_numeral(rational(min_value), true))); } } }; tactic * mk_elim01_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(elim01_tactic, m, p)); } z3-4.4.0/src/tactic/arith/add_bounds_tactic.h0000644000175000017500000000140312540347414020612 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: add_bounds.h Abstract: Tactic for bounding unbounded variables. Author: Leonardo de Moura (leonardo) 2011-06-30. Revision History: --*/ #ifndef _ADD_BOUNDS_H_ #define _ADD_BOUNDS_H_ #include"params.h" class ast_manager; class goal; class tactic; class probe; bool is_unbounded(goal const & g); probe * mk_is_unbounded_probe(); tactic * mk_add_bounds_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("add-bounds", "add bounds to unbounded variables (under approximation).", "mk_add_bounds_tactic(m, p)") ADD_PROBE("is-unbounded", "true if the goal contains integer/real constants that do not have lower/upper bounds.", "mk_is_unbounded_probe()") */ #endif z3-4.4.0/src/tactic/arith/card2bv_tactic.cpp0000644000175000017500000004051112540347414020371 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: card2bv_tactic.cpp Abstract: Tactic for converting Pseudo-Boolean constraints to BV Author: Nikolaj Bjorner (nbjorner) 2014-03-20 Notes: --*/ #include"tactical.h" #include"cooperate.h" #include"rewriter_def.h" #include"ast_smt2_pp.h" #include"expr_substitution.h" #include"card2bv_tactic.h" #include"pb_rewriter.h" #include"ast_util.h" namespace pb { unsigned card2bv_rewriter::get_num_bits(func_decl* f) { rational r(0); unsigned sz = f->get_arity(); for (unsigned i = 0; i < sz; ++i) { r += pb.get_coeff(f, i); } r = r > pb.get_k(f)? r : pb.get_k(f); return r.get_num_bits(); } card2bv_rewriter::card2bv_rewriter(ast_manager& m): m(m), au(m), pb(m), bv(m), m_sort(*this), m_lemmas(m), m_trail(m) {} void card2bv_rewriter::mk_assert(func_decl * f, unsigned sz, expr * const* args, expr_ref & result, expr_ref_vector& lemmas) { m_lemmas.reset(); SASSERT(f->get_family_id() == pb.get_family_id()); if (is_or(f)) { result = m.mk_or(sz, args); } else if (is_and(f)) { result = m.mk_and(sz, args); } else if (pb.is_eq(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { result = m_sort.eq(pb.get_k(f).get_unsigned(), sz, args); } else if (pb.is_le(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { result = m_sort.le(false, pb.get_k(f).get_unsigned(), sz, args); } else if (pb.is_ge(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { result = m_sort.ge(false, pb.get_k(f).get_unsigned(), sz, args); } else { br_status st = mk_shannon(f, sz, args, result); if (st == BR_FAILED) { mk_bv(f, sz, args, result); } } lemmas.append(m_lemmas); } std::ostream& card2bv_rewriter::pp(std::ostream& out, literal lit) { return out << mk_ismt2_pp(lit, m); } card2bv_rewriter::literal card2bv_rewriter::trail(literal l) { m_trail.push_back(l); return l; } card2bv_rewriter::literal card2bv_rewriter::fresh() { return trail(m.mk_fresh_const("sn", m.mk_bool_sort())); } void card2bv_rewriter::mk_clause(unsigned n, literal const* lits) { m_lemmas.push_back(mk_or(m, n, lits)); } br_status card2bv_rewriter::mk_app_core(func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { if (f->get_family_id() == null_family_id) { if (sz == 1) { // Expecting minimize/maximize. func_decl_ref fd(m); fd = m.mk_func_decl(f->get_name(), m.get_sort(args[0]), f->get_range()); result = m.mk_app(fd.get(), args[0]); return BR_DONE; } else return BR_FAILED; } else if (f->get_family_id() == m.get_basic_family_id()) { result = m.mk_app(f, sz, args); return BR_DONE; } else if (f->get_family_id() == pb.get_family_id()) { if (is_or(f)) { result = m.mk_or(sz, args); return BR_DONE; } if (is_and(f)) { result = m.mk_and(sz, args); return BR_DONE; } br_status st = mk_shannon(f, sz, args, result); if (st == BR_FAILED) { mk_bv(f, sz, args, result); return BR_DONE; } else { return st; } } // NSB: review // we should remove this code and rely on a layer above to deal with // whatever it accomplishes. It seems to break types. // else if (f->get_family_id() == au.get_family_id()) { if (f->get_decl_kind() == OP_ADD) { unsigned bits = 0; for (unsigned i = 0; i < sz; i++) { rational val1, val2; if (au.is_int(args[i]) && au.is_numeral(args[i], val1)) { bits += val1.get_num_bits(); } else if (m.is_ite(args[i]) && au.is_numeral(to_app(args[i])->get_arg(1), val1) && val1.is_one() && au.is_numeral(to_app(args[i])->get_arg(2), val2) && val2.is_zero()) { bits++; } else return BR_FAILED; } result = 0; for (unsigned i = 0; i < sz; i++) { rational val1, val2; expr * q; if (au.is_int(args[i]) && au.is_numeral(args[i], val1)) q = bv.mk_numeral(val1, bits); else q = mk_ite(to_app(args[i])->get_arg(0), bv.mk_numeral(1, bits), bv.mk_numeral(0, bits)); result = (i == 0) ? q : bv.mk_bv_add(result.get(), q); } return BR_DONE; } else return BR_FAILED; } else return BR_FAILED; } bool card2bv_rewriter::is_or(func_decl* f) { switch (f->get_decl_kind()) { case OP_AT_MOST_K: case OP_PB_LE: return false; case OP_AT_LEAST_K: case OP_PB_GE: return pb.get_k(f).is_one(); case OP_PB_EQ: return false; default: UNREACHABLE(); return false; } } bool card2bv_rewriter::is_and(func_decl* f) { return false; } void card2bv_rewriter::mk_bv(func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { expr_ref zero(m), a(m), b(m); expr_ref_vector es(m); unsigned bw = get_num_bits(f); zero = bv.mk_numeral(rational(0), bw); for (unsigned i = 0; i < sz; ++i) { es.push_back(mk_ite(args[i], bv.mk_numeral(pb.get_coeff(f, i), bw), zero)); } switch (es.size()) { case 0: a = zero; break; case 1: a = es[0].get(); break; default: a = es[0].get(); for (unsigned i = 1; i < es.size(); ++i) { a = bv.mk_bv_add(a, es[i].get()); } break; } b = bv.mk_numeral(pb.get_k(f), bw); switch (f->get_decl_kind()) { case OP_AT_MOST_K: case OP_PB_LE: UNREACHABLE(); result = bv.mk_ule(a, b); break; case OP_AT_LEAST_K: UNREACHABLE(); case OP_PB_GE: result = bv.mk_ule(b, a); break; case OP_PB_EQ: result = m.mk_eq(a, b); break; default: UNREACHABLE(); } TRACE("card2bv", tout << result << "\n";); } struct argc_t { expr* m_arg; rational m_coeff; argc_t():m_arg(0), m_coeff(0) {} argc_t(expr* arg, rational const& r): m_arg(arg), m_coeff(r) {} }; struct argc_gt { bool operator()(argc_t const& a, argc_t const& b) const { return a.m_coeff > b.m_coeff; } }; struct argc_entry { unsigned m_index; rational m_k; expr* m_value; argc_entry(unsigned i, rational const& k): m_index(i), m_k(k), m_value(0) {} argc_entry():m_index(0), m_k(0), m_value(0) {} struct eq { bool operator()(argc_entry const& a, argc_entry const& b) const { return a.m_index == b.m_index && a.m_k == b.m_k; } }; struct hash { unsigned operator()(argc_entry const& a) const { return a.m_index ^ a.m_k.hash(); } }; }; typedef hashtable argc_cache; br_status card2bv_rewriter::mk_shannon( func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { unsigned max_clauses = sz*10; vector argcs; for (unsigned i = 0; i < sz; ++i) { argcs.push_back(argc_t(args[i], pb.get_coeff(f, i))); } std::sort(argcs.begin(), argcs.end(), argc_gt()); DEBUG_CODE( for (unsigned i = 0; i + 1 < sz; ++i) { SASSERT(argcs[i].m_coeff >= argcs[i+1].m_coeff); } ); result = m.mk_app(f, sz, args); TRACE("card2bv", tout << result << "\n";); argc_cache cache; expr_ref_vector trail(m); vector todo_k; unsigned_vector todo_i; todo_k.push_back(pb.get_k(f)); todo_i.push_back(0); decl_kind kind = f->get_decl_kind(); argc_entry entry1; while (!todo_i.empty()) { SASSERT(todo_i.size() == todo_k.size()); if (cache.size() > max_clauses) { return BR_FAILED; } unsigned i = todo_i.back(); rational k = todo_k.back(); argc_entry entry(i, k); if (cache.contains(entry)) { todo_i.pop_back(); todo_k.pop_back(); continue; } SASSERT(i < sz); SASSERT(!k.is_neg()); rational const& coeff = argcs[i].m_coeff; expr* arg = argcs[i].m_arg; if (i + 1 == sz) { switch(kind) { case OP_AT_MOST_K: case OP_PB_LE: if (coeff <= k) { entry.m_value = m.mk_true(); } else { entry.m_value = negate(arg); trail.push_back(entry.m_value); } break; case OP_AT_LEAST_K: case OP_PB_GE: if (k.is_zero()) { entry.m_value = m.mk_true(); } else if (coeff < k) { entry.m_value = m.mk_false(); } else if (coeff.is_zero()) { entry.m_value = m.mk_true(); } else { SASSERT(coeff >= k && k.is_pos()); entry.m_value = arg; } break; case OP_PB_EQ: if (coeff == k) { entry.m_value = arg; } else if (k.is_zero()) { entry.m_value = negate(arg); trail.push_back(entry.m_value); } else { entry.m_value = m.mk_false(); } break; } todo_i.pop_back(); todo_k.pop_back(); cache.insert(entry); continue; } entry.m_index++; expr* lo = 0, *hi = 0; if (cache.find(entry, entry1)) { lo = entry1.m_value; } else { todo_i.push_back(i+1); todo_k.push_back(k); } entry.m_k -= coeff; if (kind != OP_PB_EQ && !entry.m_k.is_pos()) { switch (kind) { case OP_AT_MOST_K: case OP_PB_LE: hi = m.mk_false(); break; case OP_AT_LEAST_K: case OP_PB_GE: hi = m.mk_true(); break; default: UNREACHABLE(); } } else if (cache.find(entry, entry1)) { hi = entry1.m_value; } else { todo_i.push_back(i+1); todo_k.push_back(entry.m_k); } if (hi && lo) { todo_i.pop_back(); todo_k.pop_back(); entry.m_index = i; entry.m_k = k; entry.m_value = mk_ite(arg, hi, lo); trail.push_back(entry.m_value); cache.insert(entry); } } argc_entry entry(0, pb.get_k(f)); VERIFY(cache.find(entry, entry)); result = entry.m_value; TRACE("card2bv", tout << result << "\n";); return BR_DONE; } expr* card2bv_rewriter::negate(expr* e) { if (m.is_not(e, e)) return e; return m.mk_not(e); } expr* card2bv_rewriter::mk_ite(expr* c, expr* hi, expr* lo) { while (m.is_not(c, c)) { std::swap(hi, lo); } if (hi == lo) return hi; if (m.is_true(hi) && m.is_false(lo)) return c; if (m.is_false(hi) && m.is_true(lo)) return negate(c); if (m.is_true(hi)) return m.mk_or(c, lo); if (m.is_false(lo)) return m.mk_and(c, hi); if (m.is_false(hi)) return m.mk_and(negate(c), lo); if (m.is_true(lo)) return m.mk_implies(c, hi); return m.mk_ite(c, hi, lo); } void card_pb_rewriter::rewrite(expr* e, expr_ref& result) { if (pb.is_eq(e)) { app* a = to_app(e); ast_manager& m = m_lemmas.get_manager(); unsigned sz = a->get_num_args(); expr_ref_vector args(m); expr_ref tmp(m); for (unsigned i = 0; i < sz; ++i) { (*this)(a->get_arg(i), tmp); args.push_back(tmp); } m_cfg.m_r.mk_assert(a->get_decl(), sz, args.c_ptr(), result, m_lemmas); } else { (*this)(e, result); } } }; template class rewriter_tpl; class card2bv_tactic : public tactic { ast_manager & m; params_ref m_params; th_rewriter m_rw1; pb::card_pb_rewriter m_rw2; public: card2bv_tactic(ast_manager & m, params_ref const & p): m(m), m_params(p), m_rw1(m), m_rw2(m) { } virtual tactic * translate(ast_manager & m) { return alloc(card2bv_tactic, m, m_params); } virtual ~card2bv_tactic() { } virtual void updt_params(params_ref const & p) { m_params = p; } virtual void collect_param_descrs(param_descrs & r) { } void set_cancel(bool f) { m_rw1.set_cancel(f); m_rw2.set_cancel(f); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { TRACE("card2bv-before", g->display(tout);); SASSERT(g->is_well_sorted()); fail_if_proof_generation("card2bv", g); mc = 0; pc = 0; core = 0; result.reset(); tactic_report report("card2bv", *g); m_rw1.reset(); m_rw2.reset(); m_rw2.lemmas().reset(); if (g->inconsistent()) { result.push_back(g.get()); return; } expr_ref new_f1(m), new_f2(m); proof_ref new_pr1(m), new_pr2(m); for (unsigned idx = 0; !g->inconsistent() && idx < g->size(); idx++) { m_rw1(g->form(idx), new_f1, new_pr1); TRACE("card2bv", tout << "Rewriting " << mk_ismt2_pp(new_f1.get(), m) << std::endl;); m_rw2.rewrite(new_f1, new_f2); if (m.proofs_enabled()) { new_pr1 = m.mk_modus_ponens(g->pr(idx), new_pr1); new_pr2 = m.mk_rewrite(new_f1, new_f2); new_pr1 = m.mk_modus_ponens(new_pr1, new_pr2); } g->update(idx, new_f2, new_pr1, g->dep(idx)); } for (unsigned i = 0; i < m_rw2.lemmas().size(); ++i) { g->assert_expr(m_rw2.lemmas()[i].get()); } g->inc_depth(); result.push_back(g.get()); TRACE("card2bv", g->display(tout);); SASSERT(g->is_well_sorted()); } virtual void cleanup() { } }; tactic * mk_card2bv_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(card2bv_tactic, m, p)); } z3-4.4.0/src/tactic/arith/pb2bv_model_converter.cpp0000644000175000017500000000612012540347414021777 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: pb2bv_model_converter.cpp Abstract: Model converter for the pb2bv tactic. Author: Christoph (cwinter) 2012-02-15 Notes: --*/ #include"trace.h" #include"arith_decl_plugin.h" #include"model_v2_pp.h" #include"pb2bv_model_converter.h" pb2bv_model_converter::pb2bv_model_converter(ast_manager & _m, obj_map const & c2bit, bound_manager const & bm): m(_m) { obj_map::iterator it = c2bit.begin(); obj_map::iterator end = c2bit.end(); for ( ; it != end; it++) { m_c2bit.push_back(func_decl_pair(it->m_key, to_app(it->m_value)->get_decl())); m.inc_ref(it->m_key); m.inc_ref(to_app(it->m_value)->get_decl()); } bound_manager::iterator it2 = bm.begin(); bound_manager::iterator end2 = bm.end(); for (; it2 != end2; ++it2) { expr * c = *it2; SASSERT(is_uninterp_const(c)); func_decl * d = to_app(c)->get_decl(); if (!c2bit.contains(d)) { SASSERT(d->get_arity() == 0); m.inc_ref(d); m_c2bit.push_back(func_decl_pair(d, static_cast(0))); } } } pb2bv_model_converter::~pb2bv_model_converter() { svector::const_iterator it = m_c2bit.begin(); svector::const_iterator end = m_c2bit.end(); for (; it != end; ++it) { m.dec_ref(it->first); m.dec_ref(it->second); } } void pb2bv_model_converter::operator()(model_ref & md) { (*this)(md, 0); } void pb2bv_model_converter::operator()(model_ref & md, unsigned goal_idx) { SASSERT(goal_idx == 0); TRACE("pb2bv", tout << "converting model:\n"; model_v2_pp(tout, *md); display(tout);); arith_util a_util(m); svector::const_iterator it = m_c2bit.begin(); svector::const_iterator end = m_c2bit.end(); for (; it != end; ++it) { if (it->second) { expr * val = md->get_const_interp(it->second); if (val == 0 || m.is_false(val)) { /* false's and don't cares get the integer 0 solution*/ md->register_decl(it->first, a_util.mk_numeral(rational(0), true)); } else { md->register_decl(it->first, a_util.mk_numeral(rational(1), true)); } } else { // it->first is a don't care. md->register_decl(it->first, a_util.mk_numeral(rational(0), true)); } } } void pb2bv_model_converter::display(std::ostream & out) { out << "(pb2bv-model-converter"; svector::const_iterator it = m_c2bit.begin(); svector::const_iterator end = m_c2bit.end(); for (; it != end; ++it) { out << "\n (" << it->first->get_name() << " "; if (it->second == 0) out << "0"; else out << it->second->get_name(); out << ")"; } out << ")\n"; } model_converter * pb2bv_model_converter::translate(ast_translation & translator) { NOT_IMPLEMENTED_YET(); } z3-4.4.0/src/tactic/arith/degree_shift_tactic.h0000644000175000017500000000141512540347414021143 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: degree_shift_tactic.h Abstract: Simple degree shift procedure. Basic idea: if goal G contains a real variable x, x occurs with degrees d_1, ..., d_k in G, and n = gcd(d_1, ..., d_k) > 1. Then, replace x^n with a new fresh variable y. Author: Leonardo de Moura (leonardo) 2011-12-30. Revision History: --*/ #ifndef _DEGREE_SHIFT_TACTIC_H_ #define _DEGREE_SHIFT_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_degree_shift_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("degree-shift", "try to reduce degree of polynomials (remark: :mul2power simplification is automatically applied).", "mk_degree_shift_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/arith/fm_tactic.cpp0000644000175000017500000017230512540347414017457 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fm_tactic.cpp Abstract: Use Fourier-Motzkin to eliminate variables. This strategy can handle conditional bounds (i.e., clauses with at most one constraint). The strategy mk_occf can be used to put the formula in OCC form. Author: Leonardo de Moura (leonardo) 2012-02-04. Revision History: --*/ #include"fm_tactic.h" #include"tactical.h" #include"arith_decl_plugin.h" #include"for_each_expr.h" #include"cooperate.h" #include"ast_smt2_pp.h" #include"ast_pp.h" #include"id_gen.h" #include"model_evaluator.h" #include"model_v2_pp.h" #include"simplify_tactic.h" class fm_tactic : public tactic { typedef ptr_vector clauses; typedef unsigned var; typedef int bvar; typedef int literal; typedef svector var_vector; struct fm_model_converter : public model_converter { ast_manager & m; ptr_vector m_xs; vector m_clauses; volatile bool m_cancel; enum r_kind { NONE, LOWER, UPPER }; bool is_false(model_ref & md, app * p) { SASSERT(is_uninterp_const(p)); expr * val = md->get_const_interp(p->get_decl()); if (val == 0) { // if it is don't care, then set to false md->register_decl(p->get_decl(), m.mk_false()); return true; } return m.is_false(val); } r_kind process(func_decl * x, expr * cls, arith_util & u, model_evaluator & ev, rational & r) { unsigned num_lits; expr * const * lits; if (m.is_or(cls)) { num_lits = to_app(cls)->get_num_args(); lits = to_app(cls)->get_args(); } else { num_lits = 1; lits = &cls; } bool is_lower = false; bool found = false; for (unsigned i = 0; i < num_lits; i++) { expr * l = lits[i]; expr * atom; if (is_uninterp_const(l) || (m.is_not(l, atom) && is_uninterp_const(atom))) { expr_ref val(m); ev(l, val); if (m.is_true(val)) return NONE; // clause was satisfied } else { found = true; bool neg = m.is_not(l, l); SASSERT(u.is_le(l) || u.is_ge(l)); bool strict = neg; rational a_val; if (u.is_ge(l)) neg = !neg; expr * lhs = to_app(l)->get_arg(0); expr * rhs = to_app(l)->get_arg(1); rational c; u.is_numeral(rhs, c); if (neg) c.neg(); unsigned num_mons; expr * const * mons; if (u.is_add(lhs)) { num_mons = to_app(lhs)->get_num_args(); mons = to_app(lhs)->get_args(); } else { num_mons = 1; mons = &lhs; } for (unsigned j = 0; j < num_mons; j++) { expr * monomial = mons[j]; expr * ai; expr * xi; rational ai_val; if (u.is_mul(monomial, ai, xi)) { u.is_numeral(ai, ai_val); } else { xi = monomial; ai_val = rational(1); } if (u.is_to_real(xi)) xi = to_app(xi)->get_arg(0); SASSERT(is_uninterp_const(xi)); if (x == to_app(xi)->get_decl()) { a_val = ai_val; if (neg) a_val.neg(); } else { expr_ref val(m); ev(monomial, val); SASSERT(u.is_numeral(val)); rational tmp; u.is_numeral(val, tmp); if (neg) tmp.neg(); c -= tmp; } } if (u.is_int(x->get_range()) && strict) { // a*x < c --> a*x <= c-1 SASSERT(c.is_int()); c--; } is_lower = a_val.is_neg(); c /= a_val; if (u.is_int(x->get_range())) { if (is_lower) c = ceil(c); else c = floor(c); } r = c; } } SASSERT(found); return is_lower ? LOWER : UPPER; } public: fm_model_converter(ast_manager & _m):m(_m) {} virtual ~fm_model_converter() { m.dec_array_ref(m_xs.size(), m_xs.c_ptr()); vector::iterator it = m_clauses.begin(); vector::iterator end = m_clauses.end(); for (; it != end; ++it) m.dec_array_ref(it->size(), it->c_ptr()); } void insert(func_decl * x, clauses & c) { m.inc_ref(x); m.inc_array_ref(c.size(), c.c_ptr()); m_xs.push_back(x); m_clauses.push_back(clauses()); m_clauses.back().swap(c); } virtual void operator()(model_ref & md, unsigned goal_idx) { TRACE("fm_mc", model_v2_pp(tout, *md); display(tout);); m_cancel = false; model_evaluator ev(*(md.get())); ev.set_model_completion(true); arith_util u(m); unsigned i = m_xs.size(); while (i > 0) { --i; func_decl * x = m_xs[i]; rational lower; rational upper; rational val; bool has_lower = false; bool has_upper = false; TRACE("fm_mc", tout << "processing " << x->get_name() << "\n";); clauses::iterator it = m_clauses[i].begin(); clauses::iterator end = m_clauses[i].end(); for (; it != end; ++it) { if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); switch (process(x, *it, u, ev, val)) { case NONE: TRACE("fm_mc", tout << "no bound for:\n" << mk_ismt2_pp(*it, m) << "\n";); break; case LOWER: TRACE("fm_mc", tout << "lower bound: " << val << " for:\n" << mk_ismt2_pp(*it, m) << "\n";); if (!has_lower || val > lower) lower = val; has_lower = true; break; case UPPER: TRACE("fm_mc", tout << "upper bound: " << val << " for:\n" << mk_ismt2_pp(*it, m) << "\n";); if (!has_upper || val < upper) upper = val; has_upper = true; break; } } expr * x_val; if (u.is_int(x->get_range())) { if (has_lower) x_val = u.mk_numeral(lower, true); else if (has_upper) x_val = u.mk_numeral(upper, true); else x_val = u.mk_numeral(rational(0), true); } else { if (has_lower && has_upper) x_val = u.mk_numeral((upper + lower)/rational(2), false); else if (has_lower) x_val = u.mk_numeral(lower + rational(1), false); else if (has_upper) x_val = u.mk_numeral(upper - rational(1), false); else x_val = u.mk_numeral(rational(0), false); } TRACE("fm_mc", tout << x->get_name() << " --> " << mk_ismt2_pp(x_val, m) << "\n";); md->register_decl(x, x_val); } TRACE("fm_mc", model_v2_pp(tout, *md);); } virtual void cancel() { m_cancel = true; } virtual void display(std::ostream & out) { out << "(fm-model-converter"; SASSERT(m_xs.size() == m_clauses.size()); unsigned sz = m_xs.size(); for (unsigned i = 0; i < sz; i++) { out << "\n(" << m_xs[i]->get_name(); clauses const & cs = m_clauses[i]; clauses::const_iterator it = cs.begin(); clauses::const_iterator end = cs.end(); for (; it != end; ++it) { out << "\n " << mk_ismt2_pp(*it, m, 2); } out << ")"; } out << ")\n"; } virtual model_converter * translate(ast_translation & translator) { ast_manager & to_m = translator.to(); fm_model_converter * res = alloc(fm_model_converter, to_m); unsigned sz = m_xs.size(); for (unsigned i = 0; i < sz; i++) { func_decl * new_x = translator(m_xs[i]); to_m.inc_ref(new_x); res->m_xs.push_back(new_x); clauses const & cs = m_clauses[i]; res->m_clauses.push_back(clauses()); clauses & new_cs = res->m_clauses.back(); clauses::const_iterator it = cs.begin(); clauses::const_iterator end = cs.end(); for (; it != end; ++it) { app * new_c = translator(*it); to_m.inc_ref(new_c); new_cs.push_back(new_c); } } return res; } }; // Encode the constraint // lits \/ ( as[0]*xs[0] + ... + as[num_vars-1]*xs[num_vars-1] <= c // if strict is true, then <= is <. struct constraint { static unsigned get_obj_size(unsigned num_lits, unsigned num_vars) { return sizeof(constraint) + num_lits*sizeof(literal) + num_vars*(sizeof(var) + sizeof(rational)); } unsigned m_id; unsigned m_num_lits:29; unsigned m_strict:1; unsigned m_dead:1; unsigned m_mark:1; unsigned m_num_vars; literal * m_lits; var * m_xs; rational * m_as; rational m_c; expr_dependency * m_dep; ~constraint() { rational * it = m_as; rational * end = it + m_num_vars; for (; it != end; ++it) it->~rational(); } unsigned hash() const { return hash_u(m_id); } }; typedef ptr_vector constraints; class constraint_set { unsigned_vector m_id2pos; constraints m_set; public: typedef constraints::const_iterator iterator; bool contains(constraint const & c) const { if (c.m_id >= m_id2pos.size()) return false; return m_id2pos[c.m_id] != UINT_MAX; } bool empty() const { return m_set.empty(); } unsigned size() const { return m_set.size(); } void insert(constraint & c) { unsigned id = c.m_id; m_id2pos.reserve(id+1, UINT_MAX); if (m_id2pos[id] != UINT_MAX) return; // already in the set unsigned pos = m_set.size(); m_id2pos[id] = pos; m_set.push_back(&c); } void erase(constraint & c) { unsigned id = c.m_id; if (id >= m_id2pos.size()) return; unsigned pos = m_id2pos[id]; if (pos == UINT_MAX) return; m_id2pos[id] = UINT_MAX; unsigned last_pos = m_set.size() - 1; if (pos != last_pos) { constraint * last_c = m_set[last_pos]; m_set[pos] = last_c; m_id2pos[last_c->m_id] = pos; } m_set.pop_back(); } constraint & erase() { SASSERT(!empty()); constraint & c = *m_set.back(); m_id2pos[c.m_id] = UINT_MAX; m_set.pop_back(); return c; } void reset() { m_id2pos.reset(); m_set.reset(); } void finalize() { m_id2pos.finalize(); m_set.finalize(); } iterator begin() const { return m_set.begin(); } iterator end() const { return m_set.end(); } }; struct imp { ast_manager & m; small_object_allocator m_allocator; arith_util m_util; constraints m_constraints; expr_ref_vector m_bvar2expr; char_vector m_bvar2sign; obj_map m_expr2bvar; char_vector m_is_int; char_vector m_forbidden; expr_ref_vector m_var2expr; obj_map m_expr2var; unsigned_vector m_var2pos; vector m_lowers; vector m_uppers; obj_hashtable m_forbidden_set; // variables that cannot be eliminated because occur in non OCC ineq part goal_ref m_new_goal; ref m_mc; volatile bool m_cancel; id_gen m_id_gen; bool m_produce_models; bool m_fm_real_only; unsigned m_fm_limit; unsigned m_fm_cutoff1; unsigned m_fm_cutoff2; unsigned m_fm_extra; bool m_fm_occ; unsigned long long m_max_memory; unsigned m_counter; bool m_inconsistent; expr_dependency_ref m_inconsistent_core; constraint_set m_sub_todo; // --------------------------- // // OCC clause recognizer // // --------------------------- bool is_literal(expr * t) const { expr * atom; return is_uninterp_const(t) || (m.is_not(t, atom) && is_uninterp_const(atom)); } bool is_constraint(expr * t) const { return !is_literal(t); } bool is_var(expr * t, expr * & x) const { if (is_uninterp_const(t)) { x = t; return true; } else if (m_util.is_to_real(t) && is_uninterp_const(to_app(t)->get_arg(0))) { x = to_app(t)->get_arg(0); return true; } return false; } bool is_var(expr * t) const { expr * x; return is_var(t, x); } bool is_linear_mon_core(expr * t, expr * & x) const { expr * c; if (m_util.is_mul(t, c, x) && m_util.is_numeral(c) && is_var(x, x)) return true; return is_var(t, x); } bool is_linear_mon(expr * t) const { expr * x; return is_linear_mon_core(t, x); } bool is_linear_pol(expr * t) const { unsigned num_mons; expr * const * mons; if (m_util.is_add(t)) { num_mons = to_app(t)->get_num_args(); mons = to_app(t)->get_args(); } else { num_mons = 1; mons = &t; } expr_fast_mark2 visited; bool all_forbidden = true; for (unsigned i = 0; i < num_mons; i++) { expr * x; if (!is_linear_mon_core(mons[i], x)) return false; if (visited.is_marked(x)) return false; // duplicates are not supported... must simplify first visited.mark(x); if (!m_forbidden_set.contains(to_app(x)->get_decl()) && (!m_fm_real_only || !m_util.is_int(x))) all_forbidden = false; } return !all_forbidden; } bool is_linear_ineq(expr * t) const { m.is_not(t, t); expr * lhs, * rhs; TRACE("is_occ_bug", tout << mk_pp(t, m) << "\n";); if (m_util.is_le(t, lhs, rhs) || m_util.is_ge(t, lhs, rhs)) { if (!m_util.is_numeral(rhs)) return false; return is_linear_pol(lhs); } return false; } bool is_occ(expr * t) { if (m_fm_occ && m.is_or(t)) { unsigned num = to_app(t)->get_num_args(); bool found = false; for (unsigned i = 0; i < num; i++) { expr * l = to_app(t)->get_arg(i); if (is_literal(l)) { continue; } else if (is_linear_ineq(l)) { if (found) return false; found = true; } else { return false; } } return found; } return is_linear_ineq(t); } // --------------------------- // // Memory mng // // --------------------------- void del_constraint(constraint * c) { m.dec_ref(c->m_dep); m_sub_todo.erase(*c); m_id_gen.recycle(c->m_id); c->~constraint(); unsigned sz = constraint::get_obj_size(c->m_num_lits, c->m_num_vars); m_allocator.deallocate(sz, c); } void del_constraints(unsigned sz, constraint * const * cs) { for (unsigned i = 0; i < sz; i++) del_constraint(cs[i]); } void reset_constraints() { del_constraints(m_constraints.size(), m_constraints.c_ptr()); m_constraints.reset(); } constraint * mk_constraint(unsigned num_lits, literal * lits, unsigned num_vars, var * xs, rational * as, rational & c, bool strict, expr_dependency * dep) { unsigned sz = constraint::get_obj_size(num_lits, num_vars); char * mem = static_cast(m_allocator.allocate(sz)); char * mem_as = mem + sizeof(constraint); char * mem_lits = mem_as + sizeof(rational)*num_vars; char * mem_xs = mem_lits + sizeof(literal)*num_lits; constraint * cnstr = new (mem) constraint(); cnstr->m_id = m_id_gen.mk(); cnstr->m_num_lits = num_lits; cnstr->m_dead = false; cnstr->m_mark = false; cnstr->m_strict = strict; cnstr->m_num_vars = num_vars; cnstr->m_lits = reinterpret_cast(mem_lits); for (unsigned i = 0; i < num_lits; i++) cnstr->m_lits[i] = lits[i]; cnstr->m_xs = reinterpret_cast(mem_xs); cnstr->m_as = reinterpret_cast(mem_as); for (unsigned i = 0; i < num_vars; i++) { TRACE("mk_constraint_bug", tout << "xs[" << i << "]: " << xs[i] << "\n";); cnstr->m_xs[i] = xs[i]; new (cnstr->m_as + i) rational(as[i]); } cnstr->m_c = c; DEBUG_CODE({ for (unsigned i = 0; i < num_vars; i++) { SASSERT(cnstr->m_xs[i] == xs[i]); SASSERT(cnstr->m_as[i] == as[i]); } }); cnstr->m_dep = dep; m.inc_ref(dep); return cnstr; } // --------------------------- // // Util // // --------------------------- unsigned num_vars() const { return m_is_int.size(); } // multiply as and c, by the lcm of their denominators void mk_int(unsigned num, rational * as, rational & c) { rational l = denominator(c); for (unsigned i = 0; i < num; i++) l = lcm(l, denominator(as[i])); if (l.is_one()) return; c *= l; SASSERT(c.is_int()); for (unsigned i = 0; i < num; i++) { as[i] *= l; SASSERT(as[i].is_int()); } } void normalize_coeffs(constraint & c) { if (c.m_num_vars == 0) return; // compute gcd of all coefficients rational g = c.m_c; if (g.is_neg()) g.neg(); for (unsigned i = 0; i < c.m_num_vars; i++) { if (g.is_one()) break; if (c.m_as[i].is_pos()) g = gcd(c.m_as[i], g); else g = gcd(-c.m_as[i], g); } if (g.is_one()) return; c.m_c /= g; for (unsigned i = 0; i < c.m_num_vars; i++) c.m_as[i] /= g; } void display(std::ostream & out, constraint const & c) const { for (unsigned i = 0; i < c.m_num_lits; i++) { literal l = c.m_lits[i]; if (sign(l)) out << "~"; bvar p = lit2bvar(l); out << mk_ismt2_pp(m_bvar2expr[p], m); out << " "; } out << "("; if (c.m_num_vars == 0) out << "0"; for (unsigned i = 0; i < c.m_num_vars; i++) { if (i > 0) out << " + "; if (!c.m_as[i].is_one()) out << c.m_as[i] << "*"; out << mk_ismt2_pp(m_var2expr.get(c.m_xs[i]), m); } if (c.m_strict) out << " < "; else out << " <= "; out << c.m_c; out << ")"; } /** \brief Return true if c1 subsumes c2 c1 subsumes c2 If 1) All literals of c1 are literals of c2 2) polynomial of c1 == polynomial of c2 3) c1.m_c <= c2.m_c */ bool subsumes(constraint const & c1, constraint const & c2) { if (&c1 == &c2) return false; // quick checks first if (c1.m_num_lits > c2.m_num_lits) return false; if (c1.m_num_vars != c2.m_num_vars) return false; if (c1.m_c > c2.m_c) return false; if (!c1.m_strict && c2.m_strict && c1.m_c == c2.m_c) return false; m_counter += c1.m_num_lits + c2.m_num_lits; for (unsigned i = 0; i < c1.m_num_vars; i++) { m_var2pos[c1.m_xs[i]] = i; } bool failed = false; for (unsigned i = 0; i < c2.m_num_vars; i++) { unsigned pos1 = m_var2pos[c2.m_xs[i]]; if (pos1 == UINT_MAX || c1.m_as[pos1] != c2.m_as[i]) { failed = true; break; } } for (unsigned i = 0; i < c1.m_num_vars; i++) { m_var2pos[c1.m_xs[i]] = UINT_MAX; } if (failed) return false; for (unsigned i = 0; i < c2.m_num_lits; i++) { literal l = c2.m_lits[i]; bvar b = lit2bvar(l); SASSERT(m_bvar2sign[b] == 0); m_bvar2sign[b] = sign(l) ? -1 : 1; } for (unsigned i = 0; i < c1.m_num_lits; i++) { literal l = c1.m_lits[i]; bvar b = lit2bvar(l); char s = sign(l) ? -1 : 1; if (m_bvar2sign[b] != s) { failed = true; break; } } for (unsigned i = 0; i < c2.m_num_lits; i++) { literal l = c2.m_lits[i]; bvar b = lit2bvar(l); m_bvar2sign[b] = 0; } if (failed) return false; return true; } void backward_subsumption(constraint const & c) { if (c.m_num_vars == 0) return; var best = UINT_MAX; unsigned best_sz = UINT_MAX; bool best_lower = false; for (unsigned i = 0; i < c.m_num_vars; i++) { var xi = c.m_xs[i]; if (is_forbidden(xi)) continue; // variable is not in the index bool neg_a = c.m_as[i].is_neg(); constraints & cs = neg_a ? m_lowers[xi] : m_uppers[xi]; if (cs.size() < best_sz) { best = xi; best_sz = cs.size(); best_lower = neg_a; } } if (best_sz == 0) return; if (best == UINT_MAX) return; // none of the c variables are in the index. constraints & cs = best_lower ? m_lowers[best] : m_uppers[best]; m_counter += cs.size(); constraints::iterator it = cs.begin(); constraints::iterator it2 = it; constraints::iterator end = cs.end(); for (; it != end; ++it) { constraint * c2 = *it; if (c2->m_dead) continue; if (subsumes(c, *c2)) { TRACE("fm_subsumption", display(tout, c); tout << "\nsubsumed:\n"; display(tout, *c2); tout << "\n";); c2->m_dead = true; continue; } *it2 = *it; ++it2; } cs.set_end(it2); } void subsume() { while (!m_sub_todo.empty()) { constraint & c = m_sub_todo.erase(); if (c.m_dead) continue; backward_subsumption(c); } } // --------------------------- // // Initialization // // --------------------------- imp(ast_manager & _m, params_ref const & p): m(_m), m_allocator("fm-tactic"), m_util(m), m_bvar2expr(m), m_var2expr(m), m_inconsistent_core(m) { updt_params(p); m_cancel = false; } ~imp() { reset_constraints(); } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_fm_real_only = p.get_bool("fm_real_only", true); m_fm_limit = p.get_uint("fm_limit", 5000000); m_fm_cutoff1 = p.get_uint("fm_cutoff1", 8); m_fm_cutoff2 = p.get_uint("fm_cutoff2", 256); m_fm_extra = p.get_uint("fm_extra", 0); m_fm_occ = p.get_bool("fm_occ", false); } void set_cancel(bool f) { m_cancel = f; } struct forbidden_proc { imp & m_owner; forbidden_proc(imp & o):m_owner(o) {} void operator()(::var * n) {} void operator()(app * n) { if (is_uninterp_const(n) && m_owner.m.get_sort(n)->get_family_id() == m_owner.m_util.get_family_id()) { m_owner.m_forbidden_set.insert(n->get_decl()); } } void operator()(quantifier * n) {} }; void init_forbidden_set(goal const & g) { m_forbidden_set.reset(); expr_fast_mark1 visited; forbidden_proc proc(*this); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * f = g.form(i); if (is_occ(f)) continue; TRACE("is_occ_bug", tout << "not OCC:\n" << mk_ismt2_pp(f, m) << "\n";); quick_for_each_expr(proc, visited, f); } } void init(goal const & g) { m_sub_todo.reset(); m_id_gen.reset(); reset_constraints(); m_bvar2expr.reset(); m_bvar2sign.reset(); m_bvar2expr.push_back(0); // bvar 0 is not used m_bvar2sign.push_back(0); m_expr2var.reset(); m_is_int.reset(); m_var2pos.reset(); m_forbidden.reset(); m_var2expr.reset(); m_expr2var.reset(); m_lowers.reset(); m_uppers.reset(); m_new_goal = 0; m_mc = 0; m_counter = 0; m_inconsistent = false; m_inconsistent_core = 0; init_forbidden_set(g); } // --------------------------- // // Internal data-structures // // --------------------------- static bool sign(literal l) { return l < 0; } static bvar lit2bvar(literal l) { return l < 0 ? -l : l; } bool is_int(var x) const { return m_is_int[x] != 0; } bool is_forbidden(var x) const { return m_forbidden[x] != 0; } bool all_int(constraint const & c) const { for (unsigned i = 0; i < c.m_num_vars; i++) { if (!is_int(c.m_xs[i])) return false; } return true; } app * to_expr(constraint const & c) { expr * ineq; if (c.m_num_vars == 0) { // 0 < k (for k > 0) --> true // 0 <= 0 -- > true if (c.m_c.is_pos() || (!c.m_strict && c.m_c.is_zero())) return m.mk_true(); ineq = 0; } else { bool int_cnstr = all_int(c); ptr_buffer ms; for (unsigned i = 0; i < c.m_num_vars; i++) { expr * x = m_var2expr.get(c.m_xs[i]); if (!int_cnstr && is_int(c.m_xs[i])) x = m_util.mk_to_real(x); if (c.m_as[i].is_one()) ms.push_back(x); else ms.push_back(m_util.mk_mul(m_util.mk_numeral(c.m_as[i], int_cnstr), x)); } expr * lhs; if (c.m_num_vars == 1) lhs = ms[0]; else lhs = m_util.mk_add(ms.size(), ms.c_ptr()); expr * rhs = m_util.mk_numeral(c.m_c, int_cnstr); if (c.m_strict) { ineq = m.mk_not(m_util.mk_ge(lhs, rhs)); } else { ineq = m_util.mk_le(lhs, rhs); } } if (c.m_num_lits == 0) { if (ineq) return to_app(ineq); else return m.mk_false(); } ptr_buffer lits; for (unsigned i = 0; i < c.m_num_lits; i++) { literal l = c.m_lits[i]; if (sign(l)) lits.push_back(m.mk_not(m_bvar2expr.get(lit2bvar(l)))); else lits.push_back(m_bvar2expr.get(lit2bvar(l))); } if (ineq) lits.push_back(ineq); if (lits.size() == 1) return to_app(lits[0]); else return m.mk_or(lits.size(), lits.c_ptr()); } var mk_var(expr * t) { SASSERT(is_uninterp_const(t)); SASSERT(m_util.is_int(t) || m_util.is_real(t)); var x = m_var2expr.size(); m_var2expr.push_back(t); bool is_int = m_util.is_int(t); m_is_int.push_back(is_int); m_var2pos.push_back(UINT_MAX); m_expr2var.insert(t, x); m_lowers.push_back(constraints()); m_uppers.push_back(constraints()); bool forbidden = m_forbidden_set.contains(to_app(t)->get_decl()) || (m_fm_real_only && is_int); m_forbidden.push_back(forbidden); SASSERT(m_var2expr.size() == m_is_int.size()); SASSERT(m_lowers.size() == m_is_int.size()); SASSERT(m_uppers.size() == m_is_int.size()); SASSERT(m_forbidden.size() == m_is_int.size()); SASSERT(m_var2pos.size() == m_is_int.size()); return x; } bvar mk_bvar(expr * t) { SASSERT(is_uninterp_const(t)); SASSERT(m.is_bool(t)); bvar p = m_bvar2expr.size(); m_bvar2expr.push_back(t); m_bvar2sign.push_back(0); SASSERT(m_bvar2expr.size() == m_bvar2sign.size()); m_expr2bvar.insert(t, p); SASSERT(p > 0); return p; } var to_var(expr * t) { var x; if (!m_expr2var.find(t, x)) x = mk_var(t); SASSERT(m_expr2var.contains(t)); SASSERT(m_var2expr.get(x) == t); TRACE("to_var_bug", tout << mk_ismt2_pp(t, m) << " --> " << x << "\n";); return x; } bvar to_bvar(expr * t) { bvar p; if (m_expr2bvar.find(t, p)) return p; return mk_bvar(t); } literal to_literal(expr * t) { if (m.is_not(t, t)) return -to_bvar(t); else return to_bvar(t); } void add_constraint(expr * f, expr_dependency * dep) { SASSERT(!m.is_or(f) || m_fm_occ); sbuffer lits; sbuffer xs; buffer as; rational c; bool strict; unsigned num; expr * const * args; if (m.is_or(f)) { num = to_app(f)->get_num_args(); args = to_app(f)->get_args(); } else { num = 1; args = &f; } #if Z3DEBUG bool found_ineq = false; #endif for (unsigned i = 0; i < num; i++) { expr * l = args[i]; if (is_literal(l)) { lits.push_back(to_literal(l)); } else { // found inequality SASSERT(!found_ineq); DEBUG_CODE(found_ineq = true;); bool neg = m.is_not(l, l); SASSERT(m_util.is_le(l) || m_util.is_ge(l)); strict = neg; if (m_util.is_ge(l)) neg = !neg; expr * lhs = to_app(l)->get_arg(0); expr * rhs = to_app(l)->get_arg(1); m_util.is_numeral(rhs, c); if (neg) c.neg(); unsigned num_mons; expr * const * mons; if (m_util.is_add(lhs)) { num_mons = to_app(lhs)->get_num_args(); mons = to_app(lhs)->get_args(); } else { num_mons = 1; mons = &lhs; } bool all_int = true; for (unsigned j = 0; j < num_mons; j++) { expr * monomial = mons[j]; expr * a; rational a_val; expr * x; if (m_util.is_mul(monomial, a, x)) { VERIFY(m_util.is_numeral(a, a_val)); } else { x = monomial; a_val = rational(1); } if (neg) a_val.neg(); VERIFY(is_var(x, x)); xs.push_back(to_var(x)); as.push_back(a_val); if (!is_int(xs.back())) all_int = false; } mk_int(as.size(), as.c_ptr(), c); if (all_int && strict) { strict = false; c--; } } } TRACE("to_var_bug", tout << "before mk_constraint: "; for (unsigned i = 0; i < xs.size(); i++) tout << " " << xs[i]; tout << "\n";); constraint * new_c = mk_constraint(lits.size(), lits.c_ptr(), xs.size(), xs.c_ptr(), as.c_ptr(), c, strict, dep); TRACE("to_var_bug", tout << "add_constraint: "; display(tout, *new_c); tout << "\n";); VERIFY(register_constraint(new_c)); } bool is_false(constraint const & c) const { return c.m_num_lits == 0 && c.m_num_vars == 0 && (c.m_c.is_neg() || (c.m_strict && c.m_c.is_zero())); } bool register_constraint(constraint * c) { normalize_coeffs(*c); if (is_false(*c)) { del_constraint(c); m_inconsistent = true; TRACE("add_constraint_bug", tout << "is false "; display(tout, *c); tout << "\n";); return false; } bool r = false; for (unsigned i = 0; i < c->m_num_vars; i++) { var x = c->m_xs[i]; if (!is_forbidden(x)) { r = true; if (c->m_as[i].is_neg()) m_lowers[x].push_back(c); else m_uppers[x].push_back(c); } } if (r) { m_sub_todo.insert(*c); m_constraints.push_back(c); return true; } else { TRACE("add_constraint_bug", tout << "all variables are forbidden "; display(tout, *c); tout << "\n";); m_new_goal->assert_expr(to_expr(*c), 0, c->m_dep); del_constraint(c); return false; } } void init_use_list(goal const & g) { unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { if (m_inconsistent) return; expr * f = g.form(i); if (is_occ(f)) add_constraint(f, g.dep(i)); else m_new_goal->assert_expr(f, 0, g.dep(i)); } } unsigned get_cost(var x) const { unsigned long long r = static_cast(m_lowers[x].size()) * static_cast(m_uppers[x].size()); if (r > UINT_MAX) return UINT_MAX; return static_cast(r); } typedef std::pair x_cost; struct x_cost_lt { char_vector const m_is_int; x_cost_lt(char_vector & is_int):m_is_int(is_int) {} bool operator()(x_cost const & p1, x_cost const & p2) const { // Integer variables with cost 0 can be eliminated even if they depend on real variables. // Cost 0 == no lower or no upper bound. if (p1.second == 0) { if (p2.second > 0) return true; return p1.first < p2.first; } if (p2.second == 0) return false; bool int1 = m_is_int[p1.first] != 0; bool int2 = m_is_int[p2.first] != 0; return (!int1 && int2) || (int1 == int2 && p1.second < p2.second); } }; void sort_candidates(var_vector & xs) { svector x_cost_vector; unsigned num = num_vars(); for (var x = 0; x < num; x++) { if (!is_forbidden(x)) { x_cost_vector.push_back(x_cost(x, get_cost(x))); } } // x_cost_lt is not a total order on variables std::stable_sort(x_cost_vector.begin(), x_cost_vector.end(), x_cost_lt(m_is_int)); TRACE("fm", svector::iterator it2 = x_cost_vector.begin(); svector::iterator end2 = x_cost_vector.end(); for (; it2 != end2; ++it2) { tout << "(" << mk_ismt2_pp(m_var2expr.get(it2->first), m) << " " << it2->second << ") "; } tout << "\n";); svector::iterator it2 = x_cost_vector.begin(); svector::iterator end2 = x_cost_vector.end(); for (; it2 != end2; ++it2) { xs.push_back(it2->first); } } void cleanup_constraints(constraints & cs) { unsigned j = 0; unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { constraint * c = cs[i]; if (c->m_dead) continue; cs[j] = c; j++; } cs.shrink(j); } // Set all_int = true if all variables in c are int. // Set unit_coeff = true if the coefficient of x in c is 1 or -1. // If all_int = false, then unit_coeff may not be set. void analyze(constraint const & c, var x, bool & all_int, bool & unit_coeff) const { all_int = true; unit_coeff = true; for (unsigned i = 0; i < c.m_num_vars; i++) { if (!is_int(c.m_xs[i])) { all_int = false; return; } if (c.m_xs[i] == x) { unit_coeff = (c.m_as[i].is_one() || c.m_as[i].is_minus_one()); } } } void analyze(constraints const & cs, var x, bool & all_int, bool & unit_coeff) const { all_int = true; unit_coeff = true; constraints::const_iterator it = cs.begin(); constraints::const_iterator end = cs.end(); for (; it != end; ++it) { bool curr_unit_coeff; analyze(*(*it), x, all_int, curr_unit_coeff); if (!all_int) return; if (!curr_unit_coeff) unit_coeff = false; } } // An integer variable x may be eliminated, if // 1- All variables in the contraints it occur are integer. // 2- The coefficient of x in all lower bounds (or all upper bounds) is unit. bool can_eliminate(var x) const { if (!is_int(x)) return true; bool all_int; bool l_unit, u_unit; analyze(m_lowers[x], x, all_int, l_unit); if (!all_int) return false; analyze(m_uppers[x], x, all_int, u_unit); return all_int && (l_unit || u_unit); } void copy_constraints(constraints const & s, clauses & t) { constraints::const_iterator it = s.begin(); constraints::const_iterator end = s.end(); for (; it != end; ++it) { app * c = to_expr(*(*it)); t.push_back(c); } } clauses tmp_clauses; void save_constraints(var x) { if (m_produce_models) { tmp_clauses.reset(); copy_constraints(m_lowers[x], tmp_clauses); copy_constraints(m_uppers[x], tmp_clauses); m_mc->insert(to_app(m_var2expr.get(x))->get_decl(), tmp_clauses); } } void mark_constraints_dead(constraints const & cs) { constraints::const_iterator it = cs.begin(); constraints::const_iterator end = cs.end(); for (; it != end; ++it) (*it)->m_dead = true; } void mark_constraints_dead(var x) { save_constraints(x); mark_constraints_dead(m_lowers[x]); mark_constraints_dead(m_uppers[x]); } void get_coeff(constraint const & c, var x, rational & a) { for (unsigned i = 0; i < c.m_num_vars; i++) { if (c.m_xs[i] == x) { a = c.m_as[i]; return; } } UNREACHABLE(); } var_vector new_xs; vector new_as; svector new_lits; constraint * resolve(constraint const & l, constraint const & u, var x) { m_counter += l.m_num_vars + u.m_num_vars + l.m_num_lits + u.m_num_lits; rational a, b; get_coeff(l, x, a); get_coeff(u, x, b); SASSERT(a.is_neg()); SASSERT(b.is_pos()); a.neg(); SASSERT(!is_int(x) || a.is_one() || b.is_one()); new_xs.reset(); new_as.reset(); rational new_c = l.m_c*b + u.m_c*a; bool new_strict = l.m_strict || u.m_strict; for (unsigned i = 0; i < l.m_num_vars; i++) { var xi = l.m_xs[i]; if (xi == x) continue; unsigned pos = new_xs.size(); new_xs.push_back(xi); SASSERT(m_var2pos[xi] == UINT_MAX); m_var2pos[xi] = pos; new_as.push_back(l.m_as[i] * b); SASSERT(new_xs[m_var2pos[xi]] == xi); SASSERT(new_xs.size() == new_as.size()); } for (unsigned i = 0; i < u.m_num_vars; i++) { var xi = u.m_xs[i]; if (xi == x) continue; unsigned pos = m_var2pos[xi]; if (pos == UINT_MAX) { new_xs.push_back(xi); new_as.push_back(u.m_as[i] * a); } else { new_as[pos] += u.m_as[i] * a; } } // remove zeros and check whether all variables are int bool all_int = true; unsigned sz = new_xs.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { if (new_as[i].is_zero()) continue; if (!is_int(new_xs[i])) all_int = false; if (i != j) { new_xs[j] = new_xs[i]; new_as[j] = new_as[i]; } j++; } new_xs.shrink(j); new_as.shrink(j); if (all_int && new_strict) { new_strict = false; new_c --; } // reset m_var2pos for (unsigned i = 0; i < l.m_num_vars; i++) { m_var2pos[l.m_xs[i]] = UINT_MAX; } if (new_xs.empty() && (new_c.is_pos() || (!new_strict && new_c.is_zero()))) { // literal is true TRACE("fm", tout << "resolution " << x << " consequent literal is always true: \n"; display(tout, l); tout << "\n"; display(tout, u); tout << "\n";); return 0; // no constraint needs to be created. } new_lits.reset(); for (unsigned i = 0; i < l.m_num_lits; i++) { literal lit = l.m_lits[i]; bvar p = lit2bvar(lit); m_bvar2sign[p] = sign(lit) ? -1 : 1; new_lits.push_back(lit); } bool tautology = false; for (unsigned i = 0; i < u.m_num_lits && !tautology; i++) { literal lit = u.m_lits[i]; bvar p = lit2bvar(lit); switch (m_bvar2sign[p]) { case 0: new_lits.push_back(lit); break; case -1: if (!sign(lit)) tautology = true; break; case 1: if (sign(lit)) tautology = true; break; default: UNREACHABLE(); } } // reset m_bvar2sign for (unsigned i = 0; i < l.m_num_lits; i++) { literal lit = l.m_lits[i]; bvar p = lit2bvar(lit); m_bvar2sign[p] = 0; } if (tautology) { TRACE("fm", tout << "resolution " << x << " tautology: \n"; display(tout, l); tout << "\n"; display(tout, u); tout << "\n";); return 0; } expr_dependency * new_dep = m.mk_join(l.m_dep, u.m_dep); if (new_lits.empty() && new_xs.empty() && (new_c.is_neg() || (new_strict && new_c.is_zero()))) { TRACE("fm", tout << "resolution " << x << " inconsistent: \n"; display(tout, l); tout << "\n"; display(tout, u); tout << "\n";); m_inconsistent = true; m_inconsistent_core = new_dep; return 0; } constraint * new_cnstr = mk_constraint(new_lits.size(), new_lits.c_ptr(), new_xs.size(), new_xs.c_ptr(), new_as.c_ptr(), new_c, new_strict, new_dep); TRACE("fm", tout << "resolution " << x << "\n"; display(tout, l); tout << "\n"; display(tout, u); tout << "\n---->\n"; display(tout, *new_cnstr); tout << "\n"; tout << "new_dep: " << new_dep << "\n";); return new_cnstr; } ptr_vector new_constraints; bool try_eliminate(var x) { constraints & l = m_lowers[x]; constraints & u = m_uppers[x]; cleanup_constraints(l); cleanup_constraints(u); if (l.empty() || u.empty()) { // easy case mark_constraints_dead(x); TRACE("fm", tout << "variables was eliminated (trivial case)\n";); return true; } unsigned num_lowers = l.size(); unsigned num_uppers = u.size(); if (num_lowers > m_fm_cutoff1 && num_uppers > m_fm_cutoff1) return false; if (num_lowers * num_uppers > m_fm_cutoff2) return false; if (!can_eliminate(x)) return false; m_counter += num_lowers * num_uppers; TRACE("fm_bug", tout << "eliminating " << mk_ismt2_pp(m_var2expr.get(x), m) << "\nlowers:\n"; display_constraints(tout, l); tout << "uppers:\n"; display_constraints(tout, u);); unsigned num_old_cnstrs = num_uppers + num_lowers; unsigned limit = num_old_cnstrs + m_fm_extra; unsigned num_new_cnstrs = 0; new_constraints.reset(); for (unsigned i = 0; i < num_lowers; i++) { for (unsigned j = 0; j < num_uppers; j++) { if (m_inconsistent || num_new_cnstrs > limit) { TRACE("fm", tout << "too many new constraints: " << num_new_cnstrs << "\n";); del_constraints(new_constraints.size(), new_constraints.c_ptr()); return false; } constraint const & l_c = *(l[i]); constraint const & u_c = *(u[j]); constraint * new_c = resolve(l_c, u_c, x); if (new_c != 0) { num_new_cnstrs++; new_constraints.push_back(new_c); } } } mark_constraints_dead(x); unsigned sz = new_constraints.size(); m_counter += sz; for (unsigned i = 0; i < sz; i++) { constraint * c = new_constraints[i]; backward_subsumption(*c); register_constraint(c); } TRACE("fm", tout << "variables was eliminated old: " << num_old_cnstrs << " new_constraints: " << sz << "\n";); return true; } void copy_remaining(vector & v2cs) { vector::iterator it = v2cs.begin(); vector::iterator end = v2cs.end(); for (; it != end; ++it) { constraints & cs = *it; constraints::iterator it2 = cs.begin(); constraints::iterator end2 = cs.end(); for (; it2 != end2; ++it2) { constraint * c = *it2; if (!c->m_dead) { c->m_dead = true; expr * new_f = to_expr(*c); TRACE("fm_bug", tout << "asserting...\n" << mk_ismt2_pp(new_f, m) << "\nnew_dep: " << c->m_dep << "\n";); m_new_goal->assert_expr(new_f, 0, c->m_dep); } } } v2cs.finalize(); } // Copy remaining clauses to m_new_goal void copy_remaining() { copy_remaining(m_uppers); copy_remaining(m_lowers); } void checkpoint() { cooperate("fm"); if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("fm", *g); fail_if_proof_generation("fm", g); m_produce_models = g->models_enabled(); init(*g); m_new_goal = alloc(goal, *g, true); SASSERT(m_new_goal->depth() == g->depth()); SASSERT(m_new_goal->prec() == g->prec()); m_new_goal->inc_depth(); init_use_list(*g); if (m_inconsistent) { m_new_goal->reset(); m_new_goal->assert_expr(m.mk_false(), 0, m_inconsistent_core); } else { TRACE("fm", display(tout);); subsume(); var_vector candidates; sort_candidates(candidates); unsigned eliminated = 0; if (m_produce_models) m_mc = alloc(fm_model_converter, m); unsigned num = candidates.size(); for (unsigned i = 0; i < num; i++) { checkpoint(); if (m_counter > m_fm_limit) break; m_counter++; if (try_eliminate(candidates[i])) eliminated++; if (m_inconsistent) { m_new_goal->reset(); m_new_goal->assert_expr(m.mk_false(), 0, m_inconsistent_core); break; } } report_tactic_progress(":fm-eliminated", eliminated); report_tactic_progress(":fm-cost", m_counter); if (!m_inconsistent) { copy_remaining(); mc = m_mc.get(); } } reset_constraints(); result.push_back(m_new_goal.get()); TRACE("fm", m_new_goal->display(tout);); SASSERT(m_new_goal->is_well_sorted()); } void display_constraints(std::ostream & out, constraints const & cs) const { constraints::const_iterator it = cs.begin(); constraints::const_iterator end = cs.end(); for (; it != end; ++it) { out << " "; display(out, *(*it)); out << "\n"; } } void display(std::ostream & out) const { unsigned num = num_vars(); for (var x = 0; x < num; x++) { if (is_forbidden(x)) continue; out << mk_ismt2_pp(m_var2expr.get(x), m) << "\n"; display_constraints(out, m_lowers[x]); display_constraints(out, m_uppers[x]); } } }; imp * m_imp; params_ref m_params; public: fm_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(fm_tactic, m, m_params); } virtual ~fm_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { insert_produce_models(r); insert_max_memory(r); r.insert("fm_real_only", CPK_BOOL, "(default: true) consider only real variables for fourier-motzkin elimination."); r.insert("fm_occ", CPK_BOOL, "(default: false) consider inequalities occurring in clauses for FM."); r.insert("fm_limit", CPK_UINT, "(default: 5000000) maximum number of constraints, monomials, clauses visited during FM."); r.insert("fm_cutoff1", CPK_UINT, "(default: 8) first cutoff for FM based on maximum number of lower/upper occurrences."); r.insert("fm_cutoff2", CPK_UINT, "(default: 256) second cutoff for FM based on num_lower * num_upper occurrences."); r.insert("fm_extra", CPK_UINT, "(default: 0) max. increase on the number of inequalities for each FM variable elimination step."); } virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } virtual void cleanup() { imp * d = alloc(imp, m_imp->m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } }; tactic * mk_fm_tactic(ast_manager & m, params_ref const & p) { params_ref s_p = p; s_p.set_bool("arith_lhs", true); s_p.set_bool("elim_and", true); s_p.set_bool("som", true); return and_then(using_params(mk_simplify_tactic(m, s_p), s_p), clean(alloc(fm_tactic, m, p))); } z3-4.4.0/src/tactic/arith/nla2bv_tactic.cpp0000644000175000017500000004107712540347414020242 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: nla2bv_tactic.cpp Abstract: Convert quantified NIA problems to bounded bit-vector arithmetic problems. Author: Nikolaj (nbjorner) 2011-05-3 Notes: Ported to tactic framework on 2012-02-28 The original file was called qfnla2bv.cpp --*/ #include "tactical.h" #include "arith_decl_plugin.h" #include "bv_decl_plugin.h" #include "for_each_expr.h" #include "expr_replacer.h" #include "optional.h" #include "bv2int_rewriter.h" #include "bv2real_rewriter.h" #include "extension_model_converter.h" #include "filter_model_converter.h" #include "bound_manager.h" #include "obj_pair_hashtable.h" #include "ast_smt2_pp.h" // // // 1. for each variable, determine bounds (s.t., non-negative variables // have unsigned bit-vectors). // // 2. replace uninterpreted variables of sort int by // expressions of the form +- bv2int(b) +- k // where k is a slack. // // 3. simplify resulting assertion set to reduce occurrences of bv2int. // class nla2bv_tactic : public tactic { class imp { typedef rational numeral; ast_manager & m_manager; bool m_is_sat_preserving; arith_util m_arith; bv_util m_bv; bv2real_util m_bv2real; bv2int_rewriter_ctx m_bv2int_ctx; bound_manager m_bounds; expr_substitution m_subst; func_decl_ref_vector m_vars; expr_ref_vector m_defs; expr_ref_vector m_trail; unsigned m_num_bits; unsigned m_default_bv_size; ref m_fmc; public: imp(ast_manager & m, params_ref const& p): m_manager(m), m_is_sat_preserving(true), m_arith(m), m_bv(m), m_bv2real(m, rational(p.get_uint("nla2bv_root",2)), rational(p.get_uint("nla2bv_divisor",2)), p.get_uint("nla2bv_max_bv_size", UINT_MAX)), m_bv2int_ctx(m, p), m_bounds(m), m_subst(m), m_vars(m), m_defs(m), m_trail(m), m_fmc(0) { m_default_bv_size = m_num_bits = p.get_uint("nla2bv_bv_size", 4); } ~imp() {} void operator()(goal & g, model_converter_ref & mc) { TRACE("nla2bv", g.display(tout); tout << "Muls: " << count_mul(g) << "\n"; ); m_fmc = alloc(filter_model_converter, m_manager); m_bounds(g); collect_power2(g); if(!collect_vars(g)) { throw tactic_exception("goal is not in the fragment supported by nla2bv"); } tactic_report report("nla->bv", g); substitute_vars(g); TRACE("nla2bv", g.display(tout << "substitute vars\n");); reduce_bv2int(g); reduce_bv2real(g); TRACE("nla2bv", g.display(tout << "after reduce\n");); extension_model_converter * evc = alloc(extension_model_converter, m_manager); mc = concat(m_fmc.get(), evc); for (unsigned i = 0; i < m_vars.size(); ++i) { evc->insert(m_vars[i].get(), m_defs[i].get()); } for (unsigned i = 0; i < m_bv2real.num_aux_decls(); ++i) { m_fmc->insert(m_bv2real.get_aux_decl(i)); } IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(nla->bv :sat-preserving " << m_is_sat_preserving << ")\n";); TRACE("nla2bv_verbose", g.display(tout);); TRACE("nla2bv", tout << "Muls: " << count_mul(g) << "\n";); g.inc_depth(); if (!is_sat_preserving()) g.updt_prec(goal::UNDER); } bool const& is_sat_preserving() const { return m_is_sat_preserving; } private: void set_satisfiability_preserving(bool f) { m_is_sat_preserving = f; } void collect_power2(goal & g) { m_bv2int_ctx.collect_power2(g); obj_map const& p2 = m_bv2int_ctx.power2(); if (p2.empty()) return; obj_map::iterator it = p2.begin(), end = p2.end(); for (; it != end; ++it) { expr* v = it->m_value; unsigned num_bits = m_bv.get_bv_size(v); expr* w = m_bv.mk_bv2int(m_bv.mk_bv_shl(m_bv.mk_numeral(1, num_bits), v)); m_trail.push_back(w); m_subst.insert(it->m_key, w); TRACE("nla2bv", tout << mk_ismt2_pp(it->m_key, m_manager) << " " << mk_ismt2_pp(w, m_manager) << "\n";); } // eliminate the variables that are power of two. substitute_vars(g); m_subst.reset(); } // eliminate bv2int from formula void reduce_bv2int(goal & g) { bv2int_rewriter_star reduce(m_manager, m_bv2int_ctx); expr_ref r(m_manager); for (unsigned i = 0; i < g.size(); ++i) { reduce(g.form(i), r); g.update(i, r); } assert_side_conditions(g, m_bv2int_ctx.num_side_conditions(), m_bv2int_ctx.side_conditions()); } // eliminate bv2real from formula void reduce_bv2real(goal & g) { bv2real_rewriter_star reduce(m_manager, m_bv2real); expr_ref r(m_manager); for (unsigned i = 0; i < g.size(); ++i) { reduce(g.form(i), r); if (m_bv2real.contains_bv2real(r)) { throw tactic_exception("nla2bv could not eliminate reals"); } g.update(i, r); } assert_side_conditions(g, m_bv2real.num_side_conditions(), m_bv2real.side_conditions()); } void assert_side_conditions(goal & g, unsigned sz, expr * const * conditions) { for (unsigned i = 0; i < sz; ++i) { g.assert_expr(conditions[i]); set_satisfiability_preserving(false); } TRACE("nla2bv", for (unsigned i = 0; i < sz; ++i) { tout << mk_ismt2_pp(conditions[i], m_manager) << "\n"; }); } // substitute variables by bit-vectors void substitute_vars(goal & g) { scoped_ptr er = mk_default_expr_replacer(m_manager); er->set_substitution(&m_subst); expr_ref r(m_manager); for (unsigned i = 0; i < g.size(); ++i) { (*er)(g.form(i), r); g.update(i, r); } } // ----------------- // collect uninterpreted variables in problem. // create a substitution from the variables to // bit-vector terms. // void add_var(app* n) { if (m_arith.is_int(n)) { add_int_var(n); } else { SASSERT(m_arith.is_real(n)); add_real_var(n); } } void add_int_var(app* n) { expr_ref s_bv(m_manager); sort_ref bv_sort(m_manager); optional low, up; numeral tmp; bool is_strict; if (m_bounds.has_lower(n, tmp, is_strict)) { SASSERT(!is_strict); low = tmp; } if (m_bounds.has_upper(n, tmp, is_strict)) { SASSERT(!is_strict); up = tmp; } // // [low .. up] // num_bits = log2(1 + |up - low|) or m_num_bits // unsigned num_bits = m_num_bits; if (up && low) { num_bits = log2(abs(*up - *low)+numeral(1)); } else { TRACE("nla2bv", tout << "no bounds for " << mk_ismt2_pp(n, m_manager) << "\n";); set_satisfiability_preserving(false); } bv_sort = m_bv.mk_sort(num_bits); std::string name = n->get_decl()->get_name().str(); s_bv = m_manager.mk_fresh_const(name.c_str(), bv_sort); m_fmc->insert(to_app(s_bv)->get_decl()); s_bv = m_bv.mk_bv2int(s_bv); if (low) { if (!(*low).is_zero()) { // low <= s_bv // ~> // replace s_bv by s_bv + low // add 'low' to model for n. // s_bv = m_arith.mk_add(s_bv, m_arith.mk_numeral(*low, true)); } } else if (up) { // s_bv <= up // ~> // replace s_bv by up - s_bv // s_bv = m_arith.mk_sub(m_arith.mk_numeral(*up, true), s_bv); } else { s_bv = m_arith.mk_sub(s_bv, m_arith.mk_numeral(rational::power_of_two(num_bits-1), true)); } m_trail.push_back(s_bv); m_subst.insert(n, s_bv); m_vars.push_back(n->get_decl()); m_defs.push_back(s_bv); } void add_real_var(app* n) { expr_ref s_bv(m_manager), s_bvr(m_manager), s(m_manager), t(m_manager); sort_ref bv_sort(m_manager); bv_sort = m_bv.mk_sort(m_num_bits); set_satisfiability_preserving(false); std::string name = n->get_decl()->get_name().str(); s = m_manager.mk_fresh_const(name.c_str(), bv_sort); name += "_r"; t = m_manager.mk_fresh_const(name.c_str(), bv_sort); m_fmc->insert(to_app(s)->get_decl()); m_fmc->insert(to_app(t)->get_decl()); s_bv = m_bv2real.mk_bv2real(s, t); m_trail.push_back(s_bv); m_subst.insert(n, s_bv); m_vars.push_back(n->get_decl()); // use version without bv2real function. m_bv2real.mk_bv2real_reduced(s, t, s_bvr); m_defs.push_back(s_bvr); } // update number of bits based on the largest constant used. void update_num_bits(app* n) { bool is_int; numeral nm; if (m_arith.is_numeral(n, nm, is_int) && is_int) { nm = abs(nm); unsigned l = log2(nm); if (m_num_bits <= l) { m_num_bits = l+1; } } } unsigned log2(rational const& n) { rational pow(1), two(2); unsigned sz = 0; while (pow < n) { ++sz; pow *= two; } if (sz == 0) sz = 1; return sz; } class get_uninterp_proc { imp& m_imp; ptr_vector m_vars; bool m_in_supported_fragment; public: get_uninterp_proc(imp& s): m_imp(s), m_in_supported_fragment(true) {} ptr_vector const& vars() { return m_vars; } void operator()(var * n) { m_in_supported_fragment = false; } void operator()(app* n) { arith_util& a = m_imp.m_arith; ast_manager& m = a.get_manager(); if (a.is_int(n) && is_uninterp_const(n)) { m_vars.push_back(n); } else if (a.is_real(n) && is_uninterp_const(n)) { m_vars.push_back(n); } else if (m.is_bool(n) && is_uninterp_const(n)) { } else if (!(a.is_mul(n) || a.is_add(n) || a.is_sub(n) || a.is_le(n) || a.is_lt(n) || a.is_ge(n) || a.is_gt(n) || a.is_numeral(n) || a.is_uminus(n) || m_imp.m_bv2real.is_pos_le(n) || m_imp.m_bv2real.is_pos_lt(n) || n->get_family_id() == a.get_manager().get_basic_family_id())) { TRACE("nla2bv", tout << "Not supported: " << mk_ismt2_pp(n, a.get_manager()) << "\n";); m_in_supported_fragment = false; } m_imp.update_num_bits(n); } void operator()(quantifier* q) { m_in_supported_fragment = false; } bool is_supported() const { return m_in_supported_fragment; } }; bool collect_vars(goal const & g) { get_uninterp_proc fe_var(*this); for_each_expr_at(fe_var, g); for (unsigned i = 0; i < fe_var.vars().size(); ++i) { add_var(fe_var.vars()[i]); } return fe_var.is_supported() && !fe_var.vars().empty(); } class count_mul_proc { imp& m_imp; unsigned m_count; public: count_mul_proc(imp& s): m_imp(s), m_count(0) {} unsigned count() const { return m_count; } void operator()(var * n) {} void operator()(app* n) { if (m_imp.m_arith.is_mul(n)) { m_count += n->get_num_args()-1; } if (m_imp.m_bv.is_bv_mul(n)) { unsigned num_vars = 0; for (unsigned j = 0; j < n->get_num_args(); ++j) { if (!m_imp.m_bv.is_numeral(n->get_arg(j))) { ++num_vars; } } if (num_vars > 1) { m_count += num_vars - 1; } } } void operator()(quantifier* q) {} }; unsigned count_mul(goal const & g) { count_mul_proc c(*this); for_each_expr_at(c, g); return c.count(); } }; params_ref m_params; imp * m_imp; struct scoped_set_imp { nla2bv_tactic & m_owner; scoped_set_imp(nla2bv_tactic & o, imp & i): m_owner(o) { #pragma omp critical (tactic_cancel) { m_owner.m_imp = &i; } } ~scoped_set_imp() { #pragma omp critical (tactic_cancel) { m_owner.m_imp = 0; } } }; public: nla2bv_tactic(params_ref const & p): m_params(p), m_imp(0) { } virtual tactic * translate(ast_manager & m) { return alloc(nla2bv_tactic, m_params); } virtual ~nla2bv_tactic() { } virtual void updt_params(params_ref const & p) { m_params = p; } virtual void collect_param_descrs(param_descrs & r) { r.insert("nla2bv_max_bv_size", CPK_UINT, "(default: inf) maximum bit-vector size used by nla2bv tactic"); r.insert("nla2bv_bv_size", CPK_UINT, "(default: 4) default bit-vector size used by nla2bv tactic."); r.insert("nla2bv_root", CPK_UINT, "(default: 2) nla2bv tactic encodes reals into bit-vectors using expressions of the form a+b*sqrt(c), this parameter sets the value of c used in the encoding."); r.insert("nla2bv_divisor", CPK_UINT, "(default: 2) nla2bv tactic parameter."); } /** \brief Modify a goal to use bounded bit-vector arithmetic in place of non-linear integer arithmetic. \return false if transformation is not possible. */ virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("nla2bv", g); fail_if_unsat_core_generation("nla2bv", g); mc = 0; pc = 0; core = 0; result.reset(); imp proc(g->m(), m_params); scoped_set_imp setter(*this, proc); proc(*(g.get()), mc); result.push_back(g.get()); SASSERT(g->is_well_sorted()); } virtual void cleanup(void) { } }; tactic * mk_nla2bv_tactic(ast_manager & m, params_ref const & p) { return alloc(nla2bv_tactic, p); } z3-4.4.0/src/tactic/arith/probe_arith.cpp0000644000175000017500000004105012540347414020014 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: probe_arith.cpp Abstract: Some probes for arithmetic problems. Author: Leonardo de Moura (leonardo) 2012-03-01. Revision History: --*/ #include"probe.h" #include"expr2polynomial.h" #include"for_each_expr.h" #include"arith_decl_plugin.h" #include"goal_util.h" class arith_degree_probe : public probe { struct proc { ast_manager & m; unsynch_mpq_manager m_qm; polynomial::manager m_pm; default_expr2polynomial m_expr2poly; arith_util m_util; unsigned m_max_degree; unsigned long long m_acc_degree; unsigned m_counter; proc(ast_manager & _m):m(_m), m_pm(m_qm), m_expr2poly(m, m_pm), m_util(m) { m_max_degree = 0; m_acc_degree = 0; m_counter = 0; } void updt_degree(polynomial_ref const & p) { unsigned deg = m_pm.total_degree(p); if (deg > m_max_degree) m_max_degree = deg; m_acc_degree += deg; m_counter++; } void process(app * n) { expr * lhs = n->get_arg(0); expr * rhs = n->get_arg(1); polynomial_ref p1(m_pm); polynomial_ref p2(m_pm); scoped_mpz d1(m_qm); scoped_mpz d2(m_qm); m_expr2poly.to_polynomial(lhs, p1, d1); m_expr2poly.to_polynomial(rhs, p2, d2); updt_degree(p1); updt_degree(p2); } void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { if (m_util.is_le(n) || m_util.is_lt(n) || m_util.is_gt(n) || m_util.is_ge(n)) process(n); if (m.is_eq(n) && m_util.is_int_real(n->get_arg(0))) process(n); } }; bool m_avg; public: arith_degree_probe(bool avg):m_avg(avg) {} virtual result operator()(goal const & g) { proc p(g.m()); for_each_expr_at(p, g); if (m_avg) return p.m_counter == 0 ? 0.0 : static_cast(p.m_acc_degree)/static_cast(p.m_counter); else return p.m_max_degree; } }; class arith_bw_probe : public probe { struct proc { ast_manager & m; arith_util m_util; unsigned m_max_bw; unsigned long long m_acc_bw; unsigned m_counter; proc(ast_manager & _m):m(_m), m_util(m) { m_max_bw = 0; m_acc_bw = 0; m_counter = 0; } void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { rational val; if (m_util.is_numeral(n, val)) { unsigned bw = val.bitsize(); if (bw > m_max_bw) m_max_bw = bw; m_acc_bw += bw; m_counter++; } } }; bool m_avg; public: arith_bw_probe(bool avg):m_avg(avg) {} virtual result operator()(goal const & g) { proc p(g.m()); for_each_expr_at(p, g); if (m_avg) return p.m_counter == 0 ? 0.0 : static_cast(p.m_acc_bw)/static_cast(p.m_counter); else return p.m_max_bw; } }; probe * mk_arith_avg_degree_probe() { return alloc(arith_degree_probe, true); } probe * mk_arith_max_degree_probe() { return alloc(arith_degree_probe, false); } probe * mk_arith_avg_bw_probe() { return alloc(arith_bw_probe, true); } probe * mk_arith_max_bw_probe() { return alloc(arith_bw_probe, false); } struct is_non_qflira_functor { struct found {}; ast_manager & m; arith_util u; bool m_int; bool m_real; is_non_qflira_functor(ast_manager & _m, bool _int, bool _real):m(_m), u(m), m_int(_int), m_real(_real) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } bool compatible_sort(app * n) const { if (m.is_bool(n)) return true; if (m_int && u.is_int(n)) return true; if (m_real && u.is_real(n)) return true; return false; } void operator()(app * n) { if (!compatible_sort(n)) throw found(); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == u.get_family_id()) { switch (n->get_decl_kind()) { case OP_LE: case OP_GE: case OP_LT: case OP_GT: case OP_ADD: case OP_NUM: return; case OP_MUL: if (n->get_num_args() != 2) throw found(); if (!u.is_numeral(n->get_arg(0))) throw found(); return; case OP_TO_REAL: if (!m_real) throw found(); break; default: throw found(); } return; } if (is_uninterp_const(n)) return; throw found(); } }; struct is_non_qfauflira_functor { struct found {}; ast_manager & m; arith_util m_arith_util; array_util m_array_util; bool m_int; bool m_real; is_non_qfauflira_functor(ast_manager & _m, bool _int, bool _real) : m(_m), m_arith_util(_m), m_array_util(_m), m_int(_int), m_real(_real) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } bool compatible_sort(app * n) const { if (m.is_bool(n)) return true; if (m_int && m_arith_util.is_int(n)) return true; if (m_real && m_arith_util.is_real(n)) return true; if (m_array_util.is_array(n)) return true; return false; } void operator()(app * n) { if (!compatible_sort(n)) throw found(); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == m_arith_util.get_family_id()) { switch (n->get_decl_kind()) { case OP_LE: case OP_GE: case OP_LT: case OP_GT: case OP_ADD: case OP_NUM: return; case OP_MUL: if (n->get_num_args() != 2) throw found(); if (!m_arith_util.is_numeral(n->get_arg(0))) throw found(); return; case OP_TO_REAL: if (!m_real) throw found(); break; default: throw found(); } return; } if (is_uninterp(n)) return; throw found(); } }; static bool is_qflia(goal const & g) { is_non_qflira_functor p(g.m(), true, false); return !test(g, p); } static bool is_qfauflia(goal const & g) { is_non_qfauflira_functor p(g.m(), true, false); return !test(g, p); } class is_qflia_probe : public probe { public: virtual result operator()(goal const & g) { return is_qflia(g); } }; class is_qfauflia_probe : public probe { public: virtual result operator()(goal const & g) { return is_qfauflia(g); } }; static bool is_qflra(goal const & g) { is_non_qflira_functor p(g.m(), false, true); return !test(g, p); } class is_qflra_probe : public probe { public: virtual result operator()(goal const & g) { return is_qflra(g); } }; static bool is_qflira(goal const & g) { is_non_qflira_functor p(g.m(), true, true); return !test(g, p); } class is_qflira_probe : public probe { public: virtual result operator()(goal const & g) { return is_qflira(g); } }; static bool is_lp(goal const & g) { ast_manager & m = g.m(); arith_util u(m); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * f = g.form(i); bool sign = false; while (m.is_not(f, f)) sign = !sign; if (m.is_eq(f) && !sign) { if (m.get_sort(to_app(f)->get_arg(0))->get_family_id() != u.get_family_id()) return false; continue; } if (u.is_le(f) || u.is_ge(f) || u.is_lt(f) || u.is_gt(f)) continue; return false; } return true; } static bool is_ilp(goal const & g) { if (!is_qflia(g)) return false; if (has_term_ite(g)) return false; return is_lp(g); } static bool is_mip(goal const & g) { if (!is_qflira(g)) return false; if (has_term_ite(g)) return false; return is_lp(g); } class is_ilp_probe : public probe { public: virtual result operator()(goal const & g) { return is_ilp(g); } }; class is_mip_probe : public probe { public: virtual result operator()(goal const & g) { return is_mip(g); } }; probe * mk_is_qflia_probe() { return alloc(is_qflia_probe); } probe * mk_is_qfauflia_probe() { return alloc(is_qfauflia_probe); } probe * mk_is_qflra_probe() { return alloc(is_qflra_probe); } probe * mk_is_qflira_probe() { return alloc(is_qflira_probe); } probe * mk_is_ilp_probe() { return alloc(is_ilp_probe); } probe * mk_is_mip_probe() { return alloc(is_mip_probe); } struct is_non_nira_functor { struct found {}; ast_manager & m; arith_util u; bool m_int; bool m_real; bool m_quant; bool m_linear; is_non_nira_functor(ast_manager & _m, bool _int, bool _real, bool _quant, bool linear):m(_m), u(m), m_int(_int), m_real(_real), m_quant(_quant), m_linear(linear) {} void throw_found() { throw found(); } void operator()(var * x) { if (!m_quant) throw_found(); sort * s = x->get_sort(); if (m_int && u.is_int(s)) return; if (m_real && u.is_real(s)) return; throw_found(); } void operator()(quantifier *) { if (!m_quant) throw_found(); } bool compatible_sort(app * n) const { if (m.is_bool(n)) return true; if (m_int && u.is_int(n)) return true; if (m_real && u.is_real(n)) return true; return false; } void operator()(app * n) { if (!compatible_sort(n)) throw_found(); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == u.get_family_id()) { switch (n->get_decl_kind()) { case OP_LE: case OP_GE: case OP_LT: case OP_GT: case OP_ADD: case OP_UMINUS: case OP_SUB: case OP_ABS: case OP_NUM: return; case OP_MUL: if (m_linear) { if (n->get_num_args() != 2) throw_found(); if (!u.is_numeral(n->get_arg(0))) throw_found(); } return; case OP_IDIV: case OP_DIV: case OP_REM: case OP_MOD: if (m_linear && !u.is_numeral(n->get_arg(1))) throw_found(); return; case OP_IS_INT: if (m_real) throw_found(); return; case OP_TO_INT: case OP_TO_REAL: return; case OP_POWER: if (m_linear) throw_found(); return; case OP_IRRATIONAL_ALGEBRAIC_NUM: if (m_linear || !m_real) throw_found(); return; default: throw_found(); } return; } if (is_uninterp_const(n)) return; throw_found(); } }; static bool is_qfnia(goal const & g) { is_non_nira_functor p(g.m(), true, false, false, false); return !test(g, p); } static bool is_qfnra(goal const & g) { is_non_nira_functor p(g.m(), false, true, false, false); return !test(g, p); } static bool is_nia(goal const & g) { is_non_nira_functor p(g.m(), true, false, true, false); return !test(g, p); } static bool is_nra(goal const & g) { is_non_nira_functor p(g.m(), false, true, true, false); return !test(g, p); } static bool is_nira(goal const & g) { is_non_nira_functor p(g.m(), true, true, true, false); return !test(g, p); } static bool is_lra(goal const & g) { is_non_nira_functor p(g.m(), false, true, true, true); return !test(g, p); } static bool is_lia(goal const & g) { is_non_nira_functor p(g.m(), true, false, true, true); return !test(g, p); } static bool is_lira(goal const & g) { is_non_nira_functor p(g.m(), true, true, true, true); return !test(g, p); } struct is_non_qfufnra_functor { struct found {}; ast_manager & m; arith_util u; bool m_has_nonlinear; is_non_qfufnra_functor(ast_manager & _m): m(_m), u(m), m_has_nonlinear(false) {} void throw_found() { throw found(); } bool has_nonlinear() const { return m_has_nonlinear; } void operator()(var * x) { throw_found(); } void operator()(quantifier *) { throw_found(); } void operator()(app * n) { family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == u.get_family_id()) { switch (n->get_decl_kind()) { case OP_LE: case OP_GE: case OP_LT: case OP_GT: case OP_ADD: case OP_UMINUS: case OP_SUB: case OP_ABS: case OP_NUM: case OP_IRRATIONAL_ALGEBRAIC_NUM: return; case OP_MUL: if (n->get_num_args() == 2 && u.is_real(n->get_arg(0)) && !u.is_numeral(n->get_arg(0)) && !u.is_numeral(n->get_arg(1))) { m_has_nonlinear = true; } return; case OP_IDIV: case OP_DIV: case OP_REM: case OP_MOD: if (!u.is_numeral(n->get_arg(1))) throw_found(); return; case OP_POWER: if (!u.is_numeral(n->get_arg(1))) throw_found(); m_has_nonlinear = true; return; case OP_IS_INT: case OP_TO_INT: case OP_TO_REAL: throw_found(); return; default: throw_found(); } } } }; class is_qfnia_probe : public probe { public: virtual result operator()(goal const & g) { return is_qfnia(g); } }; class is_qfnra_probe : public probe { public: virtual result operator()(goal const & g) { return is_qfnra(g); } }; class is_nia_probe : public probe { public: virtual result operator()(goal const & g) { return is_nia(g); } }; class is_nra_probe : public probe { public: virtual result operator()(goal const & g) { return is_nra(g); } }; class is_nira_probe : public probe { public: virtual result operator()(goal const & g) { return is_nira(g); } }; class is_lia_probe : public probe { public: virtual result operator()(goal const & g) { return is_lia(g); } }; class is_lra_probe : public probe { public: virtual result operator()(goal const & g) { return is_lra(g); } }; class is_lira_probe : public probe { public: virtual result operator()(goal const & g) { return is_lira(g); } }; static bool is_qfufnra(goal const& g) { is_non_qfufnra_functor p(g.m()); return !g.proofs_enabled() && !g.unsat_core_enabled() && !test(g, p) && p.has_nonlinear(); } class is_qfufnra_probe : public probe { public: virtual result operator()(goal const & g) { return is_qfufnra(g); } }; probe * mk_is_qfnia_probe() { return alloc(is_qfnia_probe); } probe * mk_is_qfnra_probe() { return alloc(is_qfnra_probe); } probe * mk_is_nia_probe() { return alloc(is_nia_probe); } probe * mk_is_nra_probe() { return alloc(is_nra_probe); } probe * mk_is_nira_probe() { return alloc(is_nira_probe); } probe * mk_is_lia_probe() { return alloc(is_lia_probe); } probe * mk_is_lra_probe() { return alloc(is_lra_probe); } probe * mk_is_lira_probe() { return alloc(is_lira_probe); } probe* mk_is_qfufnra_probe() { return alloc(is_qfufnra_probe); } z3-4.4.0/src/tactic/arith/purify_arith_tactic.h0000644000175000017500000000356212540347414021225 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: purify_arith_tactic.h Abstract: Tactic for eliminating arithmetic operators: DIV, IDIV, MOD, TO_INT, and optionally (OP_IRRATIONAL_ALGEBRAIC_NUM). This tactic uses the simplifier for also eliminating: OP_SUB, OP_UMINUS, OP_POWER (optionally), OP_REM, OP_IS_INT. Remarks: - The semantics of division by zero is not specified. Thus, uninterpreted functions are used. An ExRCF procedure may treat the unintepreted function applications as fresh constants. Then, in any model produced by this procedure, the interpretation for division by zero must be checked. - POWER operator can only be handled if the second argument is a rational value. The tactic has an option for preserving POWER operator where the second argument is an integer. - The semantics of (^ t (/ 1 k)) is not specified when t < 0 and k is even. Similarly to the division by zero case, uninterpreted function symbols are created. - The semantics of (^ t 0) is not specified if t == 0. Thus, uninterpreted function symbols are created. - TO_REAL is not really outside of the RCF language since it is only used for "casting". - All quantifiers must occur with positive polarity. The tactic snf (with skolemization disabled) is applied to enforce that. Author: Leonardo de Moura (leonardo) 2011-12-30. Revision History: --*/ #ifndef _PURIFY_ARITH_TACTIC_H_ #define _PURIFY_ARITH_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_purify_arith_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("purify-arith", "eliminate unnecessary operators: -, /, div, mod, rem, is-int, to-int, ^, root-objects.", "mk_purify_arith_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/arith/linear_equation.cpp0000644000175000017500000001771512540347414020710 0ustar michaelmichael/*++ Copyright (c) 2006 Microsoft Corporation Module Name: linear_equation.cpp Abstract: Basic infrastructure for managing linear equations of the form: a_1 * x_1 + ... + a_n * x_n = 0 Author: Leonardo de Moura (leonardo) 2011-06-28 Revision History: --*/ #include"linear_equation.h" /** \brief Return the position of variable x_i in the linear equation. Return UINT_MAX, if the variable is not in the linear_equation. */ unsigned linear_equation::pos(unsigned x_i) const { int low = 0; int high = m_size - 1; while (true) { int mid = low + ((high - low) / 2); var x_mid = m_xs[mid]; if (x_i > x_mid) { low = mid + 1; if (low > high) return UINT_MAX; } else if (x_i < x_mid) { high = mid - 1; if (low > high) return UINT_MAX; } else { return mid; } } } void linear_equation_manager::display(std::ostream & out, linear_equation const & eq) const { unsigned sz = eq.m_size; for (unsigned i = 0; i < sz; i++) { if (i > 0) out << " + "; out << m.to_string(eq.m_as[i]) << "*x" << eq.m_xs[i]; } out << " = 0"; } linear_equation * linear_equation_manager::mk(unsigned sz, mpq * as, var * xs, bool normalized) { SASSERT(sz > 1); // compute lcm of the denominators mpz l; mpz r; m.set(l, as[0].denominator()); for (unsigned i = 1; i < sz; i++) { m.set(r, as[i].denominator()); m.lcm(r, l, l); } TRACE("linear_equation_mk", tout << "lcm: " << m.to_string(l) << "\n";); // copy l * as to m_int_buffer. m_int_buffer.reset(); for (unsigned i = 0; i < sz; i++) { TRACE("linear_equation_mk", tout << "before as[" << i << "]: " << m.to_string(as[i]) << "\n";); m.mul(l, as[i], as[i]); TRACE("linear_equation_mk", tout << "after as[" << i << "]: " << m.to_string(as[i]) << "\n";); SASSERT(m.is_int(as[i])); m_int_buffer.push_back(as[i].numerator()); } linear_equation * new_eq = mk(sz, m_int_buffer.c_ptr(), xs, normalized); m.del(r); m.del(l); return new_eq; } linear_equation * linear_equation_manager::mk_core(unsigned sz, mpz * as, var * xs) { SASSERT(sz > 0); DEBUG_CODE({ for (unsigned i = 1; i < sz; i++) { SASSERT(xs[i-1] < xs[i]); } }); TRACE("linear_equation_bug", for (unsigned i = 0; i < sz; i++) tout << m.to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";); mpz g; m.set(g, as[0]); for (unsigned i = 1; i < sz; i++) { if (m.is_one(g)) break; if (m.is_neg(as[i])) { m.neg(as[i]); m.gcd(g, as[i], g); m.neg(as[i]); } else { m.gcd(g, as[i], g); } } if (!m.is_one(g)) { for (unsigned i = 0; i < sz; i++) { m.div(as[i], g, as[i]); } } TRACE("linear_equation_bug", tout << "g: " << m.to_string(g) << "\n"; for (unsigned i = 0; i < sz; i++) tout << m.to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";); m.del(g); unsigned obj_sz = linear_equation::get_obj_size(sz); void * mem = m_allocator.allocate(obj_sz); linear_equation * new_eq = new (mem) linear_equation(); mpz * new_as = reinterpret_cast(reinterpret_cast(new_eq) + sizeof(linear_equation)); double * new_app_as = reinterpret_cast(reinterpret_cast(new_as) + sz * sizeof(mpz)); var * new_xs = reinterpret_cast(reinterpret_cast(new_app_as) + sz * sizeof(double)); for (unsigned i = 0; i < sz; i++) { new (new_as + i) mpz(); m.set(new_as[i], as[i]); new_app_as[i] = m.get_double(as[i]); var x_i = xs[i]; new_xs[i] = x_i; } new_eq->m_size = sz; new_eq->m_as = new_as; new_eq->m_approx_as = new_app_as; new_eq->m_xs = new_xs; return new_eq; } linear_equation * linear_equation_manager::mk(unsigned sz, mpz * as, var * xs, bool normalized) { if (!normalized) { for (unsigned i = 0; i < sz; i++) { var x = xs[i]; m_mark.reserve(x+1, false); m_val_buffer.reserve(x+1); if (m_mark[x]) { m.add(m_val_buffer[x], as[i], m_val_buffer[x]); } else { m.set(m_val_buffer[x], as[i]); m_mark[x] = true; } } unsigned j = 0; for (unsigned i = 0; i < sz; i++) { var x = xs[i]; if (m_mark[x]) { if (!m.is_zero(m_val_buffer[x])) { xs[j] = xs[i]; m.set(as[j], m_val_buffer[x]); j++; } m_mark[x] = false; } } sz = j; if (sz <= 1) return 0; } else { DEBUG_CODE({ for (unsigned i = 0; i < sz; i++) { var x = xs[i]; m_mark.reserve(x+1, false); SASSERT(!m_mark[x]); m_mark[x] = true; } for (unsigned i = 0; i < sz; i++) { var x = xs[i]; m_mark[x] = false; } }); } for (unsigned i = 0; i < sz; i++) { var x = xs[i]; m_val_buffer.reserve(x+1); m.swap(m_val_buffer[x], as[i]); } std::sort(xs, xs+sz); for (unsigned i = 0; i < sz; i++) { var x = xs[i]; m.swap(as[i], m_val_buffer[x]); } return mk_core(sz, as, xs); } linear_equation * linear_equation_manager::mk(mpz const & b1, linear_equation const & eq1, mpz const & b2, linear_equation const & eq2) { SASSERT(!m.is_zero(b1)); SASSERT(!m.is_zero(b2)); mpz tmp, new_a; m_int_buffer.reset(); m_var_buffer.reset(); unsigned sz1 = eq1.size(); unsigned sz2 = eq2.size(); unsigned i1 = 0; unsigned i2 = 0; while (true) { if (i1 == sz1) { // copy remaining entries from eq2 while (i2 < sz2) { m_int_buffer.push_back(eq2.a(i2)); m.mul(m_int_buffer.back(), b2, m_int_buffer.back()); m_var_buffer.push_back(eq2.x(i2)); i2++; } break; } if (i2 == sz2) { // copy remaining entries from eq1 while (i1 < sz1) { m_int_buffer.push_back(eq1.a(i1)); m.mul(m_int_buffer.back(), b1, m_int_buffer.back()); m_var_buffer.push_back(eq1.x(i1)); i1++; } break; } var x1 = eq1.x(i1); var x2 = eq2.x(i2); if (x1 < x2) { m_int_buffer.push_back(eq1.a(i1)); m.mul(m_int_buffer.back(), b1, m_int_buffer.back()); m_var_buffer.push_back(eq1.x(i1)); i1++; } else if (x1 > x2) { m_int_buffer.push_back(eq2.a(i2)); m.mul(m_int_buffer.back(), b2, m_int_buffer.back()); m_var_buffer.push_back(eq2.x(i2)); i2++; } else { m.mul(eq1.a(i1), b1, tmp); m.addmul(tmp, b2, eq2.a(i2), new_a); if (!m.is_zero(new_a)) { m_int_buffer.push_back(new_a); m_var_buffer.push_back(eq1.x(i1)); } i1++; i2++; } } m.del(tmp); m.del(new_a); SASSERT(m_int_buffer.size() == m_var_buffer.size()); if (m_int_buffer.empty()) return 0; return mk_core(m_int_buffer.size(), m_int_buffer.c_ptr(), m_var_buffer.c_ptr()); } void linear_equation_manager::del(linear_equation * eq) { for (unsigned i = 0; i < eq->m_size; i++) { m.del(eq->m_as[i]); } unsigned obj_sz = linear_equation::get_obj_size(eq->m_size); m_allocator.deallocate(obj_sz, eq); } z3-4.4.0/src/tactic/arith/bv2int_rewriter.h0000644000175000017500000001032412540347414020312 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv2int_rewriter.h Abstract: Basic rewriting rules for bv2int propagation. Author: Nikolaj (nbjorner) 2011-05-05 Notes: --*/ #ifndef _BV2INT_REWRITER_H_ #define _BV2INT_REWRITER_H_ #include"ast.h" #include"rewriter.h" #include"bv_decl_plugin.h" #include"arith_decl_plugin.h" #include"params.h" #include"goal.h" class bv2int_rewriter_ctx { unsigned m_max_size; expr_ref_vector m_side_conditions; obj_map m_power2; expr_ref_vector m_trail; public: bv2int_rewriter_ctx(ast_manager& m, params_ref const& p) : m_side_conditions(m), m_trail(m) { update_params(p); } void reset() { m_side_conditions.reset(); m_trail.reset(); m_power2.reset(); } void add_side_condition(expr* e) { m_side_conditions.push_back(e); } unsigned num_side_conditions() const { return m_side_conditions.size(); } expr* const* side_conditions() const { return m_side_conditions.c_ptr(); } unsigned get_max_num_bits() const { return m_max_size; } void collect_power2(goal const & s); bool is_power2(expr* x, expr*& log_x); obj_map const& power2() const { return m_power2; } private: void update_params(params_ref const& p); }; class bv2int_rewriter { ast_manager & m_manager; bv2int_rewriter_ctx& m_ctx; bv_util m_bv; arith_util m_arith; public: bv2int_rewriter(ast_manager & m, bv2int_rewriter_ctx& ctx); ast_manager & m() const { return m_manager; } br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_app_core(f, num_args, args, result) == BR_FAILED) result = m().mk_app(f, num_args, args); } private: br_status mk_eq(expr * arg1, expr * arg2, expr_ref & result); br_status mk_ite(expr* c, expr* s, expr* t, expr_ref& result); br_status mk_le(expr * arg1, expr * arg2, expr_ref & result); br_status mk_lt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_ge(expr * arg1, expr * arg2, expr_ref & result); br_status mk_gt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_idiv(expr * arg1, expr * arg2, expr_ref & result); br_status mk_mod(expr * arg1, expr * arg2, expr_ref & result); br_status mk_rem(expr * arg1, expr * arg2, expr_ref & result); br_status mk_add(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_mul(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_sub(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_add(expr* s, expr* t, expr_ref& result); br_status mk_mul(expr* s, expr* t, expr_ref& result); br_status mk_sub(expr* s, expr* t, expr_ref& result); br_status mk_uminus(expr* e, expr_ref & result); bool is_bv2int(expr* e, expr_ref& s); bool is_sbv2int(expr* e, expr_ref& s); bool is_bv2int_diff(expr* e, expr_ref& s, expr_ref& t); bool is_zero(expr* e); bool is_shl1(expr* e, expr_ref& s); expr* mk_bv_add(expr* s, expr* t, bool is_signed); expr* mk_bv_mul(expr* s, expr* t, bool is_signed); expr* mk_sbv2int(expr* s); expr* mk_extend(unsigned sz, expr* b, bool is_signed); void align_sizes(expr_ref& s, expr_ref& t, bool is_signed); }; struct bv2int_rewriter_cfg : public default_rewriter_cfg { bv2int_rewriter m_r; bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = 0; return m_r.mk_app_core(f, num, args, result); } bv2int_rewriter_cfg(ast_manager & m, bv2int_rewriter_ctx& ctx):m_r(m, ctx) {} }; class bv2int_rewriter_star : public rewriter_tpl { bv2int_rewriter_cfg m_cfg; public: bv2int_rewriter_star(ast_manager & m, bv2int_rewriter_ctx& ctx): rewriter_tpl(m, false, m_cfg), m_cfg(m, ctx) {} }; #endif z3-4.4.0/src/tactic/arith/fix_dl_var_tactic.cpp0000644000175000017500000002705412540347414021172 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: fix_dl_var_tactic.cpp Abstract: Fix a difference logic variable to 0. If the problem is in the difference logic fragment, that is, all arithmetic terms are of the form (x + k), and the arithmetic atoms are of the form x - y <= k or x - y = k. Then, we can set one variable to 0. This is useful because, many bounds can be exposed after this operation is performed. Author: Leonardo de Moura (leonardo) 2011-12-19 Revision History: --*/ #include"tactical.h" #include"th_rewriter.h" #include"extension_model_converter.h" #include"arith_decl_plugin.h" #include"expr_substitution.h" #include"ast_smt2_pp.h" class fix_dl_var_tactic : public tactic { struct is_target { struct failed {}; ast_manager & m; arith_util & m_util; expr_fast_mark1 * m_visited; ptr_vector m_todo; obj_map m_occs; obj_map m_non_nested_occs; is_target(arith_util & u): m(u.get_manager()), m_util(u) { } void throw_failed(expr * ctx1, expr * ctx2 = 0) { TRACE("fix_dl_var", tout << mk_ismt2_pp(ctx1, m) << "\n"; if (ctx2) tout << mk_ismt2_pp(ctx2, m) << "\n";); throw failed(); } bool is_arith(expr * n) { sort * s = m.get_sort(n); return s->get_family_id() == m_util.get_family_id(); } // Return true if n is uninterpreted with respect to arithmetic. bool is_uninterp(expr * n) { return is_app(n) && to_app(n)->get_family_id() != m_util.get_family_id(); } // Remark: we say an expression is nested, if it occurs inside the boolean structure of the formula. // That is, the expression is not part of an unit clause comprising of a single inequality/equality. void inc_occ(expr * n, bool nested) { if (is_uninterp_const(n) && is_arith(n)) { obj_map::obj_map_entry * entry = m_occs.insert_if_not_there2(to_app(n), 0); entry->get_data().m_value++; if (!nested) { entry = m_non_nested_occs.insert_if_not_there2(to_app(n), 0); entry->get_data().m_value++; } } } void visit(expr * n, bool nested) { inc_occ(n, nested); if (!m_visited->is_marked(n)) { m_visited->mark(n); m_todo.push_back(n); } } void process_app(app * t) { unsigned num = t->get_num_args(); for (unsigned i = 0; i < num; i++) visit(t->get_arg(i), false); } void process_arith_atom(expr * lhs, expr * rhs, bool nested) { if (is_uninterp(lhs) && is_uninterp(rhs)) { visit(lhs, nested); visit(rhs, nested); return; } if (m_util.is_numeral(lhs)) std::swap(lhs, rhs); if (!m_util.is_numeral(rhs)) throw_failed(lhs, rhs); expr * t, * ms, * s; // check if lhs is of the form: (+ t (* (- 1) s)) if (m_util.is_add(lhs, t, ms) && m_util.is_times_minus_one(ms, s) && is_uninterp(t) && is_uninterp(s)) { visit(t, nested); visit(s, nested); } else { CTRACE("fix_dl_var", m_util.is_add(lhs, t, ms), s = 0; tout << "is_times_minus_one: " << m_util.is_times_minus_one(ms, s) << "\n"; tout << "is_uninterp(t): " << is_uninterp(t) << "\n"; tout << "t.family_id(): " << (is_app(t) ? to_app(t)->get_family_id() : -1) << "\n"; tout << "util.family_id: " << m_util.get_family_id() << "\n"; if (s) { tout << "is_uninterp(s): " << is_uninterp(s) << "\n"; tout << "s.family_id(): " << (is_app(s) ? to_app(s)->get_family_id() : -1) << "\n"; }); throw_failed(lhs, rhs); } } void process_eq(app * t, bool nested) { if (!is_arith(t->get_arg(0))) { process_app(t); return; } process_arith_atom(t->get_arg(0), t->get_arg(1), nested); } void process_arith(app * t, bool nested) { if (m.is_bool(t)) { process_arith_atom(t->get_arg(0), t->get_arg(1), nested); return; } // check if t is of the form c + k expr * c, * k; if (m_util.is_add(t, k, c) && is_uninterp(c) && m_util.is_numeral(k)) { visit(c, nested); } else { throw_failed(t); } } void process(expr * n) { if (m_visited->is_marked(n)) return; while (m.is_not(n, n)) ; if (is_app(n) && to_app(n)->get_family_id() == m_util.get_family_id()) { process_arith(to_app(n), false); return; } m_todo.push_back(n); m_visited->mark(n); while (!m_todo.empty()) { expr * n = m_todo.back(); m_todo.pop_back(); if (!is_app(n)) throw_failed(n); app * t = to_app(n); if (m.is_eq(t)) process_eq(t, true); else if (t->get_family_id() == m_util.get_family_id()) process_arith(t, true); else process_app(t); } } app * most_occs(obj_map & occs, unsigned & best) { app * r = 0; best = 0; obj_map::iterator it = occs.begin(); obj_map::iterator end = occs.end(); for (; it != end; ++it) { if (it->m_value > best) { best = it->m_value; r = it->m_key; } } return r; } // TODO make it a parameter #define NESTED_PENALTY 10 app * most_occs() { // We try to choose a variable that when set to 0 will generate many bounded variables. // That is why we give preference to variables occuring in non-nested inequalities. unsigned best1, best2; app * r1, * r2; r1 = most_occs(m_non_nested_occs, best1); r2 = most_occs(m_occs, best2); TRACE("fix_dl_var_choice", if (r1) { tout << "r1 occs: " << best1 << "\n"; tout << mk_ismt2_pp(r1, m) << "\n"; } if (r2) { tout << "r2 occs: " << best2 << "\n"; tout << mk_ismt2_pp(r2, m) << "\n"; }); if (best2 > NESTED_PENALTY * best1) return r2; else return r1; } app * operator()(goal const & g) { try { expr_fast_mark1 visited; m_visited = &visited; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { process(g.form(i)); } return most_occs(); } catch (failed) { return 0; } } }; struct imp { ast_manager & m; arith_util u; th_rewriter m_rw; bool m_produce_models; imp(ast_manager & _m, params_ref const & p): m(_m), u(m), m_rw(m, p) { } void updt_params(params_ref const & p) { m_rw.updt_params(p); } void set_cancel(bool f) { m_rw.set_cancel(f); } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("fix-dl-var", *g); bool produce_proofs = g->proofs_enabled(); m_produce_models = g->models_enabled(); app * var = is_target(u)(*g); if (var != 0) { IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(fixing-at-zero " << var->get_decl()->get_name() << ")\n";); tactic_report report("fix-dl-var", *g); expr_substitution subst(m); app * zero = u.mk_numeral(rational(0), u.is_int(var)); subst.insert(var, zero); m_rw.set_substitution(&subst); if (m_produce_models) { extension_model_converter * _mc = alloc(extension_model_converter, m); _mc->insert(var->get_decl(), zero); mc = _mc; } expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = g->size(); for (unsigned idx = 0; !g->inconsistent() && idx < size; idx++) { expr * curr = g->form(idx); m_rw(curr, new_curr, new_pr); if (produce_proofs) { proof * pr = g->pr(idx); new_pr = m.mk_modus_ponens(pr, new_pr); } g->update(idx, new_curr, new_pr, g->dep(idx)); } g->inc_depth(); } result.push_back(g.get()); TRACE("fix_dl_var", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: fix_dl_var_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(fix_dl_var_tactic, m, m_params); } virtual ~fix_dl_var_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { th_rewriter::get_param_descrs(r); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { try { (*m_imp)(in, result, mc, pc, core); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } virtual void cleanup() { imp * d = alloc(imp, m_imp->m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_fix_dl_var_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(fix_dl_var_tactic, m, p)); } z3-4.4.0/src/tactic/arith/card2bv_tactic.h0000644000175000017500000000624112540347414020040 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: card2bv_tactic.cpp Abstract: Tactic for converting Pseudo-Boolean constraints to BV Author: Nikolaj Bjorner (nbjorner) 2014-03-20 Notes: --*/ #ifndef _CARD2BV_TACTIC_ #define _CARD2BV_TACTIC_ #include"params.h" #include"pb_decl_plugin.h" #include"th_rewriter.h" #include"rewriter.h" #include #include"sorting_network.h" class ast_manager; class tactic; namespace pb { class card2bv_rewriter { public: typedef expr* literal; typedef ptr_vector literal_vector; private: ast_manager& m; arith_util au; pb_util pb; bv_util bv; psort_nw m_sort; expr_ref_vector m_lemmas; expr_ref_vector m_trail; unsigned get_num_bits(func_decl* f); void mk_bv(func_decl * f, unsigned sz, expr * const* args, expr_ref & result); br_status mk_shannon(func_decl * f, unsigned sz, expr * const* args, expr_ref & result); expr* negate(expr* e); expr* mk_ite(expr* c, expr* hi, expr* lo); bool is_or(func_decl* f); bool is_and(func_decl* f); public: card2bv_rewriter(ast_manager& m); br_status mk_app_core(func_decl * f, unsigned sz, expr * const* args, expr_ref & result); void mk_assert(func_decl * f, unsigned sz, expr * const* args, expr_ref & result, expr_ref_vector& lemmas); // definitions used for sorting network literal mk_false() { return m.mk_false(); } literal mk_true() { return m.mk_true(); } literal mk_max(literal a, literal b) { return trail(m.mk_or(a, b)); } literal mk_min(literal a, literal b) { return trail(m.mk_and(a, b)); } literal mk_not(literal a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } std::ostream& pp(std::ostream& out, literal lit); literal fresh(); literal trail(literal l); void mk_clause(unsigned n, literal const* lits); }; struct card2bv_rewriter_cfg : public default_rewriter_cfg { card2bv_rewriter m_r; bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = 0; return m_r.mk_app_core(f, num, args, result); } card2bv_rewriter_cfg(ast_manager & m):m_r(m) {} }; class card_pb_rewriter : public rewriter_tpl { card2bv_rewriter_cfg m_cfg; pb_util pb; expr_ref_vector m_lemmas; public: card_pb_rewriter(ast_manager & m): rewriter_tpl(m, false, m_cfg), m_cfg(m), pb(m), m_lemmas(m) {} void rewrite(expr* e, expr_ref& result); expr_ref_vector& lemmas() { return m_lemmas; } }; }; tactic * mk_card2bv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("card2bv", "convert pseudo-boolean constraints to bit-vectors.", "mk_card2bv_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/arith/bound_propagator.h0000644000175000017500000002455712540347414020545 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bound_propagator.h Abstract: Bound propagators for arithmetic. Support class for implementing strategies and search procedures Author: Leonardo de Moura (leonardo) 2011-06-18. Revision History: --*/ #ifndef _BOUND_PROPAGATOR_H_ #define _BOUND_PROPAGATOR_H_ #include"mpq.h" #include"vector.h" #include"params.h" #include"statistics.h" #include"numeral_buffer.h" #include"linear_equation.h" class bound_propagator { public: typedef unsigned var; typedef unsigned assumption; typedef unsynch_mpq_manager numeral_manager; typedef unsigned_vector assumption_vector; typedef unsigned constraint_id; typedef numeral_buffer mpz_buffer; typedef svector double_vector; static const assumption null_assumption = UINT_MAX; static const var null_var = UINT_MAX; static const unsigned null_constraint_idx = UINT_MAX; class trail_info { unsigned m_x_lower; public: trail_info(var x, bool is_lower):m_x_lower((x << 1) + static_cast(is_lower)) {} trail_info():m_x_lower(UINT_MAX) {} var x() const { return m_x_lower >> 1; } bool is_lower() const { return (m_x_lower & 1) != 0; } }; protected: enum ckind { LINEAR // only linear equalities so far. }; /** \brief Constraints don't need justification. */ class constraint { friend class bound_propagator; unsigned m_kind:2; unsigned m_dead:1; unsigned m_timestamp; // Constraint tried to propagate new bounds using bounds with timestamp < m_timestamp. unsigned m_act; // activity unsigned m_counter; // number of times the constraint propagated union { linear_equation * m_eq; }; }; enum bkind { AXIOM, // doesn't need justification ASSUMPTION, // aka external case-split, it is used to connect with external search engine. DERIVED, // implied DECISION // internal case-split }; struct bound { mpq m_k; double m_approx_k; unsigned m_lower:1; unsigned m_strict:1; unsigned m_mark:1; unsigned m_kind:2; unsigned m_level:27; unsigned m_timestamp; union { assumption m_assumption; unsigned m_constraint_idx; }; bound * m_prev; bound(numeral_manager & m, mpq const & k, double approx_k, bool lower, bool strict, unsigned lvl, unsigned ts, bkind bk, unsigned c_idx, assumption a, bound * prev); bound * at(unsigned timestamp); bkind kind() const { return static_cast(m_kind); } bool is_lower() const { return m_lower != 0; } }; typedef ptr_vector var2bound; typedef svector var_vector; typedef svector constraint_vector; typedef unsigned_vector c_idx_vector; typedef c_idx_vector wlist; typedef small_object_allocator allocator; typedef linear_equation_manager lin_eq_manager; numeral_manager & m; allocator & m_allocator; lin_eq_manager m_eq_manager; constraint_vector m_constraints; char_vector m_is_int; char_vector m_dead; var2bound m_lowers; var2bound m_uppers; vector m_watches; svector m_trail; unsigned m_qhead; c_idx_vector m_reinit_stack; unsigned_vector m_lower_refinements; // number of times a lower bound was propagated for each variable (loop prevention) unsigned_vector m_upper_refinements; // number of times a upper bound was propagated for each variable (loop prevention) unsigned m_timestamp; var m_conflict; mpq m_tmp; struct scope { unsigned m_trail_limit; unsigned m_qhead_old; unsigned m_reinit_stack_limit; unsigned m_timestamp_old:31; unsigned m_in_conflict:1; }; svector m_scopes; unsigned_vector m_to_reset_ts; // temp field: ids of the constraints we must reset the field m_timestamp // config unsigned m_max_refinements; // maximum number of refinements per round double m_small_interval; double m_threshold; // improvement threshold double m_strict2double; // statistics unsigned m_conflicts; unsigned m_propagations; unsigned m_false_alarms; void del_constraint(constraint & cnstr); void del_constraints_core(); template bool relevant_bound(var x, double approx_k) const; bool relevant_lower(var x, double approx_k) const; bool relevant_upper(var x, double approx_k) const; bool get_interval_size(var x, double & r) const; void check_feasibility(var x); bool assert_lower_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a); bool assert_upper_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a); bool propagate(unsigned c_idx); bool propagate_eq(unsigned c_idx); bool propagate_lower(unsigned c_idx, unsigned i); bool propagate_upper(unsigned c_idx, unsigned i); void undo_trail(unsigned old_sz); typedef std::pair var_bound; svector m_todo; void explain(var x, bound * b, unsigned ts, assumption_vector & ex) const; bool is_a_i_pos(linear_equation const & eq, var x) const; template bool get_bound(unsigned sz, Numeral const * as, var const * xs, mpq & r, bool & st) const; void init_eq(linear_equation * eq); public: bound_propagator(numeral_manager & m, allocator & a, params_ref const & p); ~bound_propagator(); void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); void collect_statistics(statistics & st) const; void reset_statistics(); double strict2double() const { return m_strict2double; } bool is_int(var x) const { return m_is_int[x] != 0; } unsigned scope_lvl() const { return m_scopes.size(); } void mk_var(var x, bool is_int); void del_var(var x); bool is_dead(var x) const { return m_dead[x] != 0; } void mk_eq(unsigned sz, mpq * as, var * xs); void mk_eq(unsigned sz, mpz * as, var * xs); void del_constraints(); void assert_lower(var x, mpq const & k, bool strict, assumption a = null_assumption) { m.set(m_tmp, k); assert_lower_core(x, m_tmp, strict, a == null_assumption ? AXIOM : ASSUMPTION, 0, a); } void assert_upper(var x, mpq const & k, bool strict, assumption a = null_assumption) { m.set(m_tmp, k); assert_upper_core(x, m_tmp, strict, a == null_assumption ? AXIOM : ASSUMPTION, 0, a); } void assert_decided_lower(var x, mpq const & k) { m.set(m_tmp, k); assert_lower_core(x, m_tmp, false, DECISION, 0, null_assumption); } void assert_decided_upper(var x, mpq const & k) { m.set(m_tmp, k); assert_upper_core(x, m_tmp, false, DECISION, 0, null_assumption); } void propagate(); void push(); void pop(unsigned num_scopes); void reset(); bool has_lower(var x) const { return m_lowers[x] != 0; } bool has_upper(var x) const { return m_uppers[x] != 0; } bool lower(var x, mpq & k, bool & strict, unsigned & ts) const; bool upper(var x, mpq & k, bool & strict, unsigned & ts) const; bool is_fixed(var x) const { return has_lower(x) && has_upper(x) && m.eq(m_lowers[x]->m_k, m_uppers[x]->m_k) && !inconsistent(); } mpq const & lower(var x, bool & strict) const { SASSERT(has_lower(x)); bound * b = m_lowers[x]; strict = b->m_strict; return b->m_k; } mpq const & upper(var x, bool & strict) const { SASSERT(has_upper(x)); bound * b = m_uppers[x]; strict = b->m_strict; return b->m_k; } mpq const & lower(var x) const { SASSERT(has_lower(x)); return m_lowers[x]->m_k; } mpq const & upper(var x) const { SASSERT(has_upper(x)); return m_uppers[x]->m_k; } double approx_lower(var x) const { SASSERT(has_lower(x)); return m_lowers[x]->m_strict ? m_lowers[x]->m_approx_k + m_strict2double : m_lowers[x]->m_approx_k; } double approx_upper(var x) const { SASSERT(has_upper(x)); return m_uppers[x]->m_strict ? m_uppers[x]->m_approx_k - m_strict2double : m_uppers[x]->m_approx_k; } bool is_zero(var x) const { return has_lower(x) && has_upper(x) && m.is_zero(lower(x)) && m.is_zero(upper(x)); } void explain_lower(var x, unsigned ts, assumption_vector & ex) const { explain(x, m_lowers[x], ts, ex); } void explain_upper(var x, unsigned ts, assumption_vector & ex) const { explain(x, m_uppers[x], ts, ex); } void explain_lower(var x, assumption_vector & ex) const { explain_lower(x, m_timestamp, ex); } void explain_upper(var x, assumption_vector & ex) const { explain_upper(x, m_timestamp, ex); } var conflict_var() const { return m_conflict; } bool inconsistent() const { return m_conflict != null_var; } unsigned trail_size() const { return m_trail.size(); } unsigned qhead() const { return m_qhead; } typedef svector::const_iterator trail_iterator; trail_iterator begin_trail() const { return m_trail.begin(); } trail_iterator end_trail() const { return m_trail.end(); } bool lower(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const; bool upper(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const; void display(std::ostream & out) const; void display_var_bounds(std::ostream & out, var x, bool approx = true, bool precise = true) const; void display_bounds(std::ostream & out, bool approx = true, bool precise = true) const; void display_precise_bounds(std::ostream & out) const { display_bounds(out, false, true); } void display_approx_bounds(std::ostream & out) const { display_bounds(out, true, false); } void display_constraints(std::ostream & out) const; void display_bounds_of(std::ostream & out, linear_equation const & eq) const; unsigned get_num_false_alarms() const { return m_false_alarms; } unsigned get_num_propagations() const { return m_propagations; } }; #endif z3-4.4.0/src/tactic/arith/pb2bv_tactic.h0000644000175000017500000000116712540347414017532 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: pb2bv_tactic.cpp Abstract: Tactic for converting Pseudo-Boolean constraints to BV Author: Christoph (cwinter) 2012-02-15 Notes: --*/ #ifndef _PB2BV_TACTIC_ #define _PB2BV_TACTIC_ #include"params.h" class ast_manager; class tactic; tactic * mk_pb2bv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("pb2bv", "convert pseudo-boolean constraints to bit-vectors.", "mk_pb2bv_tactic(m, p)") */ probe * mk_is_pb_probe(); /* ADD_PROBE("is-pb", "true if the goal is a pseudo-boolean problem.", "mk_is_pb_probe()") */ #endif z3-4.4.0/src/tactic/arith/pb2bv_tactic.cpp0000644000175000017500000011152512540347414020065 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: pb2bv_tactic.cpp Abstract: Tactic for converting Pseudo-Boolean constraints to BV Author: Christoph (cwinter) 2012-02-15 Notes: --*/ #include"tactical.h" #include"cooperate.h" #include"bound_manager.h" #include"bool_rewriter.h" #include"rewriter_def.h" #include"ref_util.h" #include"arith_decl_plugin.h" #include"trace.h" #include"ast_smt2_pp.h" #include"expr_substitution.h" #include"filter_model_converter.h" #include"pb2bv_model_converter.h" #include"pb2bv_tactic.h" class pb2bv_tactic : public tactic { public: struct non_pb {}; struct only_01_visitor { typedef rational numeral; ast_manager & m; arith_util & m_util; bound_manager & m_bm; only_01_visitor(arith_util & u, bound_manager & bm): m(u.get_manager()), m_util(u), m_bm(bm) { } void throw_non_pb(expr * n) { TRACE("pb2bv", tout << "Not pseudo-Boolean: " << mk_ismt2_pp(n, m) << "\n";); throw non_pb(); } void operator()(var * n) { throw_non_pb(n); } void operator()(app * n) { family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) { // all basic family ops (but term-ite and distinct) are OK if (m.is_term_ite(n) || m.is_distinct(n)) throw_non_pb(n); return; } if (fid == m_util.get_family_id()) { // check if linear switch (n->get_decl_kind()) { case OP_LE: case OP_GE: case OP_LT: case OP_GT: case OP_ADD: case OP_NUM: return; case OP_MUL: if (n->get_num_args() != 2) throw_non_pb(n); if (!m_util.is_numeral(n->get_arg(0))) throw_non_pb(n); return; default: throw_non_pb(n); } } if (is_uninterp_const(n)) { if (m.is_bool(n)) return; // boolean variables are ok if (m_util.is_int(n)) { numeral l, u; bool s; if (m_bm.has_lower(n, l, s) && m_bm.has_upper(n, u, s) && (l.is_zero() || l.is_one()) && (u.is_zero() || u.is_one())) return; } } throw_non_pb(n); } void operator()(quantifier * n) { throw_non_pb(n); } }; private: struct imp { typedef rational numeral; ast_manager & m; bound_manager m_bm; bool_rewriter m_b_rw; arith_util m_arith_util; bv_util m_bv_util; expr_dependency_ref_vector m_new_deps; bool m_produce_models; bool m_produce_unsat_cores; unsigned m_all_clauses_limit; unsigned m_cardinality_limit; unsigned long long m_max_memory; // m_const2bit should be a map, since we want constant time access to it, and avoid quadratic behavior. // It is ok to use a vector at the model converter because we don't need to search that vector. obj_map m_const2bit; obj_map m_not_const2bit; expr_ref_vector m_temporary_ints; expr_dependency_ref m_used_dependencies; struct lit { expr * m_v; public: lit(expr * v, bool sign = false):m_v(TAG(expr*, v, sign)) {} bool sign() const { return GET_TAG(m_v) == 1; } expr * var() const { return UNTAG(expr*, m_v); } void neg() { #ifdef Z3DEBUG bool s = sign(); #endif m_v = TAG(expr*, UNTAG(expr*, m_v), !sign()); SASSERT(s == !sign()); } }; struct monomial { numeral m_a; lit m_lit; monomial(lit l):m_a(1), m_lit(l) {} monomial(numeral const & a, lit l):m_a(a), m_lit(l) {} }; typedef vector polynomial; struct monomial_lt { bool operator()(monomial const & m1, monomial const & m2) const { return m1.m_a > m2.m_a; } }; enum constraint_kind { EQ, GE, LE }; struct failed {}; struct visitor { imp & m_owner; visitor(imp & o):m_owner(o) {} void throw_failed(expr * n) { throw failed(); } void operator()(var * n) { throw_failed(n); } void operator()(app * n) { } void operator()(quantifier * n) { throw_failed(n); } }; void checkpoint() { cooperate("pb2bv"); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); } void quick_pb_check(goal_ref const & g) { expr_fast_mark1 visited; only_01_visitor proc(m_arith_util, m_bm); unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { expr * f = g->form(i); for_each_expr_core(proc, visited, f); } } struct rw_cfg : public default_rewriter_cfg { ast_manager & m; imp & owner; expr_ref m_saved_res; rw_cfg(imp & o): m(o.m), owner(o), m_saved_res(m) { } bool max_steps_exceeded(unsigned num_steps) const { cooperate("pb2bv"); if (memory::get_allocation_size() > owner.m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); return false; } bool get_subst(expr * s, expr * & t, proof * & t_pr) { t_pr = 0; if (owner.is_constraint_core(s)) { owner.convert(to_app(s), m_saved_res, true, false); t = m_saved_res; TRACE("pb2bv_convert", tout << mk_ismt2_pp(s, m) << "\n-->\n" << mk_ismt2_pp(t, m) << "\n";); return true; } return false; } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(imp & o): rewriter_tpl(o.m, false, m_cfg), m_cfg(o) { } }; rw m_rw; struct pb2bv_all_clauses { imp & m_owner; ast_manager & m; unsigned m_size; vector m_sums; expr_ref_vector m_lits; ptr_vector m_cls; polynomial const * m_pol; expr_ref_vector m_result; pb2bv_all_clauses(imp & owner): m_owner(owner), m(m_owner.m), m_lits(m), m_result(m) { } void init_lits(polynomial const & p) { polynomial::const_iterator it = p.begin(); polynomial::const_iterator end = p.end(); for (; it != end; ++it) m_lits.push_back(m_owner.mon_lit2lit(it->m_lit)); } void init_sums(polynomial const & p) { SASSERT(m_sums.empty()); m_size = p.size(); m_sums.resize(m_size); unsigned i = m_size; while (i > 0) { --i; if (i == m_size - 1) m_sums[i] = p[i].m_a; else m_sums[i] = p[i].m_a + m_sums[i+1]; } } void process(unsigned idx, numeral c) { if (c.is_nonpos()) return; if (idx == m_size || m_sums[idx] < c) { SASSERT(c.is_pos()); // conflict 0 >= c > 0 switch (m_cls.size()) { case 0: m_result.push_back(m.mk_false()); break; case 1: m_result.push_back(m_cls[0]); break; default: m_result.push_back(m.mk_or(m_cls.size(), m_cls.c_ptr())); } return; } m_owner.checkpoint(); m_cls.push_back(m_lits.get(idx)); process(idx+1, c); m_cls.pop_back(); process(idx+1, c - (*m_pol)[idx].m_a); } void operator()(polynomial const & m_p, numeral const & m_c, expr_ref & r) { m_pol = &(m_p); init_sums(m_p); init_lits(m_p); process(0, m_c); m_owner.m_b_rw.mk_and(m_result.size(), m_result.c_ptr(), r); } }; void display(std::ostream & out, polynomial const & m_p, numeral const & m_c) const { polynomial::const_iterator it = m_p.begin(); polynomial::const_iterator end = m_p.end(); for (bool first = true; it != end; ++it) { if (!first) out << " + "; first = false; if (!it->m_a.is_one()) out << it->m_a << "*"; if (it->m_lit.sign()) out << "~"; out << mk_ismt2_pp(it->m_lit.var(), m); } out << " >= " << m_c << "\n"; } expr * int2lit(app * x, bool sign = false) { func_decl * fd = x->get_decl(); obj_map & const2lit = sign ? m_not_const2bit : m_const2bit; expr * r = 0; const2lit.find(fd, r); if (r != 0) return r; r = m.mk_fresh_const(0, m.mk_bool_sort()); expr * not_r = m.mk_not(r); m_const2bit.insert(fd, r); m_not_const2bit.insert(fd, not_r); m.inc_ref(fd); m.inc_ref(r); m.inc_ref(not_r); return sign ? not_r : r; } expr * mon_lit2lit(monomial const & mo) { return int2lit(to_app(mo.m_lit.var()), mo.m_lit.sign()); } expr * mk_unit(expr * t, bool sign) { return mon_lit2lit(lit(t, sign)); } static bool is_cardinality(polynomial const & m_p, numeral const & m_c) { for (unsigned i = 0; i < m_p.size(); i++) { if (!m_p[i].m_a.is_one()) return false; } return true; } void bitblast_pbc(polynomial & m_p, numeral const & m_c, expr_ref & r) { bool is_card = is_cardinality(m_p, m_c); if (is_card && numeral(m_p.size()) < m_c) { r = m.mk_false(); return; } if (is_card && m_c.is_one()) { ptr_buffer args; for (unsigned i = 0; i < m_p.size(); i++) { args.push_back(mon_lit2lit(m_p[i])); } r = m.mk_or(args.size(), args.c_ptr()); return; } if (is_card && m_c == numeral(m_p.size())) { ptr_buffer args; for (unsigned i = 0; i < m_p.size(); i++) { args.push_back(mon_lit2lit(m_p[i])); } m_b_rw.mk_and(args.size(), args.c_ptr(), r); return; } if (m_p.size() <= m_all_clauses_limit) { pb2bv_all_clauses proc(*this); proc(m_p, m_c, r); return; } if (is_card) { SASSERT(m_c < numeral(m_p.size())); // After normalization, this should be true. SASSERT(m_c.is_unsigned()); // Otherwise this is not going to fit into memory... unsigned n = m_p.size(); unsigned k = m_c.get_unsigned(); unsigned rowsz = n - k + 1; unsigned long long cost = k * rowsz; if (cost <= static_cast(m_cardinality_limit)) { SASSERT(rowsz > 0); expr_ref_vector tmp(m); tmp.resize(rowsz, m.mk_true()); for (unsigned i = 0; i < k; i++) { for (unsigned j = 0; j < rowsz; j++) { expr_ref new_ite(m); m_b_rw.mk_ite(mon_lit2lit(m_p[i + j]), tmp.get(j), j == 0 ? m.mk_false() : tmp.get(j-1), new_ite); tmp.set(j, new_ite.get()); } } TRACE("pb2bv_bv", tout << "BV Cardinality: " << mk_ismt2_pp(tmp.back(), m) << std::endl;); r = tmp.back(); return; } } TRACE("pb2bv_bv_detail", tout << "encoding:\n"; display(tout, m_p, m_c);); // [Leo] improving number of bits needed. // using (sum-of-coeffs).get_num_bits() numeral sum; for (unsigned i = 0; i < m_p.size(); i++) { monomial const & mo = m_p[i]; SASSERT(mo.m_a.is_pos()); sum += mo.m_a; } if (sum < m_c) { // trivially false. r = m.mk_false(); return; } unsigned bits = sum.get_num_bits(); TRACE("num_bits_bug", tout << "bits: " << bits << " sum: " << sum << " size: " << m_p.size() << "\n";); // [Leo]: The following assertion should hold, right? // I mean, the constraints are normalized, then mo.m_a <= m_c for every monomial in cnstr. // [Christoph]: I agree and never saw it violated so far! SASSERT(m_c.get_num_bits() <= bits); ptr_buffer lhs_args; for (unsigned i = 0; i < m_p.size(); i++) { monomial const & mo = m_p[i]; // encode using if-then-else expr * bv_monom = m.mk_ite(mon_lit2lit(mo.m_lit), m_bv_util.mk_numeral(mo.m_a, bits), m_bv_util.mk_numeral(numeral(0), bits)); lhs_args.push_back(bv_monom); } expr * lhs = m.mk_app(m_bv_util.get_family_id(), OP_BADD, lhs_args.size(), lhs_args.c_ptr()); expr * rhs = m_bv_util.mk_numeral(m_c, bits); r = m_bv_util.mk_ule(rhs, lhs); } void split(polynomial & m_p, numeral & m_c, polynomial & m_clause) { if (m_p.size() <= 2 || m_c.is_one()) return; if (m_p[0].m_a != m_c || m_p[1].m_a != m_c) return; // nothing to do. unsigned sz = m_p.size(); unsigned i; for (i = 2; i < sz; i++) { if (m_p[i].m_a != m_c) break; } if (i >= sz) { // [Christoph]: In this case, all the m_a are equal to m_c. return; } // copy lits [0, i) to m_clause for (unsigned j = 0; j < i; j++) m_clause.push_back(monomial(numeral(1), m_p[j].m_lit)); app * new_var = m.mk_fresh_const(0, m_arith_util.mk_int()); m_temporary_ints.push_back(new_var); m_clause.push_back(monomial(numeral(1), lit(new_var, true))); // remove monomials [0, i) from m_p and add new_var in the beginning for (unsigned j = i; j < sz; j++) { m_p[j - i + 1] = m_p[j]; } m_p.shrink(sz - i + 1); m_p[0] = monomial(m_c, lit(new_var, false)); } void mk_pbc(polynomial & m_p, numeral & m_c, expr_ref & r, bool enable_split) { TRACE("mk_pbc", display(tout, m_p, m_c); ); if (m_c.is_nonpos()) { // constraint is equivalent to true. r = m.mk_true(); return; } polynomial::iterator it = m_p.begin(); polynomial::iterator end = m_p.end(); numeral a_gcd = (it->m_a > m_c) ? m_c : it->m_a; for (; it != end; ++it) { if (it->m_a > m_c) it->m_a = m_c; // trimming coefficients a_gcd = gcd(a_gcd, it->m_a); } SASSERT(a_gcd.is_pos()); if (!a_gcd.is_one()) { it = m_p.begin(); for (; it != end; ++it) it->m_a /= a_gcd; m_c = ceil(m_c/a_gcd); } TRACE("mk_pbc", tout << "GCD = " << a_gcd << "; Normalized: "; display(tout, m_p, m_c); tout << "\n"; ); it = m_p.begin(); numeral a_sum; for (; it != end; ++it) { a_sum += m_c; if (a_sum >= m_c) break; } if (a_sum < m_c) { // constraint is equivalent to false. r = m.mk_false(); return; } polynomial clause; TRACE("split_bug", display(tout, m_p, m_c);); if (enable_split) split(m_p, m_c, clause); TRACE("split_bug", display(tout, m_p, m_c); display(tout, clause, rational(1));); if (clause.empty()) { bitblast_pbc(m_p, m_c, r); } else { expr_ref r1(m); expr_ref r2(m); bitblast_pbc(m_p, m_c, r1); bitblast_pbc(clause, numeral(1), r2); TRACE("split_bug", tout << mk_ismt2_pp(r1, m) << "\nAND\n" << mk_ismt2_pp(r2, m) << "\n";); m_b_rw.mk_and(r1, r2, r); } } void adjust(bool & pos, constraint_kind & k, numeral & c) { if (!pos) { if (k == LE) { // not (lhs <= c) --> lhs > c --> lhs >= c+1 pos = true; k = GE; c++; } else if (k == GE) { // not (lhs >= c) --> lhs < c --> lhs <= c-1 pos = true; k = LE; c--; } } SASSERT(pos || k == EQ); } void throw_non_pb(expr * n) { TRACE("pb2bv", tout << "Not pseudo-Boolean: " << mk_ismt2_pp(n, m) << "\n";); throw non_pb(); } // check if polynomial is encoding // a_0*x_0 + a_0*~y_0 + ... + a_{n-1}*x_{n - 1} + a_{n - 1}*~y_{n - 1} = c // x_0 = y_0, ..., x_{n - 1} = y_{n - 1} bool is_eq_vector(polynomial const & p, numeral const & c) { TRACE("is_eq_vector", display(tout, p, c);); unsigned sz = p.size(); if (sz % 2 == 1) return false; // size must be even // I implemented only the easy (and very common) case, where a_i = 2^{n-i-1} and c = 2^n - 1 unsigned n = sz/2; if (c != rational::power_of_two(n) - numeral(1)) return false; for (unsigned i = 0; i < n; i++) { monomial const & m1 = p[i*2]; monomial const & m2 = p[i*2+1]; if (m1.m_lit.sign() == m2.m_lit.sign()) return false; if (m1.m_a != m2.m_a) return false; if (m1.m_a != rational::power_of_two(n - i - 1)) return false; } return true; } void add_bounds_dependencies(expr * a) { m_used_dependencies = m.mk_join(m_used_dependencies, m_bm.lower_dep(a)); m_used_dependencies = m.mk_join(m_used_dependencies, m_bm.upper_dep(a)); } void convert(app * t, expr_ref & r, bool pos, bool root) { constraint_kind k; expr * lhs, * rhs; if (m.is_eq(t, lhs, rhs)) { if (is_uninterp_const(lhs) && is_uninterp_const(rhs)) { add_bounds_dependencies(lhs); add_bounds_dependencies(rhs); r = m.mk_iff(mon_lit2lit(lit(lhs, false)), mon_lit2lit(lit(rhs, !pos))); return; } k = EQ; } else if (m_arith_util.is_le(t, lhs, rhs)) { k = LE; } else if (m_arith_util.is_ge(t, lhs, rhs)) { k = GE; } else { throw_non_pb(t); } numeral c; bool is_int; if (m_arith_util.is_numeral(lhs, c)) { adjust(pos, k, c); if (is_uninterp_const(rhs)) { add_bounds_dependencies(rhs); if (k == EQ) { bool sign = c.is_zero(); if (!pos) sign = !sign; r = mk_unit(rhs, sign); } else if ((c.is_zero() && k == LE) || (c.is_one() && k == GE)) { // redundant 0 <= x, 1 >= x TRACE("pb2bv", tout << "discarding:\n" << mk_ismt2_pp(t, m) << "\n";); SASSERT(pos); r = m.mk_true(); } else { SASSERT((c.is_zero() && k == GE) || (c.is_one() && k == LE)); // unit 0 >= x, 1 <= x SASSERT(pos); r = mk_unit(rhs, k == GE); } return; } throw_non_pb(t); } if (!m_arith_util.is_numeral(rhs, c, is_int) || !is_int) throw_non_pb(t); adjust(pos, k, c); if (is_uninterp_const(lhs)) { add_bounds_dependencies(lhs); if (k == EQ) { TRACE("pb2bv_bug", tout << "c: " << c << "\n";); if (!c.is_zero() && !c.is_one()) { // x = k --> true where k is not 0 or 1 r = pos ? m.mk_false() : m.mk_true(); } else { bool sign = c.is_zero(); if (!pos) sign = !sign; r = mk_unit(lhs, sign); } return; } else { // Our atom is of the form: (<= lhs c) or (>= lhs c) // c may be different from 0,1. if (k == LE) { // x <= c >= 1 if (c >= numeral(1)) { TRACE("pb2bv", tout << "discarding:\n" << mk_ismt2_pp(t, m) << "\n";); r = m.mk_true(); return; } else if (c.is_neg()) { // x <= c < 0 r = m.mk_false(); return; } SASSERT(c.is_zero()); } else if (k == GE) { if (c.is_nonpos()) { TRACE("pb2bv", tout << "discarding:\n" << mk_ismt2_pp(t, m) << "\n";); // x >= a <= 0 r = m.mk_true(); return; } else if (c > numeral(1)) { // x >= a > 1 r = m.mk_false(); return; } SASSERT(c.is_one()); } CTRACE("pb2bv", !(c.is_zero() || c.is_one()), tout << "BUG: " << mk_ismt2_pp(t, m) << "\nk: " << k << " " << c << "\n";); SASSERT(c.is_zero() || c.is_one()); SASSERT(!((c.is_zero() && k == GE) || (c.is_one() && k == LE))); CTRACE("pb2bv_bug", !((c.is_zero() && k == LE) || (c.is_one() && k == GE)), tout << "c: " << c << ", k: " << k << "\n"; tout << "t: " << mk_ismt2_pp(t, m) << "\n";); SASSERT((c.is_zero() && k == LE) || (c.is_one() && k == GE)); // x <= 0, x >= 1 SASSERT(pos); r = mk_unit(lhs, k == LE); return; } } if (!m_arith_util.is_add(lhs)) throw_non_pb(t); unsigned sz = to_app(lhs)->get_num_args(); expr * const * ms = to_app(lhs)->get_args(); expr * a, * x; for (unsigned i = 0; i < sz; i++) { expr * m = ms[i]; if (is_uninterp_const(m)) continue; if (m_arith_util.is_mul(m, a, x) && m_arith_util.is_numeral(a) && is_uninterp_const(x)) continue; throw_non_pb(t); } // is pb constraint. numeral a_val; polynomial m_p; numeral m_c; m_c = c; for (unsigned i = 0; i < sz; i++) { expr * m = ms[i]; if (is_uninterp_const(m)) { add_bounds_dependencies(m); m_p.push_back(monomial(lit(m))); } else if (m_arith_util.is_mul(m, a, x) && m_arith_util.is_numeral(a, a_val)) { add_bounds_dependencies(x); if (a_val.is_neg()) { a_val.neg(); // -a x --> -a(1-!x) ==> -a + a!x, m_c += a_val; m_p.push_back(monomial(a_val, lit(x, true))); } else { m_p.push_back(monomial(a_val, lit(x))); } } else { UNREACHABLE(); } } std::stable_sort(m_p.begin(), m_p.end(), monomial_lt()); if (k == GE) { mk_pbc(m_p, m_c, r, root); } else if (k == LE) { m_c.neg(); for (unsigned i = 0; i < sz; i++) { monomial & m = m_p[i]; SASSERT(m.m_a.is_nonneg()); m_c += m.m_a; m.m_lit.neg(); } mk_pbc(m_p, m_c, r, root); } else { SASSERT(k == EQ); if (is_eq_vector(m_p, m_c)) { TRACE("is_eq_vector", tout << "found eq vector\n";); unsigned sz = m_p.size(); expr_ref_vector eqs(m); for (unsigned i = 0; i < sz; i += 2) { app * x_i = to_app(m_p[i].m_lit.var()); app * y_i = to_app(m_p[i+1].m_lit.var()); eqs.push_back(m.mk_eq(int2lit(x_i), int2lit(y_i))); } m_b_rw.mk_and(eqs.size(), eqs.c_ptr(), r); if (!pos) m_b_rw.mk_not(r, r); return; } polynomial m_p2; numeral m_c2 = m_c; m_c2.neg(); for (unsigned i = 0; i < sz; i++) { monomial m = m_p[i]; SASSERT(m.m_a.is_nonneg()); m_c2 += m.m_a; m.m_lit.neg(); m_p2.push_back(m); } expr_ref r1(m); expr_ref r2(m); mk_pbc(m_p, m_c, r1, false); mk_pbc(m_p2, m_c2, r2, false); TRACE("pb2bv_convert", tout << mk_ismt2_pp(t, m) << "\n"; display(tout, m_p, m_c); display(tout, m_p2, m_c2); tout << "--->\n" << mk_ismt2_pp(r1, m) << "\nAND\n" << mk_ismt2_pp(r2, m) << "\n";); m_b_rw.mk_and(r1, r2, r); if (!pos) m_b_rw.mk_not(r, r); } } bool is_constraint_core(expr * n) { return (m.is_eq(n) && m_arith_util.is_int(to_app(n)->get_arg(0))) || m_arith_util.is_le(n) || m_arith_util.is_ge(n); } bool is_constraint(expr * n, expr * & atom, bool & pos) { pos = true; while (m.is_not(n)) { n = to_app(n)->get_arg(0); pos = !pos; } atom = n; return is_constraint_core(n); } imp(ast_manager & _m, params_ref const & p): m(_m), m_bm(m), m_b_rw(m, p), m_arith_util(m), m_bv_util(m), m_new_deps(m), m_temporary_ints(m), m_used_dependencies(m), m_rw(*this) { updt_params(p); m_b_rw.set_flat(false); // no flattening otherwise will blowup the memory m_b_rw.set_elim_and(true); } ~imp() { dec_ref_map_key_values(m, m_const2bit); dec_ref_map_values(m, m_not_const2bit); m_rw.reset(); m_bm.reset(); m_temporary_ints.reset(); } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_all_clauses_limit = p.get_uint("pb2bv_all_clauses_limit", 8); m_cardinality_limit = p.get_uint("pb2bv_cardinality_limit", UINT_MAX); m_b_rw.updt_params(p); } void collect_param_descrs(param_descrs & r) { insert_max_memory(r); r.insert("pb2bv_all_clauses_limit", CPK_UINT, "(default: 8) maximum number of literals for using equivalent CNF encoding of PB constraint."); r.insert("pb2bv_cardinality_limit", CPK_UINT, "(default: inf) limit for using arc-consistent cardinality constraint encoding."); m_b_rw.get_param_descrs(r); r.erase("flat"); r.erase("elim_and"); } void set_cancel(bool f) { m_rw.set_cancel(f); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { TRACE("pb2bv", g->display(tout);); SASSERT(g->is_well_sorted()); fail_if_proof_generation("pb2bv", g); m_produce_models = g->models_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); mc = 0; pc = 0; core = 0; result.reset(); tactic_report report("pb2bv", *g); m_bm.reset(); m_rw.reset(); m_new_deps.reset(); if (g->inconsistent()) { result.push_back(g.get()); return; } m_bm(*g); TRACE("pb2bv", m_bm.display(tout);); try { quick_pb_check(g); } catch (non_pb) { throw tactic_exception("goal is in a fragment unsupported by pb2bv"); } unsigned size = g->size(); expr_ref_vector new_exprs(m); expr_dependency_ref_vector new_deps(m); try { expr_ref new_curr(m); proof_ref new_pr(m); expr_ref new_f(m); for (unsigned idx = 0; idx < size; idx++) { expr * curr = g->form(idx); expr * atom; bool pos; if (is_constraint(curr, atom, pos)) { convert(to_app(atom), new_f, pos, true); TRACE("pb2bv_convert", tout << "pos: " << pos << "\n" << mk_ismt2_pp(atom, m) << "\n--->\n" << mk_ismt2_pp(new_f, m) << "\n";); } else { m_rw(curr, new_f); } if (m_produce_unsat_cores) { new_deps.push_back(m.mk_join(m_used_dependencies, g->dep(idx))); m_used_dependencies.reset(); } new_exprs.push_back(new_f); } } catch (non_pb) { throw tactic_exception("goal is in a fragment unsupported by pb2bv"); } for (unsigned idx = 0; idx < size; idx++) g->update(idx, new_exprs[idx].get(), 0, (m_produce_unsat_cores) ? new_deps[idx].get() : g->dep(idx)); if (m_produce_models) { filter_model_converter * mc1 = alloc(filter_model_converter, m); obj_map::iterator it = m_const2bit.begin(); obj_map::iterator end = m_const2bit.end(); for (; it != end; ++it) mc1->insert(to_app(it->m_value)->get_decl()); // store temp int constants in the filter unsigned num_temps = m_temporary_ints.size(); for (unsigned i = 0; i < num_temps; i++) mc1->insert(to_app(m_temporary_ints.get(i))->get_decl()); pb2bv_model_converter * mc2 = alloc(pb2bv_model_converter, m, m_const2bit, m_bm); mc = concat(mc1, mc2); } g->inc_depth(); result.push_back(g.get()); TRACE("pb2bv", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: pb2bv_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(pb2bv_tactic, m, m_params); } virtual ~pb2bv_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { m_imp->collect_param_descrs(r); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } virtual void cleanup() { ast_manager & m = m_imp->m; imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } protected: virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_pb2bv_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(pb2bv_tactic, m, p)); } struct is_pb_probe : public probe { virtual result operator()(goal const & g) { try { ast_manager & m = g.m(); bound_manager bm(m); bm(g); arith_util a_util(m); expr_fast_mark1 visited; pb2bv_tactic::only_01_visitor proc(a_util, bm); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * f = g.form(i); for_each_expr_core(proc, visited, f); } return true; } catch (pb2bv_tactic::non_pb) { return false; } } }; probe * mk_is_pb_probe() { return alloc(is_pb_probe); } z3-4.4.0/src/tactic/arith/add_bounds_tactic.cpp0000644000175000017500000001266312540347414021157 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: add_bounds_tactic.h Abstract: Tactic for bounding unbounded variables. Author: Leonardo de Moura (leonardo) 2011-10-22. Revision History: --*/ #include"tactical.h" #include"arith_decl_plugin.h" #include"ast_smt2_pp.h" #include"bound_manager.h" struct is_unbounded_proc { struct found {}; arith_util m_util; bound_manager & m_bm; is_unbounded_proc(bound_manager & bm):m_util(bm.m()), m_bm(bm) {} void operator()(app * t) { if (is_uninterp_const(t) && (m_util.is_int(t) || m_util.is_real(t)) && (!m_bm.has_lower(t) || !m_bm.has_upper(t))) throw found(); } void operator()(var *) {} void operator()(quantifier*) {} }; bool is_unbounded(goal const & g) { ast_manager & m = g.m(); bound_manager bm(m); bm(g); is_unbounded_proc proc(bm); return test(g, proc); } class is_unbounded_probe : public probe { public: virtual result operator()(goal const & g) { return is_unbounded(g); } }; probe * mk_is_unbounded_probe() { return alloc(is_unbounded_probe); } class add_bounds_tactic : public tactic { struct imp { ast_manager & m; rational m_lower; rational m_upper; volatile bool m_cancel; imp(ast_manager & _m, params_ref const & p): m(_m) { updt_params(p); } void updt_params(params_ref const & p) { m_lower = p.get_rat("add_bound_lower", rational(-2)); m_upper = p.get_rat("add_bound_upper", rational(2)); } void set_cancel(bool f) { m_cancel = f; } struct add_bound_proc { arith_util m_util; bound_manager & m_bm; goal & m_goal; rational const & m_lower; rational const & m_upper; unsigned m_num_bounds; add_bound_proc(bound_manager & bm, goal & g, rational const & l, rational const & u): m_util(bm.m()), m_bm(bm), m_goal(g), m_lower(l), m_upper(u) { m_num_bounds = 0; } void operator()(app * t) { if (is_uninterp_const(t) && (m_util.is_int(t) || m_util.is_real(t))) { if (!m_bm.has_lower(t)) { m_goal.assert_expr(m_util.mk_le(t, m_util.mk_numeral(m_upper, m_util.is_int(t)))); m_num_bounds++; } if (!m_bm.has_upper(t)) { m_goal.assert_expr(m_util.mk_ge(t, m_util.mk_numeral(m_lower, m_util.is_int(t)))); m_num_bounds++; } } } void operator()(var *) {} void operator()(quantifier*) {} }; virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { mc = 0; pc = 0; core = 0; tactic_report report("add-bounds", *g); bound_manager bm(m); expr_fast_mark1 visited; add_bound_proc proc(bm, *(g.get()), m_lower, m_upper); unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) quick_for_each_expr(proc, visited, g->form(i)); visited.reset(); g->inc_depth(); result.push_back(g.get()); if (proc.m_num_bounds > 0) g->updt_prec(goal::UNDER); report_tactic_progress(":added-bounds", proc.m_num_bounds); TRACE("add_bounds", g->display(tout);); } }; imp * m_imp; params_ref m_params; public: add_bounds_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(add_bounds_tactic, m, m_params); } virtual ~add_bounds_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { r.insert("add_bound_lower", CPK_NUMERAL, "(default: -2) lower bound to be added to unbounded variables."); r.insert("add_bound_upper", CPK_NUMERAL, "(default: 2) upper bound to be added to unbounded variables."); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(g, result, mc, pc, core); } virtual void cleanup() { imp * d = alloc(imp, m_imp->m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } protected: virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_add_bounds_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(add_bounds_tactic, m, p)); } z3-4.4.0/src/tactic/arith/degree_shift_tactic.cpp0000644000175000017500000003003212540347414021473 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: degree_shift_tactic.cpp Abstract: Simple degree shift procedure. Basic idea: if goal G contains a real variable x, x occurs with degrees d_1, ..., d_k in G, and n = gcd(d_1, ..., d_k) > 1. Then, replace x^n with a new fresh variable y. Author: Leonardo de Moura (leonardo) 2011-12-30. Revision History: --*/ #include"tactical.h" #include"filter_model_converter.h" #include"extension_model_converter.h" #include"cooperate.h" #include"arith_decl_plugin.h" #include"simplify_tactic.h" #include"ast_smt2_pp.h" #include"rewriter_def.h" class degree_shift_tactic : public tactic { struct imp { ast_manager & m; arith_util m_autil; obj_map m_var2degree; obj_map m_var2var; obj_map m_var2pr; expr_ref_vector m_pinned; ptr_vector m_todo; rational m_one; bool m_produce_models; bool m_produce_proofs; volatile bool m_cancel; expr * mk_power(expr * t, rational const & k) { if (k.is_one()) return t; else return m_autil.mk_power(t, m_autil.mk_numeral(k, false)); } struct rw_cfg : public default_rewriter_cfg { imp & o; rw_cfg(imp & _o):o(_o) {} br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { arith_util & u = o.m_autil; if (!is_decl_of(f, u.get_family_id(), OP_POWER) || !is_app(args[0])) return BR_FAILED; ast_manager & m = o.m; rational g; app * t = to_app(args[0]); if (!o.m_var2degree.find(t, g)) return BR_FAILED; SASSERT(g > rational(1)); SASSERT(g.is_int()); rational k; VERIFY(u.is_numeral(args[1], k)); SASSERT(gcd(k, g) == g); rational new_k = div(k, g); expr * new_arg = o.m_var2var.find(t); result = o.mk_power(new_arg, new_k); if (o.m_produce_proofs) { proof * pr = o.m_var2pr.find(t); app * fact = m.mk_eq(m.mk_app(f, num, args), result); result_pr = m.mk_th_lemma(u.get_family_id(), fact, 1, &pr); } return BR_DONE; } }; class rw : public rewriter_tpl { rw_cfg m_cfg; public: rw(imp & o): rewriter_tpl(o.m, o.m_produce_proofs, m_cfg), m_cfg(o) { } }; scoped_ptr m_rw; imp(ast_manager & _m): m(_m), m_autil(_m), m_pinned(_m), m_one(1), m_rw(0) { m_cancel = false; } void set_cancel(bool f) { m_cancel = f; } void checkpoint() { if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); cooperate("degree_shift"); } void visit(expr * t, expr_fast_mark1 & visited) { if (!visited.is_marked(t)) { visited.mark(t); m_todo.push_back(t); } } void save_degree(expr * t, rational const & k) { SASSERT(k.is_int()); if (is_uninterp_const(t) && m_autil.is_real(t)) { rational old_k; if (m_var2degree.find(to_app(t), old_k)) { old_k = gcd(k, old_k); m_var2degree.insert(to_app(t), old_k); } else { m_var2degree.insert(to_app(t), k); } } } void visit_args(expr * t, expr_fast_mark1 & visited) { if (is_app(t)) { unsigned num_args = to_app(t)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(t)->get_arg(i); save_degree(arg, m_one); visit(arg, visited); } } } void collect(expr * t, expr_fast_mark1 & visited) { rational k; visit(t, visited); while (!m_todo.empty()) { checkpoint(); expr * t = m_todo.back(); m_todo.pop_back(); if (is_var(t)) continue; if (is_quantifier(t)) { unsigned num_children = to_quantifier(t)->get_num_children(); for (unsigned i = 0; i < num_children; i ++) visit(to_quantifier(t)->get_child(i), visited); } else { SASSERT(is_app(t)); if (m_autil.is_power(t) && m_autil.is_numeral(to_app(t)->get_arg(1), k) && k.is_int() && k.is_pos()) { expr * arg = to_app(t)->get_arg(0); save_degree(arg, k); visit_args(arg, visited); } else { visit_args(t, visited); } } } } void display_candidates(std::ostream & out) { out << "candidates:\n"; obj_map::iterator it = m_var2degree.begin(); obj_map::iterator end = m_var2degree.end(); for (; it != end; ++it) { if (!it->m_value.is_one()) { out << "POWER: " << it->m_value << "\n" << mk_ismt2_pp(it->m_key, m) << "\n"; } } } void collect(goal const & g) { m_var2degree.reset(); expr_fast_mark1 visited; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { collect(g.form(i), visited); } TRACE("degree_shift", display_candidates(tout);); } void discard_non_candidates() { m_pinned.reset(); ptr_vector to_delete; obj_map::iterator it = m_var2degree.begin(); obj_map::iterator end = m_var2degree.end(); for (; it != end; ++it) { if (it->m_value.is_one()) to_delete.push_back(it->m_key); else m_pinned.push_back(it->m_key); // make sure it is not deleted during simplifications } ptr_vector::iterator it2 = to_delete.begin(); ptr_vector::iterator end2 = to_delete.end(); for (; it2 != end2; ++it2) m_var2degree.erase(*it2); } void prepare_substitution(model_converter_ref & mc) { SASSERT(!m_var2degree.empty()); filter_model_converter * fmc = 0; extension_model_converter * xmc = 0; if (m_produce_models) { fmc = alloc(filter_model_converter, m); xmc = alloc(extension_model_converter, m); mc = concat(fmc, xmc); } obj_map::iterator it = m_var2degree.begin(); obj_map::iterator end = m_var2degree.end(); for (; it != end; ++it) { SASSERT(it->m_value.is_int()); SASSERT(it->m_value >= rational(2)); app * fresh = m.mk_fresh_const(0, it->m_key->get_decl()->get_range()); m_pinned.push_back(fresh); m_var2var.insert(it->m_key, fresh); if (m_produce_models) { fmc->insert(fresh->get_decl()); xmc->insert(it->m_key->get_decl(), mk_power(fresh, rational(1)/it->m_value)); } if (m_produce_proofs) { expr * s = mk_power(it->m_key, it->m_value); expr * eq = m.mk_eq(fresh, s); proof * pr1 = m.mk_def_intro(eq); proof * result_pr = m.mk_apply_def(fresh, s, pr1); m_pinned.push_back(result_pr); m_var2pr.insert(it->m_key, result_pr); } } } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; m_produce_proofs = g->proofs_enabled(); m_produce_models = g->models_enabled(); tactic_report report("degree_shift", *g); collect(*g); discard_non_candidates(); if (!m_var2degree.empty()) { prepare_substitution(mc); m_rw = alloc(rw, *this); // substitute expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { checkpoint(); expr * curr = g->form(idx); (*m_rw)(curr, new_curr, new_pr); if (m_produce_proofs) { proof * pr = g->pr(idx); new_pr = m.mk_modus_ponens(pr, new_pr); } g->update(idx, new_curr, new_pr, g->dep(idx)); } // add >= 0 constraints for variables with even degree obj_map::iterator it = m_var2degree.begin(); obj_map::iterator end = m_var2degree.end(); for (; it != end; ++it) { SASSERT(it->m_value.is_int()); SASSERT(it->m_value >= rational(2)); if (it->m_value.is_even()) { app * new_var = m_var2var.find(it->m_key); app * new_c = m_autil.mk_ge(new_var, m_autil.mk_numeral(rational(0), false)); proof * new_pr = 0; if (m_produce_proofs) { proof * pr = m_var2pr.find(it->m_key); new_pr = m.mk_th_lemma(m_autil.get_family_id(), new_c, 1, &pr); } g->assert_expr(new_c, new_pr, 0); } } } g->inc_depth(); result.push_back(g.get()); TRACE("degree_shift", g->display(tout); if (mc) mc->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; public: degree_shift_tactic(ast_manager & m) { m_imp = alloc(imp, m); } virtual tactic * translate(ast_manager & m) { return alloc(degree_shift_tactic, m); } virtual ~degree_shift_tactic() { dealloc(m_imp); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } virtual void cleanup() { imp * d = alloc(imp, m_imp->m); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } protected: virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_degree_shift_tactic(ast_manager & m, params_ref const & p) { params_ref mul2power_p; mul2power_p.set_bool("mul_to_power", true); return and_then(using_params(mk_simplify_tactic(m), mul2power_p), clean(alloc(degree_shift_tactic, m))); } z3-4.4.0/src/tactic/arith/factor_tactic.cpp0000644000175000017500000002701012540347414020323 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: factor_tactic.cpp Abstract: Polynomial factorization tactic. Author: Leonardo de Moura (leonardo) 2012-02-03 Revision History: --*/ #include"tactical.h" #include"expr2polynomial.h" #include"rewriter_def.h" class factor_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { ast_manager & m; arith_util m_util; unsynch_mpq_manager m_qm; polynomial::manager m_pm; default_expr2polynomial m_expr2poly; polynomial::factor_params m_fparams; bool m_split_factors; rw_cfg(ast_manager & _m, params_ref const & p): m(_m), m_util(_m), m_pm(m_qm), m_expr2poly(m, m_pm) { updt_params(p); } void updt_params(params_ref const & p) { m_split_factors = p.get_bool("split_factors", true); m_fparams.updt_params(p); } expr * mk_mul(unsigned sz, expr * const * args) { SASSERT(sz > 0); if (sz == 1) return args[0]; return m_util.mk_mul(sz, args); } expr * mk_zero_for(expr * arg) { return m_util.mk_numeral(rational(0), m_util.is_int(arg)); } // p1^k1 * p2^k2 = 0 --> p1*p2 = 0 void mk_eq(polynomial::factors const & fs, expr_ref & result) { expr_ref_buffer args(m); expr_ref arg(m); for (unsigned i = 0; i < fs.distinct_factors(); i++) { m_expr2poly.to_expr(fs[i], true, arg); args.push_back(arg); } result = m.mk_eq(mk_mul(args.size(), args.c_ptr()), mk_zero_for(arg)); } // p1^k1 * p2^k2 = 0 --> p1 = 0 or p2 = 0 void mk_split_eq(polynomial::factors const & fs, expr_ref & result) { expr_ref_buffer args(m); expr_ref arg(m); for (unsigned i = 0; i < fs.distinct_factors(); i++) { m_expr2poly.to_expr(fs[i], true, arg); args.push_back(m.mk_eq(arg, mk_zero_for(arg))); } if (args.size() == 1) result = args[0]; else result = m.mk_or(args.size(), args.c_ptr()); } decl_kind flip(decl_kind k) { SASSERT(k == OP_LT || k == OP_GT || k == OP_LE || k == OP_GE); switch (k) { case OP_LT: return OP_GT; case OP_LE: return OP_GE; case OP_GT: return OP_LT; case OP_GE: return OP_LE; default: UNREACHABLE(); return k; } } // p1^{2*k1} * p2^{2*k2 + 1} >=< 0 // --> // (p1^2)*p2 >=<0 void mk_comp(decl_kind k, polynomial::factors const & fs, expr_ref & result) { SASSERT(k == OP_LT || k == OP_GT || k == OP_LE || k == OP_GE); expr_ref_buffer args(m); expr_ref arg(m); for (unsigned i = 0; i < fs.distinct_factors(); i++) { m_expr2poly.to_expr(fs[i], true, arg); if (fs.get_degree(i) % 2 == 0) arg = m_util.mk_power(arg, m_util.mk_numeral(rational(2), m_util.is_int(arg))); args.push_back(arg); } expr * lhs = mk_mul(args.size(), args.c_ptr()); result = m.mk_app(m_util.get_family_id(), k, lhs, mk_zero_for(lhs)); } // See mk_split_strict_comp and mk_split_nonstrict_comp void split_even_odd(bool strict, polynomial::factors const & fs, expr_ref_buffer & even_eqs, expr_ref_buffer & odd_factors) { expr_ref arg(m); for (unsigned i = 0; i < fs.distinct_factors(); i++) { m_expr2poly.to_expr(fs[i], true, arg); if (fs.get_degree(i) % 2 == 0) { expr * eq = m.mk_eq(arg, mk_zero_for(arg)); if (strict) even_eqs.push_back(m.mk_not(eq)); else even_eqs.push_back(eq); } else { odd_factors.push_back(arg); } } } // Strict case // p1^{2*k1} * p2^{2*k2 + 1} >< 0 // --> // p1 != 0 and p2 >< 0 // // Nonstrict // p1^{2*k1} * p2^{2*k2 + 1} >=< 0 // --> // p1 = 0 or p2 >=< 0 // void mk_split_comp(decl_kind k, polynomial::factors const & fs, expr_ref & result) { SASSERT(k == OP_LT || k == OP_GT || k == OP_LE || k == OP_GE); bool strict = (k == OP_LT) || (k == OP_GT); expr_ref_buffer args(m); expr_ref_buffer odd_factors(m); split_even_odd(strict, fs, args, odd_factors); if (odd_factors.empty()) { if (k == OP_LT) { result = m.mk_false(); return; } if (k == OP_GE) { result = m.mk_true(); return; } } else { args.push_back(m.mk_app(m_util.get_family_id(), k, mk_mul(odd_factors.size(), odd_factors.c_ptr()), mk_zero_for(odd_factors[0]))); } SASSERT(!args.empty()); if (args.size() == 1) result = args[0]; else if (strict) result = m.mk_and(args.size(), args.c_ptr()); else result = m.mk_or(args.size(), args.c_ptr()); } br_status factor(func_decl * f, expr * lhs, expr * rhs, expr_ref & result) { polynomial_ref p1(m_pm); polynomial_ref p2(m_pm); scoped_mpz d1(m_qm); scoped_mpz d2(m_qm); m_expr2poly.to_polynomial(lhs, p1, d1); m_expr2poly.to_polynomial(rhs, p2, d2); TRACE("factor_tactic_bug", tout << "lhs: " << mk_ismt2_pp(lhs, m) << "\n"; tout << "p1: " << p1 << "\n"; tout << "d1: " << d1 << "\n"; tout << "rhs: " << mk_ismt2_pp(rhs, m) << "\n"; tout << "p2: " << p2 << "\n"; tout << "d2: " << d2 << "\n";); scoped_mpz lcm(m_qm); m_qm.lcm(d1, d2, lcm); m_qm.div(lcm, d1, d1); m_qm.div(lcm, d2, d2); m_qm.neg(d2); polynomial_ref p(m_pm); p = m_pm.addmul(d1, m_pm.mk_unit(), p1, d2, m_pm.mk_unit(), p2); if (is_const(p)) return BR_FAILED; polynomial::factors fs(m_pm); TRACE("factor_tactic_bug", tout << "p: " << p << "\n";); m_pm.factor(p, fs, m_fparams); SASSERT(fs.distinct_factors() > 0); TRACE("factor_tactic_bug", tout << "factors:\n"; fs.display(tout); tout << "\n";); if (fs.distinct_factors() == 1 && fs.get_degree(0) == 1) return BR_FAILED; if (m.is_eq(f)) { if (m_split_factors) mk_split_eq(fs, result); else mk_eq(fs, result); } else { decl_kind k = f->get_decl_kind(); if (m_qm.is_neg(fs.get_constant())) k = flip(k); if (m_split_factors) mk_split_comp(k, fs, result); else mk_comp(k, fs, result); } return BR_DONE; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (num != 2) return BR_FAILED; if (m.is_eq(f) && (m_util.is_arith_expr(args[0]) || m_util.is_arith_expr(args[1])) && (!m.is_bool(args[0]))) return factor(f, args[0], args[1], result); if (f->get_family_id() != m_util.get_family_id()) return BR_FAILED; switch (f->get_decl_kind()) { case OP_LT: case OP_GT: case OP_LE: case OP_GE: return factor(f, args[0], args[1], result); } return BR_FAILED; } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { } }; struct imp { ast_manager & m; rw m_rw; imp(ast_manager & _m, params_ref const & p): m(_m), m_rw(m, p) { } void set_cancel(bool f) { m_rw.set_cancel(f); m_rw.cfg().m_pm.set_cancel(f); } void updt_params(params_ref const & p) { m_rw.cfg().updt_params(p); } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("factor", *g); bool produce_proofs = g->proofs_enabled(); expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { expr * curr = g->form(idx); m_rw(curr, new_curr, new_pr); if (produce_proofs) { proof * pr = g->pr(idx); new_pr = m.mk_modus_ponens(pr, new_pr); } g->update(idx, new_curr, new_pr, g->dep(idx)); } g->inc_depth(); result.push_back(g.get()); TRACE("factor", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: factor_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(factor_tactic, m, m_params); } virtual ~factor_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->m_rw.cfg().updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { r.insert("split_factors", CPK_BOOL, "(default: true) apply simplifications such as (= (* p1 p2) 0) --> (or (= p1 0) (= p2 0))."); polynomial::factor_params::get_param_descrs(r); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { try { (*m_imp)(in, result, mc, pc, core); } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { throw tactic_exception(ex.msg()); } } virtual void cleanup() { imp * d = alloc(imp, m_imp->m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_factor_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(factor_tactic, m, p)); } z3-4.4.0/src/tactic/arith/recover_01_tactic.h0000644000175000017500000000145412540347414020463 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: recover_01_tactic.h Abstract: Recover 01 variables Search for clauses of the form p or q or x = 0 ~p or q or x = k1 p or ~q or x = k2 ~p or ~q or x = k1+k2 Then, replaces x with k1*y1 + k2*y2 p with y1=1 q with y2=1 where y1 and y2 are fresh 01 variables The clauses are also removed. Author: Leonardo de Moura (leonardo) 2012-02-17. Revision History: --*/ #ifndef _RECOVER_01_TACTIC_H_ #define _RECOVER_01_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_recover_01_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("recover-01", "recover 0-1 variables hidden as Boolean variables.", "mk_recover_01_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/arith/purify_arith_tactic.cpp0000644000175000017500000007120512540347414021557 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: purify_arith_tactic.h Abstract: Tactic for eliminating arithmetic operators: DIV, IDIV, MOD, TO_INT, and optionally (OP_IRRATIONAL_ALGEBRAIC_NUM). This tactic uses the simplifier for also eliminating: OP_SUB, OP_UMINUS, OP_POWER (optionally), OP_REM, OP_IS_INT. Author: Leonardo de Moura (leonardo) 2011-12-30. Revision History: --*/ #include"tactical.h" #include"rewriter_def.h" #include"arith_decl_plugin.h" #include"algebraic_numbers.h" #include"nnf_tactic.h" #include"simplify_tactic.h" #include"th_rewriter.h" #include"filter_model_converter.h" #include"ast_smt2_pp.h" #include"expr_replacer.h" /* ---- Some of the rules needed in the conversion are implemented in arith_rewriter.cpp. Here is a summary of these rules: (^ t (/ p q)) --> (^ (^ t (/ 1 q)) p) (^ t n) --> t*...*t when integer power expansion is requested (is-int t) --> t = (to-real (to-int t)) (rem t1 t2) --> ite(t2 >= 0, (mod t1 t2), -(mod t1 t2)) ---- The tactic implements a set of transformation rules. These rules create fresh constants or (existential) variables, and add new constraints to the context. The context is the set of asserted formulas or a quantifier. A rule is represented as: From --> To | C It means, any expression that matches From is replaced by To, and the constraints C are added to the context. For clarity reasons, I write the constraints using ad-hoc notation. Rules (^ t 0) --> k | t != 0 implies k = 1, t = 0 implies k = 0^0 where k is fresh 0^0 is a constant used to capture the meaning of (^ 0 0). (^ t (/ 1 n)) --> k | t = k^n when n is odd where k is fresh (^ t (/ 1 n)) --> k | t >= 0 implies t = k^n, t < 0 implies t = neg-root(t, n) when n is even where k is fresh neg-root is a function symbol used to capture the meaning of a negative root (root-obj p(x) i) --> k | p(k) = 0, l < k < u when root object elimination is requested where k is fresh (l, u) is an isolating interval for the i-th root of p. (to-int t) --> k | 0 <= to-real(k) - t < 1 where k is a fresh integer constant/variable (/ t1 t2) --> k | t2 != 0 implies k*t2 = t1, t2 = 0 implies k = div-0(t1) where k is fresh div-0 is a function symbol used to capture the meaning of division by 0. Remark: If it can be shown that t2 != 0, then the div-0(t1) function application vanishes from the formula. (div t1 t2) --> k1 | t2 = 0 \/ t1 = k1 * t2 + k2, t2 = 0 \/ 0 <= k2, t2 = 0 \/ k2 < |t2|, t2 != 0 \/ k1 = idiv-0(t1), t2 != 0 \/ k2 = mod-0(t1) k1 is a fresh name for (div t1 t2) k2 is a fresh name for (mod t1 t2) (mod t1 t2) --> k2 | same constraints as above */ struct purify_arith_proc { arith_util & m_util; bool m_produce_proofs; bool m_elim_root_objs; bool m_elim_inverses; bool m_complete; purify_arith_proc(arith_util & u, bool produce_proofs, bool elim_root_objs, bool elim_inverses, bool complete): m_util(u), m_produce_proofs(produce_proofs), m_elim_root_objs(elim_root_objs), m_elim_inverses(elim_inverses), m_complete(complete) { } arith_util & u() { return m_util; } ast_manager & m() { return u().get_manager(); } struct rw_cfg : public default_rewriter_cfg { purify_arith_proc & m_owner; obj_map m_app2fresh; obj_map m_app2pr; expr_ref_vector m_pinned; expr_ref_vector m_new_cnstrs; proof_ref_vector m_new_cnstr_prs; expr_ref m_subst; proof_ref m_subst_pr; expr_ref_vector m_new_vars; rw_cfg(purify_arith_proc & o): m_owner(o), m_pinned(o.m()), m_new_cnstrs(o.m()), m_new_cnstr_prs(o.m()), m_subst(o.m()), m_subst_pr(o.m()), m_new_vars(o.m()) { } ast_manager & m() { return m_owner.m(); } arith_util & u() { return m_owner.u(); } bool produce_proofs() const { return m_owner.m_produce_proofs; } bool complete() const { return m_owner.m_complete; } bool elim_root_objs() const { return m_owner.m_elim_root_objs; } bool elim_inverses() const { return m_owner.m_elim_inverses; } expr * mk_fresh_var(bool is_int) { expr * r = m().mk_fresh_const(0, is_int ? u().mk_int() : u().mk_real()); m_new_vars.push_back(r); return r; } expr * mk_fresh_real_var() { return mk_fresh_var(false); } expr * mk_fresh_int_var() { return mk_fresh_var(true); } expr * mk_int_zero() { return u().mk_numeral(rational(0), true); } expr * mk_real_zero() { return u().mk_numeral(rational(0), false); } bool already_processed(app * t, expr_ref & result, proof_ref & result_pr) { expr * r; if (m_app2fresh.find(t, r)) { result = r; if (produce_proofs()) result_pr = m_app2pr.find(t); return true; } return false; } void mk_def_proof(expr * k, expr * def, proof_ref & result_pr) { result_pr = 0; if (produce_proofs()) { expr * eq = m().mk_eq(k, def); proof * pr1 = m().mk_def_intro(eq); result_pr = m().mk_apply_def(k, def, pr1); } } void push_cnstr_pr(proof * def_pr) { if (produce_proofs()) m_new_cnstr_prs.push_back(m().mk_th_lemma(u().get_family_id(), m_new_cnstrs.back(), 1, &def_pr)); } void push_cnstr_pr(proof * def_pr1, proof * def_pr2) { if (produce_proofs()) { proof * prs[2] = { def_pr1, def_pr2 }; m_new_cnstr_prs.push_back(m().mk_th_lemma(u().get_family_id(), m_new_cnstrs.back(), 2, prs)); } } void push_cnstr(expr * cnstr) { m_new_cnstrs.push_back(cnstr); } void cache_result(app * t, expr * r, proof * pr) { m_app2fresh.insert(t, r); m_pinned.push_back(t); m_pinned.push_back(r); if (produce_proofs()) { m_app2pr.insert(t, pr); m_pinned.push_back(pr); } } expr * OR(expr * arg1, expr * arg2) { return m().mk_or(arg1, arg2); } expr * AND(expr * arg1, expr * arg2) { return m().mk_and(arg1, arg2); } expr * EQ(expr * lhs, expr * rhs) { return m().mk_eq(lhs, rhs); } expr * NOT(expr * arg) { return m().mk_not(arg); } void process_div(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { app_ref t(m()); t = m().mk_app(f, num, args); if (already_processed(t, result, result_pr)) return; expr * k = mk_fresh_real_var(); result = k; mk_def_proof(k, t, result_pr); cache_result(t, result, result_pr); expr * x = args[0]; expr * y = args[1]; // y = 0 \/ y*k = x push_cnstr(OR(EQ(y, mk_real_zero()), EQ(u().mk_mul(y, k), x))); push_cnstr_pr(result_pr); if (complete()) { // y != 0 \/ k = div-0(x) push_cnstr(OR(NOT(EQ(y, mk_real_zero())), EQ(k, u().mk_div0(x)))); push_cnstr_pr(result_pr); } } void process_idiv(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { app_ref div_app(m()); div_app = m().mk_app(f, num, args); if (already_processed(div_app, result, result_pr)) return; expr * k1 = mk_fresh_int_var(); result = k1; mk_def_proof(k1, div_app, result_pr); cache_result(div_app, result, result_pr); expr * k2 = mk_fresh_int_var(); app_ref mod_app(m()); proof_ref mod_pr(m()); mod_app = u().mk_mod(args[0], args[1]); mk_def_proof(k2, mod_app, mod_pr); cache_result(mod_app, k2, mod_pr); expr * x = args[0]; expr * y = args[1]; // (div x y) --> k1 | y = 0 \/ x = k1 * y + k2, // y = 0 \/ 0 <= k2, // y = 0 \/ k2 < |y|, // y != 0 \/ k1 = idiv-0(x), // y != 0 \/ k2 = mod-0(x) // We can write y = 0 \/ k2 < |y| as: // y > 0 implies k2 < y ---> y <= 0 \/ k2 < y // y < 0 implies k2 < -y ---> y >= 0 \/ k2 < -y // expr * zero = mk_int_zero(); push_cnstr(OR(EQ(y, zero), EQ(x, u().mk_add(u().mk_mul(k1, y), k2)))); push_cnstr_pr(result_pr, mod_pr); push_cnstr(OR(EQ(y, zero), u().mk_le(zero, k2))); push_cnstr_pr(mod_pr); push_cnstr(OR(u().mk_le(y, zero), u().mk_lt(k2, y))); push_cnstr_pr(mod_pr); push_cnstr(OR(u().mk_ge(y, zero), u().mk_lt(k2, u().mk_mul(u().mk_numeral(rational(-1), true), y)))); push_cnstr_pr(mod_pr); if (complete()) { push_cnstr(OR(NOT(EQ(y, zero)), EQ(k1, u().mk_idiv0(x)))); push_cnstr_pr(result_pr); push_cnstr(OR(NOT(EQ(y, zero)), EQ(k2, u().mk_mod0(x)))); push_cnstr_pr(mod_pr); } } void process_mod(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { app_ref t(m()); t = m().mk_app(f, num, args); if (already_processed(t, result, result_pr)) return; process_idiv(f, num, args, result, result_pr); // it will create mod VERIFY(already_processed(t, result, result_pr)); } void process_to_int(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { app_ref t(m()); t = m().mk_app(f, num, args); if (already_processed(t, result, result_pr)) return; expr * k = mk_fresh_int_var(); result = k; mk_def_proof(k, t, result_pr); cache_result(t, result, result_pr); expr * x = args[0]; // to-real(k) - x >= 0 expr * diff = u().mk_add(u().mk_to_real(k), u().mk_mul(u().mk_numeral(rational(-1), false), x)); push_cnstr(u().mk_ge(diff, mk_real_zero())); push_cnstr_pr(result_pr); // not(to-real(k) - x >= 1) push_cnstr(NOT(u().mk_ge(diff, u().mk_numeral(rational(1), false)))); push_cnstr_pr(result_pr); } br_status process_power(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { rational y; if (!u().is_numeral(args[1], y)) return BR_FAILED; if (y.is_int() && !y.is_zero()) return BR_FAILED; app_ref t(m()); t = m().mk_app(f, num, args); if (already_processed(t, result, result_pr)) return BR_DONE; bool is_int = u().is_int(args[0]); expr * k = mk_fresh_var(is_int); result = k; mk_def_proof(k, t, result_pr); cache_result(t, result, result_pr); expr * x = args[0]; expr * zero = u().mk_numeral(rational(0), is_int); expr * one = u().mk_numeral(rational(1), is_int); if (y.is_zero()) { // (^ x 0) --> k | x != 0 implies k = 1, x = 0 implies k = 0^0 push_cnstr(OR(EQ(x, zero), EQ(k, one))); push_cnstr_pr(result_pr); if (complete()) { push_cnstr(OR(NOT(EQ(x, zero)), EQ(k, is_int ? u().mk_0_pw_0_int() : u().mk_0_pw_0_real()))); push_cnstr_pr(result_pr); } } else if (!is_int) { SASSERT(!y.is_int()); SASSERT(numerator(y).is_one()); rational n = denominator(y); if (!n.is_even()) { // (^ x (/ 1 n)) --> k | x = k^n // when n is odd push_cnstr(EQ(x, u().mk_power(k, u().mk_numeral(n, false)))); push_cnstr_pr(result_pr); } else { SASSERT(n.is_even()); // (^ x (/ 1 n)) --> k | x >= 0 implies (x = k^n and k >= 0), x < 0 implies k = neg-root(x, n) // when n is even push_cnstr(OR(NOT(u().mk_ge(x, zero)), AND(EQ(x, u().mk_power(k, u().mk_numeral(n, false))), u().mk_ge(k, zero)))); push_cnstr_pr(result_pr); if (complete()) { push_cnstr(OR(u().mk_ge(x, zero), EQ(k, u().mk_neg_root(x, u().mk_numeral(n, false))))); push_cnstr_pr(result_pr); } } } else { // root not supported for integers. SASSERT(is_int); SASSERT(!y.is_int()); return BR_FAILED; } return BR_DONE; } void process_irrat(app * s, expr_ref & result, proof_ref & result_pr) { if (already_processed(s, result, result_pr)) return; expr * k = mk_fresh_real_var(); result = k; mk_def_proof(k, s, result_pr); cache_result(s, result, result_pr); anum_manager & am = u().am(); anum const & a = u().to_irrational_algebraic_numeral(s); scoped_mpz_vector p(am.qm()); am.get_polynomial(a, p); rational lower, upper; am.get_lower(a, lower); am.get_upper(a, upper); unsigned sz = p.size(); SASSERT(sz > 2); ptr_buffer args; for (unsigned i = 0; i < sz; i++) { if (am.qm().is_zero(p[i])) continue; rational coeff = rational(p[i]); if (i == 0) { args.push_back(u().mk_numeral(coeff, false)); } else { expr * m; if (i == 1) m = k; else m = u().mk_power(k, u().mk_numeral(rational(i), false)); args.push_back(u().mk_mul(u().mk_numeral(coeff, false), m)); } } SASSERT(args.size() >= 2); push_cnstr(EQ(u().mk_add(args.size(), args.c_ptr()), mk_real_zero())); push_cnstr_pr(result_pr); push_cnstr(u().mk_lt(u().mk_numeral(lower, false), k)); push_cnstr_pr(result_pr); push_cnstr(u().mk_lt(k, u().mk_numeral(upper, false))); push_cnstr_pr(result_pr); } br_status process_asin(func_decl * f, expr * x, expr_ref & result, proof_ref & result_pr) { if (!elim_inverses()) return BR_FAILED; app_ref t(m()); t = m().mk_app(f, x); if (already_processed(t, result, result_pr)) return BR_DONE; expr * k = mk_fresh_var(false); result = k; mk_def_proof(k, t, result_pr); cache_result(t, result, result_pr); // Constraints: // -1 <= x <= 1 implies sin(k) = x, -pi/2 <= k <= pi/2 // If complete() // x < -1 implies k = asin_u(x) // x > 1 implies k = asin_u(x) expr * one = u().mk_numeral(rational(1), false); expr * mone = u().mk_numeral(rational(-1), false); expr * pi2 = u().mk_mul(u().mk_numeral(rational(1,2), false), u().mk_pi()); expr * mpi2 = u().mk_mul(u().mk_numeral(rational(-1,2), false), u().mk_pi()); // -1 <= x <= 1 implies sin(k) = x, -pi/2 <= k <= pi/2 push_cnstr(OR(OR(NOT(u().mk_ge(x, mone)), NOT(u().mk_le(x, one))), AND(EQ(x, u().mk_sin(k)), AND(u().mk_ge(k, mpi2), u().mk_le(k, pi2))))); push_cnstr_pr(result_pr); if (complete()) { // x < -1 implies k = asin_u(x) // x > 1 implies k = asin_u(x) push_cnstr(OR(u().mk_ge(x, mone), EQ(k, u().mk_u_asin(x)))); push_cnstr_pr(result_pr); push_cnstr(OR(u().mk_le(x, one), EQ(k, u().mk_u_asin(x)))); push_cnstr_pr(result_pr); } return BR_DONE; } br_status process_acos(func_decl * f, expr * x, expr_ref & result, proof_ref & result_pr) { if (!elim_inverses()) return BR_FAILED; app_ref t(m()); t = m().mk_app(f, x); if (already_processed(t, result, result_pr)) return BR_DONE; expr * k = mk_fresh_var(false); result = k; mk_def_proof(k, t, result_pr); cache_result(t, result, result_pr); // Constraints: // -1 <= x <= 1 implies cos(k) = x, 0 <= k <= pi // If complete() // x < -1 implies k = acos_u(x) // x > 1 implies k = acos_u(x) expr * one = u().mk_numeral(rational(1), false); expr * mone = u().mk_numeral(rational(-1), false); expr * pi = u().mk_pi(); expr * zero = u().mk_numeral(rational(0), false); // -1 <= x <= 1 implies cos(k) = x, 0 <= k <= pi push_cnstr(OR(OR(NOT(u().mk_ge(x, mone)), NOT(u().mk_le(x, one))), AND(EQ(x, u().mk_cos(k)), AND(u().mk_ge(k, zero), u().mk_le(k, pi))))); push_cnstr_pr(result_pr); if (complete()) { // x < -1 implies k = acos_u(x) // x > 1 implies k = acos_u(x) push_cnstr(OR(u().mk_ge(x, mone), EQ(k, u().mk_u_acos(x)))); push_cnstr_pr(result_pr); push_cnstr(OR(u().mk_le(x, one), EQ(k, u().mk_u_acos(x)))); push_cnstr_pr(result_pr); } return BR_DONE; } br_status process_atan(func_decl * f, expr * x, expr_ref & result, proof_ref & result_pr) { if (!elim_inverses()) return BR_FAILED; app_ref t(m()); t = m().mk_app(f, x); if (already_processed(t, result, result_pr)) return BR_DONE; expr * k = mk_fresh_var(false); result = k; mk_def_proof(k, t, result_pr); cache_result(t, result, result_pr); // Constraints: // tan(k) = x, -pi/2 < k < pi/2 expr * pi2 = u().mk_mul(u().mk_numeral(rational(1,2), false), u().mk_pi()); expr * mpi2 = u().mk_mul(u().mk_numeral(rational(-1,2), false), u().mk_pi()); push_cnstr(AND(EQ(x, u().mk_tan(k)), AND(u().mk_gt(k, mpi2), u().mk_lt(k, pi2)))); push_cnstr_pr(result_pr); return BR_DONE; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (f->get_family_id() != u().get_family_id()) return BR_FAILED; switch (f->get_decl_kind()) { case OP_DIV: process_div(f, num, args, result, result_pr); return BR_DONE; case OP_IDIV: process_idiv(f, num, args, result, result_pr); return BR_DONE; case OP_MOD: process_mod(f, num, args, result, result_pr); return BR_DONE; case OP_TO_INT: process_to_int(f, num, args, result, result_pr); return BR_DONE; case OP_POWER: return process_power(f, num, args, result, result_pr); case OP_ASIN: return process_asin(f, args[0], result, result_pr); case OP_ACOS: return process_acos(f, args[0], result, result_pr); case OP_ATAN: return process_atan(f, args[0], result, result_pr); default: return BR_FAILED; } } bool get_subst(expr * s, expr * & t, proof * & t_pr) { if (is_quantifier(s)) { m_owner.process_quantifier(to_quantifier(s), m_subst, m_subst_pr); t = m_subst.get(); t_pr = m_subst_pr.get(); return true; } else if (u().is_irrational_algebraic_numeral(s) && elim_root_objs()) { process_irrat(to_app(s), m_subst, m_subst_pr); t = m_subst.get(); t_pr = m_subst_pr.get(); return true; } return false; } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(purify_arith_proc & o): rewriter_tpl(o.m(), o.m_produce_proofs, m_cfg), m_cfg(o) { } }; void process_quantifier(quantifier * q, expr_ref & result, proof_ref & result_pr) { result_pr = 0; rw r(*this); expr_ref new_body(m()); proof_ref new_body_pr(m()); r(q->get_expr(), new_body, new_body_pr); unsigned num_vars = r.cfg().m_new_vars.size(); TRACE("purify_arith", tout << "num_vars: " << num_vars << "\n"; tout << "body: " << mk_ismt2_pp(q->get_expr(), m()) << "\nnew_body: " << mk_ismt2_pp(new_body, m()) << "\n";); if (num_vars == 0) { SASSERT(r.cfg().m_new_cnstrs.empty()); result = m().update_quantifier(q, new_body); if (m_produce_proofs) result_pr = m().mk_quant_intro(q, to_quantifier(result.get()), result_pr); } else { // Add new constraints expr_ref_vector & cnstrs = r.cfg().m_new_cnstrs; cnstrs.push_back(new_body); new_body = m().mk_and(cnstrs.size(), cnstrs.c_ptr()); // Open space for new variables var_shifter shifter(m()); shifter(new_body, num_vars, new_body); // Rename fresh constants in r.cfg().m_new_vars to variables ptr_buffer sorts; buffer names; expr_substitution subst(m(), false, false); for (unsigned i = 0; i < num_vars; i++) { expr * c = r.cfg().m_new_vars.get(i); sort * s = get_sort(c); sorts.push_back(s); names.push_back(m().mk_fresh_var_name("x")); unsigned idx = num_vars - i - 1; subst.insert(c, m().mk_var(idx, s)); } scoped_ptr replacer = mk_default_expr_replacer(m()); replacer->set_substitution(&subst); (*replacer)(new_body, new_body); new_body = m().mk_exists(num_vars, sorts.c_ptr(), names.c_ptr(), new_body); result = m().update_quantifier(q, new_body); if (m_produce_proofs) { proof_ref_vector & cnstr_prs = r.cfg().m_new_cnstr_prs; cnstr_prs.push_back(result_pr); // TODO: improve proof result_pr = m().mk_quant_intro(q, to_quantifier(result.get()), m().mk_rewrite_star(q->get_expr(), new_body, cnstr_prs.size(), cnstr_prs.c_ptr())); } } } void operator()(goal & g, model_converter_ref & mc, bool produce_models) { rw r(*this); // purify expr_ref new_curr(m()); proof_ref new_pr(m()); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * curr = g.form(i); r(curr, new_curr, new_pr); if (m_produce_proofs) { proof * pr = g.pr(i); new_pr = m().mk_modus_ponens(pr, new_pr); } g.update(i, new_curr, new_pr, g.dep(i)); } // add cnstraints sz = r.cfg().m_new_cnstrs.size(); for (unsigned i = 0; i < sz; i++) { g.assert_expr(r.cfg().m_new_cnstrs.get(i), m_produce_proofs ? r.cfg().m_new_cnstr_prs.get(i) : 0, 0); } // add filter_model_converter to eliminate auxiliary variables from model if (produce_models) { filter_model_converter * fmc = alloc(filter_model_converter, m()); mc = fmc; obj_map & f2v = r.cfg().m_app2fresh; obj_map::iterator it = f2v.begin(); obj_map::iterator end = f2v.end(); for (; it != end; ++it) { app * v = to_app(it->m_value); SASSERT(is_uninterp_const(v)); fmc->insert(v->get_decl()); } } } }; class purify_arith_tactic : public tactic { arith_util m_util; params_ref m_params; public: purify_arith_tactic(ast_manager & m, params_ref const & p): m_util(m), m_params(p) { } virtual tactic * translate(ast_manager & m) { return alloc(purify_arith_tactic, m, m_params); } virtual ~purify_arith_tactic() { } virtual void updt_params(params_ref const & p) { m_params = p; } virtual void collect_param_descrs(param_descrs & r) { r.insert("complete", CPK_BOOL, "(default: true) add constraints to make sure that any interpretation of a underspecified arithmetic operators is a function. The result will include additional uninterpreted functions/constants: /0, div0, mod0, 0^0, neg-root"); r.insert("elim_root_objects", CPK_BOOL, "(default: true) eliminate root objects."); r.insert("elim_inverses", CPK_BOOL, "(default: true) eliminate inverse trigonometric functions (asin, acos, atan)."); th_rewriter::get_param_descrs(r); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { try { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("purify-arith", *g); bool produce_proofs = g->proofs_enabled(); bool produce_models = g->models_enabled(); bool elim_root_objs = m_params.get_bool("elim_root_objects", true); bool elim_inverses = m_params.get_bool("elim_inverses", true); bool complete = m_params.get_bool("complete", true); purify_arith_proc proc(m_util, produce_proofs, elim_root_objs, elim_inverses, complete); proc(*(g.get()), mc, produce_models); g->inc_depth(); result.push_back(g.get()); TRACE("purify_arith", g->display(tout);); SASSERT(g->is_well_sorted()); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } virtual void cleanup() { } virtual void set_cancel(bool f) { } }; tactic * mk_purify_arith_tactic(ast_manager & m, params_ref const & p) { params_ref elim_rem_p = p; elim_rem_p.set_bool("elim_rem", true); params_ref skolemize_p; skolemize_p.set_bool("skolemize", false); return and_then(using_params(mk_snf_tactic(m, skolemize_p), skolemize_p), using_params(mk_simplify_tactic(m, elim_rem_p), elim_rem_p), alloc(purify_arith_tactic, m, p), mk_simplify_tactic(m, p)); } z3-4.4.0/src/tactic/arith/bound_manager.cpp0000644000175000017500000001326412540347414020325 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bound_manager.cpp Abstract: Collect bounds. Author: Leonardo (leonardo) 2011-05-16 Notes: --*/ #include"bound_manager.h" #include"ast_smt2_pp.h" #include"goal.h" bound_manager::bound_manager(ast_manager & m): m_util(m) { } bound_manager::~bound_manager() { reset(); } static decl_kind swap_decl(decl_kind k) { switch (k) { case OP_LE: return OP_GE; case OP_LT: return OP_GT; case OP_GE: return OP_LE; case OP_GT: return OP_LT; default: UNREACHABLE(); return k; } } decl_kind bound_manager::neg(decl_kind k) { switch (k) { case OP_LE: return OP_GT; case OP_LT: return OP_GE; case OP_GE: return OP_LT; case OP_GT: return OP_LE; default: UNREACHABLE(); return k; } } void bound_manager::norm(numeral & n, decl_kind & k) { switch (k) { case OP_LE: return; case OP_GE: return; case OP_LT: // x < n --> x <= n-1 n--; k = OP_LE; return; case OP_GT: // x > n --> x >= n+1 n++; k = OP_GE; return; default: return; } } static bool is_lower(decl_kind k) { return k == OP_GT || k == OP_GE; } static bool is_strict(decl_kind k) { return k == OP_LT || k == OP_GT; } void bound_manager::operator()(expr * f, expr_dependency * d) { TRACE("bound_manager", tout << "processing:\n" << mk_ismt2_pp(f, m()) << "\n";); expr * v; numeral n; if (is_disjunctive_bound(f, d)) return; bool pos = true; while (m().is_not(f, f)) pos = !pos; if (!is_app(f)) return; app * t = to_app(f); if (t->get_family_id() != m_util.get_family_id()) return; decl_kind k = t->get_decl_kind(); if (k != OP_LE && k != OP_GE && k != OP_LT && k != OP_GT) return; expr * lhs = t->get_arg(0); expr * rhs = t->get_arg(1); bool is_int; if (is_uninterp_const(lhs) && m_util.is_numeral(rhs, n, is_int)) { v = lhs; } else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs, n, is_int)) { v = rhs; k = swap_decl(k); } else { return; } if (!pos) k = neg(k); if (is_int) norm(n, k); TRACE("bound_manager", tout << "found bound for:\n" << mk_ismt2_pp(v, m()) << "\n";); bool strict = is_strict(k); if (is_lower(k)) { insert_lower(v, strict, n, d); } else { insert_upper(v, strict, n, d); } } void bound_manager::insert_upper(expr * v, bool strict, numeral const & n, expr_dependency * d) { limit old; if (m_uppers.find(v, old)) { if (n < old.first || (n == old.first && strict && !old.second)) { // improved bound m_uppers.insert(v, limit(n, strict)); if (d) m_upper_deps.insert(v, d); } } else { m_uppers.insert(v, limit(n, strict)); if (d) m_upper_deps.insert(v, d); if (!m_lowers.contains(v)) { m_bounded_vars.push_back(v); m().inc_ref(v); } } } void bound_manager::insert_lower(expr * v, bool strict, numeral const & n, expr_dependency * d) { limit old; if (m_lowers.find(v, old)) { if (n > old.first || (n == old.first && strict && !old.second)) { // improved bound m_lowers.insert(v, limit(n, strict)); if (d) m_lower_deps.insert(v, d); } } else { m_lowers.insert(v, limit(n, strict)); if (d) m_lower_deps.insert(v, d); if (!m_uppers.contains(v)) { m_bounded_vars.push_back(v); m().inc_ref(v); } } } bool bound_manager::is_disjunctive_bound(expr * f, expr_dependency * d) { numeral lo, hi, n; if (!m().is_or(f)) return false; unsigned sz = to_app(f)->get_num_args(); if (sz == 0) return false; expr * x, * y, * v = 0; bool is_int; for (unsigned i = 0; i < sz; ++i) { expr * e = to_app(f)->get_arg(i); if (!m().is_eq(e, x, y)) return false; if (is_uninterp_const(x) && m_util.is_numeral(y, n, is_int) && is_int && (x == v || v == 0)) { if (v == 0) { v = x; lo = hi = n; } if (n < lo) lo = n; if (n > hi) hi = n; } else if (is_uninterp_const(y) && m_util.is_numeral(x, n, is_int) && is_int && (y == v || v == 0)) { if (v == 0) { v = y; lo = hi = n; } if (n < lo) lo = n; if (n > hi) hi = n; } else { return false; } } TRACE("bound_manager", tout << "bounds: " << lo << " " << hi << "\n";); insert_lower(v, false, lo, d); insert_upper(v, false, hi, d); return true; } void bound_manager::operator()(goal const & g) { unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { operator()(g.form(i), g.dep(i)); } } void bound_manager::reset() { m().dec_array_ref(m_bounded_vars.size(), m_bounded_vars.c_ptr()); m_bounded_vars.finalize(); m_lowers.finalize(); m_uppers.finalize(); m_lower_deps.finalize(); m_upper_deps.finalize(); } void bound_manager::display(std::ostream & out) const { numeral n; bool strict; for (iterator it = begin(); it != end(); ++it) { expr * v = *it; if (has_lower(v, n, strict)) out << n << " " << (strict ? "<" : "<="); else out << "-oo <"; out << " " << mk_ismt2_pp(v, m()) << " "; if (has_upper(v, n, strict)) out << (strict ? "<" : "<=") << " " << n; else out << "< oo"; out << "\n"; } } z3-4.4.0/src/tactic/arith/propagate_ineqs_tactic.cpp0000644000175000017500000004231612540347414022234 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: propagate_ineqs_tactic.h Abstract: This tactic performs the following tasks: - Propagate bounds using the bound_propagator. - Eliminate subsumed inequalities. For example: x - y >= 3 can be replaced with true if we know that x >= 3 and y <= 0 - Convert inequalities of the form p <= k and p >= k into p = k, where p is a polynomial and k is a constant. This strategy assumes the input is in arith LHS mode. This can be achieved by using option :arith-lhs true in the simplifier. Author: Leonardo (leonardo) 2012-02-19 Notes: --*/ #include"tactical.h" #include"bound_propagator.h" #include"arith_decl_plugin.h" #include"simplify_tactic.h" #include"ast_smt2_pp.h" class propagate_ineqs_tactic : public tactic { struct imp; imp * m_imp; params_ref m_params; public: propagate_ineqs_tactic(ast_manager & m, params_ref const & p); virtual tactic * translate(ast_manager & m) { return alloc(propagate_ineqs_tactic, m, m_params); } virtual ~propagate_ineqs_tactic(); virtual void updt_params(params_ref const & p); virtual void collect_param_descrs(param_descrs & r) {} virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core); virtual void cleanup(); protected: virtual void set_cancel(bool f); }; tactic * mk_propagate_ineqs_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(propagate_ineqs_tactic, m, p)); } struct propagate_ineqs_tactic::imp { ast_manager & m; unsynch_mpq_manager nm; small_object_allocator m_allocator; bound_propagator bp; arith_util m_util; typedef bound_propagator::var a_var; obj_map m_expr2var; expr_ref_vector m_var2expr; typedef numeral_buffer mpq_buffer; typedef svector var_buffer; mpq_buffer m_num_buffer; var_buffer m_var_buffer; goal_ref m_new_goal; imp(ast_manager & _m, params_ref const & p): m(_m), m_allocator("ineq-simplifier"), bp(nm, m_allocator, p), m_util(m), m_var2expr(m), m_num_buffer(nm) { updt_params_core(p); } void updt_params_core(params_ref const & p) { } void updt_params(params_ref const & p) { updt_params_core(p); bp.updt_params(p); } void display_bounds(std::ostream & out) { unsigned sz = m_var2expr.size(); mpq k; bool strict; unsigned ts; for (unsigned x = 0; x < sz; x++) { if (bp.lower(x, k, strict, ts)) out << nm.to_string(k) << " " << (strict ? "<" : "<="); else out << "-oo <"; out << " " << mk_ismt2_pp(m_var2expr.get(x), m) << " "; if (bp.upper(x, k, strict, ts)) out << (strict ? "<" : "<=") << " " << nm.to_string(k); else out << "< oo"; out << "\n"; } nm.del(k); } a_var mk_var(expr * t) { if (m_util.is_to_real(t)) t = to_app(t)->get_arg(0); a_var x; if (m_expr2var.find(t, x)) return x; x = m_var2expr.size(); bp.mk_var(x, m_util.is_int(t)); m_var2expr.push_back(t); m_expr2var.insert(t, x); return x; } void expr2linear_pol(expr * t, mpq_buffer & as, var_buffer & xs) { mpq c_mpq_val; if (m_util.is_add(t)) { rational c_val; unsigned num = to_app(t)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * mon = to_app(t)->get_arg(i); expr * c, * x; if (m_util.is_mul(mon, c, x) && m_util.is_numeral(c, c_val)) { nm.set(c_mpq_val, c_val.to_mpq()); as.push_back(c_mpq_val); xs.push_back(mk_var(x)); } else { as.push_back(mpq(1)); xs.push_back(mk_var(mon)); } } } else { as.push_back(mpq(1)); xs.push_back(mk_var(t)); } nm.del(c_mpq_val); } a_var mk_linear_pol(expr * t) { a_var x; if (m_expr2var.find(t, x)) return x; x = mk_var(t); if (m_util.is_add(t)) { m_num_buffer.reset(); m_var_buffer.reset(); expr2linear_pol(t, m_num_buffer, m_var_buffer); m_num_buffer.push_back(mpq(-1)); m_var_buffer.push_back(x); bp.mk_eq(m_num_buffer.size(), m_num_buffer.c_ptr(), m_var_buffer.c_ptr()); } return x; } enum kind { EQ, LE, GE }; bool process(expr * t) { bool sign = false; while (m.is_not(t, t)) sign = !sign; bool strict = false; kind k; if (m.is_eq(t)) { if (sign) return false; k = EQ; } else if (m_util.is_le(t)) { if (sign) { k = GE; strict = true; } else { k = LE; } } else if (m_util.is_ge(t)) { if (sign) { k = LE; strict = true; } else { k = GE; } } else { return false; } expr * lhs = to_app(t)->get_arg(0); expr * rhs = to_app(t)->get_arg(1); if (m_util.is_numeral(lhs)) { std::swap(lhs, rhs); if (k == LE) k = GE; else if (k == GE) k = LE; } rational c; if (!m_util.is_numeral(rhs, c)) return false; a_var x = mk_linear_pol(lhs); mpq c_prime; nm.set(c_prime, c.to_mpq()); if (k == EQ) { SASSERT(!strict); bp.assert_lower(x, c_prime, false); bp.assert_upper(x, c_prime, false); } else if (k == LE) { bp.assert_upper(x, c_prime, strict); } else { SASSERT(k == GE); bp.assert_lower(x, c_prime, strict); } nm.del(c_prime); return true; } bool collect_bounds(goal const & g) { bool found = false; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * t = g.form(i); if (process(t)) found = true; else m_new_goal->assert_expr(t); // save non-bounds here } return found; } bool lower_subsumed(expr * p, mpq const & k, bool strict) { if (!m_util.is_add(p)) return false; m_num_buffer.reset(); m_var_buffer.reset(); expr2linear_pol(p, m_num_buffer, m_var_buffer); mpq implied_k; bool implied_strict; bool result = bp.lower(m_var_buffer.size(), m_num_buffer.c_ptr(), m_var_buffer.c_ptr(), implied_k, implied_strict) && (nm.gt(implied_k, k) || (nm.eq(implied_k, k) && (!strict || implied_strict))); nm.del(implied_k); return result; } bool upper_subsumed(expr * p, mpq const & k, bool strict) { if (!m_util.is_add(p)) return false; m_num_buffer.reset(); m_var_buffer.reset(); expr2linear_pol(p, m_num_buffer, m_var_buffer); mpq implied_k; bool implied_strict; bool result = bp.upper(m_var_buffer.size(), m_num_buffer.c_ptr(), m_var_buffer.c_ptr(), implied_k, implied_strict) && (nm.lt(implied_k, k) || (nm.eq(implied_k, k) && (!strict || implied_strict))); nm.del(implied_k); return result; } void restore_bounds() { mpq l, u; bool strict_l, strict_u, has_l, has_u; unsigned ts; unsigned sz = m_var2expr.size(); for (unsigned x = 0; x < sz; x++) { expr * p = m_var2expr.get(x); has_l = bp.lower(x, l, strict_l, ts); has_u = bp.upper(x, u, strict_u, ts); if (!has_l && !has_u) continue; if (has_l && has_u && nm.eq(l, u) && !strict_l && !strict_u) { // l <= p <= l --> p = l m_new_goal->assert_expr(m.mk_eq(p, m_util.mk_numeral(rational(l), m_util.is_int(p)))); continue; } if (has_l && !lower_subsumed(p, l, strict_l)) { if (strict_l) m_new_goal->assert_expr(m.mk_not(m_util.mk_le(p, m_util.mk_numeral(rational(l), m_util.is_int(p))))); else m_new_goal->assert_expr(m_util.mk_ge(p, m_util.mk_numeral(rational(l), m_util.is_int(p)))); } if (has_u && !upper_subsumed(p, u, strict_u)) { if (strict_u) m_new_goal->assert_expr(m.mk_not(m_util.mk_ge(p, m_util.mk_numeral(rational(u), m_util.is_int(p))))); else m_new_goal->assert_expr(m_util.mk_le(p, m_util.mk_numeral(rational(u), m_util.is_int(p)))); } } nm.del(l); nm.del(u); } bool is_x_minus_y_eq_0(expr * t, expr * & x, expr * & y) { expr * lhs, * rhs, * m1, * m2; if (m.is_eq(t, lhs, rhs) && m_util.is_zero(rhs) && m_util.is_add(lhs, m1, m2)) { if (m_util.is_times_minus_one(m2, y) && is_uninterp_const(m1)) { x = m1; return true; } if (m_util.is_times_minus_one(m1, y) && is_uninterp_const(m2)) { x = m2; return true; } } return false; } bool is_unbounded(expr * t) { a_var x; if (m_expr2var.find(t, x)) return !bp.has_lower(x) && !bp.has_upper(x); return true; } bool lower(expr * t, mpq & k, bool & strict) { unsigned ts; a_var x; if (m_expr2var.find(t, x)) return bp.lower(x, k, strict, ts); return false; } bool upper(expr * t, mpq & k, bool & strict) { unsigned ts; a_var x; if (m_expr2var.find(t, x)) return bp.upper(x, k, strict, ts); return false; } void find_ite_bounds(expr * root) { TRACE("find_ite_bounds_bug", display_bounds(tout);); expr * n = root; expr * target = 0; expr * c, * t, * e; expr * x, * y; bool has_l, has_u; mpq l_min, u_max; bool l_strict, u_strict; mpq curr; bool curr_strict; while (true) { TRACE("find_ite_bounds_bug", tout << mk_ismt2_pp(n, m) << "\n";); if (m.is_ite(n, c, t, e)) { if (is_x_minus_y_eq_0(t, x, y)) n = e; else if (is_x_minus_y_eq_0(e, x, y)) n = t; else break; } else if (is_x_minus_y_eq_0(n, x, y)) { n = 0; } else { break; } TRACE("find_ite_bounds_bug", tout << "x: " << mk_ismt2_pp(x, m) << ", y: " << mk_ismt2_pp(y, m) << "\n"; if (target) { tout << "target: " << mk_ismt2_pp(target, m) << "\n"; tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " has_u: " << has_u << " " << nm.to_string(u_max) << "\n"; }); if (is_unbounded(y)) std::swap(x, y); if (!is_unbounded(x)) { TRACE("find_ite_bounds_bug", tout << "x is already bounded\n";); break; } if (target == 0) { target = x; if (lower(y, curr, curr_strict)) { has_l = true; nm.set(l_min, curr); l_strict = curr_strict; } else { has_l = false; TRACE("find_ite_bounds_bug", tout << "y does not have lower\n";); } if (upper(y, curr, curr_strict)) { has_u = true; nm.set(u_max, curr); u_strict = curr_strict; } else { has_u = false; TRACE("find_ite_bounds_bug", tout << "y does not have upper\n";); } } else if (target == x) { if (has_l) { if (lower(y, curr, curr_strict)) { if (nm.lt(curr, l_min) || (!curr_strict && l_strict && nm.eq(curr, l_min))) { nm.set(l_min, curr); l_strict = curr_strict; } } else { has_l = false; TRACE("find_ite_bounds_bug", tout << "y does not have lower\n";); } } if (has_u) { if (upper(y, curr, curr_strict)) { if (nm.gt(curr, u_max) || (curr_strict && !u_strict && nm.eq(curr, u_max))) { nm.set(u_max, curr); u_strict = curr_strict; } } else { has_u = false; TRACE("find_ite_bounds_bug", tout << "y does not have upper\n";); } } } else { break; } if (!has_l && !has_u) break; if (n == 0) { TRACE("find_ite_bounds", tout << "found bounds for: " << mk_ismt2_pp(target, m) << "\n"; tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " l_strict: " << l_strict << "\n"; tout << "has_u: " << has_u << " " << nm.to_string(u_max) << " u_strict: " << u_strict << "\n"; tout << "root:\n" << mk_ismt2_pp(root, m) << "\n";); a_var x = mk_var(target); if (has_l) bp.assert_lower(x, l_min, l_strict); if (has_u) bp.assert_upper(x, u_max, u_strict); break; } } nm.del(l_min); nm.del(u_max); nm.del(curr); } void find_ite_bounds() { unsigned sz = m_new_goal->size(); for (unsigned i = 0; i < sz; i++) { expr * f = m_new_goal->form(i); if (m.is_ite(f)) find_ite_bounds(to_app(f)); } bp.propagate(); TRACE("find_ite_bounds", display_bounds(tout);); } void operator()(goal * g, goal_ref & r) { tactic_report report("propagate-ineqs", *g); m_new_goal = alloc(goal, *g, true); m_new_goal->inc_depth(); r = m_new_goal.get(); if (!collect_bounds(*g)) { m_new_goal = 0; r = g; return; // nothing to be done } TRACE("propagate_ineqs_tactic", g->display(tout); display_bounds(tout); tout << "bound propagator:\n"; bp.display(tout);); bp.propagate(); report_tactic_progress(":bound-propagations", bp.get_num_propagations()); report_tactic_progress(":bound-false-alarms", bp.get_num_false_alarms()); if (bp.inconsistent()) { r->reset(); r->assert_expr(m.mk_false()); return; } // find_ite_bounds(); // did not help restore_bounds(); TRACE("propagate_ineqs_tactic", tout << "after propagation:\n"; display_bounds(tout); bp.display(tout);); TRACE("propagate_ineqs_tactic", r->display(tout);); } void set_cancel(bool f) { // TODO } }; propagate_ineqs_tactic::propagate_ineqs_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } propagate_ineqs_tactic::~propagate_ineqs_tactic() { dealloc(m_imp); } void propagate_ineqs_tactic::updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } void propagate_ineqs_tactic::operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("propagate-ineqs", g); fail_if_unsat_core_generation("propagate-ineqs", g); mc = 0; pc = 0; core = 0; result.reset(); goal_ref r; (*m_imp)(g.get(), r); result.push_back(r.get()); SASSERT(r->is_well_sorted()); } void propagate_ineqs_tactic::set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } void propagate_ineqs_tactic::cleanup() { imp * d = alloc(imp, m_imp->m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } z3-4.4.0/src/tactic/arith/fm_tactic.h0000644000175000017500000000125112540347414017113 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fm_tactic.h Abstract: Use Fourier-Motzkin to eliminate variables. This strategy can handle conditional bounds (i.e., clauses with at most one constraint). The strategy mk_occf can be used to put the formula in OCC form. Author: Leonardo de Moura (leonardo) 2012-02-04. Revision History: --*/ #ifndef _FM_TACTIC_H_ #define _FM_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_fm_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("fm", "eliminate variables using fourier-motzkin elimination.", "mk_fm_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/arith/bound_propagator.cpp0000644000175000017500000007423212540347414021073 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bound_propagator.cpp Abstract: Bound propagators for arithmetic. Support class for implementing strategies and search procedures Author: Leonardo de Moura (leonardo) 2011-06-18. Revision History: --*/ #include"bound_propagator.h" #include // ------------------------------- // Bound Relaxation configuration // // The idea is to minimize errors in floating point computations // // If RELAX_BOUNDS is undefined, then bound relaxation is disabled. // Otherwise, lower bounds l are relaxed using the formula // PRECISION * floor(l * INV_PRECISION + TOLERANCE) // and upper bounds u as: // PRECISION * ceil(u * INV_PRECISION - TOLERANCE) // In the LP literature, the suggested values are // l := 10^-5 * floor(l*10^5 + 10^-6) // u := 10^-5 * ceil(u*10^5 - 10^-6) // I'm using the following values because of strict bounds // l := 10^-6 * floor(l*10^6 + 10^-7) // u := 10^-6 * ceil(u*10^6 - 10^-7) #define RELAX_BOUNDS #define TOLERANCE 0.0000001 #define PRECISION 0.000001 #define INV_PRECISION 1000000.0 // ------------------------------- bound_propagator::bound::bound(numeral_manager & m, mpq const & k, double approx_k, bool lower, bool strict, unsigned lvl, unsigned ts, bkind bk, unsigned c_idx, assumption a, bound * prev): m_approx_k(approx_k), m_lower(lower), m_strict(strict), m_kind(bk), m_level(lvl), m_timestamp(ts), m_prev(prev) { m.set(m_k, k); if (bk == DERIVED) m_constraint_idx = c_idx; else m_assumption = a; } bound_propagator::bound_propagator(numeral_manager & _m, allocator & a, params_ref const & p): m(_m), m_allocator(a), m_eq_manager(m, a) { m_timestamp = 0; m_qhead = 0; m_conflict = null_var; updt_params(p); reset_statistics(); } bound_propagator::~bound_propagator() { m.del(m_tmp); reset(); } void bound_propagator::del_constraints_core() { constraint_vector::iterator it = m_constraints.begin(); constraint_vector::iterator end = m_constraints.end(); for (; it != end; ++it) { del_constraint(*it); } m_constraints.reset(); } void bound_propagator::del_constraints() { SASSERT(scope_lvl() == 0); if (m_constraints.empty()) return; del_constraints_core(); m_constraints.finalize(); vector::iterator it = m_watches.begin(); vector::iterator end = m_watches.end(); for (; it != end; ++it) it->finalize(); } void bound_propagator::del_constraint(constraint & c) { switch (c.m_kind) { case LINEAR: m_eq_manager.del(c.m_eq); break; default: UNREACHABLE(); break; } } void bound_propagator::updt_params(params_ref const & p) { m_max_refinements = p.get_uint("bound_max_refinements", 16); m_threshold = p.get_double("bound_threshold", 0.05); m_small_interval = p.get_double("bound_small_interval", 128); m_strict2double = p.get_double("strict2double", 0.00001); } void bound_propagator::get_param_descrs(param_descrs & r) { r.insert("bound_max_refinements", CPK_UINT, "(default: 16) maximum number of bound refinements (per round) for unbounded variables."); r.insert("bound_threshold", CPK_DOUBLE, "(default: 0.05) bound propagation improvement threshold ratio."); } void bound_propagator::collect_statistics(statistics & st) const { st.update("bound conflicts", m_conflicts); st.update("bound propagations", m_propagations); st.update("bound false alarms", m_false_alarms); } void bound_propagator::reset_statistics() { m_conflicts = 0; m_propagations = 0; m_false_alarms = 0; } void bound_propagator::mk_var(var x, bool is_int) { m_is_int.reserve(x+1, false); m_dead.reserve(x+1, true); m_lowers.reserve(x+1, 0); m_uppers.reserve(x+1, 0); m_lower_refinements.reserve(x+1, 0); m_upper_refinements.reserve(x+1, 0); m_watches.reserve(x+1); SASSERT(m_dead[x]); m_is_int[x] = is_int; m_dead[x] = false; m_lowers[x] = 0; m_uppers[x] = 0; m_lower_refinements[x] = 0; m_upper_refinements[x] = 0; m_watches[x].reset(); } void bound_propagator::del_var(var x) { SASSERT(!m_dead[x]); m_dead[x] = true; // mark constraints containing x as dead. wlist & wl = m_watches[x]; wlist::iterator it = wl.begin(); wlist::iterator end = wl.end(); for (; it != end; ++it) { m_constraints[*it].m_dead = true; } } void bound_propagator::mk_eq(unsigned sz, mpq * as, var * xs) { linear_equation * eq = m_eq_manager.mk(sz, as, xs); init_eq(eq); } void bound_propagator::mk_eq(unsigned sz, mpz * as, var * xs) { linear_equation * eq = m_eq_manager.mk(sz, as, xs); init_eq(eq); } void bound_propagator::init_eq(linear_equation * eq) { if (eq == 0) return; unsigned c_idx = m_constraints.size(); m_constraints.push_back(constraint()); constraint & new_c = m_constraints.back(); new_c.m_kind = LINEAR; new_c.m_dead = false; new_c.m_timestamp = 0; new_c.m_act = 0; new_c.m_counter = 0; new_c.m_eq = eq; unsigned sz = eq->size(); for (unsigned i = 0; i < sz; i++) { m_watches[eq->x(i)].push_back(c_idx); } if (propagate(c_idx) && scope_lvl() > 0) m_reinit_stack.push_back(c_idx); } void bound_propagator::push() { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_trail_limit = m_trail.size(); s.m_qhead_old = m_qhead; s.m_reinit_stack_limit = m_reinit_stack.size(); s.m_timestamp_old = m_timestamp; s.m_in_conflict = inconsistent(); } void bound_propagator::undo_trail(unsigned old_sz) { SASSERT(old_sz <= m_trail.size()); unsigned i = m_trail.size(); while (i > old_sz) { --i; trail_info & info = m_trail.back(); var x = info.x(); bool is_lower = info.is_lower(); m_trail.pop_back(); bound * b; if (is_lower) { b = m_lowers[x]; m_lowers[x] = b->m_prev; } else { b = m_uppers[x]; m_uppers[x] = b->m_prev; } m.del(b->m_k); b->~bound(); m_allocator.deallocate(sizeof(bound), b); } SASSERT(m_trail.size() == old_sz); } void bound_propagator::pop(unsigned num_scopes) { unsigned lvl = scope_lvl(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; scope & s = m_scopes[new_lvl]; undo_trail(s.m_trail_limit); m_timestamp = s.m_timestamp_old; m_qhead = s.m_qhead_old; if (!s.m_in_conflict) m_conflict = null_var; unsigned reinit_stack_sz = s.m_reinit_stack_limit; m_scopes.shrink(new_lvl); // reinitialize unsigned i = reinit_stack_sz; unsigned j = reinit_stack_sz; unsigned sz = m_reinit_stack.size(); for (; i < sz; i++) { unsigned c_idx = m_reinit_stack[i]; bool p = propagate(c_idx); if (new_lvl > 0 && p) { m_reinit_stack[j] = c_idx; j++; } } m_reinit_stack.shrink(j); } bool bound_propagator::assert_lower_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a) { if (is_int(x)) { if (m.is_int(k)) { if (strict) m.inc(k); } else { m.ceil(k, k); } SASSERT(m.is_int(k)); strict = false; } TRACE("bound_propagator_detail", tout << "new lower x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";); bound * old_lower = m_lowers[x]; if (old_lower) { bool improves = m.gt(k, old_lower->m_k) || (!old_lower->m_strict && strict && m.eq(k, old_lower->m_k)); if (!improves) { if (bk == DERIVED) { TRACE("bound_propagator_detail", tout << "false alarm\n";); m_false_alarms++; } return false; } } if (bk == DERIVED) { TRACE("bound_propagator_derived", tout << "new lower x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";); m_propagations++; } if (scope_lvl() == 0 && bk == DERIVED) bk = AXIOM; // don't need justification at level 0 double approx_k = m.get_double(k); TRACE("new_bound", tout << "x" << x << " lower: " << m.to_string(k) << " approx: " << approx_k << "\n";); #ifdef RELAX_BOUNDS approx_k = PRECISION*floor(approx_k*INV_PRECISION + TOLERANCE); TRACE("new_bound", tout << "x" << x << " lower: " << m.to_string(k) << " relaxed approx: " << approx_k << "\n";); #endif void * mem = m_allocator.allocate(sizeof(bound)); bound * new_lower = new (mem) bound(m, k, approx_k, true, strict, scope_lvl(), m_timestamp, bk, c_idx, a, old_lower); m_timestamp++; m_lowers[x] = new_lower; m_trail.push_back(trail_info(x, true)); m_lower_refinements[x]++; check_feasibility(x); return true; } bool bound_propagator::assert_upper_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a) { if (is_int(x)) { if (m.is_int(k)) { if (strict) m.dec(k); } else { m.floor(k, k); } SASSERT(m.is_int(k)); strict = false; } TRACE("bound_propagator_detail", tout << "new upper x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";); bound * old_upper = m_uppers[x]; if (old_upper) { bool improves = m.lt(k, old_upper->m_k) || (!old_upper->m_strict && strict && m.eq(k, old_upper->m_k)); if (!improves) { if (bk == DERIVED) { TRACE("bound_propagator_detail", tout << "false alarm\n";); m_false_alarms++; } return false; } } if (bk == DERIVED) { m_propagations++; TRACE("bound_propagator_derived", tout << "new upper x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";); } if (scope_lvl() == 0 && bk == DERIVED) bk = AXIOM; // don't need justification at level 0 double approx_k = m.get_double(k); TRACE("new_bound", tout << "x" << x << " upper: " << m.to_string(k) << " approx: " << approx_k << "\n";); #ifdef RELAX_BOUNDS approx_k = PRECISION*ceil(approx_k*INV_PRECISION - TOLERANCE); TRACE("new_bound", tout << "x" << x << " upper: " << m.to_string(k) << " relaxed approx: " << approx_k << "\n";); #endif void * mem = m_allocator.allocate(sizeof(bound)); bound * new_upper = new (mem) bound(m, k, approx_k, false, strict, scope_lvl(), m_timestamp, bk, c_idx, a, m_uppers[x]); m_timestamp++; m_uppers[x] = new_upper; m_trail.push_back(trail_info(x, false)); m_upper_refinements[x]++; check_feasibility(x); return true; } bool bound_propagator::get_interval_size(var x, double & r) const { bound * l = m_lowers[x]; bound * u = m_uppers[x]; if (l && u) { r = u->m_approx_k - l->m_approx_k; return true; } return false; } template bool bound_propagator::relevant_bound(var x, double new_k) const { TRACE("bound_propagator_detail", tout << "relevant_bound x" << x << " " << new_k << " LOWER: " << LOWER << "\n"; if (LOWER && has_lower(x)) tout << "old: " << m.to_string(m_lowers[x]->m_k) << " | " << m_lowers[x]->m_approx_k << "\n"; if (!LOWER && has_upper(x)) tout << "old: " << m.to_string(m_uppers[x]->m_k) << " | " << m_uppers[x]->m_approx_k << "\n";); bound * b = LOWER ? m_lowers[x] : m_uppers[x]; if (b == 0) return true; // variable did not have a bound double interval_size; bool bounded = get_interval_size(x, interval_size); if (!is_int(x)) { // check if the improvement is significant double improvement; double abs_k = b->m_approx_k; if (abs_k < 0.0) abs_k -= abs_k; if (bounded) improvement = m_threshold * std::max(std::min(interval_size, abs_k), 1.0); else improvement = m_threshold * std::max(abs_k, 1.0); if (LOWER) { if (new_k <= b->m_approx_k + improvement) { TRACE("bound_propagator", tout << "LOWER new: " << new_k << " old: " << b->m_approx_k << " improvement is too small\n";); return false; // improvement is too small } } else { if (new_k >= b->m_approx_k - improvement) { TRACE("bound_propagator", tout << "UPPER new: " << new_k << " old: " << b->m_approx_k << " improvement is too small\n";); return false; // improvement is too small } } } else { if (LOWER) { if (new_k < b->m_approx_k + 1.0) return false; // no improvement } else { if (new_k > b->m_approx_k - 1.0) return false; // no improvement } } if (bounded && interval_size <= m_small_interval) return true; if (LOWER) return m_lower_refinements[x] < m_max_refinements; else return m_upper_refinements[x] < m_max_refinements; } bool bound_propagator::relevant_lower(var x, double approx_k) const { return relevant_bound(x, approx_k); } bool bound_propagator::relevant_upper(var x, double approx_k) const { return relevant_bound(x, approx_k); } void bound_propagator::check_feasibility(var x) { if (inconsistent()) return; bound * l = m_lowers[x]; bound * u = m_uppers[x]; if (l && u) { if (m.lt(l->m_k, u->m_k)) return; if (!l->m_strict && !u->m_strict && m.eq(l->m_k, u->m_k)) return; m_conflict = x; m_conflicts++; SASSERT(inconsistent()); TRACE("bound_propagator", tout << "inconsistency detected: x" << x << "\n"; display(tout);); } } void bound_propagator::propagate() { m_to_reset_ts.reset(); while (m_qhead < m_trail.size()) { if (inconsistent()) break; trail_info & info = m_trail[m_qhead]; var x = info.x(); bool is_lower = info.is_lower(); bound * b = is_lower ? m_lowers[x] : m_uppers[x]; SASSERT(b); unsigned ts = b->m_timestamp; TRACE("bound_propagator_detail", tout << "propagating x" << x << "\n";); m_qhead++; wlist const & wl = m_watches[x]; wlist::const_iterator it = wl.begin(); wlist::const_iterator end = wl.end(); for (; it != end; ++it) { unsigned c_idx = *it; constraint & c = m_constraints[c_idx]; // We don't need to visit c if it was already propagated using b. // Whenever we visit c we store in c.m_timestamp the current timestamp // So, we know that c was already propagated any bound using bounds with timestamp lower than c.m_timestamp. if (ts >= c.m_timestamp) { if (c.m_timestamp == 0) m_to_reset_ts.push_back(c_idx); c.m_timestamp = m_timestamp; propagate(c_idx); } } } unsigned_vector::iterator it = m_to_reset_ts.begin(); unsigned_vector::iterator end = m_to_reset_ts.end(); for (; it != end; ++it) m_constraints[*it].m_timestamp = 0; } bool bound_propagator::propagate(unsigned c_idx) { constraint const & c = m_constraints[c_idx]; if (c.m_dead) return false; if (c.m_kind == LINEAR) return propagate_eq(c_idx); return false; } bool bound_propagator::propagate_eq(unsigned c_idx) { constraint const & c = m_constraints[c_idx]; linear_equation * eq = c.m_eq; #if 0 { static unsigned counter = 0; static unsigned visited = 0; counter++; visited += eq->size(); if (counter % 1000 == 0) verbose_stream() << "[bound-propagator] :propagate-eq " << counter << " :visited-vars " << visited << std::endl; } #endif TRACE("bound_propagator_detail", tout << "propagating using eq: "; m_eq_manager.display(tout, *eq); tout << "\n";); // ll = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_i > 0} -a_i * upper(x_i)) // uu = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_i < 0} -a_i * upper(x_i)) unsigned ll_i = UINT_MAX; // position of the variable that couldn't contribute to ll unsigned uu_i = UINT_MAX; // position of the variable that coundn't contribute to uu bool ll_failed = false; bool uu_failed = false; double ll = 0.0; double uu = 0.0; unsigned sz = eq->size(); for (unsigned i = 0; i < sz; i++) { var x_i = eq->x(i); double a_i = eq->approx_a(i); bound * l_i = m_lowers[x_i]; bound * u_i = m_uppers[x_i]; if (a_i < 0.0) { if (!ll_failed) { if (l_i == 0) { if (ll_i == UINT_MAX) ll_i = i; else ll_failed = true; } else { ll -= a_i * l_i->m_approx_k; } } if (!uu_failed) { if (u_i == 0) { if (uu_i == UINT_MAX) uu_i = i; else uu_failed = true; } else { uu -= a_i * u_i->m_approx_k; } } } else { if (!ll_failed) { if (u_i == 0) { if (ll_i == UINT_MAX) ll_i = i; else ll_failed = true; } else { ll -= a_i * u_i->m_approx_k; } } if (!uu_failed) { if (l_i == 0) { if (uu_i == UINT_MAX) uu_i = i; else uu_failed = true; } else { uu -= a_i * l_i->m_approx_k; } } } if (ll_failed && uu_failed) return false; // nothing to propagate } bool propagated = false; SASSERT(!ll_failed || !uu_failed); if (ll_i == UINT_MAX || uu_i == UINT_MAX) { for (unsigned i = 0; i < sz; i++) { var x_i = eq->x(i); double a_i = eq->approx_a(i); bound * l_i = m_lowers[x_i]; bound * u_i = m_uppers[x_i]; // ll = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_i > 0} -a_i * upper(x_i)) // uu = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_i < 0} -a_i * upper(x_i)) if (ll_i == UINT_MAX) { // can propagate a lower bound for a_i*x_i if (a_i > 0.0) { // can propagate a lower bound for x_i double new_k = (ll + a_i * u_i->m_approx_k)/a_i; if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, i)) propagated = true; } else { // a_i < 0.0 // can propagate a upper bound for x_i double new_k = (ll + a_i * l_i->m_approx_k)/a_i; if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, i)) propagated = true; } } if (uu_i == UINT_MAX) { // can propagate an upper bound for a_i*x_i if (a_i > 0.0) { // can propagate a upper bound for x_i double new_k = (uu + a_i * l_i->m_approx_k)/a_i; if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, i)) propagated = true; } else { // a_i < 0.0 // can propagate a lower bound for x_i double new_k = (uu + a_i * u_i->m_approx_k)/a_i; if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, i)) propagated = true; } } } } if (!ll_failed && ll_i != UINT_MAX) { // can propagate a lower bound for the monomial at position ll_i var x_i = eq->x(ll_i); double a_i = eq->approx_a(ll_i); double new_k = ll/a_i; if (a_i > 0.0) { if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, ll_i)) propagated = true; } else { if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, ll_i)) propagated = true; } } if (!uu_failed && uu_i != UINT_MAX) { // can propagate a upper bound for the monomial at position uu_i var x_i = eq->x(uu_i); double a_i = eq->approx_a(uu_i); double new_k = uu/a_i; if (a_i > 0.0) { if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, uu_i)) propagated = true; } else { if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, uu_i)) propagated = true; } } return propagated; } /** \brief Try to propagate a lower bound for the variable stored at position i, using mpq's (rationals). When this method is invoked, we know that all other variables have the "right" bounds, and using doubles we improve the current known bound. */ bool bound_propagator::propagate_lower(unsigned c_idx, unsigned i) { constraint const & c = m_constraints[c_idx]; linear_equation * eq = c.m_eq; var x_i = eq->x(i); mpz const & a_i = eq->a(i); unsigned sz = eq->size(); mpq k; bool strict = false; bool neg_a_i = m.is_neg(a_i); for (unsigned j = 0; j < sz; j++) { if (i == j) continue; var x_j = eq->x(j); mpz const & a_j = eq->a(j); bound * b_j = (m.is_neg(a_j) == neg_a_i) ? m_uppers[x_j] : m_lowers[x_j]; TRACE("bound_propagator_step_detail", tout << "k: " << m.to_string(k) << " b_j->m_k: " << m.to_string(b_j->m_k) << " a_j: " << m.to_string(a_j) << "\n";); SASSERT(b_j); if (b_j->m_strict) strict = true; m.addmul(k, a_j, b_j->m_k, k); } TRACE("bound_propagator_step_detail", tout << "k: " << m.to_string(k) << "\n";); m.neg(k); m.div(k, a_i, k); TRACE("bound_propagator_step", tout << "propagating lower x" << x_i << " " << m.to_string(k) << " strict: " << strict << " using\n"; m_eq_manager.display(tout, *eq); tout << "\n"; display_bounds_of(tout, *eq);); bool r = assert_lower_core(x_i, k, strict, DERIVED, c_idx, null_assumption); m.del(k); return r; } /** \brief Try to propagate a upper bound for the variable stored at position i, using mpq's (rationals). When this method is invoked, we know that all other variables have the "right" bounds, and using doubles we improve the current known bound. */ bool bound_propagator::propagate_upper(unsigned c_idx, unsigned i) { constraint const & c = m_constraints[c_idx]; linear_equation * eq = c.m_eq; var x_i = eq->x(i); mpz const & a_i = eq->a(i); unsigned sz = eq->size(); mpq k; bool strict = false; bool neg_a_i = m.is_neg(a_i); for (unsigned j = 0; j < sz; j++) { if (i == j) continue; var x_j = eq->x(j); mpz const & a_j = eq->a(j); bound * b_j = (m.is_neg(a_j) == neg_a_i) ? m_lowers[x_j] : m_uppers[x_j]; SASSERT(b_j); if (b_j->m_strict) strict = true; m.addmul(k, a_j, b_j->m_k, k); } m.neg(k); m.div(k, a_i, k); TRACE("bound_propagator_step", tout << "propagating upper x" << x_i << " " << m.to_string(k) << " strict: " << strict << " using\n"; m_eq_manager.display(tout, *eq); tout << "\n"; display_bounds_of(tout, *eq);); bool r = assert_upper_core(x_i, k, strict, DERIVED, c_idx, null_assumption); m.del(k); return r; } void bound_propagator::reset() { undo_trail(0); del_constraints_core(); m_constraints.finalize(); m_is_int.finalize(); m_dead.finalize(); m_lowers.finalize(); m_uppers.finalize(); m_watches.finalize(); m_trail.finalize(); m_qhead = 0; m_reinit_stack.finalize(); m_lower_refinements.finalize(); m_upper_refinements.finalize(); m_timestamp = 0; m_conflict = null_var; m_scopes.finalize(); } bool bound_propagator::lower(var x, mpq & k, bool & strict, unsigned & ts) const { bound * b = m_lowers[x]; if (!b) return false; m.set(k, b->m_k); strict = b->m_strict; ts = b->m_timestamp; return true; } bool bound_propagator::upper(var x, mpq & k, bool & strict, unsigned & ts) const { bound * b = m_uppers[x]; if (!b) return false; m.set(k, b->m_k); strict = b->m_strict; ts = b->m_timestamp; return true; } bound_propagator::bound * bound_propagator::bound::at(unsigned timestamp) { bound * r = this; while (r != 0 && r->m_timestamp >= timestamp) r = r->m_prev; return r; } /** \brief Return true if the coefficient of x in eq is positive */ bool bound_propagator::is_a_i_pos(linear_equation const & eq, var x) const { unsigned i = eq.pos(x); if (i == UINT_MAX) return false; return m.is_pos(eq.a(i)); } void bound_propagator::explain(var x, bound * b, unsigned ts, assumption_vector & ex) const { if (!b) return; b = b->at(ts); if (!b) return; if (b->m_kind == AXIOM || b->m_kind == DECISION) return; if (b->m_kind == ASSUMPTION) { ex.push_back(b->m_assumption); return; } svector & todo = const_cast(this)->m_todo; todo.reset(); unsigned qhead = 0; todo.push_back(var_bound(x, b)); b->m_mark = true; while (qhead < todo.size()) { var_bound & vb = todo[qhead]; qhead ++; var x = vb.first; bound * b = vb.second; SASSERT(b->kind() == ASSUMPTION || b->kind() == DERIVED); if (b->kind() == ASSUMPTION) { ex.push_back(b->m_assumption); continue; } SASSERT(b->kind() == DERIVED); constraint const & c = m_constraints[b->m_constraint_idx]; switch (c.m_kind) { case LINEAR: { linear_equation * eq = c.m_eq; bool is_lower = b->is_lower(); if (!is_a_i_pos(*eq, x)) is_lower = !is_lower; unsigned sz = eq->size(); for (unsigned i = 0; i < sz; i++) { var x_i = eq->x(i); if (x_i == x) continue; bound * b = (m.is_neg(eq->a(i)) == is_lower) ? m_lowers[x_i] : m_uppers[x_i]; SASSERT(b); if (b->kind() == DERIVED || b->kind() == ASSUMPTION) { if (!b->m_mark) { b->m_mark = true; todo.push_back(var_bound(x_i, b)); } } } break; } default: break; } } unsigned sz = todo.size(); for (unsigned i = 0; i < sz; i++) todo[i].second->m_mark = false; todo.reset(); } /** \brief Compute lower (upper) bound for the linear polynomial as[0]*xs[0] + ... + as[sz-1]*xs[sz-1] Return false if the lower (upper) bound is -oo (oo) */ template bool bound_propagator::get_bound(unsigned sz, Numeral const * as, var const * xs, mpq & r, bool & st) const { st = false; m.reset(r); for (unsigned i = 0; i < sz; i++) { var x_i = xs[i]; Numeral const & a_i = as[i]; if (m.is_zero(a_i)) continue; bound * b = (m.is_neg(a_i) == LOWER) ? m_uppers[x_i] : m_lowers[x_i]; if (!b) { m.reset(r); return false; } if (b->m_strict) st = true; m.addmul(r, a_i, b->m_k, r); } return true; } bool bound_propagator::lower(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const { return get_bound(sz, as, xs, r, st); } bool bound_propagator::upper(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const { return get_bound(sz, as, xs, r, st); } void bound_propagator::display_bounds_of(std::ostream & out, linear_equation const & eq) const { for (unsigned i = 0; i < eq.size(); i++) { display_var_bounds(out, eq.x(i)); out << "\n"; } } void bound_propagator::display_var_bounds(std::ostream & out, var x, bool approx, bool precise) const { if (m_lowers[x]) { if (precise) out << m.to_string(m_lowers[x]->m_k); if (precise && approx) out << " | "; if (approx) out << m_lowers[x]->m_approx_k; out << " " << (m_lowers[x]->m_strict ? "<" : "<="); } else { out << "-oo <"; } out << " x" << x << " "; if (m_uppers[x]) { out << (m_uppers[x]->m_strict ? "<" : "<=") << " "; if (precise) out << m.to_string(m_uppers[x]->m_k); if (precise && approx) out << " | "; if (approx) out << m_uppers[x]->m_approx_k; } else { out << "< oo"; } } void bound_propagator::display_bounds(std::ostream & out, bool approx, bool precise) const { unsigned num_vars = m_dead.size(); for (unsigned x = 0; x < num_vars; x++) { if (!is_dead(x)) { display_var_bounds(out, x, approx, precise); out << "\n"; } } } void bound_propagator::display_constraints(std::ostream & out) const { constraint_vector::const_iterator it = m_constraints.begin(); constraint_vector::const_iterator end = m_constraints.end(); for (; it != end; ++it) { constraint const & c = *it; if (c.m_kind == LINEAR) { m_eq_manager.display(out, *(c.m_eq)); out << "\n"; } } } void bound_propagator::display(std::ostream & out) const { display_bounds(out); display_constraints(out); } z3-4.4.0/src/tactic/arith/lia2card_tactic.cpp0000644000175000017500000002351312540347414020532 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: lia2card_tactic.cpp Abstract: Convert 0-1 integer variables cardinality constraints to built-in cardinality operator. Author: Nikolaj Bjorner (nbjorner) 2013-11-5 Notes: --*/ #include"tactical.h" #include"cooperate.h" #include"bound_manager.h" #include"ast_pp.h" #include"expr_safe_replace.h" // NB: should use proof-producing expr_substitute in polished version. #include"pb_decl_plugin.h" #include"arith_decl_plugin.h" class lia2card_tactic : public tactic { public: typedef obj_hashtable expr_set; ast_manager & m; arith_util a; params_ref m_params; pb_util m_pb; mutable ptr_vector* m_todo; expr_set* m_01s; bool m_compile_equality; lia2card_tactic(ast_manager & _m, params_ref const & p): m(_m), a(m), m_pb(m), m_todo(alloc(ptr_vector)), m_01s(alloc(expr_set)), m_compile_equality(false) { } virtual ~lia2card_tactic() { dealloc(m_todo); dealloc(m_01s); } void set_cancel(bool f) { } void updt_params(params_ref const & p) { m_params = p; m_compile_equality = p.get_bool("compile_equality", false); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; m_01s->reset(); tactic_report report("cardinality-intro", *g); bound_manager bounds(m); bounds(*g); bound_manager::iterator bit = bounds.begin(), bend = bounds.end(); for (; bit != bend; ++bit) { expr* x = *bit; bool s1, s2; rational lo, hi; if (a.is_int(x) && bounds.has_lower(x, lo, s1) && !s1 && lo.is_zero() && bounds.has_upper(x, hi, s2) && !s2 && hi.is_one()) { m_01s->insert(x); TRACE("pb", tout << "add bound " << mk_pp(x, m) << "\n";); } } expr_safe_replace sub(m); extract_pb_substitution(g, sub); expr_ref new_curr(m); proof_ref new_pr(m); for (unsigned i = 0; i < g->size(); i++) { expr * curr = g->form(i); sub(curr, new_curr); if (m.proofs_enabled()) { new_pr = m.mk_rewrite(curr, new_curr); new_pr = m.mk_modus_ponens(g->pr(i), new_pr); } g->update(i, new_curr, new_pr, g->dep(i)); } g->inc_depth(); result.push_back(g.get()); TRACE("pb", g->display(tout);); SASSERT(g->is_well_sorted()); // TBD: convert models for 0-1 variables. // TBD: support proof conversion (or not..) } void extract_pb_substitution(goal_ref const& g, expr_safe_replace& sub) { ast_mark mark; for (unsigned i = 0; i < g->size(); i++) { extract_pb_substitution(mark, g->form(i), sub); } } void extract_pb_substitution(ast_mark& mark, expr* fml, expr_safe_replace& sub) { expr_ref tmp(m); m_todo->reset(); m_todo->push_back(fml); while (!m_todo->empty()) { expr* e = m_todo->back(); m_todo->pop_back(); if (mark.is_marked(e) || !is_app(e)) { continue; } mark.mark(e, true); if (get_pb_relation(sub, e, tmp)) { sub.insert(e, tmp); continue; } app* ap = to_app(e); m_todo->append(ap->get_num_args(), ap->get_args()); } } bool is_01var(expr* x) const { return m_01s->contains(x); } expr_ref mk_01(expr* x) { expr* r = m.mk_eq(x, a.mk_numeral(rational(1), m.get_sort(x))); return expr_ref(r, m); } bool get_pb_relation(expr_safe_replace& sub, expr* fml, expr_ref& result) { expr* x, *y; expr_ref_vector args(m); vector coeffs; rational coeff; if ((a.is_le(fml, x, y) || a.is_ge(fml, y, x)) && get_pb_sum(x, rational::one(), args, coeffs, coeff) && get_pb_sum(y, -rational::one(), args, coeffs, coeff)) { result = mk_le(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), -coeff); return true; } else if ((a.is_lt(fml, y, x) || a.is_gt(fml, x, y)) && get_pb_sum(x, rational::one(), args, coeffs, coeff) && get_pb_sum(y, -rational::one(), args, coeffs, coeff)) { result = m.mk_not(mk_le(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), -coeff)); return true; } else if (m.is_eq(fml, x, y) && get_pb_sum(x, rational::one(), args, coeffs, coeff) && get_pb_sum(y, -rational::one(), args, coeffs, coeff)) { result = mk_eq(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), -coeff); return true; } return false; } expr* mk_le(unsigned sz, rational const* weights, expr* const* args, rational const& w) { if (sz == 0) { return w.is_neg()?m.mk_false():m.mk_true(); } if (sz == 1 && weights[0].is_one() && w >= rational::one()) { return m.mk_true(); } if (sz == 1 && weights[0].is_one() && w.is_zero()) { return m.mk_not(args[0]); } return m_pb.mk_le(sz, weights, args, w); } expr* mk_eq(unsigned sz, rational const* weights, expr* const* args, rational const& w) { if (m_compile_equality) { return m_pb.mk_eq(sz, weights, args, w); } else { return m.mk_and(mk_ge(sz, weights, args, w), mk_le(sz, weights, args, w)); } } expr* mk_ge(unsigned sz, rational const* weights, expr* const* args, rational const& w) { if (sz == 0) { return w.is_pos()?m.mk_false():m.mk_true(); } if (sz == 1 && weights[0].is_one() && w.is_one()) { return args[0]; } if (sz == 1 && weights[0].is_one() && w.is_zero()) { return m.mk_not(args[0]); } return m_pb.mk_ge(sz, weights, args, w); } bool get_pb_sum(expr* x, rational const& mul, expr_ref_vector& args, vector& coeffs, rational& coeff) { expr *y, *z, *u; rational r, q; app* f = to_app(x); bool ok = true; if (a.is_add(x)) { for (unsigned i = 0; ok && i < f->get_num_args(); ++i) { ok = get_pb_sum(f->get_arg(i), mul, args, coeffs, coeff); } } else if (a.is_sub(x, y, z)) { ok = get_pb_sum(y, mul, args, coeffs, coeff); ok = ok && get_pb_sum(z, -mul, args, coeffs, coeff); } else if (a.is_uminus(x, y)) { ok = get_pb_sum(y, -mul, args, coeffs, coeff); } else if (a.is_mul(x, y, z) && is_numeral(y, r)) { ok = get_pb_sum(z, r*mul, args, coeffs, coeff); } else if (a.is_mul(x, z, y) && is_numeral(y, r)) { ok = get_pb_sum(z, r*mul, args, coeffs, coeff); } else if (a.is_to_real(x, y)) { ok = get_pb_sum(y, mul, args, coeffs, coeff); } else if (m.is_ite(x, y, z, u) && is_numeral(z, r) && is_numeral(u, q)) { insert_arg(r*mul, y, args, coeffs, coeff); // q*(1-y) = -q*y + q coeff += q*mul; insert_arg(-q*mul, y, args, coeffs, coeff); } else if (is_01var(x)) { insert_arg(mul, mk_01(x), args, coeffs, coeff); } else if (is_numeral(x, r)) { coeff += mul*r; } else { TRACE("pb", tout << "Can't handle " << mk_pp(x, m) << "\n";); ok = false; } return ok; } bool is_numeral(expr* e, rational& r) { if (a.is_uminus(e, e) && is_numeral(e, r)) { r.neg(); return true; } if (a.is_to_real(e, e)) { return is_numeral(e, r); } return a.is_numeral(e, r); } void insert_arg(rational const& p, expr* x, expr_ref_vector& args, vector& coeffs, rational& coeff) { if (p.is_neg()) { // p*x = -p*(1-x) + p args.push_back(m.mk_not(x)); coeffs.push_back(-p); coeff += p; } else if (p.is_pos()) { args.push_back(x); coeffs.push_back(p); } } virtual tactic * translate(ast_manager & m) { return alloc(lia2card_tactic, m, m_params); } virtual void collect_param_descrs(param_descrs & r) { r.insert("compile_equality", CPK_BOOL, "(default:false) compile equalities into pseudo-Boolean equality"); } virtual void cleanup() { expr_set* d = alloc(expr_set); ptr_vector* todo = alloc(ptr_vector); #pragma omp critical (tactic_cancel) { std::swap(m_01s, d); std::swap(m_todo, todo); } dealloc(d); dealloc(todo); } }; tactic * mk_lia2card_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(lia2card_tactic, m, p)); } bool get_pb_sum(expr* term, expr_ref_vector& args, vector& coeffs, rational& coeff) { params_ref p; ast_manager& m = args.get_manager(); lia2card_tactic tac(m, p); return tac.get_pb_sum(term, rational::one(), args, coeffs, coeff); } z3-4.4.0/src/tactic/probe.cpp0000644000175000017500000002661712540347414015532 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: probe.cpp Abstract: Evaluates/Probes a goal. A probe is used to build tactics (aka strategies) that makes decisions based on the structure of a goal. Author: Leonardo de Moura (leonardo) 2011-10-13. Revision History: --*/ #include"probe.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"goal_util.h" class memory_probe : public probe { public: virtual result operator()(goal const & g) { return result(static_cast(memory::get_allocation_size())/static_cast(1024*1024)); } }; probe * mk_memory_probe() { return alloc(memory_probe); } class depth_probe : public probe { public: virtual result operator()(goal const & g) { return result(g.depth()); } }; class size_probe : public probe { public: virtual result operator()(goal const & g) { return result(g.size()); } }; class num_exprs_probe : public probe { public: virtual result operator()(goal const & g) { return result(g.num_exprs()); } }; probe * mk_depth_probe() { return alloc(depth_probe); } probe * mk_size_probe() { return alloc(size_probe); } probe * mk_num_exprs_probe() { return alloc(num_exprs_probe); } class unary_probe : public probe { protected: probe * m_p; public: unary_probe(probe * p): m_p(p) { SASSERT(p); p->inc_ref(); } ~unary_probe() { m_p->dec_ref(); } }; class bin_probe : public probe { protected: probe * m_p1; probe * m_p2; public: bin_probe(probe * p1, probe * p2): m_p1(p1), m_p2(p2) { SASSERT(p1); SASSERT(p2); p1->inc_ref(); p2->inc_ref(); } ~bin_probe() { m_p1->dec_ref(); m_p2->dec_ref(); } }; class not_probe : public unary_probe { public: not_probe(probe * p):unary_probe(p) {} virtual result operator()(goal const & g) { return result(!m_p->operator()(g).is_true()); } }; class and_probe : public bin_probe { public: and_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} virtual result operator()(goal const & g) { return result(m_p1->operator()(g).is_true() && m_p2->operator()(g).is_true()); } }; class or_probe : public bin_probe { public: or_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} virtual result operator()(goal const & g) { return result(m_p1->operator()(g).is_true() || m_p2->operator()(g).is_true()); } }; class eq_probe : public bin_probe { public: eq_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} virtual result operator()(goal const & g) { return result(m_p1->operator()(g).get_value() == m_p2->operator()(g).get_value()); } }; class le_probe : public bin_probe { public: le_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} virtual result operator()(goal const & g) { return result(m_p1->operator()(g).get_value() <= m_p2->operator()(g).get_value()); } }; class add_probe : public bin_probe { public: add_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} virtual result operator()(goal const & g) { return result(m_p1->operator()(g).get_value() + m_p2->operator()(g).get_value()); } }; class sub_probe : public bin_probe { public: sub_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} virtual result operator()(goal const & g) { return result(m_p1->operator()(g).get_value() - m_p2->operator()(g).get_value()); } }; class mul_probe : public bin_probe { public: mul_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} virtual result operator()(goal const & g) { return result(m_p1->operator()(g).get_value() * m_p2->operator()(g).get_value()); } }; class div_probe : public bin_probe { public: div_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} virtual result operator()(goal const & g) { return result(m_p1->operator()(g).get_value() / m_p2->operator()(g).get_value()); } }; class const_probe : public probe { double m_val; public: const_probe(double v):m_val(v) {} virtual result operator()(goal const & g) { return result(m_val); } }; probe * mk_const_probe(double v) { return alloc(const_probe, v); } probe * mk_not(probe * p) { return alloc(not_probe, p); } probe * mk_and(probe * p1, probe * p2) { return alloc(and_probe, p1, p2); } probe * mk_or(probe * p1, probe * p2) { return alloc(or_probe, p1, p2); } probe * mk_implies(probe * p1, probe * p2) { return mk_or(mk_not(p1), p2); } probe * mk_eq(probe * p1, probe * p2) { return alloc(eq_probe, p1, p2); } probe * mk_neq(probe * p1, probe * p2) { return mk_not(mk_eq(p1, p2)); } probe * mk_le(probe * p1, probe * p2) { return alloc(le_probe, p1, p2); } probe * mk_ge(probe * p1, probe * p2) { return mk_le(p2, p1); } probe * mk_lt(probe * p1, probe * p2) { return mk_not(mk_ge(p1, p2)); } probe * mk_gt(probe * p1, probe * p2) { return mk_lt(p2, p1); } probe * mk_add(probe * p1, probe * p2) { return alloc(add_probe, p1, p2); } probe * mk_mul(probe * p1, probe * p2) { return alloc(mul_probe, p1, p2); } probe * mk_sub(probe * p1, probe * p2) { return alloc(sub_probe, p1, p2); } probe * mk_div(probe * p1, probe * p2) { return alloc(div_probe, p1, p2); } struct is_non_propositional_predicate { struct found {}; ast_manager & m; is_non_propositional_predicate(ast_manager & _m):m(_m) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } void operator()(app * n) { if (!m.is_bool(n)) throw found(); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (is_uninterp_const(n)) return; throw found(); } }; struct is_non_qfbv_predicate { struct found {}; ast_manager & m; bv_util u; is_non_qfbv_predicate(ast_manager & _m):m(_m), u(m) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } void operator()(app * n) { if (!m.is_bool(n) && !u.is_bv(n)) throw found(); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == u.get_family_id()) return; if (is_uninterp_const(n)) return; throw found(); } }; class is_propositional_probe : public probe { public: virtual result operator()(goal const & g) { return !test(g); } }; class is_qfbv_probe : public probe { public: virtual result operator()(goal const & g) { return !test(g); } }; probe * mk_is_propositional_probe() { return alloc(is_propositional_probe); } probe * mk_is_qfbv_probe() { return alloc(is_qfbv_probe); } struct is_non_qfaufbv_predicate { struct found {}; ast_manager & m; bv_util m_bv_util; array_util m_array_util; is_non_qfaufbv_predicate(ast_manager & _m) : m(_m), m_bv_util(_m), m_array_util(_m) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } void operator()(app * n) { if (!m.is_bool(n) && !m_bv_util.is_bv(n) && !m_array_util.is_array(n)) throw found(); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == m_bv_util.get_family_id() || fid == m_array_util.get_family_id()) return; if (is_uninterp(n)) return; throw found(); } }; class is_qfaufbv_probe : public probe { public: virtual result operator()(goal const & g) { return !test(g); } }; probe * mk_is_qfaufbv_probe() { return alloc(is_qfaufbv_probe); } class num_consts_probe : public probe { bool m_bool; // If true, track only boolean constants. Otherwise, track only non boolean constants. char const * m_family; // (Ignored if m_bool == true), if != 0 and m_bool == true, then track only constants of the given family. struct proc { ast_manager & m; bool m_bool; family_id m_fid; unsigned m_counter; proc(ast_manager & _m, bool b, char const * family):m(_m), m_bool(b), m_counter(0) { if (family != 0) m_fid = m.mk_family_id(family); else m_fid = null_family_id; } void operator()(quantifier *) {} void operator()(var *) {} void operator()(app * n) { if (n->get_num_args() == 0 && !m.is_value(n)) { if (m_bool) { if (m.is_bool(n)) m_counter++; } else { if (m_fid == null_family_id) { if (!m.is_bool(n)) m_counter++; } else { if (m.get_sort(n)->get_family_id() == m_fid) m_counter++; } } } } }; public: num_consts_probe(bool b, char const * f): m_bool(b), m_family(f) { } virtual result operator()(goal const & g) { proc p(g.m(), m_bool, m_family); unsigned sz = g.size(); expr_fast_mark1 visited; for (unsigned i = 0; i < sz; i++) { for_each_expr_core(p, visited, g.form(i)); } return result(p.m_counter); } }; probe * mk_num_consts_probe() { return alloc(num_consts_probe, false, 0); } probe * mk_num_bool_consts_probe() { return alloc(num_consts_probe, true, 0); } probe * mk_num_arith_consts_probe() { return alloc(num_consts_probe, false, "arith"); } probe * mk_num_bv_consts_probe() { return alloc(num_consts_probe, false, "bv"); } class produce_proofs_probe : public probe { public: virtual result operator()(goal const & g) { return g.proofs_enabled(); } }; class produce_models_probe : public probe { public: virtual result operator()(goal const & g) { return g.models_enabled(); } }; class produce_unsat_cores_probe : public probe { public: virtual result operator()(goal const & g) { return g.unsat_core_enabled(); } }; probe * mk_produce_proofs_probe() { return alloc(produce_proofs_probe); } probe * mk_produce_models_probe() { return alloc(produce_models_probe); } probe * mk_produce_unsat_cores_probe() { return alloc(produce_unsat_cores_probe); } struct has_pattern_probe : public probe { struct found {}; struct proc { void operator()(var * n) {} void operator()(app * n) {} void operator()(quantifier * n) { if (n->get_num_patterns() > 0 || n->get_num_no_patterns() > 0) throw found(); } }; public: virtual result operator()(goal const & g) { try { expr_fast_mark1 visited; proc p; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { quick_for_each_expr(p, visited, g.form(i)); } return false; } catch (found) { return true; } } }; probe * mk_has_pattern_probe() { return alloc(has_pattern_probe); } z3-4.4.0/src/tactic/goal.h0000644000175000017500000001623612540347414015006 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: goal.h Abstract: A goal is essentially a set of formulas. Tactics are used to build proof and model finding procedures for these sets. Remark: In a previous version of Z3, goals were called assertion_sets. Here is a summary of the main changes: - Goals track whether they are the result of applying over/under approximation steps. This prevent users from creating unsound strategies (e.g., user uses nia2sat, but does not check the sat_preserving flag). - Goals track dependencies (aka light proofs) for unsat core extraction, and building multi-tier solvers. This kind of dependency tracking is more powerful than the one used in the current Z3, since it does not prevent the use of preprocessing steps such as "Gaussian Elimination". Author: Leonardo de Moura (leonardo) 2011-10-12 Revision History: --*/ #ifndef _GOAL_H_ #define _GOAL_H_ #include"ast.h" #include"ast_translation.h" #include"ast_printer.h" #include"for_each_expr.h" #include"ref.h" #include"ref_vector.h" #include"ref_buffer.h" class goal { public: enum precision { PRECISE, UNDER, // goal is the product of an under-approximation OVER, // goal is the product of an over-approximation UNDER_OVER // goal is garbage: the produce of combined under and over approximation steps. }; static precision mk_union(precision p1, precision p2); protected: ast_manager & m_manager; unsigned m_ref_count; expr_array m_forms; expr_array m_proofs; expr_dependency_array m_dependencies; // attributes unsigned m_depth:26; // depth of the goal in the goal tree. unsigned m_models_enabled:1; // model generation is enabled. unsigned m_proofs_enabled:1; // proof production is enabled. m_manager.proofs_enabled() must be true if m_proofs_enabled == true unsigned m_core_enabled:1; // unsat core extraction is enabled. unsigned m_inconsistent:1; // true if the goal is known to be inconsistent. unsigned m_precision:2; // PRECISE, UNDER, OVER. void push_back(expr * f, proof * pr, expr_dependency * d); void quick_process(bool save_first, expr * & f, expr_dependency * d); void process_and(bool save_first, app * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr); void process_not_or(bool save_first, app * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr); void slow_process(bool save_first, expr * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr); void slow_process(expr * f, proof * pr, expr_dependency * d); unsigned get_idx(expr * f) const; unsigned get_not_idx(expr * f) const; void shrink(unsigned j); void reset_core(); public: goal(ast_manager & m, bool models_enabled = true, bool core_enabled = false); goal(ast_manager & m, bool proofs_enabled, bool models_enabled, bool core_enabled); goal(goal const & src); // Copy configuration: depth, models/proofs/cores flags, and precision from src. // The assertions are not copied goal(goal const & src, bool); ~goal(); void inc_ref() { ++m_ref_count; } void dec_ref() { --m_ref_count; if (m_ref_count == 0) dealloc(this); } ast_manager & m() const { return m_manager; } unsigned depth() const { return m_depth; } bool models_enabled() const { return m_models_enabled; } bool proofs_enabled() const { return m_proofs_enabled; } bool unsat_core_enabled() const { return m_core_enabled; } bool inconsistent() const { return m_inconsistent; } precision prec() const { return static_cast(m_precision); } void set_depth(unsigned d) { m_depth = d; } void inc_depth() { m_depth++; } void set_prec(precision d) { m_precision = d; } void updt_prec(precision d) { m_precision = mk_union(prec(), d); } void reset_all(); // reset goal and precision and depth attributes. void reset(); // reset goal but preserve precision and depth attributes. void copy_to(goal & target) const; void copy_from(goal const & src) { src.copy_to(*this); } void assert_expr(expr * f, proof * pr, expr_dependency * d); void assert_expr(expr * f, expr_dependency * d); void assert_expr(expr * f, expr * d) { assert_expr(f, m().mk_leaf(d)); } void assert_expr(expr * f) { assert_expr(f, static_cast(0)); } unsigned size() const { return m().size(m_forms); } unsigned num_exprs() const; expr * form(unsigned i) const { return m().get(m_forms, i); } proof * pr(unsigned i) const { return proofs_enabled() ? static_cast(m().get(m_proofs, i)) : 0; } expr_dependency * dep(unsigned i) const { return unsat_core_enabled() ? m().get(m_dependencies, i) : 0; } void update(unsigned i, expr * f, proof * pr = 0, expr_dependency * dep = 0); void get_formulas(ptr_vector & result); void elim_true(); void elim_redundancies(); void display(ast_printer & prn, std::ostream & out) const; void display(ast_printer_context & ctx) const; void display(std::ostream & out) const; void display_ll(std::ostream & out) const; void display_as_and(std::ostream & out) const; void display_dimacs(std::ostream & out) const; void display_with_dependencies(ast_printer & prn, std::ostream & out) const; void display_with_dependencies(ast_printer_context & ctx) const; void display_with_dependencies(std::ostream & out) const; bool sat_preserved() const; bool unsat_preserved() const; bool is_decided_sat() const; bool is_decided_unsat() const; bool is_decided() const; bool is_well_sorted() const; goal * translate(ast_translation & translator) const; }; std::ostream & operator<<(std::ostream & out, goal::precision p); typedef ref goal_ref; typedef sref_vector goal_ref_vector; typedef sref_buffer goal_ref_buffer; template inline bool is_decided(GoalCollection const & c) { return c.size() == 1 && c[0]->is_decided(); } template inline bool is_decided_sat(GoalCollection const & c) { return c.size() == 1 && c[0]->is_decided_sat(); } template inline bool is_decided_unsat(GoalCollection const & c) { return c.size() == 1 && c[0]->is_decided_unsat(); } template void for_each_expr_at(ForEachProc& proc, goal const & s) { expr_mark visited; for (unsigned i = 0; i < s.size(); ++i) { for_each_expr(proc, visited, s.form(i)); } } bool is_equal(goal const & g1, goal const & g2); template bool test(goal const & g, Predicate & proc) { expr_fast_mark1 visited; try { unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) quick_for_each_expr(proc, visited, g.form(i)); } catch (typename Predicate::found) { return true; } return false; } template bool test(goal const & g) { Predicate proc(g.m()); return test(g, proc); } #endif z3-4.4.0/src/tactic/goal_num_occurs.cpp0000644000175000017500000000064212540347414017570 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: goal_num_occurs.cpp Abstract: Author: Leonardo de Moura (leonardo) 2012-10-20. Revision History: --*/ #include"goal_num_occurs.h" #include"goal.h" void goal_num_occurs::operator()(goal const & g) { expr_fast_mark1 visited; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { process(g.form(i), visited); } } z3-4.4.0/src/tactic/filter_model_converter.h0000644000175000017500000000165612540347414020620 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: filter_model_converter.h Abstract: Filter decls from a model Author: Leonardo (leonardo) 2011-05-06 Notes: --*/ #ifndef _FILTER_MODEL_CONVERTER_H_ #define _FILTER_MODEL_CONVERTER_H_ #include"model_converter.h" class filter_model_converter : public model_converter { func_decl_ref_vector m_decls; public: filter_model_converter(ast_manager & m):m_decls(m) {} virtual ~filter_model_converter(); ast_manager & m() const { return m_decls.get_manager(); } virtual void operator()(model_ref & md, unsigned goal_idx); virtual void operator()(model_ref & md) { operator()(md, 0); } // TODO: delete virtual void cancel() {} virtual void display(std::ostream & out); void insert(func_decl * d) { m_decls.push_back(d); } virtual model_converter * translate(ast_translation & translator); }; #endif z3-4.4.0/src/tactic/extension_model_converter.h0000644000175000017500000000227312540347414021343 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: extension_model_converter.h Abstract: Model converter that introduces new interpretations into a model. It used to be called elim_var_model_converter Author: Leonardo (leonardo) 2011-10-21 Notes: --*/ #ifndef _EXTENSION_MODEL_CONVERTER_H_ #define _EXTENSION_MODEL_CONVERTER_H_ #include"ast.h" #include"model_converter.h" class model_evaluator; class extension_model_converter : public model_converter { func_decl_ref_vector m_vars; expr_ref_vector m_defs; model_evaluator * m_eval; struct set_eval; public: extension_model_converter(ast_manager & m):m_vars(m), m_defs(m), m_eval(0) { } virtual ~extension_model_converter(); ast_manager & m() const { return m_vars.get_manager(); } virtual void operator()(model_ref & md, unsigned goal_idx); virtual void cancel(); virtual void display(std::ostream & out); // register a variable that was eliminated void insert(func_decl * v, expr * def) { m_vars.push_back(v); m_defs.push_back(def); } virtual model_converter * translate(ast_translation & translator); }; #endif z3-4.4.0/src/tactic/sls/0000755000175000017500000000000012540347414014504 5ustar michaelmichaelz3-4.4.0/src/tactic/sls/sls_tactic.cpp0000644000175000017500000001035212540347414017341 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: sls_tactic.h Abstract: A Stochastic Local Search (SLS) tactic Author: Christoph (cwinter) 2012-02-29 Notes: --*/ #include"nnf.h" #include"solve_eqs_tactic.h" #include"bv_size_reduction_tactic.h" #include"max_bv_sharing_tactic.h" #include"simplify_tactic.h" #include"propagate_values_tactic.h" #include"ctx_simplify_tactic.h" #include"elim_uncnstr_tactic.h" #include"nnf_tactic.h" #include"stopwatch.h" #include"sls_tactic.h" #include"sls_params.hpp" #include"sls_engine.h" class sls_tactic : public tactic { ast_manager & m; params_ref m_params; sls_engine * m_engine; public: sls_tactic(ast_manager & _m, params_ref const & p): m(_m), m_params(p) { m_engine = alloc(sls_engine, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(sls_tactic, m, m_params); } virtual ~sls_tactic() { dealloc(m_engine); } virtual void updt_params(params_ref const & p) { m_params = p; m_engine->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { sls_params::collect_param_descrs(r); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; result.reset(); TRACE("sls", g->display(tout);); tactic_report report("sls", *g); m_engine->operator()(g, mc); g->inc_depth(); result.push_back(g.get()); TRACE("sls", g->display(tout);); SASSERT(g->is_well_sorted()); } virtual void cleanup() { sls_engine * d = alloc(sls_engine, m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_engine); } dealloc(d); } virtual void collect_statistics(statistics & st) const { m_engine->collect_statistics(st); } virtual void reset_statistics() { m_engine->reset_statistics(); } virtual void set_cancel(bool f) { if (m_engine) m_engine->set_cancel(f); } }; tactic * mk_sls_tactic(ast_manager & m, params_ref const & p) { return and_then(fail_if_not(mk_is_qfbv_probe()), // Currently only QF_BV is supported. clean(alloc(sls_tactic, m, p))); } tactic * mk_preamble(ast_manager & m, params_ref const & p) { params_ref main_p; main_p.set_bool("elim_and", true); // main_p.set_bool("pull_cheap_ite", true); main_p.set_bool("push_ite_bv", true); main_p.set_bool("blast_distinct", true); // main_p.set_bool("udiv2mul", true); main_p.set_bool("hi_div0", true); params_ref simp2_p = p; simp2_p.set_bool("som", true); simp2_p.set_bool("pull_cheap_ite", true); simp2_p.set_bool("push_ite_bv", false); simp2_p.set_bool("local_ctx", true); simp2_p.set_uint("local_ctx_limit", 10000000); params_ref hoist_p; hoist_p.set_bool("hoist_mul", true); // hoist_p.set_bool("hoist_cmul", true); hoist_p.set_bool("som", false); params_ref gaussian_p; // conservative gaussian elimination. gaussian_p.set_uint("gaussian_max_occs", 2); params_ref ctx_p; ctx_p.set_uint("max_depth", 32); ctx_p.set_uint("max_steps", 5000000); return and_then(and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), using_params(mk_solve_eqs_tactic(m), gaussian_p), mk_elim_uncnstr_tactic(m), mk_bv_size_reduction_tactic(m), using_params(mk_simplify_tactic(m), simp2_p)), using_params(mk_simplify_tactic(m), hoist_p), mk_max_bv_sharing_tactic(m), mk_nnf_tactic(m, p)); } tactic * mk_qfbv_sls_tactic(ast_manager & m, params_ref const & p) { tactic * t = and_then(mk_preamble(m, p), mk_sls_tactic(m, p)); t->updt_params(p); return t; } z3-4.4.0/src/tactic/sls/sls_powers.h0000644000175000017500000000156512540347414017064 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: sls_powers.h Abstract: Power-of-2 module for SLS Author: Christoph (cwinter) 2012-02-29 Notes: --*/ #ifndef _SLS_POWERS_H_ #define _SLS_POWERS_H_ #include"mpz.h" class powers : public u_map { unsynch_mpz_manager & m; public: powers(unsynch_mpz_manager & m) : m(m) {} ~powers() { for (iterator it = begin(); it != end(); it++) { m.del(*it->m_value); dealloc(it->m_value); } } const mpz & operator()(unsigned n) { u_map::iterator it = find_iterator(n); if (it != end()) return *it->m_value; else { mpz * new_obj = alloc(mpz); m.mul2k(m.mk_z(1), n, *new_obj); insert(n, new_obj); return *new_obj; } } }; #endifz3-4.4.0/src/tactic/sls/bvsls_opt_engine.cpp0000644000175000017500000003012712540347414020553 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: bvsls_opt_engine.cpp Abstract: Optimization extensions to bvsls Author: Christoph (cwinter) 2014-03-28 Notes: --*/ #include "nnf.h" #include "bvsls_opt_engine.h" bvsls_opt_engine::bvsls_opt_engine(ast_manager & m, params_ref const & p) : sls_engine(m, p), m_hard_tracker(sls_engine::m_tracker), m_obj_tracker(m, m_bv_util, m_mpz_manager, m_powers), m_obj_evaluator(m, m_bv_util, m_obj_tracker, m_mpz_manager, m_powers) { m_best_model = alloc(model, m); } bvsls_opt_engine::~bvsls_opt_engine() { } bvsls_opt_engine::optimization_result bvsls_opt_engine::optimize( expr_ref const & objective, model_ref initial_model, bool _maximize) { SASSERT(m_bv_util.is_bv(objective)); TRACE("sls_opt", tout << "objective: " << (_maximize?"maximize":"minimize") << " " << mk_ismt2_pp(objective, m()) << std::endl;); m_hard_tracker.initialize(m_assertions); setup_opt_tracker(objective, _maximize); if (initial_model.get() != 0) { TRACE("sls_opt", tout << "Initial model provided: " << std::endl; for (unsigned i = 0; i < initial_model->get_num_constants(); i++) { func_decl * fd = initial_model->get_constant(i); expr * val = initial_model->get_const_interp(fd); tout << fd->get_name() << " := " << mk_ismt2_pp(val, m()) << std::endl; }); m_hard_tracker.set_model(initial_model); m_evaluator.update_all(); } optimization_result res(m_manager); lbool is_sat = m_hard_tracker.is_sat() ? l_true : l_undef; TRACE("sls_opt", tout << "initial model is sat? " << is_sat << std::endl;); for (m_stats.m_restarts = 0; m_stats.m_restarts < m_max_restarts; m_stats.m_restarts++) { mpz old_best; m_mpz_manager.set(old_best, m_best_model_score); if (is_sat != l_true) { do { checkpoint(); IF_VERBOSE(1, verbose_stream() << "Satisfying... restarts left:" << (m_max_restarts - m_stats.m_restarts) << std::endl;); is_sat = search(); if (is_sat == l_undef) m_hard_tracker.randomize(m_assertions); } while (is_sat != l_true && m_stats.m_restarts++ < m_max_restarts); } if (is_sat == l_true) { IF_VERBOSE(1, verbose_stream() << "Optimizing... restarts left:" << (m_max_restarts - m_stats.m_restarts) << std::endl;); res.is_sat = l_true; m_obj_tracker.set_model(m_hard_tracker.get_model()); m_obj_evaluator.update_all(); expr_ref local_best = maximize(); if ((_maximize && m_mpz_manager.gt(m_best_model_score, old_best)) || (!_maximize && m_mpz_manager.lt(m_best_model_score, old_best))) { res.optimum = local_best; } } m_hard_tracker.randomize(m_assertions); m_evaluator.update_all(); is_sat = m_hard_tracker.is_sat() ? l_true : l_undef; } TRACE("sls_opt", tout << "sat: " << res.is_sat << "; optimum: " << mk_ismt2_pp(res.optimum, m()) << std::endl;); return res; } void bvsls_opt_engine::setup_opt_tracker(expr_ref const & objective, bool _max) { expr_ref obj(m_manager); obj = objective; if (!_max) obj = m_bv_util.mk_bv_neg(objective); m_obj_e = obj.get(); m_obj_bv_sz = m_bv_util.get_bv_size(m_obj_e); ptr_vector objs; objs.push_back(m_obj_e); m_obj_tracker.initialize(objs); } expr_ref bvsls_opt_engine::maximize() { SASSERT(m_hard_tracker.is_sat()); TRACE("sls_opt", tout << "Initial opt model:" << std::endl; m_obj_tracker.show_model(tout);); mpz score, old_score, max_score, new_value; unsigned new_const = (unsigned)-1, new_bit = 0; ptr_vector consts = m_obj_tracker.get_constants(); move_type move; m_mpz_manager.set(score, top_score()); m_mpz_manager.set(max_score, m_powers(m_obj_bv_sz)); m_mpz_manager.dec(max_score); IF_VERBOSE(10, verbose_stream() << "Initial score: " << m_mpz_manager.to_string(score) << std::endl;); save_model(score); while (m_mpz_manager.lt(score, max_score) && check_restart(m_stats.m_moves)) { checkpoint(); m_stats.m_moves++; m_mpz_manager.set(old_score, score); new_const = (unsigned)-1; mpz score(0); m_mpz_manager.set(score, find_best_move(consts, score, new_const, new_value, new_bit, move, max_score, m_obj_e)); if (new_const == static_cast(-1)) { m_mpz_manager.set(score, old_score); if (m_mpz_manager.gt(score, m_best_model_score)) save_model(score); if (!randomize_wrt_hard()) { // Can't improve and can't randomize; can't do anything other than bail out. TRACE("sls_opt", tout << "Got stuck; bailing out." << std::endl;); IF_VERBOSE(10, verbose_stream() << "No local improvements possible." << std::endl;); goto bailout; } m_mpz_manager.set(score, top_score()); } else { m_stats.m_moves++; TRACE("sls_opt", tout << "New optimum: " << m_mpz_manager.to_string(score) << std::endl;); IF_VERBOSE(10, verbose_stream() << "New optimum: " << m_mpz_manager.to_string(score) << std::endl;); func_decl * fd = consts[new_const]; incremental_score(fd, new_value); m_obj_evaluator.update(fd, new_value); m_mpz_manager.set(score, top_score()); } } bailout: m_mpz_manager.del(new_value); expr_ref res(m_manager); res = m_bv_util.mk_numeral(m_best_model_score, m_obj_bv_sz); return res; } void bvsls_opt_engine::save_model(mpz const & score) { model_ref mdl = m_hard_tracker.get_model(); model_ref obj_mdl = m_obj_tracker.get_model(); for (unsigned i = 0; i < obj_mdl->get_num_constants(); i++) { func_decl * fd = obj_mdl->get_constant(i); expr * val = obj_mdl->get_const_interp(fd); if (mdl->has_interpretation(fd)) { if (mdl->get_const_interp(fd) != val) TRACE("sls_opt", tout << "model disagreement on " << fd->get_name() << ": " << mk_ismt2_pp(val, m()) << " != " << mk_ismt2_pp(mdl->get_const_interp(fd), m()) << std::endl;); SASSERT(mdl->get_const_interp(fd) == val); } else mdl->register_decl(fd, val); } m_best_model = mdl; m_mpz_manager.set(m_best_model_score, score); } // checks whether the score outcome of a given move is better than the previous score bool bvsls_opt_engine::what_if( func_decl * fd, const unsigned & fd_inx, const mpz & temp, mpz & best_score, unsigned & best_const, mpz & best_value) { #if _EARLY_PRUNE_ double r = incremental_score_prune(fd, temp); #else double r = incremental_score(fd, temp); #endif if (r >= 1.0 && m_hard_tracker.is_sat()) { m_obj_evaluator.update(fd, temp); mpz cur_best(0); m_mpz_manager.set(cur_best, top_score()); TRACE("sls_whatif", tout << "WHAT IF " << fd->get_name() << " WERE " << m_mpz_manager.to_string(temp) << " --> " << r << "; score=" << m_mpz_manager.to_string(cur_best) << std::endl;); if (m_mpz_manager.gt(cur_best, best_score)) { m_mpz_manager.set(best_score, cur_best); best_const = fd_inx; m_mpz_manager.set(best_value, temp); return true; } } else { TRACE("sls_whatif_failed", tout << "WHAT IF " << fd->get_name() << " WERE " << m_mpz_manager.to_string(temp) << " --> unsatisfied hard constraints" << std::endl;); } return false; } mpz bvsls_opt_engine::find_best_move( ptr_vector & to_evaluate, mpz score, unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move, mpz const & max_score, expr * objective) { mpz old_value, temp; #if _USE_MUL3_ || _USE_UNARY_MINUS_ mpz temp2; #endif unsigned bv_sz; mpz new_score; m_mpz_manager.set(new_score, score); for (unsigned i = 0; i < to_evaluate.size() && m_mpz_manager.lt(new_score, max_score); i++) { func_decl * fd = to_evaluate[i]; sort * srt = fd->get_range(); bv_sz = (m_manager.is_bool(srt)) ? 1 : m_bv_util.get_bv_size(srt); m_mpz_manager.set(old_value, m_obj_tracker.get_value(fd)); // first try to flip every bit for (unsigned j = 0; j < bv_sz && m_mpz_manager.lt(new_score, max_score); j++) { // What would happen if we flipped bit #i ? mk_flip(srt, old_value, j, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) { new_bit = j; move = MV_FLIP; } } if (m_bv_util.is_bv_sort(srt) && bv_sz > 1) { #if _USE_ADDSUB_ if (!m_mpz_manager.is_even(old_value)) { // for odd values, try +1 mk_inc(bv_sz, old_value, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_INC; } else { // for even values, try -1 mk_dec(bv_sz, old_value, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_DEC; } #endif // try inverting mk_inv(bv_sz, old_value, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_INV; #if _USE_UNARY_MINUS_ mk_inc(bv_sz, temp, temp2); if (what_if(fd, i, temp2, new_score, best_const, best_value)) move = MV_UMIN; #endif #if _USE_MUL2DIV2_ // try multiplication by 2 mk_mul2(bv_sz, old_value, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_MUL2; #if _USE_MUL3_ // try multiplication by 3 mk_add(bv_sz, old_value, temp, temp2); if (what_if(fd, i, temp2, new_score, best_const, best_value)) move = MV_MUL3; #endif // try division by 2 mk_div2(bv_sz, old_value, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_DIV2; #endif } // reset to what it was before //double check = incremental_score(fd, old_value); m_obj_evaluator.update(fd, old_value); } m_mpz_manager.del(old_value); m_mpz_manager.del(temp); #if _USE_MUL3_ m_mpz_manager.del(temp2); #endif return new_score; } bool bvsls_opt_engine::randomize_wrt_hard() { ptr_vector consts = m_obj_tracker.get_constants(); unsigned csz = consts.size(); unsigned retry_count = csz; while (retry_count-- > 0) { unsigned ri = (m_obj_tracker.get_random_uint((csz < 16) ? 4 : (csz < 256) ? 8 : (csz < 4096) ? 12 : (csz < 65536) ? 16 : 32)) % csz; func_decl * random_fd = consts[ri]; // Random constant mpz random_val; // random value. m_mpz_manager.set(random_val, m_obj_tracker.get_random(random_fd->get_range())); mpz old_value; m_mpz_manager.set(old_value, m_obj_tracker.get_value(random_fd)); if (!m_mpz_manager.eq(random_val, old_value)) { m_evaluator.update(random_fd, random_val); if (m_hard_tracker.is_sat()) { TRACE("sls_opt", tout << "Randomizing " << random_fd->get_name() << " to " << m_mpz_manager.to_string(random_val) << std::endl;); m_obj_evaluator.update(random_fd, random_val); return true; } else m_evaluator.update(random_fd, old_value); } } return false; } z3-4.4.0/src/tactic/sls/sls_params.pyg0000644000175000017500000000400612540347414017371 0ustar michaelmichaeldef_module_params('sls', export=True, description='Experimental Stochastic Local Search Solver (for QFBV only).', params=(max_memory_param(), ('max_restarts', UINT, UINT_MAX, 'maximum number of restarts'), ('walksat', BOOL, 1, 'use walksat assertion selection (instead of gsat)'), ('walksat_ucb', BOOL, 1, 'use bandit heuristic for walksat assertion selection (instead of random)'), ('walksat_ucb_constant', DOUBLE, 20.0, 'the ucb constant c in the term score + c * f(touched)'), ('walksat_ucb_init', BOOL, 0, 'initialize total ucb touched to formula size'), ('walksat_ucb_forget', DOUBLE, 1.0, 'scale touched by this factor every base restart interval'), ('walksat_ucb_noise', DOUBLE, 0.0002, 'add noise 0 <= 256 * ucb_noise to ucb score for assertion selection'), ('walksat_repick', BOOL, 1, 'repick assertion if randomizing in local minima'), ('scale_unsat', DOUBLE, 0.5, 'scale score of unsat expressions by this factor'), ('paws_init', UINT, 40, 'initial/minimum assertion weights'), ('paws_sp', UINT, 52, 'smooth assertion weights with probability paws_sp / 1024'), ('wp', UINT, 100, 'random walk with probability wp / 1024'), ('vns_mc', UINT, 0, 'in local minima, try Monte Carlo sampling vns_mc many 2-bit-flips per bit'), ('vns_repick', BOOL, 0, 'in local minima, try picking a different assertion (only for walksat)'), ('restart_base', UINT, 100, 'base restart interval given by moves per run'), ('restart_init', BOOL, 0, 'initialize to 0 or random value (= 1) after restart'), ('early_prune', BOOL, 1, 'use early pruning for score prediction'), ('random_offset', BOOL, 1, 'use random offset for candidate evaluation'), ('rescore', BOOL, 1, 'rescore/normalize top-level score every base restart interval'), ('track_unsat', BOOL, 0, 'keep a list of unsat assertions as done in SAT - currently disabled internally'), ('random_seed', UINT, 0, 'random seed') )) z3-4.4.0/src/tactic/sls/sls_tracker.h0000644000175000017500000011073012540347414017173 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: sls_score_tracker.h Abstract: Score and value tracking module for SLS Author: Christoph (cwinter) 2012-02-29 Notes: --*/ #ifndef _SLS_TRACKER_H_ #define _SLS_TRACKER_H_ #include #include"for_each_expr.h" #include"ast_smt2_pp.h" #include"bv_decl_plugin.h" #include"model.h" #include"sls_params.hpp" #include"sls_powers.h" class sls_tracker { ast_manager & m_manager; unsynch_mpz_manager & m_mpz_manager; bv_util & m_bv_util; powers & m_powers; random_gen m_rng; unsigned m_random_bits; unsigned m_random_bits_cnt; mpz m_zero, m_one, m_two; struct value_score { value_score() : m(0), value(unsynch_mpz_manager::mk_z(0)), score(0.0), score_prune(0.0), has_pos_occ(0), has_neg_occ(0), distance(0), touched(1) {}; ~value_score() { if (m) m->del(value); } unsynch_mpz_manager * m; mpz value; double score; double score_prune; unsigned has_pos_occ; unsigned has_neg_occ; unsigned distance; // max distance from any root unsigned touched; value_score & operator=(const value_score & other) { SASSERT(m == 0 || m == other.m); if (m) m->set(value, 0); else m = other.m; m->set(value, other.value); score = other.score; distance = other.distance; touched = other.touched; return *this; } }; public: typedef obj_map entry_point_type; private: typedef obj_map scores_type; typedef obj_map > uplinks_type; typedef obj_map > occ_type; obj_hashtable m_top_expr; scores_type m_scores; uplinks_type m_uplinks; entry_point_type m_entry_points; ptr_vector m_constants; ptr_vector m_temp_constants; occ_type m_constants_occ; unsigned m_last_pos; unsigned m_walksat; unsigned m_ucb; double m_ucb_constant; unsigned m_ucb_init; double m_ucb_forget; double m_ucb_noise; unsigned m_touched; double m_scale_unsat; unsigned m_paws_init; obj_map m_where_false; expr** m_list_false; unsigned m_track_unsat; obj_map m_weights; double m_top_sum; obj_hashtable m_temp_seen; public: sls_tracker(ast_manager & m, bv_util & bvu, unsynch_mpz_manager & mm, powers & p) : m_manager(m), m_mpz_manager(mm), m_bv_util(bvu), m_powers(p), m_random_bits_cnt(0), m_zero(m_mpz_manager.mk_z(0)), m_one(m_mpz_manager.mk_z(1)), m_two(m_mpz_manager.mk_z(2)) { } ~sls_tracker() { m_mpz_manager.del(m_zero); m_mpz_manager.del(m_one); m_mpz_manager.del(m_two); } void updt_params(params_ref const & _p) { sls_params p(_p); m_walksat = p.walksat(); m_ucb = p.walksat_ucb(); m_ucb_constant = p.walksat_ucb_constant(); m_ucb_init = p.walksat_ucb_init(); m_ucb_forget = p.walksat_ucb_forget(); m_ucb_noise = p.walksat_ucb_noise(); m_scale_unsat = p.scale_unsat(); m_paws_init = p.paws_init(); // Andreas: track_unsat is currently disabled because I cannot guarantee that it is not buggy. // If you want to use it, you will also need to change comments in the assertion selection. m_track_unsat = 0;//p.track_unsat(); } /* Andreas: Tried to give some measure for the formula size by the following two methods but both are not used currently. unsigned get_formula_size() { return m_scores.size(); } double get_avg_bw(goal_ref const & g) { double sum = 0.0; unsigned count = 0; for (unsigned i = 0; i < g->size(); i++) { m_temp_constants.reset(); ptr_vector const & this_decls = m_constants_occ.find(g->form(i)); unsigned sz = this_decls.size(); for (unsigned i = 0; i < sz; i++) { func_decl * fd = this_decls[i]; m_temp_constants.push_back(fd); sort * srt = fd->get_range(); sum += (m_manager.is_bool(srt)) ? 1 : m_bv_util.get_bv_size(srt); count++; } } return sum / count; }*/ inline void adapt_top_sum(expr * e, double add, double sub) { m_top_sum += m_weights.find(e) * (add - sub); } inline void set_top_sum(double new_score) { m_top_sum = new_score; } inline double get_top_sum() { return m_top_sum; } inline obj_hashtable const & get_top_exprs() { return m_top_expr; } inline bool is_sat() { for (obj_hashtable::iterator it = m_top_expr.begin(); it != m_top_expr.end(); it++) if (!m_mpz_manager.is_one(get_value(*it))) return false; return true; } inline void set_value(expr * n, const mpz & r) { SASSERT(m_scores.contains(n)); m_mpz_manager.set(m_scores.find(n).value, r); } inline void set_value(func_decl * fd, const mpz & r) { SASSERT(m_entry_points.contains(fd)); expr * ep = get_entry_point(fd); set_value(ep, r); } inline mpz & get_value(expr * n) { SASSERT(m_scores.contains(n)); return m_scores.find(n).value; } inline mpz & get_value(func_decl * fd) { SASSERT(m_entry_points.contains(fd)); expr * ep = get_entry_point(fd); return get_value(ep); } inline void set_score(expr * n, double score) { SASSERT(m_scores.contains(n)); m_scores.find(n).score = score; } inline void set_score(func_decl * fd, double score) { SASSERT(m_entry_points.contains(fd)); expr * ep = get_entry_point(fd); set_score(ep, score); } inline double & get_score(expr * n) { SASSERT(m_scores.contains(n)); return m_scores.find(n).score; } inline double & get_score(func_decl * fd) { SASSERT(m_entry_points.contains(fd)); expr * ep = get_entry_point(fd); return get_score(ep); } inline void set_score_prune(expr * n, double score) { SASSERT(m_scores.contains(n)); m_scores.find(n).score_prune = score; } inline double & get_score_prune(expr * n) { SASSERT(m_scores.contains(n)); return m_scores.find(n).score_prune; } inline unsigned has_pos_occ(expr * n) { SASSERT(m_scores.contains(n)); return m_scores.find(n).has_pos_occ; } inline unsigned has_neg_occ(expr * n) { SASSERT(m_scores.contains(n)); return m_scores.find(n).has_neg_occ; } inline unsigned get_distance(expr * n) { SASSERT(m_scores.contains(n)); return m_scores.find(n).distance; } inline void set_distance(expr * n, unsigned d) { SASSERT(m_scores.contains(n)); m_scores.find(n).distance = d; } inline expr * get_entry_point(func_decl * fd) { SASSERT(m_entry_points.contains(fd)); return m_entry_points.find(fd); } inline entry_point_type const & get_entry_points() { return m_entry_points; } inline bool has_uplinks(expr * n) { return m_uplinks.contains(n); } inline bool is_top_expr(expr * n) { return m_top_expr.contains(n); } inline ptr_vector & get_uplinks(expr * n) { SASSERT(m_uplinks.contains(n)); return m_uplinks.find(n); } inline void ucb_forget(ptr_vector & as) { if (m_ucb_forget < 1.0) { expr * e; unsigned touched_old, touched_new; for (unsigned i = 0; i < as.size(); i++) { e = as[i]; touched_old = m_scores.find(e).touched; touched_new = (unsigned)((touched_old - 1) * m_ucb_forget + 1); m_scores.find(e).touched = touched_new; m_touched += touched_new - touched_old; } } } void initialize(app * n) { // Build score table if (!m_scores.contains(n)) { value_score vs; vs.m = & m_mpz_manager; m_scores.insert(n, vs); } // Update uplinks unsigned na = n->get_num_args(); for (unsigned i = 0; i < na; i++) { expr * c = n->get_arg(i); uplinks_type::obj_map_entry * entry = m_uplinks.insert_if_not_there2(c, ptr_vector()); entry->get_data().m_value.push_back(n); } func_decl * d = n->get_decl(); if (n->get_num_args() == 0) { if (d->get_family_id() != null_family_id) { // Interpreted constant mpz t; value2mpz(n, t); set_value(n, t); m_mpz_manager.del(t); } else { // Uninterpreted constant m_entry_points.insert_if_not_there(d, n); m_constants.push_back(d); } } } struct init_proc { ast_manager & m_manager; sls_tracker & m_tracker; init_proc(ast_manager & m, sls_tracker & tracker): m_manager(m), m_tracker(tracker) { } void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { m_tracker.initialize(n); } }; struct find_func_decls_proc { ast_manager & m_manager; ptr_vector & m_occs; find_func_decls_proc (ast_manager & m, ptr_vector & occs): m_manager(m), m_occs(occs) { } void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { if (n->get_num_args() != 0) return; func_decl * d = n->get_decl(); if (d->get_family_id() != null_family_id) return; m_occs.push_back(d); } }; void calculate_expr_distances(ptr_vector const & as) { // precondition: m_scores is set up. unsigned sz = as.size(); ptr_vector stack; for (unsigned i = 0; i < sz; i++) stack.push_back(to_app(as[i])); while (!stack.empty()) { app * cur = stack.back(); stack.pop_back(); unsigned d = get_distance(cur); for (unsigned i = 0; i < cur->get_num_args(); i++) { app * child = to_app(cur->get_arg(i)); unsigned d_child = get_distance(child); if (d >= d_child) { set_distance(child, d+1); stack.push_back(child); } } } } /* Andreas: Used this at some point to have values for the non-top-level expressions. However, it did not give better performance but even cause some additional m/o - is not used currently. void initialize_recursive(init_proc proc, expr_mark visited, expr * e) { if (m_manager.is_and(e) || m_manager.is_or(e)) { app * a = to_app(e); expr * const * args = a->get_args(); unsigned int sz = a->get_num_args(); for (unsigned int i = 0; i < sz; i++) { expr * q = args[i]; initialize_recursive(proc, visited, q); } } for_each_expr(proc, visited, e); } void initialize_recursive(expr * e) { if (m_manager.is_and(e) || m_manager.is_or(e)) { app * a = to_app(e); expr * const * args = a->get_args(); unsigned int sz = a->get_num_args(); for (unsigned int i = 0; i < sz; i++) { expr * q = args[i]; initialize_recursive(q); } } ptr_vector t; m_constants_occ.insert_if_not_there(e, t); find_func_decls_proc ffd_proc(m_manager, m_constants_occ.find(e)); expr_fast_mark1 visited; quick_for_each_expr(ffd_proc, visited, e); }*/ void initialize(ptr_vector const & as) { init_proc proc(m_manager, *this); expr_mark visited; unsigned sz = as.size(); for (unsigned i = 0; i < sz; i++) { expr * e = as[i]; if (!m_top_expr.contains(e)) m_top_expr.insert(e); for_each_expr(proc, visited, e); } visited.reset(); for (unsigned i = 0; i < sz; i++) { expr * e = as[i]; ptr_vector t; m_constants_occ.insert_if_not_there(e, t); find_func_decls_proc ffd_proc(m_manager, m_constants_occ.find(e)); expr_fast_mark1 visited; quick_for_each_expr(ffd_proc, visited, e); } calculate_expr_distances(as); TRACE("sls", tout << "Initial model:" << std::endl; show_model(tout); ); if (m_track_unsat) { m_list_false = new expr*[sz]; for (unsigned i = 0; i < sz; i++) { if (m_mpz_manager.eq(get_value(as[i]), m_zero)) break_assertion(as[i]); } } m_temp_seen.reset(); for (unsigned i = 0; i < sz; i++) { expr * e = as[i]; // initialize weights if (!m_weights.contains(e)) m_weights.insert(e, m_paws_init); // positive/negative occurences used for early pruning setup_occs(as[i]); } // initialize ucb total touched value (individual ones are always initialized to 1) m_touched = m_ucb_init ? as.size() : 1; } void increase_weight(expr * e) { m_weights.find(e)++; } void decrease_weight(expr * e) { unsigned old_weight = m_weights.find(e); m_weights.find(e) = old_weight > m_paws_init ? old_weight - 1 : m_paws_init; } unsigned get_weight(expr * e) { return m_weights.find(e); } void make_assertion(expr * e) { if (m_track_unsat) { if (m_where_false.contains(e)) { unsigned pos = m_where_false.find(e); m_where_false.erase(e); if (pos != m_where_false.size()) { expr * q = m_list_false[m_where_false.size()]; m_list_false[pos] = q; m_where_false.find(q) = pos; } } } } void break_assertion(expr * e) { if (m_track_unsat) { if (!m_where_false.contains(e)) { unsigned pos = m_where_false.size(); m_list_false[pos] = e; m_where_false.insert(e, pos); } } } void show_model(std::ostream & out) { unsigned sz = get_num_constants(); for (unsigned i = 0; i < sz; i++) { func_decl * fd = get_constant(i); out << fd->get_name() << " = " << m_mpz_manager.to_string(get_value(fd)) << std::endl; } } void set_model(model_ref const & mdl) { for (unsigned i = 0; i < mdl->get_num_constants(); i++) { func_decl * fd = mdl->get_constant(i); expr * val = mdl->get_const_interp(fd); if (m_entry_points.contains(fd)) { if (m_manager.is_bool(val)) { set_value(fd, m_manager.is_true(val) ? m_mpz_manager.mk_z(1) : m_mpz_manager.mk_z(0)); } else if (m_bv_util.is_numeral(val)) { rational r_val; unsigned bv_sz; m_bv_util.is_numeral(val, r_val, bv_sz); mpq q = r_val.to_mpq(); SASSERT(m_mpz_manager.is_one(q.denominator())); set_value(fd, q.numerator()); } else NOT_IMPLEMENTED_YET(); } } } model_ref get_model() { model_ref res = alloc(model, m_manager); unsigned sz = get_num_constants(); for (unsigned i = 0; i < sz; i++) { func_decl * fd = get_constant(i); res->register_decl(fd, mpz2value(fd->get_range(), get_value(fd))); } return res; } unsigned get_num_constants() { return m_constants.size(); } ptr_vector & get_constants() { return m_constants; } func_decl * get_constant(unsigned i) { return m_constants[i]; } void set_random_seed(unsigned s) { m_rng.set_seed(s); } mpz get_random_bv(sort * s) { SASSERT(m_bv_util.is_bv_sort(s)); unsigned bv_size = m_bv_util.get_bv_size(s); mpz r; m_mpz_manager.set(r, 0); mpz temp; do { m_mpz_manager.mul(r, m_two, temp); m_mpz_manager.add(temp, get_random_bool(), r); } while (--bv_size > 0); m_mpz_manager.del(temp); return r; } mpz & get_random_bool() { if (m_random_bits_cnt == 0) { m_random_bits = m_rng(); m_random_bits_cnt = 15; // random_gen produces 15 bits of randomness. } bool val = (m_random_bits & 0x01) != 0; m_random_bits = m_random_bits >> 1; m_random_bits_cnt--; return (val) ? m_one : m_zero; } unsigned get_random_uint(unsigned bits) { if (m_random_bits_cnt == 0) { m_random_bits = m_rng(); m_random_bits_cnt = 15; // random_gen produces 15 bits of randomness. } unsigned val = 0; while (bits-- > 0) { if ((m_random_bits & 0x01) != 0) val++; val <<= 1; m_random_bits >>= 1; m_random_bits_cnt--; if (m_random_bits_cnt == 0) { m_random_bits = m_rng(); m_random_bits_cnt = 15; // random_gen produces 15 bits of randomness. } } return val; } mpz get_random(sort * s) { if (m_bv_util.is_bv_sort(s)) return get_random_bv(s); else if (m_manager.is_bool(s)) return get_random_bool(); else NOT_IMPLEMENTED_YET(); // This only works for bit-vectors for now. } void randomize(ptr_vector const & as) { TRACE("sls", tout << "Abandoned model:" << std::endl; show_model(tout); ); for (entry_point_type::iterator it = m_entry_points.begin(); it != m_entry_points.end(); it++) { func_decl * fd = it->m_key; sort * s = fd->get_range(); mpz temp = get_random(s); set_value(it->m_value, temp); m_mpz_manager.del(temp); } TRACE("sls", tout << "Randomized model:" << std::endl; show_model(tout); ); } void reset(ptr_vector const & as) { TRACE("sls", tout << "Abandoned model:" << std::endl; show_model(tout); ); for (entry_point_type::iterator it = m_entry_points.begin(); it != m_entry_points.end(); it++) { mpz temp = m_zero; set_value(it->m_value, temp); m_mpz_manager.del(temp); } } void setup_occs(expr * n, bool negated = false) { if (m_manager.is_bool(n)) { if (m_manager.is_and(n) || m_manager.is_or(n)) { SASSERT(!negated); app * a = to_app(n); expr * const * args = a->get_args(); for (unsigned i = 0; i < a->get_num_args(); i++) { expr * child = args[i]; if (!m_temp_seen.contains(child)) { setup_occs(child, false); m_temp_seen.insert(child); } } } else if (m_manager.is_not(n)) { SASSERT(!negated); app * a = to_app(n); SASSERT(a->get_num_args() == 1); expr * child = a->get_arg(0); SASSERT(!m_manager.is_and(child) && !m_manager.is_or(child)); setup_occs(child, true); } else { if (negated) m_scores.find(n).has_neg_occ = 1; else m_scores.find(n).has_pos_occ = 1; } } else if (m_bv_util.is_bv(n)) { /* CMW: I need this for optimization. Safe to ignore? */ } else NOT_IMPLEMENTED_YET(); } double score_bool(expr * n, bool negated = false) { TRACE("sls_score", tout << ((negated)?"NEG ":"") << "BOOL: " << mk_ismt2_pp(n, m_manager) << std::endl; ); double res = 0.0; if (is_uninterp_const(n)) { const mpz & r = get_value(n); if (negated) res = (m_mpz_manager.is_one(r)) ? 0.0 : 1.0; else res = (m_mpz_manager.is_one(r)) ? 1.0 : 0.0; } else if (m_manager.is_and(n)) { SASSERT(!negated); app * a = to_app(n); expr * const * args = a->get_args(); /* Andreas: Seems to have no effect. But maybe you want to try it again at some point. double sum = 0.0; for (unsigned i = 0; i < a->get_num_args(); i++) sum += get_score(args[i]); res = sum / (double) a->get_num_args(); */ double min = 1.0; for (unsigned i = 0; i < a->get_num_args(); i++) { double cur = get_score(args[i]); if (cur < min) min = cur; } res = min; } else if (m_manager.is_or(n)) { SASSERT(!negated); app * a = to_app(n); expr * const * args = a->get_args(); double max = 0.0; for (unsigned i = 0; i < a->get_num_args(); i++) { double cur = get_score(args[i]); if (cur > max) max = cur; } res = max; } else if (m_manager.is_ite(n)) { SASSERT(!negated); app * a = to_app(n); SASSERT(a->get_num_args() == 3); const mpz & cond = get_value(a->get_arg(0)); double s_t = get_score(a->get_arg(1)); double s_f = get_score(a->get_arg(2)); res = (m_mpz_manager.is_one(cond)) ? s_t : s_f; } else if (m_manager.is_eq(n) || m_manager.is_iff(n)) { app * a = to_app(n); SASSERT(a->get_num_args() == 2); expr * arg0 = a->get_arg(0); expr * arg1 = a->get_arg(1); const mpz & v0 = get_value(arg0); const mpz & v1 = get_value(arg1); if (negated) { res = (m_mpz_manager.eq(v0, v1)) ? 0.0 : 1.0; TRACE("sls_score", tout << "V0 = " << m_mpz_manager.to_string(v0) << " ; V1 = " << m_mpz_manager.to_string(v1) << std::endl; ); } else if (m_manager.is_bool(arg0)) { res = m_mpz_manager.eq(v0, v1) ? 1.0 : 0.0; TRACE("sls_score", tout << "V0 = " << m_mpz_manager.to_string(v0) << " ; V1 = " << m_mpz_manager.to_string(v1) << std::endl; ); } else if (m_bv_util.is_bv(arg0)) { mpz diff, diff_m1; m_mpz_manager.bitwise_xor(v0, v1, diff); unsigned hamming_distance = 0; unsigned bv_sz = m_bv_util.get_bv_size(arg0); // unweighted hamming distance while (!m_mpz_manager.is_zero(diff)) { if (!m_mpz_manager.is_even(diff)) { hamming_distance++; } m_mpz_manager.machine_div(diff, m_two, diff); } res = 1.0 - (hamming_distance / (double) bv_sz); TRACE("sls_score", tout << "V0 = " << m_mpz_manager.to_string(v0) << " ; V1 = " << m_mpz_manager.to_string(v1) << " ; HD = " << hamming_distance << " ; SZ = " << bv_sz << std::endl; ); m_mpz_manager.del(diff); m_mpz_manager.del(diff_m1); } else NOT_IMPLEMENTED_YET(); } else if (m_bv_util.is_bv_ule(n)) { // x <= y app * a = to_app(n); SASSERT(a->get_num_args() == 2); const mpz & x = get_value(a->get_arg(0)); const mpz & y = get_value(a->get_arg(1)); int bv_sz = m_bv_util.get_bv_size(a->get_decl()->get_domain()[0]); if (negated) { if (m_mpz_manager.gt(x, y)) res = 1.0; else { mpz diff; m_mpz_manager.sub(y, x, diff); m_mpz_manager.inc(diff); rational n(diff); n /= rational(m_powers(bv_sz)); double dbl = n.get_double(); // In extreme cases, n is 0.9999 but to_double returns something > 1.0 res = (dbl > 1.0) ? 0.0 : (dbl < 0.0) ? 1.0 : 1.0 - dbl; m_mpz_manager.del(diff); } } else { if (m_mpz_manager.le(x, y)) res = 1.0; else { mpz diff; m_mpz_manager.sub(x, y, diff); rational n(diff); n /= rational(m_powers(bv_sz)); double dbl = n.get_double(); res = (dbl > 1.0) ? 0.0 : (dbl < 0.0) ? 1.0 : 1.0 - dbl; m_mpz_manager.del(diff); } } TRACE("sls_score", tout << "x = " << m_mpz_manager.to_string(x) << " ; y = " << m_mpz_manager.to_string(y) << " ; SZ = " << bv_sz << std::endl; ); } else if (m_bv_util.is_bv_sle(n)) { // x <= y app * a = to_app(n); SASSERT(a->get_num_args() == 2); mpz x; m_mpz_manager.set(x, get_value(a->get_arg(0))); mpz y; m_mpz_manager.set(y, get_value(a->get_arg(1))); unsigned bv_sz = m_bv_util.get_bv_size(a->get_decl()->get_domain()[0]); const mpz & p = m_powers(bv_sz); const mpz & p_half = m_powers(bv_sz-1); if (x >= p_half) { m_mpz_manager.sub(x, p, x); } if (y >= p_half) { m_mpz_manager.sub(y, p, y); } if (negated) { if (x > y) res = 1.0; else { mpz diff; m_mpz_manager.sub(y, x, diff); m_mpz_manager.inc(diff); rational n(diff); n /= p; double dbl = n.get_double(); res = (dbl > 1.0) ? 0.0 : (dbl < 0.0) ? 1.0 : 1.0 - dbl; m_mpz_manager.del(diff); } TRACE("sls_score", tout << "x = " << m_mpz_manager.to_string(x) << " ; y = " << m_mpz_manager.to_string(y) << " ; SZ = " << bv_sz << std::endl; ); } else { if (x <= y) res = 1.0; else { mpz diff; m_mpz_manager.sub(x, y, diff); SASSERT(!m_mpz_manager.is_neg(diff)); rational n(diff); n /= p; double dbl = n.get_double(); res = (dbl > 1.0) ? 0.0 : (dbl < 0.0) ? 1.0 : 1.0 - dbl; m_mpz_manager.del(diff); } TRACE("sls_score", tout << "x = " << m_mpz_manager.to_string(x) << " ; y = " << m_mpz_manager.to_string(y) << " ; SZ = " << bv_sz << std::endl; ); } m_mpz_manager.del(x); m_mpz_manager.del(y); } else if (m_manager.is_not(n)) { SASSERT(!negated); app * a = to_app(n); SASSERT(a->get_num_args() == 1); expr * child = a->get_arg(0); // Precondition: Assertion set is in NNF. // Also: careful about the unsat assertion scaling further down. if (m_manager.is_and(child) || m_manager.is_or(child)) NOT_IMPLEMENTED_YET(); res = score_bool(child, true); } else if (m_manager.is_distinct(n)) { app * a = to_app(n); unsigned pairs = 0, distinct_pairs = 0; unsigned sz = a->get_num_args(); for (unsigned i = 0; i < sz; i++) { for (unsigned j = i+1; j < sz; j++) { // pair i/j const mpz & v0 = get_value(a->get_arg(0)); const mpz & v1 = get_value(a->get_arg(1)); pairs++; if (v0 != v1) distinct_pairs++; } } res = (distinct_pairs/(double)pairs); if (negated) res = 1.0 - res; } else NOT_IMPLEMENTED_YET(); SASSERT(res >= 0.0 && res <= 1.0); app * a = to_app(n); family_id afid = a->get_family_id(); if (afid == m_bv_util.get_family_id()) if (res < 1.0) res *= m_scale_unsat; TRACE("sls_score", tout << "SCORE = " << res << std::endl; ); return res; } double score_bv(expr * n) { return 0.0; // a bv-expr is always scored as 0.0; we won't use those scores. } void value2mpz(expr * n, mpz & result) { m_mpz_manager.set(result, m_zero); if (m_manager.is_bool(n)) { m_mpz_manager.set(result, m_manager.is_true(n) ? m_one : m_zero); } else if (m_bv_util.is_bv(n)) { unsigned bv_sz = m_bv_util.get_bv_size(n); rational q; if (!m_bv_util.is_numeral(n, q, bv_sz)) NOT_IMPLEMENTED_YET(); mpq temp = q.to_mpq(); SASSERT(m_mpz_manager.is_one(temp.denominator())); m_mpz_manager.set(result, temp.numerator()); } else NOT_IMPLEMENTED_YET(); } expr_ref mpz2value(sort * s, const mpz & r) { expr_ref res(m_manager); if (m_manager.is_bool(s)) res = (m_mpz_manager.is_zero(r)) ? m_manager.mk_false() : m_manager.mk_true(); else if (m_bv_util.is_bv_sort(s)) { rational rat(r); res = m_bv_util.mk_numeral(rat, s); } else NOT_IMPLEMENTED_YET(); return res; } double score(expr * n) { if (m_manager.is_bool(n)) return score_bool(n); else if (m_bv_util.is_bv(n)) return score_bv(n); else NOT_IMPLEMENTED_YET(); } ptr_vector & get_constants(expr * e) { ptr_vector const & this_decls = m_constants_occ.find(e); unsigned sz = this_decls.size(); for (unsigned i = 0; i < sz; i++) { func_decl * fd = this_decls[i]; if (!m_temp_constants.contains(fd)) m_temp_constants.push_back(fd); } return m_temp_constants; } ptr_vector & get_unsat_constants_gsat(ptr_vector const & as) { unsigned sz = as.size(); if (sz == 1) { if (m_mpz_manager.neq(get_value(as[0]), m_one)) return get_constants(); } m_temp_constants.reset(); for (unsigned i = 0; i < sz; i++) { expr * q = as[i]; if (m_mpz_manager.eq(get_value(q), m_one)) continue; ptr_vector const & this_decls = m_constants_occ.find(q); unsigned sz2 = this_decls.size(); for (unsigned j = 0; j < sz2; j++) { func_decl * fd = this_decls[j]; if (!m_temp_constants.contains(fd)) m_temp_constants.push_back(fd); } } return m_temp_constants; } ptr_vector & get_unsat_constants_walksat(expr * e) { if (!e || m_temp_constants.size()) return m_temp_constants; ptr_vector const & this_decls = m_constants_occ.find(e); unsigned sz = this_decls.size(); for (unsigned j = 0; j < sz; j++) { func_decl * fd = this_decls[j]; if (!m_temp_constants.contains(fd)) m_temp_constants.push_back(fd); } return m_temp_constants; } ptr_vector & get_unsat_constants(ptr_vector const & as) { if (m_walksat) { expr * e = get_unsat_assertion(as); if (!e) { m_temp_constants.reset(); return m_temp_constants; } return get_unsat_constants_walksat(e); } else return get_unsat_constants_gsat(as); } expr * get_unsat_assertion(ptr_vector const & as) { unsigned sz = as.size(); if (sz == 1) { if (m_mpz_manager.neq(get_value(as[0]), m_one)) return as[0]; else return 0; } m_temp_constants.reset(); unsigned pos = -1; if (m_ucb) { value_score vscore; double max = -1.0; // Andreas: Commented things here might be used for track_unsat data structures as done in SLS for SAT. But seems to have no benefit. /* for (unsigned i = 0; i < m_where_false.size(); i++) { expr * e = m_list_false[i]; */ for (unsigned i = 0; i < sz; i++) { expr * e = as[i]; if (m_mpz_manager.neq(get_value(e), m_one)) { vscore = m_scores.find(e); // Andreas: Select the assertion with the greatest ucb score. Potentially add some noise. // double q = vscore.score + m_ucb_constant * sqrt(log((double)m_touched) / vscore.touched); double q = vscore.score + m_ucb_constant * sqrt(log((double)m_touched) / vscore.touched) + m_ucb_noise * get_random_uint(8); if (q > max) { max = q; pos = i; } } } if (pos == static_cast(-1)) return 0; m_touched++; m_scores.find(as[pos]).touched++; // Andreas: Also part of track_unsat data structures. Additionally disable the previous line! /* m_last_pos = pos; m_scores.find(m_list_false[pos]).touched++; return m_list_false[pos]; */ } else { // Andreas: The track_unsat data structures for random assertion selection. /* sz = m_where_false.size(); if (sz == 0) return 0; return m_list_false[get_random_uint(16) % sz]; */ unsigned cnt_unsat = 0; for (unsigned i = 0; i < sz; i++) if (m_mpz_manager.neq(get_value(as[i]), m_one) && (get_random_uint(16) % ++cnt_unsat == 0)) pos = i; if (pos == static_cast(-1)) return 0; } m_last_pos = pos; return as[pos]; } expr * get_new_unsat_assertion(ptr_vector const & as) { unsigned sz = as.size(); if (sz == 1) return 0; m_temp_constants.reset(); unsigned cnt_unsat = 0, pos = -1; for (unsigned i = 0; i < sz; i++) if ((i != m_last_pos) && m_mpz_manager.neq(get_value(as[i]), m_one) && (get_random_uint(16) % ++cnt_unsat == 0)) pos = i; if (pos == static_cast(-1)) return 0; return as[pos]; } }; #endif z3-4.4.0/src/tactic/sls/bvsls_opt_engine.h0000644000175000017500000000435512540347414020224 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: bvsls_opt_engine.h Abstract: Optimization extensions to bvsls Author: Christoph (cwinter) 2014-03-28 Notes: --*/ #ifndef _BVSLS_OPT_ENGINE_H_ #define _BVSLS_OPT_ENGINE_H_ #include "sls_engine.h" class bvsls_opt_engine : public sls_engine { sls_tracker & m_hard_tracker; sls_tracker m_obj_tracker; sls_evaluator m_obj_evaluator; model_ref m_best_model; mpz m_best_model_score; unsigned m_obj_bv_sz; expr * m_obj_e; public: bvsls_opt_engine(ast_manager & m, params_ref const & p); ~bvsls_opt_engine(); class optimization_result { public: lbool is_sat; expr_ref optimum; optimization_result(ast_manager & m) : is_sat(l_undef), optimum(m) {} optimization_result& operator=(optimization_result const& other) { is_sat = other.is_sat; optimum = other.optimum; return *this; } optimization_result(optimization_result const& other): is_sat(other.is_sat), optimum(other.optimum) { } }; optimization_result optimize(expr_ref const & objective, model_ref initial_model = model_ref(), bool maximize=true); void get_model(model_ref & result) { result = m_best_model; } protected: void setup_opt_tracker(expr_ref const & objective, bool _max); expr_ref maximize(); bool what_if(func_decl * fd, const unsigned & fd_inx, const mpz & temp, mpz & best_score, unsigned & best_const, mpz & best_value); mpz find_best_move(ptr_vector & to_evaluate, mpz score, unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move, mpz const & max_score, expr * objective); mpz top_score(void) { mpz res(0); obj_hashtable const & top_exprs = m_obj_tracker.get_top_exprs(); for (obj_hashtable::iterator it = top_exprs.begin(); it != top_exprs.end(); it++) m_mpz_manager.add(res, m_obj_tracker.get_value(*it), res); return res; } void save_model(mpz const & score); bool randomize_wrt_hard(); }; #endif z3-4.4.0/src/tactic/sls/sls_engine.cpp0000644000175000017500000005147012540347414017345 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: sls_engine.cpp Abstract: A Stochastic Local Search (SLS) engine Author: Christoph (cwinter) 2014-03-19 Notes: --*/ #include // Need DBL_MAX #include"map.h" #include"ast_smt2_pp.h" #include"ast_pp.h" #include"var_subst.h" #include"model_pp.h" #include"tactic.h" #include"cooperate.h" #include"luby.h" #include"sls_params.hpp" #include"sls_engine.h" sls_engine::sls_engine(ast_manager & m, params_ref const & p) : m_manager(m), m_powers(m_mpz_manager), m_zero(m_mpz_manager.mk_z(0)), m_one(m_mpz_manager.mk_z(1)), m_two(m_mpz_manager.mk_z(2)), m_cancel(false), m_bv_util(m), m_tracker(m, m_bv_util, m_mpz_manager, m_powers), m_evaluator(m, m_bv_util, m_tracker, m_mpz_manager, m_powers) { updt_params(p); m_tracker.updt_params(p); } sls_engine::~sls_engine() { m_mpz_manager.del(m_zero); m_mpz_manager.del(m_one); m_mpz_manager.del(m_two); } void sls_engine::updt_params(params_ref const & _p) { sls_params p(_p); m_produce_models = _p.get_bool("model", false); m_max_restarts = p.max_restarts(); m_tracker.set_random_seed(p.random_seed()); m_walksat = p.walksat(); m_walksat_repick = p.walksat_repick(); m_paws_sp = p.paws_sp(); m_paws = m_paws_sp < 1024; m_wp = p.wp(); m_vns_mc = p.vns_mc(); m_vns_repick = p.vns_repick(); m_restart_base = p.restart_base(); m_restart_next = m_restart_base; m_restart_init = p.restart_init(); m_early_prune = p.early_prune(); m_random_offset = p.random_offset(); m_rescore = p.rescore(); // Andreas: Would cause trouble because repick requires an assertion being picked before which is not the case in GSAT. if (m_walksat_repick && !m_walksat) NOT_IMPLEMENTED_YET(); if (m_vns_repick && !m_walksat) NOT_IMPLEMENTED_YET(); } void sls_engine::collect_statistics(statistics& st) const { double seconds = m_stats.m_stopwatch.get_current_seconds(); st.update("sls restarts", m_stats.m_restarts); st.update("sls full evals", m_stats.m_full_evals); st.update("sls incr evals", m_stats.m_incr_evals); st.update("sls incr evals/sec", m_stats.m_incr_evals / seconds); st.update("sls FLIP moves", m_stats.m_flips); st.update("sls INC moves", m_stats.m_incs); st.update("sls DEC moves", m_stats.m_decs); st.update("sls INV moves", m_stats.m_invs); st.update("sls moves", m_stats.m_moves); st.update("sls moves/sec", m_stats.m_moves / seconds); } void sls_engine::checkpoint() { if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); cooperate("sls"); } bool sls_engine::full_eval(model & mdl) { bool res = true; unsigned sz = m_assertions.size(); for (unsigned i = 0; i < sz && res; i++) { checkpoint(); expr_ref o(m_manager); if (!mdl.eval(m_assertions[i], o, true)) exit(ERR_INTERNAL_FATAL); res = m_manager.is_true(o.get()); } TRACE("sls", tout << "Evaluation: " << res << std::endl;); return res; } double sls_engine::top_score() { double top_sum = 0.0; unsigned sz = m_assertions.size(); for (unsigned i = 0; i < sz; i++) { expr * e = m_assertions[i]; top_sum += m_tracker.get_score(e); } TRACE("sls_top", tout << "Score distribution:"; for (unsigned i = 0; i < sz; i++) tout << " " << m_tracker.get_score(m_assertions[i]); tout << " AVG: " << top_sum / (double)sz << std::endl;); m_tracker.set_top_sum(top_sum); return top_sum; } double sls_engine::rescore() { m_evaluator.update_all(); m_stats.m_full_evals++; return top_score(); } double sls_engine::serious_score(func_decl * fd, const mpz & new_value) { m_evaluator.serious_update(fd, new_value); m_stats.m_incr_evals++; return m_tracker.get_top_sum(); } double sls_engine::incremental_score(func_decl * fd, const mpz & new_value) { m_evaluator.update(fd, new_value); m_stats.m_incr_evals++; return m_tracker.get_top_sum(); } double sls_engine::incremental_score_prune(func_decl * fd, const mpz & new_value) { m_stats.m_incr_evals++; if (m_evaluator.update_prune(fd, new_value)) return m_tracker.get_top_sum(); else return -DBL_MAX; } // checks whether the score outcome of a given move is better than the previous score bool sls_engine::what_if( func_decl * fd, const unsigned & fd_inx, const mpz & temp, double & best_score, unsigned & best_const, mpz & best_value) { #ifdef Z3DEBUG mpz old_value; m_mpz_manager.set(old_value, m_tracker.get_value(fd)); #endif double r; if (m_early_prune) r = incremental_score_prune(fd, temp); else r = incremental_score(fd, temp); #ifdef Z3DEBUG TRACE("sls_whatif", tout << "WHAT IF " << fd->get_name() << " WERE " << m_mpz_manager.to_string(temp) << " --> " << r << std::endl;); m_mpz_manager.del(old_value); #endif // Andreas: Had this idea on my last day. Maybe we could add a noise here similar to the one that worked so well for ucb assertion selection. // r += 0.0001 * m_tracker.get_random_uint(8); // Andreas: For some reason it is important to use > here instead of >=. Probably related to prefering the LSB. if (r > best_score) { best_score = r; best_const = fd_inx; m_mpz_manager.set(best_value, temp); return true; } return false; } void sls_engine::mk_add(unsigned bv_sz, const mpz & old_value, mpz & add_value, mpz & result) { mpz temp, mask, mask2; m_mpz_manager.add(old_value, add_value, temp); m_mpz_manager.set(mask, m_powers(bv_sz)); m_mpz_manager.bitwise_not(bv_sz, mask, mask2); m_mpz_manager.bitwise_and(temp, mask2, result); m_mpz_manager.del(temp); m_mpz_manager.del(mask); m_mpz_manager.del(mask2); } void sls_engine::mk_inc(unsigned bv_sz, const mpz & old_value, mpz & incremented) { unsigned shift; m_mpz_manager.add(old_value, m_one, incremented); if (m_mpz_manager.is_power_of_two(incremented, shift) && shift == bv_sz) m_mpz_manager.set(incremented, m_zero); } void sls_engine::mk_dec(unsigned bv_sz, const mpz & old_value, mpz & decremented) { if (m_mpz_manager.is_zero(old_value)) { m_mpz_manager.set(decremented, m_powers(bv_sz)); m_mpz_manager.dec(decremented); } else m_mpz_manager.sub(old_value, m_one, decremented); } void sls_engine::mk_inv(unsigned bv_sz, const mpz & old_value, mpz & inverted) { m_mpz_manager.bitwise_not(bv_sz, old_value, inverted); } void sls_engine::mk_flip(sort * s, const mpz & old_value, unsigned bit, mpz & flipped) { m_mpz_manager.set(flipped, m_zero); if (m_bv_util.is_bv_sort(s)) { mpz mask; m_mpz_manager.set(mask, m_powers(bit)); m_mpz_manager.bitwise_xor(old_value, mask, flipped); m_mpz_manager.del(mask); } else if (m_manager.is_bool(s)) m_mpz_manager.set(flipped, (m_mpz_manager.is_zero(old_value)) ? m_one : m_zero); else NOT_IMPLEMENTED_YET(); } void sls_engine::mk_random_move(ptr_vector & unsat_constants) { unsigned rnd_mv = 0; unsigned ucc = unsat_constants.size(); unsigned rc = (m_tracker.get_random_uint((ucc < 16) ? 4 : (ucc < 256) ? 8 : (ucc < 4096) ? 12 : (ucc < 65536) ? 16 : 32)) % ucc; func_decl * fd = unsat_constants[rc]; mpz new_value; sort * srt = fd->get_range(); if (m_manager.is_bool(srt)) m_mpz_manager.set(new_value, (m_mpz_manager.is_zero(m_tracker.get_value(fd))) ? m_one : m_zero); else { if (m_mpz_manager.is_one(m_tracker.get_random_bool())) rnd_mv = 2; if (m_mpz_manager.is_one(m_tracker.get_random_bool())) rnd_mv++; // Andreas: The other option would be to scale the probability for flips according to the bit-width. /* unsigned bv_sz2 = m_bv_util.get_bv_size(srt); rnd_mv = m_tracker.get_random_uint(16) % (bv_sz2 + 3); if (rnd_mv > 3) rnd_mv = 0; */ move_type mt = (move_type)rnd_mv; // Andreas: Christoph claimed inversion doesn't make sense, let's do a flip instead. Is this really true? if (mt == MV_INV) mt = MV_FLIP; unsigned bit = 0; switch (mt) { case MV_FLIP: { unsigned bv_sz = m_bv_util.get_bv_size(srt); bit = (m_tracker.get_random_uint((bv_sz < 16) ? 4 : (bv_sz < 256) ? 8 : (bv_sz < 4096) ? 12 : (bv_sz < 65536) ? 16 : 32)) % bv_sz; mk_flip(fd->get_range(), m_tracker.get_value(fd), bit, new_value); break; } case MV_INC: mk_inc(m_bv_util.get_bv_size(fd->get_range()), m_tracker.get_value(fd), new_value); break; case MV_DEC: mk_dec(m_bv_util.get_bv_size(fd->get_range()), m_tracker.get_value(fd), new_value); break; case MV_INV: mk_inv(m_bv_util.get_bv_size(fd->get_range()), m_tracker.get_value(fd), new_value); break; default: NOT_IMPLEMENTED_YET(); } TRACE("sls", tout << "Randomization candidates: "; for (unsigned i = 0; i < unsat_constants.size(); i++) tout << unsat_constants[i]->get_name() << ", "; tout << std::endl; tout << "Random move: "; switch (mt) { case MV_FLIP: tout << "Flip #" << bit << " in " << fd->get_name() << std::endl; break; case MV_INC: tout << "+1 for " << fd->get_name() << std::endl; break; case MV_DEC: tout << "-1 for " << fd->get_name() << std::endl; break; case MV_INV: tout << "NEG for " << fd->get_name() << std::endl; break; } tout << "Locally randomized model: " << std::endl; m_tracker.show_model(tout);); } m_evaluator.serious_update(fd, new_value); m_mpz_manager.del(new_value); } // finds the move that increased score the most. returns best_const = -1, if no increasing move exists. double sls_engine::find_best_move( ptr_vector & to_evaluate, double score, unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move) { mpz old_value, temp; unsigned bv_sz; double new_score = score; // Andreas: Introducting a bit of randomization by using a random offset and a random direction to go through the candidate list. unsigned sz = to_evaluate.size(); unsigned offset = (m_random_offset) ? m_tracker.get_random_uint(16) % sz : 0; for (unsigned j = 0; j < sz; j++) { unsigned i = j + offset; if (i >= sz) i -= sz; //for (unsigned i = 0; i < to_evaluate.size(); i++) { func_decl * fd = to_evaluate[i]; sort * srt = fd->get_range(); bv_sz = (m_manager.is_bool(srt)) ? 1 : m_bv_util.get_bv_size(srt); m_mpz_manager.set(old_value, m_tracker.get_value(fd)); // first try to flip every bit for (unsigned j = 0; j < bv_sz; j++) { // What would happen if we flipped bit #i ? mk_flip(srt, old_value, j, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) { new_bit = j; move = MV_FLIP; } } if (m_bv_util.is_bv_sort(srt) && bv_sz > 1) { if (!m_mpz_manager.is_even(old_value)) { // for odd values, try +1 mk_inc(bv_sz, old_value, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_INC; } else { // for even values, try -1 mk_dec(bv_sz, old_value, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_DEC; } // try inverting mk_inv(bv_sz, old_value, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_INV; } // reset to what it was before incremental_score(fd, old_value); } m_mpz_manager.del(old_value); m_mpz_manager.del(temp); return new_score; } // finds the move that increased score the most. returns best_const = -1, if no increasing move exists. double sls_engine::find_best_move_mc(ptr_vector & to_evaluate, double score, unsigned & best_const, mpz & best_value) { mpz old_value, temp, temp2; unsigned bv_sz; double new_score = score; // Andreas: Introducting a bit of randomization by using a random offset and a random direction to go through the candidate list. unsigned sz = to_evaluate.size(); unsigned offset = (m_random_offset) ? m_tracker.get_random_uint(16) % sz : 0; for (unsigned j = 0; j < sz; j++) { unsigned i = j + offset; if (i >= sz) i -= sz; //for (unsigned i = 0; i < to_evaluate.size(); i++) { func_decl * fd = to_evaluate[i]; sort * srt = fd->get_range(); bv_sz = (m_manager.is_bool(srt)) ? 1 : m_bv_util.get_bv_size(srt); m_mpz_manager.set(old_value, m_tracker.get_value(fd)); if (m_bv_util.is_bv_sort(srt) && bv_sz > 2) { for (unsigned j = 0; j < bv_sz; j++) { mk_flip(srt, old_value, j, temp); for (unsigned l = 0; l < m_vns_mc && l < bv_sz / 2; l++) { unsigned k = m_tracker.get_random_uint(16) % bv_sz; while (k == j) k = m_tracker.get_random_uint(16) % bv_sz; mk_flip(srt, temp, k, temp2); what_if(fd, i, temp2, new_score, best_const, best_value); } } } // reset to what it was before incremental_score(fd, old_value); } m_mpz_manager.del(old_value); m_mpz_manager.del(temp); m_mpz_manager.del(temp2); return new_score; } // main search loop lbool sls_engine::search() { lbool res = l_undef; double score = 0.0, old_score = 0.0; unsigned new_const = (unsigned)-1, new_bit; mpz new_value; move_type move; score = rescore(); unsigned sz = m_assertions.size(); while (check_restart(m_stats.m_moves)) { checkpoint(); m_stats.m_moves++; // Andreas: Every base restart interval ... if (m_stats.m_moves % m_restart_base == 0) { // ... potentially smooth the touched counters ... m_tracker.ucb_forget(m_assertions); // ... or normalize the top-level score. if (m_rescore) score = rescore(); } // get candidate variables ptr_vector & to_evaluate = m_tracker.get_unsat_constants(m_assertions); if (!to_evaluate.size()) { res = l_true; goto bailout; } // random walk with probability wp / 1024 if (m_wp && m_tracker.get_random_uint(10) < m_wp) { mk_random_move(to_evaluate); score = m_tracker.get_top_sum(); continue; } old_score = score; new_const = (unsigned)-1; // find best increasing move score = find_best_move(to_evaluate, score, new_const, new_value, new_bit, move); // use Monte Carlo 2-bit-flip sampling if no increasing move was found previously if (m_vns_mc && (new_const == static_cast(-1))) score = find_best_move_mc(to_evaluate, score, new_const, new_value); // repick assertion if no increasing move was found previously if (m_vns_repick && (new_const == static_cast(-1))) { expr * q = m_tracker.get_new_unsat_assertion(m_assertions); // only apply if another unsatisfied assertion actually exists if (q) { ptr_vector & to_evaluate2 = m_tracker.get_unsat_constants_walksat(q); score = find_best_move(to_evaluate2, score, new_const, new_value, new_bit, move); if (new_const != static_cast(-1)) { func_decl * fd = to_evaluate2[new_const]; score = serious_score(fd, new_value); continue; } } } // randomize if no increasing move was found if (new_const == static_cast(-1)) { score = old_score; if (m_walksat_repick) m_evaluator.randomize_local(m_assertions); else m_evaluator.randomize_local(to_evaluate); score = m_tracker.get_top_sum(); // update assertion weights if a weigthing is enabled (sp < 1024) if (m_paws) { for (unsigned i = 0; i < sz; i++) { expr * q = m_assertions[i]; // smooth weights with probability sp / 1024 if (m_tracker.get_random_uint(10) < m_paws_sp) { if (m_mpz_manager.eq(m_tracker.get_value(q),m_one)) m_tracker.decrease_weight(q); } // increase weights otherwise else { if (m_mpz_manager.eq(m_tracker.get_value(q),m_zero)) m_tracker.increase_weight(q); } } } } // otherwise, apply most increasing move else { func_decl * fd = to_evaluate[new_const]; score = serious_score(fd, new_value); } } bailout: m_mpz_manager.del(new_value); return res; } void sls_engine::operator()(goal_ref const & g, model_converter_ref & mc) { if (g->inconsistent()) { mc = 0; return; } m_produce_models = g->models_enabled(); for (unsigned i = 0; i < g->size(); i++) assert_expr(g->form(i)); lbool res = operator()(); if (res == l_true) { report_tactic_progress("Number of flips:", m_stats.m_moves); for (unsigned i = 0; i < g->size(); i++) if (!m_mpz_manager.is_one(m_tracker.get_value(g->form(i)))) { verbose_stream() << "Terminated before all assertions were SAT!" << std::endl; NOT_IMPLEMENTED_YET(); } if (m_produce_models) { model_ref mdl = m_tracker.get_model(); mc = model2model_converter(mdl.get()); TRACE("sls_model", mc->display(tout);); } g->reset(); } else mc = 0; } lbool sls_engine::operator()() { m_tracker.initialize(m_assertions); m_tracker.reset(m_assertions); if (m_restart_init) m_tracker.randomize(m_assertions); lbool res = l_undef; do { checkpoint(); report_tactic_progress("Searching... restarts left:", m_max_restarts - m_stats.m_restarts); res = search(); if (res == l_undef) { if (m_restart_init) m_tracker.randomize(m_assertions); else m_tracker.reset(m_assertions); } } while (res != l_true && m_stats.m_restarts++ < m_max_restarts); verbose_stream() << "(restarts: " << m_stats.m_restarts << " flips: " << m_stats.m_moves << " fps: " << (m_stats.m_moves / m_stats.m_stopwatch.get_current_seconds()) << ")" << std::endl; return res; } /* Andreas: Needed for Armin's restart scheme if we don't want to use loops. double sls_engine::get_restart_armin(unsigned cnt_restarts) { unsigned outer_id = (unsigned)(0.5 + sqrt(0.25 + 2 * cnt_restarts)); unsigned inner_id = cnt_restarts - (outer_id - 1) * outer_id / 2; return pow((double) _RESTART_CONST_ARMIN_, (int) inner_id + 1); } */ unsigned sls_engine::check_restart(unsigned curr_value) { if (curr_value > m_restart_next) { /* Andreas: My own scheme (= 1) seems to work best. Other schemes are disabled so that we save one parameter. I leave the other versions as comments in case you want to try it again somewhen. #if _RESTART_SCHEME_ == 5 m_restart_next += (unsigned)(m_restart_base * pow(_RESTART_CONST_ARMIN_, m_stats.m_restarts)); #elif _RESTART_SCHEME_ == 4 m_restart_next += (m_stats.m_restarts & (m_stats.m_restarts + 1)) ? m_restart_base : (m_restart_base * m_stats.m_restarts + 1); #elif _RESTART_SCHEME_ == 3 m_restart_next += (unsigned)get_restart_armin(m_stats.m_restarts + 1) * m_restart_base; #elif _RESTART_SCHEME_ == 2 m_restart_next += get_luby(m_stats.m_restarts + 1) * m_restart_base; #elif _RESTART_SCHEME_ == 1 if (m_stats.m_restarts & 1) m_restart_next += m_restart_base; else m_restart_next += (2 << (m_stats.m_restarts >> 1)) * m_restart_base; #else m_restart_limit += m_restart_base; #endif */ if (m_stats.m_restarts & 1) m_restart_next += m_restart_base; else m_restart_next += (2 << (m_stats.m_restarts >> 1)) * m_restart_base; return 0; } return 1; } z3-4.4.0/src/tactic/sls/sls_tactic.h0000644000175000017500000000076712540347414017017 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: sls_tactic.h Abstract: A Stochastic Local Search (SLS) tactic Author: Christoph (cwinter) 2012-02-29 Notes: --*/ #ifndef _SLS_TACTIC_H_ #define _SLS_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_qfbv_sls_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qfbv-sls", "(try to) solve using stochastic local search for QF_BV.", "mk_qfbv_sls_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/sls/sls_evaluator.h0000644000175000017500000010026212540347414017541 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: sls_evaluator.h Abstract: SLS Evaluator Author: Christoph (cwinter) 2012-02-29 Notes: --*/ #ifndef _SLS_EVALUATOR_H_ #define _SLS_EVALUATOR_H_ #include"model_evaluator.h" #include"sls_powers.h" #include"sls_tracker.h" class sls_evaluator { ast_manager & m_manager; bv_util & m_bv_util; family_id m_basic_fid; family_id m_bv_fid; sls_tracker & m_tracker; unsynch_mpz_manager & m_mpz_manager; mpz m_zero, m_one, m_two; powers & m_powers; expr_ref_buffer m_temp_exprs; vector > m_traversal_stack; vector > m_traversal_stack_bool; public: sls_evaluator(ast_manager & m, bv_util & bvu, sls_tracker & t, unsynch_mpz_manager & mm, powers & p) : m_manager(m), m_bv_util(bvu), m_tracker(t), m_mpz_manager(mm), m_zero(m_mpz_manager.mk_z(0)), m_one(m_mpz_manager.mk_z(1)), m_two(m_mpz_manager.mk_z(2)), m_powers(p), m_temp_exprs(m) { m_bv_fid = m_bv_util.get_family_id(); m_basic_fid = m_manager.get_basic_family_id(); } ~sls_evaluator() { m_mpz_manager.del(m_zero); m_mpz_manager.del(m_one); m_mpz_manager.del(m_two); } void operator()(app * n, mpz & result) { family_id nfid = n->get_family_id(); func_decl * fd = n->get_decl(); unsigned n_args = n->get_num_args(); if (n_args == 0) { m_mpz_manager.set(result, m_tracker.get_value(n)); return; } expr * const * args = n->get_args(); m_mpz_manager.set(result, m_zero); if (nfid == m_basic_fid) { switch (n->get_decl_kind()) { case OP_AND: { m_mpz_manager.set(result, m_one); for (unsigned i = 0; i < n_args; i++) if (m_mpz_manager.neq(m_tracker.get_value(args[i]), result)) { m_mpz_manager.set(result, m_zero); break; } break; } case OP_OR: { for (unsigned i = 0; i < n_args; i++) if (m_mpz_manager.neq(m_tracker.get_value(args[i]), result)) { m_mpz_manager.set(result, m_one); break; } break; } case OP_NOT: { SASSERT(n_args == 1); const mpz & child = m_tracker.get_value(args[0]); SASSERT(m_mpz_manager.is_one(child) || m_mpz_manager.is_zero(child)); m_mpz_manager.set(result, (m_mpz_manager.is_zero(child)) ? m_one : m_zero); break; } case OP_EQ: { SASSERT(n_args >= 2); m_mpz_manager.set(result, m_one); const mpz & first = m_tracker.get_value(args[0]); for (unsigned i = 1; i < n_args; i++) if (m_mpz_manager.neq(m_tracker.get_value(args[i]), first)) { m_mpz_manager.set(result, m_zero); break; } break; } case OP_DISTINCT: { m_mpz_manager.set(result, m_one); for (unsigned i = 0; i < n_args && m_mpz_manager.is_one(result); i++) { for (unsigned j = i+1; j < n_args && m_mpz_manager.is_one(result); j++) { if (m_mpz_manager.eq(m_tracker.get_value(args[i]), m_tracker.get_value(args[j]))) m_mpz_manager.set(result, m_zero); } } break; } case OP_ITE: { SASSERT(n_args = 3); if (m_mpz_manager.is_one(m_tracker.get_value(args[0]))) m_mpz_manager.set(result, m_tracker.get_value(args[1])); else m_mpz_manager.set(result, m_tracker.get_value(args[2])); break; } default: NOT_IMPLEMENTED_YET(); } } else if (nfid == m_bv_fid) { bv_op_kind k = static_cast(fd->get_decl_kind()); switch(k) { case OP_CONCAT: { SASSERT(n_args >= 2); for (unsigned i = 0; i < n_args; i++) { if (i != 0) { const mpz & p = m_powers(m_bv_util.get_bv_size(args[i])); m_mpz_manager.mul(result, p, result); } m_mpz_manager.add(result, m_tracker.get_value(args[i]), result); } break; } case OP_EXTRACT: { SASSERT(n_args == 1); const mpz & child = m_tracker.get_value(args[0]); unsigned h = m_bv_util.get_extract_high(n); unsigned l = m_bv_util.get_extract_low(n); m_mpz_manager.rem(child, m_powers(h+1), result); // result = [h:0] of child m_mpz_manager.machine_div2k(result, l, result); break; } case OP_BADD: { SASSERT(n_args >= 2); for (unsigned i = 0; i < n_args; i++) { const mpz & next = m_tracker.get_value(args[i]); m_mpz_manager.add(result, next, result); } const mpz & p = m_powers(m_bv_util.get_bv_size(n)); m_mpz_manager.rem(result, p, result); break; } case OP_BSUB: { SASSERT(n_args == 2); const mpz & p = m_powers(m_bv_util.get_bv_size(n)); mpz temp; m_mpz_manager.sub(m_tracker.get_value(args[0]), m_tracker.get_value(args[1]), temp); m_mpz_manager.mod(temp, p, result); m_mpz_manager.del(temp); break; } case OP_BMUL: { SASSERT(n_args >= 2); m_mpz_manager.set(result, m_tracker.get_value(args[0])); for (unsigned i = 1; i < n_args; i++) { const mpz & next = m_tracker.get_value(args[i]); m_mpz_manager.mul(result, next, result); } const mpz & p = m_powers(m_bv_util.get_bv_size(n)); m_mpz_manager.rem(result, p, result); break; } case OP_BNEG: { // 2's complement unary minus SASSERT(n_args == 1); const mpz & child = m_tracker.get_value(args[0]); if (m_mpz_manager.is_zero(child)) { m_mpz_manager.set(result, m_zero); } else { unsigned bv_sz = m_bv_util.get_bv_size(n); m_mpz_manager.bitwise_not(bv_sz, child, result); m_mpz_manager.inc(result); // can't overflow } break; } case OP_BSDIV: case OP_BSDIV0: case OP_BSDIV_I: { SASSERT(n_args == 2); mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); SASSERT(m_mpz_manager.is_nonneg(x) && m_mpz_manager.is_nonneg(y)); unsigned bv_sz = m_bv_util.get_bv_size(args[0]); const mpz & p = m_powers(bv_sz); const mpz & p_half = m_powers(bv_sz-1); if (x >= p_half) { m_mpz_manager.sub(x, p, x); } if (y >= p_half) { m_mpz_manager.sub(y, p, y); } if (m_mpz_manager.is_zero(y)) { if (m_mpz_manager.is_neg(x)) m_mpz_manager.set(result, m_one); else { m_mpz_manager.set(result, m_powers(m_bv_util.get_bv_size(n))); m_mpz_manager.dec(result); } } else { m_mpz_manager.machine_div(x, y, result); } if (m_mpz_manager.is_neg(result)) m_mpz_manager.add(result, p, result); m_mpz_manager.del(x); m_mpz_manager.del(y); break; } case OP_BUDIV: case OP_BUDIV0: case OP_BUDIV_I: { SASSERT(n_args == 2); mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); if (m_mpz_manager.is_zero(y)) { m_mpz_manager.set(result, m_powers(m_bv_util.get_bv_size(n))); m_mpz_manager.dec(result); } else { m_mpz_manager.machine_div(x, y, result); } m_mpz_manager.del(x); m_mpz_manager.del(y); break; } case OP_BSREM: case OP_BSREM0: case OP_BSREM_I: { SASSERT(n_args == 2); mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); unsigned bv_sz = m_bv_util.get_bv_size(args[0]); const mpz & p = m_powers(bv_sz); const mpz & p_half = m_powers(bv_sz-1); if (x >= p_half) { m_mpz_manager.sub(x, p, x); } if (y >= p_half) { m_mpz_manager.sub(y, p, y); } if (m_mpz_manager.is_zero(y)) { m_mpz_manager.set(result, x); } else { m_mpz_manager.rem(x, y, result); } if (m_mpz_manager.is_neg(result)) m_mpz_manager.add(result, p, result); m_mpz_manager.del(x); m_mpz_manager.del(y); break; } case OP_BUREM: case OP_BUREM0: case OP_BUREM_I: { SASSERT(n_args == 2); mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); if (m_mpz_manager.is_zero(y)) { m_mpz_manager.set(result, x); } else { m_mpz_manager.mod(x, y, result); } m_mpz_manager.del(x); m_mpz_manager.del(y); break; } case OP_BSMOD: case OP_BSMOD0: case OP_BSMOD_I:{ SASSERT(n_args == 2); mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); unsigned bv_sz = m_bv_util.get_bv_size(args[0]); const mpz & p = m_powers(bv_sz); const mpz & p_half = m_powers(bv_sz-1); if (x >= p_half) { m_mpz_manager.sub(x, p, x); } if (y >= p_half) { m_mpz_manager.sub(y, p, y); } if (m_mpz_manager.is_zero(y)) m_mpz_manager.set(result, x); else { bool neg_x = m_mpz_manager.is_neg(x); bool neg_y = m_mpz_manager.is_neg(y); mpz abs_x, abs_y; m_mpz_manager.set(abs_x, x); m_mpz_manager.set(abs_y, y); if (neg_x) m_mpz_manager.neg(abs_x); if (neg_y) m_mpz_manager.neg(abs_y); SASSERT(m_mpz_manager.is_nonneg(abs_x) && m_mpz_manager.is_nonneg(abs_y)); m_mpz_manager.mod(abs_x, abs_y, result); if (m_mpz_manager.is_zero(result) || (!neg_x && !neg_y)) { /* Nothing */ } else if (neg_x && !neg_y) { m_mpz_manager.neg(result); m_mpz_manager.add(result, y, result); } else if (!neg_x && neg_y) { m_mpz_manager.add(result, y, result); } else { m_mpz_manager.neg(result); } m_mpz_manager.del(abs_x); m_mpz_manager.del(abs_y); } if (m_mpz_manager.is_neg(result)) m_mpz_manager.add(result, p, result); m_mpz_manager.del(x); m_mpz_manager.del(y); break; } case OP_BAND: { SASSERT(n_args >= 2); m_mpz_manager.set(result, m_tracker.get_value(args[0])); for (unsigned i = 1; i < n_args; i++) m_mpz_manager.bitwise_and(result, m_tracker.get_value(args[i]), result); break; } case OP_BOR: { SASSERT(n_args >= 2); m_mpz_manager.set(result, m_tracker.get_value(args[0])); for (unsigned i = 1; i < n_args; i++) { m_mpz_manager.bitwise_or(result, m_tracker.get_value(args[i]), result); } break; } case OP_BXOR: { SASSERT(n_args >= 2); m_mpz_manager.set(result, m_tracker.get_value(args[0])); for (unsigned i = 1; i < n_args; i++) m_mpz_manager.bitwise_xor(result, m_tracker.get_value(args[i]), result); break; } case OP_BNAND: { SASSERT(n_args >= 2); mpz temp; unsigned bv_sz = m_bv_util.get_bv_size(n); m_mpz_manager.set(result, m_tracker.get_value(args[0])); for (unsigned i = 1; i < n_args; i++) { m_mpz_manager.bitwise_and(result, m_tracker.get_value(args[i]), temp); m_mpz_manager.bitwise_not(bv_sz, temp, result); } m_mpz_manager.del(temp); break; } case OP_BNOR: { SASSERT(n_args >= 2); mpz temp; unsigned bv_sz = m_bv_util.get_bv_size(n); m_mpz_manager.set(result, m_tracker.get_value(args[0])); for (unsigned i = 1; i < n_args; i++) { m_mpz_manager.bitwise_or(result, m_tracker.get_value(args[i]), temp); m_mpz_manager.bitwise_not(bv_sz, temp, result); } m_mpz_manager.del(temp); break; } case OP_BNOT: { SASSERT(n_args == 1); m_mpz_manager.bitwise_not(m_bv_util.get_bv_size(args[0]), m_tracker.get_value(args[0]), result); break; } case OP_ULT: case OP_ULEQ: case OP_UGT: case OP_UGEQ: { SASSERT(n_args == 2); const mpz & x = m_tracker.get_value(args[0]); const mpz & y = m_tracker.get_value(args[1]); if ((k == OP_ULT && m_mpz_manager.lt(x, y)) || (k == OP_ULEQ && m_mpz_manager.le(x, y)) || (k == OP_UGT && m_mpz_manager.gt(x, y)) || (k == OP_UGEQ && m_mpz_manager.ge(x, y))) m_mpz_manager.set(result, m_one); break; } case OP_SLT: case OP_SLEQ: case OP_SGT: case OP_SGEQ: { SASSERT(n_args == 2); mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); unsigned bv_sz = m_bv_util.get_bv_size(args[0]); const mpz & p = m_powers(bv_sz); const mpz & p_half = m_powers(bv_sz-1); if (x >= p_half) { m_mpz_manager.sub(x, p, x); } if (y >= p_half) { m_mpz_manager.sub(y, p, y); } if ((k == OP_SLT && m_mpz_manager.lt(x, y)) || (k == OP_SLEQ && m_mpz_manager.le(x, y)) || (k == OP_SGT && m_mpz_manager.gt(x, y)) || (k == OP_SGEQ && m_mpz_manager.ge(x, y))) m_mpz_manager.set(result, m_one); m_mpz_manager.del(x); m_mpz_manager.del(y); break; } case OP_BIT2BOOL: { SASSERT(n_args == 1); const mpz & child = m_tracker.get_value(args[0]); m_mpz_manager.set(result, child); break; } case OP_BASHR: { SASSERT(n_args == 2); m_mpz_manager.set(result, m_tracker.get_value(args[0])); mpz first; const mpz & p = m_powers(m_bv_util.get_bv_size(args[0])-1); m_mpz_manager.bitwise_and(result, p, first); mpz shift; m_mpz_manager.set(shift, m_tracker.get_value(args[1])); mpz temp; while (!m_mpz_manager.is_zero(shift)) { m_mpz_manager.machine_div(result, m_two, temp); m_mpz_manager.add(temp, first, result); m_mpz_manager.dec(shift); } m_mpz_manager.del(first); m_mpz_manager.del(shift); m_mpz_manager.del(temp); break; } case OP_BLSHR: { SASSERT(n_args == 2); m_mpz_manager.set(result, m_tracker.get_value(args[0])); mpz shift; m_mpz_manager.set(shift, m_tracker.get_value(args[1])); while (!m_mpz_manager.is_zero(shift)) { m_mpz_manager.machine_div(result, m_two, result); m_mpz_manager.dec(shift); } m_mpz_manager.del(shift); break; } case OP_BSHL: { SASSERT(n_args == 2); m_mpz_manager.set(result, m_tracker.get_value(args[0])); mpz shift; m_mpz_manager.set(shift, m_tracker.get_value(args[1])); while (!m_mpz_manager.is_zero(shift)) { m_mpz_manager.mul(result, m_two, result); m_mpz_manager.dec(shift); } const mpz & p = m_powers(m_bv_util.get_bv_size(n)); m_mpz_manager.rem(result, p, result); m_mpz_manager.del(shift); break; } case OP_SIGN_EXT: { SASSERT(n_args == 1); m_mpz_manager.set(result, m_tracker.get_value(args[0])); break; } default: NOT_IMPLEMENTED_YET(); } } else { NOT_IMPLEMENTED_YET(); } TRACE("sls_eval", tout << "(" << fd->get_name(); for (unsigned i = 0; i < n_args; i++) tout << " " << m_mpz_manager.to_string(m_tracker.get_value(args[i])); tout << ") ---> " << m_mpz_manager.to_string(result); if (m_manager.is_bool(fd->get_range())) tout << " [Boolean]"; else tout << " [vector size: " << m_bv_util.get_bv_size(fd->get_range()) << "]"; tout << std::endl; ); SASSERT(m_mpz_manager.is_nonneg(result)); } void eval_checked(expr * n, mpz & result) { switch(n->get_kind()) { case AST_APP: { app * a = to_app(n); (*this)(a, result); unsigned n_args = a->get_num_args(); m_temp_exprs.reset(); for (unsigned i = 0; i < n_args; i++) { expr * arg = a->get_arg(i); const mpz & v = m_tracker.get_value(arg); m_temp_exprs.push_back(m_tracker.mpz2value(m_manager.get_sort(arg), v)); } expr_ref q(m_manager), temp(m_manager); q = m_manager.mk_app(a->get_decl(), m_temp_exprs.size(), m_temp_exprs.c_ptr()); model dummy_model(m_manager); model_evaluator evaluator(dummy_model); evaluator(q, temp); mpz check_res; m_tracker.value2mpz(temp, check_res); CTRACE("sls", !m_mpz_manager.eq(check_res, result), tout << "EVAL BUG: IS " << m_mpz_manager.to_string(result) << " SHOULD BE " << m_mpz_manager.to_string(check_res) << std::endl; ); SASSERT(m_mpz_manager.eq(check_res, result)); m_mpz_manager.del(check_res); break; } default: NOT_IMPLEMENTED_YET(); } } void run_serious_update(unsigned cur_depth) { // precondition: m_traversal_stack contains the entry point(s) expr_fast_mark1 visited; mpz new_value; double new_score; SASSERT(cur_depth < m_traversal_stack.size()); while (cur_depth != static_cast(-1)) { ptr_vector & cur_depth_exprs = m_traversal_stack[cur_depth]; for (unsigned i = 0; i < cur_depth_exprs.size(); i++) { expr * cur = cur_depth_exprs[i]; (*this)(to_app(cur), new_value); m_tracker.set_value(cur, new_value); new_score = m_tracker.score(cur); if (m_tracker.is_top_expr(cur)) { m_tracker.adapt_top_sum(cur, new_score, m_tracker.get_score(cur)); if (m_mpz_manager.eq(new_value,m_one)) m_tracker.make_assertion(cur); else m_tracker.break_assertion(cur); } m_tracker.set_score(cur, new_score); m_tracker.set_score_prune(cur, new_score); if (m_tracker.has_uplinks(cur)) { ptr_vector & ups = m_tracker.get_uplinks(cur); for (unsigned j = 0; j < ups.size(); j++) { expr * next = ups[j]; unsigned next_d = m_tracker.get_distance(next); SASSERT(next_d < cur_depth); if (!visited.is_marked(next)) { m_traversal_stack[next_d].push_back(next); visited.mark(next); } } } } cur_depth_exprs.reset(); cur_depth--; } m_mpz_manager.del(new_value); } void run_update(unsigned cur_depth) { // precondition: m_traversal_stack contains the entry point(s) expr_fast_mark1 visited; mpz new_value; double new_score; SASSERT(cur_depth < m_traversal_stack.size()); while (cur_depth != static_cast(-1)) { ptr_vector & cur_depth_exprs = m_traversal_stack[cur_depth]; for (unsigned i = 0; i < cur_depth_exprs.size(); i++) { expr * cur = cur_depth_exprs[i]; (*this)(to_app(cur), new_value); m_tracker.set_value(cur, new_value); new_score = m_tracker.score(cur); if (m_tracker.is_top_expr(cur)) m_tracker.adapt_top_sum(cur, new_score, m_tracker.get_score(cur)); m_tracker.set_score(cur, new_score); if (m_tracker.has_uplinks(cur)) { ptr_vector & ups = m_tracker.get_uplinks(cur); for (unsigned j = 0; j < ups.size(); j++) { expr * next = ups[j]; unsigned next_d = m_tracker.get_distance(next); SASSERT(next_d < cur_depth); if (!visited.is_marked(next)) { m_traversal_stack[next_d].push_back(next); visited.mark(next); } } } } cur_depth_exprs.reset(); cur_depth--; } m_mpz_manager.del(new_value); } void update_all() { unsigned max_depth = 0; sls_tracker::entry_point_type::iterator start = m_tracker.get_entry_points().begin(); sls_tracker::entry_point_type::iterator end = m_tracker.get_entry_points().end(); for (sls_tracker::entry_point_type::iterator it = start; it != end; it++) { expr * ep = m_tracker.get_entry_point(it->m_key); unsigned cur_depth = m_tracker.get_distance(ep); if (m_traversal_stack.size() <= cur_depth) m_traversal_stack.resize(cur_depth+1); m_traversal_stack[cur_depth].push_back(ep); if (cur_depth > max_depth) max_depth = cur_depth; } run_serious_update(max_depth); } void update(func_decl * fd, const mpz & new_value) { m_tracker.set_value(fd, new_value); expr * ep = m_tracker.get_entry_point(fd); unsigned cur_depth = m_tracker.get_distance(ep); if (m_traversal_stack.size() <= cur_depth) m_traversal_stack.resize(cur_depth+1); m_traversal_stack[cur_depth].push_back(ep); run_update(cur_depth); } void serious_update(func_decl * fd, const mpz & new_value) { m_tracker.set_value(fd, new_value); expr * ep = m_tracker.get_entry_point(fd); unsigned cur_depth = m_tracker.get_distance(ep); if (m_traversal_stack.size() <= cur_depth) m_traversal_stack.resize(cur_depth+1); m_traversal_stack[cur_depth].push_back(ep); run_serious_update(cur_depth); } unsigned run_update_bool_prune(unsigned cur_depth) { expr_fast_mark1 visited; double prune_score, new_score; unsigned pot_benefits = 0; SASSERT(cur_depth < m_traversal_stack_bool.size()); ptr_vector & cur_depth_exprs = m_traversal_stack_bool[cur_depth]; for (unsigned i = 0; i < cur_depth_exprs.size(); i++) { expr * cur = cur_depth_exprs[i]; new_score = m_tracker.score(cur); if (m_tracker.is_top_expr(cur)) m_tracker.adapt_top_sum(cur, new_score, m_tracker.get_score(cur)); prune_score = m_tracker.get_score_prune(cur); m_tracker.set_score(cur, new_score); if ((new_score > prune_score) && (m_tracker.has_pos_occ(cur))) pot_benefits = 1; if ((new_score <= prune_score) && (m_tracker.has_neg_occ(cur))) pot_benefits = 1; if (m_tracker.has_uplinks(cur)) { ptr_vector & ups = m_tracker.get_uplinks(cur); for (unsigned j = 0; j < ups.size(); j++) { expr * next = ups[j]; unsigned next_d = m_tracker.get_distance(next); SASSERT(next_d < cur_depth); if (!visited.is_marked(next)) { m_traversal_stack_bool[next_d].push_back(next); visited.mark(next); } } } } cur_depth_exprs.reset(); cur_depth--; while (cur_depth != static_cast(-1)) { ptr_vector & cur_depth_exprs = m_traversal_stack_bool[cur_depth]; if (pot_benefits) { unsigned cur_size = cur_depth_exprs.size(); for (unsigned i = 0; i < cur_size; i++) { expr * cur = cur_depth_exprs[i]; new_score = m_tracker.score(cur); if (m_tracker.is_top_expr(cur)) m_tracker.adapt_top_sum(cur, new_score, m_tracker.get_score(cur)); m_tracker.set_score(cur, new_score); if (m_tracker.has_uplinks(cur)) { ptr_vector & ups = m_tracker.get_uplinks(cur); for (unsigned j = 0; j < ups.size(); j++) { expr * next = ups[j]; unsigned next_d = m_tracker.get_distance(next); SASSERT(next_d < cur_depth); if (!visited.is_marked(next)) { m_traversal_stack_bool[next_d].push_back(next); visited.mark(next); } } } } } cur_depth_exprs.reset(); cur_depth--; } return pot_benefits; } void run_update_prune(unsigned max_depth) { // precondition: m_traversal_stack contains the entry point(s) expr_fast_mark1 visited; mpz new_value; unsigned cur_depth = max_depth; SASSERT(cur_depth < m_traversal_stack.size()); while (cur_depth != static_cast(-1)) { ptr_vector & cur_depth_exprs = m_traversal_stack[cur_depth]; for (unsigned i = 0; i < cur_depth_exprs.size(); i++) { expr * cur = cur_depth_exprs[i]; (*this)(to_app(cur), new_value); m_tracker.set_value(cur, new_value); // Andreas: Should actually always have uplinks ... if (m_tracker.has_uplinks(cur)) { ptr_vector & ups = m_tracker.get_uplinks(cur); for (unsigned j = 0; j < ups.size(); j++) { expr * next = ups[j]; unsigned next_d = m_tracker.get_distance(next); SASSERT(next_d < cur_depth); if (!visited.is_marked(next)) { if (m_manager.is_bool(next)) m_traversal_stack_bool[max_depth].push_back(next); else m_traversal_stack[next_d].push_back(next); visited.mark(next); } } } } cur_depth_exprs.reset(); cur_depth--; } m_mpz_manager.del(new_value); } unsigned update_prune(func_decl * fd, const mpz & new_value) { m_tracker.set_value(fd, new_value); expr * ep = m_tracker.get_entry_point(fd); unsigned cur_depth = m_tracker.get_distance(ep); if (m_traversal_stack_bool.size() <= cur_depth) m_traversal_stack_bool.resize(cur_depth+1); if (m_traversal_stack.size() <= cur_depth) m_traversal_stack.resize(cur_depth+1); if (m_manager.is_bool(ep)) m_traversal_stack_bool[cur_depth].push_back(ep); else { m_traversal_stack[cur_depth].push_back(ep); run_update_prune(cur_depth); } return run_update_bool_prune(cur_depth); } void randomize_local(ptr_vector & unsat_constants) { // Randomize _one_ candidate: unsigned r = m_tracker.get_random_uint(16) % unsat_constants.size(); func_decl * fd = unsat_constants[r]; mpz temp = m_tracker.get_random(fd->get_range()); serious_update(fd, temp); m_mpz_manager.del(temp); TRACE("sls", tout << "Randomization candidate: " << unsat_constants[r]->get_name() << std::endl; tout << "Locally randomized model: " << std::endl; m_tracker.show_model(tout); ); } void randomize_local(expr * e) { randomize_local(m_tracker.get_constants(e)); } void randomize_local(ptr_vector const & as) { randomize_local(m_tracker.get_unsat_constants(as)); } }; #endifz3-4.4.0/src/tactic/sls/sls_engine.h0000644000175000017500000001002712540347414017003 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: sls_engine.h Abstract: A Stochastic Local Search (SLS) engine Author: Christoph (cwinter) 2014-03-19 Notes: --*/ #ifndef _SLS_ENGINE_H_ #define _SLS_ENGINE_H_ #include"stopwatch.h" #include"lbool.h" #include"model_converter.h" #include"goal.h" #include"sls_tracker.h" #include"sls_evaluator.h" #include"statistics.h" class sls_engine { public: class stats { public: unsigned m_restarts; stopwatch m_stopwatch; unsigned m_full_evals; unsigned m_incr_evals; unsigned m_moves, m_flips, m_incs, m_decs, m_invs; stats() : m_restarts(0), m_full_evals(0), m_incr_evals(0), m_moves(0), m_flips(0), m_incs(0), m_decs(0), m_invs(0) { m_stopwatch.reset(); m_stopwatch.start(); } void reset() { m_full_evals = m_flips = m_incr_evals = 0; m_stopwatch.reset(); m_stopwatch.start(); } }; protected: ast_manager & m_manager; stats m_stats; unsynch_mpz_manager m_mpz_manager; powers m_powers; mpz m_zero, m_one, m_two; bool m_produce_models; volatile bool m_cancel; bv_util m_bv_util; sls_tracker m_tracker; sls_evaluator m_evaluator; ptr_vector m_assertions; unsigned m_max_restarts; unsigned m_walksat; unsigned m_walksat_repick; unsigned m_wp; unsigned m_vns_mc; unsigned m_vns_repick; unsigned m_paws; unsigned m_paws_sp; unsigned m_restart_base; unsigned m_restart_next; unsigned m_restart_init; unsigned m_early_prune; unsigned m_random_offset; unsigned m_rescore; typedef enum { MV_FLIP = 0, MV_INC, MV_DEC, MV_INV } move_type; public: sls_engine(ast_manager & m, params_ref const & p); ~sls_engine(); ast_manager & m() const { return m_manager; } void set_cancel(bool f) { m_cancel = f; } void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } void updt_params(params_ref const & _p); void assert_expr(expr * e) { m_assertions.push_back(e); } // stats const & get_stats(void) { return m_stats; } void collect_statistics(statistics & st) const; void reset_statistics(void) { m_stats.reset(); } bool full_eval(model & mdl); void mk_add(unsigned bv_sz, const mpz & old_value, mpz & add_value, mpz & result); void mk_inc(unsigned bv_sz, const mpz & old_value, mpz & incremented); void mk_dec(unsigned bv_sz, const mpz & old_value, mpz & decremented); void mk_inv(unsigned bv_sz, const mpz & old_value, mpz & inverted); void mk_flip(sort * s, const mpz & old_value, unsigned bit, mpz & flipped); lbool search(void); lbool operator()(); void operator()(goal_ref const & g, model_converter_ref & mc); protected: void checkpoint(); bool what_if(func_decl * fd, const unsigned & fd_inx, const mpz & temp, double & best_score, unsigned & best_const, mpz & best_value); double top_score(); double rescore(); double serious_score(func_decl * fd, const mpz & new_value); double incremental_score(func_decl * fd, const mpz & new_value); double incremental_score_prune(func_decl * fd, const mpz & new_value); double find_best_move(ptr_vector & to_evaluate, double score, unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move); double find_best_move_mc(ptr_vector & to_evaluate, double score, unsigned & best_const, mpz & best_value); void mk_random_move(ptr_vector & unsat_constants); //double get_restart_armin(unsigned cnt_restarts); unsigned check_restart(unsigned curr_value); }; #endif z3-4.4.0/src/tactic/aig/0000755000175000017500000000000012540347414014443 5ustar michaelmichaelz3-4.4.0/src/tactic/aig/aig_tactic.cpp0000644000175000017500000000700112540347414017234 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: aig_tactic.cpp Abstract: Tactic for minimizing circuits using AIGs. Author: Leonardo (leonardo) 2011-10-24 Notes: --*/ #include"tactical.h" #include"aig.h" class aig_manager; class aig_tactic : public tactic { unsigned long long m_max_memory; bool m_aig_gate_encoding; bool m_aig_per_assertion; aig_manager * m_aig_manager; struct mk_aig_manager { aig_tactic & m_owner; mk_aig_manager(aig_tactic & o, ast_manager & m):m_owner(o) { aig_manager * mng = alloc(aig_manager, m, o.m_max_memory, o.m_aig_gate_encoding); #pragma omp critical (aig_tactic) { m_owner.m_aig_manager = mng; } } ~mk_aig_manager() { aig_manager * mng = m_owner.m_aig_manager; #pragma omp critical (aig_tactic) { m_owner.m_aig_manager = 0; } dealloc(mng); } }; public: aig_tactic(params_ref const & p = params_ref()):m_aig_manager(0) { updt_params(p); } virtual tactic * translate(ast_manager & m) { aig_tactic * t = alloc(aig_tactic); t->m_max_memory = m_max_memory; t->m_aig_gate_encoding = m_aig_gate_encoding; t->m_aig_per_assertion = m_aig_per_assertion; return t; } virtual void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_aig_gate_encoding = p.get_bool("aig_default_gate_encoding", true); m_aig_per_assertion = p.get_bool("aig_per_assertion", true); } virtual void collect_param_descrs(param_descrs & r) { insert_max_memory(r); r.insert("aig_per_assertion", CPK_BOOL, "(default: true) process one assertion at a time."); } void operator()(goal_ref const & g) { SASSERT(g->is_well_sorted()); tactic_report report("aig", *g); mk_aig_manager mk(*this, g->m()); if (m_aig_per_assertion) { for (unsigned i = 0; i < g->size(); i++) { aig_ref r = m_aig_manager->mk_aig(g->form(i)); m_aig_manager->max_sharing(r); expr_ref new_f(g->m()); m_aig_manager->to_formula(r, new_f); expr_dependency * ed = g->dep(i); g->update(i, new_f, 0, ed); } } else { fail_if_unsat_core_generation("aig", g); aig_ref r = m_aig_manager->mk_aig(*(g.get())); g->reset(); // save memory m_aig_manager->max_sharing(r); m_aig_manager->to_formula(r, *(g.get())); } SASSERT(g->is_well_sorted()); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { fail_if_proof_generation("aig", g); mc = 0; pc = 0; core = 0; operator()(g); g->inc_depth(); result.push_back(g.get()); } virtual void cleanup() {} protected: virtual void set_cancel(bool f) { #pragma omp critical (aig_tactic) { if (m_aig_manager) m_aig_manager->set_cancel(f); } } }; tactic * mk_aig_tactic(params_ref const & p) { return clean(alloc(aig_tactic, p)); } z3-4.4.0/src/tactic/aig/aig.cpp0000644000175000017500000015711612540347414015722 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: aig.cpp Abstract: And-inverted graphs Author: Leonardo (leonardo) 2011-05-13 Notes: --*/ #include"aig.h" #include"goal.h" #include"ast_smt2_pp.h" #include"cooperate.h" #define USE_TWO_LEVEL_RULES #define FIRST_NODE_ID (UINT_MAX/2) struct aig; class aig_lit { friend class aig_ref; aig * m_ref; public: aig_lit(aig * n = 0):m_ref(n) {} aig_lit(aig_ref const & r):m_ref(static_cast(r.m_ref)) {} bool is_inverted() const { return (reinterpret_cast(m_ref) & static_cast(1)) == static_cast(1); } void invert() { m_ref = reinterpret_cast(reinterpret_cast(m_ref) ^ static_cast(1)); } aig * ptr() const { return reinterpret_cast(reinterpret_cast(m_ref) & ~static_cast(1)); } aig * ptr_non_inverted() const { SASSERT(!is_inverted()); return m_ref; } bool is_null() const { return m_ref == 0; } friend bool operator==(aig_lit const & r1, aig_lit const & r2) { return r1.m_ref == r2.m_ref; } friend bool operator!=(aig_lit const & r1, aig_lit const & r2) { return r1.m_ref != r2.m_ref; } aig_lit & operator=(aig_lit const & r) { m_ref = r.m_ref; return *this; } static aig_lit null; }; aig_lit aig_lit::null; struct aig { unsigned m_id; unsigned m_ref_count; aig_lit m_children[2]; unsigned m_mark:1; aig() {} }; inline bool is_true(aig_lit const & r) { return !r.is_inverted() && r.ptr_non_inverted()->m_id == 0; } inline bool is_false(aig_lit const & r) { return r.is_inverted() && r.ptr()->m_id == 0; } inline bool is_var(aig * n) { return n->m_children[0].is_null(); } inline bool is_var(aig_lit const & n) { return is_var(n.ptr()); } inline unsigned id(aig_lit const & n) { return n.ptr()->m_id; } inline unsigned ref_count(aig_lit const & n) { return n.ptr()->m_ref_count; } inline aig_lit left(aig * n) { return n->m_children[0]; } inline aig_lit right(aig * n) { return n->m_children[1]; } inline aig_lit left(aig_lit const & n) { return left(n.ptr()); } inline aig_lit right(aig_lit const & n) { return right(n.ptr()); } inline unsigned to_idx(aig * p) { SASSERT(!is_var(p)); return p->m_id - FIRST_NODE_ID; } void unmark(unsigned sz, aig_lit const * ns) { for (unsigned i = 0; i < sz; i++) { ns[i].ptr()->m_mark = false; } } void unmark(unsigned sz, aig * const * ns) { for (unsigned i = 0; i < sz; i++) { ns[i]->m_mark = false; } } struct aig_hash { unsigned operator()(aig * n) const { SASSERT(!is_var(n)); return hash_u_u(id(n->m_children[0]), id(n->m_children[1])); } }; struct aig_eq { bool operator()(aig * n1, aig * n2) const { SASSERT(!is_var(n1)); SASSERT(!is_var(n2)); return n1->m_children[0] == n2->m_children[0] && n1->m_children[1] == n2->m_children[1]; } }; class aig_table : public chashtable { public: }; struct aig_lit_lt { bool operator()(aig_lit const & l1, aig_lit const & l2) const { if (id(l1) < id(l2)) return true; if (id(l1) == id(l2)) return l1.is_inverted() && !l2.is_inverted(); return false; } }; struct aig_manager::imp { id_gen m_var_id_gen; id_gen m_node_id_gen; aig_table m_table; unsigned m_num_aigs; expr_ref_vector m_var2exprs; small_object_allocator m_allocator; ptr_vector m_to_delete; aig_lit m_true; aig_lit m_false; bool m_default_gate_encoding; unsigned long long m_max_memory; volatile bool m_cancel; void dec_ref_core(aig * n) { SASSERT(n->m_ref_count > 0); n->m_ref_count--; if (n->m_ref_count == 0) m_to_delete.push_back(n); } void checkpoint() { if (memory::get_allocation_size() > m_max_memory) throw aig_exception(TACTIC_MAX_MEMORY_MSG); if (m_cancel) throw aig_exception(TACTIC_CANCELED_MSG); cooperate("aig"); } void dec_ref_core(aig_lit const & r) { dec_ref_core(r.ptr()); } void dec_ref_result(aig * n) { SASSERT(n->m_ref_count > 0); n->m_ref_count--; } void dec_ref_result(aig_lit const & r) { dec_ref_result(r.ptr()); } void process_to_delete() { while (!m_to_delete.empty()) { aig * n = m_to_delete.back(); m_to_delete.pop_back(); delete_node(n); } } void delete_node(aig * n) { TRACE("aig_lit_count", tout << "deleting: "; display_ref(tout, n); tout << "\n";); SASSERT(m_num_aigs > 0); m_num_aigs--; if (is_var(n)) { m_var_id_gen.recycle(n->m_id); m_var2exprs.set(n->m_id, 0); } else { m_table.erase(n); m_node_id_gen.recycle(n->m_id); dec_ref_core(n->m_children[0]); dec_ref_core(n->m_children[1]); } m_allocator.deallocate(sizeof(aig), n); } aig * allocate_node() { return static_cast(m_allocator.allocate(sizeof(aig))); } aig * mk_var(expr * t) { m_num_aigs++; aig * r = allocate_node(); r->m_id = m_var_id_gen.mk(); r->m_ref_count = 0; r->m_mark = false; r->m_children[0] = aig_lit(); SASSERT(r->m_id <= m_var2exprs.size()); if (r->m_id == m_var2exprs.size()) m_var2exprs.push_back(t); else m_var2exprs.set(r->m_id, t); return r; } aig_lit mk_node_core(aig_lit const & l, aig_lit const & r) { aig * new_node = allocate_node(); new_node->m_children[0] = l; new_node->m_children[1] = r; aig * old_node = m_table.insert_if_not_there(new_node); if (old_node != new_node) { m_allocator.deallocate(sizeof(aig), new_node); return aig_lit(old_node); } m_num_aigs++; new_node->m_id = m_node_id_gen.mk(); new_node->m_ref_count = 0; new_node->m_mark = false; SASSERT(new_node->m_ref_count == 0); inc_ref(l); inc_ref(r); return aig_lit(new_node); } bool is_not_eq(aig_lit const & l, aig_lit const & r) const { return l.ptr() == r.ptr() && l.is_inverted() != r.is_inverted(); } /** \brief Create an AIG representing (l and r) Apply two-level minimization rules that guarantee that locally the size is decreasing, and globally is not increasing. */ aig_lit mk_node(aig_lit l, aig_lit r) { start: bool sign1 = l.is_inverted(); aig * n1 = l.ptr(); bool sign2 = r.is_inverted(); aig * n2 = r.ptr(); if (n1->m_id == 0) { if (sign1) return m_false; // false and r else return r; // true and r } if (n2->m_id == 0) { if (sign2) return m_false; // l and false; else return l; // l and true; } if (n1 == n2) { if (sign1 == sign2) return l; // l and l; else return m_false; // l and not l } #ifdef USE_TWO_LEVEL_RULES if (!is_var(n1)) { aig_lit a = n1->m_children[0]; aig_lit b = n1->m_children[1]; if (is_not_eq(a, r) || is_not_eq(b, r)) { if (sign1) { // substitution return r; // (not (a and b)) and r --> r IF a = (not r) or b = (not r) } else { // contradiction return m_false; // (a and b) and r --> false IF a = (not r) or b = (not r) } } if (a == r) { if (sign1) { // substitution // not (a and b) and r --> (not b) and r IF a == r l = b; l.invert(); goto start; } else { // substitution return l; // (a and b) and r --> (a and b) IF a = r } } if (b == r) { if (sign1) { // subsitution // not (a and b) and r --> (not a) and r IF b == r l = a; l.invert(); goto start; } else { // substitution return l; // (a and b) and r --> (a and b) IF b = r; } } if (!is_var(n2)) { aig_lit c = n2->m_children[0]; aig_lit d = n2->m_children[1]; if (!sign1 && !sign2) { // contradiction if (is_not_eq(a, c) || is_not_eq(a, d) || is_not_eq(b, c) || is_not_eq(b, d)) return m_false; // (a and b) and (c and d) --> false IF a = not(c) OR a = not(d) or b = not(c) or b = not(d) // idempotence if (a == c || b == c) { r = d; // (a and b) and (c and d) --> (a and b) and d IF a == c or b == c goto start; } // idempotence if (b == c || b == d) { l = a; // (a and b) and (c and d) --> a and (c and d) IF b == c or b == d goto start; } // idempotence if (a == d || b == d) { r = c; goto start; // (a and b) and (c and d) --> (a and b) and c IF a == d or b == d } // idempotence if (a == c || a == d) { l = b; // (a and b) and (c and d) --> b and (c and d) IF a == c or a == d goto start; } } if (sign1 && !sign2) { // subsumption if (is_not_eq(a, c) || is_not_eq(a, d) || is_not_eq(b, c) || is_not_eq(b, d)) return r; // not (a and b) and (c and d) --> (c and d) // substitution if (b == c || b == d) { // not (a and b) and (c and d) --> (not a) and (c and d) a.invert(); l = a; goto start; } // substitution if (a == c || a == d) { // not (a and b) and (c and d) --> (not b) and (c and d) b.invert(); l = b; goto start; } } if (!sign1 && sign2) { // subsumption if (is_not_eq(a, c) || is_not_eq(a, d) || is_not_eq(b, c) || is_not_eq(b, d)) return l; // (a and b) and not (c and d) --> (a and b) // substitution if (c == a || c == b) { // (a and b) and not (c and d) --> (a and b) and (not d); d.invert(); r = d; goto start; } // substitution if (d == a || d == b) { // (a and b) and not (c and d) --> (a and b) and (not c); c.invert(); r = c; goto start; } } if (sign1 && sign2) { // resolution if (a == c && is_not_eq(b, d)) { a.invert(); // (not (a and b)) and (not (a and (not b))) --> not a return a; } SASSERT(!(a == d && is_not_eq(b, c))); // cannot happen because we sort args // resolution if (is_not_eq(a, c) && b == d) { b.invert(); // (not (a and b)) and (not (a and (not b))) --> not b return b; } SASSERT(!(is_not_eq(a, d) && b == c)); // cannot happen because we sort args } } } if (!is_var(n2)) { aig_lit a = n2->m_children[0]; aig_lit b = n2->m_children[1]; if (is_not_eq(l, a) || is_not_eq(l, b)) { if (sign2) { // substitution return l; // l and (not (a and b)) --> l IF a = (not l) or b = (not l) } else { // contradiction return m_false; // l and (a and b) --> false IF a = (not l) or b = (not l) } } if (a == l) { if (sign2) { // substitution // l and not (a and b) and r --> l and (not b) IF a == l r = b; r.invert(); goto start; } else { // substitution return r; // l and (a and b) --> (a and b) IF a = l; } } if (b == l) { if (sign2) { // substitution // l and not (a and b) --> l and (not a) IF b == l r = a; r.invert(); goto start; } else { // substitution return r; // l and (a and b) --> (a and b) IF b = l; } } } #endif if (n1->m_id > n2->m_id) return mk_node_core(r, l); else return mk_node_core(l, r); } struct expr2aig { struct frame { app * m_t; unsigned m_idx; unsigned m_spos; frame(app * t, unsigned spos):m_t(t), m_idx(0), m_spos(spos) {} }; imp & m; svector m_frame_stack; svector m_result_stack; obj_map m_cache; expr2aig(imp & _m):m(_m) {} ~expr2aig() { obj_map::iterator it = m_cache.begin(); obj_map::iterator end = m_cache.end(); for (; it != end; ++it) { TRACE("expr2aig", tout << "dec-ref: "; m.display_ref(tout, it->m_value); tout << " ref-count: " << ref_count(it->m_value) << "\n";); m.dec_ref(it->m_value); } restore_result_stack(0); } void save_result(aig_lit & r) { m.inc_ref(r); m_result_stack.push_back(r); } void cache_result(expr * t, aig_lit const & r) { TRACE("expr2aig", tout << "caching:\n" << mk_ismt2_pp(t, m.m()) << "\n---> "; m.display_ref(tout, r); tout << "\n";); SASSERT(!m_cache.contains(t)); m.inc_ref(r); m_cache.insert(t, r); } bool is_cached(expr * t) { aig_lit r; if (m_cache.find(t, r)) { save_result(r); return true; } return false; } void process_var(expr * t) { if (is_cached(t)) return; aig_lit r(m.mk_var(t)); SASSERT(ref_count(r) == 0); cache_result(t, r); save_result(r); } void mk_frame(app * t) { m_frame_stack.push_back(frame(t, m_result_stack.size())); } bool visit(expr * t) { if (is_app(t)) { app * tapp = to_app(t); if (tapp->get_family_id() == m.m().get_basic_family_id()) { switch (tapp->get_decl_kind()) { case OP_TRUE: save_result(m.m_true); return true; case OP_FALSE: save_result(m.m_false); return true; case OP_EQ: if (!m.m().is_bool(tapp->get_arg(0))) break; case OP_NOT: case OP_OR: case OP_AND: case OP_IFF: case OP_XOR: case OP_IMPLIES: case OP_ITE: if (tapp->get_ref_count() > 1 && is_cached(tapp)) return true; mk_frame(tapp); return false; default: break; } } process_var(t); return true; } else { // quantifiers and free variables are handled as aig variables process_var(t); return true; } } void restore_result_stack(unsigned old_sz) { unsigned sz = m_result_stack.size(); SASSERT(old_sz <= sz); for (unsigned i = old_sz; i < sz; i++) m.dec_ref(m_result_stack[i]); m_result_stack.shrink(old_sz); } void save_node_result(unsigned spos, aig_lit r) { m.inc_ref(r); restore_result_stack(spos); save_result(r); SASSERT(ref_count(r) >= 2); m.dec_ref(r); } void mk_or(unsigned spos) { SASSERT(spos <= m_result_stack.size()); unsigned num = m_result_stack.size() - spos; aig_lit r = m.mk_or(num, m_result_stack.begin() + spos); save_node_result(spos, r); } void mk_and(unsigned spos) { SASSERT(spos <= m_result_stack.size()); unsigned num = m_result_stack.size() - spos; aig_lit r = m.mk_and(num, m_result_stack.begin() + spos); save_node_result(spos, r); } void mk_ite(unsigned spos) { SASSERT(spos + 3 == m_result_stack.size()); aig_lit r = m.mk_ite(m_result_stack[spos], m_result_stack[spos+1], m_result_stack[spos+2]); save_node_result(spos, r); } void mk_iff(unsigned spos) { SASSERT(spos + 2 == m_result_stack.size()); aig_lit r = m.mk_iff(m_result_stack[spos], m_result_stack[spos+1]); save_node_result(spos, r); } void mk_xor(unsigned spos) { SASSERT(spos + 2 == m_result_stack.size()); aig_lit r = m.mk_xor(m_result_stack[spos], m_result_stack[spos+1]); save_node_result(spos, r); } void mk_implies(unsigned spos) { SASSERT(spos + 2 == m_result_stack.size()); aig_lit r = m.mk_implies(m_result_stack[spos], m_result_stack[spos+1]); save_node_result(spos, r); } void mk_aig(frame & fr) { SASSERT(fr.m_t->get_family_id() == m.m().get_basic_family_id()); switch (fr.m_t->get_decl_kind()) { case OP_NOT: m_result_stack[fr.m_spos].invert(); break; case OP_OR: mk_or(fr.m_spos); break; case OP_AND: mk_and(fr.m_spos); break; case OP_EQ: SASSERT(m.m().is_bool(fr.m_t->get_arg(0))); mk_iff(fr.m_spos); break; case OP_IFF: mk_iff(fr.m_spos); break; case OP_XOR: mk_xor(fr.m_spos); break; case OP_IMPLIES: mk_implies(fr.m_spos); break; case OP_ITE: mk_ite(fr.m_spos); break; default: UNREACHABLE(); } if (fr.m_t->get_ref_count() > 1) { cache_result(fr.m_t, m_result_stack.back()); } } aig_lit operator()(expr * n) { SASSERT(check_cache()); if (!visit(n)) { while (!m_frame_stack.empty()) { loop: m.checkpoint(); frame & fr = m_frame_stack.back(); if (fr.m_idx == 0 && fr.m_t->get_ref_count() > 1) { if (is_cached(fr.m_t)) { m_frame_stack.pop_back(); continue; } } unsigned num_args = fr.m_t->get_num_args(); while (fr.m_idx < num_args) { expr * arg = fr.m_t->get_arg(fr.m_idx); fr.m_idx++; if (!visit(arg)) goto loop; } mk_aig(fr); m_frame_stack.pop_back(); } } SASSERT(m_result_stack.size() == 1); aig_lit r = m_result_stack.back(); m_result_stack.pop_back(); m.dec_ref_result(r); SASSERT(check_cache()); return r; } bool check_cache() const { obj_map::iterator it = m_cache.begin(); obj_map::iterator end = m_cache.end(); for (; it != end; ++it) { SASSERT(ref_count(it->m_value) > 0); } return true; } }; /** \brief Return true if the AIG represents an if-then-else */ template bool is_ite_core(aig * n, aig_lit & c, aig_lit & t, aig_lit & e) const { if (is_var(n)) return false; aig_lit l = left(n); aig_lit r = right(n); if (l.is_inverted() && r.is_inverted()) { aig * l_ptr = l.ptr(); aig * r_ptr = r.ptr(); if (is_var(l_ptr) || is_var(r_ptr)) return false; aig_lit l1 = left(l_ptr); aig_lit l2 = right(l_ptr); aig_lit r1 = left(r_ptr); aig_lit r2 = right(r_ptr); if (is_not_eq(l1, r1)) { if (Collect) { l1.invert(); l2.invert(); r1.invert(); r2.invert(); if (l1.is_inverted()) { c = r1; t = l2; e = r2; } else { c = l1; t = r2; e = l2; } } return true; } else if (is_not_eq(l1, r2)) { if (Collect) { l1.invert(); l2.invert(); r1.invert(); r2.invert(); if (l1.is_inverted()) { c = r2; t = l2; e = r1; } else { c = l1; t = r1; e = l2; } } return true; } else if (is_not_eq(l2, r1)) { if (Collect) { l1.invert(); l2.invert(); r1.invert(); r2.invert(); if (l2.is_inverted()) { c = r1; t = l1; e = r2; } else { c = l2; t = r2; e = l1; } } return true; } else if (is_not_eq(l2, r2)) { if (Collect) { l1.invert(); l2.invert(); r1.invert(); r2.invert(); if (l2.is_inverted()) { c = r2; t = l1; e = r1; } else { c = l2; t = r1; e = l1; } } return true; } } return false; } bool is_ite(aig * n, aig_lit & c, aig_lit & t, aig_lit & e) const { return is_ite_core(n, c, t, e); } bool is_ite(aig * n) const { static aig_lit c, t, e; return is_ite_core(n, c, t, e); } /** \brief Return true if the AIG represents an iff */ bool is_iff(aig * n) const { if (is_var(n)) return false; aig_lit l = left(n); aig_lit r = right(n); if (l.is_inverted() && r.is_inverted()) { if (is_var(l) || is_var(r)) return false; return is_not_eq(left(l), left(r)) && is_not_eq(right(l), right(r)); } return false; } expr * var2expr(aig * n) const { SASSERT(is_var(n)); return m_var2exprs[n->m_id]; } struct aig2expr { imp & m; ast_manager & ast_mng; enum kind { AIG_AND, AIG_AUX_AND, // does not have an associated expr AIG_ITE }; struct frame { aig * m_node; unsigned m_kind:2; unsigned m_first:1; frame(aig * n, kind k):m_node(n), m_kind(k), m_first(true) {} }; expr_ref_vector m_cache; svector m_frame_stack; aig2expr(imp & _m):m(_m), ast_mng(m.m()), m_cache(ast_mng) {} expr * get_cached(aig * n) { if (is_var(n)) { return n->m_id == 0 ? ast_mng.mk_true() : m.var2expr(n); } else { CTRACE("aig2expr", !is_cached(n), tout << "invalid get_cached for "; m.display_ref(tout, n); tout << "\n";); SASSERT(is_cached(n)); return m_cache.get(to_idx(n)); } } expr * invert(expr * n) { if (ast_mng.is_not(n)) return to_app(n)->get_arg(0); if (ast_mng.is_true(n)) return ast_mng.mk_false(); SASSERT(!ast_mng.is_false(n)); return ast_mng.mk_not(n); } expr * get_cached(aig_lit const & n) { if (n.is_inverted()) return invert(get_cached(n.ptr())); else return get_cached(n.ptr()); } bool is_cached(aig * n) { if (is_var(n)) return true; unsigned idx = to_idx(n); if (idx >= m_cache.size()) { m_cache.resize(idx+1); return false; } return m_cache.get(idx) != 0; } void cache_result(aig * n, expr * t) { unsigned idx = to_idx(n); SASSERT(idx < m_cache.size()); SASSERT(m_cache.get(idx) == 0); m_cache.set(idx, t); } void visit_and_child(aig_lit c, bool & visited) { aig * n = c.ptr(); if (is_cached(n)) return; if (m.is_ite(n)) m_frame_stack.push_back(frame(n, AIG_ITE)); else if (!c.is_inverted() && n->m_ref_count == 1) m_frame_stack.push_back(frame(n, AIG_AUX_AND)); else m_frame_stack.push_back(frame(n, AIG_AND)); visited = false; } void visit_ite_child(aig_lit c, bool & visited) { aig * n = c.ptr(); if (is_cached(n)) return; m_frame_stack.push_back(frame(n, m.is_ite(n) ? AIG_ITE : AIG_AND)); visited = false; } ptr_vector m_and_children; ptr_vector m_and_todo; void add_child(aig_lit c) { aig * n = c.ptr(); if (c.is_inverted()) { // adding (not c) since I build an OR node m_and_children.push_back(get_cached(n)); return; } if (is_cached(n)) { m_and_children.push_back(invert(get_cached(n))); return; } SASSERT(n->m_ref_count == 1); m_and_todo.push_back(n); } void mk_and(aig * n) { m_and_children.reset(); m_and_todo.reset(); add_child(left(n)); add_child(right(n)); while (!m_and_todo.empty()) { aig * t = m_and_todo.back(); SASSERT(!is_var(t)); m_and_todo.pop_back(); add_child(left(t)); add_child(right(t)); } expr * r = ast_mng.mk_not(ast_mng.mk_or(m_and_children.size(), m_and_children.c_ptr())); cache_result(n, r); TRACE("aig2expr", tout << "caching AND "; m.display_ref(tout, n); tout << "\n";); } void mk_ite(aig * n) { aig_lit c, t, e; VERIFY(m.is_ite(n, c, t, e)); if (c.is_inverted()) { c.invert(); std::swap(t, e); } expr * r; if (m.is_not_eq(t, e)) { r = ast_mng.mk_iff(get_cached(c), get_cached(t)); } else { r = ast_mng.mk_ite(get_cached(c), get_cached(t), get_cached(e)); } cache_result(n, r); TRACE("aig2expr", tout << "caching ITE/IFF "; m.display_ref(tout, n); tout << "\n";); } /** \brief Return an expression representing the negation of p. */ expr * process_root(aig * r) { if (is_cached(r)) return get_cached(r); m_frame_stack.push_back(frame(r, m.is_ite(r) ? AIG_ITE : AIG_AND)); while (!m_frame_stack.empty()) { m.checkpoint(); frame & fr = m_frame_stack.back(); aig * n = fr.m_node; if (is_cached(n)) { m_frame_stack.pop_back(); continue; } if (fr.m_first) { fr.m_first = false; bool visited = true; switch (fr.m_kind) { case AIG_AND: case AIG_AUX_AND: visit_and_child(left(n), visited); visit_and_child(right(n), visited); break; case AIG_ITE: { aig_lit a = left(left(n)); aig_lit b = right(left(n)); aig_lit c = left(right(n)); aig_lit d = right(right(n)); visit_ite_child(a, visited); visit_ite_child(b, visited); if (c.ptr() != a.ptr() && c.ptr() != b.ptr()) visit_ite_child(c, visited); if (d.ptr() != a.ptr() && d.ptr() != b.ptr()) visit_ite_child(d, visited); break; } default: UNREACHABLE(); break; } if (!visited) continue; } switch (fr.m_kind){ case AIG_AUX_AND: // do nothing TRACE("aig2expr", tout << "skipping aux AND "; m.display_ref(tout, n); tout << "\n";); break; case AIG_AND: mk_and(n); break; case AIG_ITE: mk_ite(n); break; default: UNREACHABLE(); break; } m_frame_stack.pop_back(); } return get_cached(r); } /** \brief (Debugging) Naive AIG -> EXPR */ void naive(aig_lit const & l, expr_ref & r) { expr_ref_vector cache(ast_mng); ptr_vector todo; todo.push_back(l.ptr()); while (!todo.empty()) { aig * t = todo.back(); if (is_var(t)) { todo.pop_back(); continue; } unsigned idx = to_idx(t); cache.reserve(idx+1); if (cache.get(idx) != 0) { todo.pop_back(); continue; } bool ok = true; for (unsigned i = 0; i < 2; i++) { aig * c = t->m_children[i].ptr(); if (!is_var(c) && cache.get(to_idx(c), 0) == 0) { todo.push_back(c); ok = false; } } if (!ok) continue; expr * args[2]; for (unsigned i = 0; i < 2; i++) { aig_lit l = t->m_children[i]; aig * c = l.ptr(); if (is_var(c)) args[i] = m.m_var2exprs.get(c->m_id); else args[i] = cache.get(to_idx(c), 0); if (!l.is_inverted()) args[i] = invert(args[i]); } cache.set(idx, ast_mng.mk_not(ast_mng.mk_or(args[0], args[1]))); todo.pop_back(); } aig * c = l.ptr(); if (is_var(c)) r = m.m_var2exprs.get(c->m_id); else r = cache.get(to_idx(c)); if (l.is_inverted()) r = invert(r); } void operator()(aig_lit const & l, expr_ref & r) { naive(l, r); } void operator()(aig_lit const & l, goal & g) { g.reset(); sbuffer roots; roots.push_back(l); while (!roots.empty()) { aig_lit n = roots.back(); roots.pop_back(); if (n.is_inverted()) { g.assert_expr(invert(process_root(n.ptr())), 0, 0); continue; } aig * p = n.ptr(); if (m.is_ite(p)) { g.assert_expr(process_root(p), 0, 0); continue; } if (is_var(p)) { g.assert_expr(m.var2expr(p), 0, 0); continue; } roots.push_back(left(p)); roots.push_back(right(p)); } } }; struct max_sharing_proc { struct frame { aig * m_node; unsigned short m_idx; frame(aig * n):m_node(n), m_idx(0) {} }; imp & m; svector m_frame_stack; svector m_result_stack; svector m_cache; ptr_vector m_saved; max_sharing_proc(imp & _m):m(_m) {} ~max_sharing_proc() { reset_saved(); } void reset_saved() { m.dec_array_ref(m_saved.size(), m_saved.c_ptr()); m_saved.finalize(); } void reset_cache() { m_cache.finalize(); reset_saved(); } void push_result(aig_lit n) { m_result_stack.push_back(n); if (!n.is_null()) m.inc_ref(n); } bool is_cached(aig * p) { SASSERT(!is_var(p)); if (p->m_ref_count <= 1) return false; unsigned idx = to_idx(p); if (idx >= m_cache.size()) { m_cache.resize(idx+1, aig_lit::null); return false; } aig_lit c = m_cache[idx]; if (!c.is_null()) { push_result(c); return true; } return false; } bool visit(aig * p) { if (is_var(p)) { push_result(0); return true; } if (is_cached(p)) return true; m_frame_stack.push_back(frame(p)); return false; } bool visit(aig_lit l) { return visit(l.ptr()); } void save_result(aig * o, aig_lit n) { SASSERT(!is_var(o)); if (o->m_ref_count > 1) { unsigned idx = to_idx(o); if (idx >= m_cache.size()) m_cache.resize(idx+1, aig_lit::null); m_cache[idx] = n; m_saved.push_back(o); m_saved.push_back(n.ptr()); m.inc_ref(o); m.inc_ref(n); } if (o != n.ptr()) { push_result(n); } else { SASSERT(!n.is_inverted()); push_result(aig_lit::null); } } void pop2_result() { aig_lit r1 = m_result_stack.back(); m_result_stack.pop_back(); aig_lit r2 = m_result_stack.back(); m_result_stack.pop_back(); if (!r1.is_null()) m.dec_ref(r1); if (!r2.is_null()) m.dec_ref(r2); } bool improve_sharing_left(aig * o, aig_lit n) { SASSERT(!left(n).is_inverted()); SASSERT(!is_var(left(n))); aig_lit a = left(left(n)); aig_lit b = right(left(n)); aig_lit c = right(n); TRACE("max_sharing", tout << "trying (and "; m.display_ref(tout, a); tout << " (and "; m.display_ref(tout, b); tout << " "; m.display_ref(tout, c); tout << "))\n";); aig_lit bc = m.mk_and(b, c); m.inc_ref(bc); if (ref_count(bc) > 1) { aig_lit r = m.mk_and(a, bc); if (n.is_inverted()) r.invert(); save_result(o, r); m.dec_ref(bc); TRACE("max_sharing", tout << "improved:\n"; m.display(tout, o); tout << "---->\n"; m.display(tout, r);); return true; } m.dec_ref(bc); TRACE("max_sharing", tout << "trying (and "; m.display_ref(tout, a); tout << " (and "; m.display_ref(tout, c); tout << " "; m.display_ref(tout, b); tout << "))\n";); aig_lit ac = m.mk_and(a, c); m.inc_ref(ac); if (ref_count(ac) > 1) { aig_lit r = m.mk_and(b, ac); if (n.is_inverted()) r.invert(); save_result(o, r); m.dec_ref(ac); TRACE("max_sharing", tout << "improved:\n"; m.display(tout, o); tout << "---->\n"; m.display(tout, r);); return true; } m.dec_ref(ac); return false; } bool improve_sharing_right(aig * o, aig_lit n) { SASSERT(!right(n).is_inverted()); SASSERT(!is_var(right(n))); aig_lit a = left(n); aig_lit b = left(right(n)); aig_lit c = right(right(n)); TRACE("max_sharing", tout << "trying (and (and "; m.display_ref(tout, a); tout << " "; m.display_ref(tout, b); tout << ") "; m.display_ref(tout, c); tout << ")\n";); aig_lit ab = m.mk_and(a, b); m.inc_ref(ab); if (ref_count(ab) > 1) { aig_lit r = m.mk_and(ab, c); if (n.is_inverted()) r.invert(); save_result(o, r); m.dec_ref(ab); TRACE("max_sharing", tout << "improved:\n"; m.display(tout, o); tout << "---->\n"; m.display(tout, r);); return true; } m.dec_ref(ab); aig_lit ac = m.mk_and(a, c); TRACE("max_sharing", tout << "trying (and (and "; m.display_ref(tout, a); tout << " "; m.display_ref(tout, c); tout << ") "; m.display_ref(tout, b); tout << ")\n";); m.inc_ref(ac); if (ref_count(ac) > 1) { aig_lit r = m.mk_and(ac, b); if (n.is_inverted()) r.invert(); save_result(o, r); m.dec_ref(ac); TRACE("max_sharing", tout << "improved:\n"; m.display(tout, o); tout << "---->\n"; m.display(tout, r);); return true; } m.dec_ref(ac); return false; } void improve_sharing_core(aig * o, aig_lit n) { if (!is_var(n)) { aig_lit l = left(n); if (!l.is_inverted() && ref_count(l) == 1 && !is_var(l) && improve_sharing_left(o, n)) return; aig_lit r = right(n); if (!r.is_inverted() && ref_count(r) == 1 && !is_var(r) && improve_sharing_right(o, n)) return; } save_result(o, n); } void improve_sharing(aig * p) { unsigned sz = m_result_stack.size(); aig_lit new_l = m_result_stack[sz-2]; aig_lit new_r = m_result_stack[sz-1]; if (new_l.is_null() && new_r.is_null()) { pop2_result(); improve_sharing_core(p, aig_lit(p)); return; } aig_lit l = left(p); aig_lit r = right(p); if (!new_l.is_null()) { if (l.is_inverted()) new_l.invert(); l = new_l; } if (!new_r.is_null()) { if (r.is_inverted()) new_r.invert(); r = new_r; } aig_lit n = m.mk_and(l, r); m.inc_ref(n); pop2_result(); improve_sharing_core(p, n); m.dec_ref(n); } void process(aig * p) { if (visit(p)) return; while (!m_frame_stack.empty()) { start: frame & fr = m_frame_stack.back(); aig * n = fr.m_node; TRACE("max_sharing", tout << "processing "; m.display_ref(tout, n); tout << " idx: " << fr.m_idx << "\n";); switch (fr.m_idx) { case 0: fr.m_idx++; if (!visit(left(n))) goto start; case 1: fr.m_idx++; if (!visit(right(n))) goto start; default: if (!is_cached(n)) improve_sharing(n); m_frame_stack.pop_back(); break; } } } aig_lit operator()(aig_lit p) { process(p.ptr()); SASSERT(m_result_stack.size() == 1); aig_lit r = m_result_stack.back(); TRACE("max_sharing", tout << "r.is_null(): " << r.is_null() << "\n";); SASSERT(r.is_null() || ref_count(r) >= 1); reset_cache(); if (r.is_null()) { r = p; m.inc_ref(r); } else if (p.is_inverted()) { r.invert(); } m_result_stack.pop_back(); TRACE("max_sharing", tout << "result:\n"; m.display(tout, r);); m.dec_ref_result(r); return r; } }; public: imp(ast_manager & m, unsigned long long max_memory, bool default_gate_encoding): m_var_id_gen(0), m_node_id_gen(FIRST_NODE_ID), m_num_aigs(0), m_var2exprs(m), m_allocator("aig"), m_true(mk_var(m.mk_true())), m_cancel(false) { SASSERT(is_true(m_true)); m_false = m_true; m_false.invert(); inc_ref(m_true); inc_ref(m_false); m_max_memory = max_memory; m_default_gate_encoding = default_gate_encoding; } ~imp() { dec_ref(m_true); dec_ref(m_false); SASSERT(m_num_aigs == 0); } ast_manager & m() const { return m_var2exprs.get_manager(); } void set_cancel(bool f) { m_cancel = f; } void inc_ref(aig * n) { n->m_ref_count++; } void inc_ref(aig_lit const & r) { inc_ref(r.ptr()); } void dec_ref(aig * n) { dec_ref_core(n); process_to_delete(); } void dec_ref(aig_lit const & r) { dec_ref(r.ptr()); } void dec_array_ref(unsigned sz, aig * const * ns) { for (unsigned i = 0; i < sz; i++) if (ns[i]) dec_ref(ns[i]); } aig_lit mk_and(aig_lit r1, aig_lit r2) { aig_lit r = mk_node(r1, r2); TRACE("mk_and_bug", display(tout, r1); tout << "AND\n"; display(tout, r2); tout << "-->\n"; display(tout, r); tout << "\n";); return r; } aig_lit mk_and(unsigned num, aig_lit * args) { switch (num) { case 0: return m_true; case 1: return args[0]; case 2: return mk_and(args[0], args[1]); default: // No need to use stable_sort, aig_lit_lt is a total order on AIG nodes std::sort(args, args+num, aig_lit_lt()); aig_lit r = mk_and(args[0], args[1]); inc_ref(r); for (unsigned i = 2; i < num; i++) { aig_lit new_r = mk_and(r, args[i]); inc_ref(new_r); dec_ref(r); r = new_r; } dec_ref_result(r); return r; } } aig_lit mk_or(aig_lit r1, aig_lit r2) { r1.invert(); r2.invert(); aig_lit r = mk_and(r1, r2); r.invert(); return r; } aig_lit mk_or(unsigned num, aig_lit * args) { switch (num) { case 0: return m_false; case 1: return args[0]; case 2: return mk_or(args[0], args[1]); default: std::sort(args, args+num, aig_lit_lt()); aig_lit r = mk_or(args[0], args[1]); inc_ref(r); for (unsigned i = 2; i < num; i++) { aig_lit new_r = mk_or(r, args[i]); inc_ref(new_r); dec_ref(r); r = new_r; } dec_ref_result(r); return r; } } aig_lit mk_ite(aig_lit c, aig_lit t, aig_lit e) { if (m_default_gate_encoding) { t.invert(); aig_lit n1 = mk_and(c, t); // c and (not t) c.invert(); e.invert(); aig_lit n2 = mk_and(c, e); // (not c) and (not e) inc_ref(n1); inc_ref(n2); n1.invert(); n2.invert(); aig_lit r = mk_and(n1, n2); inc_ref(r); dec_ref(n1); dec_ref(n2); dec_ref_result(r); return r; } else { aig_lit n1 = mk_and(c, t); inc_ref(n1); c.invert(); aig_lit n2 = mk_and(c, e); inc_ref(n2); n1.invert(); n2.invert(); aig_lit r = mk_and(n1, n2); r.invert(); inc_ref(r); dec_ref(n1); dec_ref(n2); dec_ref_result(r); return r; } } aig_lit mk_iff(aig_lit lhs, aig_lit rhs) { if (m_default_gate_encoding) { rhs.invert(); aig_lit n1 = mk_and(lhs, rhs); // lhs and (not rhs) lhs.invert(); rhs.invert(); aig_lit n2 = mk_and(lhs, rhs); // (not lhs) and rhs inc_ref(n1); inc_ref(n2); n1.invert(); n2.invert(); aig_lit r = mk_and(n1, n2); inc_ref(r); dec_ref(n1); dec_ref(n2); dec_ref_result(r); return r; } else { aig_lit n1 = mk_and(lhs, rhs); // lhs and rhs inc_ref(n1); lhs.invert(); rhs.invert(); aig_lit n2 = mk_and(lhs, rhs); // not lhs and not rhs inc_ref(n2); n1.invert(); n2.invert(); aig_lit r = mk_and(n1, n2); r.invert(); inc_ref(r); dec_ref(n1); dec_ref(n2); dec_ref_result(r); return r; } } aig_lit mk_xor(aig_lit lhs, aig_lit rhs) { lhs.invert(); return mk_iff(lhs, rhs); } aig_lit mk_implies(aig_lit lhs, aig_lit rhs) { lhs.invert(); return mk_or(lhs, rhs); } aig_lit mk_aig(expr * t) { aig_lit r; { expr2aig proc(*this); r = proc(t); inc_ref(r); } dec_ref_result(r); return r; } template aig_lit mk_aig(S const & s) { aig_lit r; r = m_true; inc_ref(r); try { expr2aig proc(*this); unsigned sz = s.size(); for (unsigned i = 0; i < sz; i++) { SASSERT(ref_count(r) >= 1); expr * t = s.form(i); aig_lit n = proc(t); inc_ref(n); aig_lit new_r = mk_and(r, n); SASSERT(proc.check_cache()); inc_ref(new_r); dec_ref(r); dec_ref(n); SASSERT(proc.check_cache()); r = new_r; } SASSERT(ref_count(r) >= 1); } catch (aig_exception ex) { dec_ref(r); throw ex; } dec_ref_result(r); return r; } void to_formula(aig_lit const & r, goal & g) { aig2expr proc(*this); proc(r, g); } void to_formula(aig_lit const & r, expr_ref & result) { aig2expr proc(*this); proc(r, result); } aig_lit max_sharing(aig_lit l) { max_sharing_proc p(*this); return p(l); } void display_ref(std::ostream & out, aig * r) const { if (is_var(r)) out << "#" << r->m_id; else out << "@" << (r->m_id - FIRST_NODE_ID); } void display_ref(std::ostream & out, aig_lit const & r) const { if (r.is_inverted()) out << "-"; display_ref(out, r.ptr()); } void display(std::ostream & out, aig_lit const & r) const { display_ref(out, r); out << "\n"; ptr_vector queue; unsigned qhead = 0; queue.push_back(r.ptr()); while (qhead < queue.size()) { aig * n = queue[qhead]; qhead++; display_ref(out, n); out << ": "; if (is_var(n)) { out << mk_ismt2_pp(m_var2exprs[n->m_id], m()) << "\n"; } else { display_ref(out, n->m_children[0]); out << " "; display_ref(out, n->m_children[1]); out << "\n"; aig * c1 = n->m_children[0].ptr(); aig * c2 = n->m_children[1].ptr(); if (!c1->m_mark) { c1->m_mark = true; queue.push_back(c1); } if (!c2->m_mark) { c2->m_mark = true; queue.push_back(c2); } } } unmark(queue.size(), queue.c_ptr()); } void display_smt2_ref(std::ostream & out, aig_lit const & r) const { if (r.is_inverted()) out << "(not "; if (is_var(r)) { out << mk_ismt2_pp(var2expr(r.ptr()), m()); } else { out << "aig" << to_idx(r.ptr()); } if (r.is_inverted()) out << ")"; } void display_smt2(std::ostream & out, aig_lit const & r) const { ptr_vector to_unmark; ptr_vector todo; todo.push_back(r.ptr()); while (!todo.empty()) { aig * t = todo.back(); if (t->m_mark) { todo.pop_back(); continue; } if (is_var(t)) { to_unmark.push_back(t); t->m_mark = true; todo.pop_back(); continue; } bool visited = true; for (unsigned i = 0; i < 2; i++) { aig_lit c = t->m_children[i]; aig * c_ptr = c.ptr(); if (!c_ptr->m_mark) { todo.push_back(c_ptr); visited = false; } } if (!visited) continue; to_unmark.push_back(t); t->m_mark = true; out << "(define-fun aig" << to_idx(t) << " () Bool (and"; for (unsigned i = 0; i < 2; i++) { out << " "; display_smt2_ref(out, t->m_children[i]); } out << "))\n"; todo.pop_back(); } out << "(assert "; display_smt2_ref(out, r); out << ")\n"; unmark(to_unmark.size(), to_unmark.c_ptr()); } unsigned get_num_aigs() const { return m_num_aigs; } }; aig_ref::aig_ref(): m_manager(0), m_ref(0) { } aig_ref::aig_ref(aig_manager & m, aig_lit const & l): m_manager(&m), m_ref(l.m_ref) { m.m_imp->inc_ref(l); } aig_ref::~aig_ref() { if (m_ref != 0) { m_manager->m_imp->dec_ref(aig_lit(*this)); } } aig_ref & aig_ref::operator=(aig_ref const & r) { if (r.m_ref != 0) r.m_manager->m_imp->inc_ref(aig_lit(r)); if (m_ref != 0) m_manager->m_imp->dec_ref(aig_lit(*this)); m_ref = r.m_ref; m_manager = r.m_manager; return *this; } aig_manager::aig_manager(ast_manager & m, unsigned long long max, bool default_gate_encoding) { m_imp = alloc(imp, m, max, default_gate_encoding); } aig_manager::~aig_manager() { dealloc(m_imp); } void aig_manager::set_max_memory(unsigned long long max) { m_imp->m_max_memory = max; } aig_ref aig_manager::mk_aig(expr * n) { return aig_ref(*this, m_imp->mk_aig(n)); } aig_ref aig_manager::mk_aig(goal const & s) { return aig_ref(*this, m_imp->mk_aig(s)); } aig_ref aig_manager::mk_not(aig_ref const & r) { aig_lit l(r); l.invert(); return aig_ref(*this, l); } aig_ref aig_manager::mk_and(aig_ref const & r1, aig_ref const & r2) { return aig_ref(*this, m_imp->mk_and(aig_lit(r1), aig_lit(r2))); } aig_ref aig_manager::mk_or(aig_ref const & r1, aig_ref const & r2) { return aig_ref(*this, m_imp->mk_or(aig_lit(r1), aig_lit(r2))); } aig_ref aig_manager::mk_iff(aig_ref const & r1, aig_ref const & r2) { return aig_ref(*this, m_imp->mk_iff(aig_lit(r1), aig_lit(r2))); } aig_ref aig_manager::mk_ite(aig_ref const & r1, aig_ref const & r2, aig_ref const & r3) { return aig_ref(*this, m_imp->mk_ite(aig_lit(r1), aig_lit(r2), aig_lit(r3))); } void aig_manager::max_sharing(aig_ref & r) { r = aig_ref(*this, m_imp->max_sharing(aig_lit(r))); } void aig_manager::to_formula(aig_ref const & r, goal & g) { SASSERT(!g.proofs_enabled()); SASSERT(!g.unsat_core_enabled()); return m_imp->to_formula(aig_lit(r), g); } void aig_manager::to_formula(aig_ref const & r, expr_ref & res) { return m_imp->to_formula(aig_lit(r), res); } void aig_manager::display(std::ostream & out, aig_ref const & r) const { m_imp->display(out, aig_lit(r)); } void aig_manager::display_smt2(std::ostream & out, aig_ref const & r) const { m_imp->display_smt2(out, aig_lit(r)); } unsigned aig_manager::get_num_aigs() const { return m_imp->get_num_aigs(); } void aig_manager::set_cancel(bool f) { m_imp->set_cancel(f); } z3-4.4.0/src/tactic/aig/aig.h0000644000175000017500000000424312540347414015357 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: aig.h Abstract: And-inverted graphs Author: Leonardo (leonardo) 2011-05-13 Notes: --*/ #ifndef _AIG_H_ #define _AIG_H_ #include"ast.h" #include"tactic_exception.h" class goal; class aig_lit; class aig_manager; class aig_exception : public tactic_exception { public: aig_exception(char const * msg):tactic_exception(msg) {} }; class aig_ref { friend class aig_lit; friend class aig_manager; aig_manager * m_manager; void * m_ref; aig_ref(aig_manager & m, aig_lit const & l); public: aig_ref(); ~aig_ref(); aig_ref & operator=(aig_ref const & r); bool operator==(aig_ref const & r) const { return m_ref == r.m_ref; } bool operator!=(aig_ref const & r) const { return m_ref != r.m_ref; } }; class aig_manager { struct imp; imp * m_imp; friend class aig_ref; public: // If default_gate_encoding == true, then // ite(a, b, c) is encoded as (NOT a OR b) AND (a OR c) // iff(a, b) is encoded as (NOT a OR b) AND (a OR NOT b) // // If default_gate_encoding == false, then // ite(a, b, c) is encoded as (a AND b) OR (NOT a AND c) // iff(a, b) is encoded as (a AND b) OR (NOT a AND NOT b) aig_manager(ast_manager & m, unsigned long long max_memory = UINT64_MAX, bool default_gate_encoding = true); ~aig_manager(); void set_max_memory(unsigned long long max); aig_ref mk_aig(expr * n); aig_ref mk_aig(goal const & g); aig_ref mk_not(aig_ref const & r); aig_ref mk_and(aig_ref const & r1, aig_ref const & r2); aig_ref mk_or(aig_ref const & r1, aig_ref const & r2); aig_ref mk_iff(aig_ref const & r1, aig_ref const & r2); aig_ref mk_ite(aig_ref const & r1, aig_ref const & r2, aig_ref const & r3); void max_sharing(aig_ref & r); void to_formula(aig_ref const & r, expr_ref & result); void to_formula(aig_ref const & r, goal & result); void display(std::ostream & out, aig_ref const & r) const; void display_smt2(std::ostream & out, aig_ref const & r) const; unsigned get_num_aigs() const; void set_cancel(bool f); }; #endif z3-4.4.0/src/tactic/aig/aig_tactic.h0000644000175000017500000000066012540347414016705 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: aig_tactic.h Abstract: Tactic for minimizing circuits using AIGs. Author: Leonardo (leonardo) 2011-10-24 Notes: --*/ #ifndef _AIG_TACTIC_H_ #define _AIG_TACTIC_H_ #include"params.h" class tactic; tactic * mk_aig_tactic(params_ref const & p = params_ref()); /* ADD_TACTIC("aig", "simplify Boolean structure using AIGs.", "mk_aig_tactic()") */ #endif z3-4.4.0/src/tactic/goal_shared_occs.h0000644000175000017500000000210712540347414017333 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: goal_shared_occs.h Abstract: Functor for computing the set of shared occurrences in a goal. Author: Leonardo de Moura (leonardo) 2011-12-28 Revision History: --*/ #ifndef _GOAL_SHARED_OCCS_H_ #define _GOAL_SHARED_OCCS_H_ #include"goal.h" #include"shared_occs.h" /** \brief Functor for computing the set of shared occurrences in a goal. It is essentially a wrapper for shared_occs functor. */ class goal_shared_occs { shared_occs m_occs; public: goal_shared_occs(ast_manager & m, bool track_atomic = false, bool visit_quantifiers = true, bool visit_patterns = false): m_occs(m, track_atomic, visit_quantifiers, visit_patterns) { } void operator()(goal const & s); bool is_shared(expr * t) { return m_occs.is_shared(t); } unsigned num_shared() const { return m_occs.num_shared(); } void reset() { return m_occs.reset(); } void cleanup() { return m_occs.cleanup(); } void display(std::ostream & out, ast_manager & m) const { m_occs.display(out, m); } }; #endif z3-4.4.0/src/tactic/bv/0000755000175000017500000000000012540347414014312 5ustar michaelmichaelz3-4.4.0/src/tactic/bv/bv_size_reduction_tactic.cpp0000644000175000017500000004263012540347414022067 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: bv_size_reduction_tactic.cpp Abstract: Reduce the number of bits used to encode constants, by using signed bounds. Example: suppose x is a bit-vector of size 8, and we have signed bounds for x such that: -2 <= x <= 2 Then, x can be replaced by ((sign-extend 5) k) where k is a fresh bit-vector constant of size 3. Author: Leonardo (leonardo) 2012-02-19 Notes: --*/ #include"tactical.h" #include"bv_decl_plugin.h" #include"expr_replacer.h" #include"extension_model_converter.h" #include"filter_model_converter.h" #include"ast_smt2_pp.h" class bv_size_reduction_tactic : public tactic { struct imp; imp * m_imp; public: bv_size_reduction_tactic(ast_manager & m); virtual tactic * translate(ast_manager & m) { return alloc(bv_size_reduction_tactic, m); } virtual ~bv_size_reduction_tactic(); virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core); virtual void cleanup(); virtual void set_cancel(bool f); }; tactic * mk_bv_size_reduction_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(bv_size_reduction_tactic, m)); } struct bv_size_reduction_tactic::imp { typedef rational numeral; typedef extension_model_converter bv_size_reduction_mc; ast_manager & m; bv_util m_util; obj_map m_signed_lowers; obj_map m_signed_uppers; obj_map m_unsigned_lowers; obj_map m_unsigned_uppers; ref m_mc; ref m_fmc; scoped_ptr m_replacer; bool m_produce_models; volatile bool m_cancel; imp(ast_manager & _m): m(_m), m_util(m), m_replacer(mk_default_expr_replacer(m)), m_cancel(false) { } void update_signed_lower(app * v, numeral const & k) { // k <= v obj_map::obj_map_entry * entry = m_signed_lowers.insert_if_not_there2(v, k); if (entry->get_data().m_value < k) { // improve bound entry->get_data().m_value = k; } } void update_signed_upper(app * v, numeral const & k) { // v <= k obj_map::obj_map_entry * entry = m_signed_uppers.insert_if_not_there2(v, k); if (k < entry->get_data().m_value) { // improve bound entry->get_data().m_value = k; } } void update_unsigned_lower(app * v, numeral const & k) { SASSERT(k > numeral(0)); // k <= v obj_map::obj_map_entry * entry = m_unsigned_lowers.insert_if_not_there2(v, k); if (entry->get_data().m_value < k) { // improve bound entry->get_data().m_value = k; } } void update_unsigned_upper(app * v, numeral const & k) { SASSERT(k > numeral(0)); // v <= k obj_map::obj_map_entry * entry = m_unsigned_uppers.insert_if_not_there2(v, k); if (k < entry->get_data().m_value) { // improve bound entry->get_data().m_value = k; } } void collect_bounds(goal const & g) { unsigned sz = g.size(); numeral val; unsigned bv_sz; expr * f, * lhs, * rhs; for (unsigned i = 0; i < sz; i++) { bool negated = false; f = g.form(i); if (m.is_not(f)) { negated = true; f = to_app(f)->get_arg(0); } if (m_util.is_bv_sle(f, lhs, rhs)) { bv_sz = m_util.get_bv_size(lhs); if (is_uninterp_const(lhs) && m_util.is_numeral(rhs, val, bv_sz)) { TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; ); // v <= k val = m_util.norm(val, bv_sz, true); if (negated) { val += numeral(1); if (m_util.norm(val, bv_sz, true) != val) { // bound is infeasible. } else { update_signed_lower(to_app(lhs), val); } } else update_signed_upper(to_app(lhs), val); } else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs, val, bv_sz)) { TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; ); // k <= v val = m_util.norm(val, bv_sz, true); if (negated) { val -= numeral(1); if (m_util.norm(val, bv_sz, true) != val) { // bound is infeasible. } else { update_signed_upper(to_app(rhs), val); } } else update_signed_lower(to_app(rhs), val); } } #if 0 else if (m_util.is_bv_ule(f, lhs, rhs)) { if (is_uninterp_const(lhs) && m_util.is_numeral(rhs, val, bv_sz)) { TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; ); // v <= k if (negated) update_unsigned_lower(to_app(lhs), val+numeral(1)); else update_unsigned_upper(to_app(lhs), val); } else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs, val, bv_sz)) { TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; ); // k <= v if (negated) update_unsigned_upper(to_app(rhs), val-numeral(1)); else update_unsigned_lower(to_app(rhs), val); } } #endif } } void checkpoint() { if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); } void operator()(goal & g, model_converter_ref & mc) { if (g.inconsistent()) return; TRACE("before_bv_size_reduction", g.display(tout);); m_produce_models = g.models_enabled(); mc = 0; m_mc = 0; unsigned num_reduced = 0; { tactic_report report("bv-size-reduction", g); collect_bounds(g); // create substitution expr_substitution subst(m); if (!(m_signed_lowers.empty() || m_signed_uppers.empty())) { TRACE("bv_size_reduction", tout << "m_signed_lowers: " << std::endl; for (obj_map::iterator it = m_signed_lowers.begin(); it != m_signed_lowers.end(); it++) tout << mk_ismt2_pp(it->m_key, m) << " >= " << it->m_value.to_string() << std::endl; tout << "m_signed_uppers: " << std::endl; for (obj_map::iterator it = m_signed_uppers.begin(); it != m_signed_uppers.end(); it++) tout << mk_ismt2_pp(it->m_key, m) << " <= " << it->m_value.to_string() << std::endl; ); obj_map::iterator it = m_signed_lowers.begin(); obj_map::iterator end = m_signed_lowers.end(); for (; it != end; ++it) { app * v = it->m_key; unsigned bv_sz = m_util.get_bv_size(v); numeral l = m_util.norm(it->m_value, bv_sz, true); obj_map::obj_map_entry * entry = m_signed_uppers.find_core(v); if (entry != 0) { numeral u = m_util.norm(entry->get_data().m_value, bv_sz, true); TRACE("bv_size_reduction", tout << l << " <= " << v->get_decl()->get_name() << " <= " << u << "\n";); expr * new_def = 0; app * new_const = 0; if (l > u) { g.assert_expr(m.mk_false()); return; } else if (l == u) { new_def = m_util.mk_numeral(l, m.get_sort(v)); } else { // l < u if (l.is_neg()) { unsigned l_nb = (-l).get_num_bits(); unsigned v_nb = m_util.get_bv_size(v); if (u.is_neg()) { // l <= v <= u <= 0 unsigned i_nb = l_nb; TRACE("bv_size_reduction", tout << " l <= " << v->get_decl()->get_name() << " <= u <= 0 " << " --> " << i_nb << " bits\n";); if (i_nb < v_nb) { new_const = m.mk_fresh_const(0, m_util.mk_sort(i_nb)); new_def = m_util.mk_concat(m_util.mk_numeral(numeral(-1), v_nb - i_nb), new_const); } } else { // l <= v <= 0 <= u unsigned u_nb = u.get_num_bits(); unsigned i_nb = ((l_nb > u_nb) ? l_nb : u_nb) + 1; TRACE("bv_size_reduction", tout << " l <= " << v->get_decl()->get_name() << " <= 0 <= u " << " --> " << i_nb << " bits\n";); if (i_nb < v_nb) { new_const = m.mk_fresh_const(0, m_util.mk_sort(i_nb)); new_def = m_util.mk_sign_extend(v_nb - i_nb, new_const); } } } else { // 0 <= l <= v <= u unsigned u_nb = u.get_num_bits(); unsigned v_nb = m_util.get_bv_size(v); TRACE("bv_size_reduction", tout << l << " <= " << v->get_decl()->get_name() << " <= " << u << " --> " << u_nb << " bits\n";); if (u_nb < v_nb) { new_const = m.mk_fresh_const(0, m_util.mk_sort(u_nb)); new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), v_nb - u_nb), new_const); } } } if (new_def) { subst.insert(v, new_def); if (m_produce_models) { if (!m_mc) m_mc = alloc(bv_size_reduction_mc, m); m_mc->insert(v->get_decl(), new_def); if (!m_fmc && new_const) m_fmc = alloc(filter_model_converter, m); if (new_const) m_fmc->insert(new_const->get_decl()); } num_reduced++; } } } } #if 0 if (!(m_unsigned_lowers.empty() && m_unsigned_uppers.empty())) { TRACE("bv_size_reduction", tout << "m_unsigned_lowers: " << std::endl; for (obj_map::iterator it = m_unsigned_lowers.begin(); it != m_unsigned_lowers.end(); it++) tout << mk_ismt2_pp(it->m_key, m) << " >= " << it->m_value.to_string() << std::endl; tout << "m_unsigned_uppers: " << std::endl; for (obj_map::iterator it = m_unsigned_uppers.begin(); it != m_unsigned_uppers.end(); it++) tout << mk_ismt2_pp(it->m_key, m) << " <= " << it->m_value.to_string() << std::endl; ); obj_map::iterator it = m_unsigned_uppers.begin(); obj_map::iterator end = m_unsigned_uppers.end(); for (; it != end; ++it) { app * v = it->m_key; unsigned bv_sz = m_util.get_bv_size(v); numeral u = m_util.norm(it->m_value, bv_sz, false); obj_map::obj_map_entry * entry = m_signed_lowers.find_core(v); numeral l = (entry != 0) ? m_util.norm(entry->get_data().m_value, bv_sz, false) : numeral(0); obj_map::obj_map_entry * lse = m_signed_lowers.find_core(v); obj_map::obj_map_entry * use = m_signed_uppers.find_core(v); if ((lse != 0 && lse->get_data().m_value > l) && (use != 0 && use->get_data().m_value < u)) continue; // Skip, we had better signed bounds. if (lse != 0 && lse->get_data().m_value > l) l = lse->get_data().m_value; if (use != 0 && use->get_data().m_value < u) u = use->get_data().m_value; TRACE("bv_size_reduction", tout << l << " <= " << v->get_decl()->get_name() << " <= " << u << "\n";); expr * new_def = 0; app * new_const = 0; if (l > u) { g.assert_expr(m.mk_false()); return; } else if (l == u) { new_def = m_util.mk_numeral(l, m.get_sort(v)); } else { // 0 <= l <= v <= u unsigned u_nb = u.get_num_bits(); unsigned v_nb = m_util.get_bv_size(v); if (u_nb < v_nb) { new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), v_nb - u_nb), new_const); new_const = m.mk_fresh_const(0, m_util.mk_sort(u_nb)); } } if (new_def) { subst.insert(v, new_def); if (m_produce_models) { if (!m_mc) m_mc = alloc(bv_size_reduction_mc, m); m_mc->insert(v->get_decl(), new_def); if (!m_fmc && new_const) m_fmc = alloc(filter_model_converter, m); if (new_const) m_fmc->insert(new_const->get_decl()); } num_reduced++; TRACE("bv_size_reduction", tout << "New definition = " << mk_ismt2_pp(new_def, m) << "\n";); } } } #endif if (subst.empty()) return; m_replacer->set_substitution(&subst); unsigned sz = g.size(); expr * f; expr_ref new_f(m); for (unsigned i = 0; i < sz; i++) { if (g.inconsistent()) return; f = g.form(i); (*m_replacer)(f, new_f); g.update(i, new_f); } mc = m_mc.get(); if (m_fmc) { mc = concat(m_fmc.get(), mc.get()); } m_mc = 0; m_fmc = 0; } report_tactic_progress(":bv-reduced", num_reduced); TRACE("after_bv_size_reduction", g.display(tout); if (m_mc) m_mc->display(tout);); } void set_cancel(bool f) { m_replacer->set_cancel(f); m_cancel = f; } }; bv_size_reduction_tactic::bv_size_reduction_tactic(ast_manager & m) { m_imp = alloc(imp, m); } bv_size_reduction_tactic::~bv_size_reduction_tactic() { dealloc(m_imp); } void bv_size_reduction_tactic::operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("bv-size-reduction", g); fail_if_unsat_core_generation("bv-size-reduction", g); mc = 0; pc = 0; core = 0; result.reset(); m_imp->operator()(*(g.get()), mc); g->inc_depth(); result.push_back(g.get()); SASSERT(g->is_well_sorted()); } void bv_size_reduction_tactic::set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } void bv_size_reduction_tactic::cleanup() { imp * d = alloc(imp, m_imp->m); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } z3-4.4.0/src/tactic/bv/max_bv_sharing_tactic.h0000644000175000017500000000144312540347414021003 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: max_bv_sharing_tactic.h Abstract: Rewriter for "maximing" the number of shared terms. The idea is to rewrite AC terms to maximize sharing. This rewriter is particularly useful for reducing the number of Adders and Multipliers before "bit-blasting". Author: Leonardo de Moura (leonardo) 2011-12-29. Revision History: --*/ #ifndef _MAX_BV_SHARING_TACTIC_H_ #define _MAX_BV_SHARING_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_max_bv_sharing_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("max-bv-sharing", "use heuristics to maximize the sharing of bit-vector expressions such as adders and multipliers.", "mk_max_bv_sharing_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/bv/bit_blaster_tactic.h0000644000175000017500000000100012540347414020273 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bit_blaster_tactic.h Abstract: Apply bit-blasting to a given goal. Author: Leonardo (leonardo) 2011-10-25 Notes: --*/ #ifndef _BIT_BLASTER_TACTIC_H_ #define _BIT_BLASTER_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_bit_blaster_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("bit-blast", "reduce bit-vector expressions into SAT.", "mk_bit_blaster_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/bv/bv1_blaster_tactic.cpp0000644000175000017500000004156712540347414020566 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv1_blaster_tactic.cpp Abstract: Rewriter for "blasting" bit-vectors of size n into bit-vectors of size 1. This rewriter only supports concat and extract operators. This transformation is useful for handling benchmarks that contain many BV equalities. Remark: other operators can be mapped into concat/extract by using the simplifiers. Author: Leonardo (leonardo) 2011-10-25 Notes: --*/ #include"tactical.h" #include"bit_blaster_model_converter.h" #include"bv_decl_plugin.h" #include"rewriter_def.h" #include"for_each_expr.h" #include"cooperate.h" class bv1_blaster_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { ast_manager & m_manager; bv_util m_util; obj_map m_const2bits; expr_ref_vector m_saved; expr_ref m_bit1; expr_ref m_bit0; unsigned long long m_max_memory; // in bytes unsigned m_max_steps; bool m_produce_models; ast_manager & m() const { return m_manager; } bv_util & butil() { return m_util; } bv_util const & butil() const { return m_util; } void cleanup_buffers() { m_saved.finalize(); } rw_cfg(ast_manager & m, params_ref const & p): m_manager(m), m_util(m), m_saved(m), m_bit1(m), m_bit0(m) { m_bit1 = butil().mk_numeral(rational(1), 1); m_bit0 = butil().mk_numeral(rational(0), 1); updt_params(p); } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_max_steps = p.get_uint("max_steps", UINT_MAX); m_produce_models = p.get_bool("produce_models", false); } bool rewrite_patterns() const { UNREACHABLE(); return false; } bool max_steps_exceeded(unsigned num_steps) const { cooperate("bv1 blaster"); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); return num_steps > m_max_steps; } typedef ptr_buffer bit_buffer; void get_bits(expr * arg, bit_buffer & bits) { SASSERT(butil().is_concat(arg) || butil().get_bv_size(arg) == 1); if (butil().is_concat(arg)) bits.append(to_app(arg)->get_num_args(), to_app(arg)->get_args()); else bits.push_back(arg); } void mk_const(func_decl * f, expr_ref & result) { SASSERT(f->get_family_id() == null_family_id); SASSERT(f->get_arity() == 0); expr * r; if (m_const2bits.find(f, r)) { result = r; return; } sort * s = f->get_range(); SASSERT(butil().is_bv_sort(s)); unsigned bv_size = butil().get_bv_size(s); if (bv_size == 1) { result = m().mk_const(f); return; } sort * b = butil().mk_sort(1); ptr_buffer bits; for (unsigned i = 0; i < bv_size; i++) { bits.push_back(m().mk_fresh_const(0, b)); } r = butil().mk_concat(bits.size(), bits.c_ptr()); m_saved.push_back(r); m_const2bits.insert(f, r); result = r; } void blast_bv_term(expr * t, expr_ref & result) { bit_buffer bits; unsigned bv_size = butil().get_bv_size(t); if (bv_size == 1) { result = t; return; } unsigned i = bv_size; while (i > 0) { --i; bits.push_back(butil().mk_extract(i, i, t)); } result = butil().mk_concat(bits.size(), bits.c_ptr()); } void reduce_eq(expr * arg1, expr * arg2, expr_ref & result) { bit_buffer bits1; bit_buffer bits2; get_bits(arg1, bits1); get_bits(arg2, bits2); SASSERT(bits1.size() == bits2.size()); bit_buffer new_eqs; unsigned i = bits1.size(); while (i > 0) { --i; new_eqs.push_back(m().mk_eq(bits1[i], bits2[i])); } result = m().mk_and(new_eqs.size(), new_eqs.c_ptr()); } void reduce_ite(expr * c, expr * t, expr * e, expr_ref & result) { bit_buffer t_bits; bit_buffer e_bits; get_bits(t, t_bits); get_bits(e, e_bits); SASSERT(t_bits.size() == e_bits.size()); bit_buffer new_ites; unsigned num = t_bits.size(); for (unsigned i = 0; i < num; i++) new_ites.push_back(m().mk_ite(c, t_bits[i], e_bits[i])); result = butil().mk_concat(new_ites.size(), new_ites.c_ptr()); } void reduce_num(func_decl * f, expr_ref & result) { SASSERT(f->get_num_parameters() == 2); SASSERT(f->get_parameter(0).is_rational()); SASSERT(f->get_parameter(1).is_int()); bit_buffer bits; rational v = f->get_parameter(0).get_rational(); rational two(2); unsigned sz = f->get_parameter(1).get_int(); for (unsigned i = 0; i < sz; i++) { if ((v % two).is_zero()) bits.push_back(m_bit0); else bits.push_back(m_bit1); v = div(v, two); } std::reverse(bits.begin(), bits.end()); result = butil().mk_concat(bits.size(), bits.c_ptr()); } void reduce_extract(func_decl * f, expr * arg, expr_ref & result) { bit_buffer arg_bits; get_bits(arg, arg_bits); SASSERT(arg_bits.size() == butil().get_bv_size(arg)); unsigned high = butil().get_extract_high(f); unsigned low = butil().get_extract_low(f); unsigned sz = arg_bits.size(); unsigned start = sz - 1 - high; unsigned end = sz - 1 - low; bit_buffer bits; for (unsigned i = start; i <= end; i++) { bits.push_back(arg_bits[i]); } result = butil().mk_concat(bits.size(), bits.c_ptr()); } void reduce_concat(unsigned num, expr * const * args, expr_ref & result) { bit_buffer bits; bit_buffer arg_bits; for (unsigned i = 0; i < num; i++) { expr * arg = args[i]; arg_bits.reset(); get_bits(arg, arg_bits); bits.append(arg_bits.size(), arg_bits.c_ptr()); } result = butil().mk_concat(bits.size(), bits.c_ptr()); } void reduce_bin_xor(expr * arg1, expr * arg2, expr_ref & result) { bit_buffer bits1; bit_buffer bits2; get_bits(arg1, bits1); get_bits(arg2, bits2); SASSERT(bits1.size() == bits2.size()); bit_buffer new_bits; unsigned num = bits1.size(); for (unsigned i = 0; i < num; i++) { new_bits.push_back(m().mk_ite(m().mk_eq(bits1[i], bits2[i]), m_bit0, m_bit1)); } result = butil().mk_concat(new_bits.size(), new_bits.c_ptr()); } void reduce_xor(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args > 0); #if 1 if (num_args == 1) { result = args[0]; return; } reduce_bin_xor(args[0], args[1], result); for (unsigned i = 2; i < num_args; i++) { reduce_bin_xor(result, args[i], result); } #else ptr_buffer args_bits; for (unsigned i = 0; i < num_args; i++) { bit_buffer * buff_i = alloc(bit_buffer); get_bits(args[i], *buff_i); args_bits.push_back(buff_i); } bit_buffer new_bits; unsigned sz = butil().get_bv_size(args[0]); for (unsigned i = 0; i < sz; i++) { ptr_buffer eqs; for (unsigned j = 0; j < num_args; j++) { bit_buffer * buff_j = args_bits[j]; eqs.push_back(m().mk_eq(buff_j->get(i), m_bit1)); } expr * cond = m().mk_xor(eqs.size(), eqs.c_ptr()); new_bits.push_back(m().mk_ite(cond, m_bit1, m_bit0)); } result = butil().mk_concat(new_bits.size(), new_bits.c_ptr()); std::for_each(args_bits.begin(), args_bits.end(), delete_proc()); #endif } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = 0; if (num == 0 && f->get_family_id() == null_family_id && butil().is_bv_sort(f->get_range())) { mk_const(f, result); return BR_DONE; } if (m().is_eq(f)) { SASSERT(num == 2); if (butil().is_bv(args[0])) { reduce_eq(args[0], args[1], result); return BR_DONE; } return BR_FAILED; } if (m().is_ite(f)) { SASSERT(num == 3); if (butil().is_bv(args[1])) { reduce_ite(args[0], args[1], args[2], result); return BR_DONE; } return BR_FAILED; } if (f->get_family_id() == butil().get_family_id()) { switch (f->get_decl_kind()) { case OP_BV_NUM: reduce_num(f, result); return BR_DONE; case OP_EXTRACT: SASSERT(num == 1); reduce_extract(f, args[0], result); return BR_DONE; case OP_CONCAT: reduce_concat(num, args, result); return BR_DONE; case OP_BXOR: reduce_xor(num, args, result); return BR_DONE; default: UNREACHABLE(); return BR_FAILED; } } if (butil().is_bv_sort(f->get_range())) { blast_bv_term(m().mk_app(f, num, args), result); return BR_DONE; } return BR_FAILED; } bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { UNREACHABLE(); return false; } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { } }; struct imp { rw m_rw; unsigned m_num_steps; imp(ast_manager & m, params_ref const & p): m_rw(m, p) { } struct not_target {}; struct visitor { family_id m_bv_fid; visitor(family_id bv_fid):m_bv_fid(bv_fid) {} void operator()(var const * n) { throw not_target(); } void operator()(app const * n) { if (n->get_family_id() == m_bv_fid) { switch (n->get_decl_kind()) { case OP_BV_NUM: case OP_EXTRACT: case OP_CONCAT: return; case OP_BXOR: // it doesn't payoff to do the reduction in this case. throw not_target(); default: throw not_target(); } } } void operator()(quantifier const * n) { throw not_target(); } }; bool is_target(goal const & g) const { expr_fast_mark1 visited; unsigned sz = g.size(); visitor proc(m_rw.cfg().butil().get_family_id()); try { for (unsigned i = 0; i < sz; i++) { expr * f = g.form(i); for_each_expr_core(proc, visited, f); } } catch (not_target) { return false; } return true; } ast_manager & m() const { return m_rw.m(); } void set_cancel(bool f) { m_rw.set_cancel(f); } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { mc = 0; pc = 0; core = 0; if (!is_target(*g)) throw tactic_exception("bv1 blaster cannot be applied to goal"); tactic_report report("bv1-blaster", *g); m_num_steps = 0; bool proofs_enabled = g->proofs_enabled(); expr_ref new_curr(m()); proof_ref new_pr(m()); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { if (g->inconsistent()) break; expr * curr = g->form(idx); m_rw(curr, new_curr, new_pr); m_num_steps += m_rw.get_num_steps(); if (proofs_enabled) { proof * pr = g->pr(idx); new_pr = m().mk_modus_ponens(pr, new_pr); } g->update(idx, new_curr, new_pr, g->dep(idx)); } if (g->models_enabled()) mc = mk_bv1_blaster_model_converter(m(), m_rw.cfg().m_const2bits); g->inc_depth(); result.push_back(g.get()); m_rw.cfg().cleanup(); } unsigned get_num_steps() const { return m_num_steps; } }; imp * m_imp; params_ref m_params; public: bv1_blaster_tactic(ast_manager & m, params_ref const & p = params_ref()): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(bv1_blaster_tactic, m, m_params); } virtual ~bv1_blaster_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->m_rw.cfg().updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { insert_max_memory(r); insert_max_steps(r); } bool is_target(goal const & g) const { return m_imp->is_target(g); } /** \brief "Blast" bit-vectors of size n in s into bit-vectors of size 1. If s contains other bit-vectors operators different from concat/extract, then this is method is a NO-OP. It also does not support quantifiers. Return a model_converter that converts any model for the updated set into a model for the old set. */ virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(g, result, mc, pc, core); } virtual void cleanup() { imp * d = alloc(imp, m_imp->m(), m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } unsigned get_num_steps() const { return m_imp->get_num_steps(); } protected: virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_bv1_blaster_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(bv1_blaster_tactic, m, p)); } class is_qfbv_eq_probe : public probe { public: virtual result operator()(goal const & g) { bv1_blaster_tactic t(g.m()); return t.is_target(g); } }; probe * mk_is_qfbv_eq_probe() { return alloc(is_qfbv_eq_probe); } z3-4.4.0/src/tactic/bv/bit_blaster_model_converter.h0000644000175000017500000000105212540347414022222 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bit_blaster_model_convert.h Abstract: Model converter for bit-blasting tactics. Author: Leonardo (leonardo) 2011-05-09 Notes: --*/ #ifndef _BIT_BLASTER_MODEL_CONVERTER_H_ #define _BIT_BLASTER_MODEL_CONVERTER_H_ #include"model_converter.h" model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits); model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map const & const2bits); #endif z3-4.4.0/src/tactic/bv/bit_blaster_model_converter.cpp0000644000175000017500000001604512540347414022565 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bit_blaster_model_convert.cpp Abstract: Model converter for bit-blasting tactics. Author: Leonardo (leonardo) 2011-05-09 Notes: --*/ #include"model.h" #include"model_pp.h" #include"model_converter.h" #include"bv_decl_plugin.h" #include"ast_smt2_pp.h" /** If TO_BOOL == true, then bit-vectors of size n were blasted into n-tuples of Booleans. If TO_BOOL == false, then bit-vectors of size n were blasted into n-tuples of bit-vectors of size 1. */ template struct bit_blaster_model_converter : public model_converter { func_decl_ref_vector m_vars; expr_ref_vector m_bits; ast_manager & m() const { return m_vars.get_manager(); } bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits):m_vars(m), m_bits(m) { obj_map::iterator it = const2bits.begin(); obj_map::iterator end = const2bits.end(); for (; it != end; ++it) { func_decl * v = it->m_key; expr * bits = it->m_value; SASSERT(!TO_BOOL || is_app_of(bits, m.get_family_id("bv"), OP_MKBV)); SASSERT(TO_BOOL || is_app_of(bits, m.get_family_id("bv"), OP_CONCAT)); m_vars.push_back(v); m_bits.push_back(bits); } } virtual ~bit_blaster_model_converter() { } void collect_bits(obj_hashtable & bits) { unsigned sz = m_bits.size(); for (unsigned i = 0; i < sz; i++) { expr * bs = m_bits.get(i); SASSERT(!TO_BOOL || is_app_of(bs, m().get_family_id("bv"), OP_MKBV)); SASSERT(TO_BOOL || is_app_of(bs, m().get_family_id("bv"), OP_CONCAT)); unsigned num_args = to_app(bs)->get_num_args(); for (unsigned j = 0; j < num_args; j++) { expr * bit = to_app(bs)->get_arg(j); SASSERT(!TO_BOOL || m().is_bool(bit)); SASSERT(TO_BOOL || is_sort_of(m().get_sort(bit), m().get_family_id("bv"), BV_SORT)); SASSERT(is_uninterp_const(bit)); bits.insert(to_app(bit)->get_decl()); } } TRACE("blaster_mc", tout << "bits that should not be included in the model:\n"; obj_hashtable::iterator it = bits.begin(); obj_hashtable::iterator end = bits.end(); for (; it != end; ++it) { tout << (*it)->get_name() << " "; } tout << "\n";); } void copy_non_bits(obj_hashtable & bits, model * old_model, model * new_model) { unsigned num = old_model->get_num_constants(); for (unsigned i = 0; i < num; i++) { func_decl * f = old_model->get_constant(i); if (bits.contains(f)) continue; TRACE("blaster_mc", tout << "non-bit: " << f->get_name() << "\n";); expr * fi = old_model->get_const_interp(f); new_model->register_decl(f, fi); } TRACE("blaster_mc", tout << "after copy non bits:\n"; model_pp(tout, *new_model);); new_model->copy_func_interps(*old_model); new_model->copy_usort_interps(*old_model); TRACE("blaster_mc", tout << "after copying functions and sorts:\n"; model_pp(tout, *new_model);); } void mk_bvs(model * old_model, model * new_model) { bv_util util(m()); rational val; rational two(2); SASSERT(m_vars.size() == m_bits.size()); unsigned sz = m_vars.size(); for (unsigned i = 0; i < sz; i++) { expr * bs = m_bits.get(i); val.reset(); unsigned bv_sz = to_app(bs)->get_num_args(); if (TO_BOOL) { SASSERT(is_app_of(bs, m().get_family_id("bv"), OP_MKBV)); unsigned j = bv_sz; while (j > 0) { --j; val *= two; expr * bit = to_app(bs)->get_arg(j); SASSERT(m().is_bool(bit)); SASSERT(is_uninterp_const(bit)); func_decl * bit_decl = to_app(bit)->get_decl(); expr * bit_val = old_model->get_const_interp(bit_decl); // remark: if old_model does not assign bit_val, then assume it is false. if (bit_val != 0 && m().is_true(bit_val)) val++; } } else { SASSERT(is_app_of(bs, m().get_family_id("bv"), OP_CONCAT)); for (unsigned j = 0; j < bv_sz; j++) { val *= two; expr * bit = to_app(bs)->get_arg(j); SASSERT(util.is_bv(bit)); SASSERT(util.get_bv_size(bit) == 1); SASSERT(is_uninterp_const(bit)); func_decl * bit_decl = to_app(bit)->get_decl(); expr * bit_val = old_model->get_const_interp(bit_decl); // remark: if old_model does not assign bit_val, then assume it is false. if (bit_val != 0 && !util.is_zero(bit_val)) val++; } } expr * new_val = util.mk_numeral(val, bv_sz); new_model->register_decl(m_vars.get(i), new_val); } } virtual void operator()(model_ref & md, unsigned goal_idx) { SASSERT(goal_idx == 0); model * new_model = alloc(model, m()); obj_hashtable bits; collect_bits(bits); copy_non_bits(bits, md.get(), new_model); mk_bvs(md.get(), new_model); md = new_model; } virtual void operator()(model_ref & md) { operator()(md, 0); } virtual void display(std::ostream & out) { out << "(bit-blaster-model-converter"; unsigned sz = m_vars.size(); for (unsigned i = 0; i < sz; i++) { out << "\n (" << m_vars.get(i)->get_name() << " "; unsigned indent = m_vars.get(i)->get_name().size() + 4; out << mk_ismt2_pp(m_bits.get(i), m(), indent) << ")"; } out << ")" << std::endl; } protected: bit_blaster_model_converter(ast_manager & m):m_vars(m), m_bits(m) { } public: virtual model_converter * translate(ast_translation & translator) { bit_blaster_model_converter * res = alloc(bit_blaster_model_converter, translator.to()); for (unsigned i = 0; i < m_vars.size(); i++) res->m_vars.push_back(translator(m_vars[i].get())); for (unsigned i = 0; i < m_bits.size(); i++) res->m_bits.push_back(translator(m_bits[i].get())); return res; } }; model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits) { return alloc(bit_blaster_model_converter, m, const2bits); } model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map const & const2bits) { return alloc(bit_blaster_model_converter, m, const2bits); } z3-4.4.0/src/tactic/bv/bv_size_reduction_tactic.h0000644000175000017500000000146012540347414021530 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: bv_size_reduction.h Abstract: Reduce the number of bits used to encode constants, by using signed bounds. Example: suppose x is a bit-vector of size 8, and we have signed bounds for x such that: -2 <= x <= 2 Then, x can be replaced by ((sign-extend 5) k) where k is a fresh bit-vector constant of size 3. Author: Leonardo (leonardo) 2012-02-19 Notes: --*/ #ifndef _BV_SIZE_REDUCTION_TACTIC_H_ #define _BV_SIZE_REDUCTION_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_bv_size_reduction_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("reduce-bv-size", "try to reduce bit-vector sizes using inequalities.", "mk_bv_size_reduction_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/bv/bit_blaster_tactic.cpp0000644000175000017500000001210112540347414020632 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bit_blaster_tactic.cpp Abstract: Apply bit-blasting to a given goal Author: Leonardo (leonardo) 2011-10-25 Notes: --*/ #include"tactical.h" #include"bit_blaster_model_converter.h" #include"bit_blaster_rewriter.h" #include"ast_pp.h" #include"model_pp.h" #include"rewriter_types.h" class bit_blaster_tactic : public tactic { struct imp { bit_blaster_rewriter m_rewriter; unsigned m_num_steps; bool m_blast_quant; imp(ast_manager & m, params_ref const & p): m_rewriter(m, p) { updt_params(p); } void updt_params_core(params_ref const & p) { m_blast_quant = p.get_bool("blast_quant", false); } void updt_params(params_ref const & p) { m_rewriter.updt_params(p); updt_params_core(p); } ast_manager & m() const { return m_rewriter.m(); } void set_cancel(bool f) { m_rewriter.set_cancel(f); } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { mc = 0; pc = 0; core = 0; bool proofs_enabled = g->proofs_enabled(); if (proofs_enabled && m_blast_quant) throw tactic_exception("quantified variable blasting does not support proof generation"); tactic_report report("bit-blaster", *g); TRACE("before_bit_blaster", g->display(tout);); m_num_steps = 0; expr_ref new_curr(m()); proof_ref new_pr(m()); unsigned size = g->size(); bool change = false; for (unsigned idx = 0; idx < size; idx++) { if (g->inconsistent()) break; expr * curr = g->form(idx); m_rewriter(curr, new_curr, new_pr); m_num_steps += m_rewriter.get_num_steps(); if (proofs_enabled) { proof * pr = g->pr(idx); new_pr = m().mk_modus_ponens(pr, new_pr); } if (curr != new_curr) { change = true; TRACE("bit_blaster", tout << mk_pp(curr, m()) << " -> " << mk_pp(new_curr, m()) << "\n";); g->update(idx, new_curr, new_pr, g->dep(idx)); } } if (change && g->models_enabled()) mc = mk_bit_blaster_model_converter(m(), m_rewriter.const2bits()); else mc = 0; g->inc_depth(); result.push_back(g.get()); TRACE("after_bit_blaster", g->display(tout); if (mc) mc->display(tout); tout << "\n";); m_rewriter.cleanup(); } unsigned get_num_steps() const { return m_num_steps; } }; imp * m_imp; params_ref m_params; public: bit_blaster_tactic(ast_manager & m, params_ref const & p): m_params(p){ m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(bit_blaster_tactic, m, m_params); } virtual ~bit_blaster_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { insert_max_memory(r); insert_max_steps(r); r.insert("blast_mul", CPK_BOOL, "(default: true) bit-blast multipliers (and dividers, remainders)."); r.insert("blast_add", CPK_BOOL, "(default: true) bit-blast adders."); r.insert("blast_quant", CPK_BOOL, "(default: false) bit-blast quantified variables."); r.insert("blast_full", CPK_BOOL, "(default: false) bit-blast any term with bit-vector sort, this option will make E-matching ineffective in any pattern containing bit-vector terms."); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { try { (*m_imp)(g, result, mc, pc, core); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } virtual void cleanup() { imp * d = alloc(imp, m_imp->m(), m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } unsigned get_num_steps() const { return m_imp->get_num_steps(); } protected: virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_bit_blaster_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(bit_blaster_tactic, m, p)); } z3-4.4.0/src/tactic/bv/bv1_blaster_tactic.h0000644000175000017500000000202012540347414020210 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv1_blaster_tactic.h Abstract: Rewriter for "blasting" bit-vectors of size n into bit-vectors of size 1. This rewriter only supports concat and extract operators. This transformation is useful for handling benchmarks that contain many BV equalities. Remark: other operators can be mapped into concat/extract by using the simplifiers. Author: Leonardo (leonardo) 2011-10-25 Notes: --*/ #ifndef _BV1_BLASTER_TACTIC_H_ #define _BV1_BLASTER_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_bv1_blaster_tactic(ast_manager & m, params_ref const & p = params_ref()); probe * mk_is_qfbv_eq_probe(); /* ADD_TACTIC("bv1-blast", "reduce bit-vector expressions into bit-vectors of size 1 (notes: only equality, extract and concat are supported).", "mk_bv1_blaster_tactic(m, p)") ADD_PROBE("is-qfbv-eq", "true if the goal is in a fragment of QF_BV which uses only =, extract, concat.", "mk_is_qfbv_eq_probe()") */ #endif z3-4.4.0/src/tactic/bv/max_bv_sharing_tactic.cpp0000644000175000017500000002600612540347414021340 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: max_bv_sharing_tactic.cpp Abstract: Rewriter for "maximing" the number of shared terms. The idea is to rewrite AC terms to maximize sharing. This rewriter is particularly useful for reducing the number of Adders and Multipliers before "bit-blasting". Author: Leonardo de Moura (leonardo) 2011-12-29. Revision History: --*/ #include"tactical.h" #include"bv_decl_plugin.h" #include"rewriter_def.h" #include"obj_pair_hashtable.h" #include"ast_lt.h" #include"cooperate.h" class max_bv_sharing_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { typedef std::pair expr_pair; typedef obj_pair_hashtable set; bv_util m_util; set m_add_apps; set m_mul_apps; set m_xor_apps; set m_or_apps; unsigned long long m_max_memory; unsigned m_max_steps; unsigned m_max_args; ast_manager & m() const { return m_util.get_manager(); } rw_cfg(ast_manager & m, params_ref const & p): m_util(m) { updt_params(p); } void cleanup() { m_add_apps.finalize(); m_mul_apps.finalize(); m_or_apps.finalize(); m_xor_apps.finalize(); } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_max_steps = p.get_uint("max_steps", UINT_MAX); m_max_args = p.get_uint("max_args", 128); } bool max_steps_exceeded(unsigned num_steps) const { cooperate("max bv sharing"); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); return num_steps > m_max_steps; } set & f2set(func_decl * f) { switch (f->get_decl_kind()) { case OP_BADD: return m_add_apps; case OP_BMUL: return m_mul_apps; case OP_BXOR: return m_xor_apps; case OP_BOR: return m_or_apps; default: UNREACHABLE(); return m_or_apps; // avoid compilation error } } expr * reuse(set & s, func_decl * f, expr * arg1, expr * arg2) { if (s.contains(expr_pair(arg1, arg2))) return m().mk_app(f, arg1, arg2); if (s.contains(expr_pair(arg2, arg1))) return m().mk_app(f, arg2, arg1); return 0; } struct ref_count_lt { bool operator()(expr * t1, expr * t2) const { if (t1->get_ref_count() < t2->get_ref_count()) return true; return (t1->get_ref_count() == t2->get_ref_count()) && lt(t1, t2); } }; br_status reduce_ac_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { set & s = f2set(f); if (num_args == 2) { if (!m_util.is_numeral(args[0]) && !m_util.is_numeral(args[1])) s.insert(expr_pair(args[0], args[1])); return BR_FAILED; } ptr_buffer _args; bool first = false; expr * num = 0; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (num == 0 && m_util.is_numeral(arg)) { if (i == 0) first = true; num = arg; } else { _args.push_back(arg); } } num_args = _args.size(); // std::sort(_args.begin(), _args.end(), ref_count_lt()); // std::sort(_args.begin(), _args.end(), ast_to_lt()); try_to_reuse: if (num_args > 1 && num_args < m_max_args) { for (unsigned i = 0; i < num_args - 1; i++) { for (unsigned j = i + 1; j < num_args; j++) { expr * r = reuse(s, f, _args[i], _args[j]); if (r != 0) { TRACE("bv_sharing_detail", tout << "reusing args: " << i << " " << j << "\n";); _args[i] = r; SASSERT(num_args > 1); for (unsigned w = j; w < num_args - 1; w++) { _args[w] = _args[w+1]; } num_args--; goto try_to_reuse; } } } } // TODO: // some benchmarks are more efficiently solved using a tree-like structure (better sharing) // other benchmarks are more efficiently solved using a chain-like structure (better propagation for arguments "closer to the output"). // // One possible solution is to do a global analysis that finds a good order that increases sharing without affecting // propagation. // // Another cheap trick is to create an option, and try both for a small amount of time. #if 0 SASSERT(num_args > 0); if (num_args == 1) { result = _args[0]; } else { // ref_count_lt is not a total order on expr's std::stable_sort(_args.c_ptr(), _args.c_ptr() + num_args, ref_count_lt()); result = m().mk_app(f, _args[0], _args[1]); for (unsigned i = 2; i < num_args; i++) { result = m().mk_app(f, result.get(), _args[i]); } } if (num != 0) { if (first) result = m().mk_app(f, num, result); else result = m().mk_app(f, result, num); } return BR_DONE; #else // Create "tree-like circuit" while (true) { TRACE("bv_sharing_detail", tout << "tree-loop: num_args: " << num_args << "\n";); unsigned j = 0; for (unsigned i = 0; i < num_args; i += 2, j++) { if (i == num_args - 1) { _args[j] = _args[i]; } else { s.insert(expr_pair(_args[i], _args[i+1])); _args[j] = m().mk_app(f, _args[i], _args[i+1]); } } num_args = j; if (num_args == 1) { if (num == 0) { result = _args[0]; } else { if (first) result = m().mk_app(f, num, _args[0]); else result = m().mk_app(f, _args[0], num); } return BR_DONE; } } #endif } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (f->get_family_id() != m_util.get_family_id()) return BR_FAILED; switch (f->get_decl_kind()) { case OP_BADD: case OP_BMUL: case OP_BOR: case OP_BXOR: result_pr = 0; return reduce_ac_app(f, num, args, result); default: return BR_FAILED; } } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { } }; struct imp { rw m_rw; unsigned m_num_steps; imp(ast_manager & m, params_ref const & p): m_rw(m, p) { } ast_manager & m() const { return m_rw.m(); } void set_cancel(bool f) { m_rw.set_cancel(f); } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("max-bv-sharing", *g); bool produce_proofs = g->proofs_enabled(); expr_ref new_curr(m()); proof_ref new_pr(m()); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { if (g->inconsistent()) break; expr * curr = g->form(idx); m_rw(curr, new_curr, new_pr); m_num_steps += m_rw.get_num_steps(); if (produce_proofs) { proof * pr = g->pr(idx); new_pr = m().mk_modus_ponens(pr, new_pr); } g->update(idx, new_curr, new_pr, g->dep(idx)); } m_rw.cfg().cleanup(); g->inc_depth(); result.push_back(g.get()); TRACE("max_bv_sharing", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: max_bv_sharing_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(max_bv_sharing_tactic, m, m_params); } virtual ~max_bv_sharing_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->m_rw.cfg().updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { insert_max_memory(r); insert_max_steps(r); r.insert("max_args", CPK_UINT, "(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic."); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } virtual void cleanup() { imp * d = alloc(imp, m_imp->m(), m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_max_bv_sharing_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(max_bv_sharing_tactic, m, p)); } z3-4.4.0/src/tactic/core/0000755000175000017500000000000012540347414014633 5ustar michaelmichaelz3-4.4.0/src/tactic/core/reduce_args_tactic.h0000644000175000017500000000114712540347414020621 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: reduce_args_tactic.h Abstract: Reduce the number of arguments in function applications. Author: Leonardo (leonardo) 2012-02-19 Notes: --*/ #ifndef _REDUCE_ARGS_TACTIC_H_ #define _REDUCE_ARGS_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_reduce_args_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("reduce-args", "reduce the number of arguments of function applications, when for all occurrences of a function f the i-th is a value.", "mk_reduce_args_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/core/pb_preprocess_tactic.cpp0000644000175000017500000005305512540347414021544 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: pb_preprocess_tactic.cpp Abstract: Pre-process pseudo-Boolean inequalities using generalized Davis Putnam (resolution) to eliminate variables. Author: Nikolaj Bjorner (nbjorner) 2013-12-23 Notes: Resolution for PB constraints require the implicit inequalities that each variable ranges over [0,1] so not all resolvents produce smaller sets of clauses. We here implement subsumption resolution. x + y >= 1 A~x + B~y + Cz >= k --------------------- Cz >= k - B where A <= B, x, y do not occur elsewhere. --*/ #include "pb_preprocess_tactic.h" #include "tactical.h" #include "for_each_expr.h" #include "pb_decl_plugin.h" #include "th_rewriter.h" #include "expr_substitution.h" #include "ast_pp.h" class pb_preproc_model_converter : public model_converter { ast_manager& m; pb_util pb; expr_ref_vector m_refs; svector > m_const; public: pb_preproc_model_converter(ast_manager& m):m(m), pb(m), m_refs(m) {} virtual void operator()(model_ref & mdl, unsigned goal_idx) { SASSERT(goal_idx == 0); for (unsigned i = 0; i < m_const.size(); ++i) { mdl->register_decl(m_const[i].first->get_decl(), m_const[i].second); } } void set_value(expr* e, bool p) { while (m.is_not(e, e)) { p = !p; } SASSERT(is_app(e)); set_value_p(to_app(e), p?m.mk_true():m.mk_false()); } virtual model_converter * translate(ast_translation & translator) { pb_preproc_model_converter* mc = alloc(pb_preproc_model_converter, translator.to()); for (unsigned i = 0; i < m_const.size(); ++i) { mc->set_value_p(translator(m_const[i].first), translator(m_const[i].second)); } return mc; } private: void set_value_p(app* e, expr* v) { SASSERT(e->get_num_args() == 0); SASSERT(is_uninterp_const(e)); m_const.push_back(std::make_pair(e, v)); m_refs.push_back(e); m_refs.push_back(v); } }; class pb_preprocess_tactic : public tactic { struct rec { unsigned_vector pos, neg; rec() { } }; typedef obj_map var_map; ast_manager& m; pb_util pb; var_map m_vars; unsigned_vector m_ge; unsigned_vector m_other; bool m_progress; th_rewriter m_r; struct declassifier { var_map& m_vars; declassifier(var_map& v): m_vars(v) {} void operator()(app* e) { if (m_vars.contains(e)) { m_vars.remove(e); } } void operator()(var*) {} void operator()(quantifier*) {} }; void display_annotation(std::ostream& out, goal_ref const& g) { for (unsigned i = 0; i < m_ge.size(); ++i) { out << "ge " << m_ge[i] << ": " << mk_pp(g->form(m_ge[i]), m) << "\n"; } for (unsigned i = 0; i < m_other.size(); ++i) { out << "ot " << m_other[i] << ": " << mk_pp(g->form(m_other[i]), m) << "\n"; } var_map::iterator it = m_vars.begin(); var_map::iterator end = m_vars.end(); for (; it != end; ++it) { app* e = it->m_key; unsigned_vector const& pos = it->m_value.pos; unsigned_vector const& neg = it->m_value.neg; out << mk_pp(e, m) << ": "; for (unsigned i = 0; i < pos.size(); ++i) { out << "p: " << pos[i] << " "; } for (unsigned i = 0; i < neg.size(); ++i) { out << "n: " << neg[i] << " "; } out << "\n"; } } public: pb_preprocess_tactic(ast_manager& m, params_ref const& p = params_ref()): m(m), pb(m), m_r(m) {} virtual ~pb_preprocess_tactic() {} virtual tactic * translate(ast_manager & m) { return alloc(pb_preprocess_tactic, m); } virtual void operator()( goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); pc = 0; core = 0; if (g->unsat_core_enabled()) { throw tactic_exception("pb-preprocess does not support cores"); } if (g->proofs_enabled()) { throw tactic_exception("pb-preprocess does not support proofs"); } pb_preproc_model_converter* pp = alloc(pb_preproc_model_converter, m); mc = pp; g->inc_depth(); result.push_back(g.get()); while (simplify(g, *pp)); // decompose(g); } bool simplify(goal_ref const& g, pb_preproc_model_converter& mc) { reset(); normalize(g); if (g->inconsistent()) { return false; } for (unsigned i = 0; i < g->size(); ++i) { process_vars(i, g); } if (m_ge.empty()) { return false; } for (unsigned i = 0; i < m_ge.size(); ++i) { classify_vars(i, to_app(g->form(m_ge[i]))); } declassifier dcl(m_vars); expr_mark visited; for (unsigned i = 0; !m_vars.empty() && i < m_other.size(); ++i) { for_each_expr(dcl, visited, g->form(m_other[i])); } if (m_vars.empty()) { return false; } // display_annotation(tout, g); m_progress = false; // first eliminate variables var_map::iterator it = next_resolvent(m_vars.begin()); while (it != m_vars.end()) { app * e = it->m_key; rec const& r = it->m_value; if (r.pos.empty()) { replace(r.neg, e, m.mk_false(), g); mc.set_value(e, false); } else if (r.neg.empty()) { replace(r.pos, e, m.mk_true(), g); mc.set_value(e, true); } if (g->inconsistent()) return false; ++it; it = next_resolvent(it); } // now resolve clauses. it = next_resolvent(m_vars.begin()); while (it != m_vars.end()) { app * e = it->m_key; SASSERT(is_uninterp_const(e)); rec const& r = it->m_value; if (r.pos.size() == 1 && !r.neg.empty()) { resolve(mc, r.pos[0], r.neg, e, true, g); } else if (r.neg.size() == 1 && !r.pos.empty()) { resolve(mc, r.neg[0], r.pos, e, false, g); } if (g->inconsistent()) return false; ++it; it = next_resolvent(it); } // now check for subsumption. for (unsigned i = 0; i < m_ge.size(); ++i) { expr_ref_vector args1(m), args2(m); vector coeffs1, coeffs2; rational k1, k2; expr* fml = g->form(m_ge[i]); if (!to_ge(fml, args1, coeffs1, k1)) continue; if (args1.empty()) continue; expr* arg = args1[0].get(); bool neg = m.is_not(arg, arg); if (!is_uninterp_const(arg)) continue; if (!m_vars.contains(to_app(arg))) continue; rec const& r = m_vars.find(to_app(arg)); unsigned_vector const& pos = neg?r.neg:r.pos; for (unsigned j = 0; j < pos.size(); ++j) { unsigned k = pos[j]; if (k == i) continue; if (!to_ge(g->form(k), args2, coeffs2, k2)) continue; if (subsumes(args1, coeffs1, k1, args2, coeffs2, k2)) { IF_VERBOSE(3, verbose_stream() << "replace " << mk_pp(g->form(k), m) << "\n";); g->update(k, m.mk_true()); m_progress = true; } } } g->elim_true(); return m_progress; } virtual void set_cancel(bool f) { } virtual void updt_params(params_ref const & p) { } virtual void cleanup() { } private: void reset() { m_ge.reset(); m_other.reset(); m_vars.reset(); } expr* negate(expr* e) { if (m.is_not(e, e)) return e; return m.mk_not(e); } void normalize(goal_ref const& g) { expr* r; expr_ref tmp(m); for (unsigned i = 0; !g->inconsistent() && i < g->size(); ++i) { expr* e = g->form(i); if (m.is_not(e, r) && pb.is_ge(r)) { rational k = pb.get_k(r); rational sum(0); expr_ref_vector args(m); vector coeffs; for (unsigned j = 0; j < to_app(r)->get_num_args(); ++j) { sum += pb.get_coeff(r, j); coeffs.push_back(pb.get_coeff(r, j)); args.push_back(negate(to_app(r)->get_arg(j))); } tmp = pb.mk_ge(args.size(), coeffs.c_ptr(), args.c_ptr(), sum - k + rational::one()); g->update(i, tmp); } } } unsigned log2ceil(unsigned n) { unsigned p = 1; while (n > 0) { n /= 2; ++p; } return p; } /** \brief decompose large sums into smaller sums by intoducing auxilary variables. */ void decompose(goal_ref const& g) { expr_ref fml1(m), fml2(m); for (unsigned i = 0; !g->inconsistent() && i < g->size(); ++i) { expr* e = g->form(i); unsigned_vector cuts; if (cut(e, cuts)) { app* a = to_app(e); expr_ref_vector cut_args(m); vector cut_coeffs; if (cuts.size() < 2) continue; unsigned start = 0; for (unsigned j = 0; j < cuts.size(); ++j) { unsigned end = cuts[j]; fml1 = decompose_cut(a, start, end, cut_args, cut_coeffs); g->assert_expr(fml1); start = end; TRACE("pb", tout << fml1 << "\n";); } fml2 = pb.mk_ge(cut_args.size(), cut_coeffs.c_ptr(), cut_args.c_ptr(), pb.get_k(e)); g->update(i, fml2); TRACE("pb", tout << fml2 << "\n";); } } } bool cut(expr* e, unsigned_vector& cuts) { if (!pb.is_ge(e)) return false; if (to_app(e)->get_num_args() <= 20) return false; unsigned n = 0, cut = 0; unsigned sz = to_app(e)->get_num_args(); for (unsigned i = 0; i < sz; ++i) { rational r = pb.get_coeff(e, i); if (!r.is_unsigned()) { return false; } n += r.get_unsigned(); if (2*log2ceil(n) < cut) { cuts.push_back(i+1); n = 0; cut = 0; } else { ++cut; } } if (!cuts.empty() && cuts.back() + 20 >= sz) { cuts.pop_back(); } cuts.push_back(sz); return true; } expr_ref decompose_cut(app* e, unsigned start, unsigned end, expr_ref_vector& cut_args, vector& cut_coeffs) { unsigned n = 0, j = 1; vector coeffs; expr_ref_vector args(m); app_ref z(m); expr_ref fml1(m), fml(m); for (unsigned i = start; i < end; ++i) { rational r = pb.get_coeff(e, i); n += r.get_unsigned(); args.push_back(e->get_arg(i)); coeffs.push_back(r); } while (j <= n) { z = m.mk_fresh_const("z", m.mk_bool_sort()); coeffs.push_back(-rational(j)); args.push_back(z); cut_coeffs.push_back(rational(j)); cut_args.push_back(z); j <<= 1; } fml1 = pb.mk_ge(args.size(), coeffs.c_ptr(), args.c_ptr(), rational(0)); m_r(fml1, fml); return fml; } void process_vars(unsigned i, goal_ref const& g) { expr* r, *e; e = g->form(i); if (is_uninterp_const(e)) { m_ge.push_back(i); } else if (pb.is_ge(e) && pure_args(to_app(e))) { m_ge.push_back(i); } else if (m.is_or(e) && pure_args(to_app(e))) { m_ge.push_back(i); } else if (m.is_not(e, r) && is_uninterp_const(r)) { m_ge.push_back(i); } else { m_other.push_back(i); } } void classify_vars(unsigned idx, app* e) { expr* r; if (m.is_not(e, r) && is_uninterp_const(r)) { insert(idx, to_app(r), false); return; } if (is_uninterp_const(e)) { insert(idx, e, true); return; } for (unsigned i = 0; i < e->get_num_args(); ++i) { expr* arg = e->get_arg(i); if (m.is_true(arg) || m.is_false(arg)) { // no-op } else if (m.is_not(arg, r)) { SASSERT(is_uninterp_const(r)); insert(idx, to_app(r), false); } else { SASSERT(is_uninterp_const(arg)); insert(idx, to_app(arg), true); } } } void insert(unsigned i, app* e, bool pos) { SASSERT(is_uninterp_const(e)); if (!m_vars.contains(e)) { m_vars.insert(e, rec()); } if (pos) { m_vars.find(e).pos.push_back(i); } else { m_vars.find(e).neg.push_back(i); } } bool pure_args(app* a) const { for (unsigned i = 0; i < a->get_num_args(); ++i) { expr* e = a->get_arg(i); m.is_not(e, e); if (!is_uninterp_const(e) && !m.is_true(e) && !m.is_false(e)) { return false; } } return true; } var_map::iterator next_resolvent(var_map::iterator it) { if (it == m_vars.end()) { return it; } while (it != m_vars.end() && it->m_value.pos.size() > 1 && it->m_value.neg.size() > 1) { ++it; } return it; } rational get_coeff(unsigned num_args, expr* const* args, rational const* coeffs, expr* e) { for (unsigned i = 0; i < num_args; ++i) { if (args[i] == e) return coeffs[i]; } return rational::zero(); } // // one of the formulas are replaced by T after resolution // so if there is a pointer into that formula, we can no // longer assume variables have unique occurrences. // bool is_valid(unsigned_vector const& positions, goal_ref const& g) const { for (unsigned i = 0; i < positions.size(); ++i) { unsigned idx = positions[i]; if (m.is_true(g->form(idx))) return false; } return true; } bool is_reduction(unsigned_vector const& pos, app* fml, goal_ref const& g) { unsigned sz = fml->get_num_args(); for (unsigned i = 0; i < pos.size(); ++i) { if (!is_app(g->form(pos[i]))) return false; if (to_app(g->form(pos[i]))->get_num_args() < sz) return false; } return true; } // Implement very special case of resolution. void resolve(pb_preproc_model_converter& mc, unsigned idx1, unsigned_vector const& positions, app* e, bool pos, goal_ref const& g) { if (positions.size() != 1) return; unsigned idx2 = positions[0]; expr_ref tmp1(m), tmp2(m); expr* fml1 = g->form(idx1); expr* fml2 = g->form(idx2); TRACE("pb", tout << mk_pp(fml1, m) << " " << mk_pp(fml2, m) << "\n";); expr_ref_vector args1(m), args2(m); vector coeffs1, coeffs2; rational k1, k2; if (!to_ge(fml1, args1, coeffs1, k1)) return; if (!k1.is_one()) return; if (!to_ge(g->form(idx2), args2, coeffs2, k2)) return; // check that each variable in idx1 occurs only in idx2 unsigned min_index = 0; rational min_coeff(0); unsigned_vector indices; for (unsigned i = 0; i < args1.size(); ++i) { expr* x = args1[i].get(); m.is_not(x, x); if (!is_app(x)) return; if (!m_vars.contains(to_app(x))) return; TRACE("pb", tout << mk_pp(x, m) << "\n";); rec const& r = m_vars.find(to_app(x)); if (r.pos.size() != 1 || r.neg.size() != 1) return; if (r.pos[0] != idx2 && r.neg[0] != idx2) return; for (unsigned j = 0; j < args2.size(); ++j) { if (is_complement(args1[i].get(), args2[j].get())) { if (i == 0) { min_coeff = coeffs2[j]; } else if (min_coeff > coeffs2[j]) { min_coeff = coeffs2[j]; min_index = j; } indices.push_back(j); } } } for (unsigned i = 0; i < indices.size(); ++i) { unsigned j = indices[i]; expr* arg = args2[j].get(); if (j == min_index) { args2[j] = m.mk_false(); } else { args2[j] = m.mk_true(); } mc.set_value(arg, j != min_index); } tmp1 = pb.mk_ge(args2.size(), coeffs2.c_ptr(), args2.c_ptr(), k2); IF_VERBOSE(3, verbose_stream() << " " << tmp1 << "\n"; for (unsigned i = 0; i < args2.size(); ++i) { verbose_stream() << mk_pp(args2[i].get(), m) << " "; } verbose_stream() << "\n"; ); m_r(tmp1, tmp2); if (pb.is_ge(tmp2) && pb.get_k(to_app(tmp2)).is_one()) { tmp2 = m.mk_or(to_app(tmp2)->get_num_args(), to_app(tmp2)->get_args()); } IF_VERBOSE(3, verbose_stream() << "resolve: " << mk_pp(fml1, m) << "\n" << mk_pp(fml2, m) << "\n" << tmp1 << "\n"; verbose_stream() << "to\n" << mk_pp(fml2, m) << " -> " << tmp2 << "\n";); g->update(idx1, m.mk_true()); // proof & dependencies g->update(idx2, tmp2); // proof & dependencies m_progress = true; //IF_VERBOSE(0, if (!g->inconsistent()) display_annotation(verbose_stream(), g);); } bool is_complement(expr* x, expr* y) const { if (m.is_not(x,x)) return x == y; if (m.is_not(y,y)) return x == y; return false; } bool to_ge(expr* e, expr_ref_vector& args, vector& coeffs, rational& k) { expr* r; if (is_uninterp_const(e)) { args.push_back(e); coeffs.push_back(rational::one()); k = rational::one(); } else if (m.is_not(e, r) && is_uninterp_const(r)) { args.push_back(e); coeffs.push_back(rational::one()); k = rational::one(); } else if (pb.is_ge(e)) { app* a = to_app(e); SASSERT(pure_args(a)); for (unsigned i = 0; i < a->get_num_args(); ++i) { args.push_back(a->get_arg(i)); coeffs.push_back(pb.get_coeff(a, i)); } k = pb.get_k(e); } else if (m.is_or(e)) { app* a = to_app(e); SASSERT(pure_args(a)); for (unsigned i = 0; i < a->get_num_args(); ++i) { args.push_back(a->get_arg(i)); coeffs.push_back(rational::one()); } k = rational::one(); } else { return false; } return true; } void replace(unsigned_vector const& positions, expr* e, expr* v, goal_ref const& g) { if (!is_valid(positions, g)) return; expr_substitution sub(m); sub.insert(e, v); expr_ref tmp(m); m_r.set_substitution(&sub); for (unsigned i = 0; i < positions.size(); ++i) { unsigned idx = positions[i]; expr_ref f(m); f = g->form(idx); if (!m.is_true(f)) { m_r(f, tmp); if (tmp != f) { TRACE("pb", tout << mk_pp(f, m) << " -> " << tmp << " by " << mk_pp(e, m) << " |-> " << mk_pp(v, m) << "\n";); IF_VERBOSE(3, verbose_stream() << "replace " << mk_pp(f, m) << " -> " << tmp << "\n";); g->update(idx, tmp); // proof & dependencies. m_progress = true; } } } m_r.set_substitution(0); } bool subsumes(expr_ref_vector const& args1, vector const& coeffs1, rational const& k1, expr_ref_vector const& args2, vector const& coeffs2, rational const& k2) { if (k2 > k1) return false; for (unsigned i = 0; i < args1.size(); ++i) { bool found = false; for (unsigned j = 0; !found && j < args2.size(); ++j) { if (args1[i] == args2[j]) { if (coeffs1[i] > coeffs2[j]) return false; found = true; } } if (!found) return false; } return true; } }; tactic * mk_pb_preprocess_tactic(ast_manager & m, params_ref const & p) { return alloc(pb_preprocess_tactic, m); } z3-4.4.0/src/tactic/core/nnf_tactic.cpp0000644000175000017500000000715512540347414017457 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: nnf_tactic.cpp Abstract: NNF tactic Author: Leonardo de Moura (leonardo) 2011-12-28. Revision History: --*/ #include"nnf.h" #include"tactical.h" #include"filter_model_converter.h" class nnf_tactic : public tactic { params_ref m_params; nnf * m_nnf; struct set_nnf { nnf_tactic & m_owner; set_nnf(nnf_tactic & owner, nnf & n): m_owner(owner) { #pragma omp critical (nnf_tactic) { m_owner.m_nnf = &n; } } ~set_nnf() { #pragma omp critical (nnf_tactic) { m_owner.m_nnf = 0; } } }; public: nnf_tactic(params_ref const & p): m_params(p), m_nnf(0) { TRACE("nnf", tout << "nnf_tactic constructor: " << p << "\n";); } virtual tactic * translate(ast_manager & m) { return alloc(nnf_tactic, m_params); } virtual ~nnf_tactic() {} virtual void updt_params(params_ref const & p) { m_params = p; } virtual void collect_param_descrs(param_descrs & r) { nnf::get_param_descrs(r); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { TRACE("nnf", tout << "params: " << m_params << "\n"; g->display(tout);); SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("nnf", *g); bool produce_proofs = g->proofs_enabled(); ast_manager & m = g->m(); defined_names dnames(m); nnf local_nnf(m, dnames, m_params); set_nnf setter(*this, local_nnf); expr_ref_vector defs(m); proof_ref_vector def_prs(m); expr_ref new_curr(m); proof_ref new_pr(m); unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { expr * curr = g->form(i); local_nnf(curr, defs, def_prs, new_curr, new_pr); if (produce_proofs) { proof * pr = g->pr(i); new_pr = m.mk_modus_ponens(pr, new_pr); } g->update(i, new_curr, new_pr, g->dep(i)); } sz = defs.size(); for (unsigned i = 0; i < sz; i++) { if (produce_proofs) g->assert_expr(defs.get(i), def_prs.get(i), 0); else g->assert_expr(defs.get(i), 0, 0); } g->inc_depth(); result.push_back(g.get()); unsigned num_extra_names = dnames.get_num_names(); if (num_extra_names > 0) { filter_model_converter * fmc = alloc(filter_model_converter, m); mc = fmc; for (unsigned i = 0; i < num_extra_names; i++) fmc->insert(dnames.get_name_decl(i)); } TRACE("nnf", g->display(tout);); SASSERT(g->is_well_sorted()); } virtual void cleanup() {} virtual void set_cancel(bool f) { #pragma omp critical (nnf_tactic) { if (m_nnf) m_nnf->set_cancel(f); } } }; tactic * mk_snf_tactic(ast_manager & m, params_ref const & p) { return alloc(nnf_tactic, p); } tactic * mk_nnf_tactic(ast_manager & m, params_ref const & p) { params_ref new_p(p); new_p.set_sym("mode", symbol("full")); TRACE("nnf", tout << "mk_nnf: " << new_p << "\n";); return using_params(mk_snf_tactic(m, p), new_p); } z3-4.4.0/src/tactic/core/split_clause_tactic.h0000644000175000017500000000111312540347414021016 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: split_clause_tactic.h Abstract: Tactic that creates a subgoal for each literal in a clause (l_1 or ... or l_n). The tactic fails if the main goal does not contain any clause. Author: Leonardo (leonardo) 2011-11-21 Notes: --*/ #ifndef _SPLIT_CLAUSE_TACTIC_H_ #define _SPLIT_CLAUSE_TACTIC_H_ #include"params.h" class tactic; tactic * mk_split_clause_tactic(params_ref const & p = params_ref()); /* ADD_TACTIC("split-clause", "split a clause in many subgoals.", "mk_split_clause_tactic(p)") */ #endif z3-4.4.0/src/tactic/core/distribute_forall_tactic.h0000644000175000017500000000077712540347414022063 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: distribute_forall_tactic.h Abstract: Author: Leonardo de Moura (leonardo) 2012-02-18. --*/ #ifndef _DISTRIBUTE_FORALL_TACTIC_H_ #define _DISTRIBUTE_FORALL_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_distribute_forall_tactic(ast_manager & m, params_ref const & p); /* ADD_TACTIC("distribute-forall", "distribute forall over conjunctions.", "mk_distribute_forall_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/core/blast_term_ite_tactic.cpp0000644000175000017500000001471112540347414021667 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: blast_term_ite_tactic.cpp Abstract: Blast term if-then-else by hoisting them up. Author: Nikolaj Bjorner (nbjorner) 2013-11-4 Notes: --*/ #include"tactical.h" #include"defined_names.h" #include"rewriter_def.h" #include"filter_model_converter.h" #include"cooperate.h" #include"scoped_proof.h" // // (f (if c1 (if c2 e1 e2) e3) b c) -> // (if c1 (if c2 (f e1 b c) // class blast_term_ite_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { ast_manager& m; unsigned long long m_max_memory; // in bytes unsigned m_num_fresh; // number of expansions rw_cfg(ast_manager & _m, params_ref const & p): m(_m), m_num_fresh(0) { updt_params(p); } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); } bool max_steps_exceeded(unsigned num_steps) const { cooperate("blast term ite"); // if (memory::get_allocation_size() > m_max_memory) // throw tactic_exception(TACTIC_MAX_MEMORY_MSG); return false; } br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { if (m.is_ite(f)) { return BR_FAILED; } for (unsigned i = 0; i < num_args; ++i) { expr* c, *t, *e; if (!m.is_bool(args[i]) && m.is_ite(args[i], c, t, e)) { enable_trace("blast_term_ite"); TRACE("blast_term_ite", result = m.mk_app(f, num_args, args); tout << result << "\n";); expr_ref e1(m), e2(m); ptr_vector args1(num_args, args); args1[i] = t; ++m_num_fresh; e1 = m.mk_app(f, num_args, args1.c_ptr()); if (t == e) { result = e1; return BR_REWRITE1; } args1[i] = e; e2 = m.mk_app(f, num_args, args1.c_ptr()); result = m.mk_app(f, num_args, args); result = m.mk_ite(c, e1, e2); return BR_REWRITE3; } } return BR_FAILED; } bool rewrite_patterns() const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { return mk_app_core(f, num, args, result); } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { } }; struct imp { ast_manager & m; rw m_rw; imp(ast_manager & _m, params_ref const & p): m(_m), m_rw(m, p) { } void set_cancel(bool f) { m_rw.set_cancel(f); } void updt_params(params_ref const & p) { m_rw.cfg().updt_params(p); } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("blast-term-ite", *g); bool produce_proofs = g->proofs_enabled(); expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { expr * curr = g->form(idx); m_rw(curr, new_curr, new_pr); if (produce_proofs) { proof * pr = g->pr(idx); new_pr = m.mk_modus_ponens(pr, new_pr); } g->update(idx, new_curr, new_pr, g->dep(idx)); } report_tactic_progress(":blast-term-ite-consts", m_rw.m_cfg.m_num_fresh); g->inc_depth(); result.push_back(g.get()); TRACE("blast_term_ite", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: blast_term_ite_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(blast_term_ite_tactic, m, m_params); } virtual ~blast_term_ite_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->m_rw.cfg().updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { insert_max_memory(r); insert_max_steps(r); r.insert("max_args", CPK_UINT, "(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic."); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } virtual void cleanup() { ast_manager & m = m_imp->m; imp * d = m_imp; #pragma omp critical (tactic_cancel) { m_imp = 0; } dealloc(d); d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { m_imp = d; } } virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } static void blast_term_ite(expr_ref& fml) { ast_manager& m = fml.get_manager(); scoped_no_proof _sp(m); params_ref p; rw ite_rw(m, p); expr_ref tmp(m); ite_rw(fml, tmp); fml = tmp; } }; tactic * mk_blast_term_ite_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(blast_term_ite_tactic, m, p)); } void blast_term_ite(expr_ref& fml) { blast_term_ite_tactic::blast_term_ite(fml); } z3-4.4.0/src/tactic/core/propagate_values_tactic.h0000644000175000017500000000114612540347414021676 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: propagate_values_tactic.h Abstract: Propagate values using equalities of the form (= t v) where v is a value, and atoms t and (not t) Author: Leonardo de Moura (leonardo) 2011-12-28. Revision History: --*/ #ifndef _PROPAGATE_VALUES_TACTIC_H_ #define _PROPAGATE_VALUES_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_propagate_values_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("propagate-values", "propagate constants.", "mk_propagate_values_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/core/tseitin_cnf_tactic.h0000644000175000017500000000166512540347414020650 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: tseitin_cnf_tactic.h Abstract: Puts an assertion set in CNF. Auxiliary variables are used to avoid blowup. Author: Leonardo (leonardo) 2011-12-29 Notes: --*/ #ifndef _TSEITIN_CNF_TACTIC_H_ #define _TSEITIN_CNF_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_tseitin_cnf_core_tactic(ast_manager & m, params_ref const & p = params_ref()); tactic * mk_tseitin_cnf_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("tseitin-cnf", "convert goal into CNF using tseitin-like encoding (note: quantifiers are ignored).", "mk_tseitin_cnf_tactic(m, p)") ADD_TACTIC("tseitin-cnf-core", "convert goal into CNF using tseitin-like encoding (note: quantifiers are ignored). This tactic does not apply required simplifications to the input goal like the tseitin-cnf tactic.", "mk_tseitin_cnf_core_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/core/solve_eqs_tactic.cpp0000644000175000017500000007607512540347414020705 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: solve_eqs_tactic.cpp Abstract: Tactic for solving equations and performing gaussian elimination. Author: Leonardo de Moura (leonardo) 2011-12-29. Revision History: --*/ #include"tactical.h" #include"expr_replacer.h" #include"extension_model_converter.h" #include"occurs.h" #include"cooperate.h" #include"goal_shared_occs.h" #include"ast_smt2_pp.h" class solve_eqs_tactic : public tactic { struct imp { typedef extension_model_converter gmc; ast_manager & m_manager; expr_replacer * m_r; bool m_r_owner; arith_util m_a_util; obj_map m_num_occs; unsigned m_num_steps; unsigned m_num_eliminated_vars; bool m_theory_solver; bool m_ite_solver; unsigned m_max_occs; scoped_ptr m_subst; scoped_ptr m_norm_subst; expr_sparse_mark m_candidate_vars; expr_sparse_mark m_candidate_set; ptr_vector m_candidates; ptr_vector m_vars; ptr_vector m_ordered_vars; bool m_produce_proofs; bool m_produce_unsat_cores; bool m_produce_models; volatile bool m_cancel; imp(ast_manager & m, params_ref const & p, expr_replacer * r, bool owner): m_manager(m), m_r(r), m_r_owner(r == 0 || owner), m_a_util(m), m_num_steps(0), m_num_eliminated_vars(0), m_cancel(false) { updt_params(p); if (m_r == 0) m_r = mk_default_expr_replacer(m); } ~imp() { if (m_r_owner) dealloc(m_r); } ast_manager & m() const { return m_manager; } void updt_params(params_ref const & p) { m_ite_solver = p.get_bool("ite_solver", true); m_theory_solver = p.get_bool("theory_solver", true); m_max_occs = p.get_uint("solve_eqs_max_occs", UINT_MAX); } void set_cancel(bool f) { m_cancel = f; m_r->set_cancel(f); } void checkpoint() { if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); cooperate("solve-eqs"); } // Check if the number of occurrences of t is below the specified threshold :solve-eqs-max-occs bool check_occs(expr * t) const { if (m_max_occs == UINT_MAX) return true; unsigned num = 0; m_num_occs.find(t, num); TRACE("solve_eqs_check_occs", tout << mk_ismt2_pp(t, m_manager) << " num_occs: " << num << " max: " << m_max_occs << "\n";); return num <= m_max_occs; } // Use: (= x def) and (= def x) bool trivial_solve(expr * lhs, expr * rhs, app_ref & var, expr_ref & def, proof_ref & pr) { if (is_uninterp_const(lhs) && !m_candidate_vars.is_marked(lhs) && !occurs(lhs, rhs) && check_occs(lhs)) { var = to_app(lhs); def = rhs; pr = 0; return true; } else if (is_uninterp_const(rhs) && !m_candidate_vars.is_marked(rhs) && !occurs(rhs, lhs) && check_occs(rhs)) { var = to_app(rhs); def = lhs; if (m_produce_proofs) pr = m().mk_commutativity(m().mk_eq(lhs, rhs)); return true; } return false; } // (ite c (= x t1) (= x t2)) --> (= x (ite c t1 t2)) bool solve_ite_core(app * ite, expr * lhs1, expr * rhs1, expr * lhs2, expr * rhs2, app_ref & var, expr_ref & def, proof_ref & pr) { if (lhs1 != lhs2) return false; if (!is_uninterp_const(lhs1) || m_candidate_vars.is_marked(lhs1)) return false; if (occurs(lhs1, ite->get_arg(0)) || occurs(lhs1, rhs1) || occurs(lhs1, rhs2)) return false; if (!check_occs(lhs1)) return false; var = to_app(lhs1); def = m().mk_ite(ite->get_arg(0), rhs1, rhs2); if (m_produce_proofs) pr = m().mk_rewrite(ite, m().mk_eq(var, def)); return true; } // (ite c (= x t1) (= x t2)) --> (= x (ite c t1 t2)) bool solve_ite(app * ite, app_ref & var, expr_ref & def, proof_ref & pr) { expr * t = ite->get_arg(1); expr * e = ite->get_arg(2); if (!m().is_eq(t) || !m().is_eq(e)) return false; expr * lhs1 = to_app(t)->get_arg(0); expr * rhs1 = to_app(t)->get_arg(1); expr * lhs2 = to_app(e)->get_arg(0); expr * rhs2 = to_app(e)->get_arg(1); return solve_ite_core(ite, lhs1, rhs1, lhs2, rhs2, var, def, pr) || solve_ite_core(ite, rhs1, lhs1, lhs2, rhs2, var, def, pr) || solve_ite_core(ite, lhs1, rhs1, rhs2, lhs2, var, def, pr) || solve_ite_core(ite, rhs1, lhs1, rhs2, lhs2, var, def, pr); } bool is_pos_literal(expr * n) { return is_app(n) && to_app(n)->get_num_args() == 0 && to_app(n)->get_family_id() == null_family_id; } bool is_neg_literal(expr * n) { if (m_manager.is_not(n)) return is_pos_literal(to_app(n)->get_arg(0)); return false; } #if 0 bool not_bool_eq(expr * f, app_ref & var, expr_ref & def, proof_ref & pr) { if (!m().is_not(f)) return false; expr * eq = to_app(f)->get_arg(0); if (!m().is_eq(f)) return false; } #endif /** \brief Given t of the form (f s_0 ... s_n), return true if x occurs in some s_j for j != i */ bool occurs_except(expr * x, app * t, unsigned i) { unsigned num = t->get_num_args(); for (unsigned j = 0; j < num; j++) { if (i != j && occurs(x, t->get_arg(j))) return true; } return false; } bool solve_arith_core(app * lhs, expr * rhs, expr * eq, app_ref & var, expr_ref & def, proof_ref & pr) { SASSERT(m_a_util.is_add(lhs)); bool is_int = m_a_util.is_int(lhs); expr * a; expr * v; rational a_val; unsigned num = lhs->get_num_args(); unsigned i; for (i = 0; i < num; i++) { expr * arg = lhs->get_arg(i); if (is_uninterp_const(arg) && !m_candidate_vars.is_marked(arg) && check_occs(arg) && !occurs(arg, rhs) && !occurs_except(arg, lhs, i)) { a_val = rational(1); v = arg; break; } else if (m_a_util.is_mul(arg, a, v) && is_uninterp_const(v) && !m_candidate_vars.is_marked(v) && m_a_util.is_numeral(a, a_val) && !a_val.is_zero() && (!is_int || a_val.is_minus_one()) && check_occs(v) && !occurs(v, rhs) && !occurs_except(v, lhs, i)) { break; } } if (i == num) return false; var = to_app(v); expr_ref inv_a(m()); if (!a_val.is_one()) { inv_a = m_a_util.mk_numeral(rational(1)/a_val, is_int); rhs = m_a_util.mk_mul(inv_a, rhs); } ptr_buffer other_args; for (unsigned j = 0; j < num; j++) { if (i != j) { if (inv_a) other_args.push_back(m_a_util.mk_mul(inv_a, lhs->get_arg(j))); else other_args.push_back(lhs->get_arg(j)); } } switch (other_args.size()) { case 0: def = rhs; break; case 1: def = m_a_util.mk_sub(rhs, other_args[0]); break; default: def = m_a_util.mk_sub(rhs, m_a_util.mk_add(other_args.size(), other_args.c_ptr())); break; } if (m_produce_proofs) pr = m().mk_rewrite(eq, m().mk_eq(var, def)); return true; } bool solve_arith(expr * lhs, expr * rhs, expr * eq, app_ref & var, expr_ref & def, proof_ref & pr) { return (m_a_util.is_add(lhs) && solve_arith_core(to_app(lhs), rhs, eq, var, def, pr)) || (m_a_util.is_add(rhs) && solve_arith_core(to_app(rhs), lhs, eq, var, def, pr)); } bool solve(expr * f, app_ref & var, expr_ref & def, proof_ref & pr) { if (m().is_eq(f)) { if (trivial_solve(to_app(f)->get_arg(0), to_app(f)->get_arg(1), var, def, pr)) return true; if (m_theory_solver) { expr * lhs = to_app(f)->get_arg(0); expr * rhs = to_app(f)->get_arg(1); if (solve_arith(lhs, rhs, f, var, def, pr)) return true; } return false; } if (m().is_iff(f)) return trivial_solve(to_app(f)->get_arg(0), to_app(f)->get_arg(1), var, def, pr); #if 0 if (not_bool_eq(f, var, def, pr)) return true; #endif if (m_ite_solver && m().is_ite(f)) return solve_ite(to_app(f), var, def, pr); if (is_pos_literal(f)) { if (m_candidate_vars.is_marked(f)) return false; var = to_app(f); def = m().mk_true(); if (m_produce_proofs) { // [rewrite]: (iff (iff l true) l) // [symmetry T1]: (iff l (iff l true)) pr = m().mk_rewrite(m().mk_eq(var, def), var); pr = m().mk_symmetry(pr); } TRACE("solve_eqs_bug2", tout << "eliminating: " << mk_ismt2_pp(f, m()) << "\n";); return true; } if (is_neg_literal(f)) { var = to_app(to_app(f)->get_arg(0)); if (m_candidate_vars.is_marked(var)) return false; def = m().mk_false(); if (m_produce_proofs) { // [rewrite]: (iff (iff l false) ~l) // [symmetry T1]: (iff ~l (iff l false)) pr = m().mk_rewrite(m().mk_eq(var, def), f); pr = m().mk_symmetry(pr); } return true; } return false; } /** \brief Start collecting candidates */ void collect(goal const & g) { m_subst->reset(); m_norm_subst->reset(); m_r->set_substitution(0); m_candidate_vars.reset(); m_candidate_set.reset(); m_candidates.reset(); m_vars.reset(); app_ref var(m()); expr_ref def(m()); proof_ref pr(m()); unsigned size = g.size(); for (unsigned idx = 0; idx < size; idx++) { checkpoint(); expr * f = g.form(idx); if (solve(f, var, def, pr)) { m_vars.push_back(var); m_candidates.push_back(f); m_candidate_set.mark(f); m_candidate_vars.mark(var); if (m_produce_proofs) { if (pr == 0) pr = g.pr(idx); else pr = m().mk_modus_ponens(g.pr(idx), pr); } m_subst->insert(var, def, pr, g.dep(idx)); } m_num_steps++; } TRACE("solve_eqs", tout << "candidate vars:\n"; ptr_vector::iterator it = m_vars.begin(); ptr_vector::iterator end = m_vars.end(); for (; it != end; ++it) { tout << mk_ismt2_pp(*it, m()) << " "; } tout << "\n";); } void sort_vars() { SASSERT(m_candidates.size() == m_vars.size()); TRACE("solve_eqs_bug", tout << "sorting vars...\n";); m_ordered_vars.reset(); // The variables (and its definitions) in m_subst must remain alive until the end of this procedure. // Reason: they are scheduled for unmarking in visiting/done. // They should remain alive while they are on the stack. // To make sure this is the case, whenever a variable (and its definition) is removed from m_subst, // I add them to the saved vector. expr_ref_vector saved(m()); expr_fast_mark1 visiting; expr_fast_mark2 done; typedef std::pair frame; svector todo; ptr_vector::const_iterator it = m_vars.begin(); ptr_vector::const_iterator end = m_vars.end(); unsigned num; for (; it != end; ++it) { checkpoint(); app * v = *it; if (!m_candidate_vars.is_marked(v)) continue; todo.push_back(frame(v, 0)); while (!todo.empty()) { start: frame & fr = todo.back(); expr * t = fr.first; m_num_steps++; TRACE("solve_eqs_bug", tout << "processing:\n" << mk_ismt2_pp(t, m()) << "\n";); if (t->get_ref_count() > 1 && done.is_marked(t)) { todo.pop_back(); continue; } switch (t->get_kind()) { case AST_VAR: todo.pop_back(); break; case AST_QUANTIFIER: num = to_quantifier(t)->get_num_children(); while (fr.second < num) { expr * c = to_quantifier(t)->get_child(fr.second); fr.second++; if (c->get_ref_count() > 1 && done.is_marked(c)) continue; todo.push_back(frame(c, 0)); goto start; } if (t->get_ref_count() > 1) done.mark(t); todo.pop_back(); break; case AST_APP: num = to_app(t)->get_num_args(); if (num == 0) { if (fr.second == 0) { if (m_candidate_vars.is_marked(t)) { if (visiting.is_marked(t)) { // cycle detected: remove t visiting.reset_mark(t); m_candidate_vars.mark(t, false); SASSERT(!m_candidate_vars.is_marked(t)); // Must save t and its definition. // See comment in the beginning of the function expr * def = 0; proof * pr; expr_dependency * dep; m_subst->find(to_app(t), def, pr, dep); SASSERT(def != 0); saved.push_back(t); saved.push_back(def); // m_subst->erase(t); } else { visiting.mark(t); fr.second = 1; expr * def = 0; proof * pr; expr_dependency * dep; m_subst->find(to_app(t), def, pr, dep); SASSERT(def != 0); todo.push_back(frame(def, 0)); goto start; } } } else { SASSERT(fr.second == 1); if (m_candidate_vars.is_marked(t)) { visiting.reset_mark(t); m_ordered_vars.push_back(to_app(t)); } else { // var was removed from the list of candidate vars to elim cycle // do nothing } } } else { while (fr.second < num) { expr * arg = to_app(t)->get_arg(fr.second); fr.second++; if (arg->get_ref_count() > 1 && done.is_marked(arg)) continue; todo.push_back(frame(arg, 0)); goto start; } } if (t->get_ref_count() > 1) done.mark(t); todo.pop_back(); break; default: UNREACHABLE(); todo.pop_back(); break; } } } // cleanup it = m_vars.begin(); for (unsigned idx = 0; it != end; ++it, ++idx) { if (!m_candidate_vars.is_marked(*it)) { m_candidate_set.mark(m_candidates[idx], false); } } TRACE("solve_eqs", tout << "ordered vars:\n"; ptr_vector::iterator it = m_ordered_vars.begin(); ptr_vector::iterator end = m_ordered_vars.end(); for (; it != end; ++it) { SASSERT(m_candidate_vars.is_marked(*it)); tout << mk_ismt2_pp(*it, m()) << " "; } tout << "\n";); m_candidate_vars.reset(); } void normalize() { m_norm_subst->reset(); m_r->set_substitution(m_norm_subst.get()); expr_ref new_def(m()); proof_ref new_pr(m()); expr_dependency_ref new_dep(m()); unsigned size = m_ordered_vars.size(); for (unsigned idx = 0; idx < size; idx++) { checkpoint(); expr * v = m_ordered_vars[idx]; expr * def = 0; proof * pr = 0; expr_dependency * dep = 0; m_subst->find(v, def, pr, dep); SASSERT(def != 0); m_r->operator()(def, new_def, new_pr, new_dep); m_num_steps += m_r->get_num_steps() + 1; if (m_produce_proofs) new_pr = m().mk_transitivity(pr, new_pr); if (m_produce_unsat_cores) new_dep = m().mk_join(dep, new_dep); m_norm_subst->insert(v, new_def, new_pr, new_dep); // we updated the substituting, but we don't need to reset m_r // because all cached values there do not depend on v. } m_subst->reset(); TRACE("solve_eqs", tout << "after normalizing variables\n"; for (unsigned i = 0; i < m_ordered_vars.size(); i++) { expr * v = m_ordered_vars[i]; expr * def = 0; proof * pr = 0; expr_dependency * dep = 0; m_norm_subst->find(v, def, pr, dep); tout << mk_ismt2_pp(v, m()) << "\n----->\n" << mk_ismt2_pp(def, m()) << "\n\n"; }); #if 0 DEBUG_CODE({ for (unsigned i = 0; i < m_ordered_vars.size(); i++) { expr * v = m_ordered_vars[i]; expr * def = 0; proof * pr = 0; expr_dependency * dep = 0; m_norm_subst->find(v, def, pr, dep); SASSERT(def != 0); CASSERT("solve_eqs_bug", !occurs(v, def)); } }); #endif } void substitute(goal & g) { // force the cache of m_r to be reset. m_r->set_substitution(m_norm_subst.get()); expr_ref new_f(m()); proof_ref new_pr(m()); expr_dependency_ref new_dep(m()); unsigned size = g.size(); for (unsigned idx = 0; idx < size; idx++) { checkpoint(); expr * f = g.form(idx); TRACE("gaussian_leak", tout << "processing:\n" << mk_ismt2_pp(f, m()) << "\n";); if (m_candidate_set.is_marked(f)) { // f may be deleted after the following update. // so, we must remove remove the mark before doing the update m_candidate_set.mark(f, false); SASSERT(!m_candidate_set.is_marked(f)); g.update(idx, m().mk_true(), m().mk_true_proof(), 0); m_num_steps ++; continue; } m_r->operator()(f, new_f, new_pr, new_dep); TRACE("solve_eqs_subst", tout << mk_ismt2_pp(f, m()) << "\n--->\n" << mk_ismt2_pp(new_f, m()) << "\n";); m_num_steps += m_r->get_num_steps() + 1; if (m_produce_proofs) new_pr = m().mk_modus_ponens(g.pr(idx), new_pr); if (m_produce_unsat_cores) new_dep = m().mk_join(g.dep(idx), new_dep); g.update(idx, new_f, new_pr, new_dep); if (g.inconsistent()) return; } g.elim_true(); TRACE("solve_eqs", tout << "after applying substitution\n"; g.display(tout);); #if 0 DEBUG_CODE({ for (unsigned i = 0; i < m_ordered_vars.size(); i++) { expr * v = m_ordered_vars[i]; for (unsigned j = 0; j < g.size(); j++) { CASSERT("solve_eqs_bug", !occurs(v, g.form(j))); } }}); #endif } void save_elim_vars(model_converter_ref & mc) { IF_VERBOSE(100, if (!m_ordered_vars.empty()) verbose_stream() << "num. eliminated vars: " << m_ordered_vars.size() << "\n";); m_num_eliminated_vars += m_ordered_vars.size(); if (m_produce_models) { if (mc.get() == 0) mc = alloc(gmc, m()); ptr_vector::iterator it = m_ordered_vars.begin(); ptr_vector::iterator end = m_ordered_vars.end(); for (; it != end; ++it) { app * v = *it; expr * def = 0; proof * pr; expr_dependency * dep; m_norm_subst->find(v, def, pr, dep); SASSERT(def != 0); static_cast(mc.get())->insert(v->get_decl(), def); } } } void collect_num_occs(expr * t, expr_fast_mark1 & visited) { ptr_buffer stack; #define VISIT(ARG) { \ if (is_uninterp_const(ARG)) { \ obj_map::obj_map_entry * entry = m_num_occs.insert_if_not_there2(ARG, 0); \ entry->get_data().m_value++; \ } \ if (!visited.is_marked(ARG)) { \ visited.mark(ARG, true); \ stack.push_back(ARG); \ } \ } VISIT(t); while (!stack.empty()) { expr * t = stack.back(); stack.pop_back(); if (!is_app(t)) continue; unsigned j = to_app(t)->get_num_args(); while (j > 0) { --j; expr * arg = to_app(t)->get_arg(j); VISIT(arg); } } } void collect_num_occs(goal const & g) { if (m_max_occs == UINT_MAX) return; // no need to compute num occs m_num_occs.reset(); expr_fast_mark1 visited; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) collect_num_occs(g.form(i), visited); } unsigned get_num_steps() const { return m_num_steps; } unsigned get_num_eliminated_vars() const { return m_num_eliminated_vars; } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("solve_eqs", *g); m_produce_models = g->models_enabled(); m_produce_proofs = g->proofs_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); if (!g->inconsistent()) { m_subst = alloc(expr_substitution, m(), m_produce_unsat_cores, m_produce_proofs); m_norm_subst = alloc(expr_substitution, m(), m_produce_unsat_cores, m_produce_proofs); while (true) { collect_num_occs(*g); collect(*g); if (m_subst->empty()) break; sort_vars(); if (m_ordered_vars.empty()) break; normalize(); substitute(*(g.get())); if (g->inconsistent()) { mc = 0; break; } save_elim_vars(mc); TRACE("solve_eqs_round", g->display(tout); if (mc) mc->display(tout);); } } g->inc_depth(); result.push_back(g.get()); TRACE("solve_eqs", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: solve_eqs_tactic(ast_manager & m, params_ref const & p, expr_replacer * r, bool owner): m_params(p) { m_imp = alloc(imp, m, p, r, owner); } virtual tactic * translate(ast_manager & m) { return alloc(solve_eqs_tactic, m, m_params, mk_expr_simp_replacer(m, m_params), true); } virtual ~solve_eqs_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { r.insert("solve_eqs_max_occs", CPK_UINT, "(default: infty) maximum number of occurrences for considering a variable for gaussian eliminations."); r.insert("theory_solver", CPK_BOOL, "(default: true) use theory solvers."); r.insert("ite_solver", CPK_BOOL, "(default: true) use if-then-else solver."); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); report_tactic_progress(":num-elim-vars", m_imp->get_num_eliminated_vars()); } virtual void cleanup() { unsigned num_elim_vars = m_imp->m_num_eliminated_vars; ast_manager & m = m_imp->m(); expr_replacer * r = m_imp->m_r; if (r) r->set_substitution(0); bool owner = m_imp->m_r_owner; m_imp->m_r_owner = false; // stole replacer imp * d = alloc(imp, m, m_params, r, owner); d->m_num_eliminated_vars = num_elim_vars; #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } virtual void collect_statistics(statistics & st) const { st.update("eliminated vars", m_imp->get_num_eliminated_vars()); } virtual void reset_statistics() { m_imp->m_num_eliminated_vars = 0; } virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p, expr_replacer * r) { if (r == 0) return clean(alloc(solve_eqs_tactic, m, p, mk_expr_simp_replacer(m, p), true)); else return clean(alloc(solve_eqs_tactic, m, p, r, false)); } z3-4.4.0/src/tactic/core/ctx_simplify_tactic.cpp0000644000175000017500000004324012540347414021403 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ctx_simplify_tactic.cpp Abstract: Simple context simplifier for propagating constants. Author: Leonardo (leonardo) 2011-10-26 Notes: --*/ #include"ctx_simplify_tactic.h" #include"mk_simplified_app.h" #include"goal_num_occurs.h" #include"cooperate.h" #include"ast_ll_pp.h" #include"ast_pp.h" struct ctx_simplify_tactic::imp { struct cached_result { expr * m_to; unsigned m_lvl; cached_result * m_next; cached_result(expr * t, unsigned lvl, cached_result * next): m_to(t), m_lvl(lvl), m_next(next) { } }; struct cache_cell { expr * m_from; cached_result * m_result; cache_cell():m_from(0), m_result(0) {} }; ast_manager & m; small_object_allocator m_allocator; obj_map m_assertions; ptr_vector m_trail; svector m_scopes; svector m_cache; vector > m_cache_undo; unsigned m_scope_lvl; unsigned m_depth; unsigned m_num_steps; goal_num_occurs m_occs; mk_simplified_app m_mk_app; unsigned long long m_max_memory; unsigned m_max_depth; unsigned m_max_steps; bool m_bail_on_blowup; volatile bool m_cancel; imp(ast_manager & _m, params_ref const & p): m(_m), m_allocator("context-simplifier"), m_occs(true, true), m_mk_app(m, p) { m_cancel = false; m_scope_lvl = 0; updt_params(p); } void set_cancel(bool f) { m_cancel = f; } ~imp() { pop(m_scope_lvl); SASSERT(m_scope_lvl == 0); restore_cache(0); DEBUG_CODE({ for (unsigned i = 0; i < m_cache.size(); i++) { CTRACE("ctx_simplify_tactic_bug", m_cache[i].m_from, tout << "i: " << i << "\n" << mk_ismt2_pp(m_cache[i].m_from, m) << "\n"; tout << "m_result: " << m_cache[i].m_result << "\n"; if (m_cache[i].m_result) tout << "lvl: " << m_cache[i].m_result->m_lvl << "\n";); SASSERT(m_cache[i].m_from == 0); SASSERT(m_cache[i].m_result == 0); } }); } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_max_steps = p.get_uint("max_steps", UINT_MAX); m_max_depth = p.get_uint("max_depth", 1024); m_bail_on_blowup = p.get_bool("bail_on_blowup", false); } void checkpoint() { cooperate("ctx_simplify_tactic"); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); } bool shared(expr * t) const { TRACE("ctx_simplify_tactic_bug", tout << mk_pp(t, m) << "\n";); return t->get_ref_count() > 1 && m_occs.get_num_occs(t) > 1; } bool check_cache() { for (unsigned i = 0; i < m_cache.size(); i++) { cache_cell & cell = m_cache[i]; if (cell.m_from != 0) { SASSERT(cell.m_result != 0); cached_result * curr = cell.m_result; while (curr) { SASSERT(curr->m_lvl <= scope_level()); curr = curr->m_next; } } } return true; } void cache_core(expr * from, expr * to) { TRACE("ctx_simplify_tactic_cache", tout << "caching\n" << mk_ismt2_pp(from, m) << "\n--->\n" << mk_ismt2_pp(to, m) << "\n";); unsigned id = from->get_id(); m_cache.reserve(id+1); cache_cell & cell = m_cache[id]; void * mem = m_allocator.allocate(sizeof(cached_result)); if (cell.m_from == 0) { // new_entry cell.m_from = from; cell.m_result = new (mem) cached_result(to, m_scope_lvl, 0); m.inc_ref(from); m.inc_ref(to); } else { // update cell.m_result = new (mem) cached_result(to, m_scope_lvl, cell.m_result); m.inc_ref(to); } m_cache_undo.reserve(m_scope_lvl+1); m_cache_undo[m_scope_lvl].push_back(from); } void cache(expr * from, expr * to) { if (shared(from)) cache_core(from, to); } unsigned scope_level() const { return m_scope_lvl; } void push() { m_scope_lvl++; m_scopes.push_back(m_trail.size()); } void restore_cache(unsigned lvl) { if (lvl >= m_cache_undo.size()) return; ptr_vector & keys = m_cache_undo[lvl]; ptr_vector::iterator it = keys.end(); ptr_vector::iterator begin = keys.begin(); while (it != begin) { --it; expr * key = *it; unsigned key_id = key->get_id(); cache_cell & cell = m_cache[key_id]; SASSERT(cell.m_from == key); SASSERT(cell.m_result != 0); m.dec_ref(cell.m_result->m_to); cached_result * to_delete = cell.m_result; SASSERT(to_delete->m_lvl == lvl); TRACE("ctx_simplify_tactic_cache", tout << "uncaching: " << to_delete->m_lvl << "\n" << mk_ismt2_pp(key, m) << "\n--->\n" << mk_ismt2_pp(to_delete->m_to, m) << "\nrestoring:\n"; if (to_delete->m_next) tout << mk_ismt2_pp(to_delete->m_next->m_to, m); else tout << ""; tout << "\n";); cell.m_result = to_delete->m_next; if (cell.m_result == 0) { m.dec_ref(cell.m_from); cell.m_from = 0; } m_allocator.deallocate(sizeof(cached_result), to_delete); } keys.reset(); } void pop(unsigned num_scopes) { if (num_scopes == 0) return; SASSERT(num_scopes <= m_scope_lvl); SASSERT(m_scope_lvl == m_scopes.size()); // undo assertions unsigned old_trail_size = m_scopes[m_scope_lvl - num_scopes]; unsigned i = m_trail.size(); while (i > old_trail_size) { --i; expr * key = m_trail.back(); m_assertions.erase(key); m_trail.pop_back(); } SASSERT(m_trail.size() == old_trail_size); m_scopes.shrink(m_scope_lvl - num_scopes); // restore cache for (unsigned i = 0; i < num_scopes; i++) { restore_cache(m_scope_lvl); m_scope_lvl--; } CASSERT("ctx_simplify_tactic", check_cache()); } void assert_eq_core(expr * t, app * val) { if (m_assertions.contains(t)) { // This branch can only happen when m_max_depth was reached. // It can happen when m_assertions contains an entry t->val', // but (= t val) was not simplified to (= val' val) // because the simplifier stopped at depth m_max_depth return; } CTRACE("assert_eq_bug", m_assertions.contains(t), tout << "m_depth: " << m_depth << " m_max_depth: " << m_max_depth << "\n" << "t:\n" << mk_ismt2_pp(t, m) << "\nval:\n" << mk_ismt2_pp(val, m) << "\n"; expr * old_val = 0; m_assertions.find(t, old_val); tout << "old_val:\n" << mk_ismt2_pp(old_val, m) << "\n";); m_assertions.insert(t, val); m_trail.push_back(t); } void assert_eq_val(expr * t, app * val, bool mk_scope) { if (shared(t)) { if (mk_scope) push(); assert_eq_core(t, val); } } void assert_expr(expr * t, bool sign) { expr * p = t; if (m.is_not(t)) { t = to_app(t)->get_arg(0); sign = !sign; } bool mk_scope = true; if (shared(t) || shared(p)) { push(); mk_scope = false; assert_eq_core(t, sign ? m.mk_false() : m.mk_true()); } expr * lhs, * rhs; if (!sign && m.is_eq(t, lhs, rhs)) { if (m.is_value(rhs)) assert_eq_val(lhs, to_app(rhs), mk_scope); else if (m.is_value(lhs)) assert_eq_val(rhs, to_app(lhs), mk_scope); } } bool is_cached(expr * t, expr_ref & r) { unsigned id = t->get_id(); if (id >= m_cache.size()) return false; cache_cell & cell = m_cache[id]; SASSERT(cell.m_result == 0 || cell.m_result->m_lvl <= scope_level()); if (cell.m_result != 0 && cell.m_result->m_lvl == scope_level()) { SASSERT(cell.m_from == t); SASSERT(cell.m_result->m_to != 0); r = cell.m_result->m_to; return true; } return false; } void simplify(expr * t, expr_ref & r) { r = 0; if (m_depth >= m_max_depth || m_num_steps >= m_max_steps || !is_app(t)) { r = t; return; } checkpoint(); TRACE("ctx_simplify_tactic_detail", tout << "processing: " << mk_bounded_pp(t, m) << "\n";); expr * _r; if (m_assertions.find(t, _r)) { r = _r; SASSERT(r.get() != 0); return; } if (is_cached(t, r)) { SASSERT(r.get() != 0); return; } m_num_steps++; m_depth++; if (m.is_or(t)) simplify_or_and(to_app(t), r); else if (m.is_and(t)) simplify_or_and(to_app(t), r); else if (m.is_ite(t)) simplify_ite(to_app(t), r); else simplify_app(to_app(t), r); m_depth--; SASSERT(r.get() != 0); TRACE("ctx_simplify_tactic_detail", tout << "result:\n" << mk_bounded_pp(t, m) << "\n---->\n" << mk_bounded_pp(r, m) << "\n";); } template void simplify_or_and(app * t, expr_ref & r) { // go forwards expr_ref_buffer new_args(m); unsigned old_lvl = scope_level(); bool modified = false; unsigned num_args = t->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = t->get_arg(i); expr_ref new_arg(m); simplify(arg, new_arg); if (new_arg != arg) modified = true; if ((OR && m.is_false(new_arg)) || (!OR && m.is_true(new_arg))) { modified = true; continue; } if ((OR && m.is_true(new_arg)) || (!OR && m.is_false(new_arg))) { r = new_arg; pop(scope_level() - old_lvl); cache(t, r); return; } new_args.push_back(new_arg); if (i < num_args - 1) assert_expr(new_arg, OR); } pop(scope_level() - old_lvl); // go backwards expr_ref_buffer new_new_args(m); unsigned i = new_args.size(); while (i > 0) { --i; expr * arg = new_args[i]; expr_ref new_arg(m); simplify(arg, new_arg); if (new_arg != arg) modified = true; if ((OR && m.is_false(new_arg)) || (!OR && m.is_true(new_arg))) { modified = true; continue; } if ((OR && m.is_true(new_arg)) || (!OR && m.is_false(new_arg))) { r = new_arg; pop(scope_level() - old_lvl); cache(t, r); return; } new_new_args.push_back(new_arg); if (i > 0) assert_expr(new_arg, OR); } pop(scope_level() - old_lvl); if (!modified) { r = t; } else if (new_new_args.empty()) { r = OR?m.mk_false():m.mk_true(); } else if (new_new_args.size() == 1) { r = new_new_args[0]; } else { std::reverse(new_new_args.c_ptr(), new_new_args.c_ptr() + new_new_args.size()); m_mk_app(t->get_decl(), new_new_args.size(), new_new_args.c_ptr(), r); } cache(t, r); } void simplify_ite(app * ite, expr_ref & r) { expr * c = ite->get_arg(0); expr * t = ite->get_arg(1); expr * e = ite->get_arg(2); expr_ref new_c(m); unsigned old_lvl = scope_level(); simplify(c, new_c); if (m.is_true(new_c)) { simplify(t, r); } else if (m.is_false(new_c)) { simplify(e, r); } else { expr_ref new_t(m); expr_ref new_e(m); assert_expr(new_c, false); simplify(t, new_t); pop(scope_level() - old_lvl); assert_expr(new_c, true); simplify(e, new_e); pop(scope_level() - old_lvl); if (c == new_c && t == new_t && e == new_e) { r = ite; } else { expr * args[3] = { new_c.get(), new_t.get(), new_e.get() }; TRACE("ctx_simplify_tactic_ite_bug", tout << "mk_ite\n" << mk_ismt2_pp(new_c.get(), m) << "\n" << mk_ismt2_pp(new_t.get(), m) << "\n" << mk_ismt2_pp(new_e.get(), m) << "\n";); m_mk_app(ite->get_decl(), 3, args, r); } } cache(ite, r); } void simplify_app(app * t, expr_ref & r) { if (t->get_num_args() == 0) { r = t; return; } expr_ref_buffer new_args(m); bool modified = false; unsigned num_args = t->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = t->get_arg(i); expr_ref new_arg(m); simplify(arg, new_arg); CTRACE("ctx_simplify_tactic_bug", new_arg.get() == 0, tout << mk_ismt2_pp(arg, m) << "\n";); SASSERT(new_arg); if (new_arg != arg) modified = true; new_args.push_back(new_arg); } if (!modified) { r = t; } else { m_mk_app(t->get_decl(), new_args.size(), new_args.c_ptr(), r); } } unsigned expr_size(expr* s) { ast_mark visit; unsigned sz = 0; ptr_vector todo; todo.push_back(s); while (!todo.empty()) { s = todo.back(); todo.pop_back(); if (visit.is_marked(s)) { continue; } visit.mark(s, true); ++sz; for (unsigned i = 0; is_app(s) && i < to_app(s)->get_num_args(); ++i) { todo.push_back(to_app(s)->get_arg(i)); } } return sz; } void process(expr * s, expr_ref & r) { TRACE("ctx_simplify_tactic", tout << "simplifying:\n" << mk_ismt2_pp(s, m) << "\n";); SASSERT(m_scope_lvl == 0); m_depth = 0; simplify(s, r); SASSERT(m_scope_lvl == 0); SASSERT(m_depth == 0); SASSERT(r.get() != 0); TRACE("ctx_simplify_tactic", tout << "result\n" << mk_ismt2_pp(r, m) << " :num-steps " << m_num_steps << "\n"; tout << "old size: " << expr_size(s) << " new size: " << expr_size(r) << "\n";); if (m_bail_on_blowup && expr_size(s) < expr_size(r)) { r = s; } } void operator()(goal & g) { SASSERT(g.is_well_sorted()); bool proofs_enabled = g.proofs_enabled(); m_occs.reset(); m_occs(g); m_num_steps = 0; expr_ref r(m); proof * new_pr = 0; tactic_report report("ctx-simplify", g); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { if (g.inconsistent()) return; expr * t = g.form(i); process(t, r); if (proofs_enabled) { proof * pr = g.pr(i); new_pr = m.mk_modus_ponens(pr, m.mk_rewrite_star(t, r, 0, 0)); // TODO :-) } g.update(i, r, new_pr, g.dep(i)); } IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(ctx-simplify :num-steps " << m_num_steps << ")\n";); SASSERT(g.is_well_sorted()); } }; ctx_simplify_tactic::ctx_simplify_tactic(ast_manager & m, params_ref const & p): m_imp(alloc(imp, m, p)), m_params(p) { } ctx_simplify_tactic::~ctx_simplify_tactic() { dealloc(m_imp); } void ctx_simplify_tactic::updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } void ctx_simplify_tactic::get_param_descrs(param_descrs & r) { insert_max_memory(r); insert_max_steps(r); r.insert("max_depth", CPK_UINT, "(default: 1024) maximum term depth."); } void ctx_simplify_tactic::operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { mc = 0; pc = 0; core = 0; (*m_imp)(*(in.get())); in->inc_depth(); result.push_back(in.get()); } void ctx_simplify_tactic::set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } void ctx_simplify_tactic::cleanup() { ast_manager & m = m_imp->m; imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } z3-4.4.0/src/tactic/core/solve_eqs_tactic.h0000644000175000017500000000113212540347414020330 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: solve_eqs_tactic.h Abstract: Tactic for solving equations and performing gaussian elimination. Author: Leonardo de Moura (leonardo) 2011-12-29. Revision History: --*/ #ifndef _SOLVE_EQS_TACTIC_H_ #define _SOLVE_EQS_TACTIC_H_ #include"params.h" class ast_manager; class tactic; class expr_replacer; tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p = params_ref(), expr_replacer * r = 0); /* ADD_TACTIC("solve-eqs", "eliminate variables by solving equations.", "mk_solve_eqs_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/core/elim_term_ite_tactic.cpp0000644000175000017500000001350412540347414021507 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: elim_term_ite_tactic.cpp Abstract: Eliminate term if-then-else by adding new fresh auxiliary variables. Author: Leonardo (leonardo) 2011-12-29 Notes: --*/ #include"tactical.h" #include"defined_names.h" #include"rewriter_def.h" #include"filter_model_converter.h" #include"cooperate.h" class elim_term_ite_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { ast_manager & m; defined_names m_defined_names; ref m_mc; goal * m_goal; unsigned long long m_max_memory; // in bytes bool m_produce_models; unsigned m_num_fresh; bool max_steps_exceeded(unsigned num_steps) const { cooperate("elim term ite"); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (!m.is_term_ite(f)) return BR_FAILED; expr_ref new_ite(m); new_ite = m.mk_app(f, num, args); expr_ref new_def(m); proof_ref new_def_pr(m); app_ref _result(m); if (m_defined_names.mk_name(new_ite, new_def, new_def_pr, _result, result_pr)) { m_goal->assert_expr(new_def, new_def_pr, 0); m_num_fresh++; if (m_produce_models) { if (!m_mc) m_mc = alloc(filter_model_converter, m); m_mc->insert(_result->get_decl()); } } result = _result.get(); return BR_DONE; } rw_cfg(ast_manager & _m, params_ref const & p): m(_m), m_defined_names(m, 0 /* don't use prefix */) { updt_params(p); m_goal = 0; m_num_fresh = 0; } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { } }; struct imp { ast_manager & m; rw m_rw; imp(ast_manager & _m, params_ref const & p): m(_m), m_rw(m, p) { } void set_cancel(bool f) { m_rw.set_cancel(f); } void updt_params(params_ref const & p) { m_rw.cfg().updt_params(p); } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("elim-term-ite", *g); bool produce_proofs = g->proofs_enabled(); m_rw.cfg().m_produce_models = g->models_enabled(); m_rw.m_cfg.m_num_fresh = 0; m_rw.m_cfg.m_goal = g.get(); expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { expr * curr = g->form(idx); m_rw(curr, new_curr, new_pr); if (produce_proofs) { proof * pr = g->pr(idx); new_pr = m.mk_modus_ponens(pr, new_pr); } g->update(idx, new_curr, new_pr, g->dep(idx)); } mc = m_rw.m_cfg.m_mc.get(); report_tactic_progress(":elim-term-ite-consts", m_rw.m_cfg.m_num_fresh); g->inc_depth(); result.push_back(g.get()); TRACE("elim_term_ite", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: elim_term_ite_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(elim_term_ite_tactic, m, m_params); } virtual ~elim_term_ite_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->m_rw.cfg().updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { insert_max_memory(r); insert_max_steps(r); r.insert("max_args", CPK_UINT, "(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic."); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } virtual void cleanup() { ast_manager & m = m_imp->m; imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_elim_term_ite_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(elim_term_ite_tactic, m, p)); } z3-4.4.0/src/tactic/core/cofactor_term_ite_tactic.cpp0000644000175000017500000000450112540347414022356 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: cofactor_term_ite_tactic.cpp Abstract: Wrap cofactor_elim_term_ite as a tactic. Eliminate (ground) term if-then-else's using cofactors. Author: Leonardo de Moura (leonardo) 2012-02-20. Revision History: --*/ #include"tactical.h" #include"cofactor_elim_term_ite.h" /** \brief Wrapper for applying cofactor_elim_term_ite in an assertion set. */ class cofactor_term_ite_tactic : public tactic { params_ref m_params; cofactor_elim_term_ite m_elim_ite; void process(goal & g) { ast_manager & m = g.m(); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { if (g.inconsistent()) break; expr * f = g.form(i); expr_ref new_f(m); m_elim_ite(f, new_f); g.update(i, new_f); } } public: cofactor_term_ite_tactic(ast_manager & m, params_ref const & p): m_params(p), m_elim_ite(m, p) { } virtual tactic * translate(ast_manager & m) { return alloc(cofactor_term_ite_tactic, m, m_params); } virtual ~cofactor_term_ite_tactic() {} virtual void updt_params(params_ref const & p) { m_params = p; m_elim_ite.updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { m_elim_ite.collect_param_descrs(r); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("cofactor-term-ite", g); fail_if_unsat_core_generation("cofactor-term-ite", g); tactic_report report("cofactor-term-ite", *g); mc = 0; pc = 0; core = 0; process(*(g.get())); g->inc_depth(); result.push_back(g.get()); TRACE("cofactor-term-ite", g->display(tout);); SASSERT(g->is_well_sorted()); } virtual void cleanup() { return m_elim_ite.cleanup(); } virtual void set_cancel(bool f) { m_elim_ite.set_cancel(f); } }; tactic * mk_cofactor_term_ite_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(cofactor_term_ite_tactic, m, p)); } z3-4.4.0/src/tactic/core/blast_term_ite_tactic.h0000644000175000017500000000136212540347414021332 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: blast_term_ite_tactic.h Abstract: Blast term if-then-else by hoisting them up. This is expensive but useful in some cases, such as for enforcing constraints being in difference logic. Use elim-term-ite elsewhere when possible. Author: Nikolaj Bjorner (nbjorner) 2013-11-4 Notes: --*/ #ifndef _BLAST_TERM_ITE_TACTIC_H_ #define _BLAST_TERM_ITE_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_blast_term_ite_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("blast-term-ite", "blast term if-then-else by hoisting them.", "mk_blast_term_ite_tactic(m, p)") */ void blast_term_ite(expr_ref& fml); #endif z3-4.4.0/src/tactic/core/der_tactic.h0000644000175000017500000000061712540347414017111 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: der_tactic.h Abstract: DER tactic Author: Leonardo de Moura (leonardo) 2012-10-20 --*/ #ifndef _DER_TACTIC_H_ #define _DER_TACTIC_H_ class ast_manager; class tactic; tactic * mk_der_tactic(ast_manager & m); /* ADD_TACTIC("der", "destructive equality resolution.", "mk_der_tactic(m)") */ #endif /* _DER_TACTIC_H_ */ z3-4.4.0/src/tactic/core/occf_tactic.h0000644000175000017500000000141212540347414017243 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: occf_tactic.h Abstract: Put clauses in the assertion set in OOC (one constraint per clause) form. Constraints occuring in formulas that are not clauses are ignored. The formula can be put into CNF by using mk_sat_preprocessor strategy. Author: Leonardo de Moura (leonardo) 2011-12-28. Revision History: --*/ #ifndef _OCCF_TACTIC_H_ #define _OCCF_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_occf_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("occf", "put goal in one constraint per clause normal form (notes: fails if proof generation is enabled; only clauses are considered).", "mk_occf_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/core/nnf_tactic.h0000644000175000017500000000114712540347414017117 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: nnf_tactic.h Abstract: NNF tactic Author: Leonardo de Moura (leonardo) 2011-12-28. Revision History: --*/ #ifndef _NNF_TACTIC_H_ #define _NNF_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_snf_tactic(ast_manager & m, params_ref const & p = params_ref()); tactic * mk_nnf_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("snf", "put goal in skolem normal form.", "mk_snf_tactic(m, p)") ADD_TACTIC("nnf", "put goal in negation normal form.", "mk_nnf_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/core/tseitin_cnf_tactic.cpp0000644000175000017500000007770112540347414021207 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: tseitin_cnf_tactic.cpp Abstract: Puts an assertion set in CNF. Auxiliary variables are used to avoid blowup. Features: - Efficient encoding is used for commonly used patterns such as: (iff a (iff b c)) (or (not (or a b)) (not (or a c)) (not (or b c))) - Efficient encoding is used for chains of if-then-elses - Distributivity is applied to non-shared nodes if the blowup is acceptable. - The features above can be disabled/enabled using parameters. - The assertion-set is only modified if the resultant set of clauses is "acceptable". Notes: - Term-if-then-else expressions are not handled by this strategy. This kind of expression should be processed by other strategies. - Quantifiers are treated as "theory" atoms. They are viewed as propositional variables by this strategy. - The assertion set may contain free variables. - This strategy assumes the assertion_set_rewriter was used before invoking it. In particular, it is more effective when "and" operators were eliminated. TODO: add proof production Author: Leonardo (leonardo) 2011-12-29 Notes: --*/ #include"tactical.h" #include"goal_shared_occs.h" #include"filter_model_converter.h" #include"bool_rewriter.h" #include"simplify_tactic.h" #include"cooperate.h" static void swap_if_gt(expr * & n1, expr * & n2) { if (n1->get_id() > n2->get_id()) std::swap(n1, n2); } /** \brief Return true if n is of the form (= a b) */ static bool is_iff(ast_manager & m, expr * n, expr * & a, expr * & b) { if (m.is_iff(n, a, b)) return true; if (m.is_eq(n, a, b) && m.is_bool(a)) return true; return false; } class tseitin_cnf_tactic : public tactic { struct imp { struct frame { app * m_t; bool m_first; frame(app * n):m_t(n), m_first(true) {} }; typedef filter_model_converter mc; ast_manager & m; svector m_frame_stack; obj_map m_cache; expr_ref_vector m_cache_domain; goal_shared_occs m_occs; expr_ref_vector m_fresh_vars; ref m_mc; expr_ref_vector m_clauses; expr_dependency_ref_vector m_deps; bool_rewriter m_rw; expr_dependency * m_curr_dep; bool m_produce_models; bool m_produce_unsat_cores; // parameters bool m_common_patterns; bool m_distributivity; unsigned m_distributivity_blowup; bool m_ite_chains; bool m_ite_extra; unsigned long long m_max_memory; unsigned m_num_aux_vars; volatile bool m_cancel; imp(ast_manager & _m, params_ref const & p): m(_m), m_cache_domain(_m), m_occs(m, false /* don't track atoms */, false /* do not visit quantifiers */), m_fresh_vars(_m), m_clauses(_m), m_deps(_m), m_rw(_m), m_num_aux_vars(0), m_cancel(false) { updt_params(p); m_rw.set_flat(false); } void updt_params(params_ref const & p) { m_common_patterns = p.get_bool("common_patterns", true); m_distributivity = p.get_bool("distributivity", true); m_distributivity_blowup = p.get_uint("distributivity_blowup", 32); m_ite_chains = p.get_bool("ite_chains", true); m_ite_extra = p.get_bool("ite_extra", true); m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); } void push_frame(app * n) { m_frame_stack.push_back(frame(n)); } void throw_op_not_handled() { throw tactic_exception("operator not supported, apply simplifier before invoking this strategy"); } void inv(expr * n, expr_ref & r) { if (m.is_true(n)) { r = m.mk_false(); return; } if (m.is_false(n)) { r = m.mk_true(); return; } if (m.is_not(n)) { r = to_app(n)->get_arg(0); return; } r = m.mk_not(n); } void mk_lit(expr * n, bool sign, expr_ref & r) { if (sign) r = m.mk_not(n); else r = n; } void get_lit(expr * n, bool sign, expr_ref & r) { start: if (!is_app(n) || to_app(n)->get_num_args() == 0) { mk_lit(n, sign, r); return; } func_decl * f = to_app(n)->get_decl(); if (f->get_family_id() != m.get_basic_family_id()) { mk_lit(n, sign, r); return; } app * l; switch (f->get_decl_kind()) { case OP_NOT: n = to_app(n)->get_arg(0); sign = !sign; goto start; case OP_OR: case OP_IFF: l = 0; m_cache.find(to_app(n), l); SASSERT(l != 0); mk_lit(l, sign, r); return; case OP_ITE: case OP_EQ: if (m.is_bool(to_app(n)->get_arg(1))) { l = 0; m_cache.find(to_app(n), l); SASSERT(l != 0); mk_lit(l, sign, r); return; } mk_lit(n, sign, r); return; default: TRACE("tseitin_cnf_bug", tout << f->get_name() << "\n";); UNREACHABLE(); return; } } void visit(expr * n, bool & visited, bool root = false) { start: if (!is_app(n)) return; if (m_cache.contains(to_app(n))) return; if (to_app(n)->get_num_args() == 0) return; func_decl * f = to_app(n)->get_decl(); if (f->get_family_id() != m.get_basic_family_id()) return; switch (f->get_decl_kind()) { case OP_NOT: if (root) { visited = false; push_frame(to_app(n)); return; } else { n = to_app(n)->get_arg(0); goto start; } case OP_OR: case OP_IFF: visited = false; push_frame(to_app(n)); return; case OP_ITE: case OP_EQ: if (m.is_bool(to_app(n)->get_arg(1))) { visited = false; push_frame(to_app(n)); } return; case OP_AND: case OP_XOR: case OP_IMPLIES: case OP_DISTINCT: throw_op_not_handled(); default: return; } } bool is_shared(expr * t) { return m_occs.is_shared(t); } /** \brief Return true if n is of the form (or (not (or a b)) (not (or a c)) (not (or b c))) \remark This pattern is found in the following "circuits": - carry - less-than (signed and unsigned) */ bool is_or_3and(expr * n, expr * & a, expr * & b, expr * & c) { expr * a1, * a2, * b1, * b2, * c1, * c2; if (!m.is_or(n, a1, b1, c1) || !m.is_not(a1, a1) || is_shared(a1) || !m.is_not(b1, b1) || is_shared(b1) || !m.is_not(c1, c1) || is_shared(c1) || !m.is_or(a1, a1, a2) || !m.is_or(b1, b1, b2) || !m.is_or(c1, c1, c2)) return false; swap_if_gt(a1, a2); swap_if_gt(b1, b2); swap_if_gt(c1, c2); if ((a1 == b1 && a2 == c1 && b2 == c2) || (a1 == b1 && a2 == c2 && b2 == c1) || (a1 == c1 && a2 == b1 && b2 == c2)) { a = a1; b = a2; c = b2; return true; } if ((a1 == b2 && a2 == c2 && b1 == c1) || (a1 == c1 && a2 == b2 && b1 == c2) || (a1 == c2 && a2 == b2 && b1 == c1)) { a = a1; b = a2; c = b1; return true; } return false; } /** \brief Return true if n is of the form (iff a (iff b c)) */ bool is_iff3(expr * n, expr * & a, expr * & b, expr * & c) { expr * l1, * l2; if (!is_iff(m, n, l1, l2)) return false; if (!is_shared(l1) && is_iff(m, l1, a, b)) { c = l2; return true; } if (!is_shared(l2) && is_iff(m, l2, b, c)) { a = l1; return true; } return false; } void mk_clause(unsigned num, expr * const * ls) { expr_ref cls(m); m_rw.mk_or(num, ls, cls); m_clauses.push_back(cls); if (m_produce_unsat_cores) m_deps.push_back(m_curr_dep); } void mk_clause(expr * l1) { return mk_clause(1, &l1); } void mk_clause(expr * l1, expr * l2) { expr * ls[2] = { l1, l2 }; mk_clause(2, ls); } void mk_clause(expr * l1, expr * l2, expr * l3) { expr * ls[3] = { l1, l2, l3 }; mk_clause(3, ls); } void mk_clause(expr * l1, expr * l2, expr * l3, expr * l4) { expr * ls[4] = { l1, l2, l3, l4 }; mk_clause(4, ls); } app * mk_fresh() { m_num_aux_vars++; app * v = m.mk_fresh_const(0, m.mk_bool_sort()); m_fresh_vars.push_back(v); if (m_mc) m_mc->insert(v->get_decl()); return v; } void cache_result(app * t, app * r) { m_cache.insert(t, r); m_cache_domain.push_back(t); } enum mres { NO, // did not match CONT, // matched but the children need to be processed DONE // matched }; mres match_not(app * t, bool first, bool root) { expr * a; if (m.is_not(t, a)) { if (first) { bool visited = true; visit(a, visited); if (!visited) return CONT; } expr_ref nla(m); get_lit(a, true, nla); if (root) { mk_clause(nla); } // Remark: don't need to do anything if it is not a root. return DONE; } return NO; } mres match_or_3and(app * t, bool first, bool root) { if (!m_common_patterns) return NO; expr * a, * b, * c; if (is_or_3and(t, a, b, c)) { if (first) { bool visited = true; visit(a, visited); visit(b, visited); visit(c, visited); if (!visited) return CONT; } expr_ref nla(m), nlb(m), nlc(m); get_lit(a, true, nla); get_lit(b, true, nlb); get_lit(c, true, nlc); if (root) { mk_clause(nla, nlb); mk_clause(nla, nlc); mk_clause(nlb, nlc); } else { app_ref k(m), nk(m); k = mk_fresh(); nk = m.mk_not(k); mk_clause(nk, nla, nlb); mk_clause(nk, nla, nlc); mk_clause(nk, nlb, nlc); expr_ref la(m), lb(m), lc(m); inv(nla, la); inv(nlb, lb); inv(nlc, lc); mk_clause(k, la, lb); mk_clause(k, la, lc); mk_clause(k, lb, lc); cache_result(t, k); } return DONE; } return NO; } mres match_iff3(app * t, bool first, bool root) { if (!m_common_patterns) return NO; expr * a, * b, * c; if (is_iff3(t, a, b, c)) { if (first) { bool visited = true; visit(a, visited); visit(b, visited); visit(c, visited); if (!visited) return CONT; } expr_ref la(m), lb(m), lc(m); expr_ref nla(m), nlb(m), nlc(m); get_lit(a, false, la); get_lit(b, false, lb); get_lit(c, false, lc); inv(la, nla); inv(lb, nlb); inv(lc, nlc); if (root) { mk_clause(la, lb, lc); mk_clause(la, nlb, nlc); mk_clause(nla, lb, nlc); mk_clause(nla, nlb, lc); } else { app_ref k(m), nk(m); k = mk_fresh(); nk = m.mk_not(k); mk_clause(nk, la, lb, lc); mk_clause(nk, la, nlb, nlc); mk_clause(nk, nla, lb, nlc); mk_clause(nk, nla, nlb, lc); mk_clause(k, nla, nlb, nlc); mk_clause(k, nla, lb, lc); mk_clause(k, la, nlb, lc); mk_clause(k, la, lb, nlc); cache_result(t, k); } return DONE; } return NO; } mres match_iff(app * t, bool first, bool root) { expr * a, * b; if (is_iff(m, t, a, b)) { if (first) { bool visited = true; visit(a, visited); visit(b, visited); if (!visited) return CONT; } expr_ref la(m), lb(m); expr_ref nla(m), nlb(m); get_lit(a, false, la); get_lit(b, false, lb); inv(la, nla); inv(lb, nlb); if (root) { mk_clause(la, nlb); mk_clause(nla, lb); } else { app_ref k(m), nk(m); k = mk_fresh(); nk = m.mk_not(k); mk_clause(nk, la, nlb); mk_clause(nk, nla, lb); mk_clause(k, nla, nlb); mk_clause(k, la, lb); cache_result(t, k); } return DONE; } return NO; } mres match_ite(app * t, bool first, bool root) { if (!m.is_ite(t)) return NO; if (first) { bool visited = true; app * ite = t; while (true) { visit(ite->get_arg(0), visited); if (m_ite_chains && m.is_ite(ite->get_arg(1)) && !is_shared(ite->get_arg(1))) { visit(ite->get_arg(2), visited); ite = to_app(ite->get_arg(1)); continue; } if (m_ite_chains && m.is_ite(ite->get_arg(2)) && !is_shared(ite->get_arg(2))) { visit(ite->get_arg(1), visited); ite = to_app(ite->get_arg(2)); continue; } visit(ite->get_arg(1), visited); visit(ite->get_arg(2), visited); break; } if (!visited) return CONT; } expr_ref_buffer ctx(m); expr_ref_buffer ex_pos_ctx(m); // for extra ite clauses expr_ref_buffer ex_neg_ctx(m); // for extra ite clauses expr_ref la(m), lb(m), lc(m); expr_ref nla(m), nlb(m), nlc(m); app_ref k(m), nk(m); if (!root) { k = mk_fresh(); nk = m.mk_not(k); cache_result(t, k); } #define MK_ITE_ROOT_CLS(L1, L2) { \ ctx.push_back(L1); ctx.push_back(L2); \ mk_clause(ctx.size(), ctx.c_ptr()); \ ctx.pop_back(); ctx.pop_back(); \ } #define MK_ITE_CLS(L1, L2, L3) { \ ctx.push_back(L1); ctx.push_back(L2); ctx.push_back(L3); \ mk_clause(ctx.size(), ctx.c_ptr()); \ ctx.pop_back(); ctx.pop_back(); ctx.pop_back(); \ } app * ite = t; while (true) { get_lit(ite->get_arg(0), false, la); inv(la, nla); if (m_ite_chains && m.is_ite(ite->get_arg(1)) && !is_shared(ite->get_arg(1))) { get_lit(ite->get_arg(2), false, lc); if (root) { MK_ITE_ROOT_CLS(la, lc); } else { inv(lc, nlc); MK_ITE_CLS(la, lc, nk); MK_ITE_CLS(la, nlc, k); if (m_ite_extra) { ex_neg_ctx.push_back(lc); ex_pos_ctx.push_back(nlc); } } ctx.push_back(nla); ite = to_app(ite->get_arg(1)); continue; } if (m_ite_chains && m.is_ite(ite->get_arg(2)) && !is_shared(ite->get_arg(2))) { get_lit(ite->get_arg(1), false, lb); if (root) { MK_ITE_ROOT_CLS(nla, lb); } else { inv(lb, nlb); MK_ITE_CLS(nla, lb, nk); MK_ITE_CLS(nla, nlb, k); if (m_ite_extra) { ex_neg_ctx.push_back(lb); ex_pos_ctx.push_back(nlb); } } ctx.push_back(la); ite = to_app(ite->get_arg(2)); continue; } get_lit(ite->get_arg(1), false, lb); get_lit(ite->get_arg(2), false, lc); if (root) { MK_ITE_ROOT_CLS(nla, lb); MK_ITE_ROOT_CLS(la, lc); } else { inv(lb, nlb); inv(lc, nlc); MK_ITE_CLS(nla, lb, nk); MK_ITE_CLS(la, lc, nk); MK_ITE_CLS(nla, nlb, k); MK_ITE_CLS(la, nlc, k); if (m_ite_extra) { MK_ITE_CLS(lb, lc, nk); MK_ITE_CLS(nlb, nlc, k); ex_neg_ctx.push_back(lb); ex_neg_ctx.push_back(lc); ex_neg_ctx.push_back(nk); mk_clause(ex_neg_ctx.size(), ex_neg_ctx.c_ptr()); ex_pos_ctx.push_back(nlb); ex_pos_ctx.push_back(nlc); ex_pos_ctx.push_back(k); mk_clause(ex_pos_ctx.size(), ex_pos_ctx.c_ptr()); } } break; } return DONE; } mres match_or(app * t, bool first, bool root) { if (!m.is_or(t)) return NO; if (first) { bool visited = true; unsigned num = t->get_num_args(); unsigned blowup = 1; for (unsigned i = 0; i < num; i++) { expr * a = t->get_arg(i); expr * a0; if (m_distributivity && m.is_not(a, a0) && m.is_or(a0) && !is_shared(a0)) { unsigned num2 = to_app(a0)->get_num_args(); if (num2 < m_distributivity_blowup && blowup * num2 < m_distributivity_blowup && blowup < blowup * num2) { blowup *= num2; for (unsigned j = 0; j < num2; j++) visit(to_app(a0)->get_arg(j), visited); continue; } } visit(a, visited); } if (!visited) return CONT; } app_ref k(m), nk(m); if (!root) { k = mk_fresh(); nk = m.mk_not(k); cache_result(t, k); } unsigned num = t->get_num_args(); bool distributivity = false; if (m_distributivity) { // check if need to apply distributivity for (unsigned i = 0; i < num; i++) { expr * a = t->get_arg(i); expr * a0; if (m.is_not(a, a0) && m.is_or(a0) && !is_shared(a0) && to_app(a0)->get_num_args() < m_distributivity_blowup) { distributivity = true; break; } } } if (!distributivity) { // easy case expr_ref_buffer lits(m); expr_ref l(m); for (unsigned i = 0; i < num; i++) { get_lit(t->get_arg(i), false, l); lits.push_back(l); } if (root) { mk_clause(lits.size(), lits.c_ptr()); } else { for (unsigned i = 0; i < num; i++) { inv(lits[i], l); mk_clause(l, k); } lits.push_back(nk); mk_clause(lits.size(), lits.c_ptr()); } } else { expr_ref_buffer buffer(m); expr_ref l(m), nl(m); sbuffer szs; sbuffer it; sbuffer offsets; unsigned blowup = 1; for (unsigned i = 0; i < num; i++) { it.push_back(0); offsets.push_back(buffer.size()); expr * a = t->get_arg(i); expr * a0; if (m.is_not(a, a0) && m.is_or(a0) && !is_shared(a0)) { unsigned num2 = to_app(a0)->get_num_args(); if (num2 < m_distributivity_blowup && blowup * num2 < m_distributivity_blowup && blowup < blowup * num2) { szs.push_back(num2); blowup *= num2; expr_ref_buffer lits(m); for (unsigned j = 0; j < num2; j++) { get_lit(to_app(a0)->get_arg(j), true, nl); buffer.push_back(nl); if (!root) { inv(nl, l); lits.push_back(l); } } if (!root) { lits.push_back(k); mk_clause(lits.size(), lits.c_ptr()); } continue; } } szs.push_back(1); get_lit(a, false, l); buffer.push_back(l); if (!root) { inv(l, nl); mk_clause(nl, k); } } SASSERT(offsets.size() == num); sbuffer arg_lits; ptr_buffer lits; expr ** buffer_ptr = buffer.c_ptr(); for (unsigned i = 0; i < num; i++) { arg_lits.push_back(buffer_ptr + offsets[i]); } do { lits.reset(); for (unsigned i = 0; i < num; i++) { lits.push_back(arg_lits[i][it[i]]); } if (!root) lits.push_back(nk); mk_clause(lits.size(), lits.c_ptr()); } while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr())); } return DONE; } #define TRY(_MATCHER_) \ r = _MATCHER_(t, first, t == root); \ if (r == CONT) goto loop; \ if (r == DONE) { m_frame_stack.pop_back(); continue; } void set_cancel(bool f) { m_cancel = f; } void checkpoint() { cooperate("tseitin cnf"); if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); } void process(expr * n, expr_dependency * dep) { m_curr_dep = dep; bool visited = true; visit(n, visited, true); if (visited) { expr_ref l(m); get_lit(n, false, l); mk_clause(l); return; } expr * root = n; app * t; bool first; mres r; while (!m_frame_stack.empty()) { loop: checkpoint(); frame & fr = m_frame_stack.back(); t = fr.m_t; first = fr.m_first; fr.m_first = false; TRY(match_or_3and); TRY(match_or); TRY(match_iff3); TRY(match_iff); TRY(match_ite); TRY(match_not); UNREACHABLE(); } } void reset_cache() { m_cache.reset(); m_cache_domain.reset(); } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("tseitin-cnf", *g); fail_if_proof_generation("tseitin-cnf", g); m_produce_models = g->models_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); m_occs(*g); reset_cache(); m_deps.reset(); m_fresh_vars.reset(); m_frame_stack.reset(); m_clauses.reset(); if (m_produce_models) m_mc = alloc(filter_model_converter, m); else m_mc = 0; unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { process(g->form(idx), g->dep(idx)); g->update(idx, m.mk_true(), 0, 0); // to save memory } SASSERT(!m_produce_unsat_cores || m_clauses.size() == m_deps.size()); g->reset(); unsigned sz = m_clauses.size(); expr_fast_mark1 added; for (unsigned i = 0; i < sz; i++) { expr * cls = m_clauses.get(i); if (added.is_marked(cls)) continue; added.mark(cls); if (m_produce_unsat_cores) g->assert_expr(cls, 0, m_deps.get(i)); else g->assert_expr(cls); } if (m_produce_models && !m_fresh_vars.empty()) mc = m_mc.get(); else mc = 0; g->inc_depth(); result.push_back(g.get()); TRACE("tseitin_cnf", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: tseitin_cnf_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(tseitin_cnf_tactic, m, m_params); } virtual ~tseitin_cnf_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { insert_max_memory(r); r.insert("common_patterns", CPK_BOOL, "(default: true) minimize the number of auxiliary variables during CNF encoding by identifing commonly used patterns"); r.insert("distributivity", CPK_BOOL, "(default: true) minimize the number of auxiliary variables during CNF encoding by applying distributivity over unshared subformulas"); r.insert("distributivity_blowup", CPK_UINT, "(default: 32) maximum overhead for applying distributivity during CNF encoding"); r.insert("ite_chaing", CPK_BOOL, "(default: true) minimize the number of auxiliary variables during CNF encoding by identifing if-then-else chains"); r.insert("ite_extra", CPK_BOOL, "(default: true) add redundant clauses (that improve unit propagation) when encoding if-then-else formulas"); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); report_tactic_progress(":cnf-aux-vars", m_imp->m_num_aux_vars); } virtual void cleanup() { ast_manager & m = m_imp->m; imp * d = alloc(imp, m, m_params); d->m_num_aux_vars = m_imp->m_num_aux_vars; #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } virtual void collect_statistics(statistics & st) const { st.update("cnf encoding aux vars", m_imp->m_num_aux_vars); } virtual void reset_statistics() { m_imp->m_num_aux_vars = 0; } }; tactic * mk_tseitin_cnf_core_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(tseitin_cnf_tactic, m, p)); } tactic * mk_tseitin_cnf_tactic(ast_manager & m, params_ref const & p) { params_ref simp_p = p; simp_p.set_bool("elim_and", true); simp_p.set_bool("blast_distinct", true); return or_else(mk_tseitin_cnf_core_tactic(m, p), and_then(using_params(mk_simplify_tactic(m, p), simp_p), mk_tseitin_cnf_core_tactic(m, p))); } z3-4.4.0/src/tactic/core/ctx_simplify_tactic.h0000644000175000017500000000266512540347414021056 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ctx_simplify_tactic.h Abstract: Simple context simplifier for propagating constants. Author: Leonardo (leonardo) 2011-10-26 Notes: --*/ #ifndef _CTX_SIMPLIFY_TACTIC_H_ #define _CTX_SIMPLIFY_TACTIC_H_ #include"tactical.h" class ctx_simplify_tactic : public tactic { struct imp; imp * m_imp; params_ref m_params; public: ctx_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()); virtual tactic * translate(ast_manager & m) { return alloc(ctx_simplify_tactic, m, m_params); } virtual ~ctx_simplify_tactic(); virtual void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); virtual void collect_param_descrs(param_descrs & r) { get_param_descrs(r); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core); virtual void cleanup(); protected: virtual void set_cancel(bool f); }; inline tactic * mk_ctx_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()) { return clean(alloc(ctx_simplify_tactic, m, p)); } /* ADD_TACTIC("ctx-simplify", "apply contextual simplification rules.", "mk_ctx_simplify_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/core/cofactor_elim_term_ite.h0000644000175000017500000000154512540347414021507 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cofactor_elim_term_ite.h Abstract: Eliminate (ground) term if-then-else's using cofactors. Author: Leonardo de Moura (leonardo) 2011-06-05. Revision History: --*/ #ifndef _COFACTOR_ELIM_TERM_ITE_H_ #define _COFACTOR_ELIM_TERM_ITE_H_ #include"ast.h" #include"params.h" class cofactor_elim_term_ite { struct imp; imp * m_imp; params_ref m_params; public: cofactor_elim_term_ite(ast_manager & m, params_ref const & p = params_ref()); virtual ~cofactor_elim_term_ite(); void updt_params(params_ref const & p); void collect_param_descrs(param_descrs & r); void operator()(expr * t, expr_ref & r); void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } void cleanup(); void set_cancel(bool f); }; #endif z3-4.4.0/src/tactic/core/elim_uncnstr_tactic.cpp0000644000175000017500000012350712540347414021400 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: elim_uncnstr_vars.cpp Abstract: Eliminated unconstrained variables. Author: Leonardo (leonardo) 2011-10-22 Notes: --*/ #include"tactical.h" #include"extension_model_converter.h" #include"filter_model_converter.h" #include"rewriter_def.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"array_decl_plugin.h" #include"datatype_decl_plugin.h" #include"cooperate.h" #include"ast_smt2_pp.h" #include"ast_ll_pp.h" class elim_uncnstr_tactic : public tactic { struct imp { // unconstrained vars collector struct collect { expr_fast_mark1 m_visited; expr_fast_mark2 m_more_than_once; typedef std::pair frame; svector m_stack; ptr_vector m_vars; bool visit(expr * t) { if (m_visited.is_marked(t)) { if (is_uninterp_const(t)) m_more_than_once.mark(t); return true; } m_visited.mark(t); if (is_uninterp_const(t)) { m_vars.push_back(to_app(t)); return true; } if (is_var(t)) return true; if (is_app(t) && to_app(t)->get_num_args() == 0) return true; m_stack.push_back(frame(t, 0)); return false; } void process(expr * t) { SASSERT(m_stack.empty()); if (visit(t)) return; SASSERT(!m_stack.empty()); unsigned num; expr * child; while (!m_stack.empty()) { start: frame & fr = m_stack.back(); expr * t = fr.first; switch (t->get_kind()) { case AST_APP: num = to_app(t)->get_num_args(); while (fr.second < num) { child = to_app(t)->get_arg(fr.second); fr.second++; if (!visit(child)) goto start; } m_stack.pop_back(); break; case AST_QUANTIFIER: // don't need to visit patterns child = to_quantifier(t)->get_expr(); fr.second++; if (!visit(child)) goto start; m_stack.pop_back(); break; default: UNREACHABLE(); } } } void operator()(goal const & g, obj_hashtable & r) { unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * t = g.form(i); process(t); } ptr_vector::const_iterator it = m_vars.begin(); ptr_vector::const_iterator end = m_vars.end(); for (; it != end; ++it) { if (m_more_than_once.is_marked(*it)) continue; r.insert(*it); } m_visited.reset(); m_more_than_once.reset(); } }; typedef extension_model_converter mc; struct rw_cfg : public default_rewriter_cfg { bool m_produce_proofs; obj_hashtable & m_vars; ref m_mc; arith_util m_a_util; bv_util m_bv_util; array_util m_ar_util; datatype_util m_dt_util; app_ref_vector m_fresh_vars; obj_map m_cache; app_ref_vector m_cache_domain; unsigned long long m_max_memory; unsigned m_max_steps; rw_cfg(ast_manager & m, bool produce_proofs, obj_hashtable & vars, mc * _m, unsigned long long max_memory, unsigned max_steps): m_produce_proofs(produce_proofs), m_vars(vars), m_mc(_m), m_a_util(m), m_bv_util(m), m_ar_util(m), m_dt_util(m), m_fresh_vars(m), m_cache_domain(m), m_max_memory(max_memory), m_max_steps(max_steps) { } ast_manager & m() const { return m_a_util.get_manager(); } bool max_steps_exceeded(unsigned num_steps) const { cooperate("elim-uncnstr-vars"); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); return num_steps > m_max_steps; } bool uncnstr(expr * arg) const { return m_vars.contains(arg); } bool uncnstr(unsigned num, expr * const * args) const { for (unsigned i = 0; i < num; i++) if (!uncnstr(args[i])) return false; return true; } /** \brief Create a fresh variable for abstracting (f args[0] ... args[num-1]) Return true if it a new variable was created, and false if the variable already existed for this application. Store the variable in v */ bool mk_fresh_uncnstr_var_for(app * t, app * & v) { if (m_cache.find(t, v)) { return false; // variable already existed for this application } v = m().mk_fresh_const(0, m().get_sort(t)); TRACE("elim_uncnstr_bug", tout << "eliminating:\n" << mk_ismt2_pp(t, m()) << "\n";); TRACE("elim_uncnstr_bug_ll", tout << "eliminating:\n" << mk_bounded_pp(t, m()) << "\n";); m_fresh_vars.push_back(v); m_cache_domain.push_back(t); m_cache.insert(t, v); return true; } bool mk_fresh_uncnstr_var_for(func_decl * f, unsigned num, expr * const * args, app * & v) { return mk_fresh_uncnstr_var_for(m().mk_app(f, num, args), v); } bool mk_fresh_uncnstr_var_for(func_decl * f, expr * arg1, expr * arg2, app * & v) { return mk_fresh_uncnstr_var_for(m().mk_app(f, arg1, arg2), v); } bool mk_fresh_uncnstr_var_for(func_decl * f, expr * arg, app * & v) { return mk_fresh_uncnstr_var_for(m().mk_app(f, arg), v); } void add_def(expr * v, expr * def) { SASSERT(uncnstr(v)); SASSERT(to_app(v)->get_num_args() == 0); if (m_mc) m_mc->insert(to_app(v)->get_decl(), def); } void add_defs(unsigned num, expr * const * args, expr * u, expr * identity) { if (m_mc) { add_def(args[0], u); for (unsigned i = 1; i < num; i++) add_def(args[i], identity); } } // return a term that is different from t. bool mk_diff(expr * t, expr_ref & r) { sort * s = m().get_sort(t); if (m().is_bool(s)) { r = m().mk_not(t); return true; } family_id fid = s->get_family_id(); if (fid == m_a_util.get_family_id()) { r = m_a_util.mk_add(t, m_a_util.mk_numeral(rational(1), s)); return true; } if (fid == m_bv_util.get_family_id()) { r = m().mk_app(m_bv_util.get_family_id(), OP_BNOT, t); return true; } if (fid == m_ar_util.get_family_id()) { if (m().is_uninterp(get_array_range(s))) return false; unsigned arity = get_array_arity(s); for (unsigned i = 0; i < arity; i++) if (m().is_uninterp(get_array_domain(s, i))) return false; // building // r = (store t i1 ... in d) // where i1 ... in are arbitrary values // and d is a term different from (select t i1 ... in) ptr_buffer new_args; new_args.push_back(t); for (unsigned i = 0; i < arity; i++) new_args.push_back(m().get_some_value(get_array_domain(s, i))); expr_ref sel(m()); sel = m().mk_app(fid, OP_SELECT, new_args.size(), new_args.c_ptr()); expr_ref diff_sel(m()); if (!mk_diff(sel, diff_sel)) return false; new_args.push_back(diff_sel); r = m().mk_app(fid, OP_STORE, new_args.size(), new_args.c_ptr()); return true; } if (fid == m_dt_util.get_family_id()) { // In the current implementation, I only handle the case where // the datatype has a recursive constructor. ptr_vector const * constructors = m_dt_util.get_datatype_constructors(s); ptr_vector::const_iterator it = constructors->begin(); ptr_vector::const_iterator end = constructors->end(); for (; it != end; ++it) { func_decl * constructor = *it; unsigned num = constructor->get_arity(); unsigned target = UINT_MAX; for (unsigned i = 0; i < num; i++) { sort * s_arg = constructor->get_domain(i); if (s == s_arg) { target = i; continue; } if (m().is_uninterp(s_arg)) break; } if (target == UINT_MAX) continue; // use the constructor the distinct term constructor(...,t,...) ptr_buffer new_args; for (unsigned i = 0; i < num; i++) { if (i == target) { new_args.push_back(t); } else { new_args.push_back(m().get_some_value(constructor->get_domain(i))); } } r = m().mk_app(constructor, new_args.size(), new_args.c_ptr()); return true; } // TODO: handle more cases. return false; } return false; } app * process_eq(func_decl * f, expr * arg1, expr * arg2) { expr * v; expr * t; if (uncnstr(arg1)) { v = arg1; t = arg2; } else if (uncnstr(arg2)) { v = arg2; t = arg1; } else { return 0; } sort * s = m().get_sort(arg1); // Remark: // I currently do not support unconstrained vars that have // uninterpreted sorts, for the following reasons: // - Soundness // (forall ((x S) (y S)) (= x y)) // (not (= c1 c2)) // // The constants c1 and c2 have only one occurrence in // the formula above, but they are not really unconstrained. // The quantifier forces S to have interpretations of size 1. // If we replace (= c1 c2) with fresh k. The formula will // become satisfiable. // // - Even if the formula is quantifier free, I would still // have to build an interpretation for the eliminated // variables. // if (!m().is_fully_interp(s)) return 0; // If the interpreted sort has only one element, // then it is unsound to eliminate the unconstrained variable in the equality sort_size sz = s->get_num_elements(); if (sz.is_finite() && sz.size() <= 1) return 0; if (!m_mc) { // easy case, model generation is disabled. app * u; mk_fresh_uncnstr_var_for(f, arg1, arg2, u); return u; } expr_ref d(m()); if (mk_diff(t, d)) { app * u; if (!mk_fresh_uncnstr_var_for(f, arg1, arg2, u)) return u; add_def(v, m().mk_ite(u, t, d)); return u; } return 0; } app * process_basic_app(func_decl * f, unsigned num, expr * const * args) { SASSERT(f->get_family_id() == m().get_basic_family_id()); switch (f->get_decl_kind()) { case OP_ITE: SASSERT(num == 3); if (uncnstr(args[1]) && uncnstr(args[2])) { app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; add_def(args[1], r); add_def(args[2], r); return r; } if (uncnstr(args[0]) && uncnstr(args[1])) { app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; add_def(args[0], m().mk_true()); add_def(args[1], r); return r; } if (uncnstr(args[0]) && uncnstr(args[2])) { app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; add_def(args[0], m().mk_false()); add_def(args[2], r); return r; } return 0; case OP_NOT: SASSERT(num == 1); if (uncnstr(args[0])) { app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; if (m_mc) add_def(args[0], m().mk_not(r)); return r; } return 0; case OP_AND: if (num > 0 && uncnstr(num, args)) { app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; if (m_mc) add_defs(num, args, r, m().mk_true()); return r; } return 0; case OP_OR: if (num > 0 && uncnstr(num, args)) { app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; if (m_mc) add_defs(num, args, r, m().mk_false()); return r; } return 0; case OP_IFF: case OP_EQ: SASSERT(num == 2); return process_eq(f, args[0], args[1]); default: return 0; } } app * process_le_ge(func_decl * f, expr * arg1, expr * arg2, bool le) { expr * v; expr * t; if (uncnstr(arg1)) { v = arg1; t = arg2; } else if (uncnstr(arg2)) { v = arg2; t = arg1; le = !le; } else { return 0; } app * u; if (!mk_fresh_uncnstr_var_for(f, arg1, arg2, u)) return u; if (!m_mc) return u; // v = ite(u, t, t + 1) if le // v = ite(u, t, t - 1) if !le add_def(v, m().mk_ite(u, t, m_a_util.mk_add(t, m_a_util.mk_numeral(rational(le ? 1 : -1), m().get_sort(arg1))))); return u; } app * process_add(family_id fid, decl_kind add_k, decl_kind sub_k, unsigned num, expr * const * args) { if (num == 0) return 0; unsigned i; expr * v = 0; for (i = 0; i < num; i++) { expr * arg = args[i]; if (uncnstr(arg)) { v = arg; break; } } if (v == 0) return 0; app * u; if (!mk_fresh_uncnstr_var_for(m().mk_app(fid, add_k, num, args), u)) return u; if (!m_mc) return u; ptr_buffer new_args; for (unsigned j = 0; j < num; j++) { if (j == i) continue; new_args.push_back(args[j]); } if (new_args.empty()) { add_def(v, u); } else { expr * rest; if (new_args.size() == 1) rest = new_args[0]; else rest = m().mk_app(fid, add_k, new_args.size(), new_args.c_ptr()); add_def(v, m().mk_app(fid, sub_k, u, rest)); } return u; } app * process_arith_mul(func_decl * f, unsigned num, expr * const * args) { if (num == 0) return 0; sort * s = m().get_sort(args[0]); if (uncnstr(num, args)) { app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; if (m_mc) add_defs(num, args, r, m_a_util.mk_numeral(rational(1), s)); return r; } // c * v case for reals bool is_int; rational val; if (num == 2 && uncnstr(args[1]) && m_a_util.is_numeral(args[0], val, is_int) && !is_int) { if (val.is_zero()) return 0; app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; if (m_mc) { val = rational(1) / val; add_def(args[1], m_a_util.mk_mul(m_a_util.mk_numeral(val, false), r)); } return r; } return 0; } app * process_arith_app(func_decl * f, unsigned num, expr * const * args) { SASSERT(f->get_family_id() == m_a_util.get_family_id()); switch (f->get_decl_kind()) { case OP_ADD: return process_add(f->get_family_id(), OP_ADD, OP_SUB, num, args); case OP_MUL: return process_arith_mul(f, num, args); case OP_LE: SASSERT(num == 2); return process_le_ge(f, args[0], args[1], true); case OP_GE: SASSERT(num == 2); return process_le_ge(f, args[0], args[1], false); default: return 0; } } app * process_bv_mul(func_decl * f, unsigned num, expr * const * args) { if (num == 0) return 0; if (uncnstr(num, args)) { sort * s = m().get_sort(args[0]); app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; if (m_mc) add_defs(num, args, r, m_bv_util.mk_numeral(rational(1), s)); return r; } // c * v (c is even) case unsigned bv_size; rational val; rational inv; if (num == 2 && uncnstr(args[1]) && m_bv_util.is_numeral(args[0], val, bv_size) && m_bv_util.mult_inverse(val, bv_size, inv)) { app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; sort * s = m().get_sort(args[1]); if (m_mc) add_def(args[1], m_bv_util.mk_bv_mul(m_bv_util.mk_numeral(inv, s), r)); return r; } return 0; } app * process_extract(func_decl * f, expr * arg) { if (!uncnstr(arg)) return 0; app * r; if (!mk_fresh_uncnstr_var_for(f, arg, r)) return r; if (!m_mc) return r; unsigned high = m_bv_util.get_extract_high(f); unsigned low = m_bv_util.get_extract_low(f); unsigned bv_size = m_bv_util.get_bv_size(m().get_sort(arg)); if (bv_size == high - low + 1) { add_def(arg, r); } else { ptr_buffer args; if (high < bv_size - 1) args.push_back(m_bv_util.mk_numeral(rational(0), bv_size - high - 1)); args.push_back(r); if (low > 0) args.push_back(m_bv_util.mk_numeral(rational(0), low)); add_def(arg, m_bv_util.mk_concat(args.size(), args.c_ptr())); } return r; } app * process_bv_div(func_decl * f, expr * arg1, expr * arg2) { if (uncnstr(arg1) && uncnstr(arg2)) { sort * s = m().get_sort(arg1); app * r; if (!mk_fresh_uncnstr_var_for(f, arg1, arg2, r)) return r; if (!m_mc) return r; add_def(arg1, r); add_def(arg2, m_bv_util.mk_numeral(rational(1), s)); return r; } return 0; } app * process_concat(func_decl * f, unsigned num, expr * const * args) { if (num == 0) return 0; if (!uncnstr(num, args)) return 0; app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; if (m_mc) { unsigned i = num; unsigned low = 0; while (i > 0) { --i; expr * arg = args[i]; unsigned sz = m_bv_util.get_bv_size(arg); add_def(arg, m_bv_util.mk_extract(low + sz - 1, low, r)); low += sz; } } return r; } app * process_bv_le(func_decl * f, expr * arg1, expr * arg2, bool is_signed) { if (m_produce_proofs) { // The result of bv_le is not just introducing a new fresh name, // we need a side condition. // TODO: the correct proof step return 0; } if (uncnstr(arg1)) { // v <= t expr * v = arg1; expr * t = arg2; // v <= t ---> (u or t == MAX) u is fresh // add definition v = ite(u or t == MAX, t, t+1) unsigned bv_sz = m_bv_util.get_bv_size(arg1); rational MAX; if (is_signed) MAX = rational::power_of_two(bv_sz - 1) - rational(1); else MAX = rational::power_of_two(bv_sz) - rational(1); app * u; bool is_new = mk_fresh_uncnstr_var_for(f, arg1, arg2, u); app * r = m().mk_or(u, m().mk_eq(t, m_bv_util.mk_numeral(MAX, bv_sz))); if (m_mc && is_new) add_def(v, m().mk_ite(r, t, m_bv_util.mk_bv_add(t, m_bv_util.mk_numeral(rational(1), bv_sz)))); return r; } if (uncnstr(arg2)) { // v >= t expr * v = arg2; expr * t = arg1; // v >= t ---> (u ot t == MIN) u is fresh // add definition v = ite(u or t == MIN, t, t-1) unsigned bv_sz = m_bv_util.get_bv_size(arg1); rational MIN; if (is_signed) MIN = -rational::power_of_two(bv_sz - 1); else MIN = rational(0); app * u; bool is_new = mk_fresh_uncnstr_var_for(f, arg1, arg2, u); app * r = m().mk_or(u, m().mk_eq(t, m_bv_util.mk_numeral(MIN, bv_sz))); if (m_mc && is_new) add_def(v, m().mk_ite(r, t, m_bv_util.mk_bv_sub(t, m_bv_util.mk_numeral(rational(1), bv_sz)))); return r; } return 0; } app * process_bv_app(func_decl * f, unsigned num, expr * const * args) { SASSERT(f->get_family_id() == m_bv_util.get_family_id()); switch (f->get_decl_kind()) { case OP_BADD: return process_add(f->get_family_id(), OP_BADD, OP_BSUB, num, args); case OP_BMUL: return process_bv_mul(f, num, args); case OP_BSDIV: case OP_BUDIV: case OP_BSDIV_I: case OP_BUDIV_I: SASSERT(num == 2); return process_bv_div(f, args[0], args[1]); case OP_SLEQ: SASSERT(num == 2); return process_bv_le(f, args[0], args[1], true); case OP_ULEQ: SASSERT(num == 2); return process_bv_le(f, args[0], args[1], false); case OP_CONCAT: return process_concat(f, num, args); case OP_EXTRACT: SASSERT(num == 1); return process_extract(f, args[0]); case OP_BNOT: SASSERT(num == 1); if (uncnstr(args[0])) { app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; if (m_mc) add_def(args[0], m().mk_app(f, r)); return r; } return 0; case OP_BOR: if (num > 0 && uncnstr(num, args)) { sort * s = m().get_sort(args[0]); app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; if (m_mc) add_defs(num, args, r, m_bv_util.mk_numeral(rational(0), s)); return r; } return 0; default: return 0; } } app * process_array_app(func_decl * f, unsigned num, expr * const * args) { SASSERT(f->get_family_id() == m_ar_util.get_family_id()); switch (f->get_decl_kind()) { case OP_SELECT: if (uncnstr(args[0])) { app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; sort * s = m().get_sort(args[0]); if (m_mc) add_def(args[0], m_ar_util.mk_const_array(s, r)); return r; } return 0; case OP_STORE: if (uncnstr(args[0]) && uncnstr(args[num-1])) { app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; if (m_mc) { add_def(args[num-1], m().mk_app(m_ar_util.get_family_id(), OP_SELECT, num-1, args)); add_def(args[0], r); } return r; } default: return 0; } } app * process_datatype_app(func_decl * f, unsigned num, expr * const * args) { if (m_dt_util.is_recognizer(f)) { SASSERT(num == 1); if (uncnstr(args[0])) { if (!m_mc) { app * r; mk_fresh_uncnstr_var_for(f, num, args, r); return r; } // TODO: handle model generation } } else if (m_dt_util.is_accessor(f)) { SASSERT(num == 1); if (uncnstr(args[0])) { if (!m_mc) { app * r; mk_fresh_uncnstr_var_for(f, num, args, r); return r; } func_decl * c = m_dt_util.get_accessor_constructor(f); for (unsigned i = 0; i < c->get_arity(); i++) if (!m().is_fully_interp(c->get_domain(i))) return 0; app * u; if (!mk_fresh_uncnstr_var_for(f, num, args, u)) return u; ptr_vector const * accs = m_dt_util.get_constructor_accessors(c); ptr_buffer new_args; for (unsigned i = 0; i < accs->size(); i++) { if (accs->get(i) == f) new_args.push_back(u); else new_args.push_back(m().get_some_value(c->get_domain(i))); } add_def(args[0], m().mk_app(c, new_args.size(), new_args.c_ptr())); return u; } } else if (m_dt_util.is_constructor(f)) { if (uncnstr(num, args)) { app * u; if (!mk_fresh_uncnstr_var_for(f, num, args, u)) return u; if (!m_mc) return u; ptr_vector const * accs = m_dt_util.get_constructor_accessors(f); for (unsigned i = 0; i < num; i++) { add_def(args[i], m().mk_app(accs->get(i), u)); } return u; } } return 0; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (num == 0) return BR_FAILED; family_id fid = f->get_family_id(); if (fid == null_family_id) return BR_FAILED; for (unsigned i = 0; i < num; i++) { if (!is_ground(args[i])) return BR_FAILED; // non-ground terms are not handled. } app * u = 0; if (fid == m().get_basic_family_id()) u = process_basic_app(f, num, args); else if (fid == m_a_util.get_family_id()) u = process_arith_app(f, num, args); else if (fid == m_bv_util.get_family_id()) u = process_bv_app(f, num, args); else if (fid == m_ar_util.get_family_id()) u = process_array_app(f, num, args); else if (fid == m_dt_util.get_family_id()) u = process_datatype_app(f, num, args); if (u == 0) return BR_FAILED; result = u; if (m_produce_proofs) { expr * s = m().mk_app(f, num, args); expr * eq = m().mk_eq(s, u); proof * pr1 = m().mk_def_intro(eq); result_pr = m().mk_apply_def(s, u, pr1); } return BR_DONE; } }; class rw : public rewriter_tpl { rw_cfg m_cfg; public: rw(ast_manager & m, bool produce_proofs, obj_hashtable & vars, mc * _m, unsigned long long max_memory, unsigned max_steps): rewriter_tpl(m, produce_proofs, m_cfg), m_cfg(m, produce_proofs, vars, _m, max_memory, max_steps) { } }; ast_manager & m_manager; ref m_mc; obj_hashtable m_vars; scoped_ptr m_rw; unsigned m_num_elim_apps; unsigned long long m_max_memory; unsigned m_max_steps; imp(ast_manager & m, params_ref const & p): m_manager(m), m_num_elim_apps(0) { updt_params(p); } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_max_steps = p.get_uint("max_steps", UINT_MAX); } ast_manager & m() { return m_manager; } void init_mc(bool produce_models) { if (!produce_models) { m_mc = 0; return; } m_mc = alloc(mc, m()); } void init_rw(bool produce_proofs) { #pragma omp critical (tactic_cancel) { m_rw = alloc(rw, m(), produce_proofs, m_vars, m_mc.get(), m_max_memory, m_max_steps); } } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { mc = 0; pc = 0; core = 0; bool produce_models = g->models_enabled(); bool produce_proofs = g->proofs_enabled(); TRACE("elim_uncnstr_bug", g->display(tout);); tactic_report report("elim-uncnstr-vars", *g); m_vars.reset(); collect p; p(*g, m_vars); if (m_vars.empty()) { result.push_back(g.get()); // did not increase depth since it didn't do anything. return; } bool modified = true; TRACE("elim_uncnstr", tout << "unconstrained variables...\n"; obj_hashtable::iterator it = m_vars.begin(); obj_hashtable::iterator end = m_vars.end(); for (; it != end; ++it) { expr * v = *it; tout << mk_ismt2_pp(v, m()) << " "; } tout << "\n";); init_mc(produce_models); init_rw(produce_proofs); expr_ref new_f(m()); proof_ref new_pr(m()); unsigned round = 0; unsigned size = g->size(); unsigned idx = 0; while (true) { for (; idx < size; idx++) { expr * f = g->form(idx); m_rw->operator()(f, new_f, new_pr); if (f == new_f) continue; modified = true; if (produce_proofs) { proof * pr = g->pr(idx); new_pr = m().mk_modus_ponens(pr, new_pr); } g->update(idx, new_f, new_pr, g->dep(idx)); } if (!modified) { if (round == 0) { mc = 0; } else { app_ref_vector & fresh_vars = m_rw->cfg().m_fresh_vars; m_num_elim_apps = fresh_vars.size(); if (produce_models && !fresh_vars.empty()) { filter_model_converter * fmc = alloc(filter_model_converter, m()); for (unsigned i = 0; i < fresh_vars.size(); i++) fmc->insert(fresh_vars.get(i)->get_decl()); mc = concat(fmc, m_mc.get()); } else { mc = 0; } } m_mc = 0; #pragma omp critical (tactic_cancel) { m_rw = 0; } TRACE("elim_uncnstr", if (mc) mc->display(tout);); result.push_back(g.get()); g->inc_depth(); return; } modified = false; round ++; size = g->size(); m_rw->reset(); // reset cache m_vars.reset(); { collect p; p(*g, m_vars); } if (m_vars.empty()) idx = size; // force to finish else idx = 0; } } void set_cancel(bool f) { if (m_rw) m_rw->set_cancel(f); } }; imp * m_imp; params_ref m_params; public: elim_uncnstr_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(elim_uncnstr_tactic, m, m_params); } virtual ~elim_uncnstr_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { insert_max_memory(r); insert_max_steps(r); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(g, result, mc, pc, core); report_tactic_progress(":num-elim-apps", get_num_elim_apps()); } virtual void cleanup() { unsigned num_elim_apps = get_num_elim_apps(); ast_manager & m = m_imp->m_manager; imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); m_imp->m_num_elim_apps = num_elim_apps; } unsigned get_num_elim_apps() const { return m_imp->m_num_elim_apps; } virtual void collect_statistics(statistics & st) const { st.update("eliminated applications", get_num_elim_apps()); } virtual void reset_statistics() { m_imp->m_num_elim_apps = 0; } protected: virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_elim_uncnstr_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(elim_uncnstr_tactic, m, p)); } z3-4.4.0/src/tactic/core/propagate_values_tactic.cpp0000644000175000017500000002174512540347414022240 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: propagate_values_tactic.cpp Abstract: Propagate values using equalities of the form (= t v) where v is a value, and atoms t and (not t) Author: Leonardo de Moura (leonardo) 2011-12-28. Revision History: --*/ #include"tactical.h" #include"propagate_values_tactic.h" #include"th_rewriter.h" #include"ast_smt2_pp.h" #include"expr_substitution.h" #include"goal_shared_occs.h" class propagate_values_tactic : public tactic { struct imp { ast_manager & m_manager; th_rewriter m_r; scoped_ptr m_subst; goal * m_goal; goal_shared_occs m_occs; unsigned m_idx; unsigned m_max_rounds; bool m_modified; imp(ast_manager & m, params_ref const & p): m_manager(m), m_r(m, p), m_goal(0), m_occs(m, true /* track atoms */) { updt_params_core(p); } void updt_params_core(params_ref const & p) { m_max_rounds = p.get_uint("max_rounds", 4); } void updt_params(params_ref const & p) { m_r.updt_params(p); updt_params_core(p); } ast_manager & m() const { return m_manager; } void set_cancel(bool f) { m_r.set_cancel(f); } bool is_shared(expr * t) { return m_occs.is_shared(t); } bool is_shared_neg(expr * t, expr * & atom) { if (!m().is_not(t)) return false; atom = to_app(t)->get_arg(0); return is_shared(atom); } bool is_shared_eq(expr * t, expr * & lhs, expr * & value) { if (!m().is_eq(t)) return false; expr * arg1 = to_app(t)->get_arg(0); expr * arg2 = to_app(t)->get_arg(1); if (m().is_value(arg1) && is_shared(arg2)) { lhs = arg2; value = arg1; return true; } if (m().is_value(arg2) && is_shared(arg1)) { lhs = arg1; value = arg2; return true; } return false; } void push_result(expr * new_curr, proof * new_pr) { if (m_goal->proofs_enabled()) { proof * pr = m_goal->pr(m_idx); new_pr = m().mk_modus_ponens(pr, new_pr); } expr_dependency_ref new_d(m()); if (m_goal->unsat_core_enabled()) { new_d = m_goal->dep(m_idx); expr_dependency * used_d = m_r.get_used_dependencies(); if (used_d != 0) { new_d = m().mk_join(new_d, used_d); m_r.reset_used_dependencies(); } } m_goal->update(m_idx, new_curr, new_pr, new_d); if (is_shared(new_curr)) { m_subst->insert(new_curr, m().mk_true(), m().mk_iff_true(new_pr), new_d); } expr * atom; if (is_shared_neg(new_curr, atom)) { m_subst->insert(atom, m().mk_false(), m().mk_iff_false(new_pr), new_d); } expr * lhs, * value; if (is_shared_eq(new_curr, lhs, value)) { TRACE("shallow_context_simplifier_bug", tout << "found eq:\n" << mk_ismt2_pp(new_curr, m()) << "\n";); m_subst->insert(lhs, value, new_pr, new_d); } } void process_current() { expr * curr = m_goal->form(m_idx); expr_ref new_curr(m()); proof_ref new_pr(m()); if (!m_subst->empty()) { m_r(curr, new_curr, new_pr); } else { new_curr = curr; if (m().proofs_enabled()) new_pr = m().mk_reflexivity(curr); } TRACE("shallow_context_simplifier_bug", tout << mk_ismt2_pp(curr, m()) << "\n---->\n" << mk_ismt2_pp(new_curr, m()) << "\n";); push_result(new_curr, new_pr); if (new_curr != curr) m_modified = true; } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("propagate-values", *g); m_goal = g.get(); bool forward = true; expr_ref new_curr(m()); proof_ref new_pr(m()); unsigned size = m_goal->size(); m_idx = 0; m_modified = false; unsigned round = 0; if (m_goal->inconsistent()) goto end; if (m_max_rounds == 0) goto end; m_subst = alloc(expr_substitution, m(), g->unsat_core_enabled(), g->proofs_enabled()); m_r.set_substitution(m_subst.get()); m_occs(*m_goal); while (true) { TRACE("propagate_values", tout << "while(true) loop\n"; m_goal->display(tout);); if (forward) { for (; m_idx < size; m_idx++) { process_current(); if (m_goal->inconsistent()) goto end; } if (m_subst->empty() && !m_modified) goto end; m_occs(*m_goal); m_idx = m_goal->size(); forward = false; m_subst->reset(); m_r.set_substitution(m_subst.get()); // reset, but keep substitution } else { while (m_idx > 0) { m_idx--; process_current(); if (m_goal->inconsistent()) goto end; } if (!m_modified) goto end; m_subst->reset(); m_r.set_substitution(m_subst.get()); // reset, but keep substitution m_modified = false; m_occs(*m_goal); m_idx = 0; size = m_goal->size(); forward = true; } round++; if (round >= m_max_rounds) break; IF_VERBOSE(100, verbose_stream() << "starting new round, goal size: " << m_goal->num_exprs() << std::endl;); TRACE("propagate_values", tout << "round finished\n"; m_goal->display(tout); tout << "\n";); } end: m_goal->elim_redundancies(); m_goal->inc_depth(); result.push_back(m_goal); SASSERT(m_goal->is_well_sorted()); TRACE("propagate_values", tout << "end\n"; m_goal->display(tout);); TRACE("propagate_values_core", m_goal->display_with_dependencies(tout);); m_goal = 0; } }; imp * m_imp; params_ref m_params; public: propagate_values_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(propagate_values_tactic, m, m_params); } virtual ~propagate_values_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { th_rewriter::get_param_descrs(r); r.insert("max_rounds", CPK_UINT, "(default: 2) maximum number of rounds."); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { try { (*m_imp)(in, result, mc, pc, core); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } virtual void cleanup() { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } protected: virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_propagate_values_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(propagate_values_tactic, m, p)); } z3-4.4.0/src/tactic/core/simplify_tactic.cpp0000644000175000017500000000662012540347414020526 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: simplify_tactic.cpp Abstract: Apply simplification and rewriting rules. Author: Leonardo (leonardo) 2011-11-20 Notes: --*/ #include"simplify_tactic.h" #include"th_rewriter.h" #include"ast_smt2_pp.h" struct simplify_tactic::imp { ast_manager & m_manager; th_rewriter m_r; unsigned m_num_steps; imp(ast_manager & m, params_ref const & p): m_manager(m), m_r(m, p), m_num_steps(0) { } ~imp() { } ast_manager & m() const { return m_manager; } void set_cancel(bool f) { m_r.set_cancel(f); } void reset() { m_r.reset(); m_num_steps = 0; } void operator()(goal & g) { SASSERT(g.is_well_sorted()); tactic_report report("simplifier", g); TRACE("before_simplifier", g.display(tout);); m_num_steps = 0; if (g.inconsistent()) return; expr_ref new_curr(m()); proof_ref new_pr(m()); unsigned size = g.size(); for (unsigned idx = 0; idx < size; idx++) { if (g.inconsistent()) break; expr * curr = g.form(idx); m_r(curr, new_curr, new_pr); m_num_steps += m_r.get_num_steps(); if (g.proofs_enabled()) { proof * pr = g.pr(idx); new_pr = m().mk_modus_ponens(pr, new_pr); } g.update(idx, new_curr, new_pr, g.dep(idx)); } TRACE("after_simplifier_bug", g.display(tout);); g.elim_redundancies(); TRACE("after_simplifier", g.display(tout);); TRACE("after_simplifier_detail", g.display_with_dependencies(tout);); SASSERT(g.is_well_sorted()); } unsigned get_num_steps() const { return m_num_steps; } }; simplify_tactic::simplify_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } simplify_tactic::~simplify_tactic() { dealloc(m_imp); } void simplify_tactic::updt_params(params_ref const & p) { m_params = p; m_imp->m_r.updt_params(p); } void simplify_tactic::get_param_descrs(param_descrs & r) { th_rewriter::get_param_descrs(r); } void simplify_tactic::operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { try { (*m_imp)(*(in.get())); in->inc_depth(); result.push_back(in.get()); mc = 0; pc = 0; core = 0; } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } void simplify_tactic::set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } void simplify_tactic::cleanup() { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } unsigned simplify_tactic::get_num_steps() const { return m_imp->get_num_steps(); } tactic * mk_simplify_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(simplify_tactic, m, p)); } tactic * mk_elim_and_tactic(ast_manager & m, params_ref const & p) { params_ref xp = p; xp.set_bool("elim_and", true); return using_params(mk_simplify_tactic(m, xp), xp); } z3-4.4.0/src/tactic/core/occf_tactic.cpp0000644000175000017500000001651112540347414017604 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: occf_tactic.cpp Abstract: Put clauses in the assertion set in OOC (one constraint per clause) form. Constraints occuring in formulas that are not clauses are ignored. The formula can be put into CNF by using mk_sat_preprocessor strategy. Author: Leonardo de Moura (leonardo) 2011-12-28. Revision History: --*/ #include"tactical.h" #include"occf_tactic.h" #include"filter_model_converter.h" #include"cooperate.h" class occf_tactic : public tactic { struct imp { ast_manager & m; volatile bool m_cancel; filter_model_converter * m_mc; imp(ast_manager & _m): m(_m) { m_cancel = false; } void set_cancel(bool f) { m_cancel = f; } void checkpoint() { if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); cooperate("occf"); } bool is_literal(expr * t) const { expr * atom; return is_uninterp_const(t) || (m.is_not(t, atom) && is_uninterp_const(atom)); } bool is_constraint(expr * t) const { return !is_literal(t); } bool is_target(app * cls) { SASSERT(m.is_or(cls)); bool found = false; unsigned num = cls->get_num_args(); for (unsigned i = 0; i < num; i++) { if (is_constraint(cls->get_arg(i))) { if (found) return true; found = true; } } return false; } struct bvar_info { expr * m_bvar; unsigned m_gen_pos:1; unsigned m_gen_neg:1; bvar_info():m_bvar(0), m_gen_pos(false), m_gen_neg(false) {} bvar_info(expr * var, bool sign): m_bvar(var), m_gen_pos(!sign), m_gen_neg(sign) { } }; typedef obj_map cnstr2bvar; expr * get_aux_lit(cnstr2bvar & c2b, expr * cnstr, goal_ref const & g) { bool sign = false; while (m.is_not(cnstr)) { cnstr = to_app(cnstr)->get_arg(0); sign = !sign; } cnstr2bvar::obj_map_entry * entry = c2b.find_core(cnstr); if (entry == 0) return 0; bvar_info & info = entry->get_data().m_value; if (sign) { if (!info.m_gen_neg) { info.m_gen_neg = true; g->assert_expr(m.mk_or(info.m_bvar, m.mk_not(cnstr)), 0, 0); } return m.mk_not(info.m_bvar); } else { if (!info.m_gen_pos) { info.m_gen_pos = true; g->assert_expr(m.mk_or(m.mk_not(info.m_bvar), cnstr), 0, 0); } return info.m_bvar; } } expr * mk_aux_lit(cnstr2bvar & c2b, expr * cnstr, bool produce_models, goal_ref const & g) { bool sign = false; while (m.is_not(cnstr)) { cnstr = to_app(cnstr)->get_arg(0); sign = !sign; } SASSERT(!c2b.contains(cnstr)); expr * bvar = m.mk_fresh_const(0, m.mk_bool_sort()); if (produce_models) m_mc->insert(to_app(bvar)->get_decl()); c2b.insert(cnstr, bvar_info(bvar, sign)); if (sign) { g->assert_expr(m.mk_or(bvar, m.mk_not(cnstr)), 0, 0); return m.mk_not(bvar); } else { g->assert_expr(m.mk_or(m.mk_not(bvar), cnstr), 0, 0); return bvar; } } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; fail_if_proof_generation("occf", g); bool produce_models = g->models_enabled(); tactic_report report("occf", *g); m_mc = 0; ptr_vector new_lits; cnstr2bvar c2b; unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { checkpoint(); expr * f = g->form(i); expr_dependency * d = g->dep(i); if (!m.is_or(f)) continue; app * cls = to_app(f); if (!is_target(cls)) continue; if (produce_models && !m_mc) { m_mc = alloc(filter_model_converter, m); mc = m_mc; } expr * keep = 0; new_lits.reset(); unsigned num = cls->get_num_args(); for (unsigned j = 0; j < num; j++) { expr * l = cls->get_arg(j); if (is_constraint(l)) { expr * new_l = get_aux_lit(c2b, l, g); if (new_l != 0) { new_lits.push_back(new_l); } else if (keep == 0) { keep = l; } else { new_l = mk_aux_lit(c2b, l, produce_models, g); new_lits.push_back(new_l); } } else { new_lits.push_back(l); } } if (keep != 0) new_lits.push_back(keep); g->update(i, m.mk_or(new_lits.size(), new_lits.c_ptr()), 0, d); } g->inc_depth(); result.push_back(g.get()); TRACE("occf", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; public: occf_tactic(ast_manager & m) { m_imp = alloc(imp, m); } virtual tactic * translate(ast_manager & m) { return alloc(occf_tactic, m); } virtual ~occf_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) {} virtual void collect_param_descrs(param_descrs & r) {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } virtual void cleanup() { imp * d = alloc(imp, m_imp->m); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } protected: virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_occf_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(occf_tactic, m)); } z3-4.4.0/src/tactic/core/der_tactic.cpp0000644000175000017500000000512412540347414017442 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: der_tactic.cpp Abstract: DER tactic Author: Leonardo de Moura (leonardo) 2012-10-20 --*/ #include"der.h" #include"tactical.h" class der_tactic : public tactic { struct imp { ast_manager & m_manager; der_rewriter m_r; imp(ast_manager & m): m_manager(m), m_r(m) { } ast_manager & m() const { return m_manager; } void set_cancel(bool f) { m_r.set_cancel(f); } void reset() { m_r.reset(); } void operator()(goal & g) { SASSERT(g.is_well_sorted()); bool proofs_enabled = g.proofs_enabled(); tactic_report report("der", g); TRACE("before_der", g.display(tout);); expr_ref new_curr(m()); proof_ref new_pr(m()); unsigned size = g.size(); for (unsigned idx = 0; idx < size; idx++) { if (g.inconsistent()) break; expr * curr = g.form(idx); m_r(curr, new_curr, new_pr); if (proofs_enabled) { proof * pr = g.pr(idx); new_pr = m().mk_modus_ponens(pr, new_pr); } g.update(idx, new_curr, new_pr, g.dep(idx)); } g.elim_redundancies(); TRACE("after_der", g.display(tout);); SASSERT(g.is_well_sorted()); } }; imp * m_imp; public: der_tactic(ast_manager & m) { m_imp = alloc(imp, m); } virtual tactic * translate(ast_manager & m) { return alloc(der_tactic, m); } virtual ~der_tactic() { dealloc(m_imp); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { mc = 0; pc = 0; core = 0; (*m_imp)(*(in.get())); in->inc_depth(); result.push_back(in.get()); } virtual void cleanup() { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_der_tactic(ast_manager & m) { return alloc(der_tactic, m); } z3-4.4.0/src/tactic/core/split_clause_tactic.cpp0000644000175000017500000001203612540347414021357 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: split_clause_tactic.cpp Abstract: Tactic that creates a subgoal for each literal in a clause (l_1 or ... or l_n). The tactic fails if the main goal does not contain any clause. Author: Leonardo (leonardo) 2011-11-21 Notes: --*/ #include"tactical.h" #include"split_clause_tactic.h" class split_clause_tactic : public tactic { bool m_largest_clause; unsigned select_clause(ast_manager & m, goal_ref const & in) { unsigned result_idx = UINT_MAX; unsigned len = 0; unsigned sz = in->size(); for (unsigned i = 0; i < sz; i++) { expr * f = in->form(i); if (m.is_or(f)) { unsigned curr_len = to_app(f)->get_num_args(); if (curr_len >= 2) { // consider only non unit clauses if (!m_largest_clause) return i; if (curr_len > len) { result_idx = i; len = curr_len; } } } } return result_idx; } class split_pc : public proof_converter { ast_manager & m_manager; app * m_clause; proof * m_clause_pr; public: split_pc(ast_manager & m, app * cls, proof * pr):m_manager(m), m_clause(cls), m_clause_pr(pr) { m.inc_ref(cls); m.inc_ref(pr); } ~split_pc() { m_manager.dec_ref(m_clause); m_manager.dec_ref(m_clause_pr); } virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) { // Let m_clause be of the form (l_0 or ... or l_{num_source - 1}) // Each source[i] proof is a proof for "false" using l_i as a hypothesis // So, I use lemma for producing a proof for (not l_i) that does not contain the hypothesis, // and unit_resolution for building a proof for the goal. SASSERT(num_source == m_clause->get_num_args()); proof_ref_buffer prs(m); prs.push_back(m_clause_pr); for (unsigned i = 0; i < num_source; i++) { proof * pr_i = source[i]; expr * not_li = m.mk_not(m_clause->get_arg(i)); prs.push_back(m.mk_lemma(pr_i, not_li)); } result = m.mk_unit_resolution(prs.size(), prs.c_ptr()); } virtual proof_converter * translate(ast_translation & translator) { return alloc(split_pc, translator.to(), translator(m_clause), translator(m_clause_pr)); } }; public: split_clause_tactic(params_ref const & ref = params_ref()) { updt_params(ref); } virtual tactic * translate(ast_manager & m) { split_clause_tactic * t = alloc(split_clause_tactic); t->m_largest_clause = m_largest_clause; return t; } virtual ~split_clause_tactic() { } virtual void updt_params(params_ref const & p) { m_largest_clause = p.get_bool("split_largest_clause", false); } virtual void collect_param_descrs(param_descrs & r) { r.insert("split_largest_clause", CPK_BOOL, "(default: false) split the largest clause in the goal."); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(in->is_well_sorted()); tactic_report report("split-clause", *in); TRACE("before_split_clause", in->display(tout);); pc = 0; mc = 0; core = 0; ast_manager & m = in->m(); unsigned cls_pos = select_clause(m, in); if (cls_pos == UINT_MAX) { throw tactic_exception("split-clause tactic failed, goal does not contain any clause"); } bool produce_proofs = in->proofs_enabled(); app * cls = to_app(in->form(cls_pos)); expr_dependency * cls_dep = in->dep(cls_pos); if (produce_proofs) pc = alloc(split_pc, m, cls, in->pr(cls_pos)); unsigned cls_sz = cls->get_num_args(); report_tactic_progress(":num-new-branches", cls_sz); for (unsigned i = 0; i < cls_sz; i++) { goal * subgoal_i; if (i == cls_sz - 1) subgoal_i = in.get(); else subgoal_i = alloc(goal, *in); expr * lit_i = cls->get_arg(i); proof * pr_i = 0; if (produce_proofs) pr_i = m.mk_hypothesis(lit_i); subgoal_i->update(cls_pos, lit_i, pr_i, cls_dep); subgoal_i->inc_depth(); result.push_back(subgoal_i); } } virtual void cleanup() { // do nothing this tactic is too simple } }; tactic * mk_split_clause_tactic(params_ref const & p) { return clean(alloc(split_clause_tactic, p)); } z3-4.4.0/src/tactic/core/elim_term_ite_tactic.h0000644000175000017500000000112412540347414021147 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: elim_term_ite_tactic.h Abstract: Eliminate term if-then-else by adding new fresh auxiliary variables. Author: Leonardo (leonardo) 2011-12-29 Notes: --*/ #ifndef _ELIM_TERM_ITE_TACTIC_H_ #define _ELIM_TERM_ITE_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_elim_term_ite_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("elim-term-ite", "eliminate term if-then-else by adding fresh auxiliary declarations.", "mk_elim_term_ite_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/core/simplify_tactic.h0000644000175000017500000000305012540347414020165 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: simplify_tactic.h Abstract: Apply simplification and rewriting rules. Author: Leonardo (leonardo) 2011-11-20 Notes: --*/ #ifndef _SIMPLIFY_TACTIC_H_ #define _SIMPLIFY_TACTIC_H_ #include"tactic.h" #include"tactical.h" class simplify_tactic : public tactic { struct imp; imp * m_imp; params_ref m_params; public: simplify_tactic(ast_manager & m, params_ref const & ref = params_ref()); virtual ~simplify_tactic(); virtual void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); virtual void collect_param_descrs(param_descrs & r) { get_param_descrs(r); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core); virtual void cleanup(); unsigned get_num_steps() const; virtual tactic * translate(ast_manager & m) { return alloc(simplify_tactic, m, m_params); } protected: virtual void set_cancel(bool f); }; tactic * mk_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()); tactic * mk_elim_and_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("simplify", "apply simplification rules.", "mk_simplify_tactic(m, p)") ADD_TACTIC("elim-and", "convert (and a b) into (not (or (not a) (not b))).", "mk_elim_and_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/core/cofactor_term_ite_tactic.h0000644000175000017500000000120112540347414022015 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: cofactor_term_ite_tactic.h Abstract: Wrap cofactor_elim_term_ite as a tactic. Eliminate (ground) term if-then-else's using cofactors. Author: Leonardo de Moura (leonardo) 2012-02-20. Revision History: --*/ #ifndef _COFACTOR_TERM_ITE_TACTIC_H_ #define _COFACTOR_TERM_ITE_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_cofactor_term_ite_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("cofactor-term-ite", "eliminate term if-the-else using cofactors.", "mk_cofactor_term_ite_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/core/distribute_forall_tactic.cpp0000644000175000017500000001113712540347414022406 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: distribute_forall_tactic.cpp Abstract: Author: Leonardo de Moura (leonardo) 2012-02-18. --*/ #include"tactical.h" #include"rewriter_def.h" #include"var_subst.h" class distribute_forall_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { ast_manager & m; rw_cfg(ast_manager & _m):m(_m) {} bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { if (m.is_not(new_body) && m.is_or(to_app(new_body)->get_arg(0))) { // (forall X (not (or F1 ... Fn))) // --> // (and (forall X (not F1)) // ... // (forall X (not Fn))) app * or_e = to_app(to_app(new_body)->get_arg(0)); unsigned num_args = or_e->get_num_args(); expr_ref_buffer new_args(m); for (unsigned i = 0; i < num_args; i++) { expr * arg = or_e->get_arg(i); expr * not_arg = m.mk_not(arg); quantifier_ref tmp_q(m); tmp_q = m.update_quantifier(old_q, not_arg); expr_ref new_q(m); elim_unused_vars(m, tmp_q, new_q); new_args.push_back(new_q); } result = m.mk_and(new_args.size(), new_args.c_ptr()); return true; } if (m.is_and(new_body)) { // (forall X (and F1 ... Fn)) // --> // (and (forall X F1) // ... // (forall X Fn) unsigned num_args = to_app(new_body)->get_num_args(); expr_ref_buffer new_args(m); for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(new_body)->get_arg(i); quantifier_ref tmp_q(m); tmp_q = m.update_quantifier(old_q, arg); expr_ref new_q(m); elim_unused_vars(m, tmp_q, new_q); new_args.push_back(new_q); } result = m.mk_and(new_args.size(), new_args.c_ptr()); return true; } return false; } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m, bool proofs_enabled): rewriter_tpl(m, proofs_enabled, m_cfg), m_cfg(m) { } }; rw * m_rw; public: distribute_forall_tactic():m_rw(0) {} virtual tactic * translate(ast_manager & m) { return alloc(distribute_forall_tactic); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); ast_manager & m = g->m(); bool produce_proofs = g->proofs_enabled(); rw r(m, produce_proofs); #pragma omp critical (tactic_cancel) { m_rw = &r; } mc = 0; pc = 0; core = 0; result.reset(); tactic_report report("distribute-forall", *g); expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { if (g->inconsistent()) break; expr * curr = g->form(idx); r(curr, new_curr, new_pr); if (g->proofs_enabled()) { proof * pr = g->pr(idx); new_pr = m.mk_modus_ponens(pr, new_pr); } g->update(idx, new_curr, new_pr, g->dep(idx)); } g->inc_depth(); result.push_back(g.get()); TRACE("distribute-forall", g->display(tout);); SASSERT(g->is_well_sorted()); #pragma omp critical (tactic_cancel) { m_rw = 0; } } virtual void set_cancel(bool f) { if (m_rw) m_rw->set_cancel(f); } virtual void cleanup() {} }; tactic * mk_distribute_forall_tactic(ast_manager & m, params_ref const & p) { return alloc(distribute_forall_tactic); } z3-4.4.0/src/tactic/core/cofactor_elim_term_ite.cpp0000644000175000017500000006027212540347414022044 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cofactor_elim_term_ite.cpp Abstract: Eliminate term if-then-else's using cofactors. Author: Leonardo de Moura (leonardo) 2011-06-05. Revision History: --*/ #include"cofactor_elim_term_ite.h" #include"mk_simplified_app.h" #include"rewriter_def.h" #include"cooperate.h" #include"for_each_expr.h" #include"ast_smt2_pp.h" #include"ast_ll_pp.h" #include"tactic.h" struct cofactor_elim_term_ite::imp { ast_manager & m; params_ref m_params; unsigned long long m_max_memory; bool m_cofactor_equalities; volatile bool m_cancel; void checkpoint() { cooperate("cofactor ite"); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); } // Collect atoms that contain term if-then-else struct analyzer { struct frame { expr * m_t; unsigned m_first:1; unsigned m_form_ctx:1; frame(expr * t, bool form_ctx):m_t(t), m_first(true), m_form_ctx(form_ctx) {} }; ast_manager & m; imp & m_owner; obj_hashtable m_candidates; expr_fast_mark1 m_processed; expr_fast_mark2 m_has_term_ite; svector m_frame_stack; analyzer(ast_manager & _m, imp & owner):m(_m), m_owner(owner) {} void push_frame(expr * t, bool form_ctx) { m_frame_stack.push_back(frame(t, form_ctx && m.is_bool(t))); } void visit(expr * t, bool form_ctx, bool & visited) { if (!m_processed.is_marked(t)) { visited = false; push_frame(t, form_ctx); } } void save_candidate(expr * t, bool form_ctx) { if (!form_ctx) return; if (!m.is_bool(t)) return; if (!m_has_term_ite.is_marked(t)) return; if (!is_app(t)) return; if (to_app(t)->get_family_id() == m.get_basic_family_id()) { switch (to_app(t)->get_decl_kind()) { case OP_OR: case OP_AND: case OP_NOT: case OP_XOR: case OP_IMPLIES: case OP_TRUE: case OP_FALSE: case OP_ITE: case OP_IFF: return; case OP_EQ: case OP_DISTINCT: if (m.is_bool(to_app(t)->get_arg(0))) return; break; default: break; } } // it is an atom in a formula context (i.e., it is not nested inside a term), // and it contains a term if-then-else. m_candidates.insert(t); } void operator()(expr * t) { SASSERT(m.is_bool(t)); push_frame(t, true); SASSERT(!m_frame_stack.empty()); while (!m_frame_stack.empty()) { frame & fr = m_frame_stack.back(); expr * t = fr.m_t; bool form_ctx = fr.m_form_ctx; TRACE("cofactor", tout << "processing, form_ctx: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";); m_owner.checkpoint(); if (m_processed.is_marked(t)) { save_candidate(t, form_ctx); m_frame_stack.pop_back(); continue; } if (m.is_term_ite(t)) { m_has_term_ite.mark(t); m_processed.mark(t); m_frame_stack.pop_back(); continue; } if (fr.m_first) { fr.m_first = false; bool visited = true; if (is_app(t)) { unsigned num_args = to_app(t)->get_num_args(); for (unsigned i = 0; i < num_args; i++) visit(to_app(t)->get_arg(i), form_ctx, visited); } // ignoring quantifiers if (!visited) continue; } if (is_app(t)) { unsigned num_args = to_app(t)->get_num_args(); unsigned i; for (i = 0; i < num_args; i++) { if (m_has_term_ite.is_marked(to_app(t)->get_arg(i))) break; } if (i < num_args) { m_has_term_ite.mark(t); TRACE("cofactor", tout << "saving candidate: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";); save_candidate(t, form_ctx); } } else { SASSERT(is_quantifier(t) || is_var(t)); // ignoring quantifiers... they are treated as black boxes. } m_processed.mark(t); m_frame_stack.pop_back(); } m_processed.reset(); m_has_term_ite.reset(); } }; expr * get_first(expr * t) { TRACE("cofactor", tout << mk_ismt2_pp(t, m) << "\n";); typedef std::pair frame; expr_fast_mark1 visited; sbuffer stack; stack.push_back(frame(t, 0)); while (!stack.empty()) { start: checkpoint(); frame & fr = stack.back(); expr * curr = fr.first; if (m.is_term_ite(curr)) return to_app(curr)->get_arg(0); switch (curr->get_kind()) { case AST_VAR: case AST_QUANTIFIER: // ignore quantifiers stack.pop_back(); break; case AST_APP: { unsigned num_args = to_app(curr)->get_num_args(); while (fr.second < num_args) { expr * arg = to_app(curr)->get_arg(fr.second); fr.second++; if (arg->get_ref_count() > 1) { if (visited.is_marked(arg)) continue; visited.mark(arg); } switch (arg->get_kind()) { case AST_VAR: case AST_QUANTIFIER: // ingore quantifiers break; case AST_APP: if (to_app(arg)->get_num_args() > 0) { stack.push_back(frame(arg, 0)); goto start; } break; default: UNREACHABLE(); break; } } stack.pop_back(); break; } default: UNREACHABLE(); break; } } return 0; } /** \brief Fuctor for selecting the term if-then-else condition with the most number of occurrences. */ expr * get_best(expr * t) { TRACE("cofactor", tout << mk_ismt2_pp(t, m) << "\n";); typedef std::pair frame; obj_map occs; expr_fast_mark1 visited; sbuffer stack; stack.push_back(frame(t, 0)); while (!stack.empty()) { start: checkpoint(); frame & fr = stack.back(); expr * curr = fr.first; switch (curr->get_kind()) { case AST_VAR: case AST_QUANTIFIER: // ignore quantifiers stack.pop_back(); break; case AST_APP: { unsigned num_args = to_app(curr)->get_num_args(); bool is_term_ite = m.is_term_ite(curr); while (fr.second < num_args) { expr * arg = to_app(curr)->get_arg(fr.second); if (fr.second == 0 && is_term_ite) { unsigned num = 0; if (occs.find(arg, num)) occs.insert(arg, num+1); else occs.insert(arg, 1); } fr.second++; if (arg->get_ref_count() > 1) { if (visited.is_marked(arg)) continue; visited.mark(arg); } switch (arg->get_kind()) { case AST_VAR: case AST_QUANTIFIER: // ingore quantifiers break; case AST_APP: if (to_app(arg)->get_num_args() > 0) { stack.push_back(frame(arg, 0)); goto start; } break; default: UNREACHABLE(); break; } } stack.pop_back(); break; } default: UNREACHABLE(); break; } } expr * best = 0; unsigned best_occs = 0; obj_map::iterator it = occs.begin(); obj_map::iterator end = occs.end(); for (; it != end; ++it) { if ((!best) || (get_depth(it->m_key) < get_depth(best)) || (get_depth(it->m_key) == get_depth(best) && it->m_value > best_occs) || // break ties by giving preference to equalities (get_depth(it->m_key) == get_depth(best) && it->m_value == best_occs && m.is_eq(it->m_key) && !m.is_eq(best))) { best = it->m_key; best_occs = it->m_value; } } visited.reset(); CTRACE("cofactor", best != 0, tout << "best num-occs: " << best_occs << "\n" << mk_ismt2_pp(best, m) << "\n";); return best; } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_cofactor_equalities = p.get_bool("cofactor_equalities", true); } void collect_param_descrs(param_descrs & r) { r.insert("cofactor_equalities", CPK_BOOL, "(default: true) use equalities to rewrite bodies of ite-expressions. This is potentially expensive."); } void set_cancel(bool f) { m_cancel = f; } struct cofactor_rw_cfg : public default_rewriter_cfg { ast_manager & m; imp & m_owner; obj_hashtable * m_has_term_ite; mk_simplified_app m_mk_app; expr * m_atom; bool m_sign; expr * m_term; app * m_value; bool m_strict_lower; app * m_lower; bool m_strict_upper; app * m_upper; cofactor_rw_cfg(ast_manager & _m, imp & owner, obj_hashtable * has_term_ite = 0): m(_m), m_owner(owner), m_has_term_ite(has_term_ite), m_mk_app(m, owner.m_params) { } bool max_steps_exceeded(unsigned num_steps) const { m_owner.checkpoint(); return false; } bool pre_visit(expr * t) { return true; } void set_cofactor_atom(expr * t) { if (m.is_not(t)) { m_atom = to_app(t)->get_arg(0); m_sign = true; m_term = 0; // TODO: bounds } else { m_atom = t; m_sign = false; m_term = 0; expr * lhs; expr * rhs; if (m_owner.m_cofactor_equalities && m.is_eq(t, lhs, rhs)) { if (m.is_unique_value(lhs)) { m_term = rhs; m_value = to_app(lhs); TRACE("cofactor", tout << "term:\n" << mk_ismt2_pp(m_term, m) << "\nvalue: " << mk_ismt2_pp(m_value, m) << "\n";); } else if (m.is_unique_value(rhs)) { m_term = lhs; m_value = to_app(rhs); TRACE("cofactor", tout << "term:\n" << mk_ismt2_pp(m_term, m) << "\nvalue: " << mk_ismt2_pp(m_value, m) << "\n";); } } // TODO: bounds } } bool rewrite_patterns() const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = 0; return m_mk_app.mk_core(f, num, args, result); } bool get_subst(expr * s, expr * & t, proof * & pr) { pr = 0; if (s == m_atom) { t = m_sign ? m.mk_false() : m.mk_true(); return true; } if (s == m_term && m_value != 0) { t = m_value; return true; } // TODO: handle simple bounds // Example: s is of the form (<= s 10) and m_term == s, and m_upper is 9 // then rewrite to true. return false; } }; struct cofactor_rw : rewriter_tpl { cofactor_rw_cfg m_cfg; public: cofactor_rw(ast_manager & m, imp & owner, obj_hashtable * has_term_ite = 0): rewriter_tpl(m, false, m_cfg), m_cfg(m, owner, has_term_ite) { } void set_cofactor_atom(expr * t) { m_cfg.set_cofactor_atom(t); reset(); } }; struct main_rw_cfg : public default_rewriter_cfg { ast_manager & m; imp & m_owner; cofactor_rw m_cofactor; obj_hashtable const & m_candidates; obj_map m_cache; expr_ref_vector m_cache_domain; main_rw_cfg(ast_manager & _m, imp & owner, obj_hashtable & candidates): m(_m), m_owner(owner), m_cofactor(m, m_owner), m_candidates(candidates), m_cache_domain(_m) { } bool max_steps_exceeded(unsigned num_steps) const { m_owner.checkpoint(); return false; } bool pre_visit(expr * t) { return m.is_bool(t) && !is_quantifier(t); } bool get_subst(expr * s, expr * & t, proof * & t_pr) { if (m_candidates.contains(s)) { t_pr = 0; if (m_cache.find(s, t)) return true; unsigned step = 0; TRACE("cofactor_ite", tout << "cofactor target:\n" << mk_ismt2_pp(s, m) << "\n";); expr_ref curr(m); curr = s; while (true) { // expr * c = m_owner.get_best(curr); expr * c = m_owner.get_first(curr); if (c == 0) { m_cache.insert(s, curr); m_cache_domain.push_back(curr); t = curr.get(); return true; } step++; expr_ref pos_cofactor(m); expr_ref neg_cofactor(m); m_cofactor.set_cofactor_atom(c); m_cofactor(curr, pos_cofactor); expr_ref neg_c(m); neg_c = m.is_not(c) ? to_app(c)->get_arg(0) : m.mk_not(c); m_cofactor.set_cofactor_atom(neg_c); m_cofactor(curr, neg_cofactor); curr = m.mk_ite(c, pos_cofactor, neg_cofactor); TRACE("cofactor", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";); } } return false; } }; struct main_rw : rewriter_tpl { main_rw_cfg m_cfg; public: main_rw(ast_manager & m, imp & owner, obj_hashtable & candidates): rewriter_tpl(m, false, m_cfg), m_cfg(m, owner, candidates) { } }; struct bottom_up_elim { typedef std::pair frame; ast_manager & m; imp & m_owner; obj_map m_cache; expr_ref_vector m_cache_domain; obj_hashtable m_has_term_ite; svector m_frames; cofactor_rw m_cofactor; bottom_up_elim(ast_manager & _m, imp & owner): m(_m), m_owner(owner), m_cache_domain(m), m_cofactor(m, owner, &m_has_term_ite) { } bool is_atom(expr * t) const { if (!m.is_bool(t)) return false; if (!is_app(t)) return false; if (to_app(t)->get_family_id() == m.get_basic_family_id()) { switch (to_app(t)->get_decl_kind()) { case OP_EQ: case OP_DISTINCT: if (m.is_bool(to_app(t)->get_arg(0))) return false; else return true; default: return false; } } return true; } void cofactor(expr * t, expr_ref & r) { unsigned step = 0; TRACE("cofactor", tout << "cofactor target:\n" << mk_ismt2_pp(t, m) << "\n";); expr_ref curr(m); curr = t; while (true) { expr * c = m_owner.get_best(curr); // expr * c = m_owner.get_first(curr); if (c == 0) { r = curr.get(); return; } step++; expr_ref pos_cofactor(m); expr_ref neg_cofactor(m); m_cofactor.set_cofactor_atom(c); m_cofactor(curr, pos_cofactor); expr_ref neg_c(m); neg_c = m.is_not(c) ? to_app(c)->get_arg(0) : m.mk_not(c); m_cofactor.set_cofactor_atom(neg_c); m_cofactor(curr, neg_cofactor); if (pos_cofactor == neg_cofactor) { curr = pos_cofactor; } else if (m.is_true(pos_cofactor) && m.is_false(neg_cofactor)) { curr = c; } else if (m.is_false(pos_cofactor) && m.is_true(neg_cofactor)) { curr = neg_c; } else { curr = m.mk_ite(c, pos_cofactor, neg_cofactor); } TRACE("cofactor", tout << "cofactor_ite step: " << step << "\n"; tout << "cofactor: " << mk_ismt2_pp(c, m) << "\n"; tout << mk_ismt2_pp(curr, m) << "\n";); } } void visit(expr * t, bool & visited) { if (!m_cache.contains(t)) { m_frames.push_back(frame(t, true)); visited = false; } } void operator()(expr * t, expr_ref & r) { ptr_vector new_args; SASSERT(m_frames.empty()); m_frames.push_back(frame(t, true)); while (!m_frames.empty()) { m_owner.checkpoint(); frame & fr = m_frames.back(); expr * t = fr.first; TRACE("cofactor_bug", tout << "processing: " << t->get_id() << " :first " << fr.second << "\n";); if (!is_app(t)) { m_cache.insert(t, t); m_frames.pop_back(); continue; } if (m_cache.contains(t)) { m_frames.pop_back(); continue; } if (fr.second) { fr.second = false; bool visited = true; unsigned i = to_app(t)->get_num_args(); while (i > 0) { --i; expr * arg = to_app(t)->get_arg(i); visit(arg, visited); } if (!visited) continue; } new_args.reset(); bool has_new_args = false; bool has_term_ite = false; unsigned num = to_app(t)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = to_app(t)->get_arg(i); expr * new_arg = 0; TRACE("cofactor_bug", tout << "collecting child: " << arg->get_id() << "\n";); m_cache.find(arg, new_arg); SASSERT(new_arg != 0); if (new_arg != arg) has_new_args = true; if (m_has_term_ite.contains(new_arg)) has_term_ite = true; new_args.push_back(new_arg); } if (m.is_term_ite(t)) has_term_ite = true; expr_ref new_t(m); if (has_new_args) new_t = m.mk_app(to_app(t)->get_decl(), num, new_args.c_ptr()); else new_t = t; if (has_term_ite && is_atom(new_t)) { // update new_t expr_ref new_new_t(m); m_has_term_ite.insert(new_t); cofactor(new_t, new_new_t); m_has_term_ite.erase(new_t); new_t = new_new_t; has_term_ite = false; } if (has_term_ite) m_has_term_ite.insert(new_t); SASSERT(new_t.get() != 0); TRACE("cofactor_bug", tout << "caching: " << t->get_id() << "\n";); #if 0 counter ++; verbose_stream() << counter << "\n"; #endif m_cache.insert(t, new_t); m_cache_domain.push_back(new_t); m_frames.pop_back(); } expr * result = 0; m_cache.find(t, result); r = result; } }; imp(ast_manager & _m, params_ref const & p): m(_m), m_params(p), m_cofactor_equalities(true) { m_cancel = false; updt_params(p); } void operator()(expr * t, expr_ref & r) { #if 0 analyzer proc(m, *this); proc(t); main_rw rw(m, *this, proc.m_candidates); rw(t, r); #else bottom_up_elim proc(m, *this); proc(t, r); #endif } }; cofactor_elim_term_ite::cofactor_elim_term_ite(ast_manager & m, params_ref const & p): m_imp(alloc(imp, m, p)), m_params(p) { } cofactor_elim_term_ite::~cofactor_elim_term_ite() { dealloc(m_imp); } void cofactor_elim_term_ite::updt_params(params_ref const & p) { m_imp->updt_params(p); } void cofactor_elim_term_ite::collect_param_descrs(param_descrs & r) { m_imp->collect_param_descrs(r); } void cofactor_elim_term_ite::operator()(expr * t, expr_ref & r) { m_imp->operator()(t, r); } void cofactor_elim_term_ite::set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } void cofactor_elim_term_ite::cleanup() { ast_manager & m = m_imp->m; imp * d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } z3-4.4.0/src/tactic/core/elim_uncnstr_tactic.h0000644000175000017500000000106512540347414021037 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: elim_uncnstr_tactic.h Abstract: Eliminated applications containing unconstrained variables. Author: Leonardo (leonardo) 2011-10-22 Notes: --*/ #ifndef _ELIM_UNCNSTR_TACTIC_H_ #define _ELIM_UNCNSTR_TACTIC_H_ #include"params.h" class tactic; class ast_manager; tactic * mk_elim_uncnstr_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("elim-uncnstr", "eliminate application containing unconstrained variables.", "mk_elim_uncnstr_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/core/symmetry_reduce_tactic.cpp0000644000175000017500000005227712540347414022123 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: symmetry_reduce.cpp Abstract: Add symmetry breaking predicates to goals. Author: Nikolaj (nbjorner) 2011-05-31 Notes: This is a straight-forward and literal adaption of the algorithms proposed for veriT. --*/ #include"tactical.h" #include"for_each_expr.h" #include"map.h" #include"expr_replacer.h" #include"rewriter_def.h" #include"ast_pp.h" class symmetry_reduce_tactic : public tactic { class imp; imp * m_imp; public: symmetry_reduce_tactic(ast_manager & m); virtual tactic * translate(ast_manager & m) { return alloc(symmetry_reduce_tactic, m); } virtual ~symmetry_reduce_tactic(); virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core); virtual void cleanup(); }; class ac_rewriter { ast_manager& m_manager; public: ac_rewriter(ast_manager& m): m_manager(m) {} br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if ((f->is_associative() && f->is_commutative()) || m_manager.is_distinct(f)) { ptr_buffer buffer; buffer.append(num_args, args); std::sort(buffer.begin(), buffer.end(), ast_lt_proc()); bool change = false; for (unsigned i = 0; !change && i < num_args; ++i) { change = (args[i] != buffer[i]); } if (change) { result = m().mk_app(f, num_args, buffer.begin()); return BR_DONE; } } else if (f->is_commutative() && num_args == 2 && args[0]->get_id() > args[1]->get_id()) { expr* args2[2] = { args[1], args[0] }; result = m().mk_app(f, num_args, args2); return BR_DONE; } return BR_FAILED; } void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_app_core(f, num_args, args, result) == BR_FAILED) result = m().mk_app(f, num_args, args); } private: ast_manager& m() const { return m_manager; } }; struct ac_rewriter_cfg : public default_rewriter_cfg { ac_rewriter m_r; bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = 0; return m_r.mk_app_core(f, num, args, result); } ac_rewriter_cfg(ast_manager & m):m_r(m) {} }; class ac_rewriter_star : public rewriter_tpl { ac_rewriter_cfg m_cfg; public: ac_rewriter_star(ast_manager & m): rewriter_tpl(m, false, m_cfg), m_cfg(m) {} }; template class rewriter_tpl; class symmetry_reduce_tactic::imp { typedef ptr_vector permutation; typedef vector permutations; typedef ptr_vector term_set; typedef obj_map app_map; typedef u_map > inv_app_map; ast_manager& m_manager; ac_rewriter_star m_rewriter; scoped_ptr m_replace; ast_manager& m() const { return m_manager; } public: imp(ast_manager& m) : m_manager(m), m_rewriter(m) { m_replace = mk_default_expr_replacer(m); } ~imp() {} void operator()(goal & g) { if (g.inconsistent()) return; tactic_report report("symmetry-reduce", g); vector > P; expr_ref fml(m()); to_formula(g, fml); app_map occs; compute_occurrences(fml, occs); find_candidate_permutations(fml, occs, P); if (P.empty()) { return; } term_set T, cts; unsigned num_sym_break_preds = 0; for (unsigned i = 0; i < P.size(); ++i) { term_set& consts = P[i]; if (invariant_by_permutations(fml, consts)) { cts.reset(); select_terms(fml, consts, T); while (!T.empty() && cts.size() < consts.size()) { app* t = select_most_promising_term(fml, T, cts, consts, occs); T.erase(t); compute_used_in(t, cts, consts); app* c = select_const(consts, cts); if (!c) break; cts.push_back(c); expr* mem = mk_member(t, cts); g.assert_expr(mem); num_sym_break_preds++; TRACE("symmetry_reduce", tout << "member predicate: " << mk_pp(mem, m()) << "\n";); fml = m().mk_and(fml.get(), mem); normalize(fml); } } } report_tactic_progress(":num-symmetry-breaking ", num_sym_break_preds); } private: void to_formula(goal const & g, expr_ref& fml) { ptr_vector conjs; for (unsigned i = 0; i < g.size(); ++i) { conjs.push_back(g.form(i)); } fml = m().mk_and(conjs.size(), conjs.c_ptr()); normalize(fml); } // find candidate permutations void find_candidate_permutations(expr* fml, app_map const& occs, permutations& P) { app_map coloring; app_map depth; inv_app_map inv_color; unsigned num_occs; compute_sort_colors(fml, coloring); compute_max_depth(fml, depth); merge_colors(occs, coloring); merge_colors(depth, coloring); // compute_siblings(fml, coloring); compute_inv_app(coloring, inv_color); inv_app_map::iterator it = inv_color.begin(), end = inv_color.end(); for (; it != end; ++it) { if (it->m_value.size() < 2) { continue; } VERIFY(occs.find(it->m_value[0], num_occs)); if (num_occs < 2) { continue; } bool is_const = true; for (unsigned j = 0; is_const && j < it->m_value.size(); ++j) { is_const = it->m_value[j]->get_num_args() == 0; } if (!is_const) { continue; } P.push_back(it->m_value); TRACE("symmetry_reduce", for (unsigned i = 0; i < it->m_value.size(); ++i) { tout << mk_pp(it->m_value[i], m()) << " "; } tout << "\n";); } } // // refine coloring by taking most specific generalization. // a |-> c1, b |-> c2 |-> c // struct u_pair { unsigned m_first; unsigned m_second; u_pair(unsigned f, unsigned s) : m_first(f), m_second(s) {} u_pair(): m_first(0), m_second(0) {} struct hash { unsigned operator()(u_pair const& p) const { return mk_mix(p.m_first, p.m_second, 23); } }; struct eq { bool operator()(u_pair const& p, u_pair const& q) const { return p.m_first == q.m_first && p.m_second == q.m_second; } }; }; typedef map pair_map; bool merge_colors(app_map const& colors1, app_map& colors2) { pair_map recolor; unsigned num_colors = 0, v1, v2, w, old_max = 0; app_map::iterator it = colors2.begin(), end = colors2.end(); for (; it != end; ++it) { app* a = it->m_key; v1 = it->m_value; VERIFY(colors1.find(a, v2)); if (recolor.find(u_pair(v1, v2), w)) { it->m_value = w; } else { it->m_value = num_colors; recolor.insert(u_pair(v1, v2), num_colors++); } if (v1 > old_max) old_max = v1; } return num_colors > old_max + 1; } class sort_colors { ast_manager& m_manager; app_map& m_app2sortid; obj_map m_sort2id; unsigned m_max_id; public: sort_colors(ast_manager& m, app_map& app2sort): m_manager(m), m_app2sortid(app2sort), m_max_id(0) {} void operator()(app* n) { sort* s = m_manager.get_sort(n); unsigned id; if (!m_sort2id.find(s, id)) { id = m_max_id++; m_sort2id.insert(s, id); } m_app2sortid.insert(n, id); } void operator()(quantifier * n) {} void operator()(var * n) {} }; void compute_sort_colors(expr* fml, app_map& app2sortId) { app2sortId.reset(); sort_colors sc(m(), app2sortId); for_each_expr(sc, fml); } void compute_inv_app(app_map const& map, inv_app_map& inv_map) { app_map::iterator it = map.begin(), end = map.end(); for (; it != end; ++it) { app* t = it->m_key; unsigned n = it->m_value; if (is_uninterpreted(t)) { inv_app_map::entry* e = inv_map.insert_if_not_there2(n, ptr_vector()); e->get_data().m_value.push_back(t); } } } bool is_uninterpreted(app* t) const { return t->get_family_id() == null_family_id; } // compute maximal depth of terms. void compute_max_depth(expr* e, app_map& depth) { ptr_vector todo; unsigned_vector depths; unsigned d, d1; todo.push_back(e); depths.push_back(0); while (!todo.empty()) { e = todo.back(); d = depths.back(); todo.pop_back(); depths.pop_back(); if (is_var(e)) { // nothing } else if (is_quantifier(e)) { todo.push_back(to_quantifier(e)->get_expr()); depths.push_back(d+1); } else if (is_app(e)) { app* a = to_app(e); if (depth.find(a, d1) && d <= d1) { continue; } depth.insert(a, d); ++d; for (unsigned i = 0; i < a->get_num_args(); ++i) { todo.push_back(a->get_arg(i)); depths.push_back(d); } } else { UNREACHABLE(); } } } // color nodes according to the function symbols they appear in typedef obj_hashtable fun_set; typedef obj_map app_parents; class parents { app_parents m_use_funs; public: parents() {} app_parents const& get_parents() { return m_use_funs; } void operator()(app* n) { func_decl* f; unsigned sz = n->get_num_args(); for (unsigned i = 0; i < sz; ++i) { expr* e = n->get_arg(i); if (is_app(e)) { app_parents::obj_map_entry* entry = m_use_funs.insert_if_not_there2(to_app(e), 0); if (!entry->get_data().m_value) entry->get_data().m_value = alloc(fun_set); entry->get_data().m_value->insert(f); } } } void operator()(quantifier *n) {} void operator()(var* n) {} }; void compute_parents(expr* e, app_map& parents) { } typedef hashtable uint_set; typedef obj_map app_siblings;; class siblings { app_map const& m_colors; app_siblings m_sibs; public: siblings(app_map const& colors): m_colors(colors) {} app_siblings const& get() { return m_sibs; } void operator()(app* n) { unsigned sz = n->get_num_args(); for (unsigned i = 0; i < sz; ++i) { expr* e = n->get_arg(i); if (!is_app(e)) continue; app_siblings::obj_map_entry* entry = m_sibs.insert_if_not_there2(to_app(e), 0); if (!entry->get_data().get_value()) entry->get_data().m_value = alloc(uint_set); for (unsigned j = 0; j < sz; ++j) { expr* f = n->get_arg(j); if (is_app(f) && i != j) { unsigned c1 = 0; m_colors.find(to_app(f), c1); entry->get_data().m_value->insert(c1); } } } } void operator()(quantifier *n) {} void operator()(var* n) {} }; // refine coloring by taking colors of siblings into account. bool compute_siblings_rec(expr* e, app_map& colors) { siblings sibs(colors); app_map colors1; for_each_expr(sibs, e); app_siblings const& s = sibs.get(); app_siblings::iterator it = s.begin(), end = s.end(); for (; it != end; ++it) { app* a = it->m_key; uint_set* set = it->m_value; uint_set::iterator it2 = set->begin(), end2 = set->end(); unsigned c = 0; for(; it2 != end2; ++it2) { c += 1 + *it2; } colors1.insert(a, c); dealloc(set); } if (is_app(e)) { colors1.insert(to_app(e), 0); } return merge_colors(colors1, colors); } void compute_siblings(expr* fml, app_map& colors) { while(compute_siblings_rec(fml, colors)); } // check if assertion set is invariant under the current permutation bool invariant_by_permutations(expr* fml, permutation& p) { SASSERT(p.size() >= 2); bool result = check_swap(fml, p[0], p[1]) && check_cycle(fml, p); TRACE("symmetry_reduce", if (result) { tout << "Symmetric: "; } else { tout << "Not symmetric: "; } for (unsigned i = 0; i < p.size(); ++i) { tout << mk_pp(p[i], m()) << " "; } tout << "\n";); return result; } bool check_swap(expr* fml, app* t1, app* t2) { expr_substitution sub(m()); sub.insert(t1, t2); sub.insert(t2, t1); m_replace->set_substitution(&sub); return check_substitution(fml); } bool check_cycle(expr* fml, permutation& p) { expr_substitution sub(m()); for (unsigned i = 0; i + 1 < p.size(); ++i) { sub.insert(p[i], p[i+1]); } sub.insert(p[p.size()-1], p[0]); m_replace->set_substitution(&sub); return check_substitution(fml); } bool check_substitution(expr* t) { expr_ref r(m()); (*m_replace)(t, r); normalize(r); return t == r.get(); } void normalize(expr_ref& r) { proof_ref pr(m()); expr_ref result(m()); m_rewriter(r.get(), result, pr); r = result; } // select terms that are range restricted by set p. void select_terms(expr* fml, term_set const& p, term_set& T) { T.reset(); ptr_vector todo; todo.push_back(fml); app* t = 0; while (!todo.empty()) { fml = todo.back(); todo.pop_back(); if (m().is_and(fml)) { todo.append(to_app(fml)->get_num_args(), to_app(fml)->get_args()); } else if (is_range_restriction(fml, p, t)) { T.push_back(t); } } } bool is_range_restriction(expr* form, term_set const& C, app*& t) { if (!m().is_or(form)) return false; unsigned sz = to_app(form)->get_num_args(); t = 0; for (unsigned i = 0; i < sz; ++i) { expr* e = to_app(form)->get_arg(i); expr* e1, *e2; if (!m().is_eq(e, e1, e2)) return false; if (!is_app(e1) || !is_app(e2)) return false; app* a1 = to_app(e1), *a2 = to_app(e2); if (C.contains(a1) && (t == 0 || t == a2)) { t = a2; } else if (C.contains(a2) && (t == 0 || t == a1)) { t = a1; } else { return false; } } return t != 0; } // select the most promising term among T. // terms with the largest number of occurrences have higher weight. // terms that have fewest terms among C as subterms are preferred. class num_occurrences { app_map& m_occs; public: num_occurrences(app_map& occs): m_occs(occs) {} void operator()(app* n) { app_map::obj_map_entry* e; m_occs.insert_if_not_there2(n, 0); unsigned sz = n->get_num_args(); for (unsigned i = 0; i < sz; ++i) { expr* arg = n->get_arg(i); if (is_app(arg)) { e = m_occs.insert_if_not_there2(to_app(arg), 0); e->get_data().m_value++; } } } void operator()(quantifier * n) {} void operator()(var * n) {} }; void compute_occurrences(expr* fml, app_map& occs) { occs.reset(); num_occurrences num_occ(occs); for_each_expr(num_occ, fml); } app* select_most_promising_term( expr* fml, term_set const& T, term_set& cts, term_set const& consts, app_map const& occs) { SASSERT(!T.empty()); app* t = T[0]; unsigned weight, weight1; VERIFY(occs.find(t, weight)); unsigned cts_delta = compute_cts_delta(t, cts, consts); TRACE("symmetry_reduce", tout << mk_pp(t, m()) << " " << weight << " " << cts_delta << "\n";); for (unsigned i = 1; i < T.size(); ++i) { app* t1 = T[i]; VERIFY(occs.find(t1, weight1)); if (weight1 < weight && t->get_num_args() <= t1->get_num_args()) { continue; } unsigned cts_delta1 = compute_cts_delta(t1, cts, consts); TRACE("symmetry_reduce", tout << mk_pp(t1, m()) << " " << weight1 << " " << cts_delta1 << "\n";); if ((t->get_num_args() == t1->get_num_args() && (weight1 > weight || cts_delta1 < cts_delta)) || t->get_num_args() > t1->get_num_args()) { cts_delta = cts_delta1; weight = weight1; t = t1; } } return t; } // add to cts subterms of t that are members of consts. class member_of { term_set const& m_S; term_set& m_r; public: member_of(term_set const& S, term_set& r) : m_S(S), m_r(r) {} void operator()(app* n) { if (m_S.contains(n) && !m_r.contains(n)) { m_r.push_back(n); } } void operator()(quantifier * n) {} void operator()(var * n) {} }; void compute_used_in(app* t, term_set& cts, term_set const& consts) { member_of mem(consts, cts); for_each_expr(mem, t); TRACE("symmetry_reduce", tout << "Term: " << mk_pp(t, m()) << "\n"; tout << "Support set: "; for (unsigned i = 0; i < consts.size(); ++i) { tout << mk_pp(consts[i], m()) << " "; } tout << "\n"; tout << "Constants: "; for (unsigned i = 0; i < cts.size(); ++i) { tout << mk_pp(cts[i], m()) << " "; } tout << "\n"; ); } unsigned compute_cts_delta(app* t, term_set& cts, term_set const& consts) { unsigned cts_size = cts.size(); if (cts_size == consts.size()) { return 0; } compute_used_in(t, cts, consts); unsigned cts_delta = cts.size() - cts_size; cts.resize(cts_size); return cts_delta; } // select element in A not in B app* select_const(term_set const& A, term_set const& B) { unsigned j; for (j = 0; j < A.size() && B.contains(A[j]); ++j); return (j == A.size())?0:A[j]; } app* mk_member(app* t, term_set const& C) { expr_ref_vector eqs(m()); for (unsigned i = 0; i < C.size(); ++i) { eqs.push_back(m().mk_eq(t, C[i])); } return m().mk_or(eqs.size(), eqs.c_ptr()); } }; symmetry_reduce_tactic::symmetry_reduce_tactic(ast_manager & m) { m_imp = alloc(imp, m); } symmetry_reduce_tactic::~symmetry_reduce_tactic() { dealloc(m_imp); } void symmetry_reduce_tactic::operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { fail_if_proof_generation("symmetry_reduce", g); fail_if_unsat_core_generation("symmetry_reduce", g); mc = 0; pc = 0; core = 0; result.reset(); (*m_imp)(*(g.get())); g->inc_depth(); result.push_back(g.get()); } void symmetry_reduce_tactic::cleanup() { // no-op. } tactic * mk_symmetry_reduce_tactic(ast_manager & m, params_ref const & p) { return alloc(symmetry_reduce_tactic, m); } z3-4.4.0/src/tactic/core/pb_preprocess_tactic.h0000644000175000017500000000117112540347414021201 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: pb_preprocess_tactic.h Abstract: Pre-process pseudo-Boolean inequalities using generalized Davis Putnam (resolution) to eliminate variables. Author: Nikolaj Bjorner (nbjorner) 2013-12-23 Notes: --*/ #ifndef _PB_PREPROCESS_TACTIC_H_ #define _PB_PREPROCESS_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_pb_preprocess_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("pb-preprocess", "pre-process pseudo-Boolean constraints a la Davis Putnam.", "mk_pb_preprocess_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/core/symmetry_reduce_tactic.h0000644000175000017500000000100712540347414021551 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: symmetry_reduce.h Abstract: Add symmetry breaking predicates to assertion sets. Author: Nikolaj (nbjorner) 2011-05-31 Notes: --*/ #ifndef _SYMMETRY_REDUCE_TACTIC_H_ #define _SYMMETRY_REDUCE_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_symmetry_reduce_tactic(ast_manager & m, params_ref const & p); /* ADD_TACTIC("symmetry-reduce", "apply symmetry reduction.", "mk_symmetry_reduce_tactic(m, p)") */ #endif z3-4.4.0/src/tactic/core/reduce_args_tactic.cpp0000644000175000017500000005101312540347414021151 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: reduce_args_tactic.cpp Abstract: Reduce the number of arguments in function applications. Author: Leonardo (leonardo) 2012-02-19 Notes: --*/ #include"tactical.h" #include"cooperate.h" #include"ast_smt2_pp.h" #include"map.h" #include"rewriter_def.h" #include"extension_model_converter.h" #include"filter_model_converter.h" /** \brief Reduce the number of arguments in function applications. Example, suppose we have a function f with 2 arguments. There are 1000 applications of this function, but the first argument is always "a", "b" or "c". Thus, we replace the f(t1, t2) with f_a(t2) if t1 = a f_b(t2) if t2 = b f_c(t2) if t2 = c Since f_a, f_b, f_c are new symbols, satisfiability is preserved. This transformation is very similar in spirit to the Ackermman's reduction. This transformation should work in the following way: 1- Create a mapping decl2arg_map from declarations to tuples of booleans, an entry [f -> (true, false, true)] means that f is a declaration with 3 arguments where the first and third arguments are always values. 2- Traverse the formula and populate the mapping. For each function application f(t1, ..., tn) do a) Create a boolean tuple (is_value(t1), ..., is_value(tn)) and do the logical-and with the tuple that is already in the mapping. If there is no such tuple in the mapping, we just add a new entry. If all entries are false-tuples, then there is nothing to be done. The transformation is not applicable. Now, we create a mapping decl2new_decl from (decl, val_1, ..., val_n) to decls. Note that, n may be different for each entry, but it is the same for the same declaration. For example, suppose we have [f -> (true, false, true)] in decl2arg_map, and applications f(1, a, 2), f(1, b, 2), f(1, b, 3), f(2, b, 3), f(2, c, 3) in the formula. Then, decl2arg_map would contain (f, 1, 2) -> f_1_2 (f, 1, 3) -> f_1_3 (f, 2, 3) -> f_2_3 where f_1_2, f_1_3 and f_2_3 are new function symbols. Using the new map, we can replace the occurrences of f. */ class reduce_args_tactic : public tactic { struct imp; imp * m_imp; public: reduce_args_tactic(ast_manager & m); virtual tactic * translate(ast_manager & m) { return alloc(reduce_args_tactic, m); } virtual ~reduce_args_tactic(); virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core); virtual void cleanup(); virtual void set_cancel(bool f); }; tactic * mk_reduce_args_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(reduce_args_tactic, m)); } struct reduce_args_tactic::imp { ast_manager & m_manager; bool m_produce_models; volatile bool m_cancel; ast_manager & m() const { return m_manager; } imp(ast_manager & m): m_manager(m) { m_cancel = false; } void set_cancel(bool f) { m_cancel = f; } void checkpoint() { if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); cooperate("reduce-args"); } struct find_non_candidates_proc { ast_manager & m_manager; obj_hashtable & m_non_cadidates; find_non_candidates_proc(ast_manager & m, obj_hashtable & non_cadidates): m_manager(m), m_non_cadidates(non_cadidates) { } void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { if (n->get_num_args() == 0) return; // ignore constants func_decl * d = n->get_decl(); if (d->get_family_id() != null_family_id) return; // ignore interpreted symbols if (m_non_cadidates.contains(d)) return; // it is already in the set. unsigned j = n->get_num_args(); while (j > 0) { --j; if (m_manager.is_unique_value(n->get_arg(j))) return; } m_non_cadidates.insert(d); } }; /** \brief Populate the table non_cadidates with function declarations \c f such that there is a function application (f t1 ... tn) where t1 ... tn are not values. */ void find_non_candidates(goal const & g, obj_hashtable & non_candidates) { non_candidates.reset(); find_non_candidates_proc proc(m_manager, non_candidates); expr_fast_mark1 visited; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { checkpoint(); quick_for_each_expr(proc, visited, g.form(i)); } TRACE("reduce_args", tout << "non_candidates:\n"; obj_hashtable::iterator it = non_candidates.begin(); obj_hashtable::iterator end = non_candidates.end(); for (; it != end; ++it) { func_decl * d = *it; tout << d->get_name() << "\n"; }); } struct populate_decl2args_proc { ast_manager & m_manager; obj_hashtable & m_non_cadidates; obj_map & m_decl2args; populate_decl2args_proc(ast_manager & m, obj_hashtable & nc, obj_map & d): m_manager(m), m_non_cadidates(nc), m_decl2args(d) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { if (n->get_num_args() == 0) return; // ignore constants func_decl * d = n->get_decl(); if (d->get_family_id() != null_family_id) return; // ignore interpreted symbols if (m_non_cadidates.contains(d)) return; // declaration is not a candidate unsigned j = n->get_num_args(); obj_map::iterator it = m_decl2args.find_iterator(d); if (it == m_decl2args.end()) { m_decl2args.insert(d, bit_vector()); it = m_decl2args.find_iterator(d); SASSERT(it != m_decl2args.end()); it->m_value.reserve(j); while (j > 0) { --j; it->m_value.set(j, m_manager.is_unique_value(n->get_arg(j))); } } else { SASSERT(j == it->m_value.size()); while (j > 0) { --j; it->m_value.set(j, it->m_value.get(j) && m_manager.is_unique_value(n->get_arg(j))); } } } }; void populate_decl2args(goal const & g, obj_hashtable & non_candidates, obj_map & decl2args) { expr_fast_mark1 visited; decl2args.reset(); populate_decl2args_proc proc(m_manager, non_candidates, decl2args); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { checkpoint(); quick_for_each_expr(proc, visited, g.form(i)); } // Remove all cases where the simplification is not applicable. ptr_buffer bad_decls; obj_map::iterator it = decl2args.begin(); obj_map::iterator end = decl2args.end(); for (; it != end; it++) { bool is_zero = true; for (unsigned i = 0; i < it->m_value.size() && is_zero ; i++) { if (it->m_value.get(i)) is_zero = false; } if (is_zero) bad_decls.push_back(it->m_key); } ptr_buffer::iterator it2 = bad_decls.begin(); ptr_buffer::iterator end2 = bad_decls.end(); for (; it2 != end2; ++it2) decl2args.erase(*it2); TRACE("reduce_args", tout << "decl2args:" << std::endl; for (obj_map::iterator it = decl2args.begin() ; it != decl2args.end() ; it++) { tout << it->m_key->get_name() << ": "; for (unsigned i = 0 ; i < it->m_value.size() ; i++) tout << (it->m_value.get(i) ? "1" : "0"); tout << std::endl; }); } struct arg2func_hash_proc { bit_vector const & m_bv; arg2func_hash_proc(bit_vector const & bv):m_bv(bv) {} unsigned operator()(app const * n) const { // compute the hash-code using only the arguments where m_bv is true. unsigned a = 0x9e3779b9; unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { if (!m_bv.get(i)) continue; // ignore argument a = hash_u_u(a, n->get_arg(i)->get_id()); } return a; } }; struct arg2func_eq_proc { bit_vector const & m_bv; arg2func_eq_proc(bit_vector const & bv):m_bv(bv) {} bool operator()(app const * n1, app const * n2) const { // compare only the arguments where m_bv is true SASSERT(n1->get_num_args() == n2->get_num_args()); unsigned num_args = n1->get_num_args(); for (unsigned i = 0; i < num_args; i++) { if (!m_bv.get(i)) continue; // ignore argument if (n1->get_arg(i) != n2->get_arg(i)) return false; } return true; } }; typedef map arg2func; typedef obj_map decl2arg2func_map; struct reduce_args_ctx { ast_manager & m_manager; decl2arg2func_map m_decl2arg2funcs; reduce_args_ctx(ast_manager & m): m_manager(m) { } ~reduce_args_ctx() { obj_map::iterator it = m_decl2arg2funcs.begin(); obj_map::iterator end = m_decl2arg2funcs.end(); for (; it != end; ++it) { arg2func * map = it->m_value; arg2func::iterator it2 = map->begin(); arg2func::iterator end2 = map->end(); for (; it2 != end2; ++it2) { m_manager.dec_ref(it2->m_key); m_manager.dec_ref(it2->m_value); } dealloc(map); } } }; struct populate_decl2arg_set_proc { ast_manager & m_manager; obj_map & m_decl2args; decl2arg2func_map & m_decl2arg2funcs; populate_decl2arg_set_proc(ast_manager & m, obj_map & d, decl2arg2func_map & ds): m_manager(m), m_decl2args(d), m_decl2arg2funcs(ds) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { if (n->get_num_args() == 0) return; // ignore constants func_decl * d = n->get_decl(); if (d->get_family_id() != null_family_id) return; // ignore interpreted symbols obj_map::iterator it = m_decl2args.find_iterator(d); if (it == m_decl2args.end()) return; // not reducing the arguments of this declaration bit_vector & bv = it->m_value; arg2func * map = 0; decl2arg2func_map::iterator it2 = m_decl2arg2funcs.find_iterator(d); if (it2 == m_decl2arg2funcs.end()) { map = alloc(arg2func, arg2func_hash_proc(bv), arg2func_eq_proc(bv)); m_decl2arg2funcs.insert(d, map); } else { map = it2->m_value; } if (!map->contains(n)) { // create fresh symbol... ptr_buffer domain; unsigned arity = d->get_arity(); for (unsigned i = 0; i < arity; i++) { if (!bv.get(i)) domain.push_back(d->get_domain(i)); } func_decl * new_d = m_manager.mk_fresh_func_decl(d->get_name(), symbol::null, domain.size(), domain.c_ptr(), d->get_range()); map->insert(n, new_d); m_manager.inc_ref(n); m_manager.inc_ref(new_d); } } }; void populate_decl2arg_set(goal const & g, obj_map & decl2args, decl2arg2func_map & decl2arg2funcs) { expr_fast_mark1 visited; populate_decl2arg_set_proc proc(m_manager, decl2args, decl2arg2funcs); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { checkpoint(); quick_for_each_expr(proc, visited, g.form(i)); } } struct reduce_args_rw_cfg : public default_rewriter_cfg { ast_manager & m; imp & m_owner; obj_map & m_decl2args; decl2arg2func_map & m_decl2arg2funcs; reduce_args_rw_cfg(imp & owner, obj_map & decl2args, decl2arg2func_map & decl2arg2funcs): m(owner.m_manager), m_owner(owner), m_decl2args(decl2args), m_decl2arg2funcs(decl2arg2funcs) { } bool max_steps_exceeded(unsigned num_steps) const { m_owner.checkpoint(); return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = 0; if (f->get_arity() == 0) return BR_FAILED; // ignore constants if (f->get_family_id() != null_family_id) return BR_FAILED; // ignore interpreted symbols decl2arg2func_map::iterator it = m_decl2arg2funcs.find_iterator(f); if (it == m_decl2arg2funcs.end()) return BR_FAILED; SASSERT(m_decl2args.contains(f)); bit_vector & bv = m_decl2args.find(f); arg2func * map = it->m_value; app_ref tmp(m); tmp = m.mk_app(f, num, args); CTRACE("reduce_args", !map->contains(tmp), tout << "map does not contain tmp f: " << f->get_name() << "\n"; tout << mk_ismt2_pp(tmp, m) << "\n"; arg2func::iterator it = map->begin(); arg2func::iterator end = map->end(); for (; it != end; ++it) { tout << mk_ismt2_pp(it->m_key, m) << "\n---> " << it->m_value->get_name() << "\n"; }); SASSERT(map->contains(tmp)); func_decl * new_f = map->find(tmp); ptr_buffer new_args; for (unsigned i = 0; i < num; i++) { if (!bv.get(i)) new_args.push_back(args[i]); } result = m.mk_app(new_f, new_args.size(), new_args.c_ptr()); return BR_DONE; } }; struct reduce_args_rw : rewriter_tpl { reduce_args_rw_cfg m_cfg; public: reduce_args_rw(imp & owner, obj_map & decl2args, decl2arg2func_map & decl2arg2funcs): rewriter_tpl(owner.m_manager, false, m_cfg), m_cfg(owner, decl2args, decl2arg2funcs) { } }; model_converter * mk_mc(obj_map & decl2args, decl2arg2func_map & decl2arg2funcs) { ptr_buffer new_args; var_ref_vector new_vars(m_manager); ptr_buffer new_eqs; extension_model_converter * e_mc = alloc(extension_model_converter, m_manager); filter_model_converter * f_mc = alloc(filter_model_converter, m_manager); decl2arg2func_map::iterator it = decl2arg2funcs.begin(); decl2arg2func_map::iterator end = decl2arg2funcs.end(); for (; it != end; ++it) { func_decl * f = it->m_key; arg2func * map = it->m_value; expr * def = 0; SASSERT(decl2args.contains(f)); bit_vector & bv = decl2args.find(f); new_vars.reset(); new_args.reset(); for (unsigned i = 0; i < f->get_arity(); i++) { new_vars.push_back(m_manager.mk_var(i, f->get_domain(i))); if (!bv.get(i)) new_args.push_back(new_vars.back()); } arg2func::iterator it2 = map->begin(); arg2func::iterator end2 = map->end(); for (; it2 != end2; ++it2) { app * t = it2->m_key; func_decl * new_def = it2->m_value; f_mc->insert(new_def); SASSERT(new_def->get_arity() == new_args.size()); app * new_t = m_manager.mk_app(new_def, new_args.size(), new_args.c_ptr()); if (def == 0) { def = new_t; } else { new_eqs.reset(); for (unsigned i = 0; i < f->get_arity(); i++) { if (bv.get(i)) new_eqs.push_back(m_manager.mk_eq(new_vars.get(i), t->get_arg(i))); } SASSERT(new_eqs.size() > 0); expr * cond; if (new_eqs.size() == 1) cond = new_eqs[0]; else cond = m_manager.mk_and(new_eqs.size(), new_eqs.c_ptr()); def = m_manager.mk_ite(cond, new_t, def); } } SASSERT(def); e_mc->insert(f, def); } return concat(f_mc, e_mc); } void operator()(goal & g, model_converter_ref & mc) { if (g.inconsistent()) return; m_produce_models = g.models_enabled(); TRACE("reduce_args", g.display(tout);); tactic_report report("reduce-args", g); obj_hashtable non_candidates; obj_map decl2args; find_non_candidates(g, non_candidates); populate_decl2args(g, non_candidates, decl2args); if (decl2args.empty()) return; ptr_vector arg2funcs; reduce_args_ctx ctx(m_manager); populate_decl2arg_set(g, decl2args, ctx.m_decl2arg2funcs); reduce_args_rw rw(*this, decl2args, ctx.m_decl2arg2funcs); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { if (g.inconsistent()) break; expr * f = g.form(i); expr_ref new_f(m_manager); rw(f, new_f); g.update(i, new_f); } report_tactic_progress(":reduced-funcs", decl2args.size()); if (m_produce_models) mc = mk_mc(decl2args, ctx.m_decl2arg2funcs); TRACE("reduce_args", g.display(tout); if (mc) mc->display(tout);); } }; reduce_args_tactic::reduce_args_tactic(ast_manager & m) { m_imp = alloc(imp, m); } reduce_args_tactic::~reduce_args_tactic() { dealloc(m_imp); } void reduce_args_tactic::operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("reduce-args", g); fail_if_unsat_core_generation("reduce-args", g); mc = 0; pc = 0; core = 0; result.reset(); m_imp->operator()(*(g.get()), mc); g->inc_depth(); result.push_back(g.get()); SASSERT(g->is_well_sorted()); } void reduce_args_tactic::set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } void reduce_args_tactic::cleanup() { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m); #pragma omp critical (tactic_cancel) { std::swap(d, m_imp); } dealloc(d); } z3-4.4.0/src/tactic/proof_converter.cpp0000644000175000017500000001134412540347414017626 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: proof_converter.cpp Abstract: Abstract interface for converting proofs, and basic combinators Author: Leonardo (leonardo) 2011-11-14 Notes: --*/ #include"proof_converter.h" #include"ast_smt2_pp.h" class concat_proof_converter : public concat_converter { public: concat_proof_converter(proof_converter * pc1, proof_converter * pc2):concat_converter(pc1, pc2) {} virtual char const * get_name() const { return "concat-proof-converter"; } virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) { proof_ref tmp(m); this->m_c2->operator()(m, num_source, source, tmp); proof * new_source = tmp.get(); this->m_c1->operator()(m, 1, &new_source, result); } virtual proof_converter * translate(ast_translation & translator) { return this->translate_core(translator); } }; proof_converter * concat(proof_converter * pc1, proof_converter * pc2) { if (pc1 == 0) return pc2; if (pc2 == 0) return pc1; return alloc(concat_proof_converter, pc1, pc2); } class concat_star_proof_converter : public concat_star_converter { public: concat_star_proof_converter(proof_converter * pc1, unsigned num, proof_converter * const * pc2s, unsigned * szs): concat_star_converter(pc1, num, pc2s, szs) { } virtual char const * get_name() const { return "concat-star-proof-converter"; } virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) { unsigned num = this->m_szs.size(); #ifdef Z3DEBUG unsigned sum = 0; for (unsigned i = 0; i < num; i++) { sum += this->m_szs[i]; } SASSERT(sum == num_source); #endif proof_ref_buffer tmp_prs(m); for (unsigned i = 0; i < num; i++) { unsigned sz = m_szs[i]; proof_converter * c2 = m_c2s[i]; proof_ref pr(m); if (c2) { (*c2)(m, sz, source, pr); } else { SASSERT(sz == 1); pr = *source; } source += sz; tmp_prs.push_back(pr.get()); } if (m_c1) { (*m_c1)(m, tmp_prs.size(), tmp_prs.c_ptr(), result); } else { SASSERT(tmp_prs.size() == 1); result = tmp_prs[0]; } } virtual proof_converter * translate(ast_translation & translator) { return this->translate_core(translator); } }; proof_converter * concat(proof_converter * pc1, unsigned num, proof_converter * const * pc2s, unsigned * szs) { SASSERT(num > 0); if (num == 1) return concat(pc1, pc2s[0]); unsigned i; for (i = 0; i < num; i++) { if (pc2s[i] != 0) break; } if (i == num) { // all pc2s are 0 return pc1; } return alloc(concat_star_proof_converter, pc1, num, pc2s, szs); } class proof2pc : public proof_converter { proof_ref m_pr; public: proof2pc(ast_manager & m, proof * pr):m_pr(pr, m) {} virtual ~proof2pc() {} virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) { SASSERT(num_source == 0); result = m_pr; } virtual proof_converter * translate(ast_translation & translator) { return alloc(proof2pc, translator.to(), translator(m_pr.get())); } virtual void display(std::ostream & out) { out << "(proof->proof-converter-wrapper\n" << mk_ismt2_pp(m_pr.get(), m_pr.get_manager()) << ")\n"; } }; proof_converter * proof2proof_converter(ast_manager & m, proof * pr) { if (pr == 0) return 0; return alloc(proof2pc, m, pr); } void apply(ast_manager & m, proof_converter * pc, proof_ref & pr) { if (pc) { proof * _pr = pr.get(); (*pc)(m, 1, &_pr, pr); } } /** Let pc2s be a buffer of proof converters that are wrappers for proofs. That is, they are functors of the form: unit -> Proof Then, this function applies pc1 to the proofs produced by pc2s's and store the resultant proof in result. pc1 and pc2s must be different from 0. */ void apply(ast_manager & m, proof_converter_ref & pc1, proof_converter_ref_buffer & pc2s, proof_ref & result) { SASSERT(pc1); proof_ref_buffer prs(m); unsigned sz = pc2s.size(); for (unsigned i = 0; i < sz; i++) { proof_ref pr(m); SASSERT(pc2s[i]); // proof production is enabled pc2s[i]->operator()(m, 0, 0, pr); prs.push_back(pr); } (*pc1)(m, sz, prs.c_ptr(), result); } z3-4.4.0/src/tactic/converter.h0000644000175000017500000000612312540347414016065 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: converter.h Abstract: Abstract class and templates for proof and model converters. Author: Leonardo (leonardo) 2011-11-14 Notes: --*/ #ifndef _CONVERTER_H_ #define _CONVERTER_H_ #include"vector.h" #include"ref.h" #include"ast_translation.h" class converter { unsigned m_ref_count; public: converter():m_ref_count(0) {} virtual ~converter() {} void inc_ref() { ++m_ref_count; } void dec_ref() { --m_ref_count; if (m_ref_count == 0) dealloc(this); } virtual void cancel() {} // for debugging purposes virtual void display(std::ostream & out) {} }; template class concat_converter : public T { protected: ref m_c1; ref m_c2; template T * translate_core(ast_translation & translator) { T * t1 = m_c1->translate(translator); T * t2 = m_c2->translate(translator); return alloc(T2, t1, t2); } public: concat_converter(T * c1, T * c2):m_c1(c1), m_c2(c2) {} virtual ~concat_converter() {} virtual void cancel() { m_c2->cancel(); m_c1->cancel(); } virtual char const * get_name() const = 0; virtual void display(std::ostream & out) { out << "(" << get_name() << "\n"; m_c1->display(out); m_c2->display(out); out << ")\n"; } }; template class concat_star_converter : public T { protected: ref m_c1; ptr_vector m_c2s; unsigned_vector m_szs; template T * translate_core(ast_translation & translator) { T * t1 = m_c1 ? m_c1->translate(translator) : 0; ptr_buffer t2s; unsigned num = m_c2s.size(); for (unsigned i = 0; i < num; i++) t2s.push_back(m_c2s[i] ? m_c2s[i]->translate(translator) : 0); return alloc(T2, t1, num, t2s.c_ptr(), m_szs.c_ptr()); } public: concat_star_converter(T * c1, unsigned num, T * const * c2s, unsigned * szs): m_c1(c1) { for (unsigned i = 0; i < num; i++) { T * c2 = c2s[i]; if (c2) c2->inc_ref(); m_c2s.push_back(c2); m_szs.push_back(szs[i]); } } virtual ~concat_star_converter() { unsigned sz = m_c2s.size(); for (unsigned i = 0; i < sz; i++) { T * c2 = m_c2s[i]; if (c2) c2->dec_ref(); } } virtual void cancel() { if (m_c1) m_c1->cancel(); unsigned num = m_c2s.size(); for (unsigned i = 0; i < num; i++) { if (m_c2s[i]) m_c2s[i]->cancel(); } } virtual char const * get_name() const = 0; virtual void display(std::ostream & out) { out << "(" << get_name() << "\n"; if (m_c1) m_c1->display(out); out << "(\n"; unsigned num = m_c2s.size(); for (unsigned i = 0; i < num; i++) if (m_c2s[i]) m_c2s[i]->display(out); out << "))\n"; } }; #endif z3-4.4.0/src/tactic/tactic.h0000644000175000017500000001370712540347414015333 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: tactic.h Abstract: Abstract tactic object. It used to be called assertion_set_strategy. The main improvement is the support for multiple subgoals. Author: Leonardo (leonardo) 2011-10-13 Notes: --*/ #ifndef _TACTIC_H_ #define _TACTIC_H_ #include"goal.h" #include"params.h" #include"statistics.h" #include"model_converter.h" #include"proof_converter.h" #include"tactic_exception.h" #include"lbool.h" class progress_callback; typedef ptr_buffer goal_buffer; class tactic { unsigned m_ref_count; public: tactic():m_ref_count(0) {} virtual ~tactic() {} void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) dealloc(this); } virtual void updt_params(params_ref const & p) {} virtual void collect_param_descrs(param_descrs & r) {} void cancel(); void reset_cancel(); /** \brief Apply tactic to goal \c in. The list of resultant subgoals is stored in \c result. The content of \c in may be destroyed during the operation. The resultant model converter \c mc can be used to convert a model for one of the returned subgoals into a model for \in. If mc == 0, then model construction is disabled or any model for a subgoal of \c in is also a model for \c in. If \c result is decided_sat (i.e., it contains a single empty subgoal), then the model converter is just wrapping the model. The resultant proof converter \c pc can be used to convert proofs for each subgoal in \c result into a proof for \c in. If pc == 0, then one of the following conditions should hold: 1- proof construction is disabled, 2- result contains a single subgoal, and any proof of unsatisfiability for this subgoal is a proof for \c in. 3- result is an decided_unsat (i.e., it contains a single unsat subgoal). The actual proof can be extracted from this goal. The output parameter \c core is used to accumulate the unsat core of closed subgoals. It must be 0 if dependency tracking is disabled, or the result is decided unsat, or no tagged assertions were used to close any subgoal. Note that, this signature is not compatible with the one described in the paper: "The Strategy Challenge in SMT Solving". The approach in the paper is conceptually simpler, but (for historical reasons) it would require a lot of re-engineering in the Z3 code. In Z3, we keep a proof/justification for every formula in a goal. Therefore, in most cases, pc == 0 and core == 0 for non-branching tactics. */ virtual void operator()(/* in */ goal_ref const & in, /* out */ goal_ref_buffer & result, /* out */ model_converter_ref & mc, /* out */ proof_converter_ref & pc, /* out */ expr_dependency_ref & core) = 0; virtual void collect_statistics(statistics & st) const {} virtual void reset_statistics() {} virtual void cleanup() = 0; virtual void reset() { cleanup(); } // for backward compatibility virtual void set_logic(symbol const & l) {} virtual void set_progress_callback(progress_callback * callback) {} // translate tactic to the given manager virtual tactic * translate(ast_manager & m) = 0; protected: friend class nary_tactical; friend class binary_tactical; friend class unary_tactical; friend class nl_purify_tactic; virtual void set_cancel(bool f) {} }; typedef ref tactic_ref; typedef sref_vector tactic_ref_vector; typedef sref_buffer tactic_ref_buffer; // minimum verbosity level for tactics #define TACTIC_VERBOSITY_LVL 10 class tactic_report { struct imp; imp * m_imp; public: tactic_report(char const * id, goal const & g); ~tactic_report(); }; void report_tactic_progress(char const * id, unsigned val); class skip_tactic : public tactic { public: virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core); virtual void cleanup() {} virtual tactic * translate(ast_manager & m) { return this; } }; tactic * mk_skip_tactic(); tactic * mk_fail_tactic(); tactic * mk_fail_if_undecided_tactic(); /* ADD_TACTIC("skip", "do nothing tactic.", "mk_skip_tactic()") ADD_TACTIC("fail", "always fail tactic.", "mk_fail_tactic()") ADD_TACTIC("fail-if-undecided", "fail if goal is undecided.", "mk_fail_if_undecided_tactic()") */ tactic * mk_report_verbose_tactic(char const * msg, unsigned lvl); tactic * mk_trace_tactic(char const * tag); class tactic_factory { public: virtual ~tactic_factory() {} virtual tactic * operator()(ast_manager & m, params_ref const & p) = 0; }; #define MK_TACTIC_FACTORY(NAME, CODE) \ class NAME : public tactic_factory { \ public: \ virtual ~NAME() {} \ virtual tactic * operator()(ast_manager & m, params_ref const & p) { CODE } \ }; #define MK_SIMPLE_TACTIC_FACTORY(NAME, ST) MK_TACTIC_FACTORY(NAME, return ST;) void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core); lbool check_sat(tactic & t, goal_ref & g, model_ref & md, proof_ref & pr, expr_dependency_ref & core, std::string & reason_unknown); // Throws an exception if goal \c in requires proof generation. void fail_if_proof_generation(char const * tactic_name, goal_ref const & in); void fail_if_unsat_core_generation(char const * tactic_name, goal_ref const & in); void fail_if_model_generation(char const * tactic_name, goal_ref const & in); #endif z3-4.4.0/src/tactic/equiv_proof_converter.h0000644000175000017500000000215212540347414020501 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: equiv_proof_converter.h Abstract: Proof converter that applies equivalence rule to leaves. Given a proof P with occurrences of [asserted fml] replace [asserted fml] by a proof of the form [mp [asserted fml'] [~ fml fml']] Author: Nikolaj Bjorner (nbjorner) 2012-11-23 Revision History: --*/ #ifndef _EQUIV_PROOF_CONVERTER_H_ #define _EQUIV_PROOF_CONVERTER_H_ #include "replace_proof_converter.h" class equiv_proof_converter : public proof_converter { ast_manager& m; replace_proof_converter m_replace; public: equiv_proof_converter(ast_manager& m): m(m), m_replace(m) {} virtual ~equiv_proof_converter() {} virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) { m_replace(m, num_source, source, result); } virtual proof_converter * translate(ast_translation & translator) { return m_replace.translate(translator); } void insert(expr* fml1, expr* fml2); ast_manager& get_manager() { return m; } }; #endif z3-4.4.0/src/tactic/proof_converter.h0000644000175000017500000000270412540347414017273 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: proof_converter.h Abstract: Abstract interface for converting proofs, and basic combinators. Author: Leonardo (leonardo) 2011-04-26 Notes: --*/ #ifndef _PROOF_CONVERTER_H_ #define _PROOF_CONVERTER_H_ #include"ast.h" #include"converter.h" #include"ref.h" class proof_converter : public converter { public: virtual ~proof_converter() { } virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) = 0; virtual proof_converter * translate(ast_translation & translator) = 0; }; typedef ref proof_converter_ref; proof_converter * concat(proof_converter * pc1, proof_converter * pc2); /** \brief \c pc1 is the proof converter for a sequence of subgoals of size \c num. Given an i in [0, num), pc2s[i] is the proof converter for subgoal i, and num_subgoals[i] is the number of subgoals of subgoals[i]. */ proof_converter * concat(proof_converter * pc1, unsigned num, proof_converter * const * pc2s, unsigned * num_subgoals); proof_converter * proof2proof_converter(ast_manager & m, proof * pr); typedef sref_vector proof_converter_ref_vector; typedef sref_buffer proof_converter_ref_buffer; void apply(ast_manager & m, proof_converter_ref & pc1, proof_converter_ref_buffer & pc2s, proof_ref & result); void apply(ast_manager & m, proof_converter * pc, proof_ref & pr); #endif z3-4.4.0/src/tactic/goal_shared_occs.cpp0000644000175000017500000000077612540347414017700 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: goal_shared_occs.cpp Abstract: Functor for computing the set of shared occurrences in a goal. Author: Leonardo de Moura (leonardo) 2011-12-28 Revision History: --*/ #include"goal_shared_occs.h" void goal_shared_occs::operator()(goal const & g) { m_occs.reset(); shared_occs_mark visited; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * t = g.form(i); m_occs(t, visited); } } z3-4.4.0/src/tactic/replace_proof_converter.h0000644000175000017500000000207712540347414020771 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: replace_proof_converter.h Abstract: Proof converter to replace asserted leaves by proofs. Given a proof P with occurrences of [asserted fml] Replace [asserted fml] by proofs whose conclusions are fml. Author: Nikolaj Bjorner (nbjorner) 2012-9-16 Revision History: --*/ #ifndef _REPLACE_PROOF_CONVERTER_H_ #define _REPLACE_PROOF_CONVERTER_H_ #include "proof_converter.h" class replace_proof_converter : public proof_converter { ast_manager& m; proof_ref_vector m_proofs; public: replace_proof_converter(ast_manager& _m): m(_m), m_proofs(m) {} virtual ~replace_proof_converter() {} virtual void operator()(ast_manager & _m, unsigned num_source, proof * const * source, proof_ref & result); virtual proof_converter * translate(ast_translation & translator); void insert(proof* p) { m_proofs.push_back(p); } ast_manager& get_manager() { return m; } // run the replacements the inverse direction. void invert() { m_proofs.reverse(); } }; #endif z3-4.4.0/src/tactic/nlsat_smt/0000755000175000017500000000000012540347414015707 5ustar michaelmichaelz3-4.4.0/src/tactic/nlsat_smt/nl_purify_tactic.cpp0000644000175000017500000006135712540347414021765 0ustar michaelmichael/*++ Copyright (c) 2015 Microsoft Corporation Module Name: nl_purify_tactic.cpp Abstract: Tactic for purifying quantifier-free formulas that mix QF_NRA and other theories. It is designed to allow cooprating between the nlsat solver and other theories in a decoupled way. Let goal be formula F. Let NL goal be formula G. Assume F is in NNF. Assume F does not contain mix of real/integers. Assume F is quantifier-free (please, otherwise we need to reprocess from instantiated satisfiable formula) For each atomic nl formula f, - introduce a propositional variable p - replace f by p - add clauses p => f to G For each interface term t, - introduce interface variable v (or use t if it is already a variable) - replace t by v Check satisfiability of G. If satisfiable, then check assignment to p and interface equalities on F If unsat: Retrieve core and add core to G. else: For interface equalities from model of F that are not equal in G, add For interface variables that are equal under one model, but not the other model, create interface predicate p_vw => v = w, add to both F, G. Add interface equations to assumptions, recheck F. If unsat retrieve core add to G. Author: Nikolaj Bjorner (nbjorner) 2015-5-5. Revision History: --*/ #include "tactical.h" #include "nl_purify_tactic.h" #include "smt_tactic.h" #include "rewriter.h" #include "nlsat_tactic.h" #include "filter_model_converter.h" #include "obj_pair_hashtable.h" #include "rewriter_def.h" #include "ast_pp.h" #include "trace.h" #include "smt_solver.h" #include "solver.h" #include "model_smt2_pp.h" #include "expr_safe_replace.h" class nl_purify_tactic : public tactic { enum polarity_t { pol_pos, pol_neg, pol_dual }; ast_manager & m; arith_util m_util; params_ref m_params; bool m_produce_proofs; ref m_fmc; bool m_cancel; tactic_ref m_nl_tac; // nlsat tactic goal_ref m_nl_g; // nlsat goal ref m_solver; // SMT solver expr_ref_vector m_eq_preds; // predicates for equality between pairs of interface variables svector m_eq_values; // truth value of the equality predicates in nlsat app_ref_vector m_new_reals; // interface real variables app_ref_vector m_new_preds; // abstraction predicates for smt_solver (hide real constraints) expr_ref_vector m_asms; // assumptions to pass to SMT solver obj_pair_map m_eq_pairs; // map pairs of interface variables to auxiliary predicates obj_map m_interface_cache; // map of compound real expression to interface variable. obj_map m_polarities; // polarities of sub-expressions public: struct rw_cfg : public default_rewriter_cfg { enum mode_t { mode_interface_var, mode_bool_preds }; ast_manager& m; nl_purify_tactic & m_owner; app_ref_vector& m_new_reals; app_ref_vector& m_new_preds; obj_map& m_polarities; obj_map& m_interface_cache; expr_ref_vector m_args; mode_t m_mode; rw_cfg(nl_purify_tactic & o): m(o.m), m_owner(o), m_new_reals(o.m_new_reals), m_new_preds(o.m_new_preds), m_polarities(o.m_polarities), m_interface_cache(o.m_interface_cache), m_args(m), m_mode(mode_interface_var) { } virtual ~rw_cfg() {} arith_util & u() { return m_owner.m_util; } bool produce_proofs() const { return m_owner.m_produce_proofs; } expr * mk_interface_var(expr* arg) { expr* r; if (m_interface_cache.find(arg, r)) { return r; } if (is_uninterp_const(arg)) { m_interface_cache.insert(arg, arg); return arg; } r = m.mk_fresh_const(0, u().mk_real()); m_new_reals.push_back(to_app(r)); m_owner.m_fmc->insert(to_app(r)->get_decl()); m_interface_cache.insert(arg, r); expr_ref eq(m); eq = m.mk_eq(r, arg); if (is_real_expression(arg)) { m_owner.m_nl_g->assert_expr(eq); // m.mk_oeq(r, arg) } else { m_owner.m_solver->assert_expr(eq); } return r; } bool is_real_expression(expr* e) { return is_app(e) && (to_app(e)->get_family_id() == u().get_family_id()); } void mk_interface_bool(func_decl * f, unsigned num, expr* const* args, expr_ref& result) { expr_ref old_pred(m.mk_app(f, num, args), m); polarity_t pol; VERIFY(m_polarities.find(old_pred, pol)); result = m.mk_fresh_const(0, m.mk_bool_sort()); m_polarities.insert(result, pol); m_new_preds.push_back(to_app(result)); m_owner.m_fmc->insert(to_app(result)->get_decl()); if (pol != pol_neg) { m_owner.m_nl_g->assert_expr(m.mk_or(m.mk_not(result), m.mk_app(f, num, args))); } if (pol != pol_pos) { m_owner.m_nl_g->assert_expr(m.mk_or(result, m.mk_not(m.mk_app(f, num, args)))); } TRACE("nlsat_smt", tout << old_pred << " : " << result << "\n";); } bool reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { throw tactic_exception("quantifiers are not supported in mixed-mode nlsat engine"); } br_status reduce_app(func_decl * f, unsigned num, expr* const* args, expr_ref& result, proof_ref & pr) { if (m_mode == mode_bool_preds) { return reduce_app_bool(f, num, args, result, pr); } else { return reduce_app_real(f, num, args, result, pr); } } br_status reduce_app_bool(func_decl * f, unsigned num, expr* const* args, expr_ref& result, proof_ref & pr) { if (f->get_family_id() == m.get_basic_family_id()) { if (f->get_decl_kind() == OP_EQ && u().is_real(args[0])) { mk_interface_bool(f, num, args, result); return BR_DONE; } else { return BR_FAILED; } } if (f->get_family_id() == u().get_family_id()) { switch (f->get_decl_kind()) { case OP_LE: case OP_GE: case OP_LT: case OP_GT: // these are the only real cases of non-linear atomic formulas besides equality. mk_interface_bool(f, num, args, result); return BR_DONE; default: return BR_FAILED; } } return BR_FAILED; } br_status reduce_app_real(func_decl * f, unsigned num, expr* const* args, expr_ref& result, proof_ref & pr) { bool has_interface = false; if (f->get_family_id() == u().get_family_id()) { switch (f->get_decl_kind()) { case OP_NUM: case OP_IRRATIONAL_ALGEBRAIC_NUM: case OP_ADD: case OP_MUL: case OP_SUB: case OP_UMINUS: case OP_ABS: case OP_POWER: return BR_FAILED; default: break; } } m_args.reset(); for (unsigned i = 0; i < num; ++i) { expr* arg = args[i]; if (u().is_real(arg)) { has_interface = true; m_args.push_back(mk_interface_var(arg)); } else { m_args.push_back(arg); } } if (has_interface) { result = m.mk_app(f, num, m_args.c_ptr()); TRACE("nlsat_smt", tout << result << "\n";); return BR_DONE; } else { return BR_FAILED; } } }; private: class rw : public rewriter_tpl { rw_cfg m_cfg; public: rw(nl_purify_tactic & o): rewriter_tpl(o.m, o.m_produce_proofs, m_cfg), m_cfg(o) { } void set_bool_mode() { m_cfg.m_mode = rw_cfg::mode_bool_preds; } void set_interface_var_mode() { m_cfg.m_mode = rw_cfg::mode_interface_var; } }; arith_util & u() { return m_util; } void check_point() { if (m_cancel) { throw tactic_exception("canceled"); } } void display_result(std::ostream& out, goal_ref_buffer const& result) { for (unsigned i = 0; i < result.size(); ++i) { result[i]->display(out << "goal\n"); } } void update_eq_values(model_ref& mdl) { expr_ref tmp(m); for (unsigned i = 0; i < m_eq_preds.size(); ++i) { expr* pred = m_eq_preds[i].get(); m_eq_values[i] = l_undef; if (mdl->eval(pred, tmp)) { if (m.is_true(tmp)) { m_eq_values[i] = l_true; } else if (m.is_false(tmp)) { m_eq_values[i] = l_false; } } } } void solve(goal_ref_buffer& result, model_converter_ref& mc) { while (true) { check_point(); TRACE("nlsat_smt", m_solver->display(tout << "SMT:\n"); m_nl_g->display(tout << "\nNL:\n"); ); goal_ref tmp_nl = alloc(goal, m, true, false); model_converter_ref nl_mc; proof_converter_ref nl_pc; expr_dependency_ref nl_core(m); result.reset(); tmp_nl->copy_from(*m_nl_g.get()); (*m_nl_tac)(tmp_nl, result, nl_mc, nl_pc, nl_core); if (is_decided_unsat(result)) { TRACE("nlsat_smt", tout << "unsat\n";); break; } if (!is_decided_sat(result)) { TRACE("nlsat_smt", tout << "not a unit\n";); break; } // extract evaluation on interface variables. // assert booleans that evaluate to true. // assert equalities between equal interface real variables. model_ref mdl_nl, mdl_smt; if (mdl_nl.get()) { model_converter2model(m, nl_mc.get(), mdl_nl); update_eq_values(mdl_nl); enforce_equalities(mdl_nl, m_nl_g); setup_assumptions(mdl_nl); TRACE("nlsat_smt", model_smt2_pp(tout << "nl model\n", m, *mdl_nl.get(), 0); m_solver->display(tout << "smt goal:\n"); tout << "\n";); } result.reset(); lbool r = m_solver->check_sat(m_asms.size(), m_asms.c_ptr()); if (r == l_false) { // extract the core from the result ptr_vector core; m_solver->get_unsat_core(core); if (core.empty()) { goal_ref g = alloc(goal, m); g->assert_expr(m.mk_false()); result.push_back(g.get()); break; } expr_ref_vector clause(m); expr_ref fml(m); expr* e; for (unsigned i = 0; i < core.size(); ++i) { clause.push_back(m.is_not(core[i], e)?e:m.mk_not(core[i])); } fml = m.mk_or(clause.size(), clause.c_ptr()); m_nl_g->assert_expr(fml); continue; } else if (r == l_true) { m_solver->get_model(mdl_smt); if (enforce_equalities(mdl_smt, m_nl_g)) { // SMT enforced a new equality that wasn't true for nlsat. continue; } TRACE("nlsat_smt", m_fmc->display(tout << "joint state is sat\n"); nl_mc->display(tout << "nl\n");); if (mdl_nl.get()) { merge_models(*mdl_nl.get(), mdl_smt); } mc = m_fmc.get(); apply(mc, mdl_smt, 0); mc = model2model_converter(mdl_smt.get()); result.push_back(alloc(goal, m)); } else { TRACE("nlsat_smt", tout << "unknown\n";); } break; } TRACE("nlsat_smt", display_result(tout, result);); } void setup_assumptions(model_ref& mdl) { m_asms.reset(); app_ref_vector const& fresh_preds = m_new_preds; expr_ref tmp(m); for (unsigned i = 0; i < fresh_preds.size(); ++i) { expr* pred = fresh_preds[i]; if (mdl->eval(pred, tmp)) { polarity_t pol = m_polarities.find(pred); // if assumptinon literals are used to satisfy NL state, // we have to assume them when satisfying SMT state if (pol != pol_neg && m.is_false(tmp)) { m_asms.push_back(m.mk_not(pred)); } else if (pol != pol_pos && m.is_true(tmp)) { m_asms.push_back(pred); } } } for (unsigned i = 0; i < m_eq_preds.size(); ++i) { expr* pred = m_eq_preds[i].get(); switch (m_eq_values[i]) { case l_true: m_asms.push_back(pred); break; case l_false: m_asms.push_back(m.mk_not(pred)); break; default: break; } } TRACE("nlsat_smt", tout << "assumptions:\n"; for (unsigned i = 0; i < m_asms.size(); ++i) { tout << mk_pp(m_asms[i].get(), m) << "\n"; }); } bool enforce_equalities(model_ref& mdl, goal_ref const& nl_g) { TRACE("nlsat_smt", tout << "Enforce equalities " << m_interface_cache.size() << "\n";); bool new_equality = false; expr_ref_vector nums(m); obj_map num2var; obj_map::iterator it = m_interface_cache.begin(), end = m_interface_cache.end(); for (; it != end; ++it) { expr_ref r(m); expr* v, *w, *pred; w = it->m_value; VERIFY(mdl->eval(w, r)); TRACE("nlsat_smt", tout << mk_pp(w, m) << " |-> " << r << "\n";); nums.push_back(r); if (num2var.find(r, v)) { if (!m_eq_pairs.find(v, w, pred)) { pred = m.mk_fresh_const(0, m.mk_bool_sort()); m_eq_preds.push_back(pred); m_eq_values.push_back(l_true); m_fmc->insert(to_app(pred)->get_decl()); nl_g->assert_expr(m.mk_or(m.mk_not(pred), m.mk_eq(w, v))); nl_g->assert_expr(m.mk_or(pred, m.mk_not(m.mk_eq(w, v)))); m_solver->assert_expr(m.mk_iff(pred, m.mk_eq(w, v))); new_equality = true; m_eq_pairs.insert(v, w, pred); } else { // interface equality is already enforced. } } else { num2var.insert(r, w); } } return new_equality; } void merge_models(model const& mdl_nl, model_ref& mdl_smt) { expr_safe_replace num2num(m); expr_ref result(m), val2(m); expr_ref_vector args(m); unsigned sz = mdl_nl.get_num_constants(); for (unsigned i = 0; i < sz; ++i) { func_decl* v = mdl_nl.get_constant(i); if (u().is_real(v->get_range())) { expr* val = mdl_nl.get_const_interp(v); if (mdl_smt->eval(v, val2)) { if (val != val2) { num2num.insert(val2, val); } } } } sz = mdl_smt->get_num_functions(); for (unsigned i = 0; i < sz; ++i) { func_decl* f = mdl_smt->get_function(i); if (has_real(f)) { unsigned arity = f->get_arity(); func_interp* f1 = mdl_smt->get_func_interp(f); func_interp* f2 = alloc(func_interp, m, f->get_arity()); for (unsigned j = 0; j < f1->num_entries(); ++j) { args.reset(); func_entry const* entry = f1->get_entry(j); for (unsigned k = 0; k < arity; ++k) { translate(num2num, entry->get_arg(k), result); args.push_back(result); } translate(num2num, entry->get_result(), result); f2->insert_entry(args.c_ptr(), result); } translate(num2num, f1->get_else(), result); f2->set_else(result); mdl_smt->register_decl(f, f2); } } mdl_smt->copy_const_interps(mdl_nl); } bool has_real(func_decl* f) { for (unsigned i = 0; i < f->get_arity(); ++i) { if (u().is_real(f->get_domain(i))) return true; } return u().is_real(f->get_range()); } void translate(expr_safe_replace& num2num, expr* e, expr_ref& result) { result = 0; if (e) { num2num(e, result); } } void get_polarities(goal const& g) { ptr_vector forms; svector pols; unsigned sz = g.size(); for (unsigned i = 0; i < sz; ++i) { forms.push_back(g.form(i)); pols.push_back(pol_pos); } polarity_t p, q; while (!forms.empty()) { expr* e = forms.back(); p = pols.back(); forms.pop_back(); pols.pop_back(); if (m_polarities.find(e, q)) { if (p == q || q == pol_dual) continue; p = pol_dual; } TRACE("nlsat_smt_verbose", tout << mk_pp(e, m) << "\n";); m_polarities.insert(e, p); if (is_quantifier(e) || is_var(e)) { throw tactic_exception("nl-purify tactic does not support quantifiers"); } SASSERT(is_app(e)); app* a = to_app(e); func_decl* f = a->get_decl(); if (f->get_family_id() == m.get_basic_family_id() && p != pol_dual) { switch(f->get_decl_kind()) { case OP_NOT: p = neg(p); break; case OP_AND: case OP_OR: break; default: p = pol_dual; break; } } else { p = pol_dual; } for (unsigned i = 0; i < a->get_num_args(); ++i) { forms.push_back(a->get_arg(i)); pols.push_back(p); } } } polarity_t neg(polarity_t p) { switch (p) { case pol_pos: return pol_neg; case pol_neg: return pol_pos; case pol_dual: return pol_dual; } return pol_dual; } polarity_t join(polarity_t p, polarity_t q) { if (p == q) return p; return pol_dual; } void rewrite_goal(rw& r, goal_ref const& g) { r.reset(); expr_ref new_curr(m); proof_ref new_pr(m); unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { expr * curr = g->form(i); r(curr, new_curr, new_pr); if (m_produce_proofs) { proof * pr = g->pr(i); new_pr = m.mk_modus_ponens(pr, new_pr); } g->update(i, new_curr, new_pr, g->dep(i)); } } void remove_pure_arith(goal_ref const& g) { obj_map is_pure; unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { expr * curr = g->form(i); if (is_pure_arithmetic(is_pure, curr)) { m_nl_g->assert_expr(curr, g->pr(i), g->dep(i)); g->update(i, m.mk_true()); } } } bool is_pure_arithmetic(obj_map& is_pure, expr* e0) { ptr_vector todo; todo.push_back(e0); while (!todo.empty()) { expr* e = todo.back(); if (is_pure.contains(e)) { todo.pop_back(); continue; } if (!is_app(e)) { todo.pop_back(); is_pure.insert(e, false); continue; } app* a = to_app(e); bool pure = false, all_found = true, p; pure |= (a->get_family_id() == u().get_family_id()) && u().is_real(a); pure |= (m.is_eq(e) && u().is_real(a->get_arg(0))); pure |= (a->get_family_id() == u().get_family_id()) && m.is_bool(a) && u().is_real(a->get_arg(0)); pure |= (a->get_family_id() == m.get_basic_family_id()); pure |= is_uninterp_const(a) && u().is_real(a); for (unsigned i = 0; i < a->get_num_args(); ++i) { if (!is_pure.find(a->get_arg(i), p)) { todo.push_back(a->get_arg(i)); all_found = false; } else { pure &= p; } } if (all_found) { is_pure.insert(e, pure); todo.pop_back(); } } return is_pure.find(e0); } public: nl_purify_tactic(ast_manager & m, params_ref const& p): m(m), m_util(m), m_params(p), m_fmc(0), m_cancel(false), m_nl_tac(mk_nlsat_tactic(m, p)), m_nl_g(0), m_solver(mk_smt_solver(m, p, symbol::null)), m_eq_preds(m), m_new_reals(m), m_new_preds(m), m_asms(m) {} virtual ~nl_purify_tactic() {} virtual void updt_params(params_ref const & p) { m_params = p; } virtual tactic * translate(ast_manager& m) { return alloc(nl_purify_tactic, m, m_params); } virtual void set_cancel(bool f) { m_nl_tac->set_cancel(f); if (f) { m_solver->cancel(); } else { m_solver->reset_cancel(); } m_cancel = f; } virtual void cleanup() { m_solver = mk_smt_solver(m, m_params, symbol::null); m_nl_tac->cleanup(); m_eq_preds.reset(); m_eq_values.reset(); m_new_reals.reset(); m_new_preds.reset(); m_eq_pairs.reset(); m_polarities.reset(); } virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { tactic_report report("qfufnl-purify", *g); TRACE("nlsat_smt", g->display(tout);); m_produce_proofs = g->proofs_enabled(); mc = 0; pc = 0; core = 0; fail_if_proof_generation("qfufnra-purify", g); fail_if_unsat_core_generation("qfufnra-purify", g); rw r(*this); m_nl_g = alloc(goal, m, true, false); m_fmc = alloc(filter_model_converter, m); // first hoist interface variables, // then annotate subformulas by polarities, // finally extract polynomial inequalities by // creating a place-holder predicate inside the // original goal and extracing pure nlsat clauses. r.set_interface_var_mode(); rewrite_goal(r, g); remove_pure_arith(g); get_polarities(*g.get()); r.set_bool_mode(); rewrite_goal(r, g); g->inc_depth(); for (unsigned i = 0; i < g->size(); ++i) { m_solver->assert_expr(g->form(i)); } g->inc_depth(); solve(result, mc); } }; tactic * mk_nl_purify_tactic(ast_manager& m, params_ref const& p) { return alloc(nl_purify_tactic, m, p); } z3-4.4.0/src/tactic/nlsat_smt/nl_purify_tactic.h0000644000175000017500000000130412540347414021414 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: nl_purify_tactic.h Abstract: Tactic for purifying quantifier-free formulas that mix QF_NRA and other theories. It is designed to allow cooprating between the nlsat solver and other theories in a decoubled way. Author: Nikolaj Bjorner (nbjorner) 2015-5-5. Revision History: --*/ #ifndef _NL_PURIFY_TACTIC_H_ #define _NL_PURIFY_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_nl_purify_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("nl-purify", "Decompose goal into pure NL-sat formula and formula over other theories.", "mk_nl_purify_tactic(m, p)") */ #endif z3-4.4.0/src/interp/0000755000175000017500000000000012540347414013735 5ustar michaelmichaelz3-4.4.0/src/interp/iz3proof.cpp0000755000175000017500000004505312540347414016226 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3proof.cpp Abstract: This class defines a simple interpolating proof system. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifdef _WINDOWS #pragma warning(disable:4996) #pragma warning(disable:4800) #pragma warning(disable:4267) #pragma warning(disable:4101) #endif #include "iz3proof.h" #include "iz3profiling.h" #include #include #include #include // #define FACTOR_INTERPS // #define CHECK_PROOFS void iz3proof::resolve(ast pivot, std::vector &cls1, const std::vector &cls2){ #ifdef CHECK_PROOFS std::vector orig_cls1 = cls1; #endif ast neg_pivot = pv->mk_not(pivot); bool found_pivot1 = false, found_pivot2 = false; for(unsigned i = 0; i < cls1.size(); i++){ if(cls1[i] == neg_pivot){ cls1[i] = cls1.back(); cls1.pop_back(); found_pivot1 = true; break; } } { std::set memo; memo.insert(cls1.begin(),cls1.end()); for(unsigned j = 0; j < cls2.size(); j++){ if(cls2[j] == pivot) found_pivot2 = true; else if(memo.find(cls2[j]) == memo.end()) cls1.push_back(cls2[j]); } } if(found_pivot1 && found_pivot2) return; #ifdef CHECK_PROOFS std::cerr << "resolution anomaly: " << nodes.size()-1 << "\n"; #if 0 std::cerr << "pivot: "; {pv->print_lit(pivot); std::cout << "\n";} std::cerr << "left clause:\n"; for(unsigned i = 0; i < orig_cls1.size(); i++) {pv->print_lit(orig_cls1[i]); std::cout << "\n";} std::cerr << "right clause:\n"; for(unsigned i = 0; i < cls2.size(); i++) {pv->print_lit(cls2[i]); std::cout << "\n";} throw proof_error(); #endif #endif } iz3proof::node iz3proof::make_resolution(ast pivot, node premise1, node premise2) { if(nodes[premise1].rl == Hypothesis) return premise2; // resolve with hyp is noop if(nodes[premise2].rl == Hypothesis) return premise1; node res = make_node(); node_struct &n = nodes[res]; n.rl = Resolution; n.aux = pivot; n.premises.resize(2); n.premises[0] = (premise1); n.premises[1] = (premise2); #ifdef CHECK_PROOFS n.conclusion = nodes[premise1].conclusion; resolve(pivot,n.conclusion,nodes[premise2].conclusion); n.frame = 1; #else n.frame = 0; // compute conclusion lazily #endif return res; } iz3proof::node iz3proof::resolve_lemmas(ast pivot, node premise1, node premise2) { std::vector lits(nodes[premise1].conclusion), itp; // no interpolant resolve(pivot,lits,nodes[premise2].conclusion); return make_lemma(lits,itp); } iz3proof::node iz3proof::make_assumption(int frame, const std::vector &assumption){ #if 0 std::cout << "assumption: \n"; for(unsigned i = 0; i < assumption.size(); i++) pv->show(assumption[i]); std::cout << "\n"; #endif node res = make_node(); node_struct &n = nodes[res]; n.rl = Assumption; n.conclusion.resize(1); n.conclusion = assumption; n.frame = frame; return res; } iz3proof::node iz3proof::make_hypothesis(ast hypothesis){ node res = make_node(); node_struct &n = nodes[res]; n.rl = Hypothesis; n.conclusion.resize(2); n.conclusion[0] = hypothesis; n.conclusion[1] = pv->mk_not(hypothesis); return res; } iz3proof::node iz3proof::make_theory(const std::vector &conclusion, std::vector premises){ node res = make_node(); node_struct &n = nodes[res]; n.rl = Theory; n.conclusion = conclusion; n.premises = premises; return res; } iz3proof::node iz3proof::make_axiom(const std::vector &conclusion){ node res = make_node(); node_struct &n = nodes[res]; n.rl = Axiom; n.conclusion = conclusion; return res; } iz3proof::node iz3proof::make_contra(node prem, const std::vector &conclusion){ node res = make_node(); node_struct &n = nodes[res]; n.rl = Contra; n.conclusion = conclusion; #ifdef CHECK_PROOFS //if(!(conclusion == nodes[prem].conclusion)){ //std::cerr << "internal error: proof error\n"; //assert(0 && "proof error"); //} #endif n.premises.push_back(prem); return res; } iz3proof::node iz3proof::make_lemma(const std::vector &conclusion, const std::vector &interpolation){ node res = make_node(); node_struct &n = nodes[res]; n.rl = Lemma; n.conclusion = conclusion; n.frame = interps.size(); interps.push_back(interpolation); return res; } /** Make a Reflexivity node. This rule produces |- x = x */ iz3proof::node iz3proof::make_reflexivity(ast con){ node res = make_node(); node_struct &n = nodes[res]; n.rl = Reflexivity; n.conclusion.push_back(con); return res; } /** Make a Symmetry node. This takes a derivation of |- x = y and produces | y = x */ iz3proof::node iz3proof::make_symmetry(ast con, node prem){ node res = make_node(); node_struct &n = nodes[res]; n.rl = Reflexivity; n.conclusion.push_back(con); n.premises.push_back(prem); return res; } /** Make a transitivity node. This takes derivations of |- x = y and |- y = z produces | x = z */ iz3proof::node iz3proof::make_transitivity(ast con, node prem1, node prem2){ node res = make_node(); node_struct &n = nodes[res]; n.rl = Transitivity; n.conclusion.push_back(con); n.premises.push_back(prem1); n.premises.push_back(prem2); return res; } /** Make a congruence node. This takes derivations of |- x_i = y_i and produces |- f(x_1,...,x_n) = f(y_1,...,y_n) */ iz3proof::node iz3proof::make_congruence(ast con, const std::vector &prems){ node res = make_node(); node_struct &n = nodes[res]; n.rl = Congruence; n.conclusion.push_back(con); n.premises = prems; return res; } /** Make an equality contradicition node. This takes |- x = y and |- !(x = y) and produces false. */ iz3proof::node iz3proof::make_eqcontra(node prem1, node prem2){ node res = make_node(); node_struct &n = nodes[res]; n.rl = EqContra; n.premises.push_back(prem1); n.premises.push_back(prem2); return res; } iz3proof::node iz3proof::copy_rec(stl_ext::hash_map &memo, iz3proof &src, node n){ stl_ext::hash_map::iterator it = memo.find(n); if(it != memo.end()) return (*it).second; node_struct &ns = src.nodes[n]; std::vector prems(ns.premises.size()); for(unsigned i = 0; i < prems.size(); i++) prems[i] = copy_rec(memo,src,ns.premises[i]); nodes.push_back(ns); nodes.back().premises.swap(prems); if(ns.rl == Lemma){ nodes.back().frame = interps.size(); interps.push_back(src.interps[ns.frame]); } int res = nodes.size()-1; memo[n] = res; return res; } iz3proof::node iz3proof::copy(iz3proof &src, node n){ stl_ext::hash_map memo; return copy_rec(memo, src, n); } bool iz3proof::pred_in_A(ast id){ return weak ? pv->ranges_intersect(pv->ast_range(id),rng) : pv->range_contained(pv->ast_range(id),rng); } bool iz3proof::term_in_B(ast id){ prover::range r = pv->ast_scope(id); if(weak) { if(pv->range_min(r) == SHRT_MIN) return !pv->range_contained(r,rng); else return !pv->ranges_intersect(r,rng); } else return !pv->range_contained(r,rng); } bool iz3proof::frame_in_A(int frame){ return pv->in_range(frame,rng); } bool iz3proof::lit_in_B(ast lit){ return b_lits.find(lit) != b_lits.end() || b_lits.find(pv->mk_not(lit)) != b_lits.end(); } iz3proof::ast iz3proof::my_or(ast x, ast y){ return pv->mk_not(pv->mk_and(pv->mk_not(x),pv->mk_not(y))); } iz3proof::ast iz3proof::get_A_lits(std::vector &cls){ ast foo = pv->mk_false(); for(unsigned i = 0; i < cls.size(); i++){ ast lit = cls[i]; if(b_lits.find(pv->mk_not(lit)) == b_lits.end()){ if(pv->range_max(pv->ast_scope(lit)) == pv->range_min(pv->ast_scope(lit))){ std::cout << "bad lit: " << pv->range_max(rng) << " : " << pv->range_max(pv->ast_scope(lit)) << " : " << (pv->ast_id(lit)) << " : "; pv->show(lit); } foo = my_or(foo,lit); } } return foo; } iz3proof::ast iz3proof::get_B_lits(std::vector &cls){ ast foo = pv->mk_false(); for(unsigned i = 0; i < cls.size(); i++){ ast lit = cls[i]; if(b_lits.find(pv->mk_not(lit)) != b_lits.end()) foo = my_or(foo,lit); } return foo; } void iz3proof::set_of_B_lits(std::vector &cls, std::set &res){ for(unsigned i = 0; i < cls.size(); i++){ ast lit = cls[i]; if(b_lits.find(pv->mk_not(lit)) != b_lits.end()) res.insert(lit); } } void iz3proof::set_of_A_lits(std::vector &cls, std::set &res){ for(unsigned i = 0; i < cls.size(); i++){ ast lit = cls[i]; if(b_lits.find(pv->mk_not(lit)) == b_lits.end()) res.insert(lit); } } void iz3proof::find_B_lits(){ b_lits.clear(); for(unsigned i = 0; i < nodes.size(); i++){ node_struct &n = nodes[i]; std::vector &cls = n.conclusion; if(n.rl == Assumption){ if(weak) goto lemma; if(!frame_in_A(n.frame)) for(unsigned j = 0; j < cls.size(); j++) b_lits.insert(cls[j]); } else if(n.rl == Lemma) { lemma: for(unsigned j = 0; j < cls.size(); j++) if(term_in_B(cls[j])) b_lits.insert(cls[j]); } } } iz3proof::ast iz3proof::disj_of_set(std::set &s){ ast res = pv->mk_false(); for(std::set::iterator it = s.begin(), en = s.end(); it != en; ++it) res = my_or(*it,res); return res; } void iz3proof::mk_and_factor(int p1, int p2, int i, std::vector &itps, std::vector > &disjs){ #ifdef FACTOR_INTERPS std::set &d1 = disjs[p1]; std::set &d2 = disjs[p2]; if(!weak){ if(pv->is_true(itps[p1])){ itps[i] = itps[p2]; disjs[i] = disjs[p2]; } else if(pv->is_true(itps[p2])){ itps[i] = itps[p1]; disjs[i] = disjs[p1]; } else { std::set p1only,p2only; std::insert_iterator > p1o(p1only,p1only.begin()); std::insert_iterator > p2o(p2only,p2only.begin()); std::insert_iterator > dio(disjs[i],disjs[i].begin()); std::set_difference(d1.begin(),d1.end(),d2.begin(),d2.end(),p1o); std::set_difference(d2.begin(),d2.end(),d1.begin(),d1.end(),p2o); std::set_intersection(d1.begin(),d1.end(),d2.begin(),d2.end(),dio); ast p1i = my_or(itps[p1],disj_of_set(p1only)); ast p2i = my_or(itps[p2],disj_of_set(p2only)); itps[i] = pv->mk_and(p1i,p2i); } } else { itps[i] = pv->mk_and(itps[p1],itps[p2]); std::insert_iterator > dio(disjs[i],disjs[i].begin()); std::set_union(d1.begin(),d1.end(),d2.begin(),d2.end(),dio); } #endif } void iz3proof::mk_or_factor(int p1, int p2, int i, std::vector &itps, std::vector > &disjs){ #ifdef FACTOR_INTERPS std::set &d1 = disjs[p1]; std::set &d2 = disjs[p2]; if(weak){ if(pv->is_false(itps[p1])){ itps[i] = itps[p2]; disjs[i] = disjs[p2]; } else if(pv->is_false(itps[p2])){ itps[i] = itps[p1]; disjs[i] = disjs[p1]; } else { std::set p1only,p2only; std::insert_iterator > p1o(p1only,p1only.begin()); std::insert_iterator > p2o(p2only,p2only.begin()); std::insert_iterator > dio(disjs[i],disjs[i].begin()); std::set_difference(d1.begin(),d1.end(),d2.begin(),d2.end(),p1o); std::set_difference(d2.begin(),d2.end(),d1.begin(),d1.end(),p2o); std::set_intersection(d1.begin(),d1.end(),d2.begin(),d2.end(),dio); ast p1i = pv->mk_and(itps[p1],pv->mk_not(disj_of_set(p1only))); ast p2i = pv->mk_and(itps[p2],pv->mk_not(disj_of_set(p2only))); itps[i] = my_or(p1i,p2i); } } else { itps[i] = my_or(itps[p1],itps[p2]); std::insert_iterator > dio(disjs[i],disjs[i].begin()); std::set_union(d1.begin(),d1.end(),d2.begin(),d2.end(),dio); } #endif } void iz3proof::interpolate_lemma(node_struct &n){ if(interps[n.frame].size()) return; // already computed pv->interpolate_clause(n.conclusion,interps[n.frame]); } iz3proof::ast iz3proof::interpolate(const prover::range &_rng, bool _weak #ifdef CHECK_PROOFS , ast assump , std::vector *parents #endif ){ // std::cout << "proof size: " << nodes.size() << "\n"; rng = _rng; weak = _weak; #ifdef CHECK_PROOFS if(nodes[nodes.size()-1].conclusion.size() != 0) std::cerr << "internal error: proof conclusion is not empty clause\n"; if(!child_interps.size()){ child_interps.resize(nodes.size()); for(unsigned j = 0; j < nodes.size(); j++) child_interps[j] = pv->mk_true(); } #endif std::vector itps(nodes.size()); #ifdef FACTOR_INTERPS std::vector > disjs(nodes.size()); #endif profiling::timer_start("Blits"); find_B_lits(); profiling::timer_stop("Blits"); profiling::timer_start("interp_proof"); // strengthen(); for(unsigned i = 0; i < nodes.size(); i++){ node_struct &n = nodes[i]; ast &q = itps[i]; switch(n.rl){ case Assumption: { if(frame_in_A(n.frame)){ /* HypC-A */ if(!weak) #ifdef FACTOR_INTERPS { q = pv->mk_false(); set_of_B_lits(n.conclusion,disjs[i]); } #else q = get_B_lits(n.conclusion); #endif else q = pv->mk_false(); } else { /* HypEq-B */ if(!weak) q = pv->mk_true(); else #ifdef FACTOR_INTERPS { q = pv->mk_true(); set_of_A_lits(n.conclusion,disjs[i]); } #else q = pv->mk_not(get_A_lits(n.conclusion)); #endif } break; } case Resolution: { ast p = n.aux; p = pv->is_not(p) ? pv->mk_not(p) : p; // should be positive, but just in case if(lit_in_B(p)) #ifdef FACTOR_INTERPS mk_and_factor(n.premises[0],n.premises[1],i,itps,disjs); #else q = pv->mk_and(itps[n.premises[0]],itps[n.premises[1]]); #endif else #ifdef FACTOR_INTERPS mk_or_factor(n.premises[0],n.premises[1],i,itps,disjs); #else q = my_or(itps[n.premises[0]],itps[n.premises[1]]); #endif break; } case Lemma: { interpolate_lemma(n); // make sure lemma interpolants have been computed q = interps[n.frame][pv->range_max(rng)]; break; } case Contra: { q = itps[n.premises[0]]; #ifdef FACTOR_INTERPS disjs[i] = disjs[n.premises[0]]; #endif break; } default: assert(0 && "rule not allowed in interpolated proof"); } #ifdef CHECK_PROOFS int this_frame = pv->range_max(rng); if(0 && this_frame == 39) { std::vector alits; ast s = pv->mk_true(); for(unsigned j = 0; j < n.conclusion.size(); j++) if(pred_in_A(n.conclusion[j])){ int scpmax = pv->range_max(pv->ast_scope(n.conclusion[j])); if(scpmax == this_frame) s = pv->mk_and(s,pv->mk_not(n.conclusion[j])); } ast ci = child_interps[i]; s = pv->mk_and(pv->mk_and(s,pv->mk_and(assump,pv->mk_not(q))),ci); if(pv->is_sat(s)){ std::cout << "interpolation invariant violated at step " << i << "\n"; assert(0 && "interpolation invariant violated"); } } if((*parents)[this_frame] == 39) child_interps[i] = pv->mk_and(child_interps[i],q); #endif } ast &bar = itps[nodes.size()-1]; #ifdef FACTOR_INTERPS if(!weak) bar = my_or(bar,disj_of_set(disjs[nodes.size()-1])); else bar = pv->mk_and(bar,pv->mk_not(disj_of_set(disjs[nodes.size()-1]))); #endif profiling::timer_stop("interp_proof"); profiling::timer_start("simplifying"); bar = pv->simplify(bar); profiling::timer_stop("simplifying"); return bar; } void iz3proof::print(std::ostream &s, int id){ node_struct &n = nodes[id]; switch(n.rl){ case Assumption: s << "Assumption("; pv->print_clause(s,n.conclusion); s << ")"; break; case Hypothesis: s << "Hyp("; pv->print_expr(s,n.conclusion[0]); s << ")"; break; case Reflexivity: s << "Refl("; pv->print_expr(s,n.conclusion[0]); s << ")"; break; case Symmetry: s << "Symm("; print(s,n.premises[0]); s << ")"; break; case Transitivity: s << "Trans("; print(s,n.premises[0]); s << ","; print(s,n.premises[1]); s << ")"; break; case Congruence: s << "Cong("; pv->print_expr(s,n.conclusion[0]); for(unsigned i = 0; i < n.premises.size(); i++){ s << ","; print(s,n.premises[i]); } s << ")"; break; case EqContra: s << "EqContra("; print(s,n.premises[0]); s << ","; print(s,n.premises[1]); s << ")"; break; case Resolution: s << "Res("; pv->print_expr(s,n.aux); s << ","; print(s,n.premises[0]); s << ","; print(s,n.premises[1]); s << ")"; break; case Lemma: s << "Lemma("; pv->print_clause(s,n.conclusion); for(unsigned i = 0; i < n.premises.size(); i++){ s << ","; print(s,n.premises[i]); } s << ")"; break; case Contra: s << "Contra("; print(s,n.premises[0]); s << ")"; break; default:; } } void iz3proof::show(int id){ std::ostringstream ss; print(ss,id); iz3base::pretty_print(std::cout,ss.str()); // std::cout << ss.str(); std::cout << "\n"; } z3-4.4.0/src/interp/iz3interp.h0000644000175000017500000000507012540347414016037 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3interp.h Abstract: Interpolation based on proof translation. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef IZ3_INTERP_H #define IZ3_INTERP_H #include "iz3hash.h" #include "solver.h" class iz3base; struct interpolation_options_struct { stl_ext::hash_map map; public: void set(const std::string &name, const std::string &value){ map[name] = value; } void apply(iz3base &b); }; /** This object is thrown if a tree interpolation problem is mal-formed */ struct iz3_bad_tree { }; /** This object is thrown when iz3 fails due to an incompleteness in the secondary solver. */ struct iz3_incompleteness { }; typedef interpolation_options_struct *interpolation_options; /* Compute an interpolant from a proof. This version uses the parents vector representation, for compatibility with the old API. */ void iz3interpolate(ast_manager &_m_manager, ast *proof, const ptr_vector &cnsts, const ::vector &parents, ptr_vector &interps, const ptr_vector &theory, interpolation_options_struct * options = 0); /* Same as above, but each constraint is a vector of formulas. */ void iz3interpolate(ast_manager &_m_manager, ast *proof, const vector > &cnsts, const ::vector &parents, ptr_vector &interps, const ptr_vector &theory, interpolation_options_struct * options = 0); /* Compute an interpolant from a proof. This version uses the ast representation, for compatibility with the new API. Here, cnsts is a vector of all the assertions in the proof. This can be over-approximated by the set of all assertions in the solver. However, if it is empty it will be reconstructed from the proof, so it can be considered a hint. */ void iz3interpolate(ast_manager &_m_manager, ast *proof, const ptr_vector &cnsts, ast *tree, ptr_vector &interps, interpolation_options_struct * options); /* Compute an interpolant from an ast representing an interpolation problem, if unsat, else return a model (if enabled). Uses the given solver to produce the proof/model. Also returns a vector of the constraints in the problem, helpful for checking correctness. */ lbool iz3interpolate(ast_manager &_m_manager, solver &s, ast *tree, ptr_vector &cnsts, ptr_vector &interps, model_ref &m, interpolation_options_struct * options); #endif z3-4.4.0/src/interp/iz3secondary.h0000755000175000017500000000110512540347414016523 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3secondary Abstract: Interface for secondary provers. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef IZ3SECONDARY_H #define IZ3SECONDARY_H /** Interface class for secondary provers. */ #include "iz3base.h" #include class iz3secondary : public iz3mgr { public: virtual int interpolate(const std::vector &frames, std::vector &interpolants) = 0; virtual ~iz3secondary(){} protected: iz3secondary(const iz3mgr &mgr) : iz3mgr(mgr) {} }; #endif z3-4.4.0/src/interp/iz3hash.h0000644000175000017500000003025612540347414015465 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3hash.h Abstract: Simple implementation of bucket-list hash tables conforming to SGI hash_map and hash_set interfaces. Just enough members are implemented to support iz3 and duality. iz3 and duality need this package because they assume that insert preserves iterators and references to elements, which is not true of the hashtable packages in util. This package lives in namespace hash_space. Specializations of class "hash" should be made in this namespace. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef IZ3_HASH_H #define IZ3_HASH_H #ifdef _WINDOWS #pragma warning(disable:4267) #endif #include #include #include #include "hash.h" #define stl_ext hash_space namespace hash_space { template class hash {}; template <> class hash { public: size_t operator()(const int &s) const { return s; } }; template <> class hash { public: size_t operator()(const std::string &s) const { return string_hash(s.c_str(), s.size(), 0); } }; template <> class hash > { public: size_t operator()(const std::pair &p) const { return p.first + p.second; } }; template class hash > { public: size_t operator()(const std::pair &p) const { return (size_t)p.first + (size_t)p.second; } }; enum { num_primes = 29 }; static const unsigned long primes[num_primes] = { 7ul, 53ul, 97ul, 193ul, 389ul, 769ul, 1543ul, 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul, 1610612741ul, 3221225473ul, 4294967291ul }; inline unsigned long next_prime(unsigned long n) { const unsigned long* to = primes + (int)num_primes; for(const unsigned long* p = primes; p < to; p++) if(*p >= n) return *p; return primes[num_primes-1]; } template class hashtable { public: typedef Value &reference; typedef const Value &const_reference; struct Entry { Entry* next; Value val; Entry(const Value &_val) : val(_val) {next = 0;} }; struct iterator { Entry* ent; hashtable* tab; typedef std::forward_iterator_tag iterator_category; typedef Value value_type; typedef std::ptrdiff_t difference_type; typedef size_t size_type; typedef Value& reference; typedef Value* pointer; iterator(Entry* _ent, hashtable* _tab) : ent(_ent), tab(_tab) { } iterator() { } Value &operator*() const { return ent->val; } Value *operator->() const { return &(operator*()); } iterator &operator++() { Entry *old = ent; ent = ent->next; if (!ent) { size_t bucket = tab->get_bucket(old->val); while (!ent && ++bucket < tab->buckets.size()) ent = tab->buckets[bucket]; } return *this; } iterator operator++(int) { iterator tmp = *this; operator++(); return tmp; } bool operator==(const iterator& it) const { return ent == it.ent; } bool operator!=(const iterator& it) const { return ent != it.ent; } }; struct const_iterator { const Entry* ent; const hashtable* tab; typedef std::forward_iterator_tag iterator_category; typedef Value value_type; typedef std::ptrdiff_t difference_type; typedef size_t size_type; typedef const Value& reference; typedef const Value* pointer; const_iterator(const Entry* _ent, const hashtable* _tab) : ent(_ent), tab(_tab) { } const_iterator() { } const Value &operator*() const { return ent->val; } const Value *operator->() const { return &(operator*()); } const_iterator &operator++() { Entry *old = ent; ent = ent->next; if (!ent) { size_t bucket = tab->get_bucket(old->val); while (!ent && ++bucket < tab->buckets.size()) ent = tab->buckets[bucket]; } return *this; } const_iterator operator++(int) { const_iterator tmp = *this; operator++(); return tmp; } bool operator==(const const_iterator& it) const { return ent == it.ent; } bool operator!=(const const_iterator& it) const { return ent != it.ent; } }; private: typedef std::vector Table; Table buckets; size_t entries; HashFun hash_fun ; GetKey get_key; KeyEqFun key_eq_fun; public: hashtable(size_t init_size) : buckets(init_size,(Entry *)0) { entries = 0; } hashtable(const hashtable& other) { dup(other); } hashtable& operator= (const hashtable& other) { if (&other != this) dup(other); return *this; } ~hashtable() { clear(); } size_t size() const { return entries; } bool empty() const { return size() == 0; } void swap(hashtable& other) { buckets.swap(other.buckets); std::swap(entries, other.entries); } iterator begin() { for (size_t i = 0; i < buckets.size(); ++i) if (buckets[i]) return iterator(buckets[i], this); return end(); } iterator end() { return iterator(0, this); } const_iterator begin() const { for (size_t i = 0; i < buckets.size(); ++i) if (buckets[i]) return const_iterator(buckets[i], this); return end(); } const_iterator end() const { return const_iterator(0, this); } size_t get_bucket(const Value& val, size_t n) const { return hash_fun(get_key(val)) % n; } size_t get_key_bucket(const Key& key) const { return hash_fun(key) % buckets.size(); } size_t get_bucket(const Value& val) const { return get_bucket(val,buckets.size()); } Entry *lookup(const Value& val, bool ins = false) { resize(entries + 1); size_t n = get_bucket(val); Entry* from = buckets[n]; for (Entry* ent = from; ent; ent = ent->next) if (key_eq_fun(get_key(ent->val), get_key(val))) return ent; if(!ins) return 0; Entry* tmp = new Entry(val); tmp->next = from; buckets[n] = tmp; ++entries; return tmp; } Entry *lookup_key(const Key& key) const { size_t n = get_key_bucket(key); Entry* from = buckets[n]; for (Entry* ent = from; ent; ent = ent->next) if (key_eq_fun(get_key(ent->val), key)) return ent; return 0; } const_iterator find(const Key& key) const { return const_iterator(lookup_key(key),this); } iterator find(const Key& key) { return iterator(lookup_key(key),this); } std::pair insert(const Value& val){ size_t old_entries = entries; Entry *ent = lookup(val,true); return std::pair(iterator(ent,this),entries > old_entries); } iterator insert(const iterator &it, const Value& val){ Entry *ent = lookup(val,true); return iterator(ent,this); } size_t erase(const Key& key) { Entry** p = &(buckets[get_key_bucket(key)]); size_t count = 0; while(*p){ Entry *q = *p; if (key_eq_fun(get_key(q->val), key)) { ++count; *p = q->next; delete q; } else p = &(q->next); } entries -= count; return count; } void resize(size_t new_size) { const size_t old_n = buckets.size(); if (new_size <= old_n) return; const size_t n = next_prime(new_size); if (n <= old_n) return; Table tmp(n, (Entry*)(0)); for (size_t i = 0; i < old_n; ++i) { Entry* ent = buckets[i]; while (ent) { size_t new_bucket = get_bucket(ent->val, n); buckets[i] = ent->next; ent->next = tmp[new_bucket]; tmp[new_bucket] = ent; ent = buckets[i]; } } buckets.swap(tmp); } void clear() { for (size_t i = 0; i < buckets.size(); ++i) { for (Entry* ent = buckets[i]; ent != 0;) { Entry* next = ent->next; delete ent; ent = next; } buckets[i] = 0; } entries = 0; } void dup(const hashtable& other) { buckets.resize(other.buckets.size()); for (size_t i = 0; i < other.buckets.size(); ++i) { Entry** to = &buckets[i]; for (Entry* from = other.buckets[i]; from; from = from->next) to = &((*to = new Entry(from->val))->next); } entries = other.entries; } }; template class equal { public: bool operator()(const T& x, const T &y) const { return x == y; } }; template class identity { public: const T &operator()(const T &x) const { return x; } }; template class proj1 { public: const T &operator()(const std::pair &x) const { return x.first; } }; template , class EqFun = equal > class hash_set : public hashtable,EqFun> { public: typedef Element value_type; hash_set() : hashtable,EqFun>(7) {} }; template , class EqFun = equal > class hash_map : public hashtable,Key,HashFun,proj1,EqFun> { public: hash_map() : hashtable,Key,HashFun,proj1,EqFun>(7) {} Value &operator[](const Key& key) { std::pair kvp(key,Value()); return hashtable,Key,HashFun,proj1,EqFun>:: lookup(kvp,true)->val.second; } }; } #endif z3-4.4.0/src/interp/iz3translate.h0000755000175000017500000000256412540347414016543 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3translate.h Abstract: Interface for proof translations from Z3 proofs to interpolatable proofs. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef IZ3TRANSLATION_H #define IZ3TRANSLATION_H #include "iz3proof.h" #include "iz3secondary.h" // This is a interface class for translation from Z3 proof terms to // an interpolatable proof class iz3translation : public iz3base { public: virtual iz3proof::node translate(ast, iz3proof &) = 0; virtual ast quantify(ast e, const range &rng){return e;} virtual ~iz3translation(){} /** This is thrown when the proof cannot be translated. */ struct unsupported { }; static iz3translation *create(iz3mgr &mgr, iz3secondary *secondary, const std::vector > &frames, const std::vector &parents, const std::vector &theory); protected: iz3translation(iz3mgr &mgr, const std::vector > &_cnsts, const std::vector &_parents, const std::vector &_theory) : iz3base(mgr,_cnsts,_parents,_theory) {} }; //#define IZ3_TRANSLATE_DIRECT2 #ifdef _FOCI2 #define IZ3_TRANSLATE_DIRECT #else #define IZ3_TRANSLATE_FULL #endif #endif z3-4.4.0/src/interp/iz3interp.cpp0000755000175000017500000004446012540347414016403 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3interp.cpp Abstract: Interpolation based on proof translation. Author: Ken McMillan (kenmcmil) Revision History: --*/ /* Copyright 2011 Microsoft Research. */ #ifdef _WINDOWS #pragma warning(disable:4996) #pragma warning(disable:4800) #pragma warning(disable:4267) #pragma warning(disable:4101) #endif #include #include #include #include #include #include #include #include "iz3profiling.h" #include "iz3translate.h" #include "iz3foci.h" #include "iz3proof.h" #include "iz3hash.h" #include "iz3interp.h" #include"scoped_proof.h" using namespace stl_ext; #if 1 struct frame_reducer : public iz3mgr { int frames; hash_map frame_map; std::vector assertions_map; std::vector orig_parents_copy; std::vector used_frames; frame_reducer(const iz3mgr &other) : iz3mgr(other) {} void get_proof_assumptions_rec(z3pf proof, hash_set &memo, std::vector &used_frames){ if(memo.find(proof) != memo.end())return; memo.insert(proof); pfrule dk = pr(proof); if(dk == PR_ASSERTED){ ast con = conc(proof); if(frame_map.find(con) != frame_map.end()){ // false for theory facts int frame = frame_map[con]; used_frames[frame] = true; } } else { unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ z3pf arg = prem(proof,i); get_proof_assumptions_rec(arg,memo,used_frames); } } } void get_frames(const std::vector >&z3_preds, const std::vector &orig_parents, std::vector >&assertions, std::vector &parents, z3pf proof){ frames = z3_preds.size(); orig_parents_copy = orig_parents; for(unsigned i = 0; i < z3_preds.size(); i++) for(unsigned j = 0; j < z3_preds[i].size(); j++) frame_map[z3_preds[i][j]] = i; used_frames.resize(frames); hash_set memo; get_proof_assumptions_rec(proof,memo,used_frames); std::vector assertions_back_map(frames); // if multiple children of a tree node are used, we can't delete it std::vector used_children; for(int i = 0; i < frames; i++) used_children.push_back(0); for(int i = 0; i < frames; i++) if(orig_parents[i] != SHRT_MAX) if(used_frames[i] || used_children[i]){ if(used_children[i] > 1) used_frames[i] = true; used_children[orig_parents[i]]++; } for(unsigned i = 0; i < z3_preds.size(); i++) if(used_frames[i] || i == z3_preds.size() - 1){ assertions.push_back(z3_preds[i]); assertions_map.push_back(i); assertions_back_map[i] = assertions.size() - 1; } if(orig_parents.size()){ parents.resize(assertions.size()); for(unsigned i = 0; i < assertions.size(); i++){ int p = orig_parents[assertions_map[i]]; while(p != SHRT_MAX && !used_frames[p]) p = orig_parents[p]; parents[i] = p == SHRT_MAX ? p : assertions_back_map[p]; } } // std::cout << "used frames = " << frames << "\n"; } void fix_interpolants(std::vector &interpolants){ std::vector unfixed = interpolants; interpolants.resize(frames - 1); for(int i = 0; i < frames - 1; i++) interpolants[i] = mk_true(); for(unsigned i = 0; i < unfixed.size(); i++) interpolants[assertions_map[i]] = unfixed[i]; for(int i = 0; i < frames-2; i++){ int p = orig_parents_copy.size() == 0 ? i+1 : orig_parents_copy[i]; if(p < frames - 1 && !used_frames[p]) interpolants[p] = mk_and(interpolants[i],interpolants[p]); } } }; #else struct frame_reducer { frame_reducer(context _ctx){ } void get_frames(const std::vector &z3_preds, const std::vector &orig_parents, std::vector &assertions, std::vector &parents, ast proof){ assertions = z3_preds; parents = orig_parents; } void fix_interpolants(std::vector &interpolants){ } }; #endif #if 0 static lbool test_secondary(context ctx, int num, ast *cnsts, ast *interps, int *parents = 0 ){ iz3secondary *sp = iz3foci::create(ctx,num,parents); std::vector frames(num), interpolants(num-1); std::copy(cnsts,cnsts+num,frames.begin()); int res = sp->interpolate(frames,interpolants); if(res == 0) std::copy(interpolants.begin(),interpolants.end(),interps); return res ? L_TRUE : L_FALSE; } #endif template struct killme { T *p; killme(){p = 0;} void set(T *_p) {p = _p;} ~killme(){ if(p) delete p; } }; class iz3interp : public iz3base { public: killme sp_killer; killme tr_killer; bool is_linear(std::vector &parents){ for(int i = 0; i < ((int)parents.size())-1; i++) if(parents[i] != i+1) return false; return true; } void test_secondary(const std::vector &cnsts, const std::vector &parents, std::vector &interps ){ int num = cnsts.size(); iz3secondary *sp = iz3foci::create(this,num,(int *)(parents.empty()?0:&parents[0])); int res = sp->interpolate(cnsts, interps); if(res != 0) throw "secondary failed"; } void proof_to_interpolant(z3pf proof, const std::vector > &cnsts, const std::vector &parents, std::vector &interps, const std::vector &theory, interpolation_options_struct *options = 0 ){ #if 0 test_secondary(cnsts,parents,interps); return; #endif profiling::timer_start("Interpolation prep"); // get rid of frames not used in proof std::vector > cnsts_vec; std::vector parents_vec; frame_reducer fr(*this); fr.get_frames(cnsts,parents,cnsts_vec,parents_vec,proof); int num = cnsts_vec.size(); std::vector interps_vec(num-1); // if this is really a sequence problem, we can make it easier if(is_linear(parents_vec)) parents_vec.clear(); // create a secondary prover iz3secondary *sp = iz3foci::create(this,num,parents_vec.empty()?0:&parents_vec[0]); sp_killer.set(sp); // kill this on exit #define BINARY_INTERPOLATION #ifndef BINARY_INTERPOLATION // create a translator iz3translation *tr = iz3translation::create(*this,sp,cnsts_vec,parents_vec,theory); tr_killer.set(tr); // set the translation options, if needed if(options) for(hash_map::iterator it = options->map.begin(), en = options->map.end(); it != en; ++it) tr->set_option(it->first, it->second); // create a proof object to hold the translation iz3proof pf(tr); profiling::timer_stop("Interpolation prep"); // translate into an interpolatable proof profiling::timer_start("Proof translation"); tr->translate(proof,pf); profiling::timer_stop("Proof translation"); // translate the proof into interpolants profiling::timer_start("Proof interpolation"); for(int i = 0; i < num-1; i++){ interps_vec[i] = pf.interpolate(tr->range_downward(i),tr->weak_mode()); interps_vec[i] = tr->quantify(interps_vec[i],tr->range_downward(i)); } profiling::timer_stop("Proof interpolation"); #else iz3base the_base(*this,cnsts_vec,parents_vec,theory); profiling::timer_stop("Interpolation prep"); for(int i = 0; i < num-1; i++){ range rng = the_base.range_downward(i); std::vector > cnsts_vec_vec(2); for(unsigned j = 0; j < cnsts_vec.size(); j++){ bool is_A = the_base.in_range(j,rng); for(unsigned k = 0; k < cnsts_vec[j].size(); k++) cnsts_vec_vec[is_A ? 0 : 1].push_back(cnsts_vec[j][k]); } killme tr_killer_i; iz3translation *tr = iz3translation::create(*this,sp,cnsts_vec_vec,std::vector(),theory); tr_killer_i.set(tr); // set the translation options, if needed if(options) for(hash_map::iterator it = options->map.begin(), en = options->map.end(); it != en; ++it) tr->set_option(it->first, it->second); // create a proof object to hold the translation iz3proof pf(tr); // translate into an interpolatable proof profiling::timer_start("Proof translation"); tr->translate(proof,pf); profiling::timer_stop("Proof translation"); // translate the proof into interpolants profiling::timer_start("Proof interpolation"); interps_vec[i] = pf.interpolate(tr->range_downward(0),tr->weak_mode()); interps_vec[i] = tr->quantify(interps_vec[i],tr->range_downward(0)); profiling::timer_stop("Proof interpolation"); } #endif // put back in the removed frames fr.fix_interpolants(interps_vec); interps = interps_vec; } void proof_to_interpolant(z3pf proof, std::vector &cnsts, const std::vector &parents, std::vector &interps, const std::vector &theory, interpolation_options_struct *options = 0 ){ std::vector > cnsts_vec(cnsts.size()); for(unsigned i = 0; i < cnsts.size(); i++) cnsts_vec[i].push_back(cnsts[i]); proof_to_interpolant(proof,cnsts_vec,parents,interps,theory,options); } // same as above, but represents the tree using an ast void proof_to_interpolant(const z3pf &proof, const std::vector &_cnsts, const ast &tree, std::vector &interps, interpolation_options_struct *options = 0 ){ std::vector pos_map; // convert to the parents vector representation to_parents_vec_representation(_cnsts, tree, cnsts, parents, theory, pos_map); //use the parents vector representation to compute interpolant proof_to_interpolant(proof,cnsts,parents,interps,theory,options); // get the interps for the tree positions std::vector _interps = interps; interps.resize(pos_map.size()); for(unsigned i = 0; i < pos_map.size(); i++){ unsigned j = pos_map[i]; interps[i] = j < _interps.size() ? _interps[j] : mk_false(); } } bool has_interp(hash_map &memo, const ast &t){ if(memo.find(t) != memo.end()) return memo[t]; bool res = false; if(op(t) == Interp) res = true; else if(op(t) == And){ int nargs = num_args(t); for(int i = 0; i < nargs; i++) res |= has_interp(memo, arg(t,i)); } memo[t] = res; return res; } void collect_conjuncts(std::vector &cnsts, hash_map &memo, const ast &t){ if(!has_interp(memo,t)) cnsts.push_back(t); else { int nargs = num_args(t); for(int i = 0; i < nargs; i++) collect_conjuncts(cnsts, memo, arg(t,i)); } } void assert_conjuncts(solver &s, std::vector &cnsts, const ast &t){ hash_map memo; collect_conjuncts(cnsts,memo,t); for(unsigned i = 0; i < cnsts.size(); i++) s.assert_expr(to_expr(cnsts[i].raw())); } void get_proof_assumptions(z3pf proof, std::vector &cnsts, hash_set &memo){ if(memo.find(proof) != memo.end())return; memo.insert(proof); pfrule dk = pr(proof); if(dk == PR_ASSERTED) cnsts.push_back(conc(proof)); else { unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ z3pf arg = prem(proof,i); get_proof_assumptions(arg,cnsts,memo); } } } iz3interp(ast_manager &_m_manager) : iz3base(_m_manager) {} }; void iz3interpolate(ast_manager &_m_manager, ast *proof, const ptr_vector &cnsts, const ::vector &parents, ptr_vector &interps, const ptr_vector &theory, interpolation_options_struct * options) { iz3interp itp(_m_manager); if(options) options->apply(itp); std::vector _cnsts(cnsts.size()); std::vector _parents(parents.size()); std::vector _interps; std::vector _theory(theory.size()); for(unsigned i = 0; i < cnsts.size(); i++) _cnsts[i] = itp.cook(cnsts[i]); for(unsigned i = 0; i < parents.size(); i++) _parents[i] = parents[i]; for(unsigned i = 0; i < theory.size(); i++) _theory[i] = itp.cook(theory[i]); iz3mgr::ast _proof = itp.cook(proof); itp.proof_to_interpolant(_proof,_cnsts,_parents,_interps,_theory,options); interps.resize(_interps.size()); for(unsigned i = 0; i < interps.size(); i++) interps[i] = itp.uncook(_interps[i]); } void iz3interpolate(ast_manager &_m_manager, ast *proof, const ::vector > &cnsts, const ::vector &parents, ptr_vector &interps, const ptr_vector &theory, interpolation_options_struct * options) { iz3interp itp(_m_manager); if(options) options->apply(itp); std::vector > _cnsts(cnsts.size()); std::vector _parents(parents.size()); std::vector _interps; std::vector _theory(theory.size()); for(unsigned i = 0; i < cnsts.size(); i++) for(unsigned j = 0; j < cnsts[i].size(); j++) _cnsts[i].push_back(itp.cook(cnsts[i][j])); for(unsigned i = 0; i < parents.size(); i++) _parents[i] = parents[i]; for(unsigned i = 0; i < theory.size(); i++) _theory[i] = itp.cook(theory[i]); iz3mgr::ast _proof = itp.cook(proof); itp.proof_to_interpolant(_proof,_cnsts,_parents,_interps,_theory,options); interps.resize(_interps.size()); for(unsigned i = 0; i < interps.size(); i++) interps[i] = itp.uncook(_interps[i]); } void iz3interpolate(ast_manager &_m_manager, ast *proof, const ptr_vector &cnsts, ast *tree, ptr_vector &interps, interpolation_options_struct * options) { iz3interp itp(_m_manager); if(options) options->apply(itp); std::vector _cnsts(cnsts.size()); std::vector _interps; for(unsigned i = 0; i < cnsts.size(); i++) _cnsts[i] = itp.cook(cnsts[i]); iz3mgr::ast _proof = itp.cook(proof); iz3mgr::ast _tree = itp.cook(tree); // if consts isn't provided, we can reconstruct it if(_cnsts.empty()){ hash_set memo; itp.get_proof_assumptions(_proof,_cnsts,memo); } itp.proof_to_interpolant(_proof,_cnsts,_tree,_interps,options); interps.resize(_interps.size()); for(unsigned i = 0; i < interps.size(); i++) interps[i] = itp.uncook(_interps[i]); } lbool iz3interpolate(ast_manager &_m_manager, solver &s, ast *tree, ptr_vector &cnsts, ptr_vector &interps, model_ref &m, interpolation_options_struct * options) { iz3interp itp(_m_manager); if(options) options->apply(itp); iz3mgr::ast _tree = itp.cook(tree); std::vector _cnsts; itp.assert_conjuncts(s,_cnsts,_tree); profiling::timer_start("solving"); lbool res = s.check_sat(0,0); profiling::timer_stop("solving"); if(res == l_false){ ast *proof = s.get_proof(); iz3mgr::ast _proof = itp.cook(proof); std::vector _interps; itp.proof_to_interpolant(_proof,_cnsts,_tree,_interps,options); interps.resize(_interps.size()); for(unsigned i = 0; i < interps.size(); i++) interps[i] = itp.uncook(_interps[i]); } else if(m){ s.get_model(m); } cnsts.resize(_cnsts.size()); for(unsigned i = 0; i < cnsts.size(); i++) cnsts[i] = itp.uncook(_cnsts[i]); return res; } void interpolation_options_struct::apply(iz3base &b){ for(stl_ext::hash_map::iterator it = map.begin(), en = map.end(); it != en; ++it) b.set_option((*it).first,(*it).second); } // On linux and mac, unlimit stack space so we get recursion #if defined(_WINDOWS) || defined(_CYGWIN) #else #include #include class iz3stack_unlimiter { public: iz3stack_unlimiter() { struct rlimit rl = {RLIM_INFINITY, RLIM_INFINITY}; setrlimit(RLIMIT_STACK, &rl); // nothing to be done if above fails } }; // initializing this will unlimit stack iz3stack_unlimiter the_iz3stack_unlimiter; #endif z3-4.4.0/src/interp/iz3base.h0000755000175000017500000001275712540347414015465 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3base.h Abstract: Base class for interpolators. Includes an AST manager and a scoping object as bases. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef IZ3BASE_H #define IZ3BASE_H #include "iz3mgr.h" #include "iz3scopes.h" namespace hash_space { template <> class hash { public: size_t operator()(func_decl * const &s) const { return (size_t) s; } }; } /* Base class for interpolators. Includes an AST manager and a scoping object as bases. */ class iz3base : public iz3mgr, public scopes { public: /** Get the range in which an expression occurs. This is the smallest subtree containing all occurrences of the expression. */ range &ast_range(ast); /** Get the scope of an expression. This is the set of tree nodes in which all of the expression's symbols are in scope. */ range &ast_scope(ast); /** Get the range of a symbol. This is the smallest subtree containing all occurrences of the symbol. */ range &sym_range(symb); /** Is an expression local (in scope in some frame)? */ bool is_local(ast node){ return !range_is_empty(ast_scope(node)); } /** Simplify an expression */ ast simplify(ast); /** Constructor */ iz3base(ast_manager &_m_manager, const std::vector &_cnsts, const std::vector &_parents, const std::vector &_theory) : iz3mgr(_m_manager), scopes(_parents) { initialize(_cnsts,_parents,_theory); weak = false; } iz3base(const iz3mgr& other, const std::vector &_cnsts, const std::vector &_parents, const std::vector &_theory) : iz3mgr(other), scopes(_parents) { initialize(_cnsts,_parents,_theory); weak = false; } iz3base(const iz3mgr& other, const std::vector > &_cnsts, const std::vector &_parents, const std::vector &_theory) : iz3mgr(other), scopes(_parents) { initialize(_cnsts,_parents,_theory); weak = false; } iz3base(const iz3mgr& other) : iz3mgr(other), scopes() { weak = false; } /* Set our options */ void set_option(const std::string &name, const std::string &value){ if(name == "weak" && value == "1") weak = true; } /* Are we doing weak interpolants? */ bool weak_mode(){return weak;} /** Print interpolation problem to an SMTLIB format file */ void print(const std::string &filename); /** Check correctness of a solutino to this problem. */ void check_interp(const std::vector &itps, std::vector &theory); /** For convenience -- is this formula SAT? */ bool is_sat(const std::vector &consts, ast &_proof, std::vector &vars); /** Interpolator for clauses, to be implemented */ virtual void interpolate_clause(std::vector &lits, std::vector &itps){ throw "no interpolator"; } ast get_proof_check_assump(range &rng){ std::vector cs(theory); cs.push_back(cnsts[rng.hi]); return make(And,cs); } int frame_of_assertion(const ast &ass){ stl_ext::hash_map::iterator it = frame_map.find(ass); if(it == frame_map.end()) throw "unknown assertion"; return it->second; } void to_parents_vec_representation(const std::vector &_cnsts, const ast &tree, std::vector &cnsts, std::vector &parents, std::vector &theory, std::vector &pos_map, bool merge = false ); protected: std::vector cnsts; std::vector theory; private: struct ranges { range rng; range scp; bool scope_computed; ranges(){scope_computed = false;} }; stl_ext::hash_map sym_range_hash; stl_ext::hash_map ast_ranges_hash; stl_ext::hash_map simplify_memo; stl_ext::hash_map frame_map; // map assertions to frames // int frames; // number of frames protected: void add_frame_range(int frame, ast t); private: void initialize(const std::vector &_parts, const std::vector &_parents, const std::vector &_theory); void initialize(const std::vector > &_parts, const std::vector &_parents, const std::vector &_theory); bool is_literal(ast n); void gather_conjuncts_rec(ast n, std::vector &conjuncts, stl_ext::hash_set &memo); void gather_conjuncts(ast n, std::vector &conjuncts); ast simplify_and(std::vector &conjuncts); ast simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map &memo, int depth); ast simplify_with_lit(ast n, ast lit); void find_children(const stl_ext::hash_set &cnsts_set, const ast &tree, std::vector &cnsts, std::vector &parents, std::vector &conjuncts, std::vector &children, std::vector &pos_map, bool merge ); bool weak; }; #endif z3-4.4.0/src/interp/iz3base.cpp0000755000175000017500000002774712540347414016025 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3base.cpp Abstract: Base class for interpolators. Includes an AST manager and a scoping object as bases. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifdef _WINDOWS #pragma warning(disable:4996) #pragma warning(disable:4800) #pragma warning(disable:4267) #pragma warning(disable:4101) #endif #include "iz3base.h" #include #include #include #include #include "solver.h" #include "../smt/smt_solver.h" using namespace stl_ext; iz3base::range &iz3base::ast_range(ast t){ return ast_ranges_hash[t].rng; } iz3base::range &iz3base::sym_range(symb d){ return sym_range_hash[d]; } void iz3base::add_frame_range(int frame, ast t){ range &rng = ast_range(t); if(!in_range(frame,rng)){ range_add(frame,rng); for(int i = 0, n = num_args(t); i < n; ++i) add_frame_range(frame,arg(t,i)); if(op(t) == Uninterpreted) range_add(frame,sym_range(sym(t))); } } #if 1 iz3base::range &iz3base::ast_scope(ast t){ ranges &rngs = ast_ranges_hash[t]; range &rng = rngs.scp; if(!rngs.scope_computed){ // not computed yet rng = range_full(); for(int i = 0, n = num_args(t); i < n; ++i) rng = range_glb(rng,ast_scope(arg(t,i))); if(op(t) == Uninterpreted) if(parents.empty() || num_args(t) == 0) // in tree mode, all function syms are global rng = range_glb(rng,sym_range(sym(t))); rngs.scope_computed = true; } return rng; } #else iz3base::range &iz3base::ast_scope(ast t){ ranges &rngs = ast_ranges_hash[t]; if(rngs.scope_computed) return rngs.scp; range rng = range_full(); for(int i = 0, n = num_args(t); i < n; ++i) rng = range_glb(rng,ast_scope(arg(t,i))); if(op(t) == Uninterpreted) if(parents.empty() || num_args(t) == 0) // in tree mode, all function syms are global rng = range_glb(rng,sym_range(sym(t))); rngs = ast_ranges_hash[t]; rngs.scope_computed = true; rngs.scp = rng; return rngs.scp; } #endif void iz3base::print(const std::string &filename){ ast t = make(And,cnsts); std::ofstream f(filename.c_str()); print_sat_problem(f,t); f.close(); } void iz3base::gather_conjuncts_rec(ast n, std::vector &conjuncts, stl_ext::hash_set &memo){ if(memo.find(n) == memo.end()){ memo.insert(n); if(op(n) == And){ int nargs = num_args(n); for(int i = 0; i < nargs; i++) gather_conjuncts_rec(arg(n,i),conjuncts,memo); } else conjuncts.push_back(n); } } void iz3base::gather_conjuncts(ast n, std::vector &conjuncts){ hash_set memo; gather_conjuncts_rec(n,conjuncts,memo); } bool iz3base::is_literal(ast n){ if(is_not(n))n = arg(n,0); if(is_true(n) || is_false(n)) return false; if(op(n) == And) return false; return true; } iz3base::ast iz3base::simplify_and(std::vector &conjuncts){ hash_set memo; for(unsigned i = 0; i < conjuncts.size(); i++){ if(is_false(conjuncts[i])) return conjuncts[i]; if(is_true(conjuncts[i]) || memo.find(conjuncts[i]) != memo.end()){ std::swap(conjuncts[i],conjuncts.back()); conjuncts.pop_back(); } else if(memo.find(mk_not(conjuncts[i])) != memo.end()) return mk_false(); // contradiction! else memo.insert(conjuncts[i]); } if(conjuncts.empty())return mk_true(); return make(And,conjuncts); } iz3base::ast iz3base::simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map &memo, int depth){ if(is_not(n))return mk_not(simplify_with_lit_rec(mk_not(n),lit,memo,depth)); if(n == lit) return mk_true(); ast not_lit = mk_not(lit); if(n == not_lit) return mk_false(); if(op(n) != And || depth <= 0) return n; std::pair foo(n,ast()); std::pair::iterator,bool> bar = memo.insert(foo); ast &res = bar.first->second; if(!bar.second) return res; int nargs = num_args(n); std::vector args(nargs); for(int i = 0; i < nargs; i++) args[i] = simplify_with_lit_rec(arg(n,i),lit,memo,depth-1); res = simplify_and(args); return res; } iz3base::ast iz3base::simplify_with_lit(ast n, ast lit){ hash_map memo; return simplify_with_lit_rec(n,lit,memo,1); } iz3base::ast iz3base::simplify(ast n){ if(is_not(n)) return mk_not(simplify(mk_not(n))); std::pair memo_obj(n,ast()); std::pair::iterator,bool> memo = simplify_memo.insert(memo_obj); ast &res = memo.first->second; if(!memo.second) return res; switch(op(n)){ case And: { std::vector conjuncts; gather_conjuncts(n,conjuncts); for(unsigned i = 0; i < conjuncts.size(); i++) conjuncts[i] = simplify(conjuncts[i]); #if 0 for(unsigned i = 0; i < conjuncts.size(); i++) if(is_literal(conjuncts[i])) for(unsigned j = 0; j < conjuncts.size(); j++) if(j != i) conjuncts[j] = simplify_with_lit(conjuncts[j],conjuncts[i]); #endif res = simplify_and(conjuncts); } break; case Equal: { ast x = arg(n,0); ast y = arg(n,1); if(ast_id(x) > ast_id(y)) std::swap(x,y); res = make(Equal,x,y); break; } default: res = n; } return res; } void iz3base::initialize(const std::vector &_parts, const std::vector &_parents, const std::vector &_theory){ cnsts = _parts; theory = _theory; for(unsigned i = 0; i < cnsts.size(); i++) add_frame_range(i, cnsts[i]); for(unsigned i = 0; i < _theory.size(); i++){ add_frame_range(SHRT_MIN, _theory[i]); add_frame_range(SHRT_MAX, _theory[i]); } for(unsigned i = 0; i < cnsts.size(); i++) frame_map[cnsts[i]] = i; for(unsigned i = 0; i < theory.size(); i++) frame_map[theory[i]] = INT_MAX; } void iz3base::initialize(const std::vector > &_parts, const std::vector &_parents, const std::vector &_theory){ cnsts.resize(_parts.size()); theory = _theory; for(unsigned i = 0; i < _parts.size(); i++) for(unsigned j = 0; j < _parts[i].size(); j++){ cnsts[i] = make(And,_parts[i]); add_frame_range(i, _parts[i][j]); frame_map[_parts[i][j]] = i; } for(unsigned i = 0; i < _theory.size(); i++){ add_frame_range(SHRT_MIN, _theory[i]); add_frame_range(SHRT_MAX, _theory[i]); frame_map[theory[i]] = INT_MAX; } } void iz3base::check_interp(const std::vector &itps, std::vector &theory){ #if 0 Z3_config config = Z3_mk_config(); Z3_context vctx = Z3_mk_context(config); int frames = cnsts.size(); std::vector foocnsts(cnsts); for(unsigned i = 0; i < frames; i++) foocnsts[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),cnsts[i]); Z3_write_interpolation_problem(ctx,frames,&foocnsts[0],0, "temp_lemma.smt", theory.size(), &theory[0]); int vframes,*vparents; Z3_ast *vcnsts; const char *verror; bool ok = Z3_read_interpolation_problem(vctx,&vframes,&vcnsts,0,"temp_lemma.smt",&verror); assert(ok); std::vector vvcnsts(vframes); std::copy(vcnsts,vcnsts+vframes,vvcnsts.begin()); std::vector vitps(itps.size()); for(unsigned i = 0; i < itps.size(); i++) vitps[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),itps[i]); Z3_write_interpolation_problem(ctx,itps.size(),&vitps[0],0,"temp_interp.smt"); int iframes,*iparents; Z3_ast *icnsts; const char *ierror; ok = Z3_read_interpolation_problem(vctx,&iframes,&icnsts,0,"temp_interp.smt",&ierror); assert(ok); const char *error = 0; bool iok = Z3_check_interpolant(vctx, frames, &vvcnsts[0], parents.size() ? &parents[0] : 0, icnsts, &error); assert(iok); #endif } bool iz3base::is_sat(const std::vector &q, ast &_proof, std::vector &vars){ params_ref p; p.set_bool("proof", true); // this is currently useless p.set_bool("model", true); p.set_bool("unsat_core", true); scoped_ptr sf = mk_smt_solver_factory(); ::solver *m_solver = (*sf)(m(), p, true, true, true, ::symbol::null); ::solver &s = *m_solver; for(unsigned i = 0; i < q.size(); i++) s.assert_expr(to_expr(q[i].raw())); lbool res = s.check_sat(0,0); if(res == l_false){ ::ast *proof = s.get_proof(); _proof = cook(proof); } else if(vars.size()) { model_ref(_m); s.get_model(_m); for(unsigned i = 0; i < vars.size(); i++){ expr_ref r(m()); _m.get()->eval(to_expr(vars[i].raw()),r,true); vars[i] = cook(r.get()); } } dealloc(m_solver); return res != l_false; } void iz3base::find_children(const stl_ext::hash_set &cnsts_set, const ast &tree, std::vector &cnsts, std::vector &parents, std::vector &conjuncts, std::vector &children, std::vector &pos_map, bool merge ){ std::vector my_children; std::vector my_conjuncts; if(op(tree) == Interp){ // if we've hit an interpolation position... find_children(cnsts_set,arg(tree,0),cnsts,parents,my_conjuncts,my_children,pos_map,merge); if(my_conjuncts.empty()) my_conjuncts.push_back(mk_true()); // need at least one conjunct int root = cnsts.size() + my_conjuncts.size() - 1; for(unsigned i = 0; i < my_conjuncts.size(); i++){ parents.push_back(root); cnsts.push_back(my_conjuncts[i]); } for(unsigned i = 0; i < my_children.size(); i++) parents[my_children[i]] = root; children.push_back(root); pos_map.push_back(root); } else { if(op(tree) == And){ int nargs = num_args(tree); for(int i = 0; i < nargs; i++) find_children(cnsts_set,arg(tree,i),cnsts,parents,my_conjuncts,my_children,pos_map,merge); } if(cnsts_set.find(tree) != cnsts_set.end()){ if(merge && !my_conjuncts.empty()) my_conjuncts.back() = mk_and(my_conjuncts.back(),tree); else my_conjuncts.push_back(tree); } for(unsigned i = 0; i < my_children.size(); i++) children.push_back(my_children[i]); for(unsigned i = 0; i < my_conjuncts.size(); i++) conjuncts.push_back(my_conjuncts[i]); } } void iz3base::to_parents_vec_representation(const std::vector &_cnsts, const ast &tree, std::vector &cnsts, std::vector &parents, std::vector &theory, std::vector &pos_map, bool merge ){ std::vector my_children; std::vector my_conjuncts; hash_set cnsts_set; for(unsigned i = 0; i < _cnsts.size(); i++) cnsts_set.insert(_cnsts[i]); ast _tree = (op(tree) != Interp) ? make(Interp,tree) : tree; find_children(cnsts_set,_tree,cnsts,parents,my_conjuncts,my_children,pos_map,merge); if(op(tree) != Interp) pos_map.pop_back(); parents[parents.size()-1] = SHRT_MAX; // rest of the constraints are the background theory hash_set used_set; for(unsigned i = 0; i < cnsts.size(); i++) used_set.insert(cnsts[i]); for(unsigned i = 0; i < _cnsts.size(); i++) if(used_set.find(_cnsts[i]) == used_set.end()) theory.push_back(_cnsts[i]); } z3-4.4.0/src/interp/iz3pp.cpp0000644000175000017500000001052612540347414015512 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: iz3pp.cpp Abstract: Pretty-print interpolation problems Author: Ken McMillan (kenmcmil) Revision History: --*/ /* Copyright 2011 Microsoft Research. */ #include #include #include #include #include #include #include #include "iz3mgr.h" #include "iz3pp.h" #include "func_decl_dependencies.h" #include"for_each_expr.h" #include"ast_smt_pp.h" #include"ast_smt2_pp.h" #include"expr_functors.h" #include"expr_abstract.h" using namespace stl_ext; // We promise not to use this for hash_map with range destructor namespace stl_ext { template <> class hash { public: size_t operator()(const expr *p) const { return (size_t) p; } }; } // TBD: algebraic data-types declarations will not be printed. class free_func_visitor { ast_manager& m; func_decl_set m_funcs; obj_hashtable m_sorts; public: free_func_visitor(ast_manager& m): m(m) {} void operator()(var * n) { } void operator()(app * n) { m_funcs.insert(n->get_decl()); class sort* s = m.get_sort(n); if (s->get_family_id() == null_family_id) { m_sorts.insert(s); } } void operator()(quantifier * n) { } func_decl_set& funcs() { return m_funcs; } obj_hashtable& sorts() { return m_sorts; } }; class iz3pp_helper : public iz3mgr { public: void print_tree(const ast &tree, hash_map &cnames, std::ostream &out){ hash_map::iterator foo = cnames.find(to_expr(tree.raw())); if(foo != cnames.end()){ symbol nm = foo->second; if (is_smt2_quoted_symbol(nm)) { out << mk_smt2_quoted_symbol(nm); } else { out << nm; } } else if(op(tree) == And){ out << "(and"; int nargs = num_args(tree); for(int i = 0; i < nargs; i++){ out << " "; print_tree(arg(tree,i), cnames, out); } out << ")"; } else if(op(tree) == Interp){ out << "(interp "; print_tree(arg(tree,0), cnames, out); out << ")"; } else throw iz3pp_bad_tree(); } iz3pp_helper(ast_manager &_m_manager) : iz3mgr(_m_manager) {} }; void iz3pp(ast_manager &m, const ptr_vector &cnsts_vec, expr *tree, std::ostream& out) { unsigned sz = cnsts_vec.size(); expr* const* cnsts = &cnsts_vec[0]; out << "(set-option :produce-interpolants true)\n"; free_func_visitor visitor(m); expr_mark visited; bool print_low_level = true; // m_params.print_low_level_smt2(); #define PP(_e_) if (print_low_level) out << mk_smt_pp(_e_, m); else ast_smt2_pp(out, _e_, env); smt2_pp_environment_dbg env(m); for (unsigned i = 0; i < sz; ++i) { expr* e = cnsts[i]; for_each_expr(visitor, visited, e); } // name all the constraints hash_map cnames; int ctr = 1; for(unsigned i = 0; i < sz; i++){ symbol nm; std::ostringstream s; s << "f!" << (ctr++); cnames[cnsts[i]] = symbol(s.str().c_str()); } func_decl_set &funcs = visitor.funcs(); func_decl_set::iterator it = funcs.begin(), end = funcs.end(); obj_hashtable& sorts = visitor.sorts(); obj_hashtable::iterator sit = sorts.begin(), send = sorts.end(); for (; sit != send; ++sit) { PP(*sit); } for (; it != end; ++it) { func_decl* f = *it; if(f->get_family_id() == null_family_id){ PP(f); out << "\n"; } } for (unsigned i = 0; i < sz; ++i) { out << "(assert "; expr* r = cnsts[i]; symbol nm = cnames[r]; out << "(! "; PP(r); out << " :named "; if (is_smt2_quoted_symbol(nm)) { out << mk_smt2_quoted_symbol(nm); } else { out << nm; } out << ")"; out << ")\n"; } out << "(check-sat)\n"; out << "(get-interpolant "; iz3pp_helper pp(m); pp.print_tree(pp.cook(tree),cnames,out); out << ")\n"; } z3-4.4.0/src/interp/iz3proof.h0000755000175000017500000001743412540347414015675 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3proof.h Abstract: This class defines a simple interpolating proof system. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef IZ3PROOF_H #define IZ3PROOF_H #include #include "iz3base.h" #include "iz3secondary.h" // #define CHECK_PROOFS /** This class defines a simple proof system. A proof is a dag consisting of "nodes". The children of each node are its "premises". Each node has a "conclusion" that is a clause, represented as a vector of literals. The literals are represented by abstract syntax trees. Operations on these, including computation of scopes are provided by iz3base. A proof can be interpolated, provided it is restricted to the rules Resolution, Assumption, Contra and Lemma, and that all clauses are strict (i.e., each literal in each clause is local). */ class iz3proof { public: /** The type of proof nodes (nodes in the derivation tree). */ typedef int node; /** Enumeration of proof rules. */ enum rule {Resolution,Assumption,Hypothesis,Theory,Axiom,Contra,Lemma,Reflexivity,Symmetry,Transitivity,Congruence,EqContra}; /** Interface to prover. */ typedef iz3base prover; /** Ast type. */ typedef prover::ast ast; /** Object thrown in case of a proof error. */ struct proof_error {}; /* Null proof node */ static const node null = -1; /** Make a resolution node with given pivot liter and premises. The conclusion of premise1 should contain the negation of the pivot literal, while the conclusion of premise2 should containe the pivot literal. */ node make_resolution(ast pivot, node premise1, node premise2); /** Make an assumption node. The given clause is assumed in the given frame. */ node make_assumption(int frame, const std::vector &assumption); /** Make a hypothesis node. If phi is the hypothesis, this is effectively phi |- phi. */ node make_hypothesis(ast hypothesis); /** Make a theory node. This can be any inference valid in the theory. */ node make_theory(const std::vector &conclusion, std::vector premises); /** Make an axiom node. The conclusion must be an instance of an axiom. */ node make_axiom(const std::vector &conclusion); /** Make a Contra node. This rule takes a derivation of the form Gamma |- False and produces |- \/~Gamma. */ node make_contra(node prem, const std::vector &conclusion); /** Make a lemma node. A lemma node must have an interpolation. */ node make_lemma(const std::vector &conclusion, const std::vector &interpolation); /** Make a Reflexivity node. This rule produces |- x = x */ node make_reflexivity(ast con); /** Make a Symmetry node. This takes a derivation of |- x = y and produces | y = x */ node make_symmetry(ast con, node prem); /** Make a transitivity node. This takes derivations of |- x = y and |- y = z produces | x = z */ node make_transitivity(ast con, node prem1, node prem2); /** Make a congruence node. This takes derivations of |- x_i = y_i and produces |- f(x_1,...,x_n) = f(y_1,...,y_n) */ node make_congruence(ast con, const std::vector &prems); /** Make an equality contradicition node. This takes |- x = y and |- !(x = y) and produces false. */ node make_eqcontra(node prem1, node prem2); /** Get the rule of a node in a proof. */ rule get_rule(node n){ return nodes[n].rl; } /** Get the pivot of a resolution node. */ ast get_pivot(node n){ return nodes[n].aux; } /** Get the frame of an assumption node. */ int get_frame(node n){ return nodes[n].frame; } /** Get the number of literals of the conclusion of a node. */ int get_num_conclusion_lits(node n){ return get_conclusion(n).size(); } /** Get the nth literal of the conclusion of a node. */ ast get_nth_conclusion_lit(node n, int i){ return get_conclusion(n)[i]; } /** Get the conclusion of a node. */ void get_conclusion(node n, std::vector &result){ result = get_conclusion(n); } /** Get the number of premises of a node. */ int get_num_premises(node n){ return nodes[n].premises.size(); } /** Get the nth premise of a node. */ int get_nth_premise(node n, int i){ return nodes[n].premises[i]; } /** Get all the premises of a node. */ void get_premises(node n, std::vector &result){ result = nodes[n].premises; } /** Create a new proof node, replacing the premises of an old one. */ node clone(node n, std::vector &premises){ if(premises == nodes[n].premises) return n; nodes.push_back(nodes[n]); nodes.back().premises = premises; return nodes.size()-1; } /** Copy a proof node from src */ node copy(iz3proof &src, node n); /** Resolve two lemmas on a given literal. */ node resolve_lemmas(ast pivot, node left, node right); /** Swap two proofs. */ void swap(iz3proof &other){ std::swap(pv,other.pv); nodes.swap(other.nodes); interps.swap(other.interps); } /** Compute an interpolant for a proof, where the "A" side is defined by the given range of frames. Parameter "weak", when true, uses different interpolation system that resutls in generally weaker interpolants. */ ast interpolate(const prover::range &_rng, bool weak = false #ifdef CHECK_PROOFS , Z3_ast assump = (Z3_ast)0, std::vector *parents = 0 #endif ); /** print proof node to a stream */ void print(std::ostream &s, node n); /** show proof node on stdout */ void show(node n); /** Construct a proof, with a given prover. */ iz3proof(prover *p){ pv = p; } /** Default constructor */ iz3proof(){pv = 0;} protected: struct node_struct { rule rl; ast aux; int frame; std::vector conclusion; std::vector premises; }; std::vector nodes; std::vector > interps; // interpolations of lemmas prover *pv; node make_node(){ nodes.push_back(node_struct()); return nodes.size()-1; } void resolve(ast pivot, std::vector &cls1, const std::vector &cls2); node copy_rec(stl_ext::hash_map &memo, iz3proof &src, node n); void interpolate_lemma(node_struct &n); // lazily compute the result of resolution // the node member "frame" indicates result is computed const std::vector &get_conclusion(node x){ node_struct &n = nodes[x]; if(n.rl == Resolution && !n.frame){ n.conclusion = get_conclusion(n.premises[0]); resolve(n.aux,n.conclusion,get_conclusion(n.premises[1])); n.frame = 1; } return n.conclusion; } prover::range rng; bool weak; stl_ext::hash_set b_lits; ast my_or(ast x, ast y); #ifdef CHECK_PROOFS std::vector child_interps; #endif bool pred_in_A(ast id); bool term_in_B(ast id); bool frame_in_A(int frame); bool lit_in_B(ast lit); ast get_A_lits(std::vector &cls); ast get_B_lits(std::vector &cls); void find_B_lits(); ast disj_of_set(std::set &s); void mk_or_factor(int p1, int p2, int i, std::vector &itps, std::vector > &disjs); void mk_and_factor(int p1, int p2, int i, std::vector &itps, std::vector > &disjs); void set_of_B_lits(std::vector &cls, std::set &res); void set_of_A_lits(std::vector &cls, std::set &res); }; #endif z3-4.4.0/src/interp/iz3checker.cpp0000755000175000017500000001560612540347414016506 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3checker.cpp Abstract: check correctness of interpolant Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifdef _WINDOWS #pragma warning(disable:4996) #pragma warning(disable:4800) #pragma warning(disable:4267) #pragma warning(disable:4101) #endif #include "iz3base.h" #include "iz3checker.h" #include #include #include #include #include #include #include using namespace stl_ext; struct iz3checker : iz3base { /* HACK: for tree interpolants, we assume that uninterpreted functions are global. This is because in the current state of the tree interpolation code, symbols that appear in sibling sub-trees have to be global, and we have no way to eliminate such function symbols. When tree interpoaltion is fixed, we can tree function symbols the same as constant symbols. */ bool is_tree; void support(const ast &t, std::set &res, hash_set &memo){ if(memo.find(t) != memo.end()) return; memo.insert(t); int nargs = num_args(t); for(int i = 0; i < nargs; i++) support(arg(t,i),res,memo); switch(op(t)){ case Uninterpreted: if(nargs == 0 || !is_tree) { std::string name = string_of_symbol(sym(t)); res.insert(name); } break; case Forall: case Exists: support(get_quantifier_body(t),res,memo); break; default:; } } bool check(solver *s, std::ostream &err, const std::vector &cnsts, const std::vector &parents, const std::vector &itp, const std::vector &theory){ is_tree = !parents.empty(); int num = cnsts.size(); std::vector > children(num); for(int i = 0; i < num-1; i++){ if(parents.size()) children[parents[i]].push_back(i); else children[i+1].push_back(i); } for(int i = 0; i < num; i++){ s->push(); for(unsigned j = 0; j < theory.size(); j++) s->assert_expr(to_expr(theory[j].raw())); s->assert_expr(to_expr(cnsts[i].raw())); std::vector &cs = children[i]; for(unsigned j = 0; j < cs.size(); j++) s->assert_expr(to_expr(itp[cs[j]].raw())); if(i != num-1) s->assert_expr(to_expr(mk_not(itp[i]).raw())); lbool result = s->check_sat(0,0); if(result != l_false){ err << "interpolant " << i << " is incorrect"; s->pop(1); for(unsigned j = 0; j < theory.size(); j++) s->assert_expr(to_expr(theory[j].raw())); for(unsigned j = 0; j < cnsts.size(); j++) if(in_range(j,range_downward(i))) s->assert_expr(to_expr(cnsts[j].raw())); if(i != num-1) s->assert_expr(to_expr(mk_not(itp[i]).raw())); lbool result = s->check_sat(0,0); if(result != l_false) err << "interpolant " << i << " is not implied by its downeard closurn"; return false; } s->pop(1); } std::vector > supports(num); for(int i = 0; i < num; i++){ hash_set memo; support(cnsts[i],supports[i],memo); } for(int i = 0; i < num-1; i++){ std::vector Bside(num); for(int j = num-1; j >= 0; j--) Bside[j] = j != i; for(int j = num-1; j >= 0; j--) if(!Bside[j]){ std::vector &cs = children[i]; for(unsigned k = 0; k < cs.size(); k++) Bside[cs[k]] = false; } std::set Asup, Bsup,common,Isup,bad; for(int j = num-1; j >= 0; j--){ std::set &side = Bside[j] ? Bsup : Asup; side.insert(supports[j].begin(),supports[j].end()); } std::set_intersection(Asup.begin(),Asup.end(),Bsup.begin(),Bsup.end(),std::inserter(common,common.begin())); { hash_set tmemo; for(unsigned j = 0; j < theory.size(); j++) support(theory[j],common,tmemo); // all theory symbols allowed in interps } hash_set memo; support(itp[i],Isup,memo); std::set_difference(Isup.begin(),Isup.end(),common.begin(),common.end(),std::inserter(bad,bad.begin())); if(!bad.empty()){ err << "bad symbols in interpolant " << i << ":"; std::copy(bad.begin(),bad.end(),std::ostream_iterator(err,",")); return false; } } return true; } bool check(solver *s, std::ostream &err, const std::vector &_cnsts, const ast &tree, const std::vector &itp){ std::vector pos_map; // convert to the parents vector representation to_parents_vec_representation(_cnsts, tree, cnsts, parents, theory, pos_map); //use the parents vector representation to compute interpolant return check(s,err,cnsts,parents,itp,theory); } iz3checker(ast_manager &_m) : iz3base(_m) { } iz3checker(iz3mgr &_m) : iz3base(_m) { } }; template std::vector to_std_vector(const ::vector &v){ std::vector _v(v.size()); for(unsigned i = 0; i < v.size(); i++) _v[i] = v[i]; return _v; } bool iz3check(ast_manager &_m_manager, solver *s, std::ostream &err, const ptr_vector &cnsts, const ::vector &parents, const ptr_vector &interps, const ptr_vector &theory) { iz3checker chk(_m_manager); return chk.check(s,err,chk.cook(cnsts),to_std_vector(parents),chk.cook(interps),chk.cook(theory)); } bool iz3check(iz3mgr &mgr, solver *s, std::ostream &err, const std::vector &cnsts, const std::vector &parents, const std::vector &interps, const std::vector &theory) { iz3checker chk(mgr); return chk.check(s,err,cnsts,parents,interps,theory); } bool iz3check(ast_manager &_m_manager, solver *s, std::ostream &err, const ptr_vector &_cnsts, ast *tree, const ptr_vector &interps) { iz3checker chk(_m_manager); return chk.check(s,err,chk.cook(_cnsts),chk.cook(tree),chk.cook(interps)); } z3-4.4.0/src/interp/iz3profiling.cpp0000755000175000017500000000646012540347414017071 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3profiling.h Abstract: Some routines for measuring performance. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifdef _WINDOWS #pragma warning(disable:4996) #pragma warning(disable:4800) #pragma warning(disable:4267) #pragma warning(disable:4101) #endif #include "iz3profiling.h" #include #include #include #include #include #include "stopwatch.h" // FIXME fill in these stubs #define clock_t double static double current_time() { static stopwatch sw; static bool started = false; if(!started){ sw.start(); started = true; } return sw.get_current_seconds(); } static void output_time(std::ostream &os, clock_t time){ os << time; } namespace profiling { void show_time(){ output_time(std::cout,current_time()); std::cout << "\n"; } typedef std::map nmap; struct node { std::string name; clock_t time; clock_t start_time; nmap sub; struct node *parent; node(); } top; node::node(){ time = 0; parent = 0; } struct node *current; struct init { init(){ top.name = "TOTAL"; current = ⊤ } } initializer; struct time_entry { clock_t t; time_entry(){t = 0;}; void add(clock_t incr){t += incr;} }; struct ltstr { bool operator()(const char* s1, const char* s2) const { return strcmp(s1, s2) < 0; } }; typedef std::map tmap; static std::ostream *pfs; void print_node(node &top, int indent, tmap &totals){ for(int i = 0; i < indent; i++) (*pfs) << " "; (*pfs) << top.name; int dots = 70 - 2 * indent - top.name.size(); for(int i = 0; i second,indent+1,totals); } void print(std::ostream &os) { pfs = &os; top.time = 0; for(nmap::iterator it = top.sub.begin(); it != top.sub.end(); it++) top.time += it->second.time; tmap totals; print_node(top,0,totals); (*pfs) << "TOTALS:" << std::endl; for(tmap::iterator it = totals.begin(); it != totals.end(); it++){ (*pfs) << (it->first) << " "; output_time(*pfs, it->second.t); (*pfs) << std::endl; } } void timer_start(const char *name){ node &child = current->sub[name]; if(child.name.empty()){ // a new node child.parent = current; child.name = name; } child.start_time = current_time(); current = &child; } void timer_stop(const char *name){ if(current->name != name || !current->parent){ std::cerr << "imbalanced timer_start and timer_stop"; exit(1); } current->time += (current_time() - current->start_time); current = current->parent; } } z3-4.4.0/src/interp/foci2.h0000755000175000017500000000354212540347414015117 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: foci2.h Abstract: An interface class for foci2. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef FOCI2_H #define FOCI2_H #include #include #ifdef _WINDOWS #define FOCI2_EXPORT __declspec(dllexport) #else #define FOCI2_EXPORT __attribute__ ((visibility ("default"))) #endif class foci2 { public: virtual ~foci2(){} typedef int ast; typedef int symb; /** Built-in operators */ enum ops { And = 0, Or, Not, Iff, Ite, Equal, Plus, Times, Floor, Leq, Div, Bool, Int, Array, Tsym, Fsym, Forall, Exists, Distinct, LastOp }; virtual symb mk_func(const std::string &s) = 0; virtual symb mk_pred(const std::string &s) = 0; virtual ast mk_op(ops op, const std::vector args) = 0; virtual ast mk_op(ops op, ast) = 0; virtual ast mk_op(ops op, ast, ast) = 0; virtual ast mk_op(ops op, ast, ast, ast) = 0; virtual ast mk_int(const std::string &) = 0; virtual ast mk_rat(const std::string &) = 0; virtual ast mk_true() = 0; virtual ast mk_false() = 0; virtual ast mk_app(symb,const std::vector args) = 0; virtual bool get_func(ast, symb &) = 0; virtual bool get_pred(ast, symb &) = 0; virtual bool get_op(ast, ops &) = 0; virtual bool get_true(ast id) = 0; virtual bool get_false(ast id) = 0; virtual bool get_int(ast id, std::string &res) = 0; virtual bool get_rat(ast id, std::string &res) = 0; virtual const std::string &get_symb(symb) = 0; virtual int get_num_args(ast) = 0; virtual ast get_arg(ast, int) = 0; virtual void show_ast(ast) = 0; virtual bool interpolate(const std::vector &frames, std::vector &itps, std::vector parents) = 0; FOCI2_EXPORT static foci2 *create(const std::string &); }; #endif z3-4.4.0/src/interp/iz3scopes.h0000755000175000017500000001166612540347414016045 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3scopes.h Abstract: Calculations with scopes, for both sequence and tree interpolation. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef IZ3SOPES_H #define IZ3SOPES_H #include #include #include "iz3hash.h" class scopes { public: /** Construct from parents vector. */ scopes(const std::vector &_parents){ parents = _parents; } scopes(){ } void initialize(const std::vector &_parents){ parents = _parents; } /** The parents vector defining the tree structure */ std::vector parents; // #define FULL_TREE #ifndef FULL_TREE struct range { range(){ lo = SHRT_MAX; hi = SHRT_MIN; } short lo, hi; }; /** computes the lub (smallest containing subtree) of two ranges */ range range_lub(const range &rng1, const range &rng2); /** computes the glb (intersection) of two ranges */ range range_glb(const range &rng1, const range &rng2); /** is this range empty? */ bool range_is_empty(const range &rng){ return rng.hi < rng.lo; } /** is this range full? */ bool range_is_full(const range &rng){ return rng.lo == SHRT_MIN && rng.hi == SHRT_MAX; } /** return an empty range */ range range_empty(){ range res; res.lo = SHRT_MAX; res.hi = SHRT_MIN; return res; } /** return an empty range */ range range_full(){ range res; res.lo = SHRT_MIN; res.hi = SHRT_MAX; return res; } /** return the maximal element of a range */ int range_max(const range &rng){ return rng.hi; } /** return a minimal (not necessarily unique) element of a range */ int range_min(const range &rng){ return rng.lo; } /** return range consisting of downward closure of a point */ range range_downward(int _hi){ range foo; foo.lo = SHRT_MIN; foo.hi = _hi; return foo; } void range_add(int i, range &n){ #if 0 if(i < n.lo) n.lo = i; if(i > n.hi) n.hi = i; #else range rng; rng.lo = i; rng.hi = i; n = range_lub(rng,n); #endif } /** Choose an element of rng1 that is near to rng2 */ int range_near(const range &rng1, const range &rng2){ int frame; int thing = tree_lca(rng1.lo,rng2.hi); if(thing == rng1.lo) frame = rng1.lo; else frame = tree_gcd(thing,rng1.hi); return frame; } #else struct range_lo { int lo; range_lo *next; range_lo(int _lo, range_lo *_next){ lo = _lo; next = _next; } }; struct range { int hi; range_lo *lo; range(int _hi, range_lo *_lo){ hi = _hi; lo = _lo; } range(){ hi = SHRT_MIN; lo = 0; } }; range_tables *rt; /** computes the lub (smallest containing subtree) of two ranges */ range range_lub(const range &rng1, const range &rng2); /** computes the glb (intersection) of two ranges */ range range_glb(const range &rng1, const range &rng2); /** is this range empty? */ bool range_is_empty(const range &rng); /** return an empty range */ range range_empty(); /** return a full range */ range range_full(); /** return the maximal element of a range */ int range_max(const range &rng); /** return a minimal (not necessarily unique) element of a range */ int range_min(const range &rng); /** return range consisting of downward closure of a point */ range range_downward(int _hi); /** add an element to a range */ void range_add(int i, range &n); /** Choose an element of rng1 that is near to rng2 */ int range_near(const range &rng1, const range &rng2); range_lo *find_range_lo(int lo, range_lo *next); range_lo *range_lub_lo(range_lo *rng1, range_lo *rng2); range_lo *range_glb_lo(range_lo *rng1, range_lo *rng2, int lim); #endif /** test whether a tree node is contained in a range */ bool in_range(int n, const range &rng); /** test whether two ranges of tree nodes intersect */ bool ranges_intersect(const range &rng1, const range &rng2); /** test whether range rng1 contained in range rng2 */ bool range_contained(const range &rng1, const range &rng2); private: int tree_lca(int n1, int n2); int tree_gcd(int n1, int n2); bool tree_mode(){return parents.size() != 0;} }; // let us hash on ranges #ifndef FULL_TREE namespace hash_space { template <> class hash { public: size_t operator()(const scopes::range &p) const { return (size_t)p.lo + (size_t)p.hi; } }; } inline bool operator==(const scopes::range &x, const scopes::range &y){ return x.lo == y.lo && x.hi == y.hi; } #endif #endif z3-4.4.0/src/interp/iz3checker.h0000644000175000017500000000170112540347414016137 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3checker.h Abstract: check correctness of an interpolant Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef IZ3_CHECKER_H #define IZ3_CHECKER_H #include "iz3mgr.h" #include "solver.h" bool iz3check(ast_manager &_m_manager, solver *s, std::ostream &err, const ptr_vector &cnsts, const ::vector &parents, const ptr_vector &interps, const ptr_vector &theory); bool iz3check(ast_manager &_m_manager, solver *s, std::ostream &err, const ptr_vector &cnsts, ast *tree, const ptr_vector &interps); bool iz3check(iz3mgr &mgr, solver *s, std::ostream &err, const std::vector &cnsts, const std::vector &parents, const std::vector &interps, const ptr_vector &theory); #endif z3-4.4.0/src/interp/iz3foci.cpp0000755000175000017500000002773512540347414016030 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3foci.cpp Abstract: Implements a secondary solver using foci2. Author: Ken McMillan (kenmcmil) Revision History: --*/ #include #include #include #include "iz3hash.h" #include "foci2.h" #include "iz3foci.h" using namespace stl_ext; class iz3foci_impl : public iz3secondary { int frames; int *parents; foci2 *foci; foci2::symb select_op; foci2::symb store_op; foci2::symb mod_op; public: iz3foci_impl(iz3mgr *mgr, int _frames, int *_parents) : iz3secondary(*mgr) { frames = _frames; parents = _parents; foci = 0; } typedef hash_map AstToNode; AstToNode ast_to_node; // maps Z3 ast's to foci expressions typedef hash_map NodeToAst; NodeToAst node_to_ast; // maps Z3 ast's to foci expressions // We only use this for FuncDeclToSymbol, which has no range destructor struct symb_hash { size_t operator()(const symb &s) const { return (size_t) s; } }; typedef hash_map FuncDeclToSymbol; FuncDeclToSymbol func_decl_to_symbol; // maps Z3 func decls to symbols typedef hash_map SymbolToFuncDecl; SymbolToFuncDecl symbol_to_func_decl; // maps symbols to Z3 func decls int from_symb(symb func){ std::string name = string_of_symbol(func); bool is_bool = is_bool_type(get_range_type(func)); foci2::symb f; if(is_bool) f = foci->mk_pred(name); else f = foci->mk_func(name); symbol_to_func_decl[f] = func; func_decl_to_symbol[func] = f; return f; } // create a symbol corresponding to a DeBruijn index of a particular type // the type has to be encoded into the name because the same index can // occur with different types foci2::symb make_deBruijn_symbol(int index, type ty){ std::ostringstream s; // s << "#" << index << "#" << type; return foci->mk_func(s.str()); } int from_Z3_ast(ast t){ std::pair foo(t,0); std::pair bar = ast_to_node.insert(foo); int &res = bar.first->second; if(!bar.second) return res; int nargs = num_args(t); std::vector args(nargs); for(int i = 0; i < nargs; i++) args[i] = from_Z3_ast(arg(t,i)); switch(op(t)){ case True: res = foci->mk_true(); break; case False: res = foci->mk_false(); break; case And: res = foci->mk_op(foci2::And,args); break; case Or: res = foci->mk_op(foci2::Or,args); break; case Not: res = foci->mk_op(foci2::Not,args[0]); break; case Iff: res = foci->mk_op(foci2::Iff,args); break; case OP_OEQ: // bit of a mystery, this one... if(args[0] == args[1]) res = foci->mk_true(); else res = foci->mk_op(foci2::Iff,args); break; case Ite: if(is_bool_type(get_type(t))) res = foci->mk_op(foci2::And,foci->mk_op(foci2::Or,foci->mk_op(foci2::Not,args[0]),args[1]),foci->mk_op(foci2::Or,args[0],args[2])); else res = foci->mk_op(foci2::Ite,args); break; case Equal: res = foci->mk_op(foci2::Equal,args); break; case Implies: args[0] = foci->mk_op(foci2::Not,args[0]); res = foci->mk_op(foci2::Or,args); break; case Xor: res = foci->mk_op(foci2::Not,foci->mk_op(foci2::Iff,args)); break; case Leq: res = foci->mk_op(foci2::Leq,args); break; case Geq: std::swap(args[0],args[1]); res = foci->mk_op(foci2::Leq,args); break; case Gt: res = foci->mk_op(foci2::Not,foci->mk_op(foci2::Leq,args)); break; case Lt: std::swap(args[0],args[1]); res = foci->mk_op(foci2::Not,foci->mk_op(foci2::Leq,args)); break; case Plus: res = foci->mk_op(foci2::Plus,args); break; case Sub: args[1] = foci->mk_op(foci2::Times,foci->mk_int("-1"),args[1]); res = foci->mk_op(foci2::Plus,args); break; case Uminus: res = foci->mk_op(foci2::Times,foci->mk_int("-1"),args[0]); break; case Times: res = foci->mk_op(foci2::Times,args); break; case Idiv: res = foci->mk_op(foci2::Div,args); break; case Mod: res = foci->mk_app(mod_op,args); break; case Select: res = foci->mk_app(select_op,args); break; case Store: res = foci->mk_app(store_op,args); break; case Distinct: res = foci->mk_op(foci2::Distinct,args); break; case Uninterpreted: { symb func = sym(t); FuncDeclToSymbol::iterator it = func_decl_to_symbol.find(func); foci2::symb f = (it == func_decl_to_symbol.end()) ? from_symb(func) : it->second; if(foci->get_symb(f).substr(0,3) == "lbl" && args.size()==1) // HACK to handle Z3 labels res = args[0]; else if(foci->get_symb(f).substr(0,3) == "lbl" && args.size()==0) // HACK to handle Z3 labels res = foci->mk_true(); else res = foci->mk_app(f,args); break; } case Numeral: { std::string s = string_of_numeral(t); res = foci->mk_int(s); break; } case Forall: case Exists: { bool is_forall = op(t) == Forall; foci2::ops qop = is_forall ? foci2::Forall : foci2::Exists; int bvs = get_quantifier_num_bound(t); std::vector foci_bvs(bvs); for(int i = 0; i < bvs; i++){ std::string name = get_quantifier_bound_name(t,i); //Z3_string name = Z3_get_symbol_string(ctx,sym); // type ty = get_quantifier_bound_type(t,i); foci2::symb f = foci->mk_func(name); foci2::ast v = foci->mk_app(f,std::vector()); foci_bvs[i] = v; } foci2::ast body = from_Z3_ast(get_quantifier_body(t)); foci_bvs.push_back(body); res = foci->mk_op(qop,foci_bvs); node_to_ast[res] = t; // desperate break; } case Variable: { // a deBruijn index int index = get_variable_index_value(t); type ty = get_type(t); foci2::symb symbol = make_deBruijn_symbol(index,ty); res = foci->mk_app(symbol,std::vector()); break; } default: { std::cerr << "iZ3: unsupported Z3 operator in expression\n "; print_expr(std::cerr,t); std::cerr << "\n"; assert(0 && "iZ3: unsupported Z3 operator"); } } return res; } // convert an expr to Z3 ast ast to_Z3_ast(foci2::ast i){ std::pair foo(i,ast()); std::pair bar = node_to_ast.insert(foo); if(!bar.second) return bar.first->second; ast &res = bar.first->second; if(i < 0){ res = mk_not(to_Z3_ast(-i)); return res; } // get the arguments unsigned n = foci->get_num_args(i); std::vector args(n); for(unsigned j = 0; j < n; j++) args[j] = to_Z3_ast(foci->get_arg(i,j)); // handle operators foci2::ops o; foci2::symb f; std::string nval; if(foci->get_true(i)) res = mk_true(); else if(foci->get_false(i)) res = mk_false(); else if(foci->get_op(i,o)){ switch(o){ case foci2::And: res = make(And,args); break; case foci2::Or: res = make(Or,args); break; case foci2::Not: res = mk_not(args[0]); break; case foci2::Iff: res = make(Iff,args[0],args[1]); break; case foci2::Ite: res = make(Ite,args[0],args[1],args[2]); break; case foci2::Equal: res = make(Equal,args[0],args[1]); break; case foci2::Plus: res = make(Plus,args); break; case foci2::Times: res = make(Times,args); break; case foci2::Div: res = make(Idiv,args[0],args[1]); break; case foci2::Leq: res = make(Leq,args[0],args[1]); break; case foci2::Distinct: res = make(Distinct,args); break; case foci2::Tsym: res = mk_true(); break; case foci2::Fsym: res = mk_false(); break; case foci2::Forall: case foci2::Exists: { int nargs = n; std::vector bounds(nargs-1); for(int i = 0; i < nargs-1; i++) bounds[i] = args[i]; opr oz = o == foci2::Forall ? Forall : Exists; res = make_quant(oz,bounds,args[nargs-1]); } break; default: assert("unknown built-in op"); } } else if(foci->get_int(i,nval)){ res = make_int(nval); } else if(foci->get_func(i,f)){ if(f == select_op){ assert(n == 2); res = make(Select,args[0],args[1]); } else if(f == store_op){ assert(n == 3); res = make(Store,args[0],args[1],args[2]); } else if(f == mod_op){ assert(n == 2); res = make(Mod,args[0],args[1]); } else { std::pair foo(f,(symb)0); std::pair bar = symbol_to_func_decl.insert(foo); symb &func_decl = bar.first->second; if(bar.second){ std::cout << "unknown function symbol:\n"; foci->show_ast(i); assert(0); } res = make(func_decl,args); } } else { std::cerr << "iZ3: unknown FOCI expression kind\n"; assert(0 && "iZ3: unknown FOCI expression kind"); } return res; } int interpolate(const std::vector &cnsts, std::vector &itps){ assert((int)cnsts.size() == frames); std::string lia("lia"); #ifdef _FOCI2 foci = foci2::create(lia); #else foci = 0; #endif if(!foci){ std::cerr << "iZ3: cannot find foci lia solver.\n"; assert(0); } select_op = foci->mk_func("select"); store_op = foci->mk_func("store"); mod_op = foci->mk_func("mod"); std::vector foci_cnsts(frames), foci_itps(frames-1), foci_parents; if(parents) foci_parents.resize(frames); for(int i = 0; i < frames; i++){ foci_cnsts[i] = from_Z3_ast(cnsts[i]); if(parents) foci_parents[i] = parents[i]; } int res = foci->interpolate(foci_cnsts, foci_itps, foci_parents); if(res == 0){ assert((int)foci_itps.size() == frames-1); itps.resize(frames-1); for(int i = 0; i < frames-1; i++){ // foci->show_ast(foci_itps[i]); itps[i] = to_Z3_ast(foci_itps[i]); } } ast_to_node.clear(); node_to_ast.clear(); func_decl_to_symbol.clear(); symbol_to_func_decl.clear(); delete foci; return res; } }; iz3secondary *iz3foci::create(iz3mgr *mgr, int num, int *parents){ return new iz3foci_impl(mgr,num,parents); } z3-4.4.0/src/interp/foci2stub/0000755000175000017500000000000012540347414015635 5ustar michaelmichaelz3-4.4.0/src/interp/foci2stub/foci2.cpp0000644000175000017500000000042012540347414017337 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: foci2.cpp Abstract: Fake foci2, to be replaced Author: Ken McMillan (kenmcmil) Revision History: --*/ #include "foci2.h" FOCI2_EXPORT foci2 *foci2::create(const std::string &){ return 0; } z3-4.4.0/src/interp/foci2stub/foci2.h0000755000175000017500000000343012540347414017013 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: foci2.h Abstract: An interface class for foci2. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef FOCI2_H #define FOCI2_H #include #include #ifdef WIN32 #define FOCI2_EXPORT __declspec(dllexport) #else #define FOCI2_EXPORT __attribute__ ((visibility ("default"))) #endif class foci2 { public: virtual ~foci2(){} typedef int ast; typedef int symb; /** Built-in operators */ enum ops { And = 0, Or, Not, Iff, Ite, Equal, Plus, Times, Floor, Leq, Div, Bool, Int, Array, Tsym, Fsym, Forall, Exists, Distinct, LastOp }; virtual symb mk_func(const std::string &s) = 0; virtual symb mk_pred(const std::string &s) = 0; virtual ast mk_op(ops op, const std::vector args) = 0; virtual ast mk_op(ops op, ast) = 0; virtual ast mk_op(ops op, ast, ast) = 0; virtual ast mk_op(ops op, ast, ast, ast) = 0; virtual ast mk_int(const std::string &) = 0; virtual ast mk_rat(const std::string &) = 0; virtual ast mk_true() = 0; virtual ast mk_false() = 0; virtual ast mk_app(symb,const std::vector args) = 0; virtual bool get_func(ast, symb &) = 0; virtual bool get_pred(ast, symb &) = 0; virtual bool get_op(ast, ops &) = 0; virtual bool get_true(ast id) = 0; virtual bool get_false(ast id) = 0; virtual bool get_int(ast id, std::string &res) = 0; virtual bool get_rat(ast id, std::string &res) = 0; virtual const std::string &get_symb(symb) = 0; virtual int get_num_args(ast) = 0; virtual ast get_arg(ast, int) = 0; virtual void show_ast(ast) = 0; virtual bool interpolate(const std::vector &frames, std::vector &itps, std::vector parents) = 0; FOCI2_EXPORT static foci2 *create(const std::string &); }; #endif z3-4.4.0/src/interp/iz3mgr.cpp0000755000175000017500000006752312540347414015674 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3mgr.cpp Abstract: A wrapper around an ast manager, providing convenience methods. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifdef _WINDOWS #pragma warning(disable:4996) #pragma warning(disable:4800) #pragma warning(disable:4267) #pragma warning(disable:4101) #pragma warning(disable:4805) #pragma warning(disable:4800) #endif #include "iz3mgr.h" #include #include #include #include #include "expr_abstract.h" #include "params.h" using namespace stl_ext; std::ostream &operator <<(std::ostream &s, const iz3mgr::ast &a){ return s; } iz3mgr::ast iz3mgr::make_var(const std::string &name, type ty){ symbol s = symbol(name.c_str()); return cook(m().mk_const(m().mk_const_decl(s, ty))); } iz3mgr::ast iz3mgr::make(opr op, int n, raw_ast **args){ switch(op) { case True: return mki(m_basic_fid,OP_TRUE,n,args); case False: return mki(m_basic_fid,OP_FALSE,n,args); case Equal: return mki(m_basic_fid,OP_EQ,n,args); case Distinct: return mki(m_basic_fid,OP_DISTINCT,n,args); case Ite: return mki(m_basic_fid,OP_ITE,n,args); case And: return mki(m_basic_fid,OP_AND,n,args); case Or: return mki(m_basic_fid,OP_OR,n,args); case Iff: return mki(m_basic_fid,OP_IFF,n,args); case Xor: return mki(m_basic_fid,OP_XOR,n,args); case Not: return mki(m_basic_fid,OP_NOT,n,args); case Implies: return mki(m_basic_fid,OP_IMPLIES,n,args); case Oeq: return mki(m_basic_fid,OP_OEQ,n,args); case Interp: return mki(m_basic_fid,OP_INTERP,n,args); case Leq: return mki(m_arith_fid,OP_LE,n,args); case Geq: return mki(m_arith_fid,OP_GE,n,args); case Lt: return mki(m_arith_fid,OP_LT,n,args); case Gt: return mki(m_arith_fid,OP_GT,n,args); case Plus: return mki(m_arith_fid,OP_ADD,n,args); case Sub: return mki(m_arith_fid,OP_SUB,n,args); case Uminus: return mki(m_arith_fid,OP_UMINUS,n,args); case Times: return mki(m_arith_fid,OP_MUL,n,args); case Div: return mki(m_arith_fid,OP_DIV,n,args); case Idiv: return mki(m_arith_fid,OP_IDIV,n,args); case Rem: return mki(m_arith_fid,OP_REM,n,args); case Mod: return mki(m_arith_fid,OP_MOD,n,args); case Power: return mki(m_arith_fid,OP_POWER,n,args); case ToReal: return mki(m_arith_fid,OP_TO_REAL,n,args); case ToInt: return mki(m_arith_fid,OP_TO_INT,n,args); case IsInt: return mki(m_arith_fid,OP_IS_INT,n,args); case Store: return mki(m_array_fid,OP_STORE,n,args); case Select: return mki(m_array_fid,OP_SELECT,n,args); case ConstArray: return mki(m_array_fid,OP_CONST_ARRAY,n,args); case ArrayDefault: return mki(m_array_fid,OP_ARRAY_DEFAULT,n,args); case ArrayMap: return mki(m_array_fid,OP_ARRAY_MAP,n,args); case SetUnion: return mki(m_array_fid,OP_SET_UNION,n,args); case SetIntersect: return mki(m_array_fid,OP_SET_INTERSECT,n,args); case SetDifference: return mki(m_array_fid,OP_SET_DIFFERENCE,n,args); case SetComplement: return mki(m_array_fid,OP_SET_COMPLEMENT,n,args); case SetSubSet: return mki(m_array_fid,OP_SET_SUBSET,n,args); case AsArray: return mki(m_array_fid,OP_AS_ARRAY,n,args); default: assert(0); return ast(); } } iz3mgr::ast iz3mgr::mki(family_id fid, decl_kind dk, int n, raw_ast **args){ return cook(m().mk_app(fid, dk, 0, 0, n, (expr **)args)); } iz3mgr::ast iz3mgr::make(opr op, const std::vector &args){ static std::vector a(10); if(a.size() < args.size()) a.resize(args.size()); for(unsigned i = 0; i < args.size(); i++) a[i] = args[i].raw(); return make(op,args.size(), args.size() ? &a[0] : 0); } iz3mgr::ast iz3mgr::make(opr op){ return make(op,0,0); } iz3mgr::ast iz3mgr::make(opr op, const ast &arg0){ raw_ast *a = arg0.raw(); return make(op,1,&a); } iz3mgr::ast iz3mgr::make(opr op, const ast &arg0, const ast &arg1){ raw_ast *args[2]; args[0] = arg0.raw(); args[1] = arg1.raw(); return make(op,2,args); } iz3mgr::ast iz3mgr::make(opr op, const ast &arg0, const ast &arg1, const ast &arg2){ raw_ast *args[3]; args[0] = arg0.raw(); args[1] = arg1.raw(); args[2] = arg2.raw(); return make(op,3,args); } iz3mgr::ast iz3mgr::make(symb sym, int n, raw_ast **args){ return cook(m().mk_app(sym, n, (expr **) args)); } iz3mgr::ast iz3mgr::make(symb sym, const std::vector &args){ static std::vector a(10); if(a.size() < args.size()) a.resize(args.size()); for(unsigned i = 0; i < args.size(); i++) a[i] = args[i].raw(); return make(sym,args.size(), args.size() ? &a[0] : 0); } iz3mgr::ast iz3mgr::make(symb sym){ return make(sym,0,0); } iz3mgr::ast iz3mgr::make(symb sym, const ast &arg0){ raw_ast *a = arg0.raw(); return make(sym,1,&a); } iz3mgr::ast iz3mgr::make(symb sym, const ast &arg0, const ast &arg1){ raw_ast *args[2]; args[0] = arg0.raw(); args[1] = arg1.raw(); return make(sym,2,args); } iz3mgr::ast iz3mgr::make(symb sym, const ast &arg0, const ast &arg1, const ast &arg2){ raw_ast *args[3]; args[0] = arg0.raw(); args[1] = arg1.raw(); args[2] = arg2.raw(); return make(sym,3,args); } iz3mgr::ast iz3mgr::make_quant(opr op, const std::vector &bvs, ast &body){ if(bvs.size() == 0) return body; std::vector foo(bvs.size()); std::vector names; std::vector types; std::vector bound_asts; unsigned num_bound = bvs.size(); for (unsigned i = 0; i < num_bound; ++i) { app* a = to_app(bvs[i].raw()); symbol s(to_app(a)->get_decl()->get_name()); names.push_back(s); types.push_back(m().get_sort(a)); bound_asts.push_back(a); } expr_ref abs_body(m()); expr_abstract(m(), 0, num_bound, &bound_asts[0], to_expr(body.raw()), abs_body); expr_ref result(m()); result = m().mk_quantifier( op == Forall, names.size(), &types[0], &names[0], abs_body.get(), 0, symbol("itp"), symbol(), 0, 0, 0, 0 ); return cook(result.get()); } // FIXME replace this with existing Z3 functionality iz3mgr::ast iz3mgr::clone(const ast &t, const std::vector &_args){ if(_args.size() == 0) return t; ast_manager& m = m_manager; expr* a = to_expr(t.raw()); static std::vector rargs(10); if(rargs.size() < _args.size()) rargs.resize(_args.size()); for(unsigned i = 0; i < _args.size(); i++) rargs[i] = _args[i].raw(); expr* const* args = (expr **)&rargs[0]; switch(a->get_kind()) { case AST_APP: { app* e = to_app(a); if (e->get_num_args() != _args.size()) { assert(0); } else { a = m.mk_app(e->get_decl(), _args.size(), args); } break; } case AST_QUANTIFIER: { if (_args.size() != 1) { assert(0); } else { a = m.update_quantifier(to_quantifier(a), args[0]); } break; } default: break; } return cook(a); } void iz3mgr::show(ast t){ if(t.null()){ std::cout << "(null)" << std::endl; } params_ref p; p.set_bool("flat_assoc",false); std::cout << mk_pp(t.raw(), m(), p) << std::endl; } void iz3mgr::show_symb(symb s){ std::cout << mk_pp(s, m()) << std::endl; } void iz3mgr::print_expr(std::ostream &s, const ast &e){ params_ref p; p.set_bool("flat_assoc",false); s << mk_pp(e.raw(), m(), p); } void iz3mgr::print_clause(std::ostream &s, std::vector &cls){ s << "("; for(unsigned i = 0; i < cls.size(); i++){ if(i > 0) s << ","; print_expr(s,cls[i]); } s << ")"; } void iz3mgr::show_clause(std::vector &cls){ print_clause(std::cout,cls); std::cout << std::endl; } void iz3mgr::print_lit(ast lit){ ast abslit = is_not(lit) ? arg(lit,0) : lit; int f = op(abslit); if(f == And || f == Or || f == Iff){ if(is_not(lit)) std::cout << "~"; std::cout << "[" << abslit << "]"; } else std::cout << lit; } static int pretty_cols = 79; static int pretty_indent_chars = 2; static int pretty_find_delim(const std::string &s, int pos){ int level = 0; int end = s.size(); for(; pos < end; pos++){ int ch = s[pos]; if(ch == '(')level++; if(ch == ')')level--; if(level < 0 || (level == 0 && ch == ','))break; } return pos; } static void pretty_newline(std::ostream &f, int indent){ f << std::endl; for(int i = 0; i < indent; i++) f << " "; } void iz3mgr::pretty_print(std::ostream &f, const std::string &s){ int cur_indent = 0; int indent = 0; int col = 0; int pos = 0; while(pos < (int)s.size()){ int delim = pretty_find_delim(s,pos); if(s[pos] != ')' && s[pos] != ',' && cur_indent > indent){ pretty_newline(f,indent); cur_indent = indent; col = indent; continue; } if (col + delim - pos > pretty_cols) { if (col > indent) { pretty_newline(f,indent); cur_indent = indent; col = indent; continue; } int paren = s.find('(',pos); if(paren != (int)std::string::npos){ int chars = paren - pos + 1; f << s.substr(pos,chars); indent += pretty_indent_chars; if(col) pretty_newline(f,indent); cur_indent = indent; pos += chars; col = indent; continue; } } int chars = delim - pos + 1; f << s.substr(pos,chars); pos += chars; col += chars; if(s[delim] == ')') indent -= pretty_indent_chars; } } iz3mgr::opr iz3mgr::op(const ast &t){ ast_kind dk = t.raw()->get_kind(); switch(dk){ case AST_APP: { expr * e = to_expr(t.raw()); func_decl *d = to_app(t.raw())->get_decl(); if (null_family_id == d->get_family_id()) return Uninterpreted; // return (opr)d->get_decl_kind(); if (m_basic_fid == d->get_family_id()) { switch(d->get_decl_kind()) { case OP_TRUE: return True; case OP_FALSE: return False; case OP_EQ: return Equal; case OP_DISTINCT: return Distinct; case OP_ITE: return Ite; case OP_AND: return And; case OP_OR: return Or; case OP_IFF: return Iff; case OP_XOR: return Xor; case OP_NOT: return Not; case OP_IMPLIES: return Implies; case OP_OEQ: return Oeq; case OP_INTERP: return Interp; default: return Other; } } if (m_arith_fid == d->get_family_id()) { switch(d->get_decl_kind()) { case OP_LE: return Leq; case OP_GE: return Geq; case OP_LT: return Lt; case OP_GT: return Gt; case OP_ADD: return Plus; case OP_SUB: return Sub; case OP_UMINUS: return Uminus; case OP_MUL: return Times; case OP_DIV: return Div; case OP_IDIV: return Idiv; case OP_REM: return Rem; case OP_MOD: return Mod; case OP_POWER: return Power; case OP_TO_REAL: return ToReal; case OP_TO_INT: return ToInt; case OP_IS_INT: return IsInt; default: if (m().is_unique_value(e)) return Numeral; return Other; } } if (m_array_fid == d->get_family_id()) { switch(d->get_decl_kind()) { case OP_STORE: return Store; case OP_SELECT: return Select; case OP_CONST_ARRAY: return ConstArray; case OP_ARRAY_DEFAULT: return ArrayDefault; case OP_ARRAY_MAP: return ArrayMap; case OP_SET_UNION: return SetUnion; case OP_SET_INTERSECT: return SetIntersect; case OP_SET_DIFFERENCE: return SetDifference; case OP_SET_COMPLEMENT: return SetComplement; case OP_SET_SUBSET: return SetSubSet; case OP_AS_ARRAY: return AsArray; default: return Other; } } return Other; } case AST_QUANTIFIER: return to_quantifier(t.raw())->is_forall() ? Forall : Exists; case AST_VAR: return Variable; default:; } return Other; } iz3mgr::pfrule iz3mgr::pr(const ast &t){ func_decl *d = to_app(t.raw())->get_decl(); assert(m_basic_fid == d->get_family_id()); return d->get_decl_kind(); } void iz3mgr::print_sat_problem(std::ostream &out, const ast &t){ ast_smt_pp pp(m()); pp.set_simplify_implies(false); pp.display_smt2(out, to_expr(t.raw())); } iz3mgr::ast iz3mgr::z3_simplify(const ast &e){ ::expr * a = to_expr(e.raw()); params_ref p; th_rewriter m_rw(m(), p); expr_ref result(m()); m_rw(a, result); return cook(result); } iz3mgr::ast iz3mgr::z3_really_simplify(const ast &e){ ::expr * a = to_expr(e.raw()); params_ref simp_params; simp_params.set_bool(":som",true); simp_params.set_bool(":sort-sums",true); th_rewriter m_rw(m(), simp_params); expr_ref result(m()); m_rw(a, result); return cook(result); } #if 0 static rational lcm(const rational &x, const rational &y){ int a = x.numerator(); int b = y.numerator(); return rational(a * b / gcd(a, b)); } #endif static rational extract_lcd(std::vector &res){ if(res.size() == 0) return rational(1); // shouldn't happen rational lcd = denominator(res[0]); for(unsigned i = 1; i < res.size(); i++) lcd = lcm(lcd,denominator(res[i])); for(unsigned i = 0; i < res.size(); i++) res[i] *= lcd; return lcd; } void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& coeffs){ std::vector rats; get_farkas_coeffs(proof,rats); coeffs.resize(rats.size()); for(unsigned i = 0; i < rats.size(); i++){ class sort *is = m().mk_sort(m_arith_fid, INT_SORT); ast coeff = cook(m_arith_util.mk_numeral(rats[i],is)); coeffs[i] = coeff; } } static void abs_rat(std::vector &rats){ // check that they are all non-neg -- if neg, take abs val and warn! for(unsigned i = 0; i < rats.size(); i++) if(rats[i].is_neg()){ // std::cout << "negative Farkas coeff!\n"; rats[i] = -rats[i]; } } bool iz3mgr::is_farkas_coefficient_negative(const ast &proof, int n){ rational r; symb s = sym(proof); bool ok = s->get_parameter(n+2).is_rational(r); if(!ok) throw "Bad Farkas coefficient"; return r.is_neg(); } void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& rats){ symb s = sym(proof); int numps = s->get_num_parameters(); rats.resize(numps-2); #if 0 if(num_prems(proof) < numps-2){ std::cout << "bad farkas rule: " << num_prems(proof) << " premises should be " << numps-2 << "\n"; } #endif for(int i = 2; i < numps; i++){ rational r; bool ok = s->get_parameter(i).is_rational(r); if(!ok) throw "Bad Farkas coefficient"; #if 0 { ast con = conc(prem(proof,i-2)); ast temp = make_real(r); // for debugging opr o = is_not(con) ? op(arg(con,0)) : op(con); if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt)) r = -r; } #endif rats[i-2] = r; } #if 0 if(rats.size() != 0 && rats[0].is_neg()){ for(unsigned i = 0; i < rats.size(); i++){ assert(rats[i].is_neg()); rats[i] = -rats[i]; } } #endif abs_rat(rats); extract_lcd(rats); } void iz3mgr::get_broken_gcd_test_coeffs(const ast &proof, std::vector& rats){ symb s = sym(proof); int numps = s->get_num_parameters(); rats.resize(numps-2); for(int i = 2; i < numps; i++){ rational r; bool ok = s->get_parameter(i).is_rational(r); if(!ok) throw "Bad Farkas coefficient"; rats[i-2] = r; } extract_lcd(rats); } void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector& coeffs){ std::vector rats; get_assign_bounds_coeffs(proof,rats); coeffs.resize(rats.size()); for(unsigned i = 0; i < rats.size(); i++){ coeffs[i] = make_int(rats[i]); } } void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector& rats){ symb s = sym(proof); int numps = s->get_num_parameters(); rats.resize(numps-1); rats[0] = rational(1); ast conseq = arg(conc(proof),0); opr conseq_o = is_not(conseq) ? op(arg(conseq,0)) : op(conseq); bool conseq_neg = is_not(conseq) ? (conseq_o == Leq || conseq_o == Lt) : (conseq_o == Geq || conseq_o == Gt); for(int i = 2; i < numps; i++){ rational r; bool ok = s->get_parameter(i).is_rational(r); if(!ok) throw "Bad Farkas coefficient"; { ast con = arg(conc(proof),i-1); ast temp = make_real(r); // for debugging opr o = is_not(con) ? op(arg(con,0)) : op(con); if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt)) r = -r; if(conseq_neg) r = -r; } rats[i-1] = r; } #if 0 if(rats[1].is_neg()){ // work around bug -- if all coeffs negative, negate them for(unsigned i = 1; i < rats.size(); i++){ if(!rats[i].is_neg()) throw "Bad Farkas coefficients"; rats[i] = -rats[i]; } } #endif abs_rat(rats); extract_lcd(rats); } void iz3mgr::get_gomory_cut_coeffs(const ast &proof, std::vector& coeffs){ std::vector rats; get_gomory_cut_coeffs(proof,rats); coeffs.resize(rats.size()); for(unsigned i = 0; i < rats.size(); i++){ coeffs[i] = make_int(rats[i]); } } void iz3mgr::get_gomory_cut_coeffs(const ast &proof, std::vector& rats){ symb s = sym(proof); int numps = s->get_num_parameters(); rats.resize(numps-2); for(int i = 2; i < numps; i++){ rational r; bool ok = s->get_parameter(i).is_rational(r); if(!ok) throw "Bad Farkas coefficient"; rats[i-2] = r; } abs_rat(rats); extract_lcd(rats); } void iz3mgr::get_assign_bounds_rule_coeffs(const ast &proof, std::vector& coeffs){ std::vector rats; get_assign_bounds_rule_coeffs(proof,rats); coeffs.resize(rats.size()); for(unsigned i = 0; i < rats.size(); i++){ coeffs[i] = make_int(rats[i]); } } void iz3mgr::get_assign_bounds_rule_coeffs(const ast &proof, std::vector& rats){ symb s = sym(proof); int numps = s->get_num_parameters(); rats.resize(numps-1); rats[0] = rational(1); ast conseq = arg(conc(proof),0); opr conseq_o = is_not(conseq) ? op(arg(conseq,0)) : op(conseq); bool conseq_neg = is_not(conseq) ? (conseq_o == Leq || conseq_o == Lt) : (conseq_o == Geq || conseq_o == Gt); for(int i = 2; i < numps; i++){ rational r; bool ok = s->get_parameter(i).is_rational(r); if(!ok) throw "Bad Farkas coefficient"; { ast con = conc(prem(proof,i-2)); ast temp = make_real(r); // for debugging opr o = is_not(con) ? op(arg(con,0)) : op(con); if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt)) r = -r; if(conseq_neg) r = -r; } rats[i-1] = r; } #if 0 if(rats[1].is_neg()){ // work around bug -- if all coeffs negative, negate them for(unsigned i = 1; i < rats.size(); i++){ if(!rats[i].is_neg()) throw "Bad Farkas coefficients"; rats[i] = -rats[i]; } } #endif abs_rat(rats); extract_lcd(rats); } /** Set P to P + cQ, where P and Q are linear inequalities. Assumes P is 0 <= y or 0 < y. */ void iz3mgr::linear_comb(ast &P, const ast &c, const ast &Q, bool round_off){ ast Qrhs; bool qstrict = false; if(is_not(Q)){ ast nQ = arg(Q,0); switch(op(nQ)){ case Gt: Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); break; case Lt: Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); break; case Geq: Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); qstrict = true; break; case Leq: Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); qstrict = true; break; default: throw "not an inequality"; } } else { switch(op(Q)){ case Leq: Qrhs = make(Sub,arg(Q,1),arg(Q,0)); break; case Geq: Qrhs = make(Sub,arg(Q,0),arg(Q,1)); break; case Lt: Qrhs = make(Sub,arg(Q,1),arg(Q,0)); qstrict = true; break; case Gt: Qrhs = make(Sub,arg(Q,0),arg(Q,1)); qstrict = true; break; default: throw "not an inequality"; } } bool pstrict = op(P) == Lt; if(qstrict && round_off && (pstrict || !(c == make_int(rational(1))))){ Qrhs = make(Sub,Qrhs,make_int(rational(1))); qstrict = false; } Qrhs = make(Times,c,Qrhs); bool strict = pstrict || qstrict; if(strict) P = make(Lt,arg(P,0),make(Plus,arg(P,1),Qrhs)); else P = make(Leq,arg(P,0),make(Plus,arg(P,1),Qrhs)); } iz3mgr::ast iz3mgr::sum_inequalities(const std::vector &coeffs, const std::vector &ineqs, bool round_off){ ast zero = make_int("0"); ast thing = make(Leq,zero,zero); for(unsigned i = 0; i < ineqs.size(); i++){ linear_comb(thing,coeffs[i],ineqs[i], round_off); } thing = simplify_ineq(thing); return thing; } void iz3mgr::mk_idiv(const ast& t, const rational &d, ast &whole, ast &frac){ opr o = op(t); if(o == Plus){ int nargs = num_args(t); for(int i = 0; i < nargs; i++) mk_idiv(arg(t,i),d,whole,frac); return; } else if(o == Times){ rational coeff; if(is_numeral(arg(t,0),coeff)){ if(gcd(coeff,d) == d){ whole = make(Plus,whole,make(Times,make_int(coeff/d),arg(t,1))); return; } } } frac = make(Plus,frac,t); } iz3mgr::ast iz3mgr::mk_idiv(const ast& q, const rational &d){ ast t = z3_simplify(q); if(d == rational(1)) return t; else { ast whole = make_int("0"); ast frac = whole; mk_idiv(t,d,whole,frac); return z3_simplify(make(Plus,whole,make(Idiv,z3_simplify(frac),make_int(d)))); } } iz3mgr::ast iz3mgr::mk_idiv(const ast& t, const ast &d){ rational r; if(is_numeral(d,r)) return mk_idiv(t,r); return make(Idiv,t,d); } // does variable occur in expression? int iz3mgr::occurs_in1(stl_ext::hash_map &occurs_in_memo,ast var, ast e){ std::pair foo(e,false); std::pair::iterator,bool> bar = occurs_in_memo.insert(foo); bool &res = bar.first->second; if(bar.second){ if(e == var) res = true; int nargs = num_args(e); for(int i = 0; i < nargs; i++) res |= occurs_in1(occurs_in_memo,var,arg(e,i)); } return res; } int iz3mgr::occurs_in(ast var, ast e){ hash_map memo; return occurs_in1(memo,var,e); } bool iz3mgr::solve_arith(const ast &v, const ast &x, const ast &y, ast &res){ if(occurs_in(v,y)) return false; if(op(x) == Plus){ int n = num_args(x); for(int i = 0; i < n; i++){ if(arg(x,i) == v){ res = z3_simplify(make(Sub, y, make(Sub, x, v))); return true; } } } return false; } // find a controlling equality for a given variable v in a term // a controlling equality is of the form v = t, which, being // false would force the formula to have the specifid truth value // returns t, or null if no such iz3mgr::ast iz3mgr::cont_eq(stl_ext::hash_set &cont_eq_memo, bool truth, ast v, ast e){ if(is_not(e)) return cont_eq(cont_eq_memo, !truth,v,arg(e,0)); if(cont_eq_memo.find(e) != cont_eq_memo.end()) return ast(); cont_eq_memo.insert(e); if(!truth && op(e) == Equal){ if(arg(e,0) == v && !occurs_in(v,arg(e,1))) return(arg(e,1)); if(arg(e,1) == v && !occurs_in(v,arg(e,0))) return(arg(e,0)); ast res; if(solve_arith(v,arg(e,0),arg(e,1),res)) return res; if(solve_arith(v,arg(e,1),arg(e,0),res)) return res; } if((!truth && op(e) == And) || (truth && op(e) == Or)){ int nargs = num_args(e); for(int i = 0; i < nargs; i++){ ast res = cont_eq(cont_eq_memo, truth, v, arg(e,i)); if(!res.null()) return res; } } if(truth && op(e) == Implies){ ast res = cont_eq(cont_eq_memo, !truth, v, arg(e,0)); if(!res.null()) return res; res = cont_eq(cont_eq_memo, truth, v, arg(e,1)); if(!res.null()) return res; } return ast(); } // substitute a term t for unbound occurrences of variable v in e iz3mgr::ast iz3mgr::subst(stl_ext::hash_map &subst_memo, ast var, ast t, ast e){ if(e == var) return t; std::pair foo(e,ast()); std::pair::iterator,bool> bar = subst_memo.insert(foo); ast &res = bar.first->second; if(bar.second){ int nargs = num_args(e); std::vector args(nargs); for(int i = 0; i < nargs; i++) args[i] = subst(subst_memo,var,t,arg(e,i)); opr f = op(e); if(f == Equal && args[0] == args[1]) res = mk_true(); else res = clone(e,args); } return res; } iz3mgr::ast iz3mgr::subst(ast var, ast t, ast e){ hash_map memo; return subst(memo,var,t,e); } iz3mgr::ast iz3mgr::subst(stl_ext::hash_map &subst_memo,ast e){ std::pair foo(e,ast()); std::pair::iterator,bool> bar = subst_memo.insert(foo); ast &res = bar.first->second; if(bar.second){ int nargs = num_args(e); std::vector args(nargs); for(int i = 0; i < nargs; i++) args[i] = subst(subst_memo,arg(e,i)); opr f = op(e); if(f == Equal && args[0] == args[1]) res = mk_true(); else res = clone(e,args); } return res; } // apply a quantifier to a formula, with some optimizations // 1) bound variable does not occur -> no quantifier // 2) bound variable must be equal to some term -> substitute iz3mgr::ast iz3mgr::apply_quant(opr quantifier, ast var, ast e){ if((quantifier == Forall && op(e) == And) || (quantifier == Exists && op(e) == Or)){ int n = num_args(e); std::vector args(n); for(int i = 0; i < n; i++) args[i] = apply_quant(quantifier,var,arg(e,i)); return make(op(e),args); } if(!occurs_in(var,e))return e; hash_set cont_eq_memo; ast cterm = cont_eq(cont_eq_memo, quantifier == Forall, var, e); if(!cterm.null()){ return subst(var,cterm,e); } std::vector bvs; bvs.push_back(var); return make_quant(quantifier,bvs,e); } #if 0 void iz3mgr::get_bound_substitutes(stl_ext::hash_map &memo, const ast &e, const ast &var, std::vector &substs){ std::pair foo(e,false); std::pair::iterator,bool> bar = memo.insert(foo); if(bar.second){ if(op(e) == } } #endif z3-4.4.0/src/interp/iz3translate.cpp0000755000175000017500000023220312540347414017071 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3translate.cpp Abstract: Translate a Z3 proof to in interpolated proof. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifdef _WINDOWS #pragma warning(disable:4996) #pragma warning(disable:4800) #pragma warning(disable:4267) #pragma warning(disable:4101) #endif #include "iz3translate.h" #include "iz3proof.h" #include "iz3profiling.h" #include "iz3interp.h" #include "iz3proof_itp.h" #include #include #include #include #include #include #include //using std::vector; using namespace stl_ext; /* This translator goes directly from Z3 proofs to interpolated proofs without an intermediate representation. No secondary prover is used. */ class iz3translation_full : public iz3translation { public: typedef iz3proof_itp Iproof; Iproof *iproof; /* Here we have lots of hash tables for memoizing various methods and other such global data structures. */ typedef hash_map AstToInt; AstToInt locality; // memoizes locality of Z3 proof terms typedef std::pair EquivEntry; typedef hash_map EquivTab; EquivTab equivs; // maps non-local terms to equivalent local terms, with proof typedef hash_set AstHashSet; AstHashSet equivs_visited; // proofs already checked for equivalences typedef std::pair, hash_map > AstToIpf; AstToIpf translation; // Z3 proof nodes to Iproof nodes int frames; // number of frames typedef std::set AstSet; typedef hash_map AstToAstSet; AstToAstSet hyp_map; // map proof terms to hypothesis set struct LocVar { // localization vars ast var; // a fresh variable ast term; // term it represents int frame; // frame in which it's defined LocVar(ast v, ast t, int f){var=v;term=t;frame=f;} }; std::vector localization_vars; // localization vars in order of creation typedef hash_map AstToAst; AstToAst localization_map; // maps terms to their localization vars typedef hash_map AstToBool; AstToBool occurs_in_memo; // memo of occurs_in function AstHashSet cont_eq_memo; // memo of cont_eq function AstToAst subst_memo; // memo of subst function symb commute; public: #define from_ast(x) (x) // #define NEW_LOCALITY #ifdef NEW_LOCALITY range rng; // the range of frames in the "A" part of the interpolant #endif /* To handle skolemization, we have to scan the proof for skolem symbols and assign each to a frame. THe assignment is heuristic. */ int scan_skolems_rec(hash_map &memo, const ast &proof, int frame){ std::pair foo(proof,INT_MAX); std::pair bar = memo.insert(foo); int &res = bar.first->second; if(!bar.second) return res; pfrule dk = pr(proof); if(dk == PR_ASSERTED){ ast ass = conc(proof); res = frame_of_assertion(ass); } else if(dk == PR_SKOLEMIZE){ ast quanted = arg(conc(proof),0); if(op(quanted) == Not) quanted = arg(quanted,0); // range r = ast_range(quanted); // if(range_is_empty(r)) range r = ast_scope(quanted); if(range_is_empty(r)) throw "can't skolemize"; if(frame == INT_MAX || !in_range(frame,r)) frame = range_max(r); // this is desperation -- may fail if(frame >= frames) frame = frames - 1; add_frame_range(frame,arg(conc(proof),1)); r = ast_scope(arg(conc(proof),1)); } else if(dk==PR_MODUS_PONENS_OEQ){ frame = scan_skolems_rec(memo,prem(proof,0),frame); scan_skolems_rec(memo,prem(proof,1),frame); } else { unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ int bar = scan_skolems_rec(memo,prem(proof,i),frame); if(res == INT_MAX || res == bar) res = bar; else if(bar != INT_MAX) res = -1; } } return res; } void scan_skolems(const ast &proof){ hash_map memo; scan_skolems_rec(memo,proof, INT_MAX); } // determine locality of a proof term // return frame of derivation if local, or -1 if not // result INT_MAX means the proof term is a tautology // memoized in hash_map "locality" int get_locality_rec(ast proof){ std::pair foo(proof,INT_MAX); std::pair bar = locality.insert(foo); int &res = bar.first->second; if(!bar.second) return res; pfrule dk = pr(proof); if(dk == PR_ASSERTED){ ast ass = conc(proof); res = frame_of_assertion(ass); #ifdef NEW_LOCALITY if(in_range(res,rng)) res = range_max(rng); else res = frames-1; #endif } else if(dk == PR_QUANT_INST){ std::vector lits; ast con = conc(proof); get_Z3_lits(con, lits); iproof->make_axiom(lits); } #ifdef LOCALIZATION_KLUDGE else if(dk == PR_MODUS_PONENS && pr(prem(proof,0)) == PR_QUANT_INST && get_locality_rec(prem(proof,1)) == INT_MAX){ std::vector lits; ast con = conc(proof); get_Z3_lits(con, lits); iproof->make_axiom(lits); } #endif else { unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ ast arg = prem(proof,i); int bar = get_locality_rec(arg); if(res == INT_MAX || res == bar) res = bar; else if(bar != INT_MAX) res = -1; } } return res; } int get_locality(ast proof){ // if(lia_z3_axioms_only) return -1; int res = get_locality_rec(proof); if(res != -1){ ast con = conc(proof); range rng = ast_scope(con); // hack: if a clause contains "true", it reduces to "true", // which means we won't compute the range correctly. we handle // this case by computing the ranges of the literals separately if(is_true(con)){ std::vector lits; get_Z3_lits(conc(proof),lits); for(unsigned i = 0; i < lits.size(); i++) rng = range_glb(rng,ast_scope(lits[i])); } if(!range_is_empty(rng)){ AstSet &hyps = get_hyps(proof); for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ ast hyp = *it; rng = range_glb(rng,ast_scope(hyp)); } } if(res == INT_MAX){ if(range_is_empty(rng)) res = -1; else res = range_max(rng); } else { if(!in_range(res,rng)) res = -1; } } return res; } AstSet &get_hyps(ast proof){ std::pair foo(proof,AstSet()); std::pair bar = hyp_map.insert(foo); AstSet &res = bar.first->second; if(!bar.second) return res; pfrule dk = pr(proof); if(dk == PR_HYPOTHESIS){ ast con = conc(proof); res.insert(con); } else { unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ ast arg = prem(proof,i); AstSet &arg_hyps = get_hyps(arg); res.insert(arg_hyps.begin(),arg_hyps.end()); } if(dk == PR_LEMMA){ ast con = conc(proof); res.erase(mk_not(con)); if(is_or(con)){ int clause_size = num_args(con); for(int i = 0; i < clause_size; i++){ ast neglit = mk_not(arg(con,i)); res.erase(neglit); } } } } #if 0 AstSet::iterator it = res.begin(), en = res.end(); if(it != en){ AstSet::iterator old = it; ++it; for(; it != en; ++it, ++old) if(!(*old < *it)) std::cout << "foo!"; } #endif return res; } // Find all the judgements of the form p <-> q, where // p is local and q is non-local, recording them in "equivs" // the map equivs_visited is used to record the already visited proof terms void find_equivs(ast proof){ if(equivs_visited.find(proof) != equivs_visited.end()) return; equivs_visited.insert(proof); unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++) // do all the sub_terms find_equivs(prem(proof,i)); ast con = conc(proof); // get the conclusion if(is_iff(con)){ ast iff = con; for(int i = 0; i < 2; i++) if(!is_local(arg(iff,i)) && is_local(arg(iff,1-i))){ std::pair > foo(arg(iff,i),std::pair(arg(iff,1-i),proof)); equivs.insert(foo); } } } // get the lits of a Z3 clause void get_Z3_lits(ast t, std::vector &lits){ opr dk = op(t); if(dk == False) return; // false = empty clause if(dk == Or){ unsigned nargs = num_args(t); lits.resize(nargs); for(unsigned i = 0; i < nargs; i++) // do all the sub_terms lits[i] = arg(t,i); } else { lits.push_back(t); } } // resolve two clauses represented as vectors of lits. replace first clause void resolve(ast pivot, std::vector &cls1, std::vector &cls2){ ast neg_pivot = mk_not(pivot); for(unsigned i = 0; i < cls1.size(); i++){ if(cls1[i] == pivot){ cls1[i] = cls1.back(); cls1.pop_back(); bool found_pivot2 = false; for(unsigned j = 0; j < cls2.size(); j++){ if(cls2[j] == neg_pivot) found_pivot2 = true; else cls1.push_back(cls2[j]); } assert(found_pivot2); return; } } assert(0 && "resolve failed"); } // get lits resulting from unit resolution up to and including "position" // TODO: this is quadratic -- fix it void do_unit_resolution(ast proof, int position, std::vector &lits){ ast orig_clause = conc(prem(proof,0)); get_Z3_lits(orig_clause,lits); for(int i = 1; i <= position; i++){ std::vector unit(1); unit[0] = conc(prem(proof,i)); resolve(mk_not(unit[0]),lits,unit); } } #if 0 // clear the localization variables void clear_localization(){ localization_vars.clear(); localization_map.clear(); } // create a fresh variable for localization ast fresh_localization_var(ast term, int frame){ std::ostringstream s; s << "%" << (localization_vars.size()); ast var = make_var(s.str().c_str(),get_type(term)); sym_range(sym(var)) = range_full(); // make this variable global localization_vars.push_back(LocVar(var,term,frame)); return var; } // "localize" a term to a given frame range by // creating new symbols to represent non-local subterms ast localize_term(ast e, const range &rng){ if(ranges_intersect(ast_scope(e),rng)) return e; // this term occurs in range, so it's O.K. AstToAst::iterator it = localization_map.find(e); if(it != localization_map.end()) return it->second; // if is is non-local, we must first localize the arguments to // the range of its function symbol int nargs = num_args(e); if(nargs > 0 /* && (!is_local(e) || flo <= hi || fhi >= lo) */){ range frng = rng; if(op(e) == Uninterpreted){ symb f = sym(e); range srng = sym_range(f); if(ranges_intersect(srng,rng)) // localize to desired range if possible frng = range_glb(srng,rng); } std::vector largs(nargs); for(int i = 0; i < nargs; i++){ largs[i] = localize_term(arg(e,i),frng); frng = range_glb(frng,ast_scope(largs[i])); } e = clone(e,largs); assert(is_local(e)); } if(ranges_intersect(ast_scope(e),rng)) return e; // this term occurs in range, so it's O.K. // choose a frame for the constraint that is close to range int frame = range_near(ast_scope(e),rng); ast new_var = fresh_localization_var(e,frame); localization_map[e] = new_var; ast cnst = make(Equal,new_var,e); // antes.push_back(std::pair(cnst,frame)); return new_var; } // some patterm matching functions // match logical or with nargs arguments // assumes AIG form bool match_or(ast e, ast *args, int nargs){ if(op(e) != Or) return false; int n = num_args(e); if(n != nargs) return false; for(int i = 0; i < nargs; i++) args[i] = arg(e,i); return true; } // match operator f with exactly nargs arguments bool match_op(ast e, opr f, ast *args, int nargs){ if(op(e) != f) return false; int n = num_args(e); if(n != nargs) return false; for(int i = 0; i < nargs; i++) args[i] = arg(e,i); return true; } // see if the given formula can be interpreted as // an axiom instance (e.g., an array axiom instance). // if so, add it to "antes" in an appropriate frame. // this may require "localization" void get_axiom_instance(ast e){ // "store" axiom // (or (= w q) (= (select (store a1 w y) q) (select a1 q))) // std::cout << "ax: "; show(e); ast lits[2],eq_ops_l[2],eq_ops_r[2],sel_ops[2], sto_ops[3], sel_ops2[2] ; if(match_or(e,lits,2)) if(match_op(lits[0],Equal,eq_ops_l,2)) if(match_op(lits[1],Equal,eq_ops_r,2)) for(int i = 0; i < 2; i++){ // try the second equality both ways if(match_op(eq_ops_r[0],Select,sel_ops,2)) if(match_op(sel_ops[0],Store,sto_ops,3)) if(match_op(eq_ops_r[1],Select,sel_ops2,2)) for(int j = 0; j < 2; j++){ // try the first equality both ways if(eq_ops_l[0] == sto_ops[1] && eq_ops_l[1] == sel_ops[1] && eq_ops_l[1] == sel_ops2[1] && sto_ops[0] == sel_ops2[0]) if(is_local(sel_ops[0])) // store term must be local { ast sto = sel_ops[0]; ast addr = localize_term(eq_ops_l[1],ast_scope(sto)); ast res = make(Or, make(Equal,eq_ops_l[0],addr), make(Equal, make(Select,sto,addr), make(Select,sel_ops2[0],addr))); // int frame = range_min(ast_scope(res)); TODO // antes.push_back(std::pair(res,frame)); return; } std::swap(eq_ops_l[0],eq_ops_l[1]); } std::swap(eq_ops_r[0],eq_ops_r[1]); } } // a quantifier instantation looks like (~ forall x. P) \/ P[z/x] // we need to find a time frame for P, then localize P[z/x] in this frame void get_quantifier_instance(ast e){ ast disjs[2]; if(match_or(e,disjs,2)){ if(is_local(disjs[0])){ ast res = localize_term(disjs[1], ast_scope(disjs[0])); // int frame = range_min(ast_scope(res)); TODO // antes.push_back(std::pair(res,frame)); return; } } } ast get_judgement(ast proof){ ast con = from_ast(conc(proof)); AstSet &hyps = get_hyps(proof); std::vector hyps_vec; for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) hyps_vec.push_back(*it); if(hyps_vec.size() == 0) return con; con = make(Or,mk_not(make(And,hyps_vec)),con); return con; } // does variable occur in expression? int occurs_in1(ast var, ast e){ std::pair foo(e,false); std::pair bar = occurs_in_memo.insert(foo); bool &res = bar.first->second; if(bar.second){ if(e == var) res = true; int nargs = num_args(e); for(int i = 0; i < nargs; i++) res |= occurs_in1(var,arg(e,i)); } return res; } int occurs_in(ast var, ast e){ occurs_in_memo.clear(); return occurs_in1(var,e); } // find a controlling equality for a given variable v in a term // a controlling equality is of the form v = t, which, being // false would force the formula to have the specifid truth value // returns t, or null if no such ast cont_eq(bool truth, ast v, ast e){ if(is_not(e)) return cont_eq(!truth,v,arg(e,0)); if(cont_eq_memo.find(e) != cont_eq_memo.end()) return ast(); cont_eq_memo.insert(e); if(!truth && op(e) == Equal){ if(arg(e,0) == v) return(arg(e,1)); if(arg(e,1) == v) return(arg(e,0)); } if((!truth && op(e) == And) || (truth && op(e) == Or)){ int nargs = num_args(e); for(int i = 0; i < nargs; i++){ ast res = cont_eq(truth, v, arg(e,i)); if(!res.null()) return res; } } return ast(); } // substitute a term t for unbound occurrences of variable v in e ast subst(ast var, ast t, ast e){ if(e == var) return t; std::pair foo(e,ast()); std::pair bar = subst_memo.insert(foo); ast &res = bar.first->second; if(bar.second){ int nargs = num_args(e); std::vector args(nargs); for(int i = 0; i < nargs; i++) args[i] = subst(var,t,arg(e,i)); opr f = op(e); if(f == Equal && args[0] == args[1]) res = mk_true(); else res = clone(e,args); } return res; } // apply a quantifier to a formula, with some optimizations // 1) bound variable does not occur -> no quantifier // 2) bound variable must be equal to some term -> substitute ast apply_quant(opr quantifier, ast var, ast e){ if(!occurs_in(var,e))return e; cont_eq_memo.clear(); ast cterm = cont_eq(quantifier == Forall, var, e); if(!cterm.null()){ subst_memo.clear(); return subst(var,cterm,e); } std::vector bvs; bvs.push_back(var); return make_quant(quantifier,bvs,e); } // add quantifiers over the localization vars // to an interpolant for frames lo-hi ast add_quants(ast e, int lo, int hi){ for(int i = localization_vars.size() - 1; i >= 0; i--){ LocVar &lv = localization_vars[i]; opr quantifier = (lv.frame >= lo && lv.frame <= hi) ? Exists : Forall; e = apply_quant(quantifier,lv.var,e); } return e; } int get_lits_locality(std::vector &lits){ range rng = range_full(); for(std::vector::iterator it = lits.begin(), en = lits.end(); it != en; ++it){ ast lit = *it; rng = range_glb(rng,ast_scope(lit)); } if(range_is_empty(rng)) return -1; int hi = range_max(rng); if(hi >= frames) return frames - 1; return hi; } #endif int num_lits(ast ast){ opr dk = op(ast); if(dk == False) return 0; if(dk == Or){ unsigned nargs = num_args(ast); int n = 0; for(unsigned i = 0; i < nargs; i++) // do all the sub_terms n += num_lits(arg(ast,i)); return n; } else return 1; } void symbols_out_of_scope_rec(hash_set &memo, hash_set &symb_memo, int frame, const ast &t){ if(memo.find(t) != memo.end()) return; memo.insert(t); if(op(t) == Uninterpreted){ symb s = sym(t); range r = sym_range(s); if(!in_range(frame,r) && symb_memo.find(s) == symb_memo.end()){ std::cout << string_of_symbol(s) << "\n"; symb_memo.insert(s); } } int nargs = num_args(t); for(int i = 0; i < nargs; i++) symbols_out_of_scope_rec(memo,symb_memo,frame,arg(t,i)); } void symbols_out_of_scope(int frame, const ast &t){ hash_set memo; hash_set symb_memo; symbols_out_of_scope_rec(memo,symb_memo,frame,t); } void conc_symbols_out_of_scope(int frame, const ast &t){ symbols_out_of_scope(frame,conc(t)); } std::vector lit_trace; hash_set marked_proofs; bool proof_has_lit(const ast &proof, const ast &lit){ AstSet &hyps = get_hyps(proof); if(hyps.find(mk_not(lit)) != hyps.end()) return true; std::vector lits; ast con = conc(proof); get_Z3_lits(con, lits); for(unsigned i = 0; i < lits.size(); i++) if(lits[i] == lit) return true; return false; } void trace_lit_rec(const ast &lit, const ast &proof, AstHashSet &memo){ if(memo.find(proof) == memo.end()){ memo.insert(proof); AstSet &hyps = get_hyps(proof); std::vector lits; for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) lits.push_back(mk_not(*it)); ast con = conc(proof); get_Z3_lits(con, lits); for(unsigned i = 0; i < lits.size(); i++){ if(lits[i] == lit){ print_expr(std::cout,proof); std::cout << "\n"; marked_proofs.insert(proof); pfrule dk = pr(proof); if(dk == PR_UNIT_RESOLUTION || dk == PR_LEMMA){ unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ ast arg = prem(proof,i); trace_lit_rec(lit,arg,memo); } } else lit_trace.push_back(proof); } } } } ast traced_lit; int trace_lit(const ast &lit, const ast &proof){ marked_proofs.clear(); lit_trace.clear(); traced_lit = lit; AstHashSet memo; trace_lit_rec(lit,proof,memo); return lit_trace.size(); } bool is_literal_or_lit_iff(const ast &lit){ if(my_is_literal(lit)) return true; if(op(lit) == Iff){ return my_is_literal(arg(lit,0)) && my_is_literal(arg(lit,1)); } return false; } bool my_is_literal(const ast &lit){ ast abslit = is_not(lit) ? arg(lit,0) : lit; int f = op(abslit); return !(f == And || f == Or || f == Iff); } hash_map asts_by_id; void print_lit(const ast &lit){ ast abslit = is_not(lit) ? arg(lit,0) : lit; if(!is_literal_or_lit_iff(lit)){ if(is_not(lit)) std::cout << "~"; int id = ast_id(abslit); asts_by_id[id] = abslit; std::cout << "[" << id << "]"; } else print_expr(std::cout,lit); } void expand(int id){ if(asts_by_id.find(id) == asts_by_id.end()) std::cout << "undefined\n"; else { ast lit = asts_by_id[id]; std::string s = string_of_symbol(sym(lit)); std::cout << "(" << s; unsigned nargs = num_args(lit); for(unsigned i = 0; i < nargs; i++){ std::cout << " "; print_lit(arg(lit,i)); } std::cout << ")\n";; } } void show_lit(const ast &lit){ print_lit(lit); std::cout << "\n"; } void print_z3_lit(const ast &a){ print_lit(from_ast(a)); } void show_z3_lit(const ast &a){ print_z3_lit(a); std::cout << "\n"; } void show_con(const ast &proof, bool brief){ if(!traced_lit.null() && proof_has_lit(proof,traced_lit)) std::cout << "(*) "; ast con = conc(proof); AstSet &hyps = get_hyps(proof); int count = 0; for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ if(brief && ++count > 5){ std::cout << "... "; break; } print_lit(*it); std::cout << " "; } std::cout << "|- "; std::vector lits; get_Z3_lits(con,lits); for(unsigned i = 0; i < lits.size(); i++){ print_lit(lits[i]); std::cout << " "; } range r = ast_scope(con); std::cout << " {" << r.lo << "," << r.hi << "}"; std::cout << "\n"; } void show_step(const ast &proof){ std::cout << "\n"; unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ std::cout << "(" << i << ") "; ast arg = prem(proof,i); show_con(arg,true); } std::cout << "|------ "; std::cout << string_of_symbol(sym(proof)) << "\n"; show_con(proof,false); } void show_marked( const ast &proof){ std::cout << "\n"; unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ ast arg = prem(proof,i); if(!traced_lit.null() && proof_has_lit(arg,traced_lit)){ std::cout << "(" << i << ") "; show_con(arg,true); } } } std::vector pfhist; int pfhist_pos; void pfgoto(const ast &proof){ if(pfhist.size() == 0) pfhist_pos = 0; else pfhist_pos++; pfhist.resize(pfhist_pos); pfhist.push_back(proof); show_step(proof); } void pfback(){ if(pfhist_pos > 0){ pfhist_pos--; show_step(pfhist[pfhist_pos]); } } void pffwd(){ if(pfhist_pos < ((int)pfhist.size()) - 1){ pfhist_pos++; show_step(pfhist[pfhist_pos]); } } void pfprem(int i){ if(pfhist.size() > 0){ ast proof = pfhist[pfhist_pos]; unsigned nprems = num_prems(proof); if(i >= 0 && i < (int)nprems) pfgoto(prem(proof,i)); } } // translate a unit resolution sequence Iproof::node translate_ur(ast proof){ ast prem0 = prem(proof,0); Iproof::node itp = translate_main(prem0,true); std::vector clause; ast conc0 = conc(prem0); int nprems = num_prems(proof); if(nprems == 2 && conc0 == mk_not(conc(prem(proof,1)))) clause.push_back(conc0); else get_Z3_lits(conc0,clause); for(int position = 1; position < nprems; position++){ ast ante = prem(proof,position); ast pnode = conc(ante); ast pnode_abs = !is_not(pnode) ? pnode : mk_not(pnode); Iproof::node neg = itp; Iproof::node pos = translate_main(ante, false); if(is_not(pnode)){ pnode = mk_not(pnode); std::swap(neg,pos); } std::vector unit(1); unit[0] = conc(ante); resolve(mk_not(conc(ante)),clause,unit); itp = iproof->make_resolution(pnode,clause,neg,pos); } return itp; } // get an inequality in the form 0 <= t where t is a linear term ast rhs_normalize_inequality(const ast &ineq){ ast zero = make_int("0"); ast thing = make(Leq,zero,zero); linear_comb(thing,make_int("1"),ineq); thing = simplify_ineq(thing); return thing; } bool check_farkas(const std::vector &prems, const ast &con){ ast zero = make_int("0"); ast thing = make(Leq,zero,zero); for(unsigned i = 0; i < prems.size(); i++) linear_comb(thing,make_int(rational(1)),prems[i]); linear_comb(thing,make_int(rational(-1)),con); thing = simplify_ineq(thing); return arg(thing,1) == make_int(rational(0)); } // get an inequality in the form t <= c or t < c, there t is affine and c constant ast normalize_inequality(const ast &ineq){ ast zero = make_int("0"); ast thing = make(Leq,zero,zero); linear_comb(thing,make_int("1"),ineq); thing = simplify_ineq(thing); ast lhs = arg(thing,0); ast rhs = arg(thing,1); opr o = op(rhs); if(o != Numeral){ if(op(rhs) == Plus){ int nargs = num_args(rhs); ast const_term = zero; int i = 0; if(nargs > 0 && op(arg(rhs,0)) == Numeral){ const_term = arg(rhs,0); i++; } if(i < nargs){ std::vector non_const; for(; i < nargs; i++) non_const.push_back(arg(rhs,i)); lhs = make(Sub,lhs,make(Plus,non_const)); } rhs = const_term; } else { lhs = make(Sub,lhs,make(Plus,rhs)); rhs = zero; } lhs = z3_simplify(lhs); rhs = z3_simplify(rhs); thing = make(op(thing),lhs,rhs); } return thing; } void get_linear_coefficients(const ast &t, std::vector &coeffs){ if(op(t) == Plus){ int nargs = num_args(t); for(int i = 0; i < nargs; i++) coeffs.push_back(get_coeff(arg(t,i))); } else coeffs.push_back(get_coeff(t)); } /* given an affine term t, get the GCD of the coefficients in t. */ ast gcd_of_coefficients(const ast &t){ std::vector coeffs; get_linear_coefficients(t,coeffs); if(coeffs.size() == 0) return make_int("1"); // arbitrary rational d = abs(coeffs[0]); for(unsigned i = 1; i < coeffs.size(); i++){ d = gcd(d,coeffs[i]); } return make_int(d); } ast get_bounded_variable(const ast &ineq, bool &lb){ ast nineq = normalize_inequality(ineq); ast lhs = arg(nineq,0); switch(op(lhs)){ case Uninterpreted: lb = false; return lhs; case Times: if(arg(lhs,0) == make_int(rational(1))) lb = false; else if(arg(lhs,0) == make_int(rational(-1))) lb = true; else throw unsupported(); return arg(lhs,1); default: throw unsupported(); } } rational get_term_coefficient(const ast &t1, const ast &v){ ast t = arg(normalize_inequality(t1),0); if(op(t) == Plus){ int nargs = num_args(t); for(int i = 0; i < nargs; i++){ if(get_linear_var(arg(t,i)) == v) return get_coeff(arg(t,i)); } } else if(get_linear_var(t) == v) return get_coeff(t); return rational(0); } Iproof::node GCDtoDivRule(const ast &proof, bool pol, std::vector &coeffs, std::vector &prems, ast &cut_con){ // gather the summands of the desired polarity std::vector my_prems; std::vector my_coeffs; std::vector my_prem_cons; for(unsigned i = pol ? 0 : 1; i < coeffs.size(); i+= 2){ rational &c = coeffs[i]; if(c.is_pos()){ my_prems.push_back(prems[i]); my_coeffs.push_back(make_int(c)); my_prem_cons.push_back(conc(prem(proof,i))); } else if(c.is_neg()){ int j = (i % 2 == 0) ? i + 1 : i - 1; my_prems.push_back(prems[j]); my_coeffs.push_back(make_int(-coeffs[j])); my_prem_cons.push_back(conc(prem(proof,j))); } } ast my_con = sum_inequalities(my_coeffs,my_prem_cons); // handle generalized GCD test. sadly, we dont' get the coefficients... if(coeffs[0].is_zero()){ bool lb; int xtra_prem = 0; ast bv = get_bounded_variable(conc(prem(proof,0)),lb); rational bv_coeff = get_term_coefficient(my_con,bv); if(bv_coeff.is_pos() != lb) xtra_prem = 1; if(bv_coeff.is_neg()) bv_coeff = -bv_coeff; my_prems.push_back(prems[xtra_prem]); my_coeffs.push_back(make_int(bv_coeff)); my_prem_cons.push_back(conc(prem(proof,xtra_prem))); my_con = sum_inequalities(my_coeffs,my_prem_cons); } my_con = normalize_inequality(my_con); Iproof::node hyp = iproof->make_hypothesis(mk_not(my_con)); my_prems.push_back(hyp); my_coeffs.push_back(make_int("1")); my_prem_cons.push_back(mk_not(my_con)); Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_prem_cons,my_coeffs); ast t = arg(my_con,0); ast c = arg(my_con,1); ast d = gcd_of_coefficients(t); t = z3_simplify(mk_idiv(t,d)); c = z3_simplify(mk_idiv(c,d)); cut_con = make(op(my_con),t,c); return iproof->make_cut_rule(my_con,d,cut_con,res); } rational get_first_coefficient(const ast &t, ast &v){ if(op(t) == Plus){ unsigned best_id = UINT_MAX; rational best_coeff(0); int nargs = num_args(t); for(int i = 0; i < nargs; i++) if(op(arg(t,i)) != Numeral){ ast lv = get_linear_var(arg(t,i)); unsigned id = ast_id(lv); if(id < best_id) { v = lv; best_id = id; best_coeff = get_coeff(arg(t,i)); } } return best_coeff; } else if(op(t) != Numeral) return(get_coeff(t)); return rational(0); } ast divide_inequalities(const ast &x, const ast&y){ ast xvar, yvar; rational xcoeff = get_first_coefficient(arg(x,0),xvar); rational ycoeff = get_first_coefficient(arg(y,0),yvar); if(xcoeff == rational(0) || ycoeff == rational(0) || xvar != yvar) throw "bad assign-bounds lemma"; rational ratio = xcoeff/ycoeff; if(denominator(ratio) != rational(1)) throw "bad assign-bounds lemma"; return make_int(ratio); // better be integer! } ast AssignBounds2Farkas(const ast &proof, const ast &con){ std::vector farkas_coeffs; get_assign_bounds_coeffs(proof,farkas_coeffs); int nargs = num_args(con); if(nargs != (int)(farkas_coeffs.size())) throw "bad assign-bounds theory lemma"; #if 0 if(farkas_coeffs[0] != make_int(rational(1))) farkas_coeffs[0] = make_int(rational(1)); #else std::vector lits, lit_coeffs; for(int i = 1; i < nargs; i++){ lits.push_back(mk_not(arg(con,i))); lit_coeffs.push_back(farkas_coeffs[i]); } ast sum = normalize_inequality(sum_inequalities(lit_coeffs,lits)); ast conseq = normalize_inequality(arg(con,0)); ast d = divide_inequalities(sum,conseq); #if 0 if(d != farkas_coeffs[0]) std::cout << "wow!\n"; #endif farkas_coeffs[0] = d; #endif std::vector my_coeffs; std::vector my_cons; for(int i = 1; i < nargs; i++){ my_cons.push_back(mk_not(arg(con,i))); my_coeffs.push_back(farkas_coeffs[i]); } ast farkas_con = normalize_inequality(sum_inequalities(my_coeffs,my_cons,true /* round_off */)); my_cons.push_back(mk_not(farkas_con)); my_coeffs.push_back(make_int("1")); std::vector my_hyps; for(int i = 0; i < nargs; i++) my_hyps.push_back(iproof->make_hypothesis(my_cons[i])); ast res = iproof->make_farkas(mk_false(),my_hyps,my_cons,my_coeffs); res = iproof->make_cut_rule(farkas_con,farkas_coeffs[0],arg(con,0),res); return res; } ast AssignBoundsRule2Farkas(const ast &proof, const ast &con, std::vector prems){ std::vector farkas_coeffs; get_assign_bounds_rule_coeffs(proof,farkas_coeffs); int nargs = num_prems(proof)+1; if(nargs != (int)(farkas_coeffs.size())) throw "bad assign-bounds theory lemma"; #if 0 if(farkas_coeffs[0] != make_int(rational(1))) farkas_coeffs[0] = make_int(rational(1)); #else std::vector lits, lit_coeffs; for(int i = 1; i < nargs; i++){ lits.push_back(conc(prem(proof,i-1))); lit_coeffs.push_back(farkas_coeffs[i]); } ast sum = normalize_inequality(sum_inequalities(lit_coeffs,lits)); ast conseq = normalize_inequality(con); ast d = divide_inequalities(sum,conseq); #if 0 if(d != farkas_coeffs[0]) std::cout << "wow!\n"; #endif farkas_coeffs[0] = d; #endif std::vector my_coeffs; std::vector my_cons; for(int i = 1; i < nargs; i++){ my_cons.push_back(conc(prem(proof,i-1))); my_coeffs.push_back(farkas_coeffs[i]); } ast farkas_con = normalize_inequality(sum_inequalities(my_coeffs,my_cons,true /* round_off */)); std::vector my_hyps; for(int i = 1; i < nargs; i++) my_hyps.push_back(prems[i-1]); my_cons.push_back(mk_not(farkas_con)); my_coeffs.push_back(make_int("1")); my_hyps.push_back(iproof->make_hypothesis(mk_not(farkas_con))); ast res = iproof->make_farkas(mk_false(),my_hyps,my_cons,my_coeffs); res = iproof->make_cut_rule(farkas_con,farkas_coeffs[0],conc(proof),res); return res; } ast GomoryCutRule2Farkas(const ast &proof, const ast &con, std::vector prems){ std::vector my_prems = prems; std::vector my_coeffs; std::vector my_prem_cons; get_gomory_cut_coeffs(proof,my_coeffs); int nargs = num_prems(proof); if(nargs != (int)(my_coeffs.size())) throw "bad gomory-cut theory lemma"; for(int i = 0; i < nargs; i++) my_prem_cons.push_back(conc(prem(proof,i))); ast my_con = normalize_inequality(sum_inequalities(my_coeffs,my_prem_cons)); Iproof::node hyp = iproof->make_hypothesis(mk_not(my_con)); my_prems.push_back(hyp); my_coeffs.push_back(make_int("1")); my_prem_cons.push_back(mk_not(my_con)); Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_prem_cons,my_coeffs); ast t = arg(my_con,0); ast c = arg(my_con,1); ast d = gcd_of_coefficients(t); t = z3_simplify(mk_idiv(t,d)); c = z3_simplify(mk_idiv(c,d)); ast cut_con = make(op(my_con),t,c); return iproof->make_cut_rule(my_con,d,cut_con,res); } Iproof::node RewriteClause(Iproof::node clause, const ast &rew){ if(pr(rew) == PR_MONOTONICITY){ int nequivs = num_prems(rew); for(int i = 0; i < nequivs; i++){ Iproof::node equiv_pf = translate_main(prem(rew,i),false); ast equiv = conc(prem(rew,i)); clause = iproof->make_mp(equiv,clause,equiv_pf); } return clause; } if(pr(rew) == PR_TRANSITIVITY){ clause = RewriteClause(clause,prem(rew,0)); clause = RewriteClause(clause,prem(rew,1)); return clause; } if(pr(rew) == PR_REWRITE){ return clause; // just hope the rewrite does nothing! } throw unsupported(); } // Following code is for elimination of "commutativity" axiom Iproof::node make_commuted_modus_ponens(const ast &proof, const std::vector &args){ ast pf = arg(args[1],0); ast comm_equiv = arg(args[1],1); // equivalence relation with possible commutations ast P = conc(prem(proof,0)); ast Q = conc(proof); Iproof::node P_pf = args[0]; ast P_comm = arg(comm_equiv,0); ast Q_comm = arg(comm_equiv,1); if(P != P_comm) P_pf = iproof->make_symmetry(P_comm,P,P_pf); Iproof::node res = iproof->make_mp(comm_equiv,P_pf,pf); if(Q != Q_comm) res = iproof->make_symmetry(Q,Q_comm,res); return res; } Iproof::node make_commuted_monotonicity(const ast &proof, const std::vector &args){ ast pf = arg(args[0],0); ast comm_equiv = arg(args[0],1); // equivalence relation with possible commutations ast con = make(Iff,make(Not,arg(comm_equiv,0)),make(Not,arg(comm_equiv,1))); std::vector eqs; eqs.push_back(comm_equiv); std::vector pfs; pfs.push_back(pf); ast res = iproof->make_congruence(eqs,con,pfs); res = make(commute,res,con); return res; } Iproof::node make_commuted_symmetry(const ast &proof, const std::vector &args){ ast pf = arg(args[0],0); ast comm_equiv = arg(args[0],1); // equivalence relation with possible commutations ast con = make(Iff,arg(comm_equiv,1),arg(comm_equiv,0)); ast res = iproof->make_symmetry(con,comm_equiv,pf); res = make(commute,res,con); return res; } void unpack_commuted(const ast &proof, const ast &cm, ast &pf, ast &comm_equiv){ if(sym(cm) == commute){ pf = arg(cm,0); comm_equiv = arg(cm,1); } else { pf = cm; comm_equiv = conc(proof); } } Iproof::node make_commuted_transitivity(const ast &proof, const std::vector &args){ ast pf[2], comm_equiv[2]; for(int i = 0; i < 2; i++) unpack_commuted(prem(proof,i),args[i],pf[i],comm_equiv[i]); if(!(arg(comm_equiv[0],1) == arg(comm_equiv[1],0))){ ast tw = twist(prem(proof,1)); ast np = translate_main(tw,false); unpack_commuted(tw,np,pf[1],comm_equiv[1]); } ast con = make(Iff,arg(comm_equiv[0],0),arg(comm_equiv[1],1)); ast res = iproof->make_transitivity(arg(comm_equiv[0],0),arg(comm_equiv[0],1),arg(comm_equiv[1],1),pf[0],pf[1]); res = make(commute,res,con); return res; } ast commute_equality(const ast &eq){ return make(Equal,arg(eq,1),arg(eq,0)); } ast commute_equality_iff(const ast &con){ if(op(con) != Iff || op(arg(con,0)) != Equal) throw unsupported(); return make(Iff,commute_equality(arg(con,0)),commute_equality(arg(con,1))); } // convert a proof of a=b <-> c=d into a proof of b=a <-> d=c // TODO: memoize this? ast twist(const ast &proof){ pfrule dk = pr(proof); ast con = commute_equality_iff(conc(proof)); int n = num_prems(proof); std::vector prs(n); if(dk == PR_MONOTONICITY){ for(int i = 0; i < n; i++) prs[i] = prem(proof,i); } else for(int i = 0; i < n; i++) prs[i] = twist(prem(proof,i)); switch(dk){ case PR_MONOTONICITY: case PR_SYMMETRY: case PR_TRANSITIVITY: case PR_COMMUTATIVITY: prs.push_back(con); return clone(proof,prs); default: throw unsupported(); } } struct TermLt { iz3mgr &m; bool operator()(const ast &x, const ast &y){ unsigned xid = m.ast_id(x); unsigned yid = m.ast_id(y); return xid < yid; } TermLt(iz3mgr &_m) : m(_m) {} }; void SortTerms(std::vector &terms){ TermLt foo(*this); std::sort(terms.begin(),terms.end(),foo); } ast SortSum(const ast &t){ if(!(op(t) == Plus)) return t; int nargs = num_args(t); if(nargs < 2) return t; std::vector args(nargs); for(int i = 0; i < nargs; i++) args[i] = arg(t,i); SortTerms(args); return make(Plus,args); } void get_sum_as_vector(const ast &t, std::vector &coeffs, std::vector &vars){ if(!(op(t) == Plus)){ coeffs.push_back(get_coeff(t)); vars.push_back(get_linear_var(t)); } else { int nargs = num_args(t); for(int i = 0; i < nargs; i++) get_sum_as_vector(arg(t,i),coeffs,vars); } } ast replace_summands_with_fresh_vars(const ast &t, hash_map &map){ if(op(t) == Plus){ int nargs = num_args(t); std::vector args(nargs); for(int i = 0; i < nargs; i++) args[i] = replace_summands_with_fresh_vars(arg(t,i),map); return make(Plus,args); } if(op(t) == Times) return make(Times,arg(t,0),replace_summands_with_fresh_vars(arg(t,1),map)); if(map.find(t) == map.end()) map[t] = mk_fresh_constant("@s",get_type(t)); return map[t]; } rational lcd(const std::vector &rats){ rational res = rational(1); for(unsigned i = 0; i < rats.size(); i++){ res = lcm(res,denominator(rats[i])); } return res; } Iproof::node reconstruct_farkas_with_dual(const std::vector &prems, const std::vector &pfs, const ast &con){ int nprems = prems.size(); std::vector npcons(nprems); hash_map pain_map; // not needed for(int i = 0; i < nprems; i++){ npcons[i] = painfully_normalize_ineq(conc(prems[i]),pain_map); if(op(npcons[i]) == Lt){ ast constval = z3_simplify(make(Sub,arg(npcons[i],1),make_int(rational(1)))); npcons[i] = make(Leq,arg(npcons[i],0),constval); } } ast ncon = painfully_normalize_ineq(mk_not(con),pain_map); npcons.push_back(ncon); hash_map dual_map; std::vector cvec, vars_seen; ast rhs = make_real(rational(0)); for(unsigned i = 0; i < npcons.size(); i++){ ast c= mk_fresh_constant("@c",real_type()); cvec.push_back(c); ast lhs = arg(npcons[i],0); std::vector coeffs; std::vector vars; get_sum_as_vector(lhs,coeffs,vars); for(unsigned j = 0; j < coeffs.size(); j++){ rational coeff = coeffs[j]; ast var = vars[j]; if(dual_map.find(var) == dual_map.end()){ dual_map[var] = make_real(rational(0)); vars_seen.push_back(var); } ast foo = make(Plus,dual_map[var],make(Times,make_real(coeff),c)); dual_map[var] = foo; } rhs = make(Plus,rhs,make(Times,c,arg(npcons[i],1))); } std::vector cnstrs; for(unsigned i = 0; i < vars_seen.size(); i++) cnstrs.push_back(make(Equal,dual_map[vars_seen[i]],make_real(rational(0)))); cnstrs.push_back(make(Leq,rhs,make_real(rational(0)))); for(unsigned i = 0; i < cvec.size() - 1; i++) cnstrs.push_back(make(Geq,cvec[i],make_real(rational(0)))); cnstrs.push_back(make(Equal,cvec.back(),make_real(rational(1)))); ast new_proof; // greedily reduce the core for(unsigned i = 0; i < cvec.size() - 1; i++){ std::vector dummy; cnstrs.push_back(make(Equal,cvec[i],make_real(rational(0)))); if(!is_sat(cnstrs,new_proof,dummy)) cnstrs.pop_back(); } std::vector vals = cvec; if(!is_sat(cnstrs,new_proof,vals)) throw "Proof error!"; std::vector rat_farkas_coeffs; for(unsigned i = 0; i < cvec.size(); i++){ ast bar = vals[i]; rational r; if(is_numeral(bar,r)) rat_farkas_coeffs.push_back(r); else throw "Proof error!"; } rational the_lcd = lcd(rat_farkas_coeffs); std::vector farkas_coeffs; std::vector my_prems; std::vector my_pcons; for(unsigned i = 0; i < prems.size(); i++){ ast fc = make_int(rat_farkas_coeffs[i] * the_lcd); if(!(fc == make_int(rational(0)))){ farkas_coeffs.push_back(fc); my_prems.push_back(pfs[i]); my_pcons.push_back(conc(prems[i])); } } farkas_coeffs.push_back(make_int(the_lcd)); my_prems.push_back(iproof->make_hypothesis(mk_not(con))); my_pcons.push_back(mk_not(con)); Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); return res; } ast painfully_normalize_ineq(const ast &ineq, hash_map &map){ ast res = normalize_inequality(ineq); ast lhs = arg(res,0); lhs = replace_summands_with_fresh_vars(lhs,map); res = make(op(res),SortSum(lhs),arg(res,1)); return res; } Iproof::node painfully_reconstruct_farkas(const std::vector &prems, const std::vector &pfs, const ast &con){ int nprems = prems.size(); std::vector pcons(nprems),npcons(nprems); hash_map pcon_to_pf, npcon_to_pcon, pain_map; for(int i = 0; i < nprems; i++){ pcons[i] = conc(prems[i]); npcons[i] = painfully_normalize_ineq(pcons[i],pain_map); pcon_to_pf[npcons[i]] = pfs[i]; npcon_to_pcon[npcons[i]] = pcons[i]; } // ast leq = make(Leq,arg(con,0),arg(con,1)); ast ncon = painfully_normalize_ineq(mk_not(con),pain_map); pcons.push_back(mk_not(con)); npcons.push_back(ncon); // ast assumps = make(And,pcons); ast new_proof; std::vector dummy; if(is_sat(npcons,new_proof,dummy)) throw "Proof error!"; pfrule dk = pr(new_proof); int nnp = num_prems(new_proof); std::vector my_prems; std::vector farkas_coeffs, my_pcons; if(dk == PR_TH_LEMMA && get_theory_lemma_theory(new_proof) == ArithTheory && get_theory_lemma_kind(new_proof) == FarkasKind) get_farkas_coeffs(new_proof,farkas_coeffs); else if(dk == PR_UNIT_RESOLUTION && nnp == 2){ for(int i = 0; i < nprems; i++) farkas_coeffs.push_back(make_int(rational(1))); } else return reconstruct_farkas_with_dual(prems,pfs,con); for(int i = 0; i < nnp; i++){ ast p = conc(prem(new_proof,i)); p = really_normalize_ineq(p); if(pcon_to_pf.find(p) != pcon_to_pf.end()){ my_prems.push_back(pcon_to_pf[p]); my_pcons.push_back(npcon_to_pcon[p]); } else if(p == ncon){ my_prems.push_back(iproof->make_hypothesis(mk_not(con))); my_pcons.push_back(mk_not(con)); } else return reconstruct_farkas_with_dual(prems,pfs,con); } Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); return res; } ast really_normalize_ineq(const ast &ineq){ ast res = normalize_inequality(ineq); res = make(op(res),SortSum(arg(res,0)),arg(res,1)); return res; } Iproof::node reconstruct_farkas(const std::vector &prems, const std::vector &pfs, const ast &con){ int nprems = prems.size(); std::vector pcons(nprems),npcons(nprems); hash_map pcon_to_pf, npcon_to_pcon; for(int i = 0; i < nprems; i++){ pcons[i] = conc(prems[i]); npcons[i] = really_normalize_ineq(pcons[i]); pcon_to_pf[npcons[i]] = pfs[i]; npcon_to_pcon[npcons[i]] = pcons[i]; } // ast leq = make(Leq,arg(con,0),arg(con,1)); ast ncon = really_normalize_ineq(mk_not(con)); pcons.push_back(mk_not(con)); npcons.push_back(ncon); // ast assumps = make(And,pcons); ast new_proof; std::vector dummy; if(is_sat(npcons,new_proof,dummy)) throw "Proof error!"; pfrule dk = pr(new_proof); int nnp = num_prems(new_proof); std::vector my_prems; std::vector farkas_coeffs, my_pcons; if(dk == PR_TH_LEMMA && get_theory_lemma_theory(new_proof) == ArithTheory && get_theory_lemma_kind(new_proof) == FarkasKind) get_farkas_coeffs(new_proof,farkas_coeffs); else if(dk == PR_UNIT_RESOLUTION && nnp == 2){ for(int i = 0; i < nprems; i++) farkas_coeffs.push_back(make_int(rational(1))); } else return painfully_reconstruct_farkas(prems,pfs,con); for(int i = 0; i < nnp; i++){ ast p = conc(prem(new_proof,i)); p = really_normalize_ineq(p); if(pcon_to_pf.find(p) != pcon_to_pf.end()){ my_prems.push_back(pcon_to_pf[p]); my_pcons.push_back(npcon_to_pcon[p]); } else if(p == ncon){ my_prems.push_back(iproof->make_hypothesis(mk_not(con))); my_pcons.push_back(mk_not(con)); } else return painfully_reconstruct_farkas(prems,pfs,con); } Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); return res; } bool is_eq_propagate(const ast &proof){ return pr(proof) == PR_TH_LEMMA && get_theory_lemma_theory(proof) == ArithTheory && get_theory_lemma_kind(proof) == EqPropagateKind; } ast EqPropagate(const ast &con, const std::vector &prems, const std::vector &args){ Iproof::node fps[2]; ast ineq_con[2]; for(int i = 0; i < 2; i++){ opr o = i == 0 ? Leq : Geq; ineq_con[i] = make(o, arg(con,0), arg(con,1)); fps[i] = reconstruct_farkas(prems,args,ineq_con[i]); } ast res = iproof->make_leq2eq(arg(con,0), arg(con,1), ineq_con[0], ineq_con[1]); std::vector dummy_clause; for(int i = 0; i < 2; i++) res = iproof->make_resolution(ineq_con[i],dummy_clause,res,fps[i]); return res; } ast ArithMysteryRule(const ast &con, const std::vector &prems, const std::vector &args){ // Hope for the best! Iproof::node guess = reconstruct_farkas(prems,args,con); return guess; } struct CannotCombineEqPropagate {}; void CombineEqPropagateRec(const ast &proof, std::vector &prems, std::vector &args, ast &eqprem){ if(pr(proof) == PR_TRANSITIVITY && is_eq_propagate(prem(proof,1))){ CombineEqPropagateRec(prem(proof,0), prems, args, eqprem); ast dummy; CombineEqPropagateRec(prem(proof,1), prems, args, dummy); return; } if(is_eq_propagate(proof)){ int nprems = num_prems(proof); for(int i = 0; i < nprems; i++){ prems.push_back(prem(proof,i)); ast ppf = translate_main(prem(proof,i),false); args.push_back(ppf); } return; } eqprem = proof; } ast CombineEqPropagate(const ast &proof){ std::vector prems, args; ast eq1; CombineEqPropagateRec(proof, prems, args, eq1); ast eq2con = conc(proof); if(!eq1.null()) eq2con = make(Equal,arg(conc(eq1),1),arg(conc(proof),1)); ast eq2 = EqPropagate(eq2con,prems,args); if(!eq1.null()){ Iproof::node foo = translate_main(eq1,false); eq2 = iproof->make_transitivity(arg(conc(eq1),0), arg(conc(eq1),1), arg(conc(proof),1), foo, eq2); } return eq2; } bool get_store_array(const ast &t, ast &res){ if(op(t) == Store){ res = t; return true; } int nargs = num_args(t); for(int i = 0; i < nargs; i++) if(get_store_array(arg(t,i),res)) return true; return false; } // translate a Z3 proof term into interpolating proof system Iproof::node translate_main(ast proof, bool expect_clause = true){ AstToIpf &tr = translation; hash_map &trc = expect_clause ? tr.first : tr.second; std::pair foo(proof,Iproof::node()); std::pair::iterator, bool> bar = trc.insert(foo); Iproof::node &res = bar.first->second; if(!bar.second) return res; // Try the locality rule first int frame = get_locality(proof); if(frame != -1){ ast e = from_ast(conc(proof)); if(frame >= frames) frame = frames - 1; std::vector foo; if(expect_clause) get_Z3_lits(conc(proof),foo); else foo.push_back(e); AstSet &hyps = get_hyps(proof); for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) foo.push_back(mk_not(*it)); res = iproof->make_assumption(frame,foo); return res; } // If the proof is not local, break it down by proof rule pfrule dk = pr(proof); unsigned nprems = num_prems(proof); if(dk == PR_UNIT_RESOLUTION){ res = translate_ur(proof); } else if(dk == PR_LEMMA){ ast contra = prem(proof,0); // this is a proof of false from some hyps res = translate_main(contra); if(!expect_clause){ std::vector foo; // the negations of the hyps form a clause foo.push_back(from_ast(conc(proof))); AstSet &hyps = get_hyps(proof); for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) foo.push_back(mk_not(*it)); res = iproof->make_contra(res,foo); } } else { std::vector lits; ast con = conc(proof); if(expect_clause) get_Z3_lits(con, lits); else lits.push_back(from_ast(con)); // pattern match some idioms if(dk == PR_MODUS_PONENS && pr(prem(proof,0)) == PR_QUANT_INST){ if(get_locality_rec(prem(proof,1)) == INT_MAX) { res = iproof->make_axiom(lits); return res; } } if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or && op(conc(prem(proof,0))) == Or){ Iproof::node clause = translate_main(prem(proof,0),true); res = RewriteClause(clause,prem(proof,1)); return res; } #if 0 if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or) std::cout << "foo!\n"; #endif // no idea why this shows up if(dk == PR_MODUS_PONENS_OEQ){ if(conc(prem(proof,0)) == con){ res = translate_main(prem(proof,0),expect_clause); return res; } if(expect_clause && op(con) == Or){ // skolemization does this Iproof::node clause = translate_main(prem(proof,0),true); res = RewriteClause(clause,prem(proof,1)); return res; } } #if 0 if(1 && dk == PR_TRANSITIVITY && pr(prem(proof,1)) == PR_COMMUTATIVITY){ Iproof::node clause = translate_main(prem(proof,0),true); res = make(commute,clause,conc(prem(proof,0))); // HACK -- we depend on Iproof::node being same as ast. return res; } if(1 && dk == PR_TRANSITIVITY && pr(prem(proof,0)) == PR_COMMUTATIVITY){ Iproof::node clause = translate_main(prem(proof,1),true); res = make(commute,clause,conc(prem(proof,1))); // HACK -- we depend on Iproof::node being same as ast. return res; } #endif if(dk == PR_TRANSITIVITY && is_eq_propagate(prem(proof,1))){ try { res = CombineEqPropagate(proof); return res; } catch(const CannotCombineEqPropagate &){ } } /* this is the symmetry rule for ~=, that is, takes x ~= y and yields y ~= x. the proof idiom uses commutativity, monotonicity and mp, but we replace it here with symmtrey and resolution, that is, we prove y = x |- x = y, then resolve with the proof of ~(x=y) to get ~y=x. */ if(dk == PR_MODUS_PONENS && pr(prem(proof,1)) == PR_MONOTONICITY && pr(prem(prem(proof,1),0)) == PR_COMMUTATIVITY && num_prems(prem(proof,1)) == 1){ Iproof::node ante = translate_main(prem(proof,0),false); ast eq0 = arg(conc(prem(prem(proof,1),0)),0); ast eq1 = arg(conc(prem(prem(proof,1),0)),1); Iproof::node eq1hy = iproof->make_hypothesis(eq1); Iproof::node eq0pf = iproof->make_symmetry(eq0,eq1,eq1hy); std::vector clause; // just a dummy res = iproof->make_resolution(eq0,clause,ante,eq0pf); return res; } /* This idiom takes ~P and Q=P, yielding ~Q. It uses a "rewrite" (Q=false) = ~Q. We eliminate the rewrite by using symmetry, congruence and modus ponens. */ if(dk == PR_MODUS_PONENS && pr(prem(proof,1)) == PR_REWRITE && pr(prem(proof,0)) == PR_TRANSITIVITY && pr(prem(prem(proof,0),1)) == PR_IFF_FALSE){ if(op(con) == Not && arg(con,0) == arg(conc(prem(proof,0)),0)){ Iproof::node ante1 = translate_main(prem(prem(proof,0),0),false); Iproof::node ante2 = translate_main(prem(prem(prem(proof,0),1),0),false); ast ante1_con = conc(prem(prem(proof,0),0)); ast eq0 = arg(ante1_con,0); ast eq1 = arg(ante1_con,1); ast symm_con = make(Iff,eq1,eq0); Iproof::node ante1s = iproof->make_symmetry(symm_con,ante1_con,ante1); ast cong_con = make(Iff,make(Not,eq1),make(Not,eq0)); Iproof::node ante1sc = iproof->make_congruence(symm_con,cong_con,ante1s); res = iproof->make_mp(cong_con,ante2,ante1sc); return res; } } // translate all the premises std::vector args(nprems); for(unsigned i = 0; i < nprems; i++) args[i] = translate_main(prem(proof,i),false); for(unsigned i = 0; i < nprems; i++) if(sym(args[i]) == commute && !(dk == PR_TRANSITIVITY || dk == PR_MODUS_PONENS || dk == PR_SYMMETRY || (dk == PR_MONOTONICITY && op(arg(con,0)) == Not))) throw unsupported(); switch(dk){ case PR_TRANSITIVITY: { if(sym(args[0]) == commute || sym(args[1]) == commute) res = make_commuted_transitivity(proof,args); else { // assume the premises are x = y, y = z ast x = arg(conc(prem(proof,0)),0); ast y = arg(conc(prem(proof,0)),1); ast z = arg(conc(prem(proof,1)),1); res = iproof->make_transitivity(x,y,z,args[0],args[1]); } break; } case PR_QUANT_INTRO: case PR_MONOTONICITY: { std::vector eqs; eqs.resize(args.size()); for(unsigned i = 0; i < args.size(); i++) eqs[i] = conc(prem(proof,i)); if(op(arg(con,0)) == Not && sym(args[0]) == commute) res = make_commuted_monotonicity(proof,args); else res = iproof->make_congruence(eqs,con,args); break; } case PR_REFLEXIVITY: { res = iproof->make_reflexivity(con); break; } case PR_SYMMETRY: { if(sym(args[0]) == commute) res = make_commuted_symmetry(proof,args); else res = iproof->make_symmetry(con,conc(prem(proof,0)),args[0]); break; } case PR_MODUS_PONENS: { if(sym(args[1]) == commute) res = make_commuted_modus_ponens(proof,args); else res = iproof->make_mp(conc(prem(proof,1)),args[0],args[1]); break; } case PR_TH_LEMMA: { switch(get_theory_lemma_theory(proof)){ case ArithTheory: switch(get_theory_lemma_kind(proof)){ case FarkasKind: { std::vector farkas_coeffs, prem_cons; get_farkas_coeffs(proof,farkas_coeffs); if(nprems == 0) {// axiom, not rule int nargs = num_args(con); if(farkas_coeffs.size() != (unsigned)nargs){ pfgoto(proof); throw unsupported(); } for(int i = 0; i < nargs; i++){ ast lit = mk_not(arg(con,i)); prem_cons.push_back(lit); args.push_back(iproof->make_hypothesis(lit)); } } else { // rule version (proves false) prem_cons.resize(nprems); for(unsigned i = 0; i < nprems; i++) prem_cons[i] = conc(prem(proof,i)); } res = iproof->make_farkas(con,args,prem_cons,farkas_coeffs); break; } case Leq2EqKind: { // conc should be (or x = y (not (leq x y)) (not(leq y z)) ) ast xeqy = arg(conc(proof),0); ast x = arg(xeqy,0); ast y = arg(xeqy,1); res = iproof->make_leq2eq(x,y,arg(arg(conc(proof),1),0),arg(arg(conc(proof),2),0)); break; } case Eq2LeqKind: { // conc should be (or (not (= x y)) (leq x y)) ast xeqy = arg(arg(conc(proof),0),0); ast xleqy = arg(conc(proof),1); ast x = arg(xeqy,0); ast y = arg(xeqy,1); res = iproof->make_eq2leq(x,y,xleqy); break; } case GCDTestKind: { std::vector farkas_coeffs; get_broken_gcd_test_coeffs(proof,farkas_coeffs); if(farkas_coeffs.size() != nprems){ pfgoto(proof); throw unsupported(); } std::vector my_prems; my_prems.resize(2); std::vector my_prem_cons; my_prem_cons.resize(2); std::vector my_farkas_coeffs; my_farkas_coeffs.resize(2); my_prems[0] = GCDtoDivRule(proof, true, farkas_coeffs, args, my_prem_cons[0]); my_prems[1] = GCDtoDivRule(proof, false, farkas_coeffs, args, my_prem_cons[1]); ast con = mk_false(); my_farkas_coeffs[0] = my_farkas_coeffs[1] = make_int("1"); res = iproof->make_farkas(con,my_prems,my_prem_cons,my_farkas_coeffs); break; } case AssignBoundsKind: { if(args.size() > 0) res = AssignBoundsRule2Farkas(proof, conc(proof), args); else res = AssignBounds2Farkas(proof,conc(proof)); break; } case GomoryCutKind: { if(args.size() > 0) res = GomoryCutRule2Farkas(proof, conc(proof), args); else throw unsupported(); break; } case EqPropagateKind: { std::vector prems(nprems); for(unsigned i = 0; i < nprems; i++) prems[i] = prem(proof,i); res = EqPropagate(con,prems,args); break; } case ArithMysteryKind: { // Z3 hasn't told us what kind of lemma this is -- maybe we can guess std::vector prems(nprems); for(unsigned i = 0; i < nprems; i++) prems[i] = prem(proof,i); res = ArithMysteryRule(con,prems,args); break; } default: throw unsupported(); } break; case ArrayTheory: {// nothing fancy for this ast store_array; if(get_store_array(con,store_array)) res = iproof->make_axiom(lits,ast_scope(store_array)); else res = iproof->make_axiom(lits); // for array extensionality axiom break; } default: throw unsupported(); } break; } case PR_HYPOTHESIS: { res = iproof->make_hypothesis(conc(proof)); break; } case PR_QUANT_INST: { res = iproof->make_axiom(lits); break; } case PR_DEF_AXIOM: { // this should only happen for formulas resulting from quantifier instantiation res = iproof->make_axiom(lits); break; } case PR_IFF_TRUE: { // turns p into p <-> true, noop for us res = args[0]; break; } case PR_IFF_FALSE: { // turns ~p into p <-> false, noop for us if(is_local(con)) res = args[0]; else throw unsupported(); break; } case PR_COMMUTATIVITY: { ast comm_equiv = make(op(con),arg(con,0),arg(con,0)); ast pf = iproof->make_reflexivity(comm_equiv); res = make(commute,pf,comm_equiv); break; } case PR_AND_ELIM: { std::vector rule_ax, res_conc; ast piv = conc(prem(proof,0)); rule_ax.push_back(make(Not,piv)); rule_ax.push_back(con); ast pf = iproof->make_axiom(rule_ax); res_conc.push_back(con); res = iproof->make_resolution(piv,res_conc,pf,args[0]); break; } default: pfgoto(proof); assert(0 && "translate_main: unsupported proof rule"); throw unsupported(); } } return res; } void clear_translation(){ translation.first.clear(); translation.second.clear(); } // We actually compute the interpolant here and then produce a proof consisting of just a lemma iz3proof::node translate(ast proof, iz3proof &dst){ std::vector itps; scan_skolems(proof); for(int i = 0; i < frames -1; i++){ #ifdef NEW_LOCALITY rng = range_downward(i); locality.clear(); #endif iproof = iz3proof_itp::create(this,range_downward(i),weak_mode()); Iproof::node ipf = translate_main(proof); ast itp = iproof->interpolate(ipf); itps.push_back(itp); delete iproof; clear_translation(); } // Very simple proof -- lemma of the empty clause with computed interpolation iz3proof::node Ipf = dst.make_lemma(std::vector(),itps); // builds result in dst return Ipf; } iz3translation_full(iz3mgr &mgr, iz3secondary *_secondary, const std::vector > &cnsts, const std::vector &parents, const std::vector &theory) : iz3translation(mgr, cnsts, parents, theory) { frames = cnsts.size(); traced_lit = ast(); type boolbooldom[2] = {bool_type(),bool_type()}; commute = function("@commute",2,boolbooldom,bool_type()); m().inc_ref(commute); } ~iz3translation_full(){ m().dec_ref(commute); } }; #ifdef IZ3_TRANSLATE_FULL iz3translation *iz3translation::create(iz3mgr &mgr, iz3secondary *secondary, const std::vector > &cnsts, const std::vector &parents, const std::vector &theory){ return new iz3translation_full(mgr,secondary,cnsts,parents,theory); } #if 1 // This is just to make sure certain methods are compiled, so we can call then from the debugger. void iz3translation_full_trace_lit(iz3translation_full *p, iz3mgr::ast lit, iz3mgr::ast proof){ p->trace_lit(lit, proof); } void iz3translation_full_show_step(iz3translation_full *p, iz3mgr::ast proof){ p->show_step(proof); } void iz3translation_full_show_marked(iz3translation_full *p, iz3mgr::ast proof){ p->show_marked(proof); } void iz3translation_full_show_lit(iz3translation_full *p, iz3mgr::ast lit){ p->show_lit(lit); } void iz3translation_full_show_z3_lit(iz3translation_full *p, iz3mgr::ast a){ p->show_z3_lit(a); } void iz3translation_full_pfgoto(iz3translation_full *p, iz3mgr::ast proof){ p->pfgoto(proof); } void iz3translation_full_pfback(iz3translation_full *p ){ p->pfback(); } void iz3translation_full_pffwd(iz3translation_full *p ){ p->pffwd(); } void iz3translation_full_pfprem(iz3translation_full *p, int i){ p->pfprem(i); } void iz3translation_full_expand(iz3translation_full *p, int i){ p->expand(i); } void iz3translation_full_symbols_out_of_scope(iz3translation_full *p, int i, const iz3mgr::ast &t){ p->symbols_out_of_scope(i,t); } void iz3translation_full_conc_symbols_out_of_scope(iz3translation_full *p, int i, const iz3mgr::ast &t){ p->conc_symbols_out_of_scope(i,t); } struct stdio_fixer { stdio_fixer(){ std::cout.rdbuf()->pubsetbuf(0,0); } } my_stdio_fixer; #endif #endif z3-4.4.0/src/interp/iz3foci.h0000755000175000017500000000064512540347414015464 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3foci.h Abstract: Implements a secondary solver using foci2. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef IZ3FOCI_H #define IZ3FOCI_H #include "iz3secondary.h" /** Secondary prover based on Cadence FOCI. */ class iz3foci { public: static iz3secondary *create(iz3mgr *mgr, int num, int *parents); }; #endif z3-4.4.0/src/interp/iz3params.pyg0000644000175000017500000000047212540347414016372 0ustar michaelmichaeldef_module_params('interp', description='interpolation parameters', export=True, params=(('profile', BOOL, False, '(INTERP) profile interpolation'), ('check', BOOL, False, '(INTERP) check interpolants'), )) z3-4.4.0/src/interp/iz3scopes.cpp0000755000175000017500000002145312540347414016373 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3scopes.cpp Abstract: Calculations with scopes, for both sequence and tree interpolation. Author: Ken McMillan (kenmcmil) Revision History: --*/ #include #include #include "iz3scopes.h" /** computes the least common ancestor of two nodes in the tree, or SHRT_MAX if none */ int scopes::tree_lca(int n1, int n2){ if(!tree_mode()) return std::max(n1,n2); if(n1 == SHRT_MIN) return n2; if(n2 == SHRT_MIN) return n1; if(n1 == SHRT_MAX || n2 == SHRT_MAX) return SHRT_MAX; while(n1 != n2){ if(n1 == SHRT_MAX || n2 == SHRT_MAX) return SHRT_MAX; assert(n1 >= 0 && n2 >= 0 && n1 < (int)parents.size() && n2 < (int)parents.size()); if(n1 < n2) n1 = parents[n1]; else n2 = parents[n2]; } return n1; } /** computes the greatest common descendant two nodes in the tree, or SHRT_MIN if none */ int scopes::tree_gcd(int n1, int n2){ if(!tree_mode()) return std::min(n1,n2); int foo = tree_lca(n1,n2); if(foo == n1) return n2; if(foo == n2) return n1; return SHRT_MIN; } #ifndef FULL_TREE /** test whether a tree node is contained in a range */ bool scopes::in_range(int n, const range &rng){ return tree_lca(rng.lo,n) == n && tree_gcd(rng.hi,n) == n; } /** test whether two ranges of tree nodes intersect */ bool scopes::ranges_intersect(const range &rng1, const range &rng2){ return tree_lca(rng1.lo,rng2.hi) == rng2.hi && tree_lca(rng1.hi,rng2.lo) == rng1.hi; } bool scopes::range_contained(const range &rng1, const range &rng2){ return tree_lca(rng2.lo,rng1.lo) == rng1.lo && tree_lca(rng1.hi,rng2.hi) == rng2.hi; } scopes::range scopes::range_lub(const range &rng1, const range &rng2){ range res; res.lo = tree_gcd(rng1.lo,rng2.lo); res.hi = tree_lca(rng1.hi,rng2.hi); return res; } scopes::range scopes::range_glb(const range &rng1, const range &rng2){ range res; res.lo = tree_lca(rng1.lo,rng2.lo); res.hi = tree_gcd(rng1.hi,rng2.hi); return res; } #else namespace std { template <> class hash { public: size_t operator()(const scopes::range_lo &p) const { return p.lo + (size_t)p.next; } }; } template <> inline size_t stdext::hash_value(const scopes::range_lo& p) { std::hash h; return h(p); } namespace std { template <> class less { public: bool operator()(const scopes::range_lo &x, const scopes::range_lo &y) const { return x.lo < y.lo || x.lo == y.lo && (size_t)x.next < (size_t)y.next; } }; } struct range_op { scopes::range_lo *x, *y; int hi; range_op(scopes::range_lo *_x, scopes::range_lo *_y, int _hi){ x = _x; y = _y; hi = _hi; } }; namespace std { template <> class hash { public: size_t operator()(const range_op &p) const { return (size_t) p.x + (size_t)p.y + p.hi; } }; } template <> inline size_t stdext::hash_value(const range_op& p) { std::hash h; return h(p); } namespace std { template <> class less { public: bool operator()(const range_op &x, const range_op &y) const { return (size_t)x.x < (size_t)y.x || x.x == y.x && ((size_t)x.y < (size_t)y.y || x.y == y.y && x.hi < y.hi); } }; } struct range_tables { hash_map unique; hash_map lub; hash_map glb; }; scopes::range_lo *scopes::find_range_lo(int lo, range_lo *next){ range_lo foo(lo,next); std::pair baz(foo,(range_lo *)0); std::pair::iterator,bool> bar = rt->unique.insert(baz); if(bar.second) bar.first->second = new range_lo(lo,next); return bar.first->second; //std::pair::iterator,bool> bar = rt->unique.insert(foo); // const range_lo *baz = &*(bar.first); // return (range_lo *)baz; // coerce const } scopes::range_lo *scopes::range_lub_lo(range_lo *rng1, range_lo *rng2){ if(!rng1) return rng2; if(!rng2) return rng1; if(rng1->lo > rng2->lo) std::swap(rng1,rng2); std::pair foo(range_op(rng1,rng2,0),(range_lo *)0); std::pair::iterator,bool> bar = rt->lub.insert(foo); range_lo *&res = bar.first->second; if(!bar.second) return res; if(!(rng1->next && rng1->next->lo <= rng2->lo)){ for(int lo = rng1->lo; lo <= rng2->lo; lo = parents[lo]) if(lo == rng2->lo) {rng2 = rng2->next; break;} } range_lo *baz = range_lub_lo(rng1->next,rng2); res = find_range_lo(rng1->lo,baz); return res; } scopes::range_lo *scopes::range_glb_lo(range_lo *rng1, range_lo *rng2, int hi){ if(!rng1) return rng1; if(!rng2) return rng2; if(rng1->lo > rng2->lo) std::swap(rng1,rng2); std::pair cand(range_op(rng1,rng2,hi),(range_lo *)0); std::pair::iterator,bool> bar = rt->glb.insert(cand); range_lo *&res = bar.first->second; if(!bar.second) return res; range_lo *foo; if(!(rng1->next && rng1->next->lo <= rng2->lo)){ int lim = hi; if(rng1->next) lim = std::min(lim,rng1->next->lo); int a = rng1->lo, b = rng2->lo; while(a != b && b <= lim){ a = parents[a]; if(a > b)std::swap(a,b); } if(a == b && b <= lim){ foo = range_glb_lo(rng1->next,rng2->next,hi); foo = find_range_lo(b,foo); } else foo = range_glb_lo(rng2,rng1->next,hi); } else foo = range_glb_lo(rng1->next,rng2,hi); res = foo; return res; } /** computes the lub (smallest containing subtree) of two ranges */ scopes::range scopes::range_lub(const range &rng1, const range &rng2){ int hi = tree_lca(rng1.hi,rng2.hi); if(hi == SHRT_MAX) return range_full(); range_lo *lo = range_lub_lo(rng1.lo,rng2.lo); return range(hi,lo); } /** computes the glb (intersection) of two ranges */ scopes::range scopes::range_glb(const range &rng1, const range &rng2){ if(rng1.hi == SHRT_MAX) return rng2; if(rng2.hi == SHRT_MAX) return rng1; int hi = tree_gcd(rng1.hi,rng2.hi); range_lo *lo = hi == SHRT_MIN ? 0 : range_glb_lo(rng1.lo,rng2.lo,hi); if(!lo) hi = SHRT_MIN; return range(hi,lo); } /** is this range empty? */ bool scopes::range_is_empty(const range &rng){ return rng.hi == SHRT_MIN; } /** return an empty range */ scopes::range scopes::range_empty(){ return range(SHRT_MIN,0); } /** return a full range */ scopes::range scopes::range_full(){ return range(SHRT_MAX,0); } /** return the maximal element of a range */ int scopes::range_max(const range &rng){ return rng.hi; } /** return a minimal (not necessarily unique) element of a range */ int scopes::range_min(const range &rng){ if(rng.hi == SHRT_MAX) return SHRT_MIN; return rng.lo ? rng.lo->lo : SHRT_MAX; } /** return range consisting of downward closure of a point */ scopes::range scopes::range_downward(int _hi){ std::vector descendants(parents.size()); for(int i = descendants.size() - 1; i >= 0 ; i--) descendants[i] = i == _hi || parents[i] < parents.size() && descendants[parents[i]]; for(unsigned i = 0; i < descendants.size() - 1; i++) if(parents[i] < parents.size()) descendants[parents[i]] = false; range_lo *foo = 0; for(int i = descendants.size() - 1; i >= 0; --i) if(descendants[i]) foo = find_range_lo(i,foo); return range(_hi,foo); } /** add an element to a range */ void scopes::range_add(int i, range &n){ range foo = range(i, find_range_lo(i,0)); n = range_lub(foo,n); } /** Choose an element of rng1 that is near to rng2 */ int scopes::range_near(const range &rng1, const range &rng2){ int frame; int thing = tree_lca(rng1.hi,rng2.hi); if(thing != rng1.hi) return rng1.hi; range line = range(rng1.hi,find_range_lo(rng2.hi,(range_lo *)0)); line = range_glb(line,rng1); return range_min(line); } /** test whether a tree node is contained in a range */ bool scopes::in_range(int n, const range &rng){ range r = range_empty(); range_add(n,r); r = range_glb(rng,r); return !range_is_empty(r); } /** test whether two ranges of tree nodes intersect */ bool scopes::ranges_intersect(const range &rng1, const range &rng2){ range r = range_glb(rng1,rng2); return !range_is_empty(r); } bool scopes::range_contained(const range &rng1, const range &rng2){ range r = range_glb(rng1,rng2); return r.hi == rng1.hi && r.lo == rng1.lo; } #endif z3-4.4.0/src/interp/iz3mgr.h0000755000175000017500000004541612540347414015336 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3mgr.h Abstract: A wrapper around an ast manager, providing convenience methods. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef IZ3MGR_H #define IZ3MGR_H #include #include #include #include "iz3hash.h" #include"well_sorted.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"datatype_decl_plugin.h" #include"array_decl_plugin.h" #include"ast_translation.h" #include"ast_pp.h" #include"ast_ll_pp.h" #include"ast_smt_pp.h" #include"ast_smt2_pp.h" #include"th_rewriter.h" #include"var_subst.h" #include"expr_substitution.h" #include"pp.h" #include"scoped_ctrl_c.h" #include"cancel_eh.h" #include"scoped_timer.h" //# include"pp_params.hpp" /* A wrapper around an ast manager, providing convenience methods. */ /** Shorthands for some built-in operators. */ // rename this to keep it accessible, as we use ast for something else typedef ast raw_ast; /** Wrapper around an ast pointer */ class ast_i { protected: raw_ast *_ast; public: raw_ast * const &raw() const {return _ast;} ast_i(raw_ast *a){_ast = a;} ast_i(){_ast = 0;} bool eq(const ast_i &other) const { return _ast == other._ast; } bool lt(const ast_i &other) const { return _ast->get_id() < other._ast->get_id(); } friend bool operator==(const ast_i &x, const ast_i&y){ return x.eq(y); } friend bool operator!=(const ast_i &x, const ast_i&y){ return !x.eq(y); } friend bool operator<(const ast_i &x, const ast_i&y){ return x.lt(y); } size_t hash() const {return _ast->get_id();} bool null() const {return !_ast;} }; /** Reference counting verison of above */ class ast_r : public ast_i { ast_manager *_m; public: ast_r(ast_manager *m, raw_ast *a) : ast_i(a) { _m = m; m->inc_ref(a); } ast_r() {_m = 0;} ast_r(const ast_r &other) : ast_i(other) { _m = other._m; _m->inc_ref(_ast); } ast_r &operator=(const ast_r &other) { if(_ast) _m->dec_ref(_ast); _ast = other._ast; _m = other._m; _m->inc_ref(_ast); return *this; } ~ast_r(){ if(_ast) _m->dec_ref(_ast); } ast_manager *mgr() const {return _m;} }; // to make ast_r hashable namespace hash_space { template <> class hash { public: size_t operator()(const ast_r &s) const { return s.raw()->get_id(); } }; } // to make ast_r usable in ordered collections namespace std { template <> class less { public: bool operator()(const ast_r &s, const ast_r &t) const { // return s.raw() < t.raw(); return s.raw()->get_id() < t.raw()->get_id(); } }; } /** Wrapper around an AST manager, providing convenience methods. */ class iz3mgr { public: typedef ast_r ast; // typedef decl_kind opr; typedef func_decl *symb; typedef sort *type; typedef ast_r z3pf; typedef decl_kind pfrule; enum opr { True, False, And, Or, Not, Iff, Ite, Equal, Implies, Distinct, Xor, Oeq, Interp, Leq, Geq, Lt, Gt, Plus, Sub, Uminus, Times, Div, Idiv, Rem, Mod, Power, ToReal, ToInt, IsInt, Select, Store, ConstArray, ArrayDefault, ArrayMap, SetUnion, SetIntersect, SetDifference, SetComplement, SetSubSet, AsArray, Numeral, Forall, Exists, Variable, Uninterpreted, Other }; opr op(const ast &t); unsigned ast_id(const ast &x) { return to_expr(x.raw())->get_id(); } /** Overloads for constructing ast. */ ast make_var(const std::string &name, type ty); ast make(opr op, const std::vector &args); ast make(opr op); ast make(opr op, const ast &arg0); ast make(opr op, const ast &arg0, const ast &arg1); ast make(opr op, const ast &arg0, const ast &arg1, const ast &arg2); ast make(symb sym, const std::vector &args); ast make(symb sym); ast make(symb sym, const ast &arg0); ast make(symb sym, const ast &arg0, const ast &arg1); ast make(symb sym, const ast &arg0, const ast &arg1, const ast &arg2); ast make_quant(opr op, const std::vector &bvs, ast &body); ast clone(const ast &t, const std::vector &args); ast_manager &m() {return m_manager;} ast cook(raw_ast *a) {return ast(&m_manager,a);} std::vector cook(ptr_vector v) { std::vector _v(v.size()); for(unsigned i = 0; i < v.size(); i++) _v[i] = cook(v[i]); return _v; } raw_ast *uncook(const ast &a) { m_manager.inc_ref(a.raw()); return a.raw(); } /** Methods for destructing ast. */ int num_args(ast t){ ast_kind dk = t.raw()->get_kind(); switch(dk){ case AST_APP: return to_app(t.raw())->get_num_args(); case AST_QUANTIFIER: return 1; case AST_VAR: return 0; default:; } assert(0); return 0; } ast arg(const ast &t, int i){ ast_kind dk = t.raw()->get_kind(); switch(dk){ case AST_APP: return cook(to_app(t.raw())->get_arg(i)); case AST_QUANTIFIER: return cook(to_quantifier(t.raw())->get_expr()); default:; } assert(0); return ast(); } void get_args(const ast &t, std::vector &res){ res.resize(num_args(t)); for(unsigned i = 0; i < res.size(); i++) res[i] = arg(t,i); } std::vector args(const ast &t){ std::vector res; get_args(t,res); return res; } symb sym(ast t){ raw_ast *_ast = t.raw(); return is_app(_ast) ? to_app(_ast)->get_decl() : 0; } std::string string_of_symbol(symb s){ symbol _s = s->get_name(); if (_s.is_numerical()) { std::ostringstream buffer; buffer << _s.get_num(); return buffer.str(); } else { return _s.bare_str(); } } type get_type(ast t){ return m().get_sort(to_expr(t.raw())); } std::string string_of_numeral(const ast& t){ rational r; expr* e = to_expr(t.raw()); assert(e); if (m_arith_util.is_numeral(e, r)) return r.to_string(); assert(0); return "NaN"; } bool is_numeral(const ast& t, rational &r){ expr* e = to_expr(t.raw()); assert(e); return m_arith_util.is_numeral(e, r); } rational get_coeff(const ast& t){ rational res; if(op(t) == Times && is_numeral(arg(t,0),res)) return res; return rational(1); } ast get_linear_var(const ast& t){ rational res; if(op(t) == Times && is_numeral(arg(t,0),res)) return arg(t,1); return t; } int get_quantifier_num_bound(const ast &t) { return to_quantifier(t.raw())->get_num_decls(); } std::string get_quantifier_bound_name(const ast &t, unsigned i) { return to_quantifier(t.raw())->get_decl_names()[i].bare_str(); } type get_quantifier_bound_type(const ast &t, unsigned i) { return to_quantifier(t.raw())->get_decl_sort(i); } ast get_quantifier_body(const ast &t) { return cook(to_quantifier(t.raw())->get_expr()); } unsigned get_variable_index_value(const ast &t) { var* va = to_var(t.raw()); return va->get_idx(); } bool is_bool_type(type t){ family_id fid = to_sort(t)->get_family_id(); decl_kind k = to_sort(t)->get_decl_kind(); return fid == m().get_basic_family_id() && k == BOOL_SORT; } bool is_array_type(type t){ family_id fid = to_sort(t)->get_family_id(); decl_kind k = to_sort(t)->get_decl_kind(); return fid == m_array_fid && k == ARRAY_SORT; } type get_range_type(symb s){ return to_func_decl(s)->get_range(); } int get_num_parameters(const symb &s){ return to_func_decl(s)->get_num_parameters(); } ast get_ast_parameter(const symb &s, int idx){ return cook(to_func_decl(s)->get_parameters()[idx].get_ast()); } enum lemma_theory {ArithTheory,ArrayTheory,UnknownTheory}; lemma_theory get_theory_lemma_theory(const ast &proof){ symb s = sym(proof); ::symbol p0; bool ok = s->get_parameter(0).is_symbol(p0); if(!ok) return UnknownTheory; std::string foo(p0.bare_str()); if(foo == "arith") return ArithTheory; if(foo == "array") return ArrayTheory; return UnknownTheory; } enum lemma_kind {FarkasKind,Leq2EqKind,Eq2LeqKind,GCDTestKind,AssignBoundsKind,EqPropagateKind,GomoryCutKind,ArithMysteryKind,UnknownKind}; lemma_kind get_theory_lemma_kind(const ast &proof){ symb s = sym(proof); if(s->get_num_parameters() < 2) { return ArithMysteryKind; // Bad -- Z3 hasn't told us } ::symbol p0; bool ok = s->get_parameter(1).is_symbol(p0); if(!ok) return UnknownKind; std::string foo(p0.bare_str()); if(foo == "farkas") return FarkasKind; if(foo == "triangle-eq") return is_not(arg(conc(proof),0)) ? Eq2LeqKind : Leq2EqKind; if(foo == "gcd-test") return GCDTestKind; if(foo == "assign-bounds") return AssignBoundsKind; if(foo == "eq-propagate") return EqPropagateKind; if(foo == "gomory-cut") return GomoryCutKind; return UnknownKind; } void get_farkas_coeffs(const ast &proof, std::vector& coeffs); void get_farkas_coeffs(const ast &proof, std::vector& rats); void get_broken_gcd_test_coeffs(const ast &proof, std::vector& rats); void get_assign_bounds_coeffs(const ast &proof, std::vector& rats); void get_assign_bounds_coeffs(const ast &proof, std::vector& rats); void get_assign_bounds_rule_coeffs(const ast &proof, std::vector& rats); void get_assign_bounds_rule_coeffs(const ast &proof, std::vector& rats); void get_gomory_cut_coeffs(const ast &proof, std::vector& rats); void get_gomory_cut_coeffs(const ast &proof, std::vector& rats); bool is_farkas_coefficient_negative(const ast &proof, int n); bool is_true(ast t){ return op(t) == True; } bool is_false(ast t){ return op(t) == False; } bool is_iff(ast t){ return op(t) == Iff; } bool is_or(ast t){ return op(t) == Or; } bool is_not(ast t){ return op(t) == Not; } /** Simplify an expression using z3 simplifier */ ast z3_simplify(const ast& e); /** Simplify, sorting sums */ ast z3_really_simplify(const ast &e); // Some constructors that simplify things ast mk_not(ast x){ opr o = op(x); if(o == True) return make(False); if(o == False) return make(True); if(o == Not) return arg(x,0); return make(Not,x); } ast mk_and(ast x, ast y){ opr ox = op(x); opr oy = op(y); if(ox == True) return y; if(oy == True) return x; if(ox == False) return x; if(oy == False) return y; if(x == y) return x; return make(And,x,y); } ast mk_or(ast x, ast y){ opr ox = op(x); opr oy = op(y); if(ox == False) return y; if(oy == False) return x; if(ox == True) return x; if(oy == True) return y; if(x == y) return x; return make(Or,x,y); } ast mk_implies(ast x, ast y){ opr ox = op(x); opr oy = op(y); if(ox == True) return y; if(oy == False) return mk_not(x); if(ox == False) return mk_true(); if(oy == True) return y; if(x == y) return mk_true(); return make(Implies,x,y); } ast mk_or(const std::vector &x){ ast res = mk_false(); for(unsigned i = 0; i < x.size(); i++) res = mk_or(res,x[i]); return res; } ast mk_and(const std::vector &x){ std::vector conjs; for(unsigned i = 0; i < x.size(); i++){ const ast &e = x[i]; opr o = op(e); if(o == False) return mk_false(); if(o != True) conjs.push_back(e); } if(conjs.size() == 0) return mk_true(); if(conjs.size() == 1) return conjs[0]; return make(And,conjs); } ast mk_equal(ast x, ast y){ if(x == y) return make(True); opr ox = op(x); opr oy = op(y); if(ox == True) return y; if(oy == True) return x; if(ox == False) return mk_not(y); if(oy == False) return mk_not(x); if(ox == False && oy == True) return make(False); if(oy == False && ox == True) return make(False); return make(Equal,x,y); } ast z3_ite(ast x, ast y, ast z){ opr ox = op(x); opr oy = op(y); opr oz = op(z); if(ox == True) return y; if(ox == False) return z; if(y == z) return y; if(oy == True && oz == False) return x; if(oz == True && oy == False) return mk_not(x); return make(Ite,x,y,z); } ast make_int(const std::string &s) { sort *r = m().mk_sort(m_arith_fid, INT_SORT); return cook(m_arith_util.mk_numeral(rational(s.c_str()),r)); } ast make_int(const rational &s) { sort *r = m().mk_sort(m_arith_fid, INT_SORT); return cook(m_arith_util.mk_numeral(s,r)); } ast make_real(const std::string &s) { sort *r = m().mk_sort(m_arith_fid, REAL_SORT); return cook(m_arith_util.mk_numeral(rational(s.c_str()),r)); } ast make_real(const rational &s) { sort *r = m().mk_sort(m_arith_fid, REAL_SORT); return cook(m_arith_util.mk_numeral(s,r)); } ast mk_false() { return make(False); } ast mk_true() { return make(True); } ast mk_fresh_constant(char const * prefix, type s){ return cook(m().mk_fresh_const(prefix, s)); } type bool_type() { ::sort *s = m().mk_sort(m_basic_fid, BOOL_SORT); return s; } type int_type() { ::sort *s = m().mk_sort(m_arith_fid, INT_SORT); return s; } type real_type() { ::sort *s = m().mk_sort(m_arith_fid, REAL_SORT); return s; } type array_type(type d, type r) { parameter params[2] = { parameter(d), parameter(to_sort(r)) }; ::sort * s = m().mk_sort(m_array_fid, ARRAY_SORT, 2, params); return s; } symb function(const std::string &str_name, unsigned arity, type *domain, type range) { ::symbol name = ::symbol(str_name.c_str()); std::vector< ::sort *> sv(arity); for(unsigned i = 0; i < arity; i++) sv[i] = domain[i]; ::func_decl* d = m().mk_func_decl(name,arity,&sv[0],range); return d; } void linear_comb(ast &P, const ast &c, const ast &Q, bool round_off = false); ast sum_inequalities(const std::vector &coeffs, const std::vector &ineqs, bool round_off = false); ast simplify_ineq(const ast &ineq){ ast res = make(op(ineq),arg(ineq,0),z3_simplify(arg(ineq,1))); return res; } void mk_idiv(const ast& t, const rational &d, ast &whole, ast &frac); ast mk_idiv(const ast& t, const rational &d); ast mk_idiv(const ast& t, const ast &d); /** methods for destructing proof terms */ pfrule pr(const z3pf &t); int num_prems(const z3pf &t){return to_app(t.raw())->get_num_args()-1;} z3pf prem(const z3pf &t, int n){return arg(t,n);} z3pf conc(const z3pf &t){return arg(t,num_prems(t));} /* quantifier handling */ // substitute a term t for unbound occurrences of variable v in e ast subst(ast var, ast t, ast e); // apply a substitution defined by a map ast subst(stl_ext::hash_map &map, ast e); // apply a quantifier to a formula, with some optimizations // 1) bound variable does not occur -> no quantifier // 2) bound variable must be equal to some term -> substitute ast apply_quant(opr quantifier, ast var, ast e); /** For debugging */ void show(ast); void show_symb(symb s); /** Constructor */ void print_lit(ast lit); void print_expr(std::ostream &s, const ast &e); void print_clause(std::ostream &s, std::vector &cls); void print_sat_problem(std::ostream &out, const ast &t); void show_clause(std::vector &cls); static void pretty_print(std::ostream &f, const std::string &s); iz3mgr(ast_manager &_m_manager) : m_manager(_m_manager), m_arith_util(_m_manager) { m_basic_fid = m().get_basic_family_id(); m_arith_fid = m().mk_family_id("arith"); m_bv_fid = m().mk_family_id("bv"); m_array_fid = m().mk_family_id("array"); m_dt_fid = m().mk_family_id("datatype"); m_datalog_fid = m().mk_family_id("datalog_relation"); } iz3mgr(const iz3mgr& other) : m_manager(other.m_manager), m_arith_util(other.m_manager) { m_basic_fid = m().get_basic_family_id(); m_arith_fid = m().mk_family_id("arith"); m_bv_fid = m().mk_family_id("bv"); m_array_fid = m().mk_family_id("array"); m_dt_fid = m().mk_family_id("datatype"); m_datalog_fid = m().mk_family_id("datalog_relation"); } protected: ast_manager &m_manager; int occurs_in(ast var, ast e); private: ast mki(family_id fid, decl_kind sk, int n, raw_ast **args); ast make(opr op, int n, raw_ast **args); ast make(symb sym, int n, raw_ast **args); int occurs_in1(stl_ext::hash_map &occurs_in_memo, ast var, ast e); bool solve_arith(const ast &v, const ast &x, const ast &y, ast &res); ast cont_eq(stl_ext::hash_set &cont_eq_memo, bool truth, ast v, ast e); ast subst(stl_ext::hash_map &subst_memo, ast var, ast t, ast e); family_id m_basic_fid; family_id m_array_fid; family_id m_arith_fid; family_id m_bv_fid; family_id m_dt_fid; family_id m_datalog_fid; arith_util m_arith_util; }; #endif z3-4.4.0/src/interp/iz3translate_direct.cpp0000755000175000017500000016661312540347414020436 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3translate_direct.cpp Abstract: Translate a Z3 proof into the interpolating proof calculus. Translation is direct, without transformations on the target proof representaiton. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifdef _WINDOWS #pragma warning(disable:4996) #pragma warning(disable:4800) #pragma warning(disable:4267) #pragma warning(disable:4101) #pragma warning(disable:4390) #endif #include "iz3translate.h" #include "iz3proof.h" #include "iz3profiling.h" #include "iz3interp.h" #include #include #include #include #include #include #include //using std::vector; using namespace stl_ext; /* This can introduce an address dependency if the range type of hash_map has a destructor. Since the code in this file is not used and only here for historical comparisons, we allow this non-determinism. */ namespace stl_ext { template class hash { public: size_t operator()(const T *p) const { return (size_t) p; } }; } static int lemma_count = 0; #if 0 static int nll_lemma_count = 0; #endif #define SHOW_LEMMA_COUNT -1 // One half of a resolution. We need this to distinguish // between resolving as a clause and as a unit clause. // if pivot == conclusion(proof) it is unit. struct Z3_resolvent { iz3base::ast proof; bool is_unit; iz3base::ast pivot; Z3_resolvent(const iz3base::ast &_proof, bool _is_unit, const iz3base::ast &_pivot){ proof = _proof; is_unit = _is_unit; pivot = _pivot; } }; namespace hash_space { template <> class hash { public: size_t operator()(const Z3_resolvent &p) const { return (p.proof.hash() + p.pivot.hash()); } }; } bool operator==(const Z3_resolvent &x, const Z3_resolvent &y) { return x.proof == y.proof && x.pivot == y.pivot; } typedef std::vector ResolventAppSet; struct non_local_lits { ResolventAppSet proofs; // the proof nodes being raised non_local_lits(ResolventAppSet &_proofs){ proofs.swap(_proofs); } }; namespace hash_space { template <> class hash { public: size_t operator()(const non_local_lits &p) const { size_t h = 0; for(ResolventAppSet::const_iterator it = p.proofs.begin(), en = p.proofs.end(); it != en; ++it) h += (size_t)*it; return h; } }; } bool operator==(const non_local_lits &x, const non_local_lits &y) { ResolventAppSet::const_iterator itx = x.proofs.begin(); ResolventAppSet::const_iterator ity = y.proofs.begin(); while(true){ if(ity == y.proofs.end()) return itx == x.proofs.end(); if(itx == x.proofs.end()) return ity == y.proofs.end(); if(*itx != *ity) return false; ++itx; ++ity; } } /* This translator goes directly from Z3 proofs to interpolatable proofs without an intermediate representation as an iz3proof. */ class iz3translation_direct : public iz3translation { public: typedef ast Zproof; // type of non-interpolating proofs typedef iz3proof Iproof; // type of interpolating proofs /* Here we have lots of hash tables for memoizing various methods and other such global data structures. */ typedef hash_map AstToInt; AstToInt locality; // memoizes locality of Z3 proof terms typedef std::pair EquivEntry; typedef hash_map EquivTab; EquivTab equivs; // maps non-local terms to equivalent local terms, with proof typedef hash_set AstHashSet; AstHashSet equivs_visited; // proofs already checked for equivalences typedef std::pair, hash_map > AstToIpf; AstToIpf translation; // Zproof nodes to Iproof nodes AstHashSet antes_added; // Z3 proof terms whose antecedents have been added to the list std::vector > antes; // list of antecedent/frame pairs std::vector local_antes; // list of local antecedents Iproof *iproof; // the interpolating proof we are constructing int frames; // number of frames typedef std::set AstSet; typedef hash_map AstToAstSet; AstToAstSet hyp_map; // map proof terms to hypothesis set struct LocVar { // localization vars ast var; // a fresh variable ast term; // term it represents int frame; // frame in which it's defined LocVar(ast v, ast t, int f){var=v;term=t;frame=f;} }; std::vector localization_vars; // localization vars in order of creation typedef hash_map AstToAst; AstToAst localization_map; // maps terms to their localization vars typedef hash_map AstToBool; iz3secondary *secondary; // the secondary prover // Unique table for sets of non-local resolutions hash_map non_local_lits_unique; // Unique table for resolvents hash_map Z3_resolvent_unique; // Translation memo for case of non-local resolutions hash_map non_local_translation; public: #define from_ast(x) (x) // determine locality of a proof term // return frame of derivation if local, or -1 if not // result INT_MAX means the proof term is a tautology // memoized in hash_map "locality" int get_locality_rec(ast proof){ std::pair foo(proof,INT_MAX); std::pair bar = locality.insert(foo); int &res = bar.first->second; if(!bar.second) return res; if(pr(proof) == PR_ASSERTED){ ast ass = conc(proof); res = frame_of_assertion(ass); } else { unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ ast arg = prem(proof,i); int bar = get_locality_rec(arg); if(res == INT_MAX || res == bar) res = bar; else if(bar != INT_MAX) res = -1; } } return res; } int get_locality(ast proof){ // if(lia_z3_axioms_only) return -1; int res = get_locality_rec(proof); if(res != -1){ ast con = conc(proof); range rng = ast_scope(con); // hack: if a clause contains "true", it reduces to "true", // which means we won't compute the range correctly. we handle // this case by computing the ranges of the literals separately if(is_true(con)){ std::vector lits; get_Z3_lits(conc(proof),lits); for(unsigned i = 0; i < lits.size(); i++) rng = range_glb(rng,ast_scope(lits[i])); } if(!range_is_empty(rng)){ AstSet &hyps = get_hyps(proof); for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ ast hyp = *it; rng = range_glb(rng,ast_scope(hyp)); } } if(res == INT_MAX){ if(range_is_empty(rng)) res = -1; else res = range_max(rng); } else { if(!in_range(res,rng)) res = -1; } } return res; } AstSet &get_hyps(ast proof){ std::pair foo(proof,AstSet()); std::pair bar = hyp_map.insert(foo); AstSet &res = bar.first->second; if(!bar.second) return res; pfrule dk = pr(proof); if(dk == PR_HYPOTHESIS){ ast con = conc(proof); res.insert(con); } else { unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ ast arg = prem(proof,i); AstSet &arg_hyps = get_hyps(arg); res.insert(arg_hyps.begin(),arg_hyps.end()); } if(dk == PR_LEMMA){ ast con = conc(proof); res.erase(mk_not(con)); if(is_or(con)){ int clause_size = num_args(con); for(int i = 0; i < clause_size; i++){ ast neglit = mk_not(arg(con,i)); res.erase(neglit); } } } } #if 0 AstSet::iterator it = res.begin(), en = res.end(); if(it != en){ AstSet::iterator old = it; ++it; for(; it != en; ++it, ++old) if(!(*old < *it)) std::cout << "foo!"; } #endif return res; } // Find all the judgements of the form p <-> q, where // p is local and q is non-local, recording them in "equivs" // the map equivs_visited is used to record the already visited proof terms void find_equivs(ast proof){ if(equivs_visited.find(proof) != equivs_visited.end()) return; equivs_visited.insert(proof); unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++) // do all the sub_terms find_equivs(prem(proof,i)); ast con = conc(proof); // get the conclusion if(is_iff(con)){ ast iff = con; for(int i = 0; i < 2; i++) if(!is_local(arg(iff,i)) && is_local(arg(iff,1-i))){ std::pair > foo(arg(iff,i),std::pair(arg(iff,1-i),proof)); equivs.insert(foo); } } } // get the lits of a Z3 clause as foci terms void get_Z3_lits(ast t, std::vector &lits){ opr dk = op(t); if(dk == False) return; // false = empty clause if(dk == Or){ unsigned nargs = num_args(t); lits.resize(nargs); for(unsigned i = 0; i < nargs; i++) // do all the sub_terms lits[i] = arg(t,i); } else { lits.push_back(t); } } // resolve two clauses represented as vectors of lits. replace first clause void resolve(ast pivot, std::vector &cls1, std::vector &cls2){ ast neg_pivot = mk_not(pivot); for(unsigned i = 0; i < cls1.size(); i++){ if(cls1[i] == pivot){ cls1[i] = cls1.back(); cls1.pop_back(); bool found_pivot2 = false; for(unsigned j = 0; j < cls2.size(); j++){ if(cls2[j] == neg_pivot) found_pivot2 = true; else cls1.push_back(cls2[j]); } assert(found_pivot2); return; } } assert(0 && "resolve failed"); } // get lits resulting from unit resolution up to and including "position" // TODO: this is quadratic -- fix it void do_unit_resolution(ast proof, int position, std::vector &lits){ ast orig_clause = conc(prem(proof,0)); get_Z3_lits(orig_clause,lits); for(int i = 1; i <= position; i++){ std::vector unit(1); unit[0] = conc(prem(proof,i)); resolve(mk_not(unit[0]),lits,unit); } } // clear the localization variables void clear_localization(){ localization_vars.clear(); localization_map.clear(); } // create a fresh variable for localization ast fresh_localization_var(ast term, int frame){ std::ostringstream s; s << "%" << (localization_vars.size()); ast var = make_var(s.str().c_str(),get_type(term)); sym_range(sym(var)) = range_full(); // make this variable global localization_vars.push_back(LocVar(var,term,frame)); return var; } // "localize" a term to a given frame range by // creating new symbols to represent non-local subterms ast localize_term(ast e, const range &rng){ if(ranges_intersect(ast_scope(e),rng)) return e; // this term occurs in range, so it's O.K. AstToAst::iterator it = localization_map.find(e); if(it != localization_map.end()) return it->second; // if is is non-local, we must first localize the arguments to // the range of its function symbol int nargs = num_args(e); if(nargs > 0 /* && (!is_local(e) || flo <= hi || fhi >= lo) */){ range frng = rng; if(op(e) == Uninterpreted){ symb f = sym(e); range srng = sym_range(f); if(ranges_intersect(srng,rng)) // localize to desired range if possible frng = range_glb(srng,rng); } std::vector largs(nargs); for(int i = 0; i < nargs; i++){ largs[i] = localize_term(arg(e,i),frng); frng = range_glb(frng,ast_scope(largs[i])); } e = clone(e,largs); assert(is_local(e)); } if(ranges_intersect(ast_scope(e),rng)) return e; // this term occurs in range, so it's O.K. // choose a frame for the constraint that is close to range int frame = range_near(ast_scope(e),rng); ast new_var = fresh_localization_var(e,frame); localization_map[e] = new_var; ast cnst = make(Equal,new_var,e); antes.push_back(std::pair(cnst,frame)); return new_var; } // some patterm matching functions // match logical or with nargs arguments // assumes AIG form bool match_or(ast e, ast *args, int nargs){ if(op(e) != Or) return false; int n = num_args(e); if(n != nargs) return false; for(int i = 0; i < nargs; i++) args[i] = arg(e,i); return true; } // match operator f with exactly nargs arguments bool match_op(ast e, opr f, ast *args, int nargs){ if(op(e) != f) return false; int n = num_args(e); if(n != nargs) return false; for(int i = 0; i < nargs; i++) args[i] = arg(e,i); return true; } // see if the given formula can be interpreted as // an axiom instance (e.g., an array axiom instance). // if so, add it to "antes" in an appropriate frame. // this may require "localization" void get_axiom_instance(ast e){ // "store" axiom // (or (= w q) (= (select (store a1 w y) q) (select a1 q))) // std::cout << "ax: "; show(e); ast lits[2],eq_ops_l[2],eq_ops_r[2],sel_ops[2], sto_ops[3], sel_ops2[2] ; if(match_or(e,lits,2)) if(match_op(lits[0],Equal,eq_ops_l,2)) if(match_op(lits[1],Equal,eq_ops_r,2)) for(int i = 0; i < 2; i++){ // try the second equality both ways if(match_op(eq_ops_r[0],Select,sel_ops,2)) if(match_op(sel_ops[0],Store,sto_ops,3)) if(match_op(eq_ops_r[1],Select,sel_ops2,2)) for(int j = 0; j < 2; j++){ // try the first equality both ways if(eq_ops_l[0] == sto_ops[1] && eq_ops_l[1] == sel_ops[1] && eq_ops_l[1] == sel_ops2[1] && sto_ops[0] == sel_ops2[0]) if(is_local(sel_ops[0])) // store term must be local { ast sto = sel_ops[0]; ast addr = localize_term(eq_ops_l[1],ast_scope(sto)); ast res = make(Or, make(Equal,eq_ops_l[0],addr), make(Equal, make(Select,sto,addr), make(Select,sel_ops2[0],addr))); int frame = range_min(ast_scope(res)); antes.push_back(std::pair(res,frame)); return; } std::swap(eq_ops_l[0],eq_ops_l[1]); } std::swap(eq_ops_r[0],eq_ops_r[1]); } } // a quantifier instantation looks like (~ forall x. P) \/ P[z/x] // we need to find a time frame for P, then localize P[z/x] in this frame void get_quantifier_instance(ast e){ ast disjs[2]; if(match_or(e,disjs,2)){ if(is_local(disjs[0])){ ast res = localize_term(disjs[1], ast_scope(disjs[0])); int frame = range_min(ast_scope(res)); antes.push_back(std::pair(res,frame)); return; } } } ast get_judgement(ast proof){ ast con = from_ast(conc(proof)); AstSet &hyps = get_hyps(proof); std::vector hyps_vec; for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) hyps_vec.push_back(*it); if(hyps_vec.size() == 0) return con; con = make(Or,mk_not(make(And,hyps_vec)),con); return con; } // add the premises of a proof term to the "antes" list void add_antes(ast proof){ if(antes_added.find(proof) != antes_added.end()) return; antes_added.insert(proof); int frame = get_locality(proof); if(frame != -1) if(1){ ast e = get_judgement(proof); if(frame >= frames) frame = frames-1; // can happen if there are no symbols antes.push_back(std::pair(e,frame)); return; } pfrule dk = pr(proof); if(dk == PR_ASSERTED){ ast ass = conc(proof); frame = frame_of_assertion(ass); if(frame >= frames) frame = frames-1; // can happen if a theory fact antes.push_back(std::pair(ass,frame)); return; } if(dk == PR_TH_LEMMA && num_prems(proof) == 0){ get_axiom_instance(conc(proof)); } if(dk == PR_QUANT_INST && num_prems(proof) == 0){ get_quantifier_instance(conc(proof)); } unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ ast arg = prem(proof,i); add_antes(arg); } } // add quantifiers over the localization vars // to an interpolant for frames lo-hi ast add_quants(ast e, int lo, int hi){ for(int i = localization_vars.size() - 1; i >= 0; i--){ LocVar &lv = localization_vars[i]; opr quantifier = (lv.frame >= lo && lv.frame <= hi) ? Exists : Forall; e = apply_quant(quantifier,lv.var,e); } return e; } int get_lits_locality(std::vector &lits){ range rng = range_full(); for(std::vector::iterator it = lits.begin(), en = lits.end(); it != en; ++it){ ast lit = *it; rng = range_glb(rng,ast_scope(lit)); } if(range_is_empty(rng)) return -1; int hi = range_max(rng); if(hi >= frames) return frames - 1; return hi; } struct invalid_lemma {}; // prove a lemma (clause) using current antes list // return proof of the lemma // use the secondary prover int prove_lemma(std::vector &lits){ // first try localization if(antes.size() == 0){ int local_frame = get_lits_locality(lits); if(local_frame != -1) return iproof->make_assumption(local_frame,lits); // no proof needed for purely local fact } // group the assumptions by frame std::vector preds(frames); for(unsigned i = 0; i < preds.size(); i++) preds[i] = mk_true(); for(unsigned i = 0; i < antes.size(); i++){ int frame = antes[i].second; preds[frame] = mk_and(preds[frame],antes[i].first); // conjoin it to frame } for(unsigned i = 0; i < lits.size(); i++){ int frame; if(!weak_mode()){ frame = range_max(ast_scope(lits[i])); if(frame >= frames) frame = frames-1; // could happen if contains no symbols } else { frame = range_min(ast_scope(lits[i])); if(frame < 0){ frame = range_max(ast_scope(lits[i])); // could happen if contains no symbols if(frame >= frames) frame = frames-1; } } preds[frame] = mk_and(preds[frame],mk_not(lits[i])); } std::vector itps; // holds interpolants #if 1 ++lemma_count; // std::cout << "lemma: " << lemma_count << std::endl; if(lemma_count == SHOW_LEMMA_COUNT){ for(unsigned i = 0; i < lits.size(); i++) show_lit(lits[i]); std::cerr << "lemma written to file lemma.smt:\n"; iz3base foo(*this,preds,std::vector(),std::vector()); foo.print("lemma.smt"); throw invalid_lemma(); } #endif #if 0 std::cout << "\nLemma:\n"; for(unsigned i = 0; i < lits.size(); i++) show_lit(lits[i]); #endif // interpolate using secondary prover profiling::timer_start("foci"); int sat = secondary->interpolate(preds,itps); profiling::timer_stop("foci"); std::cout << "lemma done" << std::endl; // if sat, lemma isn't valid, something is wrong if(sat){ #if 1 std::cerr << "invalid lemma written to file invalid_lemma.smt:\n"; iz3base foo(*this,preds,std::vector(),std::vector()); foo.print("invalid_lemma.smt"); #endif throw iz3_incompleteness(); } assert(sat == 0); // if sat, lemma doesn't hold! // quantifiy the localization vars for(unsigned i = 0; i < itps.size(); i++) itps[i] = add_quants(itps[i],0,i); // Make a lemma, storing interpolants Iproof::node res = iproof->make_lemma(lits,itps); #if 0 std::cout << "Lemma interps\n"; for(unsigned i = 0; i < itps.size(); i++) show(itps[i]); #endif // Reset state for the next lemma antes.clear(); antes_added.clear(); clear_localization(); // use a fresh localization for each lemma return res; } // sanity check: make sure that any non-local lit is really resolved // with something in the non_local_lits set void check_non_local(ast lit, non_local_lits *nll){ if(nll) for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ ast con = (*it)->pivot; if(con == mk_not(lit)) return; } assert(0 && "bug in non-local resolution handling"); } void get_local_conclusion_lits(ast proof, bool expect_clause, AstSet &lits){ std::vector reslits; if(expect_clause) get_Z3_lits(conc(proof),reslits); else reslits.push_back(conc(proof)); for(unsigned i = 0; i < reslits.size(); i++) if(is_local(reslits[i])) lits.insert(reslits[i]); AstSet &pfhyps = get_hyps(proof); for(AstSet::iterator hit = pfhyps.begin(), hen = pfhyps.end(); hit != hen; ++hit) if(is_local(*hit)) lits.insert(mk_not(*hit)); } void collect_resolvent_lits(Z3_resolvent *res, const AstSet &hyps, std::vector &lits){ if(!res->is_unit){ std::vector reslits; get_Z3_lits(conc(res->proof),reslits); for(unsigned i = 0; i < reslits.size(); i++) if(reslits[i] != res->pivot) lits.push_back(reslits[i]); } AstSet &pfhyps = get_hyps(res->proof); for(AstSet::iterator hit = pfhyps.begin(), hen = pfhyps.end(); hit != hen; ++hit) if(hyps.find(*hit) == hyps.end()) lits.push_back(mk_not(*hit)); } void filter_resolvent_lits(non_local_lits *nll, std::vector &lits){ std::vector orig_lits; orig_lits.swap(lits); std::set pivs; for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ Z3_resolvent *res = *it; pivs.insert(res->pivot); pivs.insert(mk_not(res->pivot)); } for(unsigned i = 0; i < orig_lits.size(); i++) if(pivs.find(orig_lits[i]) == pivs.end()) lits.push_back(orig_lits[i]); } void collect_all_resolvent_lits(non_local_lits *nll, std::vector &lits){ if(nll){ std::vector orig_lits; orig_lits.swap(lits); std::set pivs; for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ Z3_resolvent *res = *it; pivs.insert(res->pivot); pivs.insert(mk_not(res->pivot)); } for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ Z3_resolvent *res = *it; { std::vector reslits; if(!res->is_unit) get_Z3_lits(conc(res->proof),reslits); else reslits.push_back(conc(res->proof)); for(unsigned i = 0; i < reslits.size(); i++) #if 0 if(reslits[i] != res->pivot && pivs.find(reslits[i]) == pivs.end()) #endif if(is_local(reslits[i])) lits.push_back(reslits[i]); } } for(unsigned i = 0; i < orig_lits.size(); i++) if(pivs.find(orig_lits[i]) == pivs.end()) lits.push_back(orig_lits[i]); } } void collect_proof_clause(ast proof, bool expect_clause, std::vector &lits){ if(expect_clause) get_Z3_lits(conc(proof),lits); else lits.push_back(from_ast(conc(proof))); AstSet &hyps = get_hyps(proof); for(AstSet::iterator hit = hyps.begin(), hen = hyps.end(); hit != hen; ++hit) lits.push_back(mk_not(*hit)); } // turn a bunch of literals into a lemma, replacing // non-local lits with their local equivalents // adds the accumulated antecedents (antes) as // proof obligations of the lemma Iproof::node fix_lemma(std::vector &con_lits, AstSet &hyps, non_local_lits *nll){ std::vector lits(con_lits); for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) lits.push_back(mk_not(*it)); if(nll){ for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ Z3_resolvent *res = *it; collect_resolvent_lits(res,hyps,lits); add_antes(res->proof); } filter_resolvent_lits(nll,lits); } for(unsigned int i = 0; i < lits.size(); i++){ EquivTab::iterator it = equivs.find(lits[i]); if(it != equivs.end()){ lits[i] = it->second.first; // replace with local equivalent add_antes(it->second.second); // collect the premises that prove this } else { if(!is_local(lits[i])){ check_non_local(lits[i],nll); lits[i] = mk_false(); } } } // TODO: should check here that derivation is local? Iproof::node res = prove_lemma(lits); return res; } int num_lits(ast ast){ opr dk = op(ast); if(dk == False) return 0; if(dk == Or){ unsigned nargs = num_args(ast); int n = 0; for(unsigned i = 0; i < nargs; i++) // do all the sub_terms n += num_lits(arg(ast,i)); return n; } else return 1; } struct non_lit_local_ante {}; bool local_antes_simple; bool add_local_antes(ast proof, AstSet &hyps, bool expect_clause = false){ if(antes_added.find(proof) != antes_added.end()) return true; antes_added.insert(proof); ast con = from_ast(conc(proof)); pfrule dk = pr(proof); if(is_local(con) || equivs.find(con) != equivs.end()){ if(!expect_clause || num_lits(conc(proof)) == 1){ AstSet &this_hyps = get_hyps(proof); if(std::includes(hyps.begin(),hyps.end(),this_hyps.begin(),this_hyps.end())){ // if(hyps.find(con) == hyps.end()) #if 0 if(/* lemma_count == SHOW_LEMMA_COUNT - 1 && */ !is_literal_or_lit_iff(conc(proof))){ std::cout << "\nnon-lit local ante\n"; show_step(proof); show(conc(proof)); throw non_lit_local_ante(); } #endif local_antes.push_back(proof); return true; } else ; //std::cout << "bar!\n"; } } if(dk == PR_ASSERTED //|| dk == PR_HYPOTHESIS //|| dk == PR_TH_LEMMA || dk == PR_QUANT_INST //|| dk == PR_UNIT_RESOLUTION //|| dk == PR_LEMMA ) return false; if(dk == PR_HYPOTHESIS && hyps.find(con) != hyps.end()) ; //std::cout << "blif!\n"; if(dk == PR_HYPOTHESIS || dk == PR_LEMMA) ; //std::cout << "foo!\n"; if(dk == PR_TH_LEMMA && num_prems(proof) == 0){ // Check if this is an axiom instance get_axiom_instance(conc(proof)); } // #define SIMPLE_PROOFS #ifdef SIMPLE_PROOFS if(!(dk == PR_TRANSITIVITY || dk == PR_MONOTONICITY)) local_antes_simple = false; #endif unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ ast arg = prem(proof,i); try { if(!add_local_antes(arg, hyps, dk == PR_UNIT_RESOLUTION && i == 0)) return false; } catch (non_lit_local_ante) { std::cout << "\n"; show_step(proof); show(conc(proof)); throw non_lit_local_ante(); } } return true; } std::vector lit_trace; hash_set marked_proofs; bool proof_has_lit(const ast &proof, const ast &lit){ AstSet &hyps = get_hyps(proof); if(hyps.find(mk_not(lit)) != hyps.end()) return true; std::vector lits; ast con = conc(proof); get_Z3_lits(con, lits); for(unsigned i = 0; i < lits.size(); i++) if(lits[i] == lit) return true; return false; } void trace_lit_rec(const ast &lit, const ast &proof, AstHashSet &memo){ if(memo.find(proof) == memo.end()){ memo.insert(proof); AstSet &hyps = get_hyps(proof); std::vector lits; for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) lits.push_back(mk_not(*it)); ast con = conc(proof); get_Z3_lits(con, lits); for(unsigned i = 0; i < lits.size(); i++){ if(lits[i] == lit){ print_expr(std::cout,proof); std::cout << "\n"; marked_proofs.insert(proof); pfrule dk = pr(proof); if(dk == PR_UNIT_RESOLUTION || dk == PR_LEMMA){ unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ ast arg = prem(proof,i); trace_lit_rec(lit,arg,memo); } } else lit_trace.push_back(proof); } } } } ast traced_lit; int trace_lit(const ast &lit, const ast &proof){ marked_proofs.clear(); lit_trace.clear(); traced_lit = lit; AstHashSet memo; trace_lit_rec(lit,proof,memo); return lit_trace.size(); } bool is_literal_or_lit_iff(const ast &lit){ if(my_is_literal(lit)) return true; if(op(lit) == Iff){ return my_is_literal(arg(lit,0)) && my_is_literal(arg(lit,1)); } return false; } bool my_is_literal(const ast &lit){ ast abslit = is_not(lit) ? arg(lit,0) : lit; int f = op(abslit); return !(f == And || f == Or || f == Iff); } void print_lit(const ast &lit){ ast abslit = is_not(lit) ? arg(lit,0) : lit; if(!is_literal_or_lit_iff(lit)){ if(is_not(lit)) std::cout << "~"; std::cout << "["; print_expr(std::cout,abslit); std::cout << "]"; } else print_expr(std::cout,lit); } void show_lit(const ast &lit){ print_lit(lit); std::cout << "\n"; } void print_z3_lit(const ast &a){ print_lit(from_ast(a)); } void show_z3_lit(const ast &a){ print_z3_lit(a); std::cout << "\n"; } void show_con(const ast &proof, bool brief){ if(!traced_lit.null() && proof_has_lit(proof,traced_lit)) std::cout << "(*) "; ast con = conc(proof); AstSet &hyps = get_hyps(proof); int count = 0; for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ if(brief && ++count > 5){ std::cout << "... "; break; } print_lit(*it); std::cout << " "; } std::cout << "|- "; std::vector lits; get_Z3_lits(con,lits); for(unsigned i = 0; i < lits.size(); i++){ print_lit(lits[i]); std::cout << " "; } std::cout << "\n"; } void show_step(const ast &proof){ std::cout << "\n"; unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ std::cout << "(" << i << ") "; ast arg = prem(proof,i); show_con(arg,true); } std::cout << "|------ "; std::cout << string_of_symbol(sym(proof)) << "\n"; show_con(proof,false); } void show_marked( const ast &proof){ std::cout << "\n"; unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ ast arg = prem(proof,i); if(!traced_lit.null() && proof_has_lit(arg,traced_lit)){ std::cout << "(" << i << ") "; show_con(arg,true); } } } std::vector pfhist; int pfhist_pos; void pfgoto(const ast &proof){ if(pfhist.size() == 0) pfhist_pos = 0; else pfhist_pos++; pfhist.resize(pfhist_pos); pfhist.push_back(proof); show_step(proof); } void show_nll(non_local_lits *nll){ if(!nll)return; for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ Z3_resolvent *res = *it; show_step(res->proof); std::cout << "Pivot: "; show(res->pivot); std::cout << std::endl; } } void pfback(){ if(pfhist_pos > 0){ pfhist_pos--; show_step(pfhist[pfhist_pos]); } } void pffwd(){ if(pfhist_pos < ((int)pfhist.size()) - 1){ pfhist_pos++; show_step(pfhist[pfhist_pos]); } } void pfprem(int i){ if(pfhist.size() > 0){ ast proof = pfhist[pfhist_pos]; unsigned nprems = num_prems(proof); if(i >= 0 && i < (int)nprems) pfgoto(prem(proof,i)); } } int extract_th_lemma_common(std::vector &lits, non_local_lits *nll, bool lemma_nll = true){ std::vector la = local_antes; local_antes.clear(); // clear antecedents for next lemma antes_added.clear(); // std::vector lits; AstSet hyps; // no hyps for(unsigned i = 0; i < la.size(); i++) lits.push_back(mk_not(from_ast(conc(la[i])))); // lits.push_back(from_ast(conc(proof))); Iproof::node res =fix_lemma(lits,hyps, lemma_nll ? nll : 0); for(unsigned i = 0; i < la.size(); i++){ Iproof::node q = translate_main(la[i],nll,false); ast pnode = from_ast(conc(la[i])); assert(is_local(pnode) || equivs.find(pnode) != equivs.end()); Iproof::node neg = res; Iproof::node pos = q; if(is_not(pnode)){ pnode = mk_not(pnode); std::swap(neg,pos); } try { res = iproof->make_resolution(pnode,neg,pos); } catch (const iz3proof::proof_error){ std::cout << "\nresolution error in theory lemma\n"; std::cout << "lits:\n"; for(unsigned j = 0; j < lits.size(); j++) show_lit(lits[j]); std::cout << "\nstep:\n"; show_step(la[i]); throw invalid_lemma(); } } return res; } Iproof::node extract_simple_proof(const ast &proof, hash_set &leaves){ if(leaves.find(proof) != leaves.end()) return iproof->make_hypothesis(conc(proof)); ast con = from_ast(conc(proof)); pfrule dk = pr(proof); unsigned nprems = num_prems(proof); std::vector args(nprems); for(unsigned i = 0; i < nprems; i++){ ast arg = prem(proof,i); args[i] = extract_simple_proof(arg,leaves); } switch(dk){ case PR_TRANSITIVITY: return iproof->make_transitivity(con,args[0],args[1]); case PR_MONOTONICITY: return iproof->make_congruence(con,args); case PR_REFLEXIVITY: return iproof->make_reflexivity(con); case PR_SYMMETRY: return iproof->make_symmetry(con,args[0]); } assert(0 && "extract_simple_proof: unknown op"); return 0; } int extract_th_lemma_simple(const ast &proof, std::vector &lits){ std::vector la = local_antes; local_antes.clear(); // clear antecedents for next lemma antes_added.clear(); hash_set leaves; for(unsigned i = 0; i < la.size(); i++) leaves.insert(la[i]); Iproof::node ipf = extract_simple_proof(proof,leaves); ast con = from_ast(conc(proof)); Iproof::node hyp = iproof->make_hypothesis(mk_not(con)); ipf = iproof->make_eqcontra(ipf,hyp); // std::vector lits; AstSet hyps; // no hyps for(unsigned i = 0; i < la.size(); i++) lits.push_back(mk_not(from_ast(conc(la[i])))); // lits.push_back(from_ast(conc(proof))); Iproof::node res = iproof->make_contra(ipf,lits); for(unsigned i = 0; i < la.size(); i++){ Iproof::node q = translate_main(la[i],0,false); ast pnode = from_ast(conc(la[i])); assert(is_local(pnode) || equivs.find(pnode) != equivs.end()); Iproof::node neg = res; Iproof::node pos = q; if(is_not(pnode)){ pnode = mk_not(pnode); std::swap(neg,pos); } try { res = iproof->make_resolution(pnode,neg,pos); } catch (const iz3proof::proof_error){ std::cout << "\nresolution error in theory lemma\n"; std::cout << "lits:\n"; for(unsigned j = 0; j < lits.size(); j++) show_lit(lits[j]); std::cout << "\nstep:\n"; show_step(la[i]); throw invalid_lemma(); } } return res; } // #define NEW_EXTRACT_TH_LEMMA void get_local_hyps(const ast &proof, std::set &res){ std::set hyps = get_hyps(proof); for(std::set::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ ast hyp = *it; if(is_local(hyp)) res.insert(hyp); } } int extract_th_lemma(ast proof, std::vector &lits, non_local_lits *nll){ pfrule dk = pr(proof); unsigned nprems = num_prems(proof); #ifdef NEW_EXTRACT_TH_LEMMA if(nprems == 0 && !nll) #else if(nprems == 0) #endif return 0; if(nprems == 0 && dk == PR_TH_LEMMA) // Check if this is an axiom instance get_axiom_instance(conc(proof)); local_antes_simple = true; for(unsigned i = 0; i < nprems; i++){ ast arg = prem(proof,i); if(!add_local_antes(arg,get_hyps(proof))){ local_antes.clear(); // clear antecedents for next lemma antes_added.clear(); antes.clear(); return 0; } } #ifdef NEW_EXTRACT_TH_LEMMA bool lemma_nll = nprems > 1; if(nll && !lemma_nll){ lemma_nll = false; // std::cout << "lemma count = " << nll_lemma_count << "\n"; for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ Z3_resolvent *res = *it; ast arg = res->proof; std::set loc_hyps; get_local_hyps(arg,loc_hyps); if(!add_local_antes(arg,loc_hyps)){ local_antes.clear(); // clear antecedents for next lemma antes_added.clear(); antes.clear(); return 0; } } collect_all_resolvent_lits(nll,lits); } int my_count = nll_lemma_count++; int res; try { res = extract_th_lemma_common(lits,nll,lemma_nll); } #if 1 catch (const invalid_lemma &) { std::cout << "\n\nlemma: " << my_count; std::cout << "\n\nproof node: \n"; show_step(proof); std::cout << "\n\nnon-local: \n"; show_nll(nll); pfgoto(nll->proofs[0]->proof); show(conc(pfhist.back())); pfprem(1); show(conc(pfhist.back())); pfprem(0); show(conc(pfhist.back())); pfprem(0); show(conc(pfhist.back())); pfprem(0); show(conc(pfhist.back())); std::cout << "\n\nliterals: \n"; for(int i = 0; i < lits.size(); i++) show_lit(lits[i]); throw invalid_lemma(); } #endif return res; #else #ifdef SIMPLE_PROOFS if(local_antes_simple && !nll) return extract_th_lemma_simple(proof, lits); #endif return extract_th_lemma_common(lits,nll); #endif } int extract_th_lemma_ur(ast proof, int position, std::vector &lits, non_local_lits *nll){ for(int i = 0; i <= position; i++){ ast arg = prem(proof,i); if(!add_local_antes(arg,get_hyps(proof),i==0)){ local_antes.clear(); // clear antecedents for next lemma antes_added.clear(); antes.clear(); return 0; } } return extract_th_lemma_common(lits,nll); } // see if any of the pushed resolvents are resolutions // push the current proof into the latest such int push_into_resolvent(ast proof, std::vector &lits, non_local_lits *nll, bool expect_clause){ if(!nll) return 0; if(num_args(proof) > 1) return 0; ResolventAppSet resos = nll->proofs; int pos = resos.size()-1; for( ResolventAppSet::reverse_iterator it = resos.rbegin(), en = resos.rend(); it != en; ++it, --pos){ Z3_resolvent *reso = *it; ast ante = reso->proof; ast pivot = reso->pivot; bool is_unit = reso->is_unit; pfrule dk = pr(ante); bool pushable = dk == PR_UNIT_RESOLUTION || dk == PR_LEMMA; if(!pushable && num_args(ante) > 1){ #if 0 if (!is_local(conc(ante))) std::cout << "non-local "; std::cout << "pushable!\n"; #endif pushable = true; } if(pushable){ // remove the resolvent from list and add new clause as resolvent resos.erase((++it).base()); for(; pos < (int)resos.size(); pos++){ Z3_resolvent *r = resos[pos]; resos[pos] = find_resolvent(r->proof,r->is_unit,mk_not(pivot)); } resos.push_back(find_resolvent(proof,!expect_clause,mk_not(pivot))); non_local_lits *new_nll = find_nll(resos); try { int res = translate_main(ante,new_nll,!is_unit); return res; } catch (const invalid_lemma &) { std::cout << "\n\npushing: \n"; std::cout << "nproof node: \n"; show_step(proof); std::cout << "\n\nold non-local: \n"; show_nll(nll); std::cout << "\n\nnew non-local: \n"; show_nll(new_nll); throw invalid_lemma(); } } } return 0; // no pushed resolvents are resolution steps } non_local_lits *find_nll(ResolventAppSet &proofs){ if(proofs.empty()) return (non_local_lits *)0; std::pair foo(non_local_lits(proofs),(non_local_lits *)0); std::pair::iterator,bool> bar = non_local_lits_unique.insert(foo); non_local_lits *&res = bar.first->second; if(bar.second) res = new non_local_lits(bar.first->first); return res; } Z3_resolvent *find_resolvent(ast proof, bool unit, ast pivot){ std::pair foo(Z3_resolvent(proof,unit,pivot),(Z3_resolvent *)0); std::pair::iterator,bool> bar = Z3_resolvent_unique.insert(foo); Z3_resolvent *&res = bar.first->second; if(bar.second) res = new Z3_resolvent(bar.first->first); return res; } // translate a unit resolution at position pos of given app int translate_ur(ast proof, int position, non_local_lits *nll){ ast ante = prem(proof,position); if(position <= 0) return translate_main(ante, nll); ast pnode = conc(ante); ast pnode_abs = !is_not(pnode) ? pnode : mk_not(pnode); if(is_local(pnode) || equivs.find(pnode) != equivs.end()){ Iproof::node neg = translate_ur(proof,position-1,nll); Iproof::node pos = translate_main(ante, nll, false); if(is_not(pnode)){ pnode = mk_not(pnode); std::swap(neg,pos); } try { return iproof->make_resolution(pnode,neg,pos); } catch (const iz3proof::proof_error){ std::cout << "resolution error in unit_resolution, position" << position << "\n"; show_step(proof); throw invalid_lemma(); } } else { // non-local pivot we have no local equivalent for if(true){ // try pushing the non-local resolution up pfrule dk = pr(ante); non_local_lits *old_nll = nll; if(dk == PR_HYPOTHESIS) ; //std::cout << "non-local hyp!\n"; // resolving with a hyp is a no-op else { ResolventAppSet new_proofs; if(nll) new_proofs = nll->proofs; Z3_resolvent *reso = find_resolvent(ante,true,pnode); new_proofs.push_back(reso); nll = find_nll(new_proofs); } try { return translate_ur(proof,position-1,nll); } catch (const invalid_lemma &) { if(old_nll != nll){ std::cout << "\n\nadded_nll: \n"; std::cout << "nproof node: \n"; show_step(proof); std::cout << "\n\new non-local step: \n"; show_step(nll->proofs.back()->proof); } throw invalid_lemma(); } } else { // just make a lemma std::vector lits; do_unit_resolution(proof,position,lits); int res; if(!(res = extract_th_lemma_ur(proof,position,lits,nll))){ for(int i = 0; i <= position; i++){ z3pf p = prem(proof,i); add_antes(p); } res = fix_lemma(lits,get_hyps(proof),nll); } return res; } } } non_local_lits *update_nll(ast proof, bool expect_clause, non_local_lits *nll){ std::vector lits; collect_proof_clause(proof,expect_clause,lits); AstSet litset; litset.insert(lits.begin(),lits.end()); ResolventAppSet to_keep; for(int i = nll->proofs.size()-1; i >= 0; --i){ ast traced_lit = (nll->proofs[i])->pivot; ast traced_lit_neg = mk_not(traced_lit); if(litset.find(traced_lit) != litset.end() || litset.find(traced_lit_neg) != litset.end()){ to_keep.push_back(nll->proofs[i]); std::vector reslits; AstSet dummy; collect_resolvent_lits(nll->proofs[i],dummy,reslits); litset.insert(reslits.begin(),reslits.end()); } } if(to_keep.size() == nll->proofs.size()) return nll; ResolventAppSet new_proofs; for(int i = to_keep.size() - 1; i >= 0; --i) new_proofs.push_back(to_keep[i]); return find_nll(new_proofs); } // translate a Z3 proof term into a foci proof term Iproof::node translate_main(ast proof, non_local_lits *nll, bool expect_clause = true){ non_local_lits *old_nll = nll; if(nll) nll = update_nll(proof,expect_clause,nll); AstToIpf &tr = nll ? non_local_translation[nll] : translation; hash_map &trc = expect_clause ? tr.first : tr.second; std::pair foo(proof,INT_MAX); std::pair bar = trc.insert(foo); int &res = bar.first->second; if(!bar.second) return res; try { int frame = get_locality(proof); if(frame != -1){ ast e = from_ast(conc(proof)); if(frame >= frames) frame = frames - 1; std::vector foo; if(expect_clause) get_Z3_lits(conc(proof),foo); else foo.push_back(e); AstSet &hyps = get_hyps(proof); for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) foo.push_back(mk_not(*it)); res = iproof->make_assumption(frame,foo); return res; } pfrule dk = pr(proof); unsigned nprems = num_prems(proof); if(dk == PR_UNIT_RESOLUTION){ res = translate_ur(proof, nprems - 1, nll); } else if(dk == PR_LEMMA){ ast contra = prem(proof,0); // this is a proof of false from some hyps res = translate_main(contra, nll); if(!expect_clause){ std::vector foo; // the negations of the hyps form a clause foo.push_back(from_ast(conc(proof))); AstSet &hyps = get_hyps(proof); for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) foo.push_back(mk_not(*it)); res = iproof->make_contra(res,foo); } } else { std::vector lits; ast con = conc(proof); if(expect_clause) get_Z3_lits(con, lits); else lits.push_back(from_ast(con)); #ifdef NEW_EXTRACT_TH_LEMMA if(!(res = push_into_resolvent(proof,lits,nll,expect_clause))){ if(!(res = extract_th_lemma(proof,lits,nll))){ #else if(!(res = extract_th_lemma(proof,lits,nll))){ if(!(res = push_into_resolvent(proof,lits,nll,expect_clause))){ #endif // std::cout << "extract theory lemma failed\n"; add_antes(proof); res = fix_lemma(lits,get_hyps(proof),nll); } } } #ifdef CHECK_PROOFS if(0){ AstSet zpf_con_lits, ipf_con_lits; get_local_conclusion_lits(proof, expect_clause, zpf_con_lits); if(nll){ for(unsigned i = 0; i < nll->proofs.size(); i++) get_local_conclusion_lits(nll->proofs[i]->proof,!nll->proofs[i]->is_unit,zpf_con_lits); } std::vector ipf_con; iproof->get_conclusion(res,ipf_con); for(unsigned i = 0; i < ipf_con.size(); i++) ipf_con_lits.insert(ipf_con[i]); if(!(ipf_con_lits == zpf_con_lits)){ std::cout << "proof error:\n"; std::cout << "expected lits:\n"; for(AstSet::iterator hit = zpf_con_lits.begin(), hen = zpf_con_lits.end(); hit != hen; ++hit) show_lit(*hit); std::cout << "got lits:\n"; for(AstSet::iterator hit = ipf_con_lits.begin(), hen = ipf_con_lits.end(); hit != hen; ++hit) show_lit(*hit); std::cout << "\nproof step:"; show_step(proof); std::cout << "\n"; throw invalid_lemma(); } } #endif return res; } catch (const invalid_lemma &) { if(old_nll != nll){ std::cout << "\n\nupdated nll: \n"; std::cout << "nproof node: \n"; show_step(proof); std::cout << "\n\new non-local: \n"; show_nll(nll); } throw invalid_lemma(); } } // Proof translation is in two stages: // 1) Translate ast proof term to Zproof // 2) Translate Zproof to Iproof Iproof::node translate(ast proof, Iproof &dst){ iproof = &dst; Iproof::node Ipf = translate_main(proof,0); // builds result in dst return Ipf; } iz3translation_direct(iz3mgr &mgr, iz3secondary *_secondary, const std::vector > &cnsts, const std::vector &parents, const std::vector &theory) : iz3translation(mgr, cnsts, parents, theory) { secondary = _secondary; frames = cnsts.size(); traced_lit = ast(); } ~iz3translation_direct(){ for(hash_map::iterator it = non_local_lits_unique.begin(), en = non_local_lits_unique.end(); it != en; ++it) delete it->second; for(hash_map::iterator it = Z3_resolvent_unique.begin(), en = Z3_resolvent_unique.end(); it != en; ++it) delete it->second; } }; #ifdef IZ3_TRANSLATE_DIRECT iz3translation *iz3translation::create(iz3mgr &mgr, iz3secondary *secondary, const std::vector > &cnsts, const std::vector &parents, const std::vector &theory){ return new iz3translation_direct(mgr,secondary,cnsts,parents,theory); } #if 1 void iz3translation_direct_trace_lit(iz3translation_direct *p, iz3mgr::ast lit, iz3mgr::ast proof){ p->trace_lit(lit, proof); } void iz3translation_direct_show_step(iz3translation_direct *p, iz3mgr::ast proof){ p->show_step(proof); } void iz3translation_direct_show_marked(iz3translation_direct *p, iz3mgr::ast proof){ p->show_marked(proof); } void iz3translation_direct_show_lit(iz3translation_direct *p, iz3mgr::ast lit){ p->show_lit(lit); } void iz3translation_direct_show_z3_lit(iz3translation_direct *p, iz3mgr::ast a){ p->show_z3_lit(a); } void iz3translation_direct_pfgoto(iz3translation_direct *p, iz3mgr::ast proof){ p->pfgoto(proof); } void iz3translation_direct_show_nll(iz3translation_direct *p, non_local_lits *nll){ p->show_nll(nll); } void iz3translation_direct_pfback(iz3translation_direct *p ){ p->pfback(); } void iz3translation_direct_pffwd(iz3translation_direct *p ){ p->pffwd(); } void iz3translation_direct_pfprem(iz3translation_direct *p, int i){ p->pfprem(i); } struct stdio_fixer { stdio_fixer(){ std::cout.rdbuf()->pubsetbuf(0,0); } } my_stdio_fixer; #endif #endif z3-4.4.0/src/interp/iz3proof_itp.cpp0000755000175000017500000033143312540347414017102 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3proof.cpp Abstract: This class defines a simple interpolating proof system. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifdef _WINDOWS #pragma warning(disable:4996) #pragma warning(disable:4800) #pragma warning(disable:4267) #pragma warning(disable:4101) #endif #include "iz3proof_itp.h" using namespace stl_ext; // #define INVARIANT_CHECKING class iz3proof_itp_impl : public iz3proof_itp { prover *pv; prover::range rng; bool weak; enum LitType {LitA,LitB,LitMixed}; hash_map placeholders; // These symbols represent deduction rules /* This symbol represents a proof by contradiction. That is, contra(p,l1 /\ ... /\ lk) takes a proof p of l1,...,lk |- false and returns a proof of |- ~l1,...,~l2 */ symb contra; /* The summation rule. The term sum(p,c,i) takes a proof p of an inequality i', an integer coefficient c and an inequality i, and yieds a proof of i' + ci. */ symb sum; /* Proof rotation. The proof term rotate(q,p) takes a proof p of: Gamma, q |- false and yields a proof of: Gamma |- ~q */ symb rotate_sum; /* Inequalities to equality. leq2eq(p, q, r) takes a proof p of ~x=y, a proof q of x <= y and a proof r of y <= x and yields a proof of false. */ symb leq2eq; /* Equality to inequality. eq2leq(p, q) takes a proof p of x=y, and a proof q ~(x <= y) and and yields a proof of false. */ symb eq2leq; /* Proof term cong(p,q) takes a proof p of x=y and a proof q of t != t and returns a proof of false. */ symb cong; /* Excluded middle. exmid(phi,p,q) takes a proof p of phi and a proof q of ~\phi and returns a proof of false. */ symb exmid; /* Symmetry. symm(p) takes a proof p of x=y and produces a proof of y=x. */ symb symm; /* Modus ponens. modpon(p,e,q) takes proofs p of P, e of P=Q and q of ~Q and returns a proof of false. */ symb modpon; /* This oprerator represents a concatenation of rewrites. The term a=b;c=d represents an A rewrite from a to b, followed by a B rewrite fron b to c, followed by an A rewrite from c to d. */ symb concat; /* This represents a lack of a proof */ ast no_proof; // This is used to represent an infinitessimal value ast epsilon; // Represents the top position of a term ast top_pos; // add_pos(i,pos) represents position pos if the ith argument symb add_pos; // rewrite proof rules /* rewrite_A(pos,cond,x=y) derives A |- cond => t[x]_p = t[y]_p where t is an arbitrary term */ symb rewrite_A; /* rewrite_B(pos,cond,x=y) derives B |- cond => t[x]_p = t[y]_p, where t is an arbitrary term */ symb rewrite_B; /* a normalization step is of the form (lhs=rhs) : proof, where "proof" is a proof of lhs=rhs and lhs is a mixed term. If rhs is a mixed term then it must have a greater index than lhs. */ symb normal_step; /* A chain of normalization steps is either "true" (the null chain) or normal_chain( ), where step is a normalization step and tail is a normalization chain. The lhs of must have a less term index than any lhs in the chain. Moreover, the rhs of may not occur as the lhs of step in . If we wish to add lhs=rhs to the beginning of and rhs=rhs' occurs in we must apply transitivity, transforming to lhs=rhs'. */ symb normal_chain; /* If p is a proof of Q and c is a normalization chain, then normal(p,c) is a proof of Q(c) (that is, Q with all substitutions in c performed). */ symb normal; /** Stand-ins for quantifiers */ symb sforall, sexists; ast get_placeholder(ast t){ hash_map::iterator it = placeholders.find(t); if(it != placeholders.end()) return it->second; ast &res = placeholders[t]; res = mk_fresh_constant("@p",get_type(t)); #if 0 std::cout << "placeholder "; print_expr(std::cout,res); std::cout << " = "; print_expr(std::cout,t); std::cout << std::endl; #endif return res; } ast make_contra_node(const ast &pf, const std::vector &lits, int pfok = -1){ if(lits.size() == 0) return pf; std::vector reslits; reslits.push_back(make(contra,pf,mk_false())); for(unsigned i = 0; i < lits.size(); i++){ ast bar; if(pfok & (1 << i)) bar = make(rotate_sum,lits[i],pf); else bar = no_proof; ast foo = make(contra,bar,lits[i]); reslits.push_back(foo); } return make(And,reslits); } LitType get_term_type(const ast &lit){ prover::range r = pv->ast_scope(lit); if(pv->range_is_empty(r)) return LitMixed; if(weak) { if(pv->range_min(r) == SHRT_MIN) return pv->range_contained(r,rng) ? LitA : LitB; else return pv->ranges_intersect(r,rng) ? LitA : LitB; } else return pv->range_contained(r,rng) ? LitA : LitB; } bool term_common(const ast &t){ prover::range r = pv->ast_scope(t); return pv->ranges_intersect(r,rng) && !pv->range_contained(r,rng); } bool term_in_vocab(LitType ty, const ast &lit){ prover::range r = pv->ast_scope(lit); if(ty == LitA){ return pv->ranges_intersect(r,rng); } return !pv->range_contained(r,rng); } /** Make a resolution node with given pivot literal and premises. The conclusion of premise1 should contain the negation of the pivot literal, while the conclusion of premise2 should contain the pivot literal. */ node make_resolution(ast pivot, const std::vector &conc, node premise1, node premise2) { LitType lt = get_term_type(pivot); if(lt == LitA) return my_or(premise1,premise2); if(lt == LitB) return my_and(premise1,premise2); /* the mixed case is a bit complicated */ static int non_local_count = 0; ast res = resolve_arith(pivot,conc,premise1,premise2); #ifdef INVARIANT_CHECKING check_contra(conc,res); #endif non_local_count++; return res; } /* Handles the case of resolution on a mixed arith atom. */ ast resolve_arith(const ast &pivot, const std::vector &conc, node premise1, node premise2){ ast atom = get_lit_atom(pivot); hash_map memo; ast neg_pivot_lit = mk_not(atom); if(op(pivot) != Not) std::swap(premise1,premise2); if(op(pivot) == Equal && op(arg(pivot,0)) == Select && op(arg(pivot,1)) == Select){ neg_pivot_lit = mk_not(neg_pivot_lit); std::swap(premise1,premise2); } return resolve_arith_rec1(memo, neg_pivot_lit, premise1, premise2); } ast apply_coeff(const ast &coeff, const ast &t){ #if 0 rational r; if(!is_integer(coeff,r)) throw "ack!"; ast n = make_int(r.numerator()); ast res = make(Times,n,t); if(!r.is_int()) { ast d = make_int(r.numerator()); res = mk_idiv(res,d); } return res; #endif return make(Times,coeff,t); } ast sum_ineq(const ast &coeff1, const ast &ineq1, const ast &coeff2, const ast &ineq2){ opr sum_op = Leq; if(op(ineq1) == Lt || op(ineq2) == Lt) sum_op = Lt; ast sum_sides[2]; for(int i = 0; i < 2; i++){ sum_sides[i] = make(Plus,apply_coeff(coeff1,arg(ineq1,i)),apply_coeff(coeff2,arg(ineq2,i))); sum_sides[i] = z3_simplify(sum_sides[i]); } return make(sum_op,sum_sides[0],sum_sides[1]); } void collect_contra_resolvents(int from, const ast &pivot1, const ast &pivot, const ast &conj, std::vector &res){ int nargs = num_args(conj); for(int i = from; i < nargs; i++){ ast f = arg(conj,i); if(!(f == pivot)){ ast ph = get_placeholder(mk_not(arg(pivot1,1))); ast pf = arg(pivot1,0); ast thing = pf == no_proof ? no_proof : subst_term_and_simp(ph,pf,arg(f,0)); ast newf = make(contra,thing,arg(f,1)); res.push_back(newf); } } } bool is_negative_equality(const ast &e){ if(op(e) == Not){ opr o = op(arg(e,0)); return o == Equal || o == Iff; } return false; } int count_negative_equalities(const std::vector &resolvent){ int res = 0; for(unsigned i = 0; i < resolvent.size(); i++) if(is_negative_equality(arg(resolvent[i],1))) res++; return res; } ast resolve_contra_nf(const ast &pivot1, const ast &conj1, const ast &pivot2, const ast &conj2){ std::vector resolvent; collect_contra_resolvents(0,pivot1,pivot2,conj2,resolvent); collect_contra_resolvents(1,pivot2,pivot1,conj1,resolvent); if(count_negative_equalities(resolvent) > 1) throw proof_error(); if(resolvent.size() == 1) // we have proved a contradiction return simplify(arg(resolvent[0],0)); // this is the proof -- get interpolant return make(And,resolvent); } ast resolve_contra(const ast &pivot1, const ast &conj1, const ast &pivot2, const ast &conj2){ if(arg(pivot1,0) != no_proof) return resolve_contra_nf(pivot1, conj1, pivot2, conj2); if(arg(pivot2,0) != no_proof) return resolve_contra_nf(pivot2, conj2, pivot1, conj1); return resolve_with_quantifier(pivot1, conj1, pivot2, conj2); } bool is_contra_itp(const ast &pivot1, ast itp2, ast &pivot2){ if(op(itp2) == And){ int nargs = num_args(itp2); for(int i = 1; i < nargs; i++){ ast foo = arg(itp2,i); if(op(foo) == Uninterpreted && sym(foo) == contra){ if(arg(foo,1) == pivot1){ pivot2 = foo; return true; } } else break; } } return false; } ast resolve_arith_rec2(hash_map &memo, const ast &pivot1, const ast &conj1, const ast &itp2){ ast &res = memo[itp2]; if(!res.null()) return res; ast pivot2; if(is_contra_itp(mk_not(arg(pivot1,1)),itp2,pivot2)) res = resolve_contra(pivot1,conj1,pivot2,itp2); else { switch(op(itp2)){ case Or: case And: case Implies: { unsigned nargs = num_args(itp2); std::vector args; args.resize(nargs); for(unsigned i = 0; i < nargs; i++) args[i] = resolve_arith_rec2(memo, pivot1, conj1, arg(itp2,i)); ast foo = itp2; // get rid of const res = clone(foo,args); break; } default: { opr o = op(itp2); if(o == Uninterpreted){ symb s = sym(itp2); if(s == sforall || s == sexists) res = make(s,arg(itp2,0),resolve_arith_rec2(memo, pivot1, conj1, arg(itp2,1))); else res = itp2; } else { res = itp2; } } } } return res; } ast resolve_arith_rec1(hash_map &memo, const ast &neg_pivot_lit, const ast &itp1, const ast &itp2){ ast &res = memo[itp1]; if(!res.null()) return res; ast pivot1; if(is_contra_itp(neg_pivot_lit,itp1,pivot1)){ hash_map memo2; res = resolve_arith_rec2(memo2,pivot1,itp1,itp2); } else { switch(op(itp1)){ case Or: case And: case Implies: { unsigned nargs = num_args(itp1); std::vector args; args.resize(nargs); for(unsigned i = 0; i < nargs; i++) args[i] = resolve_arith_rec1(memo, neg_pivot_lit, arg(itp1,i), itp2); ast foo = itp1; // get rid of const res = clone(foo,args); break; } default: { opr o = op(itp1); if(o == Uninterpreted){ symb s = sym(itp1); if(s == sforall || s == sexists) res = make(s,arg(itp1,0),resolve_arith_rec1(memo, neg_pivot_lit, arg(itp1,1), itp2)); else res = itp1; } else { res = itp1; } } } } return res; } void check_contra(hash_set &memo, hash_set &neg_lits, const ast &foo){ if(memo.find(foo) != memo.end()) return; memo.insert(foo); if(op(foo) == Uninterpreted && sym(foo) == contra){ ast neg_lit = arg(foo,1); if(!is_false(neg_lit) && neg_lits.find(neg_lit) == neg_lits.end()) throw "lost a literal"; return; } else { switch(op(foo)){ case Or: case And: case Implies: { unsigned nargs = num_args(foo); std::vector args; args.resize(nargs); for(unsigned i = 0; i < nargs; i++) check_contra(memo, neg_lits, arg(foo,i)); break; } default: break; } } } void check_contra(const std::vector &neg_lits, const ast &foo){ hash_set memo; hash_set neg_lits_set; for(unsigned i = 0; i < neg_lits.size(); i++) if(get_term_type(neg_lits[i]) == LitMixed) neg_lits_set.insert(mk_not(neg_lits[i])); check_contra(memo,neg_lits_set,foo); } hash_map subst_memo; // memo of subst function ast subst_term_and_simp(const ast &var, const ast &t, const ast &e){ subst_memo.clear(); return subst_term_and_simp_rec(var,t,e); } ast subst_term_and_simp_rec(const ast &var, const ast &t, const ast &e){ if(e == var) return t; std::pair foo(e,ast()); std::pair::iterator,bool> bar = subst_memo.insert(foo); ast &res = bar.first->second; if(bar.second){ if(op(e) == Uninterpreted){ symb g = sym(e); if(g == rotate_sum){ if(var == get_placeholder(arg(e,0))){ res = e; } else res = make(rotate_sum,arg(e,0),subst_term_and_simp_rec(var,t,arg(e,1))); return res; } if(g == concat){ res = e; return res; } } int nargs = num_args(e); std::vector args(nargs); for(int i = 0; i < nargs; i++) args[i] = subst_term_and_simp_rec(var,t,arg(e,i)); opr f = op(e); if(f == Equal && args[0] == args[1]) res = mk_true(); else if(f == And) res = my_and(args); else if(f == Or) res = my_or(args); else if(f == Idiv) res = mk_idiv(args[0],args[1]); else res = clone(e,args); } return res; } /* This is where the real work happens. Here, we simplify the proof obtained by cut elimination, obtaining an interpolant. */ struct cannot_simplify {}; hash_map simplify_memo; ast simplify(const ast &t){ ast res = normalize(simplify_rec(t)); #ifdef BOGUS_QUANTS if(localization_vars.size()) res = add_quants(z3_simplify(res)); #endif return res; } ast simplify_rec(const ast &e){ std::pair foo(e,ast()); std::pair::iterator,bool> bar = simplify_memo.insert(foo); ast &res = bar.first->second; if(bar.second){ int nargs = num_args(e); std::vector args(nargs); bool placeholder_arg = false; symb g = sym(e); if(g == concat){ res = e; return res; } for(int i = 0; i < nargs; i++){ if(i == 0 && g == rotate_sum) args[i] = arg(e,i); else args[i] = simplify_rec(arg(e,i)); placeholder_arg |= is_placeholder(args[i]); } try { opr f = op(e); if(f == Equal && args[0] == args[1]) res = mk_true(); else if(f == And) res = my_and(args); else if(f == Or) res = my_or(args); else if(f == Idiv) res = mk_idiv(args[0],args[1]); else if(f == Uninterpreted && !placeholder_arg){ if(g == rotate_sum) res = simplify_rotate(args); else if(g == symm) res = simplify_symm(args); else if(g == modpon) res = simplify_modpon(args); else if(g == sum) res = simplify_sum(args); else if(g == exmid) res = simplify_exmid(args); else if(g == cong) res = simplify_cong(args); #if 0 else if(g == modpon) res = simplify_modpon(args); else if(g == leq2eq) res = simplify_leq2eq(args); else if(g == eq2leq) res = simplify_eq2leq(args); #endif else res = clone(e,args); } else res = clone(e,args); } catch (const cannot_simplify &){ res = clone(e,args); } } return res; } ast simplify_rotate(const std::vector &args){ const ast &pf = args[1]; ast pl = get_placeholder(args[0]); if(op(pf) == Uninterpreted){ symb g = sym(pf); if(g == sum) return simplify_rotate_sum(pl,pf); if(g == leq2eq) return simplify_rotate_leq2eq(pl,args[0],pf); if(g == eq2leq) return simplify_rotate_eq2leq(pl,args[0],pf); if(g == cong) return simplify_rotate_cong(pl,args[0],pf); if(g == modpon) return simplify_rotate_modpon(pl,args[0],pf); // if(g == symm) return simplify_rotate_symm(pl,args[0],pf); } if(op(pf) == Leq) throw "foo!"; throw cannot_simplify(); } bool is_normal_ineq(const ast &ineq){ if(sym(ineq) == normal) return is_ineq(arg(ineq,0)); return is_ineq(ineq); } ast destruct_cond_ineq(const ast &ineq, ast &Aproves, ast &Bproves){ ast res = ineq; opr o = op(res); if(o == And){ Aproves = my_and(Aproves,arg(res,0)); res = arg(res,1); o = op(res); } if(o == Implies){ Bproves = my_and(Bproves,arg(res,0)); res = arg(res,1); } return res; } ast distribute_coeff(const ast &coeff, const ast &s){ if(sym(s) == sum){ if(sym(arg(s,2)) == sum) return make(sum, distribute_coeff(coeff,arg(s,0)), make_int(rational(1)), distribute_coeff(make(Times,coeff,arg(s,1)), arg(s,2))); else return make(sum, distribute_coeff(coeff,arg(s,0)), make(Times,coeff,arg(s,1)), arg(s,2)); } if(op(s) == Leq && arg(s,1) == make_int(rational(0)) && arg(s,2) == make_int(rational(0))) return s; return make(sum,make(Leq,make_int(rational(0)),make_int(rational(0))),coeff,s); } ast simplify_sum(std::vector &args){ if(args[1] != make_int(rational(1))){ if(sym(args[2]) == sum) return make(sum,args[0],make_int(rational(1)),distribute_coeff(args[1],args[2])); } ast Aproves = mk_true(), Bproves = mk_true(); ast ineq = destruct_cond_ineq(args[0],Aproves,Bproves); if(!is_normal_ineq(ineq)) throw cannot_simplify(); sum_cond_ineq(ineq,args[1],args[2],Aproves,Bproves); return my_and(Aproves,my_implies(Bproves,ineq)); } ast simplify_rotate_sum(const ast &pl, const ast &pf){ ast Aproves = mk_true(), Bproves = mk_true(); ast ineq = make(Leq,make_int("0"),make_int("0")); ineq = rotate_sum_rec(pl,pf,Aproves,Bproves,ineq); if(is_true(Aproves) && is_true(Bproves)) return ineq; return my_and(Aproves,my_implies(Bproves,ineq)); } bool is_rewrite_chain(const ast &chain){ return sym(chain) == concat; } #if 0 ast ineq_from_chain_simple(const ast &chain, ast &cond){ if(is_true(chain)) return chain; ast last = chain_last(chain); ast rest = chain_rest(chain); if(is_true(rest) && is_rewrite_side(LitA,last) && is_true(rewrite_lhs(last))){ cond = my_and(cond,rewrite_cond(last)); return rewrite_rhs(last); } if(is_rewrite_side(LitB,last) && is_true(rewrite_cond(last))) return ineq_from_chain_simple(rest,cond); return chain; } #endif ast ineq_from_chain(const ast &chain, ast &Aproves, ast &Bproves){ if(is_rewrite_chain(chain)) return rewrite_chain_to_normal_ineq(chain,Aproves,Bproves); return chain; } void sum_cond_ineq(ast &ineq, const ast &coeff2, const ast &ineq2, ast &Aproves, ast &Bproves){ opr o = op(ineq2); if(o == And){ sum_cond_ineq(ineq,coeff2,arg(ineq2,1),Aproves,Bproves); Aproves = my_and(Aproves,arg(ineq2,0)); } else if(o == Implies){ sum_cond_ineq(ineq,coeff2,arg(ineq2,1),Aproves,Bproves); Bproves = my_and(Bproves,arg(ineq2,0)); } else { ast the_ineq = ineq_from_chain(ineq2,Aproves,Bproves); if(sym(ineq) == normal || sym(the_ineq) == normal){ sum_normal_ineq(ineq,coeff2,the_ineq,Aproves,Bproves); return; } if(is_ineq(the_ineq)) linear_comb(ineq,coeff2,the_ineq); else throw cannot_simplify(); } } void destruct_normal(const ast &pf, ast &p, ast &n){ if(sym(pf) == normal){ p = arg(pf,0); n = arg(pf,1); } else { p = pf; n = mk_true(); } } void sum_normal_ineq(ast &ineq, const ast &coeff2, const ast &ineq2, ast &Aproves, ast &Bproves){ ast in1,in2,n1,n2; destruct_normal(ineq,in1,n1); destruct_normal(ineq2,in2,n2); ast dummy1, dummy2; sum_cond_ineq(in1,coeff2,in2,dummy1,dummy2); n1 = merge_normal_chains(n1,n2, Aproves, Bproves); ineq = is_true(n1) ? in1 : make_normal(in1,n1); } bool is_ineq(const ast &ineq){ opr o = op(ineq); if(o == Not) o = op(arg(ineq,0)); return o == Leq || o == Lt || o == Geq || o == Gt; } // divide both sides of inequality by a non-negative integer divisor ast idiv_ineq(const ast &ineq1, const ast &divisor){ if(sym(ineq1) == normal){ ast in1,n1; destruct_normal(ineq1,in1,n1); in1 = idiv_ineq(in1,divisor); return make_normal(in1,n1); } if(divisor == make_int(rational(1))) return ineq1; ast ineq = ineq1; if(op(ineq) == Lt) ineq = simplify_ineq(make(Leq,arg(ineq,0),make(Sub,arg(ineq,1),make_int("1")))); return make(op(ineq),mk_idiv(arg(ineq,0),divisor),mk_idiv(arg(ineq,1),divisor)); } ast rotate_sum_rec(const ast &pl, const ast &pf, ast &Aproves, ast &Bproves, ast &ineq){ if(pf == pl){ if(sym(ineq) == normal) return ineq; return simplify_ineq(ineq); } if(op(pf) == Uninterpreted && sym(pf) == sum){ if(arg(pf,2) == pl){ sum_cond_ineq(ineq,make_int("1"),arg(pf,0),Aproves,Bproves); ineq = idiv_ineq(ineq,arg(pf,1)); return ineq; } sum_cond_ineq(ineq,arg(pf,1),arg(pf,2),Aproves,Bproves); return rotate_sum_rec(pl,arg(pf,0),Aproves,Bproves,ineq); } throw cannot_simplify(); } ast simplify_rotate_leq2eq(const ast &pl, const ast &neg_equality, const ast &pf){ if(pl == arg(pf,0)){ ast equality = arg(neg_equality,0); ast x = arg(equality,0); ast y = arg(equality,1); ast Aproves1 = mk_true(), Bproves1 = mk_true(); ast pf1 = destruct_cond_ineq(arg(pf,1), Aproves1, Bproves1); ast pf2 = destruct_cond_ineq(arg(pf,2), Aproves1, Bproves1); ast xleqy = round_ineq(ineq_from_chain(pf1,Aproves1,Bproves1)); ast yleqx = round_ineq(ineq_from_chain(pf2,Aproves1,Bproves1)); ast ineq1 = make(Leq,make_int("0"),make_int("0")); sum_cond_ineq(ineq1,make_int("-1"),xleqy,Aproves1,Bproves1); sum_cond_ineq(ineq1,make_int("-1"),yleqx,Aproves1,Bproves1); ast Acond = my_implies(Aproves1,my_and(Bproves1,z3_simplify(ineq1))); ast Aproves2 = mk_true(), Bproves2 = mk_true(); ast ineq2 = make(Leq,make_int("0"),make_int("0")); sum_cond_ineq(ineq2,make_int("1"),xleqy,Aproves2,Bproves2); sum_cond_ineq(ineq2,make_int("1"),yleqx,Aproves2,Bproves2); ast Bcond = my_implies(Bproves1,my_and(Aproves1,z3_simplify(ineq2))); // if(!is_true(Aproves1) || !is_true(Bproves1)) // std::cout << "foo!\n";; if(y == make_int(rational(0)) && op(x) == Plus && num_args(x) == 2){ if(get_term_type(arg(x,0)) == LitA){ ast iter = z3_simplify(make(Plus,arg(x,0),get_ineq_rhs(xleqy))); ast rewrite1 = make_rewrite(LitA,pos_add(0,top_pos),Acond,make(Equal,arg(x,0),iter)); iter = make(Plus,iter,arg(x,1)); ast rewrite2 = make_rewrite(LitB,top_pos,Bcond,make(Equal,iter,y)); return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); } if(get_term_type(arg(x,1)) == LitA){ ast iter = z3_simplify(make(Plus,arg(x,1),get_ineq_rhs(xleqy))); ast rewrite1 = make_rewrite(LitA,pos_add(1,top_pos),Acond,make(Equal,arg(x,1),iter)); iter = make(Plus,arg(x,0),iter); ast rewrite2 = make_rewrite(LitB,top_pos,Bcond,make(Equal,iter,y)); return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); } } if(get_term_type(x) == LitA){ ast iter = z3_simplify(make(Plus,x,get_ineq_rhs(xleqy))); ast rewrite1 = make_rewrite(LitA,top_pos,Acond,make(Equal,x,iter)); ast rewrite2 = make_rewrite(LitB,top_pos,Bcond,make(Equal,iter,y)); return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); } if(get_term_type(y) == LitA){ ast iter = z3_simplify(make(Plus,y,get_ineq_rhs(yleqx))); ast rewrite2 = make_rewrite(LitA,top_pos,Acond,make(Equal,iter,y)); ast rewrite1 = make_rewrite(LitB,top_pos,Bcond,make(Equal,x,iter)); return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); } throw cannot_simplify(); } throw cannot_simplify(); } ast round_ineq(const ast &ineq){ if(sym(ineq) == normal) return make_normal(round_ineq(arg(ineq,0)),arg(ineq,1)); if(!is_ineq(ineq)) throw cannot_simplify(); ast res = simplify_ineq(ineq); if(op(res) == Lt) res = make(Leq,arg(res,0),make(Sub,arg(res,1),make_int("1"))); return res; } ast unmixed_eq2ineq(const ast &lhs, const ast &rhs, opr comp_op, const ast &equa, ast &cond){ ast ineqs= chain_ineqs(comp_op,LitA,equa,lhs,rhs); // chain must be from lhs to rhs cond = my_and(cond,chain_conditions(LitA,equa)); ast Bconds = z3_simplify(chain_conditions(LitB,equa)); if(is_true(Bconds) && op(ineqs) != And) return my_implies(cond,ineqs); if(op(ineqs) != And) return my_and(Bconds,my_implies(cond,ineqs)); throw "help!"; } ast add_mixed_eq2ineq(const ast &lhs, const ast &rhs, const ast &equa, const ast &itp){ if(is_true(equa)) return itp; std::vector args(3); args[0] = itp; args[1] = make_int("1"); ast ineq = make(Leq,make_int(rational(0)),make_int(rational(0))); args[2] = make_normal(ineq,cons_normal(fix_normal(lhs,rhs,equa),mk_true())); return simplify_sum(args); } ast simplify_rotate_eq2leq(const ast &pl, const ast &neg_equality, const ast &pf){ if(pl == arg(pf,1)){ ast cond = mk_true(); ast equa = sep_cond(arg(pf,0),cond); if(is_equivrel_chain(equa)){ ast lhs,rhs; eq_from_ineq(arg(neg_equality,0),lhs,rhs); // get inequality we need to prove if(!rewrites_from_to(equa,lhs,rhs)){ lhs = arg(arg(neg_equality,0),0); // the equality proved is ambiguous, sadly rhs = arg(arg(neg_equality,0),1); } LitType lhst = get_term_type(lhs), rhst = get_term_type(rhs); if(lhst != LitMixed && rhst != LitMixed) return unmixed_eq2ineq(lhs, rhs, op(arg(neg_equality,0)), equa, cond); else { ast left, left_term, middle, right_term, right; left = get_left_movers(equa,lhs,middle,left_term); middle = get_right_movers(middle,rhs,right,right_term); ast itp = unmixed_eq2ineq(left_term, right_term, op(arg(neg_equality,0)), middle, cond); // itp = my_implies(cond,itp); itp = add_mixed_eq2ineq(lhs, left_term, left, itp); itp = add_mixed_eq2ineq(right_term, rhs, right, itp); return itp; } } } throw "help!"; } void reverse_modpon(std::vector &args){ std::vector sargs(1); sargs[0] = args[1]; args[1] = simplify_symm(sargs); if(is_equivrel_chain(args[2])) args[1] = down_chain(args[1]); std::swap(args[0],args[2]); } ast simplify_rotate_modpon(const ast &pl, const ast &neg_equality, const ast &pf){ std::vector args; args.resize(3); args[0] = arg(pf,0); args[1] = arg(pf,1); args[2] = arg(pf,2); if(pl == args[0]) reverse_modpon(args); if(pl == args[2]){ ast cond = mk_true(); ast chain = simplify_modpon_fwd(args, cond); return my_implies(cond,chain); } throw cannot_simplify(); } ast get_ineq_rhs(const ast &ineq2){ opr o = op(ineq2); if(o == Implies) return get_ineq_rhs(arg(ineq2,1)); else if(o == Leq || o == Lt) return arg(ineq2,1); throw cannot_simplify(); } ast simplify_rotate_cong(const ast &pl, const ast &neg_equality, const ast &pf){ if(pl == arg(pf,2)){ if(op(arg(pf,0)) == True) return mk_true(); rational pos; if(is_numeral(arg(pf,1),pos)){ int ipos = pos.get_unsigned(); ast cond = mk_true(); ast equa = sep_cond(arg(pf,0),cond); #if 0 if(op(equa) == Equal){ ast pe = mk_not(neg_equality); ast lhs = subst_in_arg_pos(ipos,arg(equa,0),arg(pe,0)); ast rhs = subst_in_arg_pos(ipos,arg(equa,1),arg(pe,1)); ast res = make(Equal,lhs,rhs); return my_implies(cond,res); } #endif ast res = chain_pos_add(ipos,equa); return my_implies(cond,res); } } throw cannot_simplify(); } ast simplify_symm(const std::vector &args){ if(op(args[0]) == True) return mk_true(); ast cond = mk_true(); ast equa = sep_cond(args[0],cond); if(is_equivrel_chain(equa)) return my_implies(cond,reverse_chain(equa)); if(is_negation_chain(equa)) return commute_negation_chain(equa); throw cannot_simplify(); } ast simplify_modpon_fwd(const std::vector &args, ast &cond){ ast P = sep_cond(args[0],cond); ast PeqQ = sep_cond(args[1],cond); ast chain; if(is_equivrel_chain(P)){ try { ast split[2]; split_chain(PeqQ,split); chain = reverse_chain(split[0]); chain = concat_rewrite_chain(chain,P); chain = concat_rewrite_chain(chain,split[1]); } catch(const cannot_split &){ static int this_count = 0; this_count++; ast tail, pref = get_head_chain(PeqQ,tail,false); // pref is x=y, tail is x=y -> x'=y' ast split[2]; split_chain(tail,split); // rewrites from x to x' and y to y' ast head = chain_last(pref); ast prem = make_rewrite(rewrite_side(head),top_pos,rewrite_cond(head),make(Iff,mk_true(),mk_not(rewrite_lhs(head)))); ast back_chain = chain_cons(mk_true(),prem); back_chain = concat_rewrite_chain(back_chain,chain_pos_add(0,reverse_chain(chain_rest(pref)))); ast cond = contra_chain(back_chain,P); if(is_rewrite_side(LitA,head)) cond = mk_not(cond); ast fwd_rewrite = make_rewrite(rewrite_side(head),top_pos,cond,rewrite_rhs(head)); P = chain_cons(mk_true(),fwd_rewrite); chain = reverse_chain(split[0]); chain = concat_rewrite_chain(chain,P); chain = concat_rewrite_chain(chain,split[1]); } } else { // if not an equivalence, must be of form T <-> pred chain = concat_rewrite_chain(P,PeqQ); } return chain; } struct subterm_normals_failed {}; void get_subterm_normals(const ast &ineq1, const ast &ineq2, const ast &chain, ast &normals, const ast &pos, hash_set &memo, ast &Aproves, ast &Bproves){ opr o1 = op(ineq1); opr o2 = op(ineq2); if(o1 == Not || o1 == Leq || o1 == Lt || o1 == Geq || o1 == Gt || o1 == Plus || o1 == Times){ int n = num_args(ineq1); if(o2 != o1 || num_args(ineq2) != n) throw "bad inequality rewriting"; for(int i = 0; i < n; i++){ ast new_pos = add_pos_to_end(pos,i); get_subterm_normals(arg(ineq1,i), arg(ineq2,i), chain, normals, new_pos, memo, Aproves, Bproves); } } else if(get_term_type(ineq2) == LitMixed){ if(memo.find(ineq2) == memo.end()){ memo.insert(ineq2); ast sub_chain = extract_rewrites(chain,pos); if(is_true(sub_chain)) throw "bad inequality rewriting"; ast new_normal = make_normal_step(ineq2,ineq1,reverse_chain(sub_chain)); normals = merge_normal_chains(normals,cons_normal(new_normal,mk_true()), Aproves, Bproves); } } else if(!(ineq1 == ineq2)) throw subterm_normals_failed(); } ast rewrites_to_normals(const ast &ineq1, const ast &chain, ast &normals, ast &Aproves, ast &Bproves, ast &Aineqs){ if(is_true(chain)) return ineq1; ast last = chain_last(chain); ast rest = chain_rest(chain); ast new_ineq1 = rewrites_to_normals(ineq1, rest, normals, Aproves, Bproves, Aineqs); ast p1 = rewrite_pos(last); ast term1; ast coeff = arith_rewrite_coeff(new_ineq1,p1,term1); ast res = subst_in_pos(new_ineq1,rewrite_pos(last),rewrite_rhs(last)); ast rpos; pos_diff(p1,rewrite_pos(last),rpos); ast term2 = subst_in_pos(term1,rpos,rewrite_rhs(last)); if(get_term_type(term1) != LitMixed && get_term_type(term2) != LitMixed){ if(is_rewrite_side(LitA,last)) linear_comb(Aineqs,coeff,make(Leq,make_int(rational(0)),make(Sub,term2,term1))); } else { ast pf = extract_rewrites(make(concat,mk_true(),last),p1); ast new_normal = fix_normal(term1,term2,pf); normals = merge_normal_chains(normals,cons_normal(new_normal,mk_true()), Aproves, Bproves); } return res; } ast arith_rewrite_coeff(const ast &ineq, ast &p1, ast &term){ ast coeff = make_int(rational(1)); if(p1 == top_pos){ term = ineq; return coeff; } int argpos = pos_arg(p1); opr o = op(ineq); switch(o){ case Leq: case Lt: coeff = argpos ? make_int(rational(1)) : make_int(rational(-1)); break; case Geq: case Gt: coeff = argpos ? make_int(rational(-1)) : make_int(rational(1)); break; case Not: coeff = make_int(rational(-1)); case Plus: break; case Times: coeff = arg(ineq,0); break; default: p1 = top_pos; term = ineq; return coeff; } p1 = arg(p1,1); ast res = arith_rewrite_coeff(arg(ineq,argpos),p1,term); p1 = pos_add(argpos,p1); return coeff == make_int(rational(1)) ? res : make(Times,coeff,res); } ast rewrite_chain_to_normal_ineq(const ast &chain, ast &Aproves, ast &Bproves){ ast tail, pref = get_head_chain(chain,tail,false); // pref is x=y, tail is x=y -> x'=y' ast head = chain_last(pref); ast ineq1 = rewrite_rhs(head); ast ineq2 = apply_rewrite_chain(ineq1,tail); ast nc = mk_true(); hash_set memo; ast itp = make(Leq,make_int(rational(0)),make_int(rational(0))); ast Aproves_save = Aproves, Bproves_save = Bproves; try { get_subterm_normals(ineq1,ineq2,tail,nc,top_pos,memo, Aproves, Bproves); } catch (const subterm_normals_failed &){ Aproves = Aproves_save; Bproves = Bproves_save; nc = mk_true(); rewrites_to_normals(ineq1, tail, nc, Aproves, Bproves, itp); } if(is_rewrite_side(LitA,head)){ linear_comb(itp,make_int("1"),ineq1); // make sure it is normal form //itp = ineq1; ast mc = z3_simplify(chain_side_proves(LitB,pref)); Bproves = my_and(Bproves,mc); } else { ast mc = z3_simplify(chain_side_proves(LitA,pref)); Aproves = my_and(Aproves,mc); } if(is_true(nc)) return itp; return make_normal(itp,nc); } /* Given a chain rewrite chain deriving not P and a rewrite chain deriving P, return an interpolant. */ ast contra_chain(const ast &neg_chain, const ast &pos_chain){ // equality is a special case. we use the derivation of x=y to rewrite not(x=y) to not(y=y) if(is_equivrel_chain(pos_chain)){ ast tail, pref = get_head_chain(neg_chain,tail); // pref is not(x=y), tail is not(x,y) -> not(x',y') ast split[2]; split_chain(down_chain(tail),split); // rewrites from x to x' and y to y' ast chain = split[0]; chain = concat_rewrite_chain(chain,pos_chain); // rewrites from x to y' chain = concat_rewrite_chain(chain,reverse_chain(split[1])); // rewrites from x to y chain = concat_rewrite_chain(pref,chain_pos_add(0,chain_pos_add(0,chain))); // rewrites t -> not(y=y) ast head = chain_last(pref); if(is_rewrite_side(LitB,head)){ ast condition = chain_conditions(LitB,chain); return my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),condition); } else { ast condition = chain_conditions(LitA,chain); return my_and(chain_conditions(LitB,chain),my_implies(condition,mk_not(chain_formulas(LitB,chain)))); } // ast chain = concat_rewrite_chain(neg_chain,chain_pos_add(0,chain_pos_add(0,pos_chain))); // return my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),chain_conditions(LitB,chain)); } // otherwise, we reverse the derivation of t = P and use it to rewrite not(P) to not(t) ast chain = concat_rewrite_chain(neg_chain,chain_pos_add(0,reverse_chain(pos_chain))); return my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),chain_conditions(LitB,chain)); } ast simplify_modpon(const std::vector &args){ ast Aproves = mk_true(), Bproves = mk_true(); ast chain = simplify_modpon_fwd(args,Bproves); ast Q2 = destruct_cond_ineq(args[2],Aproves,Bproves); ast interp; if(is_normal_ineq(Q2)){ // inequalities are special ast nQ2 = rewrite_chain_to_normal_ineq(chain,Aproves,Bproves); sum_cond_ineq(nQ2,make_int(rational(1)),Q2,Aproves,Bproves); interp = normalize(nQ2); } else interp = is_negation_chain(chain) ? contra_chain(chain,Q2) : contra_chain(Q2,chain); return my_and(Aproves,my_implies(Bproves,interp)); } ast simplify_exmid(const std::vector &args){ if(is_equivrel(args[0])){ ast Aproves = mk_true(), Bproves = mk_true(); ast chain = destruct_cond_ineq(args[1],Aproves,Bproves); ast Q2 = destruct_cond_ineq(args[2],Aproves,Bproves); ast interp = contra_chain(Q2,chain); return my_and(Aproves,my_implies(Bproves,interp)); } throw "bad exmid"; } ast simplify_cong(const std::vector &args){ ast Aproves = mk_true(), Bproves = mk_true(); ast chain = destruct_cond_ineq(args[0],Aproves,Bproves); rational pos; if(is_numeral(args[1],pos)){ int ipos = pos.get_unsigned(); chain = chain_pos_add(ipos,chain); ast Q2 = destruct_cond_ineq(args[2],Aproves,Bproves); ast interp = contra_chain(Q2,chain); return my_and(Aproves,my_implies(Bproves,interp)); } throw "bad cong"; } bool is_equivrel(const ast &p){ opr o = op(p); return o == Equal || o == Iff; } struct rewrites_failed{}; /* Suppose p in Lang(B) and A |- p -> q and B |- q -> r. Return a z in Lang(B) such that B |- p -> z and A |- z -> q. Collect any side conditions in "rules". */ ast commute_rewrites(const ast &p, const ast &q, const ast &r, ast &rules){ if(q == r) return p; if(p == q) return r; else { ast rew = make(Equal,q,r); if(get_term_type(rew) == LitB){ apply_common_rewrites(p,p,q,rules); // A rewrites must be over comon vocab return r; } } if(sym(p) != sym(q) || sym(q) != sym(r)) throw rewrites_failed(); int nargs = num_args(p); if(nargs != num_args(q) || nargs != num_args(r)) throw rewrites_failed(); std::vector args; args.resize(nargs); for(int i = 0; i < nargs; i++) args[i] = commute_rewrites(arg(p,i),arg(q,i),arg(r,i),rules); return clone(p,args); } ast apply_common_rewrites(const ast &p, const ast &q, const ast &r, ast &rules){ if(q == r) return p; ast rew = make(Equal,q,r); if(term_common(rew)){ if(p != q) throw rewrites_failed(); rules = my_and(rules,rew); return r; } if(sym(p) != sym(q) || sym(q) != sym(r)) return p; int nargs = num_args(p); if(nargs != num_args(q) || nargs != num_args(r)) return p; std::vector args; args.resize(nargs); for(int i = 0; i < nargs; i++) args[i] = apply_common_rewrites(arg(p,i),arg(q,i),arg(r,i),rules); return clone(p,args); } ast apply_all_rewrites(const ast &p, const ast &q, const ast &r){ if(q == r) return p; if(p == q) return r; if(sym(p) != sym(q) || sym(q) != sym(r)) throw rewrites_failed(); int nargs = num_args(p); if(nargs != num_args(q) || nargs != num_args(r)) throw rewrites_failed(); std::vector args; args.resize(nargs); for(int i = 0; i < nargs; i++) args[i] = apply_all_rewrites(arg(p,i),arg(q,i),arg(r,i)); return clone(p,args); } ast delta(const ast &x, const ast &y){ if(op(x) != op(y) || (op(x) == Uninterpreted && sym(x) != sym(y)) || num_args(x) != num_args(y)) return make(Equal,x,y); ast res = mk_true(); int nargs = num_args(x); for(int i = 0; i < nargs; i++) res = my_and(res,delta(arg(x,i),arg(y,i))); return res; } bool diff_rec(LitType t, const ast &p, const ast &q, ast &pd, ast &qd){ if(p == q) return false; if(term_in_vocab(t,p) && term_in_vocab(t,q)){ pd = p; qd = q; return true; } else { if(sym(p) != sym(q)) return false; int nargs = num_args(p); if(num_args(q) != nargs) return false; for(int i = 0; i < nargs; i++) if(diff_rec(t,arg(p,i),arg(q,i),pd,qd)) return true; return false; } } void diff(LitType t, const ast &p, const ast &q, ast &pd, ast &qd){ if(!diff_rec(t,p,q,pd,qd)) throw cannot_simplify(); } bool apply_diff_rec(LitType t, const ast &inp, const ast &p, const ast &q, ast &out){ if(p == q) return false; if(term_in_vocab(t,p) && term_in_vocab(t,q)){ if(inp != p) throw cannot_simplify(); out = q; return true; } else { int nargs = num_args(p); if(sym(p) != sym(q)) throw cannot_simplify(); if(num_args(q) != nargs) throw cannot_simplify(); if(sym(p) != sym(inp)) throw cannot_simplify(); if(num_args(inp) != nargs) throw cannot_simplify(); for(int i = 0; i < nargs; i++) if(apply_diff_rec(t,arg(inp,i),arg(p,i),arg(q,i),out)) return true; return false; } } ast apply_diff(LitType t, const ast &inp, const ast &p, const ast &q){ ast out; if(!apply_diff_rec(t,inp,p,q,out)) throw cannot_simplify(); return out; } bool merge_A_rewrites(const ast &A1, const ast &A2, ast &merged) { if(arg(A1,1) == arg(A2,0)){ merged = make(op(A1),arg(A1,0),arg(A2,1)); return true; } ast diff1l, diff1r, diff2l, diff2r,diffBl,diffBr; diff(LitA,arg(A1,0),arg(A1,1),diff1l,diff1r); diff(LitA,arg(A2,0),arg(A2,1),diff2l,diff2r); diff(LitB,arg(A1,1),arg(A2,0),diffBl,diffBr); if(!term_common(diff2l) && !term_common(diffBr)){ ast A1r = apply_diff(LitB,arg(A2,1),arg(A2,0),arg(A1,1)); merged = make(op(A1),arg(A1,0),A1r); return true; } if(!term_common(diff1r) && !term_common(diffBl)){ ast A2l = apply_diff(LitB,arg(A1,0),arg(A1,1),arg(A2,0)); merged = make(op(A1),A2l,arg(A2,1)); return true; } return false; } void collect_A_rewrites(const ast &t, std::vector &res){ if(is_true(t)) return; if(sym(t) == concat){ res.push_back(arg(t,0)); collect_A_rewrites(arg(t,0),res); return; } res.push_back(t); } ast concat_A_rewrites(const std::vector &rew){ if(rew.size() == 0) return mk_true(); ast res = rew[0]; for(unsigned i = 1; i < rew.size(); i++) res = make(concat,res,rew[i]); return res; } ast merge_concat_rewrites(const ast &A1, const ast &A2){ std::vector rew; collect_A_rewrites(A1,rew); int first = rew.size(), last = first; // range that might need merging collect_A_rewrites(A2,rew); while(first > 0 && first < (int)rew.size() && first <= last){ ast merged; if(merge_A_rewrites(rew[first-1],rew[first],merged)){ rew[first] = merged; first--; rew.erase(rew.begin()+first); last--; if(first >= last) last = first+1; } else first++; } return concat_A_rewrites(rew); } ast sep_cond(const ast &t, ast &cond){ if(op(t) == Implies){ cond = my_and(cond,arg(t,0)); return arg(t,1); } return t; } /* operations on term positions */ /** Finds the difference between two positions. If p1 < p2 (p1 is a position below p2), returns -1 and sets diff to p2-p1 (the psath from position p2 to position p1). If p2 < p1 (p2 is a position below p1), returns 1 and sets diff to p1-p2 (the psath from position p1 to position p2). If equal, return 0 and set diff to top_pos. Else (if p1 and p2 are independent) returns 2 and leaves diff unchanged. */ int pos_diff(const ast &p1, const ast &p2, ast &diff){ if(p1 == top_pos && p2 != top_pos){ diff = p2; return 1; } if(p2 == top_pos && p1 != top_pos){ diff = p1; return -1; } if(p1 == top_pos && p2 == top_pos){ diff = p1; return 0; } if(arg(p1,0) == arg(p2,0)) // same argument position, recur return pos_diff(arg(p1,1),arg(p2,1),diff); return 2; } /* return the position of pos in the argth argument */ ast pos_add(int arg, const ast &pos){ return make(add_pos,make_int(rational(arg)),pos); } ast add_pos_to_end(const ast &pos, int i){ if(pos == top_pos) return pos_add(i,pos); return make(add_pos,arg(pos,0),add_pos_to_end(arg(pos,1),i)); } /* return the argument number of position, if not top */ int pos_arg(const ast &pos){ rational r; if(is_numeral(arg(pos,0),r)) return r.get_unsigned(); throw "bad position!"; } /* substitute y into position pos in x */ ast subst_in_pos(const ast &x, const ast &pos, const ast &y){ if(pos == top_pos) return y; int p = pos_arg(pos); int nargs = num_args(x); if(p >= 0 && p < nargs){ std::vector args(nargs); for(int i = 0; i < nargs; i++) args[i] = i == p ? subst_in_pos(arg(x,i),arg(pos,1),y) : arg(x,i); return clone(x,args); } throw "bad term position!"; } ast diff_chain(LitType t, const ast &pos, const ast &x, const ast &y, const ast &prefix){ int nargs = num_args(x); if(x == y) return prefix; if(sym(x) == sym(y) && nargs == num_args(y)){ ast res = prefix; for(int i = 0; i < nargs; i++) res = diff_chain(t,pos_add(i,pos),arg(x,i),arg(y,i),res); return res; } return chain_cons(prefix,make_rewrite(t,pos,mk_true(),make_equiv_rel(x,y))); } /* operations on rewrites */ ast make_rewrite(LitType t, const ast &pos, const ast &cond, const ast &equality){ #if 0 if(pos == top_pos && op(equality) == Iff && !is_true(arg(equality,0))) throw "bad rewrite"; #endif if(!is_equivrel(equality)) throw "bad rewrite"; return make(t == LitA ? rewrite_A : rewrite_B, pos, cond, equality); } ast rewrite_pos(const ast &rew){ return arg(rew,0); } ast rewrite_cond(const ast &rew){ return arg(rew,1); } ast rewrite_equ(const ast &rew){ return arg(rew,2); } ast rewrite_lhs(const ast &rew){ return arg(arg(rew,2),0); } ast rewrite_rhs(const ast &rew){ return arg(arg(rew,2),1); } /* operations on rewrite chains */ ast chain_cons(const ast &chain, const ast &elem){ return make(concat,chain,elem); } ast chain_rest(const ast &chain){ return arg(chain,0); } ast chain_last(const ast &chain){ return arg(chain,1); } ast rewrite_update_rhs(const ast &rew, const ast &pos, const ast &new_rhs, const ast &new_cond){ ast foo = subst_in_pos(rewrite_rhs(rew),pos,new_rhs); ast equality = arg(rew,2); return make(sym(rew),rewrite_pos(rew),my_and(rewrite_cond(rew),new_cond),make(op(equality),arg(equality,0),foo)); } ast rewrite_update_lhs(const ast &rew, const ast &pos, const ast &new_lhs, const ast &new_cond){ ast foo = subst_in_pos(rewrite_lhs(rew),pos,new_lhs); ast equality = arg(rew,2); return make(sym(rew),rewrite_pos(rew),my_and(rewrite_cond(rew),new_cond),make(op(equality),foo,arg(equality,1))); } bool is_common_rewrite(const ast &rew){ return term_common(arg(rew,2)); } bool is_right_mover(const ast &rew){ return term_common(rewrite_lhs(rew)) && !term_common(rewrite_rhs(rew)); } bool is_left_mover(const ast &rew){ return term_common(rewrite_rhs(rew)) && !term_common(rewrite_lhs(rew)); } bool same_side(const ast &rew1, const ast &rew2){ return sym(rew1) == sym(rew2); } bool is_rewrite_side(LitType t, const ast &rew){ if(t == LitA) return sym(rew) == rewrite_A; return sym(rew) == rewrite_B; } LitType rewrite_side(const ast &rew){ return (sym(rew) == rewrite_A) ? LitA : LitB; } ast rewrite_to_formula(const ast &rew){ return my_implies(arg(rew,1),arg(rew,2)); } // make rewrite rew conditon on rewrite cond ast rewrite_conditional(const ast &cond, const ast &rew){ ast cf = rewrite_to_formula(cond); return make(sym(rew),arg(rew,0),my_and(arg(rew,1),cf),arg(rew,2)); } ast reverse_rewrite(const ast &rew){ ast equ = arg(rew,2); return make(sym(rew),arg(rew,0),arg(rew,1),make(op(equ),arg(equ,1),arg(equ,0))); } ast rewrite_pos_add(int apos, const ast &rew){ return make(sym(rew),pos_add(apos,arg(rew,0)),arg(rew,1),arg(rew,2)); } ast rewrite_pos_set(const ast &pos, const ast &rew){ return make(sym(rew),pos,arg(rew,1),arg(rew,2)); } ast rewrite_up(const ast &rew){ return make(sym(rew),arg(arg(rew,0),1),arg(rew,1),arg(rew,2)); } /** Adds a rewrite to a chain of rewrites, keeping the chain in normal form. An empty chain is represented by true.*/ ast add_rewrite_to_chain(const ast &chain, const ast &rewrite){ if(is_true(chain)) return chain_cons(chain,rewrite); ast last = chain_last(chain); ast rest = chain_rest(chain); if(same_side(last,rewrite)){ ast p1 = rewrite_pos(last); ast p2 = rewrite_pos(rewrite); ast diff; switch(pos_diff(p1,p2,diff)){ case 1: { ast absorb = rewrite_update_rhs(last,diff,rewrite_rhs(rewrite),rewrite_cond(rewrite)); return add_rewrite_to_chain(rest,absorb); } case 0: case -1: { ast absorb = rewrite_update_lhs(rewrite,diff,rewrite_lhs(last),rewrite_cond(last)); return add_rewrite_to_chain(rest,absorb); } default: {// independent case bool rm = is_right_mover(last); bool lm = is_left_mover(rewrite); if((lm && !rm) || (rm && !lm)) return chain_swap(rest,last,rewrite); } } } else { if(is_left_mover(rewrite)){ if(is_common_rewrite(last)) return add_rewrite_to_chain(chain_cons(rest,flip_rewrite(last)),rewrite); if(!is_left_mover(last)) return chain_swap(rest,last,rewrite); } if(is_right_mover(last)){ if(is_common_rewrite(rewrite)) return add_rewrite_to_chain(chain,flip_rewrite(rewrite)); if(!is_right_mover(rewrite)) return chain_swap(rest,last,rewrite); } } return chain_cons(chain,rewrite); } ast chain_swap(const ast &rest, const ast &last, const ast &rewrite){ return chain_cons(add_rewrite_to_chain(rest,rewrite),last); } ast flip_rewrite(const ast &rew){ symb flip_sym = (sym(rew) == rewrite_A) ? rewrite_B : rewrite_A; ast cf = rewrite_to_formula(rew); return make(flip_sym,arg(rew,0),my_implies(arg(rew,1),cf),arg(rew,2)); } /** concatenates two rewrite chains, keeping result in normal form. */ ast concat_rewrite_chain(const ast &chain1, const ast &chain2){ if(is_true(chain2)) return chain1; if(is_true(chain1)) return chain2; ast foo = concat_rewrite_chain(chain1,chain_rest(chain2)); return add_rewrite_to_chain(foo,chain_last(chain2)); } /** reverse a chain of rewrites */ ast reverse_chain_rec(const ast &chain, const ast &prefix){ if(is_true(chain)) return prefix; ast last = reverse_rewrite(chain_last(chain)); ast rest = chain_rest(chain); return reverse_chain_rec(rest,chain_cons(prefix,last)); } ast reverse_chain(const ast &chain){ return reverse_chain_rec(chain,mk_true()); } bool is_equivrel_chain(const ast &chain){ if(is_true(chain)) return true; ast last = chain_last(chain); ast rest = chain_rest(chain); if(is_true(rest)) return !is_true(rewrite_lhs(last)); return is_equivrel_chain(rest); } bool is_negation_chain(const ast &chain){ if(is_true(chain)) return false; ast last = chain_last(chain); ast rest = chain_rest(chain); if(is_true(rest)) return op(rewrite_rhs(last)) == Not; return is_negation_chain(rest); } ast commute_negation_chain(const ast &chain){ if(is_true(chain)) return chain; ast last = chain_last(chain); ast rest = chain_rest(chain); if(is_true(rest)){ ast old = rewrite_rhs(last); if(!(op(old) == Not)) throw "bad negative equality chain"; ast equ = arg(old,0); if(!is_equivrel(equ)) throw "bad negative equality chain"; last = rewrite_update_rhs(last,top_pos,make(Not,make(op(equ),arg(equ,1),arg(equ,0))),make(True)); return chain_cons(rest,last); } ast pos = rewrite_pos(last); if(pos == top_pos) throw "bad negative equality chain"; int idx = pos_arg(pos); if(idx != 0) throw "bad negative equality chain"; pos = arg(pos,1); if(pos == top_pos){ ast lhs = rewrite_lhs(last); ast rhs = rewrite_rhs(last); if(op(lhs) != Equal || op(rhs) != Equal) throw "bad negative equality chain"; last = make_rewrite(rewrite_side(last),rewrite_pos(last),rewrite_cond(last), make(Iff,make(Equal,arg(lhs,1),arg(lhs,0)),make(Equal,arg(rhs,1),arg(rhs,0)))); } else { idx = pos_arg(pos); if(idx == 0) idx = 1; else if(idx == 1) idx = 0; else throw "bad negative equality chain"; pos = pos_add(0,pos_add(idx,arg(pos,1))); last = make_rewrite(rewrite_side(last),pos,rewrite_cond(last),rewrite_equ(last)); } return chain_cons(commute_negation_chain(rest),last); } // split a rewrite chain into head and tail at last top-level rewrite ast get_head_chain(const ast &chain, ast &tail, bool is_not = true){ ast last = chain_last(chain); ast rest = chain_rest(chain); ast pos = rewrite_pos(last); if(pos == top_pos || (is_not && arg(pos,1) == top_pos)){ tail = mk_true(); return chain; } if(is_true(rest)) throw "bad rewrite chain"; ast head = get_head_chain(rest,tail,is_not); tail = chain_cons(tail,last); return head; } bool has_mixed_summands(const ast &e){ if(op(e) == Plus){ int nargs = num_args(e); for(int i = 0; i < nargs; i++) if(has_mixed_summands(arg(e,i))) return true; return false; } return get_term_type(e) == LitMixed; } // split a rewrite chain into head and tail at last sum with no mixed sumands ast get_right_movers(const ast &chain, const ast &rhs, ast &tail, ast &mid){ if(is_true(chain) || !has_mixed_summands(rhs)){ mid = rhs; tail = mk_true(); return chain; } ast last = chain_last(chain); ast rest = chain_rest(chain); ast mm = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); ast res = get_right_movers(rest,mm,tail,mid); tail = chain_cons(tail,last); return res; } // split a rewrite chain into head and tail at first sum with no mixed sumands ast get_left_movers(const ast &chain, const ast &lhs, ast &tail, ast &mid){ if(is_true(chain)){ mid = lhs; if(!has_mixed_summands(lhs)){ tail = mk_true(); return chain; } return ast(); } ast last = chain_last(chain); ast rest = chain_rest(chain); ast res = get_left_movers(rest,lhs,tail,mid); if(res.null()){ mid = subst_in_pos(mid,rewrite_pos(last),rewrite_rhs(last)); if(get_term_type(mid) != LitMixed){ tail = mk_true(); return chain; } return ast(); } tail = chain_cons(tail,last); return res; } struct cannot_split {}; /** Split a chain of rewrites two chains, operating on positions 0 and 1. Fail if any rewrite in the chain operates on top position. */ void split_chain_rec(const ast &chain, ast *res){ if(is_true(chain)) return; ast last = chain_last(chain); ast rest = chain_rest(chain); split_chain_rec(rest,res); ast pos = rewrite_pos(last); if(pos == top_pos){ if(rewrite_lhs(last) == rewrite_rhs(last)) return; // skip if it's a noop throw cannot_split(); } int arg = pos_arg(pos); if(arg<0 || arg > 1) throw cannot_split(); res[arg] = chain_cons(res[arg],rewrite_up(last)); } void split_chain(const ast &chain, ast *res){ res[0] = res[1] = mk_true(); split_chain_rec(chain,res); } ast extract_rewrites(const ast &chain, const ast &pos){ if(is_true(chain)) return chain; ast last = chain_last(chain); ast rest = chain_rest(chain); ast new_rest = extract_rewrites(rest,pos); ast p1 = rewrite_pos(last); ast diff; switch(pos_diff(p1,pos,diff)){ case -1: { ast new_last = rewrite_pos_set(diff, last); return chain_cons(new_rest,new_last); } case 1: if(rewrite_lhs(last) != rewrite_rhs(last)) throw "bad rewrite chain"; break; default:; } return new_rest; } ast down_chain(const ast &chain){ ast split[2]; split_chain(chain,split); return split[0]; } ast chain_conditions(LitType t, const ast &chain){ if(is_true(chain)) return mk_true(); ast last = chain_last(chain); ast rest = chain_rest(chain); ast cond = chain_conditions(t,rest); if(is_rewrite_side(t,last)) cond = my_and(cond,rewrite_cond(last)); return cond; } ast chain_formulas(LitType t, const ast &chain){ if(is_true(chain)) return mk_true(); ast last = chain_last(chain); ast rest = chain_rest(chain); ast cond = chain_formulas(t,rest); if(is_rewrite_side(t,last)) cond = my_and(cond,rewrite_equ(last)); return cond; } bool rewrites_from_to(const ast &chain, const ast &lhs, const ast &rhs){ if(is_true(chain)) return lhs == rhs; ast last = chain_last(chain); ast rest = chain_rest(chain); ast mid = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); return rewrites_from_to(rest,lhs,mid); } struct bad_ineq_inference {}; ast chain_ineqs(opr comp_op, LitType t, const ast &chain, const ast &lhs, const ast &rhs){ if(is_true(chain)){ if(lhs != rhs) throw bad_ineq_inference(); return make(Leq,make_int(rational(0)),make_int(rational(0))); } ast last = chain_last(chain); ast rest = chain_rest(chain); ast mid = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); ast cond = chain_ineqs(comp_op,t,rest,lhs,mid); if(is_rewrite_side(t,last)){ ast diff; if(comp_op == Leq) diff = make(Sub,rhs,mid); else diff = make(Sub,mid,rhs); ast foo = make(Leq,make_int("0"),z3_simplify(diff)); if(is_true(cond)) cond = foo; else { linear_comb(cond,make_int(rational(1)),foo); cond = simplify_ineq(cond); } } return cond; } ast ineq_to_lhs(const ast &ineq){ ast s = make(Leq,make_int(rational(0)),make_int(rational(0))); linear_comb(s,make_int(rational(1)),ineq); return simplify_ineq(s); } void eq_from_ineq(const ast &ineq, ast &lhs, ast &rhs){ // ast s = ineq_to_lhs(ineq); // ast srhs = arg(s,1); ast srhs = arg(ineq,0); if(op(srhs) == Plus && num_args(srhs) == 2 && arg(ineq,1) == make_int(rational(0))){ lhs = arg(srhs,0); rhs = arg(srhs,1); // if(op(lhs) == Times) // std::swap(lhs,rhs); if(op(rhs) == Times){ rhs = arg(rhs,1); // if(op(ineq) == Leq) // std::swap(lhs,rhs); return; } } if(op(ineq) == Leq || op(ineq) == Geq){ lhs = srhs; rhs = arg(ineq,1); return; } throw "bad ineq"; } ast chain_pos_add(int arg, const ast &chain){ if(is_true(chain)) return mk_true(); ast last = rewrite_pos_add(arg,chain_last(chain)); ast rest = chain_pos_add(arg,chain_rest(chain)); return chain_cons(rest,last); } ast apply_rewrite_chain(const ast &t, const ast &chain){ if(is_true(chain)) return t; ast last = chain_last(chain); ast rest = chain_rest(chain); ast mid = apply_rewrite_chain(t,rest); ast res = subst_in_pos(mid,rewrite_pos(last),rewrite_rhs(last)); return res; } ast drop_rewrites(LitType t, const ast &chain, ast &remainder){ if(!is_true(chain)){ ast last = chain_last(chain); ast rest = chain_rest(chain); if(is_rewrite_side(t,last)){ ast res = drop_rewrites(t,rest,remainder); remainder = chain_cons(remainder,last); return res; } } remainder = mk_true(); return chain; } // Normalization chains ast cons_normal(const ast &first, const ast &rest){ return make(normal_chain,first,rest); } ast normal_first(const ast &t){ return arg(t,0); } ast normal_rest(const ast &t){ return arg(t,1); } ast normal_lhs(const ast &t){ return arg(arg(t,0),0); } ast normal_rhs(const ast &t){ return arg(arg(t,0),1); } ast normal_proof(const ast &t){ return arg(t,1); } ast make_normal_step(const ast &lhs, const ast &rhs, const ast &proof){ return make(normal_step,make_equiv(lhs,rhs),proof); } ast make_normal(const ast &ineq, const ast &nrml){ if(!is_ineq(ineq)) throw "what?"; return make(normal,ineq,nrml); } ast fix_normal(const ast &lhs, const ast &rhs, const ast &proof){ LitType lhst = get_term_type(lhs); LitType rhst = get_term_type(rhs); if(lhst == LitMixed && (rhst != LitMixed || ast_id(lhs) < ast_id(rhs))) return make_normal_step(lhs,rhs,proof); if(rhst == LitMixed && (lhst != LitMixed || ast_id(rhs) < ast_id(lhs))) return make_normal_step(rhs,lhs,reverse_chain(proof)); throw "help!"; } ast chain_side_proves(LitType side, const ast &chain){ LitType other_side = side == LitA ? LitB : LitA; return my_and(chain_conditions(other_side,chain),my_implies(chain_conditions(side,chain),chain_formulas(side,chain))); } // Merge two normalization chains ast merge_normal_chains_rec(const ast &chain1, const ast &chain2, hash_map &trans, ast &Aproves, ast &Bproves){ if(is_true(chain1)) return chain2; if(is_true(chain2)) return chain1; ast f1 = normal_first(chain1); ast f2 = normal_first(chain2); ast lhs1 = normal_lhs(f1); ast lhs2 = normal_lhs(f2); int id1 = ast_id(lhs1); int id2 = ast_id(lhs2); if(id1 < id2) return cons_normal(f1,merge_normal_chains_rec(normal_rest(chain1),chain2,trans,Aproves,Bproves)); if(id2 < id1) return cons_normal(f2,merge_normal_chains_rec(chain1,normal_rest(chain2),trans,Aproves,Bproves)); ast rhs1 = normal_rhs(f1); ast rhs2 = normal_rhs(f2); LitType t1 = get_term_type(rhs1); LitType t2 = get_term_type(rhs2); int tid1 = ast_id(rhs1); int tid2 = ast_id(rhs2); ast pf1 = normal_proof(f1); ast pf2 = normal_proof(f2); ast new_normal; if(t1 == LitMixed && (t2 != LitMixed || tid2 > tid1)){ ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); new_normal = f2; trans[rhs1] = make_normal_step(rhs1,rhs2,new_proof); } else if(t2 == LitMixed && (t1 != LitMixed || tid1 > tid2)) return merge_normal_chains_rec(chain2,chain1,trans,Aproves,Bproves); else if(t1 == LitA && t2 == LitB){ ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); ast Bproof, Aproof = drop_rewrites(LitB,new_proof,Bproof); ast mcA = chain_side_proves(LitB,Aproof); Bproves = my_and(Bproves,mcA); ast mcB = chain_side_proves(LitA,Bproof); Aproves = my_and(Aproves,mcB); ast rep = apply_rewrite_chain(rhs1,Aproof); new_proof = concat_rewrite_chain(pf1,Aproof); new_normal = make_normal_step(lhs1,rep,new_proof); ast A_normal = make_normal_step(rhs1,rep,Aproof); ast res = cons_normal(new_normal,merge_normal_chains_rec(normal_rest(chain1),normal_rest(chain2),trans,Aproves,Bproves)); res = merge_normal_chains_rec(res,cons_normal(A_normal,make(True)),trans,Aproves,Bproves); return res; } else if(t1 == LitB && t2 == LitA) return merge_normal_chains_rec(chain2,chain1,trans,Aproves,Bproves); else if(t1 == LitA) { ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); ast mc = chain_side_proves(LitB,new_proof); Bproves = my_and(Bproves,mc); new_normal = f1; // choice is arbitrary } else { /* t1 = t2 = LitB */ ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); ast mc = chain_side_proves(LitA,new_proof); Aproves = my_and(Aproves,mc); new_normal = f1; // choice is arbitrary } return cons_normal(new_normal,merge_normal_chains_rec(normal_rest(chain1),normal_rest(chain2),trans,Aproves,Bproves)); } ast trans_normal_chain(const ast &chain, hash_map &trans){ if(is_true(chain)) return chain; ast f = normal_first(chain); ast r = normal_rest(chain); r = trans_normal_chain(r,trans); ast rhs = normal_rhs(f); hash_map::iterator it = trans.find(rhs); ast new_normal; if(it != trans.end() && get_term_type(normal_lhs(f)) == LitMixed){ const ast &f2 = it->second; ast pf = concat_rewrite_chain(normal_proof(f),normal_proof(f2)); new_normal = make_normal_step(normal_lhs(f),normal_rhs(f2),pf); } else new_normal = f; if(get_term_type(normal_lhs(f)) == LitMixed) trans[normal_lhs(f)] = new_normal; return cons_normal(new_normal,r); } ast merge_normal_chains(const ast &chain1, const ast &chain2, ast &Aproves, ast &Bproves){ hash_map trans; ast res = merge_normal_chains_rec(chain1,chain2,trans,Aproves,Bproves); res = trans_normal_chain(res,trans); return res; } bool destruct_cond_ineq(ast t, ast &Aproves, ast &Bproves, ast&ineq){ if(op(t) == And){ Aproves = arg(t,0); t = arg(t,1); } else Aproves = mk_true(); if(op(t) == Implies){ Bproves = arg(t,0); t = arg(t,1); } else Bproves = mk_true(); if(is_normal_ineq(t)){ ineq = t; return true; } return false; } ast cons_cond_ineq(const ast &Aproves, const ast &Bproves, const ast &ineq){ return my_and(Aproves,my_implies(Bproves,ineq)); } ast normalize(const ast &ct){ ast Aproves,Bproves,t; if(!destruct_cond_ineq(ct,Aproves,Bproves,t)) return ct; if(sym(t) != normal) return ct; ast chain = arg(t,1); hash_map map; for(ast c = chain; !is_true(c); c = normal_rest(c)){ ast first = normal_first(c); ast lhs = normal_lhs(first); ast rhs = normal_rhs(first); map[lhs] = rhs; } ast res = subst(map,arg(t,0)); return cons_cond_ineq(Aproves,Bproves,res); } /** Make an assumption node. The given clause is assumed in the given frame. */ virtual node make_assumption(int frame, const std::vector &assumption){ if(!weak){ if(pv->in_range(frame,rng)){ std::vector itp_clause; for(unsigned i = 0; i < assumption.size(); i++) if(get_term_type(assumption[i]) != LitA) itp_clause.push_back(assumption[i]); ast res = my_or(itp_clause); return res; } else { return mk_true(); } } else { if(pv->in_range(frame,rng)){ return mk_false(); } else { std::vector itp_clause; for(unsigned i = 0; i < assumption.size(); i++) if(get_term_type(assumption[i]) != LitB) itp_clause.push_back(assumption[i]); ast res = my_or(itp_clause); return mk_not(res); } } } ast make_local_rewrite(LitType t, const ast &p){ ast rew = is_equivrel(p) ? p : make(Iff,mk_true(),p); #if 0 if(op(rew) == Iff && !is_true(arg(rew,0))) return diff_chain(t,top_pos,arg(rew,0),arg(rew,1), mk_true()); #endif return chain_cons(mk_true(),make_rewrite(t, top_pos, mk_true(), rew)); } ast triv_interp(const symb &rule, const std::vector &premises, int mask_in){ std::vector ps; ps.resize(premises.size()); std::vector conjs; int mask = 0; for(unsigned i = 0; i < ps.size(); i++){ ast p = premises[i]; LitType t = get_term_type(p); switch(t){ case LitA: case LitB: ps[i] = make_local_rewrite(t,p); break; default: ps[i] = get_placeholder(p); // can only prove consequent! if(mask_in & (1 << i)) mask |= (1 << conjs.size()); conjs.push_back(p); } } ast ref = make(rule,ps); ast res = make_contra_node(ref,conjs,mask); return res; } ast triv_interp(const symb &rule, const ast &p0, const ast &p1, const ast &p2, int mask){ std::vector ps; ps.resize(3); ps[0] = p0; ps[1] = p1; ps[2] = p2; return triv_interp(rule,ps,mask); } /** Make a modus-ponens node. This takes derivations of |- x and |- x = y and produces |- y */ virtual node make_mp(const ast &p_eq_q, const ast &prem1, const ast &prem2){ /* Interpolate the axiom p, p=q -> q */ ast p = arg(p_eq_q,0); ast q = arg(p_eq_q,1); ast itp; if(get_term_type(p_eq_q) == LitMixed){ int mask = 1 << 2; if(op(p) == Not && is_equivrel(arg(p,0))) mask |= 1; // we may need to run this rule backward if first premise is negative equality itp = triv_interp(modpon,p,p_eq_q,mk_not(q),mask); } else { if(get_term_type(p) == LitA){ if(get_term_type(q) == LitA){ if(op(q) == Or) itp = make_assumption(rng.hi,args(q)); else itp = mk_false(); } else { if(get_term_type(p_eq_q) == LitA) itp = q; else throw proof_error(); } } else { if(get_term_type(q) == LitA){ if(get_term_type(make(Equal,p,q)) == LitA) itp = mk_not(p); else throw proof_error(); } else itp = mk_true(); } } /* Resolve it with the premises */ std::vector conc; conc.push_back(q); conc.push_back(mk_not(p_eq_q)); itp = make_resolution(p,conc,itp,prem1); conc.pop_back(); itp = make_resolution(p_eq_q,conc,itp,prem2); return itp; } ast capture_localization(ast e){ // #define CAPTURE_LOCALIZATION #ifdef CAPTURE_LOCALIZATION for(int i = localization_vars.size() - 1; i >= 0; i--){ LocVar &lv = localization_vars[i]; if(occurs_in(lv.var,e)){ symb q = (pv->in_range(lv.frame,rng)) ? sexists : sforall; e = make(q,make(Equal,lv.var,lv.term),e); // use Equal because it is polymorphic } } #endif return e; } /** Make an axiom node. The conclusion must be an instance of an axiom. */ virtual node make_axiom(const std::vector &conclusion, prover::range frng){ int nargs = conclusion.size(); std::vector largs(nargs); std::vector eqs; std::vector pfs; for(int i = 0; i < nargs; i++){ ast argpf; ast lit = conclusion[i]; largs[i] = localize_term(lit,frng,argpf); frng = pv->range_glb(frng,pv->ast_scope(largs[i])); if(largs[i] != lit){ eqs.push_back(make_equiv(largs[i],lit)); pfs.push_back(argpf); } } int frame = pv->range_max(frng); ast itp = make_assumption(frame,largs); for(unsigned i = 0; i < eqs.size(); i++) itp = make_mp(eqs[i],itp,pfs[i]); return capture_localization(itp); } virtual node make_axiom(const std::vector &conclusion){ return make_axiom(conclusion,pv->range_full()); } /** Make a Contra node. This rule takes a derivation of the form Gamma |- False and produces |- \/~Gamma. */ virtual node make_contra(node prem, const std::vector &conclusion){ return prem; } /** Make hypothesis. Creates a node of the form P |- P. */ virtual node make_hypothesis(const ast &P){ if(is_not(P)) return make_hypothesis(arg(P,0)); switch(get_term_type(P)){ case LitA: return mk_false(); case LitB: return mk_true(); default: // mixed hypothesis switch(op(P)){ case Geq: case Leq: case Gt: case Lt: { ast zleqz = make(Leq,make_int("0"),make_int("0")); ast fark1 = make(sum,zleqz,make_int("1"),get_placeholder(P)); ast fark2 = make(sum,fark1,make_int("1"),get_placeholder(mk_not(P))); ast res = make(And,make(contra,fark2,mk_false()), make(contra,get_placeholder(mk_not(P)),P), make(contra,get_placeholder(P),mk_not(P))); return res; } default: { ast em = make(exmid,P,get_placeholder(P),get_placeholder(mk_not(P))); ast res = make(And,make(contra,em,mk_false()), make(contra,get_placeholder(mk_not(P)),P), make(contra,get_placeholder(P),mk_not(P))); return res; } } } } /** Make a Reflexivity node. This rule produces |- x = x */ virtual node make_reflexivity(ast con){ if(get_term_type(con) == LitA) return mk_false(); if(get_term_type(con) == LitB) return mk_true(); ast itp = make(And,make(contra,no_proof,mk_false()), make(contra,mk_true(),mk_not(con))); return itp; } /** Make a Symmetry node. This takes a derivation of |- x = y and produces | y = x. Ditto for ~(x=y) */ virtual node make_symmetry(ast con, const ast &premcon, node prem){ #if 0 ast x = arg(con,0); ast y = arg(con,1); ast p = make(op(con),y,x); #endif if(get_term_type(con) != LitMixed) return prem; // symmetry shmymmetry... ast em = make(exmid,con,make(symm,get_placeholder(premcon)),get_placeholder(mk_not(con))); ast itp = make(And,make(contra,em,mk_false()), make(contra,make(symm,get_placeholder(mk_not(con))),premcon), make(contra,make(symm,get_placeholder(premcon)),mk_not(con))); std::vector conc; conc.push_back(con); itp = make_resolution(premcon,conc,itp,prem); return itp; } ast make_equiv_rel(const ast &x, const ast &y){ if(is_bool_type(get_type(x))) return make(Iff,x,y); return make(Equal,x,y); } /** Make a transitivity node. This takes derivations of |- x = y and |- y = z produces | x = z */ virtual node make_transitivity(const ast &x, const ast &y, const ast &z, node prem1, node prem2){ /* Interpolate the axiom x=y,y=z,-> x=z */ ast p = make_equiv_rel(x,y); ast q = make_equiv_rel(y,z); ast r = make_equiv_rel(x,z); ast equiv = make(Iff,p,r); ast itp; itp = make_congruence(q,equiv,prem2); itp = make_mp(equiv,prem1,itp); return itp; } /** Make a congruence node. This takes derivations of |- x_i = y_i and produces |- f(x_1,...,x_n) = f(y_1,...,y_n) */ virtual node make_congruence(const ast &p, const ast &con, const ast &prem1){ ast x = arg(p,0), y = arg(p,1); ast itp; LitType con_t = get_term_type(con); if(get_term_type(p) == LitA){ if(con_t == LitA) itp = mk_false(); else if(con_t == LitB) itp = p; else itp = make_mixed_congruence(x, y, p, con, prem1); } else { if(con_t == LitA) itp = mk_not(p); else{ if(con_t == LitB) itp = mk_true(); else itp = make_mixed_congruence(x, y, p, con, prem1); } } std::vector conc; conc.push_back(con); itp = make_resolution(p,conc,itp,prem1); return itp; } int find_congruence_position(const ast &p, const ast &con){ // find the argument position of x and y const ast &x = arg(p,0); const ast &y = arg(p,1); int nargs = num_args(arg(con,0)); for(int i = 0; i < nargs; i++) if(x == arg(arg(con,0),i) && y == arg(arg(con,1),i)) return i; throw proof_error(); } /** Make a congruence node. This takes derivations of |- x_i1 = y_i1, |- x_i2 = y_i2,... and produces |- f(...x_i1...x_i2...) = f(...y_i1...y_i2...) */ node make_congruence(const std::vector &p, const ast &con, const std::vector &prems){ if(p.size() == 0) throw proof_error(); if(p.size() == 1) return make_congruence(p[0],con,prems[0]); ast thing = con; ast res = mk_true(); for(unsigned i = 0; i < p.size(); i++){ int pos = find_congruence_position(p[i],thing); ast next = subst_in_arg_pos(pos,arg(p[i],1),arg(thing,0)); ast goal = make(op(thing),arg(thing,0),next); ast equa = make_congruence(p[i],goal,prems[i]); if(i == 0) res = equa; else { ast trace = make(op(con),arg(con,0),arg(thing,0)); ast equiv = make(Iff,trace,make(op(trace),arg(trace,0),next)); ast foo = make_congruence(goal,equiv,equa); res = make_mp(equiv,res,foo); } thing = make(op(thing),next,arg(thing,1)); } return res; } /* Interpolate a mixed congruence axiom. */ virtual ast make_mixed_congruence(const ast &x, const ast &y, const ast &p, const ast &con, const ast &prem1){ ast foo = p; std::vector conjs; LitType t = get_term_type(foo); switch(t){ case LitA: case LitB: foo = make_local_rewrite(t,foo); break; case LitMixed: conjs.push_back(foo); foo = get_placeholder(foo); } // find the argument position of x and y int pos = -1; int nargs = num_args(arg(con,0)); for(int i = 0; i < nargs; i++) if(x == arg(arg(con,0),i) && y == arg(arg(con,1),i)) pos = i; if(pos == -1) throw proof_error(); ast bar = make(cong,foo,make_int(rational(pos)),get_placeholder(mk_not(con))); conjs.push_back(mk_not(con)); return make_contra_node(bar,conjs); } ast subst_in_arg_pos(int pos, ast term, ast app){ std::vector args; get_args(app,args); args[pos] = term; return clone(app,args); } /** Make a farkas proof node. */ virtual node make_farkas(ast con, const std::vector &prems, const std::vector &prem_cons, const std::vector &coeffs){ /* Compute the interpolant for the clause */ ast zero = make_int("0"); std::vector conjs; ast thing = make(Leq,zero,zero); for(unsigned i = 0; i < prem_cons.size(); i++){ const ast &lit = prem_cons[i]; if(get_term_type(lit) == LitA) // Farkas rule seems to assume strict integer inequalities are rounded linear_comb(thing,coeffs[i],lit,true /*round_off*/); } thing = simplify_ineq(thing); for(unsigned i = 0; i < prem_cons.size(); i++){ const ast &lit = prem_cons[i]; if(get_term_type(lit) == LitMixed){ thing = make(sum,thing,coeffs[i],get_placeholder(lit)); conjs.push_back(lit); } } thing = make_contra_node(thing,conjs); /* Resolve it with the premises */ std::vector conc; conc.resize(prem_cons.size()); for(unsigned i = 0; i < prem_cons.size(); i++) conc[prem_cons.size()-i-1] = prem_cons[i]; for(unsigned i = 0; i < prem_cons.size(); i++){ thing = make_resolution(prem_cons[i],conc,thing,prems[i]); conc.pop_back(); } return thing; } /** Set P to P + cQ, where P and Q are linear inequalities. Assumes P is 0 <= y or 0 < y. */ void linear_comb(ast &P, const ast &c, const ast &Q, bool round_off = false){ ast Qrhs; bool qstrict = false; if(is_not(Q)){ ast nQ = arg(Q,0); switch(op(nQ)){ case Gt: Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); break; case Lt: Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); break; case Geq: Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); qstrict = true; break; case Leq: Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); qstrict = true; break; default: throw proof_error(); } } else { switch(op(Q)){ case Leq: Qrhs = make(Sub,arg(Q,1),arg(Q,0)); break; case Geq: Qrhs = make(Sub,arg(Q,0),arg(Q,1)); break; case Lt: Qrhs = make(Sub,arg(Q,1),arg(Q,0)); qstrict = true; break; case Gt: Qrhs = make(Sub,arg(Q,0),arg(Q,1)); qstrict = true; break; default: throw proof_error(); } } #if 0 bool pstrict = op(P) == Lt, strict = pstrict || qstrict; if(pstrict && qstrict && round_off) Qrhs = make(Sub,Qrhs,make_int(rational(1))); #else bool pstrict = op(P) == Lt; if(qstrict && round_off && (pstrict || !(c == make_int(rational(1))))){ Qrhs = make(Sub,Qrhs,make_int(rational(1))); qstrict = false; } Qrhs = make(Times,c,Qrhs); bool strict = pstrict || qstrict; #endif if(strict) P = make(Lt,arg(P,0),make(Plus,arg(P,1),Qrhs)); else P = make(Leq,arg(P,0),make(Plus,arg(P,1),Qrhs)); } /* Make an axiom instance of the form |- x<=y, y<= x -> x =y */ virtual node make_leq2eq(ast x, ast y, const ast &xleqy, const ast &yleqx){ ast con = make(Equal,x,y); ast itp; switch(get_term_type(con)){ case LitA: itp = mk_false(); break; case LitB: itp = mk_true(); break; default: { // mixed equality if(get_term_type(x) == LitMixed || get_term_type(y) == LitMixed){ if(y == make_int(rational(0)) && op(x) == Plus && num_args(x) == 2){ // std::cerr << "WARNING: untested case in leq2eq\n"; } else { // std::cerr << "WARNING: mixed term in leq2eq\n"; std::vector lits; lits.push_back(con); lits.push_back(make(Not,xleqy)); lits.push_back(make(Not,yleqx)); return make_axiom(lits); } } std::vector conjs; conjs.resize(3); conjs[0] = mk_not(con); conjs[1] = xleqy; conjs[2] = yleqx; itp = make_contra_node(make(leq2eq, get_placeholder(mk_not(con)), get_placeholder(xleqy), get_placeholder(yleqx)), conjs,1); } } return itp; } /* Make an axiom instance of the form |- x = y -> x <= y */ virtual node make_eq2leq(ast x, ast y, const ast &xleqy){ ast itp; switch(get_term_type(xleqy)){ case LitA: itp = mk_false(); break; case LitB: itp = mk_true(); break; default: { // mixed equality std::vector conjs; conjs.resize(2); conjs[0] = make(Equal,x,y); conjs[1] = mk_not(xleqy); itp = make(eq2leq,get_placeholder(conjs[0]),get_placeholder(conjs[1])); itp = make_contra_node(itp,conjs,2); } } return itp; } /* Make an inference of the form t <= c |- t/d <= floor(c/d) where t is an affine term divisble by d and c is an integer constant */ virtual node make_cut_rule(const ast &tleqc, const ast &d, const ast &con, const ast &prem){ ast itp = mk_false(); switch(get_term_type(con)){ case LitA: itp = mk_false(); break; case LitB: itp = mk_true(); break; default: { std::vector conjs; conjs.resize(2); conjs[0] = tleqc; conjs[1] = mk_not(con); itp = make(sum,get_placeholder(conjs[0]),d,get_placeholder(conjs[1])); itp = make_contra_node(itp,conjs); } } std::vector conc; conc.push_back(con); itp = make_resolution(tleqc,conc,itp,prem); return itp; } // create a fresh variable for localization ast fresh_localization_var(const ast &term, int frame){ std::ostringstream s; s << "%" << (localization_vars.size()); ast var = make_var(s.str().c_str(),get_type(term)); pv->sym_range(sym(var)) = pv->range_full(); // make this variable global localization_vars.push_back(LocVar(var,term,frame)); return var; } struct LocVar { // localization vars ast var; // a fresh variable ast term; // term it represents int frame; // frame in which it's defined LocVar(ast v, ast t, int f){var=v;term=t;frame=f;} }; std::vector localization_vars; // localization vars in order of creation struct locmaps { hash_map localization_map; // maps terms to their localization vars hash_map localization_pf_map; // maps terms to proofs of their localizations }; hash_map localization_maps_per_range; /* "localize" a term e to a given frame range, creating new symbols to represent non-local subterms. This returns the localized version e_l, as well as a proof thet e_l = l. */ ast make_refl(const ast &e){ if(get_term_type(e) == LitA) return mk_false(); return mk_true(); // TODO: is this right? } ast make_equiv(const ast &x, const ast &y){ if(get_type(x) == bool_type()) return make(Iff,x,y); else return make(Equal,x,y); } bool range_is_global(const prover::range &r){ if(pv->range_contained(r,rng)) return false; if(!pv->ranges_intersect(r,rng)) return false; return true; } ast localize_term(ast e, const prover::range &rng, ast &pf){ // we need to memoize this function separately for A, B and global prover::range map_range = rng; if(range_is_global(map_range)) map_range = pv->range_full(); locmaps &maps = localization_maps_per_range[map_range]; hash_map &localization_map = maps.localization_map; hash_map &localization_pf_map = maps.localization_pf_map; ast orig_e = e; pf = make_refl(e); // proof that e = e // prover::range erng = pv->ast_scope(e); #if 0 if(!(erng.lo > erng.hi) && pv->ranges_intersect(pv->ast_scope(e),rng)){ return e; // this term occurs in range, so it's O.K. } #endif hash_map::iterator it = localization_map.find(e); if(it != localization_map.end() && is_bool_type(get_type(e)) && !pv->ranges_intersect(pv->ast_scope(it->second),rng)) it = localization_map.end(); // prevent quantifiers over booleans if(it != localization_map.end()){ pf = localization_pf_map[e]; e = it->second; } else { // if it is non-local, we must first localize the arguments to // the range of its function symbol int nargs = num_args(e); if(nargs > 0 /* && (!is_local(e) || flo <= hi || fhi >= lo) */){ prover::range frng = rng; opr o = op(e); if(o == Uninterpreted){ symb f = sym(e); prover::range srng = pv->sym_range(f); if(pv->ranges_intersect(srng,rng)) // localize to desired range if possible frng = pv->range_glb(srng,rng); else frng = srng; // this term will be localized } else if(o == Plus || o == Times){ // don't want bound variables inside arith ops // std::cout << "WARNING: non-local arithmetic\n"; // frng = erng; // this term will be localized } else if(o == Select){ // treat the array term like a function symbol prover::range srng = pv->ast_scope(arg(e,0)); if(!(srng.lo > srng.hi) && pv->ranges_intersect(srng,rng)) // localize to desired range if possible frng = pv->range_glb(srng,rng); else frng = srng; // this term will be localized } std::vector largs(nargs); std::vector eqs; std::vector pfs; for(int i = 0; i < nargs; i++){ ast argpf; largs[i] = localize_term(arg(e,i),frng,argpf); frng = pv->range_glb(frng,pv->ast_scope(largs[i])); if(largs[i] != arg(e,i)){ eqs.push_back(make_equiv(largs[i],arg(e,i))); pfs.push_back(argpf); } } e = clone(e,largs); if(pfs.size()) pf = make_congruence(eqs,make_equiv(e,orig_e),pfs); // assert(is_local(e)); } localization_pf_map[orig_e] = pf; localization_map[orig_e] = e; } if(pv->ranges_intersect(pv->ast_scope(e),rng)) return e; // this term occurs in range, so it's O.K. if(is_array_type(get_type(e))) std::cerr << "WARNING: array quantifier\n"; // choose a frame for the constraint that is close to range int frame = pv->range_near(pv->ast_scope(e),rng); ast new_var = fresh_localization_var(e,frame); localization_map[orig_e] = new_var; std::vector foo; foo.push_back(make_equiv(new_var,e)); ast bar = make_assumption(frame,foo); pf = make_transitivity(new_var,e,orig_e,bar,pf); localization_pf_map[orig_e] = pf; // HACK: try to bias this term in the future if(!pv->range_is_full(rng)){ prover::range rf = pv->range_full(); locmaps &fmaps = localization_maps_per_range[rf]; hash_map &flocalization_map = fmaps.localization_map; hash_map &flocalization_pf_map = fmaps.localization_pf_map; // if(flocalization_map.find(orig_e) == flocalization_map.end()) { flocalization_map[orig_e] = new_var; flocalization_pf_map[orig_e] = pf; } } return new_var; } ast delete_quant(hash_map &memo, const ast &v, const ast &e){ std::pair foo(e,ast()); std::pair::iterator,bool> bar = memo.insert(foo); ast &res = bar.first->second; if(bar.second){ opr o = op(e); switch(o){ case Or: case And: case Implies: { unsigned nargs = num_args(e); std::vector args; args.resize(nargs); for(unsigned i = 0; i < nargs; i++) args[i] = delete_quant(memo, v, arg(e,i)); res = make(o,args); break; } case Uninterpreted: { symb s = sym(e); ast w = arg(arg(e,0),0); if(s == sforall || s == sexists){ res = delete_quant(memo,v,arg(e,1)); if(w != v) res = make(s,w,res); break; } } default: res = e; } } return res; } ast insert_quants(hash_map &memo, const ast &e){ std::pair foo(e,ast()); std::pair::iterator,bool> bar = memo.insert(foo); ast &res = bar.first->second; if(bar.second){ opr o = op(e); switch(o){ case Or: case And: case Implies: { unsigned nargs = num_args(e); std::vector args; args.resize(nargs); for(unsigned i = 0; i < nargs; i++) args[i] = insert_quants(memo, arg(e,i)); res = make(o,args); break; } case Uninterpreted: { symb s = sym(e); if(s == sforall || s == sexists){ opr q = (s == sforall) ? Forall : Exists; ast v = arg(arg(e,0),0); hash_map dmemo; ast body = delete_quant(dmemo,v,arg(e,1)); body = insert_quants(memo,body); res = apply_quant(q,v,body); break; } } default: res = e; } } return res; } ast add_quants(ast e){ #ifdef CAPTURE_LOCALIZATION if(!localization_vars.empty()){ hash_map memo; e = insert_quants(memo,e); } #else for(int i = localization_vars.size() - 1; i >= 0; i--){ LocVar &lv = localization_vars[i]; opr quantifier = (pv->in_range(lv.frame,rng)) ? Exists : Forall; e = apply_quant(quantifier,lv.var,e); } #endif return e; } node make_resolution(ast pivot, node premise1, node premise2) { std::vector lits; return make_resolution(pivot,lits,premise1,premise2); } /* Return an interpolant from a proof of false */ ast interpolate(const node &pf){ // proof of false must be a formula, with quantified symbols #ifndef BOGUS_QUANTS return add_quants(z3_simplify(pf)); #else return z3_simplify(pf); #endif } ast resolve_with_quantifier(const ast &pivot1, const ast &conj1, const ast &pivot2, const ast &conj2){ if(is_not(arg(pivot1,1))) return resolve_with_quantifier(pivot2,conj2,pivot1,conj1); ast eqpf; ast P = arg(pivot1,1); ast Ploc = localize_term(P, rng, eqpf); ast pPloc = make_hypothesis(Ploc); ast pP = make_mp(make(Iff,Ploc,P),pPloc,eqpf); ast rP = make_resolution(P,conj1,pP); ast nP = mk_not(P); ast nPloc = mk_not(Ploc); ast neqpf = make_congruence(make(Iff,Ploc,P),make(Iff,nPloc,nP),eqpf); ast npPloc = make_hypothesis(nPloc); ast npP = make_mp(make(Iff,nPloc,nP),npPloc,neqpf); ast nrP = make_resolution(nP,conj2,npP); ast res = make_resolution(Ploc,rP,nrP); return capture_localization(res); } ast get_contra_coeff(const ast &f){ ast c = arg(f,0); // if(!is_not(arg(f,1))) // c = make(Uminus,c); return c; } ast my_or(const ast &a, const ast &b){ return mk_or(a,b); } ast my_and(const ast &a, const ast &b){ return mk_and(a,b); } ast my_implies(const ast &a, const ast &b){ return mk_implies(a,b); } ast my_or(const std::vector &a){ return mk_or(a); } ast my_and(const std::vector &a){ return mk_and(a); } ast get_lit_atom(const ast &l){ if(op(l) == Not) return arg(l,0); return l; } bool is_placeholder(const ast &e){ if(op(e) == Uninterpreted){ std::string name = string_of_symbol(sym(e)); if(name.size() > 2 && name[0] == '@' && name[1] == 'p') return true; } return false; } public: iz3proof_itp_impl(prover *p, const prover::range &r, bool w) : iz3proof_itp(*p) { pv = p; rng = r; weak = false ; //w; type boolintbooldom[3] = {bool_type(),int_type(),bool_type()}; type booldom[1] = {bool_type()}; type boolbooldom[2] = {bool_type(),bool_type()}; type boolboolbooldom[3] = {bool_type(),bool_type(),bool_type()}; type intbooldom[2] = {int_type(),bool_type()}; contra = function("@contra",2,boolbooldom,bool_type()); m().inc_ref(contra); sum = function("@sum",3,boolintbooldom,bool_type()); m().inc_ref(sum); rotate_sum = function("@rotsum",2,boolbooldom,bool_type()); m().inc_ref(rotate_sum); leq2eq = function("@leq2eq",3,boolboolbooldom,bool_type()); m().inc_ref(leq2eq); eq2leq = function("@eq2leq",2,boolbooldom,bool_type()); m().inc_ref(eq2leq); cong = function("@cong",3,boolintbooldom,bool_type()); m().inc_ref(cong); exmid = function("@exmid",3,boolboolbooldom,bool_type()); m().inc_ref(exmid); symm = function("@symm",1,booldom,bool_type()); m().inc_ref(symm); epsilon = make_var("@eps",int_type()); modpon = function("@mp",3,boolboolbooldom,bool_type()); m().inc_ref(modpon); no_proof = make_var("@nop",bool_type()); concat = function("@concat",2,boolbooldom,bool_type()); m().inc_ref(concat); top_pos = make_var("@top_pos",bool_type()); add_pos = function("@add_pos",2,intbooldom,bool_type()); m().inc_ref(add_pos); rewrite_A = function("@rewrite_A",3,boolboolbooldom,bool_type()); m().inc_ref(rewrite_A); rewrite_B = function("@rewrite_B",3,boolboolbooldom,bool_type()); m().inc_ref(rewrite_B); normal_step = function("@normal_step",2,boolbooldom,bool_type()); m().inc_ref(normal_step); normal_chain = function("@normal_chain",2,boolbooldom,bool_type()); m().inc_ref(normal_chain); normal = function("@normal",2,boolbooldom,bool_type()); m().inc_ref(normal); sforall = function("@sforall",2,boolbooldom,bool_type()); m().inc_ref(sforall); sexists = function("@sexists",2,boolbooldom,bool_type()); m().inc_ref(sexists); } ~iz3proof_itp_impl(){ m().dec_ref(contra); m().dec_ref(sum); m().dec_ref(rotate_sum); m().dec_ref(leq2eq); m().dec_ref(eq2leq); m().dec_ref(cong); m().dec_ref(exmid); m().dec_ref(symm); m().dec_ref(modpon); m().dec_ref(concat); m().dec_ref(add_pos); m().dec_ref(rewrite_A); m().dec_ref(rewrite_B); m().dec_ref(normal_step); m().dec_ref(normal_chain); m().dec_ref(normal); m().dec_ref(sforall); m().dec_ref(sexists); } }; iz3proof_itp *iz3proof_itp::create(prover *p, const prover::range &r, bool w){ return new iz3proof_itp_impl(p,r,w); } z3-4.4.0/src/interp/iz3pp.h0000644000175000017500000000073712540347414015162 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: iz3pp.cpp Abstract: Pretty-print interpolation problems Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef IZ3_PP_H #define IZ3_PP_H #include "iz3mgr.h" /** Exception thrown in case of mal-formed tree interpoloation specification */ struct iz3pp_bad_tree { }; void iz3pp(ast_manager &m, const ptr_vector &cnsts_vec, expr *tree, std::ostream& out); #endif z3-4.4.0/src/interp/iz3proof_itp.h0000644000175000017500000001073212540347414016540 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3proof.h Abstract: This class defines a simple interpolating proof system. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef IZ3PROOF_ITP_H #define IZ3PROOF_ITP_H #include #include "iz3base.h" #include "iz3secondary.h" // #define CHECK_PROOFS /** This class defines a simple proof system. As opposed to iz3proof, this class directly computes interpolants, so the proof representation is just the interpolant itself. */ class iz3proof_itp : public iz3mgr { public: /** Enumeration of proof rules. */ enum rule {Resolution,Assumption,Hypothesis,Theory,Axiom,Contra,Lemma,Reflexivity,Symmetry,Transitivity,Congruence,EqContra}; /** Interface to prover. */ typedef iz3base prover; /** Ast type. */ typedef prover::ast ast; /** The type of proof nodes (just interpolants). */ typedef ast node; /** Object thrown in case of a proof error. */ struct proof_error {}; /** Make a resolution node with given pivot literal and premises. The conclusion of premise1 should contain the negation of the pivot literal, while the conclusion of premise2 should containe the pivot literal. */ virtual node make_resolution(ast pivot, const std::vector &conc, node premise1, node premise2) = 0; /** Make an assumption node. The given clause is assumed in the given frame. */ virtual node make_assumption(int frame, const std::vector &assumption) = 0; /** Make a hypothesis node. If phi is the hypothesis, this is effectively phi |- phi. */ virtual node make_hypothesis(const ast &hypothesis) = 0; /** Make an axiom node. The conclusion must be an instance of an axiom. */ virtual node make_axiom(const std::vector &conclusion) = 0; /** Make an axiom node. The conclusion must be an instance of an axiom. Localize axiom instance to range*/ virtual node make_axiom(const std::vector &conclusion, prover::range) = 0; /** Make a Contra node. This rule takes a derivation of the form Gamma |- False and produces |- \/~Gamma. */ virtual node make_contra(node prem, const std::vector &conclusion) = 0; /** Make a Reflexivity node. This rule produces |- x = x */ virtual node make_reflexivity(ast con) = 0; /** Make a Symmetry node. This takes a derivation of |- x = y and produces | y = x */ virtual node make_symmetry(ast con, const ast &premcon, node prem) = 0; /** Make a transitivity node. This takes derivations of |- x = y and |- y = z produces | x = z */ virtual node make_transitivity(const ast &x, const ast &y, const ast &z, node prem1, node prem2) = 0; /** Make a congruence node. This takes a derivation of |- x_i = y_i and produces |- f(...x_i,...) = f(...,y_i,...) */ virtual node make_congruence(const ast &xi_eq_yi, const ast &con, const ast &prem1) = 0; /** Make a congruence node. This takes derivations of |- x_i1 = y_i1, |- x_i2 = y_i2,... and produces |- f(...x_i1...x_i2...) = f(...y_i1...y_i2...) */ virtual node make_congruence(const std::vector &xi_eq_yi, const ast &con, const std::vector &prems) = 0; /** Make a modus-ponens node. This takes derivations of |- x and |- x = y and produces |- y */ virtual node make_mp(const ast &x_eq_y, const ast &prem1, const ast &prem2) = 0; /** Make a farkas proof node. */ virtual node make_farkas(ast con, const std::vector &prems, const std::vector &prem_cons, const std::vector &coeffs) = 0; /* Make an axiom instance of the form |- x<=y, y<= x -> x =y */ virtual node make_leq2eq(ast x, ast y, const ast &xleqy, const ast &yleqx) = 0; /* Make an axiom instance of the form |- x = y -> x <= y */ virtual node make_eq2leq(ast x, ast y, const ast &xeqy) = 0; /* Make an inference of the form t <= c |- t/d <= floor(c/d) where t is an affine term divisble by d and c is an integer constant */ virtual node make_cut_rule(const ast &tleqc, const ast &d, const ast &con, const ast &prem) = 0; /* Return an interpolant from a proof of false */ virtual ast interpolate(const node &pf) = 0; /** Create proof object to construct an interpolant. */ static iz3proof_itp *create(prover *p, const prover::range &r, bool _weak); protected: iz3proof_itp(iz3mgr &m) : iz3mgr(m) { } public: virtual ~iz3proof_itp(){ } }; #endif z3-4.4.0/src/interp/iz3profiling.h0000755000175000017500000000110112540347414016521 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: iz3profiling.h Abstract: Some routines for measuring performance. Author: Ken McMillan (kenmcmil) Revision History: --*/ #ifndef IZ3PROFILING_H #define IZ3PROFILING_H #include namespace profiling { /** Start a timer with given name */ void timer_start(const char *); /** Stop a timer with given name */ void timer_stop(const char *); /** Print out timings */ void print(std::ostream &s); /** Show the current time. */ void show_time(); } #endif z3-4.4.0/src/cmd_context/0000755000175000017500000000000012540347414014743 5ustar michaelmichaelz3-4.4.0/src/cmd_context/tactic_manager.h0000644000175000017500000000270212540347414020056 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: tactic_manager.h Abstract: Collection of tactics & probes Author: Leonardo (leonardo) 2012-03-06 Notes: --*/ #ifndef _TACTIC_MANAGER_H_ #define _TACTIC_MANAGER_H_ #include"tactic_cmds.h" #include"dictionary.h" class tactic_manager { protected: dictionary m_name2tactic; dictionary m_name2probe; ptr_vector m_tactics; ptr_vector m_probes; void finalize_tactic_cmds(); void finalize_probes(); public: ~tactic_manager(); void insert(tactic_cmd * c); void insert(probe_info * p); tactic_cmd * find_tactic_cmd(symbol const & s) const; probe_info * find_probe(symbol const & s) const; unsigned num_tactics() const { return m_tactics.size(); } unsigned num_probes() const { return m_probes.size(); } tactic_cmd * get_tactic(unsigned i) const { return m_tactics[i]; } probe_info * get_probe(unsigned i) const { return m_probes[i]; } typedef ptr_vector::const_iterator tactic_cmd_iterator; tactic_cmd_iterator begin_tactic_cmds() const { return m_tactics.begin(); } tactic_cmd_iterator end_tactic_cmds() const { return m_tactics.end(); } typedef ptr_vector::const_iterator probe_iterator; probe_iterator begin_probes() const { return m_probes.begin(); } probe_iterator end_probes() const { return m_probes.end(); } }; #endif z3-4.4.0/src/cmd_context/extra_cmds/0000755000175000017500000000000012540347414017074 5ustar michaelmichaelz3-4.4.0/src/cmd_context/extra_cmds/subpaving_cmds.h0000644000175000017500000000041212540347414022246 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_cmds.h Abstract: Commands for debugging subpaving module. Author: Leonardo (leonardo) 2012-08-09 Notes: --*/ class cmd_context; void install_subpaving_cmds(cmd_context & ctx); z3-4.4.0/src/cmd_context/extra_cmds/dbg_cmds.cpp0000644000175000017500000003067512540347414021355 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dbg_cmds.cpp Abstract: SMT2 front-end commands for debugging purposes. Author: Leonardo (leonardo) 2011-04-01 Notes: --*/ #include #include"cmd_context.h" #include"cmd_util.h" #include"rewriter.h" #include"shared_occs.h" #include"for_each_expr.h" #include"rewriter.h" #include"bool_rewriter.h" #include"ast_lt.h" #include"simplify_cmd.h" #include"ast_smt2_pp.h" #include"bound_manager.h" #include"used_vars.h" #include"var_subst.h" #ifndef _EXTERNAL_RELEASE BINARY_SYM_CMD(get_quantifier_body_cmd, "dbg-get-qbody", " ", "store the body of the quantifier in the global variable ", CPK_EXPR, expr *, { if (!is_quantifier(arg)) throw cmd_exception("invalid command, term must be a quantifier"); store_expr_ref(ctx, m_sym, to_quantifier(arg)->get_expr()); }); BINARY_SYM_CMD(set_cmd, "dbg-set", " ", "store in the global variable ", CPK_EXPR, expr *, { store_expr_ref(ctx, m_sym, arg); }); UNARY_CMD(pp_var_cmd, "dbg-pp-var", "", "pretty print a global variable that references an AST", CPK_SYMBOL, symbol const &, { expr * t = get_expr_ref(ctx, arg); SASSERT(t != 0); ctx.display(ctx.regular_stream(), t); ctx.regular_stream() << std::endl; }); BINARY_SYM_CMD(shift_vars_cmd, "dbg-shift-vars", " ", "shift the free variables by in the term referenced by the global variable , the result is stored in ", CPK_UINT, unsigned, { expr * t = get_expr_ref(ctx, m_sym); expr_ref r(ctx.m()); var_shifter s(ctx.m()); s(t, arg, r); store_expr_ref(ctx, m_sym, r.get()); }); UNARY_CMD(pp_shared_cmd, "dbg-pp-shared", "", "display shared subterms of the given term", CPK_EXPR, expr *, { shared_occs s(ctx.m()); s(arg); ctx.regular_stream() << "(shared"; shared_occs::iterator it = s.begin_shared(); shared_occs::iterator end = s.end_shared(); for (; it != end; ++it) { expr * curr = *it; ctx.regular_stream() << std::endl << " "; ctx.display(ctx.regular_stream(), curr, 2); } ctx.regular_stream() << ")" << std::endl; }); UNARY_CMD(num_shared_cmd, "dbg-num-shared", "", "return the number of shared subterms", CPK_EXPR, expr *, { shared_occs s(ctx.m()); s(arg); ctx.regular_stream() << s.num_shared() << std::endl; }); UNARY_CMD(size_cmd, "dbg-size", "", "return the size of the given term", CPK_EXPR, expr *, { ctx.regular_stream() << get_num_exprs(arg) << std::endl; }); class subst_cmd : public cmd { unsigned m_idx; expr * m_source; symbol m_target; ptr_vector m_subst; public: subst_cmd():cmd("dbg-subst") {} virtual char const * get_usage() const { return " (*) "; } virtual char const * get_descr() const { return "substitute the free variables in the AST referenced by using the ASTs referenced by *"; } virtual unsigned get_arity() const { return 3; } virtual void prepare(cmd_context & ctx) { m_idx = 0; m_source = 0; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { if (m_idx == 1) return CPK_SYMBOL_LIST; return CPK_SYMBOL; } virtual void set_next_arg(cmd_context & ctx, symbol const & s) { if (m_idx == 0) { m_source = get_expr_ref(ctx, s); } else { m_target = s; } m_idx++; } virtual void set_next_arg(cmd_context & ctx, unsigned num, symbol const * s) { m_subst.reset(); unsigned i = num; while (i > 0) { --i; m_subst.push_back(get_expr_ref(ctx, s[i])); } m_idx++; } virtual void execute(cmd_context & ctx) { expr_ref r(ctx.m()); beta_reducer p(ctx.m()); p(m_source, m_subst.size(), m_subst.c_ptr(), r); store_expr_ref(ctx, m_target, r.get()); } }; UNARY_CMD(bool_rewriter_cmd, "dbg-bool-rewriter", "", "apply the Boolean rewriter to the given term", CPK_EXPR, expr *, { expr_ref t(ctx.m()); params_ref p; p.set_bool("flat", false); SASSERT(p.get_bool("flat", true) == false); bool_rewriter_star r(ctx.m(), p); r(arg, t); ctx.display(ctx.regular_stream(), t); ctx.regular_stream() << std::endl; }); UNARY_CMD(bool_frewriter_cmd, "dbg-bool-flat-rewriter", "", "apply the Boolean (flattening) rewriter to the given term", CPK_EXPR, expr *, { expr_ref t(ctx.m()); { params_ref p; p.set_bool("flat", true); bool_rewriter_star r(ctx.m(), p); r(arg, t); } ctx.display(ctx.regular_stream(), t); ctx.regular_stream() << std::endl; }); UNARY_CMD(elim_and_cmd, "dbg-elim-and", "", "apply the Boolean rewriter (eliminating AND operator and flattening) to the given term", CPK_EXPR, expr *, { expr_ref t(ctx.m()); { params_ref p; p.set_bool("flat", true); p.set_bool("elim_and", true); bool_rewriter_star r(ctx.m(), p); r(arg, t); } ctx.display(ctx.regular_stream(), t); ctx.regular_stream() << std::endl; }); class lt_cmd : public cmd { expr * m_t1; expr * m_t2; public: lt_cmd():cmd("dbg-lt") {} virtual char const * get_usage() const { return " "; } virtual char const * get_descr(cmd_context & ctx) const { return "return true if the first term is smaller than the second one in the internal Z3 total order on terms."; } virtual unsigned get_arity() const { return 2; } virtual void prepare(cmd_context & ctx) { m_t1 = 0; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_EXPR; } virtual void set_next_arg(cmd_context & ctx, expr * arg) { if (m_t1 == 0) m_t1 = arg; else m_t2 = arg; } virtual void execute(cmd_context & ctx) { bool r = lt(m_t1, m_t2); ctx.regular_stream() << (r ? "true" : "false") << std::endl; } }; UNARY_CMD(some_value_cmd, "dbg-some-value", "", "retrieve some value of the given sort", CPK_SORT, sort *, { ast_manager & m = ctx.m(); expr_ref val(m); val = m.get_some_value(arg); ctx.display(ctx.regular_stream(), val); ctx.regular_stream() << std::endl; }); void tst_params(cmd_context & ctx) { params_ref p1; params_ref p2; p1.set_uint("val", 100); p2 = p1; SASSERT(p2.get_uint("val", 0) == 100); p2.set_uint("val", 200); SASSERT(p2.get_uint("val", 0) == 200); SASSERT(p1.get_uint("val", 0) == 100); p2 = p1; SASSERT(p2.get_uint("val", 0) == 100); SASSERT(p1.get_uint("val", 0) == 100); ctx.regular_stream() << "worked" << std::endl; } ATOMIC_CMD(params_cmd, "dbg-params", "test parameters", tst_params(ctx);); UNARY_CMD(translator_cmd, "dbg-translator", "", "test AST translator", CPK_EXPR, expr *, { ast_manager & m = ctx.m(); scoped_ptr m2 = alloc(ast_manager, m.proof_mode()); ast_translation proc(m, *m2); expr_ref s(m); expr_ref t(*m2); s = arg; t = proc(s.get()); ctx.regular_stream() << mk_ismt2_pp(s, m) << "\n--->\n" << mk_ismt2_pp(t, *m2) << std::endl; }); UNARY_CMD(sexpr_cmd, "dbg-sexpr", "", "display an s-expr", CPK_SEXPR, sexpr *, { arg->display(ctx.regular_stream()); ctx.regular_stream() << std::endl; }); #define GUARDED_CODE(CODE) try { CODE } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { ctx.regular_stream() << "(error \"" << escaped(ex.msg()) << "\")" << std::endl; } UNARY_CMD(set_next_id, "dbg-set-next-id", "", "set the next expression id to be at least the given value", CPK_UINT, unsigned, { ctx.m().set_next_expr_id(arg); }); UNARY_CMD(used_vars_cmd, "dbg-used-vars", "", "test used_vars functor", CPK_EXPR, expr *, { used_vars proc; if (is_quantifier(arg)) arg = to_quantifier(arg)->get_expr(); proc(arg); ctx.regular_stream() << "(vars"; for (unsigned i = 0; i < proc.get_max_found_var_idx_plus_1(); i++) { sort * s = proc.get(i); ctx.regular_stream() << "\n (" << std::left << std::setw(6) << i << " "; if (s != 0) ctx.display(ctx.regular_stream(), s, 10); else ctx.regular_stream() << ""; ctx.regular_stream() << ")"; } ctx.regular_stream() << ")" << std::endl; }); UNARY_CMD(elim_unused_vars_cmd, "dbg-elim-unused-vars", "", "eliminate unused vars from a quantifier", CPK_EXPR, expr *, { if (!is_quantifier(arg)) { ctx.display(ctx.regular_stream(), arg); return; } expr_ref r(ctx.m()); elim_unused_vars(ctx.m(), to_quantifier(arg), r); SASSERT(!is_quantifier(r) || !to_quantifier(r)->may_have_unused_vars()); ctx.display(ctx.regular_stream(), r); ctx.regular_stream() << std::endl; }); class instantiate_cmd_core : public cmd { protected: quantifier * m_q; ptr_vector m_args; public: instantiate_cmd_core(char const * name):cmd(name) {} virtual char const * get_usage() const { return " (*)"; } virtual char const * get_descr() const { return "instantiate the quantifier using the given expressions."; } virtual unsigned get_arity() const { return 2; } virtual void prepare(cmd_context & ctx) { m_q = 0; m_args.reset(); } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { if (m_q == 0) return CPK_EXPR; else return CPK_EXPR_LIST; } virtual void set_next_arg(cmd_context & ctx, expr * s) { if (!is_quantifier(s)) throw cmd_exception("invalid command, quantifier expected."); m_q = to_quantifier(s); } virtual void set_next_arg(cmd_context & ctx, unsigned num, expr * const * ts) { if (num != m_q->get_num_decls()) throw cmd_exception("invalid command, mismatch between the number of quantified variables and the number of arguments."); unsigned i = num; while (i > 0) { --i; sort * s = ctx.m().get_sort(ts[i]); if (s != m_q->get_decl_sort(i)) { std::ostringstream buffer; buffer << "invalid command, sort mismatch at position " << i; throw cmd_exception(buffer.str()); } } m_args.append(num, ts); } virtual void execute(cmd_context & ctx) { expr_ref r(ctx.m()); instantiate(ctx.m(), m_q, m_args.c_ptr(), r); ctx.display(ctx.regular_stream(), r); ctx.regular_stream() << std::endl; } }; class instantiate_cmd : public instantiate_cmd_core { public: instantiate_cmd():instantiate_cmd_core("dbg-instantiate") {} }; class instantiate_nested_cmd : public instantiate_cmd_core { public: instantiate_nested_cmd():instantiate_cmd_core("dbg-instantiate-nested") {} virtual char const * get_descr() const { return "instantiate the quantifier nested in the outermost quantifier, this command is used to test the instantiation procedure with quantifiers that contain free variables."; } virtual void set_next_arg(cmd_context & ctx, expr * s) { instantiate_cmd_core::set_next_arg(ctx, s); if (!is_quantifier(m_q->get_expr())) throw cmd_exception("invalid command, nested quantifier expected"); m_q = to_quantifier(m_q->get_expr()); } }; #endif void install_dbg_cmds(cmd_context & ctx) { #ifndef _EXTERNAL_RELEASE ctx.insert(alloc(get_quantifier_body_cmd)); ctx.insert(alloc(set_cmd)); ctx.insert(alloc(pp_var_cmd)); ctx.insert(alloc(shift_vars_cmd)); ctx.insert(alloc(pp_shared_cmd)); ctx.insert(alloc(num_shared_cmd)); ctx.insert(alloc(size_cmd)); ctx.insert(alloc(subst_cmd)); ctx.insert(alloc(bool_rewriter_cmd)); ctx.insert(alloc(bool_frewriter_cmd)); ctx.insert(alloc(elim_and_cmd)); install_simplify_cmd(ctx, "dbg-th-rewriter"); ctx.insert(alloc(lt_cmd)); ctx.insert(alloc(some_value_cmd)); ctx.insert(alloc(params_cmd)); ctx.insert(alloc(translator_cmd)); ctx.insert(alloc(sexpr_cmd)); ctx.insert(alloc(used_vars_cmd)); ctx.insert(alloc(elim_unused_vars_cmd)); ctx.insert(alloc(instantiate_cmd)); ctx.insert(alloc(instantiate_nested_cmd)); ctx.insert(alloc(set_next_id)); #endif } z3-4.4.0/src/cmd_context/extra_cmds/polynomial_cmds.cpp0000644000175000017500000001714512540347414023001 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: polynomial_cmds.cpp Abstract: Commands for debugging polynomial module. Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #include #include"cmd_context.h" #include"cmd_util.h" #include"scoped_timer.h" #include"scoped_ctrl_c.h" #include"cancel_eh.h" #include"ast_smt2_pp.h" #include"expr2polynomial.h" #include"parametric_cmd.h" #include"mpq.h" #include"algebraic_numbers.h" #include"polynomial_var2value.h" #include"expr2var.h" #include"pp.h" #include"pp_params.hpp" static void to_poly(cmd_context & ctx, expr * t) { polynomial::numeral_manager nm; polynomial::manager pm(nm); default_expr2polynomial expr2poly(ctx.m(), pm); polynomial::polynomial_ref p(pm); polynomial::scoped_numeral d(nm); if (!expr2poly.to_polynomial(t, p, d)) { throw cmd_exception("expression is not a polynomial"); } expr_ref r(ctx.m()); expr2poly.to_expr(p, true, r); if (!nm.is_one(d)) ctx.regular_stream() << "(* " << nm.to_string(d) << " "; ctx.display(ctx.regular_stream(), r); if (!nm.is_one(d)) ctx.regular_stream() << ")"; ctx.regular_stream() << std::endl; } static void factor(cmd_context & ctx, expr * t, polynomial::factor_params const & ps) { polynomial::numeral_manager nm; polynomial::manager pm(nm); default_expr2polynomial expr2poly(ctx.m(), pm); polynomial::polynomial_ref p(pm); polynomial::scoped_numeral d(nm); if (!expr2poly.to_polynomial(t, p, d)) { throw cmd_exception("expression is not a polynomial"); } polynomial::factors fs(pm); factor(p, fs, ps); ctx.regular_stream() << "(factors"; rational f0(fs.get_constant()); f0 = f0 / rational(d); ctx.regular_stream() << std::endl << f0; unsigned num_factors = fs.distinct_factors(); expr_ref f(ctx.m()); for (unsigned i = 0; i < num_factors; i++) { ctx.regular_stream() << std::endl; if (fs.get_degree(i) > 1) ctx.regular_stream() << "(^ "; expr2poly.to_expr(fs[i], true, f); ctx.display(ctx.regular_stream(), f); if (fs.get_degree(i) > 1) ctx.regular_stream() << " " << fs.get_degree(i) << ")"; } ctx.regular_stream() << ")" << std::endl; } class poly_isolate_roots_cmd : public cmd { struct context { arith_util m_util; unsynch_mpq_manager m_qm; polynomial::manager m_pm; algebraic_numbers::manager m_am; polynomial_ref m_p; default_expr2polynomial m_expr2poly; polynomial::var m_var; typedef polynomial::simple_var2value x2v; x2v m_x2v; context(ast_manager & m): m_util(m), m_pm(m_qm), m_am(m_qm), m_p(m_pm), m_expr2poly(m, m_pm), m_var(polynomial::null_var), m_x2v(m_am) { } void set_next_arg(cmd_context & ctx, expr * arg) { if (m_p.get() == 0) { scoped_mpz d(m_qm); if (!m_expr2poly.to_polynomial(arg, m_p, d)) throw cmd_exception("expression is not a polynomial"); } else if (m_var == polynomial::null_var) { if (!m_expr2poly.is_var(arg)) throw cmd_exception("invalid assignment, argument is not a variable in the given polynomial"); m_var = m_expr2poly.get_mapping().to_var(arg); } else { rational k; scoped_anum v(m_am); if (m_util.is_numeral(arg, k)) { m_am.set(v, k.to_mpq()); } else if (m_util.is_irrational_algebraic_numeral(arg)) { m_am.set(v, m_util.to_irrational_algebraic_numeral(arg)); } else { throw cmd_exception("invalid assignment, argument is not a value"); } m_x2v.push_back(m_var, v); m_var = polynomial::null_var; } } void execute(cmd_context & ctx) { if (m_p.get() == 0) throw cmd_exception("polynomial expected"); polynomial::var_vector xs; m_pm.vars(m_p, xs); unsigned num_assigned = 0; for (unsigned i = 0; i < xs.size(); i++) { if (m_x2v.contains(xs[i])) num_assigned++; } if (num_assigned != xs.size() && num_assigned + 1 != xs.size()) throw cmd_exception("given assignment is not sufficient to make the given polynomial univariate"); scoped_anum_vector rs(m_am); m_am.isolate_roots(m_p, m_x2v, rs); ctx.regular_stream() << "(roots"; pp_params params; bool pp_decimal = params.decimal(); for (unsigned i = 0; i < rs.size(); i++) { ctx.regular_stream() << std::endl; if (!pp_decimal) m_am.display_root_smt2(ctx.regular_stream(), rs[i]); else m_am.display_decimal(ctx.regular_stream(), rs[i]); } ctx.regular_stream() << ")" << std::endl; } }; scoped_ptr m_ctx; public: poly_isolate_roots_cmd(char const * name = "poly/isolate-roots"):cmd(name), m_ctx(0) {} virtual char const * get_usage() const { return " ( )*"; } virtual char const * get_descr(cmd_context & ctx) const { return "isolate the roots a multivariate polynomial modulo an assignment"; } virtual unsigned get_arity() const { return VAR_ARITY; } virtual void prepare(cmd_context & ctx) { m_ctx = alloc(context, ctx.m()); } virtual void finalize(cmd_context & ctx) { m_ctx = 0; } virtual void failure_cleanup(cmd_context & ctx) { m_ctx = 0; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_EXPR; } virtual void set_next_arg(cmd_context & ctx, expr * arg) { m_ctx->set_next_arg(ctx, arg); } virtual void execute(cmd_context & ctx) { m_ctx->execute(ctx); m_ctx = 0; } }; UNARY_CMD(to_poly_cmd, "to-poly", "", "convert expression into sum-of-monomials form", CPK_EXPR, expr *, to_poly(ctx, arg);); class poly_factor_cmd : public parametric_cmd { expr * m_target; public: poly_factor_cmd(char const * name = "poly/factor"):parametric_cmd(name) {} virtual char const * get_usage() const { return " ( )*"; } virtual char const * get_main_descr() const { return "factor a polynomial"; } virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { polynomial::factor_params::get_param_descrs(p); } virtual void prepare(cmd_context & ctx) { parametric_cmd::prepare(ctx); m_target = 0; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { if (m_target == 0) return CPK_EXPR; return parametric_cmd::next_arg_kind(ctx); } virtual void set_next_arg(cmd_context & ctx, expr * arg) { m_target = arg; } virtual void execute(cmd_context & ctx) { polynomial::factor_params ps; ps.updt_params(m_params); factor(ctx, m_target, ps); } }; void install_polynomial_cmds(cmd_context & ctx) { #ifndef _EXTERNAL_RELEASE ctx.insert(alloc(to_poly_cmd)); ctx.insert(alloc(poly_factor_cmd)); ctx.insert(alloc(poly_isolate_roots_cmd)); #endif } z3-4.4.0/src/cmd_context/extra_cmds/dbg_cmds.h0000644000175000017500000000046712540347414021016 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dbg_cmds.h Abstract: SMT2 front-end commands for debugging purposes. Author: Leonardo (leonardo) 2011-04-01 Notes: --*/ #ifndef _DBG_CMDS_H_ #define _DBG_CMDS_H_ class cmd_context; void install_dbg_cmds(cmd_context & ctx); #endif z3-4.4.0/src/cmd_context/extra_cmds/subpaving_cmds.cpp0000644000175000017500000000273512540347414022613 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_cmds.cpp Abstract: Commands for debugging subpaving module. Author: Leonardo (leonardo) 2012-08-09 Notes: --*/ #include #include"cmd_context.h" #include"cmd_util.h" #include"expr2subpaving.h" #include"th_rewriter.h" #include"ast_smt2_pp.h" #include"expr2var.h" static void to_subpaving(cmd_context & ctx, expr * t) { ast_manager & m = ctx.m(); unsynch_mpq_manager qm; scoped_ptr s; s = subpaving::mk_mpq_context(qm); expr2var e2v(m); expr2subpaving e2s(m, *s, &e2v); params_ref p; p.set_bool("mul_to_power", true); th_rewriter simp(m, p); expr_ref t_s(m); simp(t, t_s); scoped_mpz n(qm), d(qm); ctx.regular_stream() << mk_ismt2_pp(t_s, m) << "\n=======>" << std::endl; subpaving::var x = e2s.internalize_term(t_s, n, d); expr2var::iterator it = e2v.begin(); expr2var::iterator end = e2v.end(); for (; it != end; ++it) { ctx.regular_stream() << "x" << it->m_value << " := " << mk_ismt2_pp(it->m_key, m) << "\n"; } s->display_constraints(ctx.regular_stream()); ctx.regular_stream() << n << "/" << d << " x" << x << "\n"; } UNARY_CMD(to_subpaving_cmd, "to-subpaving", "", "internalize expression into subpaving module", CPK_EXPR, expr *, to_subpaving(ctx, arg);); void install_subpaving_cmds(cmd_context & ctx) { #ifndef _EXTERNAL_RELEASE ctx.insert(alloc(to_subpaving_cmd)); #endif } z3-4.4.0/src/cmd_context/extra_cmds/polynomial_cmds.h0000644000175000017500000000051412540347414022436 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: polynomial_cmds.h Abstract: Commands for debugging polynomial module. Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #ifndef _POLYNOMIAL_CMDS_H_ #define _POLYNOMIAL_CMDS_H_ class cmd_context; void install_polynomial_cmds(cmd_context & ctx); #endif z3-4.4.0/src/cmd_context/cmd_util.cpp0000644000175000017500000000154212540347414017251 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cmd_util.cpp Abstract: Macros for definining new SMT2 front-end cmds. Author: Leonardo (leonardo) 2011-04-01 Notes: --*/ #include"cmd_context.h" ast * get_ast_ref(cmd_context & ctx, symbol const & v) { object_ref * r = ctx.find_object_ref(v); SASSERT(r != 0); if (r->kind() != ast_object_ref::cls_kind()) throw cmd_exception("global variable does not reference an AST"); return static_cast(r)->get_ast(); } expr * get_expr_ref(cmd_context & ctx, symbol const & v) { ast * r = get_ast_ref(ctx, v); if (!is_expr(r)) throw cmd_exception("global variable does not reference a term"); return to_expr(r); } void store_expr_ref(cmd_context & ctx, symbol const & v, expr * t) { ctx.insert(v, alloc(ast_object_ref, ctx, t)); } z3-4.4.0/src/cmd_context/cmd_context_to_goal.cpp0000644000175000017500000000275412540347414021472 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: cmd_context_to_goal.cpp Abstract: Procedure for copying the assertions in the command context to a goal object. Author: Leonardo (leonardo) 2012-10-21 Notes: --*/ #include"cmd_context.h" #include"goal.h" /** \brief Assert expressions from ctx into t. */ void assert_exprs_from(cmd_context const & ctx, goal & t) { if (ctx.produce_proofs() && ctx.produce_unsat_cores()) throw cmd_exception("Frontend does not support simultaneous generation of proofs and unsat cores"); ast_manager & m = t.m(); bool proofs_enabled = t.proofs_enabled(); if (ctx.produce_unsat_cores()) { ptr_vector::const_iterator it = ctx.begin_assertions(); ptr_vector::const_iterator end = ctx.end_assertions(); ptr_vector::const_iterator it2 = ctx.begin_assertion_names(); ptr_vector::const_iterator end2 = ctx.end_assertion_names(); SASSERT(end - it == end2 - it2); for (; it != end; ++it, ++it2) { t.assert_expr(*it, proofs_enabled ? m.mk_asserted(*it) : 0, m.mk_leaf(*it2)); } } else { ptr_vector::const_iterator it = ctx.begin_assertions(); ptr_vector::const_iterator end = ctx.end_assertions(); for (; it != end; ++it) { t.assert_expr(*it, proofs_enabled ? m.mk_asserted(*it) : 0, 0); } SASSERT(ctx.begin_assertion_names() == ctx.end_assertion_names()); } } z3-4.4.0/src/cmd_context/echo_tactic.h0000644000175000017500000000103312540347414017356 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: echo_tactic.h Abstract: Tactic and probe for dumping data. Author: Leonardo (leonardo) 2012-10-20 Notes: --*/ #ifndef _ECHO_TACTICS_H_ #define _ECHO_TACTICS_H_ class cmd_context; class tactic; class probe; tactic * mk_echo_tactic(cmd_context & ctx, char const * msg, bool newline = true); // Display the value returned by p in the diagnostic_stream tactic * mk_probe_value_tactic(cmd_context & ctx, char const * msg, probe * p, bool newline = true); #endif z3-4.4.0/src/cmd_context/cmd_context.h0000644000175000017500000004406212540347414017431 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cmd_context.h Abstract: Ultra-light command context. It provides a generic command pluging infrastructure. A command context also provides names (aka symbols) to Z3 objects. These names are used to reference Z3 objects in commands. Author: Leonardo (leonardo) 2011-03-01 Notes: --*/ #ifndef _CMD_CONTEXT_H_ #define _CMD_CONTEXT_H_ #include #include"ast.h" #include"ast_printer.h" #include"pdecl.h" #include"dictionary.h" #include"solver.h" #include"datatype_decl_plugin.h" #include"stopwatch.h" #include"cmd_context_types.h" #include"event_handler.h" #include"sexpr.h" #include"tactic_manager.h" #include"check_logic.h" #include"progress_callback.h" #include"scoped_ptr_vector.h" #include"context_params.h" class func_decls { func_decl * m_decls; public: func_decls():m_decls(0) {} func_decls(ast_manager & m, func_decl * f); void finalize(ast_manager & m); bool contains(func_decl * f) const; bool insert(ast_manager & m, func_decl * f); void erase(ast_manager & m, func_decl * f); bool more_than_one() const; bool clash(func_decl * f) const; bool empty() const { return m_decls == 0; } func_decl * first() const; func_decl * find(unsigned arity, sort * const * domain, sort * range) const; func_decl * find(ast_manager & m, unsigned num_args, expr * const * args, sort * range) const; }; /** \brief Generic wrapper. */ class object_ref { unsigned m_ref_count; public: object_ref():m_ref_count(0) {} virtual ~object_ref() {} virtual void finalize(cmd_context & ctx) = 0; void inc_ref(cmd_context & ctx) { m_ref_count++; } void dec_ref(cmd_context & ctx) { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) { finalize(ctx); dealloc(this); } } virtual char const * kind() const = 0; }; class ast_object_ref : public object_ref { ast * m_ast; public: ast_object_ref(cmd_context & ctx, ast * a); virtual void finalize(cmd_context & ctx); ast * get_ast() const { return m_ast; } static char const * cls_kind() { return "AST"; } virtual char const * kind() const { return cls_kind(); } }; class stream_ref { std::string m_default_name; std::ostream & m_default; std::string m_name; std::ostream * m_stream; bool m_owner; public: stream_ref(std::string n, std::ostream & d):m_default_name(n), m_default(d), m_name(n), m_stream(&d), m_owner(false) {} ~stream_ref() { reset(); } void set(char const * name); void reset(); std::ostream & operator*() { return *m_stream; } char const * name() const { return m_name.c_str(); } }; struct builtin_decl { family_id m_fid; decl_kind m_decl; builtin_decl * m_next; builtin_decl():m_fid(null_family_id), m_decl(0), m_next(0) {} builtin_decl(family_id fid, decl_kind k, builtin_decl * n = 0):m_fid(fid), m_decl(k), m_next(n) {} }; class opt_wrapper : public check_sat_result { public: virtual bool empty() = 0; virtual void push() = 0; virtual void pop(unsigned n) = 0; virtual void set_cancel(bool f) = 0; virtual void reset_cancel() = 0; virtual void cancel() = 0; virtual lbool optimize() = 0; virtual void set_hard_constraints(ptr_vector & hard) = 0; virtual void display_assignment(std::ostream& out) = 0; virtual bool is_pareto() = 0; virtual void set_logic(symbol const& s) = 0; virtual bool print_model() const = 0; }; class cmd_context : public progress_callback, public tactic_manager, public ast_printer_context { public: enum status { UNSAT, SAT, UNKNOWN }; enum check_sat_state { css_unsat, css_sat, css_unknown, css_clear }; typedef std::pair macro; struct scoped_watch { cmd_context & m_ctx; public: scoped_watch(cmd_context & ctx):m_ctx(ctx) { m_ctx.m_watch.reset(); m_ctx.m_watch.start(); } ~scoped_watch() { m_ctx.m_watch.stop(); } }; protected: context_params m_params; bool m_main_ctx; symbol m_logic; bool m_interactive_mode; bool m_global_decls; bool m_print_success; unsigned m_random_seed; bool m_produce_unsat_cores; bool m_produce_assignments; status m_status; bool m_numeral_as_real; bool m_ignore_check; // used by the API to disable check-sat() commands when parsing SMT 2.0 files. bool m_exit_on_error; static std::ostringstream g_error_stream; ast_manager * m_manager; bool m_own_manager; bool m_manager_initialized; pdecl_manager * m_pmanager; sexpr_manager * m_sexpr_manager; check_logic m_check_logic; stream_ref m_regular; stream_ref m_diagnostic; dictionary m_cmds; dictionary m_builtin_decls; scoped_ptr_vector m_extra_builtin_decls; // make sure that dynamically allocated builtin_decls are deleted dictionary m_object_refs; // anything that can be named. dictionary m_user_tactic_decls; dictionary m_func_decls; obj_map m_func_decl2alias; dictionary m_psort_decls; dictionary m_macros; // the following fields m_func_decls_stack, m_psort_decls_stack and m_exprs_stack are used when m_global_decls == false typedef std::pair sf_pair; svector m_func_decls_stack; svector m_psort_decls_stack; svector m_macros_stack; // ptr_vector m_aux_pdecls; ptr_vector m_assertions; vector m_assertion_strings; ptr_vector m_assertion_names; // named assertions are represented using boolean variables. struct scope { unsigned m_func_decls_stack_lim; unsigned m_psort_decls_stack_lim; unsigned m_macros_stack_lim; unsigned m_aux_pdecls_lim; // only m_assertions_lim is relevant when m_global_decls = true unsigned m_assertions_lim; }; svector m_scopes; scoped_ptr m_solver_factory; scoped_ptr m_interpolating_solver_factory; ref m_solver; ref m_check_sat_result; ref m_opt; stopwatch m_watch; class dt_eh : public new_datatype_eh { cmd_context & m_owner; datatype_util m_dt_util; public: dt_eh(cmd_context & owner); virtual ~dt_eh(); virtual void operator()(sort * dt); }; friend class dt_eh; scoped_ptr m_dt_eh; class pp_env; friend class pp_env; scoped_ptr m_pp_env; pp_env & get_pp_env() const; void register_builtin_sorts(decl_plugin * p); void register_builtin_ops(decl_plugin * p); void load_plugin(symbol const & name, bool install_names, svector& fids); void init_manager_core(bool new_manager); void init_manager(); void init_external_manager(); void reset_cmds(); void finalize_cmds(); void restore_func_decls(unsigned old_sz); void restore_psort_decls(unsigned old_sz); void restore_macros(unsigned old_sz); void restore_aux_pdecls(unsigned old_sz); void restore_assertions(unsigned old_sz); void erase_func_decl_core(symbol const & s, func_decl * f); void erase_psort_decl_core(symbol const & s); void erase_macro_core(symbol const & s); bool logic_has_arith_core(symbol const & s) const; bool logic_has_bv_core(symbol const & s) const; bool logic_has_array_core(symbol const & s) const; bool logic_has_seq_core(symbol const & s) const; bool logic_has_fpa_core(symbol const & s) const; bool logic_has_horn(symbol const& s) const; bool logic_has_arith() const; bool logic_has_bv() const; bool logic_has_seq() const; bool logic_has_array() const; bool logic_has_datatype() const; bool logic_has_fpa() const; bool supported_logic(symbol const & s) const; void print_unsupported_msg() { regular_stream() << "unsupported" << std::endl; } void print_unsupported_info(symbol const& s) { if (s != symbol::null) diagnostic_stream() << "; " << s << std::endl;} void mk_solver(); public: cmd_context(bool main_ctx = true, ast_manager * m = 0, symbol const & l = symbol::null); ~cmd_context(); void set_cancel(bool f); void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } context_params & params() { return m_params; } solver_factory &get_solver_factory() { return *m_solver_factory; } solver_factory &get_interpolating_solver_factory() { return *m_interpolating_solver_factory; } opt_wrapper* get_opt(); void set_opt(opt_wrapper* o); void global_params_updated(); // this method should be invoked when global (and module) params are updated. bool set_logic(symbol const & s); bool has_logic() const { return m_logic != symbol::null; } symbol const & get_logic() const { return m_logic; } bool is_logic(char const * l_name) const { return has_logic() && strcmp(m_logic.bare_str(), l_name) == 0; } bool numeral_as_real() const { return m_numeral_as_real; } void set_numeral_as_real(bool f) { m_numeral_as_real = f; } void set_interactive_mode(bool flag) { m_interactive_mode = flag; } void set_ignore_check(bool flag) { m_ignore_check = flag; } void set_exit_on_error(bool flag) { m_exit_on_error = flag; } bool exit_on_error() const { return m_exit_on_error; } bool interactive_mode() const { return m_interactive_mode; } void set_print_success(bool flag) { m_print_success = flag; } bool print_success_enabled() const { return m_print_success; } void print_success() { if (print_success_enabled()) regular_stream() << "success" << std::endl; } void print_unsupported(symbol const & s) { print_unsupported_msg(); print_unsupported_info(s); } bool global_decls() const { return m_global_decls; } void set_global_decls(bool flag) { SASSERT(!has_manager()); m_global_decls = flag; } unsigned random_seed() const { return m_random_seed; } void set_random_seed(unsigned s) { m_random_seed = s; } bool produce_models() const; bool produce_proofs() const; bool produce_interpolants() const; bool produce_unsat_cores() const; bool well_sorted_check_enabled() const; bool validate_model_enabled() const; void set_produce_models(bool flag); void set_produce_unsat_cores(bool flag); void set_produce_proofs(bool flag); void set_produce_interpolants(bool flag); bool produce_assignments() const { return m_produce_assignments; } void set_produce_assignments(bool flag) { m_produce_assignments = flag; } void set_status(status st) { m_status = st; } status get_status() const { return m_status; } std::string reason_unknown() const; bool has_manager() const { return m_manager != 0; } ast_manager & m() const { const_cast(this)->init_manager(); return *m_manager; } virtual ast_manager & get_ast_manager() { return m(); } pdecl_manager & pm() const { if (!m_pmanager) const_cast(this)->init_manager(); return *m_pmanager; } sexpr_manager & sm() const { if (!m_sexpr_manager) const_cast(this)->m_sexpr_manager = alloc(sexpr_manager); return *m_sexpr_manager; } void set_solver_factory(solver_factory * s); void set_interpolating_solver_factory(solver_factory * s); void set_check_sat_result(check_sat_result * r) { m_check_sat_result = r; } check_sat_result * get_check_sat_result() const { return m_check_sat_result.get(); } check_sat_state cs_state() const; void validate_model(); void register_plugin(symbol const & name, decl_plugin * p, bool install_names); bool is_func_decl(symbol const & s) const; bool is_sort_decl(symbol const& s) const { return m_psort_decls.contains(s); } void insert(cmd * c); void insert(symbol const & s, func_decl * f); void insert(func_decl * f) { insert(f->get_name(), f); } void insert(symbol const & s, psort_decl * p); void insert(psort_decl * p) { insert(p->get_name(), p); } void insert(symbol const & s, unsigned arity, expr * t); void insert(symbol const & s, object_ref *); void insert(tactic_cmd * c) { tactic_manager::insert(c); } void insert(probe_info * p) { tactic_manager::insert(p); } void insert_user_tactic(symbol const & s, sexpr * d); void insert_aux_pdecl(pdecl * p); func_decl * find_func_decl(symbol const & s) const; func_decl * find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices, unsigned arity, sort * const * domain, sort * range) const; psort_decl * find_psort_decl(symbol const & s) const; macro find_macro(symbol const & s) const; cmd * find_cmd(symbol const & s) const; sexpr * find_user_tactic(symbol const & s) const; object_ref * find_object_ref(symbol const & s) const; void mk_const(symbol const & s, expr_ref & result) const; void mk_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range, expr_ref & r) const; void erase_cmd(symbol const & s); void erase_func_decl(symbol const & s); void erase_func_decl(symbol const & s, func_decl * f); void erase_func_decl(func_decl * f) { erase_func_decl(f->get_name(), f); } void erase_psort_decl(symbol const & s); void erase_macro(symbol const & s); void erase_object_ref(symbol const & s); void erase_user_tactic(symbol const & s); void reset_func_decls(); void reset_psort_decls(); void reset_macros(); void reset_object_refs(); void reset_user_tactics(); void set_regular_stream(char const * name) { m_regular.set(name); } void set_diagnostic_stream(char const * name); virtual std::ostream & regular_stream() { return *m_regular; } virtual std::ostream & diagnostic_stream() { return *m_diagnostic; } char const * get_regular_stream_name() const { return m_regular.name(); } char const * get_diagnostic_stream_name() const { return m_diagnostic.name(); } typedef dictionary::iterator cmd_iterator; cmd_iterator begin_cmds() const { return m_cmds.begin(); } cmd_iterator end_cmds() const { return m_cmds.end(); } typedef dictionary::iterator user_tactic_iterator; user_tactic_iterator begin_user_tactics() const { return m_user_tactic_decls.begin(); } user_tactic_iterator end_user_tactics() const { return m_user_tactic_decls.end(); } void display_assertions(); void display_statistics(bool show_total_time = false, double total_time = 0.0); void reset(bool finalize = false); void assert_expr(expr * t); void assert_expr(symbol const & name, expr * t); void push_assert_string(std::string const & s) { SASSERT(m_interactive_mode); m_assertion_strings.push_back(s); } void push(); void push(unsigned n); void pop(unsigned n); void check_sat(unsigned num_assumptions, expr * const * assumptions); // display the result produced by a check-sat or check-sat-using commands in the regular stream void display_sat_result(lbool r); // check if result produced by check-sat or check-sat-using matches the known status void validate_check_sat_result(lbool r); unsigned num_scopes() const { return m_scopes.size(); } dictionary const & get_macros() const { return m_macros; } bool is_model_available() const; double get_seconds() const { return m_watch.get_seconds(); } ptr_vector::const_iterator begin_assertions() const { return m_assertions.begin(); } ptr_vector::const_iterator end_assertions() const { return m_assertions.end(); } ptr_vector::const_iterator begin_assertion_names() const { return m_assertion_names.begin(); } ptr_vector::const_iterator end_assertion_names() const { return m_assertion_names.end(); } /** \brief Hack: consume assertions if there are no scopes. This method is useful for reducing memory consumption in huge benchmarks were incrementality is not an issue. */ bool consume_assertions() { if (num_scopes() > 0) return false; restore_assertions(0); return true; } format_ns::format * pp(sort * s) const; virtual void pp(sort * s, format_ns::format_ref & r) const { r = pp(s); } virtual void pp(func_decl * f, format_ns::format_ref & r) const; virtual void pp(expr * n, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names) const; virtual void pp(expr * n, format_ns::format_ref & r) const; virtual void display(std::ostream & out, sort * s, unsigned indent = 0) const; virtual void display(std::ostream & out, expr * n, unsigned indent, unsigned num_vars, char const * var_prefix, sbuffer & var_names) const; virtual void display(std::ostream & out, expr * n, unsigned indent = 0) const; virtual void display(std::ostream & out, func_decl * f, unsigned indent = 0) const; // dump assertions in out using the pretty printer. void dump_assertions(std::ostream & out) const; // display assertions as a SMT2 benchmark. void display_smt2_benchmark(std::ostream & out, unsigned num, expr * const * assertions, symbol const & logic = symbol::null) const; virtual void slow_progress_sample(); virtual void fast_progress_sample(); }; std::ostream & operator<<(std::ostream & out, cmd_context::status st); #endif z3-4.4.0/src/cmd_context/simplify_cmd.h0000644000175000017500000000053612540347414017577 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: simplify_cmd.h Abstract: SMT2 front-end 'simplify' command. Author: Leonardo (leonardo) 2011-04-20 Notes: --*/ #ifndef _SIMPLIFY_CMD_H_ #define _SIMPLIFY_CMD_H_ class cmd_context; void install_simplify_cmd(cmd_context & ctx, char const * cmd_name = "simplify"); #endif z3-4.4.0/src/cmd_context/context_params.cpp0000644000175000017500000001362312540347414020503 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: context_params.cpp Abstract: Goodies for managing context parameters in the cmd_context and api_context Author: Leonardo (leonardo) 2012-12-01 Notes: --*/ #include"context_params.h" #include"gparams.h" #include"params.h" #include"ast.h" #include"solver.h" context_params::context_params() { m_unsat_core = false; m_model = true; m_model_validate = false; m_auto_config = true; m_proof = false; m_trace = false; m_debug_ref_count = false; m_smtlib2_compliant = false; m_well_sorted_check = false; m_timeout = UINT_MAX; updt_params(); } void context_params::set_bool(bool & opt, char const * param, char const * value) { if (strcmp(value, "true") == 0) { opt = true; } else if (strcmp(value, "false") == 0) { opt = false; } else { throw default_exception("invalid value '%s' for Boolean parameter '%s'", value, param); } } void context_params::set(char const * param, char const * value) { std::string p = param; unsigned n = static_cast(p.size()); for (unsigned i = 0; i < n; i++) { if (p[i] >= 'A' && p[i] <= 'Z') p[i] = p[i] - 'A' + 'a'; else if (p[i] == '-') p[i] = '_'; } if (p == "timeout") { long val = strtol(value, 0, 10); m_timeout = static_cast(val); } else if (p == "type_check" || p == "well_sorted_check") { set_bool(m_well_sorted_check, param, value); } else if (p == "auto_config") { set_bool(m_auto_config, param, value); } else if (p == "proof") { set_bool(m_proof, param, value); } else if (p == "model") { set_bool(m_model, param, value); } else if (p == "model_validate") { set_bool(m_model_validate, param, value); } else if (p == "trace") { set_bool(m_trace, param, value); } else if (p == "trace_file_name") { m_trace_file_name = value; } else if (p == "unsat_core") { set_bool(m_unsat_core, param, value); } else if (p == "debug_ref_count") { set_bool(m_debug_ref_count, param, value); } else if (p == "smtlib2_compliant") { set_bool(m_smtlib2_compliant, param, value); } else { param_descrs d; collect_param_descrs(d); std::stringstream strm; strm << "unknown parameter '" << p << "'\n"; strm << "Legal parameters are:\n"; d.display(strm, 2, false, false); throw default_exception(strm.str()); } } void context_params::updt_params() { updt_params(gparams::get()); } void context_params::updt_params(params_ref const & p) { m_timeout = p.get_uint("timeout", m_timeout); m_well_sorted_check = p.get_bool("type_check", p.get_bool("well_sorted_check", m_well_sorted_check)); m_auto_config = p.get_bool("auto_config", m_auto_config); m_proof = p.get_bool("proof", m_proof); m_model = p.get_bool("model", m_model); m_model_validate = p.get_bool("model_validate", m_model_validate); m_trace = p.get_bool("trace", m_trace); m_trace_file_name = p.get_str("trace_file_name", "z3.log"); m_unsat_core = p.get_bool("unsat_core", m_unsat_core); m_debug_ref_count = p.get_bool("debug_ref_count", m_debug_ref_count); m_smtlib2_compliant = p.get_bool("smtlib2_compliant", m_smtlib2_compliant); } void context_params::collect_param_descrs(param_descrs & d) { d.insert("timeout", CPK_UINT, "default timeout (in milliseconds) used for solvers", "4294967295"); d.insert("well_sorted_check", CPK_BOOL, "type checker", "true"); d.insert("type_check", CPK_BOOL, "type checker (alias for well_sorted_check)", "true"); d.insert("auto_config", CPK_BOOL, "use heuristics to automatically select solver and configure it", "true"); d.insert("model_validate", CPK_BOOL, "validate models produced by solvers", "false"); d.insert("trace", CPK_BOOL, "trace generation for VCC", "false"); d.insert("trace_file_name", CPK_STRING, "trace out file name (see option 'trace')", "z3.log"); d.insert("debug_ref_count", CPK_BOOL, "debug support for AST reference counting", "false"); d.insert("smtlib2_compliant", CPK_BOOL, "enable/disable SMT-LIB 2.0 compliance", "false"); collect_solver_param_descrs(d); } void context_params::collect_solver_param_descrs(param_descrs & d) { d.insert("proof", CPK_BOOL, "proof generation, it must be enabled when the Z3 context is created", "false"); d.insert("model", CPK_BOOL, "model generation for solvers, this parameter can be overwritten when creating a solver", "true"); d.insert("unsat_core", CPK_BOOL, "unsat-core generation for solvers, this parameter can be overwritten when creating a solver, not every solver in Z3 supports unsat core generation", "false"); } params_ref context_params::merge_default_params(params_ref const & p) { if (!m_auto_config && !p.contains("auto_config")) { params_ref new_p = p; new_p.set_bool("auto_config", false); return new_p; } else { return p; } } void context_params::get_solver_params(ast_manager const & m, params_ref & p, bool & proofs_enabled, bool & models_enabled, bool & unsat_core_enabled) { proofs_enabled = m.proofs_enabled() && p.get_bool("proof", m_proof); models_enabled = p.get_bool("model", m_model); unsat_core_enabled = p.get_bool("unsat_core", m_unsat_core); p = merge_default_params(p); } ast_manager * context_params::mk_ast_manager() { ast_manager * r = alloc(ast_manager, m_proof ? PGM_FINE : PGM_DISABLED, m_trace ? m_trace_file_name.c_str() : 0); if (m_smtlib2_compliant) r->enable_int_real_coercions(false); if (m_debug_ref_count) r->debug_ref_count(); return r; } z3-4.4.0/src/cmd_context/cmd_util.h0000644000175000017500000000703512540347414016721 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cmd_util.h Abstract: Macros for definining new SMT2 front-end cmds. Author: Leonardo (leonardo) 2011-04-01 Notes: --*/ #ifndef _CMD_UTIL_H_ #define _CMD_UTIL_H_ #define ATOMIC_CMD(CLS_NAME, NAME, DESCR, ACTION) \ class CLS_NAME : public cmd { \ public: \ CLS_NAME():cmd(NAME) {} \ virtual char const * get_usage() const { return 0; } \ virtual char const * get_descr(cmd_context & ctx) const { return DESCR; } \ virtual unsigned get_arity() const { return 0; } \ virtual void execute(cmd_context & ctx) { ACTION } \ }; #define UNARY_CMD(CLS_NAME, NAME, USAGE, DESCR, ARG_KIND, ARG_TYPE, ACTION) \ class CLS_NAME : public cmd { \ public: \ CLS_NAME():cmd(NAME) {} \ virtual char const * get_usage() const { return USAGE; } \ virtual char const * get_descr(cmd_context & ctx) const { return DESCR; } \ virtual unsigned get_arity() const { return 1; } \ virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return ARG_KIND; } \ virtual void set_next_arg(cmd_context & ctx, ARG_TYPE arg) { ACTION } \ } // Macro for creating commands where the first argument is a symbol // The second argument cannot be a symbol #define BINARY_SYM_CMD(CLS_NAME, NAME, USAGE, DESCR, ARG_KIND, ARG_TYPE, ACTION) \ class CLS_NAME : public cmd { \ symbol m_sym; \ public: \ CLS_NAME():cmd(NAME) {} \ virtual char const * get_usage() const { return USAGE; } \ virtual char const * get_descr(cmd_context & ctx) const { return DESCR; } \ virtual unsigned get_arity() const { return 2; } \ virtual void prepare(cmd_context & ctx) { m_sym = symbol::null; } \ virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { \ return m_sym == symbol::null ? CPK_SYMBOL : ARG_KIND; \ } \ virtual void set_next_arg(cmd_context & ctx, symbol const & s) { m_sym = s; } \ virtual void set_next_arg(cmd_context & ctx, ARG_TYPE arg) { ACTION } \ }; class ast; class expr; class symbol; class cmd_context; /** \brief Return the AST that is referenced by the global variable v in the given command context. */ ast * get_ast_ref(cmd_context & ctx, symbol const & v); /** \brief Return the expression that is referenced by the global variable v in the given command context. */ expr * get_expr_ref(cmd_context & ctx, symbol const & v); /** \brief Store t in the global variable v. */ void store_expr_ref(cmd_context & ctx, symbol const & v, expr * t); #endif z3-4.4.0/src/cmd_context/context_params.h0000644000175000017500000000347412540347414020153 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: context_params.h Abstract: Goodies for managing context parameters in the cmd_context and api_context Author: Leonardo (leonardo) 2012-12-01 Notes: --*/ #ifndef _CONTEXT_PARAMS_H_ #define _CONTEXT_PARAMS_H_ #include"params.h" class ast_manager; class context_params { void set_bool(bool & opt, char const * param, char const * value); public: bool m_auto_config; bool m_proof; bool m_interpolants; bool m_debug_ref_count; bool m_trace; std::string m_trace_file_name; bool m_well_sorted_check; bool m_model; bool m_model_validate; bool m_unsat_core; bool m_smtlib2_compliant; // it must be here because it enable/disable the use of coercions in the ast_manager. unsigned m_timeout; context_params(); void set(char const * param, char const * value); void updt_params(); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); /* REG_PARAMS('context_params::collect_param_descrs') */ /** \brief Goodies for extracting parameters for creating a solver object. */ void get_solver_params(ast_manager const & m, params_ref & p, bool & proofs_enabled, bool & models_enabled, bool & unsat_core_enabled); static void collect_solver_param_descrs(param_descrs & d); /** \brief Include in p parameters derived from this context_params. These are parameters that are meaningful for tactics and solvers. Example: auto_config */ params_ref merge_default_params(params_ref const & p); /** \brief Create an AST manager using this configuration. */ ast_manager * mk_ast_manager(); }; #endif z3-4.4.0/src/cmd_context/cmd_context_to_goal.h0000644000175000017500000000056712540347414021137 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: cmd_context_to_goal.h Abstract: Procedure for copying the assertions in the command context to a goal object. Author: Leonardo (leonardo) 2012-10-21 Notes: --*/ #ifndef _CMD_CONTEXT_TO_GOAL_H_ #define _CMD_CONTEXT_TO_GOAL_H_ void assert_exprs_from(cmd_context const & ctx, goal & t); #endif z3-4.4.0/src/cmd_context/basic_cmds.h0000644000175000017500000000054212540347414017204 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: basic_cmds.h Abstract: Basic commands for SMT2 front end. Author: Leonardo (leonardo) 2011-03-30 Notes: --*/ #ifndef _BASIC_CMDS_H_ #define _BASIC_CMDS_H_ class cmd_context; void install_basic_cmds(cmd_context & ctx); void install_ext_basic_cmds(cmd_context & ctx); #endif z3-4.4.0/src/cmd_context/tactic_cmds.cpp0000644000175000017500000010560012540347414017726 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: tactic_cmds.cpp Abstract: Support for tactics in SMT 2.0 frontend. Author: Leonardo (leonardo) 2011-10-20 Notes: --*/ #include #include"tactic_cmds.h" #include"cmd_context.h" #include"cmd_util.h" #include"parametric_cmd.h" #include"scoped_timer.h" #include"scoped_ctrl_c.h" #include"cancel_eh.h" #include"model_smt2_pp.h" #include"ast_smt2_pp.h" #include"tactic.h" #include"tactical.h" #include"probe.h" #include"check_sat_result.h" #include"cmd_context_to_goal.h" #include"echo_tactic.h" tactic_cmd::~tactic_cmd() { dealloc(m_factory); } tactic * tactic_cmd::mk(ast_manager & m) { return (*m_factory)(m, params_ref()); } probe_info::probe_info(symbol const & n, char const * d, probe * p): m_name(n), m_descr(d), m_probe(p) { } probe_info::~probe_info() { } class declare_tactic_cmd : public cmd { symbol m_name; sexpr * m_decl; public: declare_tactic_cmd(): cmd("declare-tactic"), m_decl(0) { } virtual char const * get_usage() const { return " "; } virtual char const * get_descr(cmd_context & ctx) const { return "declare a new tactic, use (help-tactic) for the tactic language syntax."; } virtual unsigned get_arity() const { return 2; } virtual void prepare(cmd_context & ctx) { m_name = symbol::null; m_decl = 0; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { if (m_name == symbol::null) return CPK_SYMBOL; return CPK_SEXPR; } virtual void set_next_arg(cmd_context & ctx, symbol const & s) { m_name = s; } virtual void set_next_arg(cmd_context & ctx, sexpr * n) { m_decl = n; } virtual void execute(cmd_context & ctx) { tactic_ref t = sexpr2tactic(ctx, m_decl); // make sure the tactic is well formed. ctx.insert_user_tactic(m_name, m_decl); } }; ATOMIC_CMD(get_user_tactics_cmd, "get-user-tactics", "display tactics defined using the declare-tactic command.", { ctx.regular_stream() << "("; std::ostringstream buf; cmd_context::user_tactic_iterator it = ctx.begin_user_tactics(); cmd_context::user_tactic_iterator end = ctx.end_user_tactics(); for (bool first = true; it != end; ++it) { if (first) first = false; else buf << "\n "; buf << "(declare-tactic " << it->m_key << " "; it->m_value->display(buf); buf << ")"; } std::string r = buf.str(); ctx.regular_stream() << escaped(r.c_str()); ctx.regular_stream() << ")\n"; }); void help_tactic(cmd_context & ctx) { std::ostringstream buf; buf << "combinators:\n"; buf << "- (and-then +) executes the given tactics sequencially.\n"; buf << "- (or-else +) tries the given tactics in sequence until one of them succeeds.\n"; buf << "- (par-or +) executes the given tactics in parallel until one of them succeeds.\n"; buf << "- (par-then ) executes tactic1 and then tactic2 to every subgoal produced by tactic1. All subgoals are processed in parallel.\n"; buf << "- (try-for ) excutes the given tactic for at most milliseconds, it fails if the execution takes more than milliseconds.\n"; buf << "- (if ) if evaluates to true, then execute the first tactic. Otherwise execute the second.\n"; buf << "- (when ) shorthand for (if skip).\n"; buf << "- (fail-if ) fail if evaluates to true.\n"; buf << "- (using-params *) executes the given tactic using the given attributes, where ::= . ! is a syntax sugar for using-params.\n"; buf << "builtin tactics:\n"; cmd_context::tactic_cmd_iterator it = ctx.begin_tactic_cmds(); cmd_context::tactic_cmd_iterator end = ctx.end_tactic_cmds(); for (; it != end; ++it) { tactic_cmd * cmd = *it; buf << "- " << cmd->get_name() << " " << cmd->get_descr() << "\n"; tactic_ref t = cmd->mk(ctx.m()); param_descrs descrs; t->collect_param_descrs(descrs); descrs.display(buf, 4); } buf << "builtin probes:\n"; cmd_context::probe_iterator it2 = ctx.begin_probes(); cmd_context::probe_iterator end2 = ctx.end_probes(); for (; it2 != end2; ++it2) { probe_info * pinfo = *it2; buf << "- " << pinfo->get_name() << " " << pinfo->get_descr() << "\n"; } ctx.regular_stream() << "\"" << escaped(buf.str().c_str()) << "\"\n"; } ATOMIC_CMD(help_tactic_cmd, "help-tactic", "display the tactic combinators and primitives.", help_tactic(ctx);); class exec_given_tactic_cmd : public parametric_cmd { protected: sexpr * m_tactic; public: exec_given_tactic_cmd(char const * name): parametric_cmd(name) { } virtual char const * get_usage() const { return " ( )*"; } virtual void prepare(cmd_context & ctx) { parametric_cmd::prepare(ctx); m_tactic = 0; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { if (m_tactic == 0) return CPK_SEXPR; return parametric_cmd::next_arg_kind(ctx); } virtual void set_next_arg(cmd_context & ctx, sexpr * arg) { m_tactic = arg; } virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { insert_timeout(p); insert_max_memory(p); p.insert("print_statistics", CPK_BOOL, "(default: false) print statistics."); } void display_statistics(cmd_context & ctx, tactic * t) { statistics stats; unsigned long long max_mem = memory::get_max_used_memory(); unsigned long long mem = memory::get_allocation_size(); stats.update("time", ctx.get_seconds()); stats.update("memory", static_cast(mem)/static_cast(1024*1024)); stats.update("max memory", static_cast(max_mem)/static_cast(1024*1024)); t->collect_statistics(stats); stats.display_smt2(ctx.regular_stream()); } }; typedef simple_check_sat_result check_sat_tactic_result; class check_sat_using_tactict_cmd : public exec_given_tactic_cmd { public: check_sat_using_tactict_cmd(): exec_given_tactic_cmd("check-sat-using") { } virtual char const * get_main_descr() const { return "check if the current context is satisfiable using the given tactic, use (help-tactic) for the tactic language syntax."; } virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { exec_given_tactic_cmd::init_pdescrs(ctx, p); p.insert("print_unsat_core", CPK_BOOL, "(default: false) print unsatisfiable core."); p.insert("print_proof", CPK_BOOL, "(default: false) print proof."); p.insert("print_model", CPK_BOOL, "(default: false) print model."); } virtual void execute(cmd_context & ctx) { params_ref p = ctx.params().merge_default_params(ps()); tactic_ref tref = using_params(sexpr2tactic(ctx, m_tactic), p); tref->set_logic(ctx.get_logic()); ast_manager & m = ctx.m(); unsigned timeout = p.get_uint("timeout", UINT_MAX); goal_ref g = alloc(goal, m, ctx.produce_proofs(), ctx.produce_models(), ctx.produce_unsat_cores()); assert_exprs_from(ctx, *g); TRACE("check_sat_using", g->display(tout);); model_ref md; proof_ref pr(m); expr_dependency_ref core(m); std::string reason_unknown; ref result = alloc(check_sat_tactic_result, m); ctx.set_check_sat_result(result.get()); { tactic & t = *tref; cancel_eh eh(t); { scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); cmd_context::scoped_watch sw(ctx); lbool r = l_undef; try { r = check_sat(t, g, md, pr, core, reason_unknown); ctx.display_sat_result(r); result->set_status(r); if (r == l_undef) { if (reason_unknown != "") { result->m_unknown = reason_unknown; // ctx.diagnostic_stream() << "\"" << escaped(reason_unknown.c_str(), true) << "\"" << std::endl; } else { result->m_unknown = "unknown"; } } } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { result->set_status(l_undef); result->m_unknown = ex.msg(); ctx.regular_stream() << "(error \"tactic failed: " << ex.msg() << "\")" << std::endl; } ctx.validate_check_sat_result(r); } t.collect_statistics(result->m_stats); } if (ctx.produce_unsat_cores()) { ptr_vector core_elems; m.linearize(core, core_elems); result->m_core.append(core_elems.size(), core_elems.c_ptr()); if (p.get_bool("print_unsat_core", false)) { ctx.regular_stream() << "(unsat-core"; ptr_vector::const_iterator it = core_elems.begin(); ptr_vector::const_iterator end = core_elems.end(); for (; it != end; ++it) { ctx.regular_stream() << " "; ctx.display(ctx.regular_stream(), *it); } ctx.regular_stream() << ")" << std::endl; } } if (ctx.produce_models() && md) { result->m_model = md; if (p.get_bool("print_model", false)) { ctx.regular_stream() << "(model " << std::endl; model_smt2_pp(ctx.regular_stream(), ctx, *md, 2); ctx.regular_stream() << ")" << std::endl; } if (result->status() == l_true) ctx.validate_model(); } if (ctx.produce_proofs() && pr) { result->m_proof = pr; if (p.get_bool("print_proof", false)) { ctx.regular_stream() << mk_ismt2_pp(pr, m) << "\n"; } } if (p.get_bool("print_statistics", false)) display_statistics(ctx, tref.get()); } }; class apply_tactic_cmd : public exec_given_tactic_cmd { public: apply_tactic_cmd(): exec_given_tactic_cmd("apply") { } virtual char const * get_main_descr() const { return "apply the given tactic to the current context, and print the resultant set of goals."; } virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { p.insert("print", CPK_BOOL, "(default: true) print resultant goals."); #ifndef _EXTERNAL_RELEASE p.insert("print_proof", CPK_BOOL, "(default: false) print proof associated with each assertion."); #endif p.insert("print_model_converter", CPK_BOOL, "(default: false) print model converter."); p.insert("print_benchmark", CPK_BOOL, "(default: false) display resultant goals as a SMT2 benchmark."); #ifndef _EXTERNAL_RELEASE p.insert("print_dependencies", CPK_BOOL, "(default: false) print dependencies when displaying the resultant set of goals."); #endif exec_given_tactic_cmd::init_pdescrs(ctx, p); } virtual void execute(cmd_context & ctx) { params_ref p = ctx.params().merge_default_params(ps()); tactic_ref tref = using_params(sexpr2tactic(ctx, m_tactic), p); { tactic & t = *(tref.get()); ast_manager & m = ctx.m(); goal_ref g = alloc(goal, m, ctx.produce_proofs(), ctx.produce_models(), ctx.produce_unsat_cores()); assert_exprs_from(ctx, *g); unsigned timeout = p.get_uint("timeout", UINT_MAX); goal_ref_buffer result_goals; model_converter_ref mc; proof_converter_ref pc; expr_dependency_ref core(m); std::string reason_unknown; bool failed = false; cancel_eh eh(t); { scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); cmd_context::scoped_watch sw(ctx); try { exec(t, g, result_goals, mc, pc, core); } catch (tactic_exception & ex) { ctx.regular_stream() << "(error \"tactic failed: " << ex.msg() << "\")" << std::endl; failed = true; } } if (!failed && p.get_bool("print", true)) { bool print_dependencies = p.get_bool("print_dependencies", false); ctx.regular_stream() << "(goals\n"; unsigned sz = result_goals.size(); for (unsigned i = 0; i < sz; i++) { if (print_dependencies) result_goals[i]->display_with_dependencies(ctx); else result_goals[i]->display(ctx); } ctx.regular_stream() << ")\n"; } #ifndef _EXTERNAL_RELEASE if (!failed && ctx.produce_proofs() && p.get_bool("print_proof", false)) { // TODO } #endif if (!failed && p.get_bool("print_benchmark", false)) { unsigned num_goals = result_goals.size(); SASSERT(num_goals > 0); if (num_goals == 1) { // easy case goal * fg = result_goals[0]; unsigned sz = fg->size(); ptr_buffer assertions; for (unsigned i = 0; i < sz; i++) { assertions.push_back(fg->form(i)); } ctx.display_smt2_benchmark(ctx.regular_stream(), assertions.size(), assertions.c_ptr()); } else { // create a big OR expr_ref_buffer or_args(m); ptr_vector formulas; for (unsigned i = 0; i < num_goals; i++) { formulas.reset(); result_goals[i]->get_formulas(formulas); if (formulas.size() == 1) or_args.push_back(formulas[0]); else or_args.push_back(m.mk_and(formulas.size(), formulas.c_ptr())); } expr_ref assertion_ref(m); assertion_ref = m.mk_or(or_args.size(), or_args.c_ptr()); expr * assertions[1] = { assertion_ref.get() }; ctx.display_smt2_benchmark(ctx.regular_stream(), 1, assertions); } } if (!failed && mc && p.get_bool("print_model_converter", false)) mc->display(ctx.regular_stream()); if (p.get_bool("print_statistics", false)) display_statistics(ctx, tref.get()); } } }; // The install_tactics procedure is automatically generated for every // component that includes the cmd_context & tactic modules. void install_tactics(tactic_manager & ctx); void install_core_tactic_cmds(cmd_context & ctx) { ctx.insert(alloc(declare_tactic_cmd)); ctx.insert(alloc(get_user_tactics_cmd)); ctx.insert(alloc(help_tactic_cmd)); ctx.insert(alloc(check_sat_using_tactict_cmd)); ctx.insert(alloc(apply_tactic_cmd)); install_tactics(ctx); } static tactic * mk_and_then(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children < 2) throw cmd_exception("invalid and-then combinator, at least one argument expected", n->get_line(), n->get_pos()); if (num_children == 2) return sexpr2tactic(ctx, n->get_child(1)); tactic_ref_buffer args; for (unsigned i = 1; i < num_children; i++) args.push_back(sexpr2tactic(ctx, n->get_child(i))); return and_then(args.size(), args.c_ptr()); } static tactic * mk_or_else(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children < 2) throw cmd_exception("invalid or-else combinator, at least one argument expected", n->get_line(), n->get_pos()); if (num_children == 2) return sexpr2tactic(ctx, n->get_child(1)); tactic_ref_buffer args; for (unsigned i = 1; i < num_children; i++) args.push_back(sexpr2tactic(ctx, n->get_child(i))); return or_else(args.size(), args.c_ptr()); } static tactic * mk_par(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children < 2) throw cmd_exception("invalid par-or combinator, at least one argument expected", n->get_line(), n->get_pos()); if (num_children == 2) return sexpr2tactic(ctx, n->get_child(1)); tactic_ref_buffer args; for (unsigned i = 1; i < num_children; i++) args.push_back(sexpr2tactic(ctx, n->get_child(i))); return par(args.size(), args.c_ptr()); } static tactic * mk_par_then(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children < 2) throw cmd_exception("invalid par-then combinator, at least one argument expected", n->get_line(), n->get_pos()); if (num_children == 2) return sexpr2tactic(ctx, n->get_child(1)); tactic_ref_buffer args; for (unsigned i = 1; i < num_children; i++) args.push_back(sexpr2tactic(ctx, n->get_child(i))); return par_and_then(args.size(), args.c_ptr()); } static tactic * mk_try_for(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 3) throw cmd_exception("invalid try-for combinator, two arguments expected", n->get_line(), n->get_pos()); if (!n->get_child(2)->is_numeral() || !n->get_child(2)->get_numeral().is_unsigned()) throw cmd_exception("invalid try-for combinator, second argument must be an unsigned integer", n->get_line(), n->get_pos()); tactic * t = sexpr2tactic(ctx, n->get_child(1)); unsigned timeout = n->get_child(2)->get_numeral().get_unsigned(); return try_for(t, timeout); } static tactic * mk_repeat(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 3 && num_children != 2) throw cmd_exception("invalid repeat combinator, one or two arguments expected", n->get_line(), n->get_pos()); unsigned max = UINT_MAX; if (num_children == 3) { if (!n->get_child(2)->is_numeral() || !n->get_child(2)->get_numeral().is_unsigned()) throw cmd_exception("invalid repeat combinator, second argument must be an unsigned integer", n->get_line(), n->get_pos()); max = n->get_child(2)->get_numeral().get_unsigned(); } tactic * t = sexpr2tactic(ctx, n->get_child(1)); return repeat(t, max); } static tactic * mk_using_params(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children < 2) throw cmd_exception("invalid using-params combinator, at least one argument expected", n->get_line(), n->get_pos()); if (num_children == 2) return sexpr2tactic(ctx, n->get_child(1)); tactic_ref t = sexpr2tactic(ctx, n->get_child(1)); param_descrs descrs; t->collect_param_descrs(descrs); params_ref p; unsigned i = 2; while (i < num_children) { sexpr * c = n->get_child(i); i++; if (!c->is_keyword()) throw cmd_exception("invalid using-params combinator, keyword expected", c->get_line(), c->get_pos()); if (i == num_children) throw cmd_exception("invalid using-params combinator, parameter value expected", c->get_line(), c->get_pos()); symbol param_name = symbol(norm_param_name(c->get_symbol()).c_str()); c = n->get_child(i); i++; switch (descrs.get_kind_in_module(param_name)) { case CPK_INVALID: throw cmd_exception("invalid using-params combinator, unknown parameter ", param_name, c->get_line(), c->get_pos()); case CPK_BOOL: if (!c->is_symbol() || (c->get_symbol() != "true" && c->get_symbol() != "false")) throw cmd_exception("invalid parameter value, true or false expected", c->get_line(), c->get_pos()); p.set_bool(param_name, c->get_symbol() == "true"); break; case CPK_UINT: if (!c->is_numeral() || !c->get_numeral().is_unsigned()) throw cmd_exception("invalid parameter value, unsigned integer expected", c->get_line(), c->get_pos()); p.set_uint(param_name, c->get_numeral().get_unsigned()); break; case CPK_NUMERAL: if (!c->is_numeral()) throw cmd_exception("invalid parameter value, numeral expected", c->get_line(), c->get_pos()); p.set_rat(param_name, c->get_numeral()); break; case CPK_SYMBOL: if (!c->is_symbol()) throw cmd_exception("invalid parameter value, symbol expected", c->get_line(), c->get_pos()); p.set_sym(param_name, c->get_symbol()); break; case CPK_DOUBLE: if (!c->is_numeral()) throw cmd_exception("invalid parameter value, numeral expected", c->get_line(), c->get_pos()); p.set_double(param_name, c->get_numeral().get_double()); break; default: throw cmd_exception("invalid using-params combinator, unsupported parameter kind"); } } return using_params(t.get(), p); } static tactic * mk_if(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 4) throw cmd_exception("invalid if/conditional combinator, three arguments expected", n->get_line(), n->get_pos()); probe_ref c = sexpr2probe(ctx, n->get_child(1)); tactic_ref t = sexpr2tactic(ctx, n->get_child(2)); tactic_ref e = sexpr2tactic(ctx, n->get_child(3)); return cond(c.get(), t.get(), e.get()); } static tactic * mk_fail_if(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 2) throw cmd_exception("invalid fail-if tactic, one argument expected", n->get_line(), n->get_pos()); probe_ref c = sexpr2probe(ctx, n->get_child(1)); return fail_if(c.get()); } static tactic * mk_when(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 3) throw cmd_exception("invalid when combinator, two arguments expected", n->get_line(), n->get_pos()); probe_ref c = sexpr2probe(ctx, n->get_child(1)); tactic_ref t = sexpr2tactic(ctx, n->get_child(2)); return cond(c.get(), t.get(), mk_skip_tactic()); } static tactic * mk_echo(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children < 2) throw cmd_exception("invalid echo tactic, must have at least one argument", n->get_line(), n->get_pos()); tactic_ref res; for (unsigned i = 1; i < num_children; i++) { sexpr * curr = n->get_child(i); bool last = (i == num_children - 1); tactic * t; if (curr->is_string()) t = mk_echo_tactic(ctx, curr->get_string().c_str(), last); else t = mk_probe_value_tactic(ctx, 0, sexpr2probe(ctx, curr), last); tactic * new_res; if (res.get() == 0) new_res = t; else new_res = and_then(res.get(), t); if (last) return new_res; res = new_res; } UNREACHABLE(); return 0; } static tactic * mk_fail_if_branching(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 3 && num_children != 2) throw cmd_exception("invalid fail-if-branching combinator, one or two arguments expected", n->get_line(), n->get_pos()); unsigned threshold = 1; if (num_children == 3) { if (!n->get_child(2)->is_numeral() || !n->get_child(2)->get_numeral().is_unsigned()) throw cmd_exception("invalid fail-if-branching combinator, second argument must be an unsigned integer", n->get_line(), n->get_pos()); threshold = n->get_child(2)->get_numeral().get_unsigned(); } tactic * t = sexpr2tactic(ctx, n->get_child(1)); return fail_if_branching(t, threshold); } static tactic * mk_if_no_proofs(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 2) throw cmd_exception("invalid if-no-proofs combinator, one argument expected", n->get_line(), n->get_pos()); tactic * t = sexpr2tactic(ctx, n->get_child(1)); return if_no_proofs(t); } static tactic * mk_if_no_models(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 2) throw cmd_exception("invalid if-no-models combinator, one argument expected", n->get_line(), n->get_pos()); tactic * t = sexpr2tactic(ctx, n->get_child(1)); return if_no_models(t); } static tactic * mk_if_no_unsat_cores(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 2) throw cmd_exception("invalid if-no-unsat-cores combinator, one argument expected", n->get_line(), n->get_pos()); tactic * t = sexpr2tactic(ctx, n->get_child(1)); return if_no_unsat_cores(t); } static tactic * mk_skip_if_failed(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 2) throw cmd_exception("invalid skip-if-failed combinator, one argument expected", n->get_line(), n->get_pos()); tactic * t = sexpr2tactic(ctx, n->get_child(1)); return skip_if_failed(t); } tactic * sexpr2tactic(cmd_context & ctx, sexpr * n) { if (n->is_symbol()) { tactic_cmd * cmd = ctx.find_tactic_cmd(n->get_symbol()); if (cmd != 0) return cmd->mk(ctx.m()); sexpr * decl = ctx.find_user_tactic(n->get_symbol()); if (decl != 0) return sexpr2tactic(ctx, decl); throw cmd_exception("invalid tactic, unknown tactic ", n->get_symbol(), n->get_line(), n->get_pos()); } else if (n->is_composite()) { unsigned num_children = n->get_num_children(); if (num_children == 0) throw cmd_exception("invalid tactic, arguments expected", n->get_line(), n->get_pos()); sexpr * head = n->get_child(0); if (!head->is_symbol()) throw cmd_exception("invalid tactic, symbol expected", n->get_line(), n->get_pos()); symbol const & cmd_name = head->get_symbol(); if (cmd_name == "and-then" || cmd_name == "then") return mk_and_then(ctx, n); else if (cmd_name == "or-else") return mk_or_else(ctx, n); else if (cmd_name == "par") return mk_par(ctx, n); else if (cmd_name == "par-or") return mk_par(ctx, n); else if (cmd_name == "par-then") return mk_par_then(ctx, n); else if (cmd_name == "try-for") return mk_try_for(ctx, n); else if (cmd_name == "repeat") return mk_repeat(ctx, n); else if (cmd_name == "if" || cmd_name == "ite" || cmd_name == "cond") return mk_if(ctx, n); else if (cmd_name == "fail-if") return mk_fail_if(ctx, n); else if (cmd_name == "fail-if-branching") return mk_fail_if_branching(ctx, n); else if (cmd_name == "when") return mk_when(ctx, n); else if (cmd_name == "!" || cmd_name == "using-params" || cmd_name == "with") return mk_using_params(ctx, n); else if (cmd_name == "echo") return mk_echo(ctx, n); else if (cmd_name == "if-no-proofs") return mk_if_no_proofs(ctx, n); else if (cmd_name == "if-no-models") return mk_if_no_models(ctx, n); else if (cmd_name == "if-no-unsat-cores") return mk_if_no_unsat_cores(ctx, n); else if (cmd_name == "skip-if-failed") return mk_skip_if_failed(ctx, n); else throw cmd_exception("invalid tactic, unknown tactic combinator ", cmd_name, n->get_line(), n->get_pos()); } else { throw cmd_exception("invalid tactic, unexpected input", n->get_line(), n->get_pos()); } } static probe * mk_not_probe (cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 2) throw cmd_exception("invalid probe expression, one argument expected", n->get_line(), n->get_pos()); return mk_not(sexpr2probe(ctx, n->get_child(1))); } #define MK_BIN_PROBE(NAME) \ static probe * NAME ## _probe (cmd_context & ctx, sexpr * n) { \ SASSERT(n->is_composite()); \ unsigned num_children = n->get_num_children(); \ if (num_children != 3) \ throw cmd_exception("invalid probe expression, two arguments expected", n->get_line(), n->get_pos()); \ ref p1 = sexpr2probe(ctx, n->get_child(1)); \ ref p2 = sexpr2probe(ctx, n->get_child(2)); \ return NAME(p1.get(), p2.get()); \ } MK_BIN_PROBE(mk_eq); MK_BIN_PROBE(mk_le); MK_BIN_PROBE(mk_lt); MK_BIN_PROBE(mk_ge); MK_BIN_PROBE(mk_gt); MK_BIN_PROBE(mk_implies); MK_BIN_PROBE(mk_div); MK_BIN_PROBE(mk_sub); #define MK_NARY_PROBE(NAME) \ static probe * NAME ## _probe(cmd_context & ctx, sexpr * n) { \ SASSERT(n->is_composite()); \ unsigned num_children = n->get_num_children(); \ if (num_children < 2) \ throw cmd_exception("invalid probe, at least one argument expected", n->get_line(), n->get_pos()); \ probe * r = sexpr2probe(ctx, n->get_child(1)); \ if (num_children == 2) \ return r; \ ref prev = r; \ unsigned i = 1; \ while (true) { \ r = NAME(prev.get(), sexpr2probe(ctx, n->get_child(i))); \ if (i == num_children - 1) \ return r; \ i++; \ prev = r; \ } \ } MK_NARY_PROBE(mk_and); MK_NARY_PROBE(mk_or); MK_NARY_PROBE(mk_add); MK_NARY_PROBE(mk_mul); probe * sexpr2probe(cmd_context & ctx, sexpr * n) { if (n->is_symbol()) { probe_info * pinfo = ctx.find_probe(n->get_symbol()); if (pinfo != 0) return pinfo->get(); throw cmd_exception("invalid probe, unknown builtin probe ", n->get_symbol(), n->get_line(), n->get_pos()); } else if (n->is_numeral()) { rational const & v = n->get_numeral(); if (!v.is_int32()) throw cmd_exception("invalid probe, constant is too big to fit in a fixed size integer", n->get_line(), n->get_pos()); return mk_const_probe(static_cast(v.get_int64())); } else if (n->is_composite()) { unsigned num_children = n->get_num_children(); if (num_children == 0) throw cmd_exception("invalid probe, arguments expected", n->get_line(), n->get_pos()); sexpr * head = n->get_child(0); if (!head->is_symbol()) throw cmd_exception("invalid probe, symbol expected", n->get_line(), n->get_pos()); symbol const & p_name = head->get_symbol(); if (p_name == "=") return mk_eq_probe(ctx, n); else if (p_name == "<=") return mk_le_probe(ctx, n); else if (p_name == ">=") return mk_ge_probe(ctx, n); else if (p_name == "<") return mk_lt_probe(ctx, n); else if (p_name == ">") return mk_gt_probe(ctx, n); else if (p_name == "and") return mk_and_probe(ctx, n); else if (p_name == "or") return mk_or_probe(ctx, n); else if (p_name == "=>" || p_name == "implies") return mk_implies_probe(ctx, n); else if (p_name == "not") return mk_not_probe(ctx, n); else if (p_name == "*") return mk_mul_probe(ctx, n); else if (p_name == "+") return mk_add_probe(ctx, n); else if (p_name == "-") return mk_sub_probe(ctx, n); else if (p_name == "/") return mk_div_probe(ctx, n); else throw cmd_exception("invalid probe, unknown probe expression ", p_name, n->get_line(), n->get_pos()); } else { throw cmd_exception("invalid probe, unexpected input", n->get_line(), n->get_pos()); } } z3-4.4.0/src/cmd_context/pdecl.cpp0000644000175000017500000010042712540347414016542 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdecl.cpp Abstract: Parametric declarations for SMT-LIB 2.0 + inductive data-types. Author: Leonardo de Moura (leonardo) 2011-03-02. Revision History: --*/ #include"pdecl.h" #include"datatype_decl_plugin.h" using namespace format_ns; class psort_inst_cache { unsigned m_num_params; sort * m_const; obj_map m_map; // if m_num_params == 1 value is a sort, otherwise it is a reference to another inst_cache public: psort_inst_cache(unsigned num_params):m_num_params(num_params), m_const(0) { } ~psort_inst_cache() { SASSERT(m_map.empty()); SASSERT(m_const == 0); } void finalize(pdecl_manager & m) { if (m_num_params == 0) { SASSERT(m_map.empty()); if (m_const) m.m().dec_ref(m_const); m_const = 0; } else { SASSERT(m_const == 0); obj_map::iterator it = m_map.begin(); obj_map::iterator end = m_map.end(); for (; it != end; ++it) { m.m().dec_ref((*it).m_key); if (m_num_params == 1) { m.m().dec_ref(static_cast((*it).m_value)); } else { psort_inst_cache * child = static_cast((*it).m_value); child->finalize(m); child->~psort_inst_cache(); m.a().deallocate(sizeof(psort_inst_cache), child); } } m_map.reset(); } } void insert(pdecl_manager & m, sort * const * s, sort * r) { if (m_num_params == 0) { SASSERT(m_const == 0); m.m().inc_ref(r); m_const = r; return; } psort_inst_cache * curr = this; while (true) { if (curr->m_num_params == 1) { SASSERT(!curr->m_map.contains(*s)); curr->m_map.insert(*s, r); m.m().inc_ref(*s); m.m().inc_ref(r); return; } void * next = 0; if (!curr->m_map.find(*s, next)) { next = new (m.a().allocate(sizeof(psort_inst_cache))) psort_inst_cache(curr->m_num_params-1); curr->m_map.insert(*s, next); m.m().inc_ref(*s); } SASSERT(next != 0); SASSERT(curr->m_num_params == static_cast(next)->m_num_params + 1); s++; curr = static_cast(next); } } sort * find(sort * const * s) const { if (m_num_params == 0) return m_const; psort_inst_cache const * curr = this; while (true) { if (curr->m_num_params == 1) { void * r = 0; curr->m_map.find(*s, r); return static_cast(r); } else { void * next = 0; curr->m_map.find(*s, next); if (next == 0) return 0; s++; curr = static_cast(next); } } } bool empty() const { return m_num_params == 0 ? m_const == 0 : m_map.empty(); } }; void psort::cache(pdecl_manager & m, sort * const * s, sort * r) { if (!m_inst_cache) m_inst_cache = m.mk_inst_cache(m_num_params); m_inst_cache->insert(m, s, r); } sort * psort::find(sort * const * s) const { if (!m_inst_cache) return 0; return m_inst_cache->find(s); } void psort::finalize(pdecl_manager & m) { m.del_inst_cache(m_inst_cache); m_inst_cache = 0; } /** \brief wrapper for sorts. */ class psort_sort : public psort { friend class pdecl_manager; sort * m_sort; psort_sort(unsigned id, pdecl_manager & m, sort * s):psort(id, 0), m_sort(s) { m.m().inc_ref(m_sort); } virtual void finalize(pdecl_manager & m) { m.m().dec_ref(m_sort); psort::finalize(m); } virtual bool check_num_params(pdecl * other) const { return true; } virtual size_t obj_size() const { return sizeof(psort_sort); } sort * get_sort() const { return m_sort; } virtual sort * instantiate(pdecl_manager & m, sort * const * s) { return m_sort; } public: virtual ~psort_sort() {} virtual bool is_sort_wrapper() const { return true; } virtual char const * hcons_kind() const { return "psort_sort"; } virtual unsigned hcons_hash() const { return m_sort->get_id(); } virtual bool hcons_eq(psort const * other) const { if (other->hcons_kind() != hcons_kind()) return false; return m_sort == static_cast(other)->m_sort; } virtual void display(std::ostream & out) const { out << m_sort->get_name(); } }; class psort_var : public psort { friend class pdecl_manager; unsigned m_idx; psort_var(unsigned id, unsigned num_params, unsigned idx):psort(id, num_params), m_idx(idx) { SASSERT(idx < num_params); } virtual sort * instantiate(pdecl_manager & m, sort * const * s) { return s[m_idx]; } virtual size_t obj_size() const { return sizeof(psort_var); } public: virtual ~psort_var() {} virtual char const * hcons_kind() const { return "psort_var"; } virtual unsigned hcons_hash() const { return hash_u_u(m_num_params, m_idx); } virtual bool hcons_eq(psort const * other) const { if (other->hcons_kind() != hcons_kind()) return false; return get_num_params() == other->get_num_params() && m_idx == static_cast(other)->m_idx; } virtual void display(std::ostream & out) const { out << "s_" << m_idx; } }; class psort_app : public psort { friend class pdecl_manager; psort_decl * m_decl; ptr_vector m_args; psort_app(unsigned id, unsigned num_params, pdecl_manager & m, psort_decl * d, unsigned num_args, psort * const * args): psort(id, num_params), m_decl(d), m_args(num_args, args) { m.inc_ref(d); m.inc_ref(num_args, args); SASSERT(num_args == m_decl->get_num_params() || m_decl->has_var_params()); DEBUG_CODE(if (num_args == num_params) { for (unsigned i = 0; i < num_params; i++) args[i]->check_num_params(this); }); } virtual void finalize(pdecl_manager & m) { m.lazy_dec_ref(m_decl); m.lazy_dec_ref(m_args.size(), m_args.c_ptr()); psort::finalize(m); } virtual size_t obj_size() const { return sizeof(psort_app); } struct khasher { unsigned operator()(psort_app const * d) const { return d->m_decl->hash(); } }; struct chasher { unsigned operator()(psort_app const * d, unsigned idx) const { return d->m_args[idx]->hash(); } }; virtual sort * instantiate(pdecl_manager & m, sort * const * s) { sort * r = find(s); if (r) return r; sort_ref_buffer args_i(m.m()); unsigned sz = m_args.size(); for (unsigned i = 0; i < sz; i++) { sort * a = m_args[i]->instantiate(m, s); args_i.push_back(a); } r = m_decl->instantiate(m, args_i.size(), args_i.c_ptr()); cache(m, s, r); return r; } public: virtual ~psort_app() {} virtual char const * hcons_kind() const { return "psort_app"; } virtual unsigned hcons_hash() const { return get_composite_hash(const_cast(this), m_args.size()); } virtual bool hcons_eq(psort const * other) const { if (other->hcons_kind() != hcons_kind()) return false; if (get_num_params() != other->get_num_params()) return false; psort_app const * _other = static_cast(other); if (m_decl != _other->m_decl) return false; SASSERT(m_args.size() == _other->m_args.size()); unsigned sz = m_args.size(); for (unsigned i = 0; i < sz; i++) { if (m_args[i] != _other->m_args[i]) return false; } return true; } virtual void display(std::ostream & out) const { if (m_args.empty()) { out << m_decl->get_name(); } else { out << "(" << m_decl->get_name(); unsigned sz = m_args.size(); for (unsigned i = 0; i < sz; i++) { out << " "; m_args[i]->display(out); } out << ")"; } } }; psort_decl::psort_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n): pdecl(id, num_params), m_name(n), m_inst_cache(0) { } void psort_decl::finalize(pdecl_manager & m) { m.del_inst_cache(m_inst_cache); m_inst_cache = 0; } void psort_decl::cache(pdecl_manager & m, sort * const * s, sort * r) { if (!m_inst_cache) m_inst_cache = m.mk_inst_cache(m_num_params); m_inst_cache->insert(m, s, r); } sort * psort_decl::find(sort * const * s) { if (!m_inst_cache) return 0; return m_inst_cache->find(s); } psort_user_decl::psort_user_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, psort * p): psort_decl(id, num_params, m, n), m_def(p) { m.inc_ref(p); SASSERT(p == 0 || num_params == p->get_num_params()); } void psort_user_decl::finalize(pdecl_manager & m) { m.dec_ref(m_def); m_def = 0; psort_decl::finalize(m); } sort * psort_user_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) { SASSERT(n == m_num_params); sort * r = find(s); if (r) return r; if (m_def == 0) { buffer ps; for (unsigned i = 0; i < n; i++) ps.push_back(parameter(s[i])); r = m.m().mk_uninterpreted_sort(m_name, ps.size(), ps.c_ptr()); } else { r = m_def->instantiate(m, s); } cache(m, s, r); m.save_info(r, this, n, s); return r; } void display_sort_args(std::ostream & out, unsigned num_params) { if (num_params > 0) out << " ("; for (unsigned i = 0; i < num_params; i++) { if (i > 0) out << " "; out << "s_" << i; } if (num_params > 0) out << ") "; } void psort_user_decl::display(std::ostream & out) const { out << "(declare-sort " << m_name; display_sort_args(out, m_num_params); if (m_def) m_def->display(out); out << ")"; } psort_builtin_decl::psort_builtin_decl(unsigned id, pdecl_manager & m, symbol const & n, family_id fid, decl_kind k): psort_decl(id, PSORT_DECL_VAR_PARAMS, m, n), m_fid(fid), m_kind(k) { } sort * psort_builtin_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) { if (n == 0) { sort * r = m.m().mk_sort(m_fid, m_kind); m.save_info(r, this, 0, s); return r; } else { buffer params; for (unsigned i = 0; i < n; i++) params.push_back(parameter(s[i])); sort * r = m.m().mk_sort(m_fid, m_kind, n, params.c_ptr()); m.save_info(r, this, n, s); return r; } } sort * psort_builtin_decl::instantiate(pdecl_manager & m, unsigned n, unsigned const * s) { if (n == 0) { sort * r = m.m().mk_sort(m_fid, m_kind); m.save_info(r, this, 0, s); return r; } else { buffer params; for (unsigned i = 0; i < n; i++) params.push_back(parameter(s[i])); sort * r = m.m().mk_sort(m_fid, m_kind, n, params.c_ptr()); m.save_info(r, this, n, s); return r; } } void psort_builtin_decl::display(std::ostream & out) const { out << "(declare-builtin-sort " << m_name << ")"; } void ptype::display(std::ostream & out, pdatatype_decl const * const * dts) const { switch (kind()) { case PTR_PSORT: get_psort()->display(out); break; case PTR_REC_REF: out << dts[get_idx()]->get_name(); break; case PTR_MISSING_REF: out << get_missing_ref(); break; } } paccessor_decl::paccessor_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, ptype const & r): pdecl(id, num_params), m_name(n), m_type(r) { if (m_type.kind() == PTR_PSORT) m.inc_ref(r.get_psort()); } void paccessor_decl::finalize(pdecl_manager & m) { if (m_type.kind() == PTR_PSORT) m.lazy_dec_ref(m_type.get_psort()); } bool paccessor_decl::has_missing_refs(symbol & missing) const { if (m_type.kind() == PTR_MISSING_REF) { missing = m_type.get_missing_ref(); return true; } return false; } bool paccessor_decl::fix_missing_refs(dictionary const & symbol2idx, symbol & missing) { TRACE("fix_missing_refs", tout << "m_type.kind(): " << m_type.kind() << "\n"; if (m_type.kind() == PTR_MISSING_REF) tout << m_type.get_missing_ref() << "\n";); if (m_type.kind() != PTR_MISSING_REF) return true; int idx; if (symbol2idx.find(m_type.get_missing_ref(), idx)) { m_type = ptype(idx); SASSERT(m_type.kind() == PTR_REC_REF); return true; } missing = m_type.get_missing_ref(); return false; } accessor_decl * paccessor_decl::instantiate_decl(pdecl_manager & m, sort * const * s) { switch (m_type.kind()) { case PTR_REC_REF: return mk_accessor_decl(m_name, type_ref(m_type.get_idx())); case PTR_PSORT: return mk_accessor_decl(m_name, type_ref(m_type.get_psort()->instantiate(m, s))); default: // missing refs must have been eliminated. UNREACHABLE(); return 0; } } void paccessor_decl::display(std::ostream & out, pdatatype_decl const * const * dts) const { out << "(" << m_name << " "; m_type.display(out, dts); out << ")"; } pconstructor_decl::pconstructor_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, symbol const & r, unsigned num_accessors, paccessor_decl * const * accessors): pdecl(id, num_params), m_name(n), m_recogniser_name(r), m_accessors(num_accessors, accessors) { m.inc_ref(num_accessors, accessors); TRACE("pconstructor_decl", tout << "name: " << n << ", recognizer: " << r << "\n";); } void pconstructor_decl::finalize(pdecl_manager & m) { m.lazy_dec_ref(m_accessors.size(), m_accessors.c_ptr()); } bool pconstructor_decl::has_missing_refs(symbol & missing) const { ptr_vector::const_iterator it = m_accessors.begin(); ptr_vector::const_iterator end = m_accessors.end(); for (; it != end; ++it) { if ((*it)->has_missing_refs(missing)) return true; } return false; } bool pconstructor_decl::fix_missing_refs(dictionary const & symbol2idx, symbol & missing) { ptr_vector::iterator it = m_accessors.begin(); ptr_vector::iterator end = m_accessors.end(); for (; it != end; ++it) { if (!(*it)->fix_missing_refs(symbol2idx, missing)) return false; } return true; } constructor_decl * pconstructor_decl::instantiate_decl(pdecl_manager & m, sort * const * s) { ptr_buffer as; ptr_vector::iterator it = m_accessors.begin(); ptr_vector::iterator end = m_accessors.end(); for (; it != end; ++it) as.push_back((*it)->instantiate_decl(m, s)); return mk_constructor_decl(m_name, m_recogniser_name, as.size(), as.c_ptr()); } void pconstructor_decl::display(std::ostream & out, pdatatype_decl const * const * dts) const { out << "(" << m_name; ptr_vector::const_iterator it = m_accessors.begin(); ptr_vector::const_iterator end = m_accessors.end(); for (; it != end; ++it) { out << " "; (*it)->display(out, dts); } out << ")"; } pdatatype_decl::pdatatype_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, unsigned num_constructors, pconstructor_decl * const * constructors): psort_decl(id, num_params, m, n), m_constructors(num_constructors, constructors), m_parent(0) { m.inc_ref(num_constructors, constructors); } void pdatatype_decl::finalize(pdecl_manager & m) { m.lazy_dec_ref(m_constructors.size(), m_constructors.c_ptr()); psort_decl::finalize(m); } bool pdatatype_decl::has_missing_refs(symbol & missing) const { ptr_vector::const_iterator it = m_constructors.begin(); ptr_vector::const_iterator end = m_constructors.end(); for (; it != end; ++it) { if ((*it)->has_missing_refs(missing)) return true; } return false; } bool pdatatype_decl::has_duplicate_accessors(symbol & duplicated) const { hashtable names; ptr_vector::const_iterator it = m_constructors.begin(); ptr_vector::const_iterator end = m_constructors.end(); for (; it != end; ++it) { ptr_vector const& acc = (*it)->m_accessors; for (unsigned i = 0; i < acc.size(); ++i) { symbol const& name = acc[i]->get_name(); if (names.contains(name)) { duplicated = name; return true; } names.insert(name); } } return false; } bool pdatatype_decl::fix_missing_refs(dictionary const & symbol2idx, symbol & missing) { ptr_vector::iterator it = m_constructors.begin(); ptr_vector::iterator end = m_constructors.end(); for (; it != end; ++it) { if (!(*it)->fix_missing_refs(symbol2idx, missing)) return false; } return true; } datatype_decl * pdatatype_decl::instantiate_decl(pdecl_manager & m, sort * const * s) { ptr_buffer cs; ptr_vector::iterator it = m_constructors.begin(); ptr_vector::iterator end = m_constructors.end(); for (; it != end; ++it) { cs.push_back((*it)->instantiate_decl(m, s)); } return mk_datatype_decl(m_name, cs.size(), cs.c_ptr()); } struct datatype_decl_buffer { ptr_buffer m_buffer; ~datatype_decl_buffer() { del_datatype_decls(m_buffer.size(), m_buffer.c_ptr()); } }; sort * pdatatype_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) { SASSERT(m_num_params == n); sort * r = find(s); if (r) return r; if (m_parent != 0) { if (m_parent->instantiate(m, s)) { r = find(s); SASSERT(r); return r; } } else { datatype_decl_buffer dts; dts.m_buffer.push_back(instantiate_decl(m, s)); datatype_decl * d_ptr = dts.m_buffer[0]; sort_ref_vector sorts(m.m()); bool is_ok = m.get_dt_plugin()->mk_datatypes(1, &d_ptr, sorts); TRACE("pdatatype_decl", tout << "instantiating " << m_name << " is_ok: " << is_ok << "\n";); if (is_ok) { r = sorts.get(0); cache(m, s, r); m.save_info(r, this, n, s); m.notify_new_dt(r); return r; } } return 0; } void pdatatype_decl::display(std::ostream & out) const { out << "(declare-datatype " << m_name; display_sort_args(out, m_num_params); ptr_vector::const_iterator it = m_constructors.begin(); ptr_vector::const_iterator end = m_constructors.end(); for (bool first = true; it != end; ++it) { if (!first) out << " "; if (m_parent) { (*it)->display(out, m_parent->children()); } else { pdatatype_decl const * dts[1] = { this }; (*it)->display(out, dts); } first = false; } out << ")"; } pdatatypes_decl::pdatatypes_decl(unsigned id, unsigned num_params, pdecl_manager & m, unsigned num_datatypes, pdatatype_decl * const * dts): pdecl(id, num_params), m_datatypes(num_datatypes, dts) { m.inc_ref(num_datatypes, dts); ptr_vector::iterator it = m_datatypes.begin(); ptr_vector::iterator end = m_datatypes.end(); for (; it != end; ++it) { SASSERT((*it)->m_parent == 0); (*it)->m_parent = this; } } void pdatatypes_decl::finalize(pdecl_manager & m) { m.lazy_dec_ref(m_datatypes.size(), m_datatypes.c_ptr()); } bool pdatatypes_decl::fix_missing_refs(symbol & missing) { TRACE("fix_missing_refs", tout << "pdatatypes_decl::fix_missing_refs\n";); dictionary symbol2idx; ptr_vector::iterator it = m_datatypes.begin(); ptr_vector::iterator end = m_datatypes.end(); for (unsigned idx = 0; it != end; ++it, ++idx) symbol2idx.insert((*it)->get_name(), idx); it = m_datatypes.begin(); for (unsigned idx = 0; it != end; ++it, ++idx) { if (!(*it)->fix_missing_refs(symbol2idx, missing)) return false; } return true; } bool pdatatypes_decl::instantiate(pdecl_manager & m, sort * const * s) { datatype_decl_buffer dts; ptr_vector::iterator it = m_datatypes.begin(); ptr_vector::iterator end = m_datatypes.end(); for (; it != end; ++it) { dts.m_buffer.push_back((*it)->instantiate_decl(m, s)); } sort_ref_vector sorts(m.m()); bool is_ok = m.get_dt_plugin()->mk_datatypes(dts.m_buffer.size(), dts.m_buffer.c_ptr(), sorts); if (!is_ok) return false; it = m_datatypes.begin(); for (unsigned i = 0; it != end; ++it, ++i) { sort * new_dt = sorts.get(i); (*it)->cache(m, s, new_dt); m.save_info(new_dt, *it, m_num_params, s); m.notify_new_dt(new_dt); } return true; } struct pdecl_manager::sort_info { psort_decl * m_decl; sort_info(pdecl_manager & m, psort_decl * d): m_decl(d) { m.inc_ref(d); } virtual ~sort_info() {} virtual unsigned obj_size() const { return sizeof(sort_info); } virtual void finalize(pdecl_manager & m) { m.dec_ref(m_decl); } virtual void display(std::ostream & out, pdecl_manager const & m) const = 0; virtual format * pp(pdecl_manager const & m) const = 0; }; struct pdecl_manager::app_sort_info : public pdecl_manager::sort_info { ptr_vector m_args; app_sort_info(pdecl_manager & m, psort_decl * d, unsigned n, sort * const * s): sort_info(m, d), m_args(n, s) { m.m().inc_array_ref(n, s); } virtual ~app_sort_info() {} virtual unsigned obj_size() const { return sizeof(app_sort_info); } virtual void finalize(pdecl_manager & m) { sort_info::finalize(m); m.m().dec_array_ref(m_args.size(), m_args.c_ptr()); } virtual void display(std::ostream & out, pdecl_manager const & m) const { if (m_args.empty()) { out << m_decl->get_name(); } else { out << "(" << m_decl->get_name(); for (unsigned i = 0; i < m_args.size(); i++) { out << " "; m.display(out, m_args[i]); } out << ")"; } } virtual format * pp(pdecl_manager const & m) const { if (m_args.empty()) { return mk_string(m.m(), m_decl->get_name().str().c_str()); } else { ptr_buffer b; for (unsigned i = 0; i < m_args.size(); i++) b.push_back(m.pp(m_args[i])); return mk_seq1(m.m(), b.begin(), b.end(), f2f(), m_decl->get_name().str().c_str()); } } }; struct pdecl_manager::indexed_sort_info : public pdecl_manager::sort_info { svector m_indices; indexed_sort_info(pdecl_manager & m, psort_decl * d, unsigned n, unsigned const * s): sort_info(m, d), m_indices(n, s) { } virtual ~indexed_sort_info() {} virtual unsigned obj_size() const { return sizeof(indexed_sort_info); } virtual void display(std::ostream & out, pdecl_manager const & m) const { if (m_indices.empty()) { out << m_decl->get_name(); } else { out << "(_ " << m_decl->get_name(); for (unsigned i = 0; i < m_indices.size(); i++) { out << " " << m_indices[i]; } out << ")"; } } virtual format * pp(pdecl_manager const & m) const { if (m_indices.empty()) { return mk_string(m.m(), m_decl->get_name().str().c_str()); } else { ptr_buffer b; b.push_back(mk_string(m.m(), m_decl->get_name().str().c_str())); for (unsigned i = 0; i < m_indices.size(); i++) b.push_back(mk_unsigned(m.m(), m_indices[i])); return mk_seq1(m.m(), b.begin(), b.end(), f2f(), "_"); } } }; void pdecl_manager::init_list() { SASSERT(m_list == 0); psort * v = mk_psort_var(1, 0); ptype T(v); ptype ListT(0); paccessor_decl * as[2] = { mk_paccessor_decl(1, symbol("head"), T), mk_paccessor_decl(1, symbol("tail"), ListT) }; pconstructor_decl * cs[2] = { mk_pconstructor_decl(1, symbol("nil"), symbol("is-nil"), 0, 0), mk_pconstructor_decl(1, symbol("insert"), symbol("is-insert"), 2, as) }; m_list = mk_pdatatype_decl(1, symbol("List"), 2, cs); inc_ref(m_list); } pdecl_manager::pdecl_manager(ast_manager & m): m_manager(m), m_allocator(m.get_allocator()), m_new_dt_eh(0) { m_list = 0; m_datatype_fid = m.mk_family_id("datatype"); } pdecl_manager::~pdecl_manager() { dec_ref(m_list); reset_sort_info(); SASSERT(m_sort2psort.empty()); SASSERT(m_table.empty()); } psort * pdecl_manager::mk_psort_cnst(sort * s) { psort * r = 0; if (m_sort2psort.find(s, r)) return r; r = new (a().allocate(sizeof(psort_sort))) psort_sort(m_id_gen.mk(), *this, s); m_sort2psort.insert(s, r); return r; } psort * pdecl_manager::register_psort(psort * n) { TRACE("register_psort", tout << "registering psort...\n"; n->display(tout); tout << "\n";); psort * r = m_table.insert_if_not_there(n); if (r != n) { del_decl_core(n); return r; } return n; } psort * pdecl_manager::mk_psort_var(unsigned num_params, unsigned vidx) { psort_var * n = new (a().allocate(sizeof(psort_var))) psort_var(m_id_gen.mk(), num_params, vidx); return register_psort(n); } paccessor_decl * pdecl_manager::mk_paccessor_decl(unsigned num_params, symbol const & s, ptype const & p) { return new (a().allocate(sizeof(paccessor_decl))) paccessor_decl(m_id_gen.mk(), num_params, *this, s, p); } pconstructor_decl * pdecl_manager::mk_pconstructor_decl(unsigned num_params, symbol const & s, symbol const & r, unsigned num, paccessor_decl * const * as) { return new (a().allocate(sizeof(pconstructor_decl))) pconstructor_decl(m_id_gen.mk(), num_params, *this, s, r, num, as); } pdatatype_decl * pdecl_manager::mk_pdatatype_decl(unsigned num_params, symbol const & s, unsigned num, pconstructor_decl * const * cs) { return new (a().allocate(sizeof(pdatatype_decl))) pdatatype_decl(m_id_gen.mk(), num_params, *this, s, num, cs); } pdatatypes_decl * pdecl_manager::mk_pdatatypes_decl(unsigned num_params, unsigned num, pdatatype_decl * const * dts) { return new (a().allocate(sizeof(pdatatypes_decl))) pdatatypes_decl(m_id_gen.mk(), num_params, *this, num, dts); } psort * pdecl_manager::mk_psort_app(unsigned num_params, psort_decl * d, unsigned num_args, psort * const * args) { psort * n = new (a().allocate(sizeof(psort_app))) psort_app(m_id_gen.mk(), num_params, *this, d, num_args, args); return register_psort(n); } psort * pdecl_manager::mk_psort_app(psort_decl * d) { SASSERT(d->get_num_params() == 0 || d->get_num_params() == PSORT_DECL_VAR_PARAMS); sort * s = d->instantiate(*this, 0, static_cast(0)); if (s == 0) return 0; return mk_psort_cnst(s); } psort_decl * pdecl_manager::mk_psort_user_decl(unsigned num_params, symbol const & n, psort * def) { return new (a().allocate(sizeof(psort_user_decl))) psort_user_decl(m_id_gen.mk(), num_params, *this, n, def); } psort_decl * pdecl_manager::mk_psort_builtin_decl(symbol const & n, family_id fid, decl_kind k) { return new (a().allocate(sizeof(psort_builtin_decl))) psort_builtin_decl(m_id_gen.mk(), *this, n, fid, k); } sort * pdecl_manager::instantiate(psort * p, unsigned num, sort * const * args) { // ignoring stack overflows... sorts are not deeply nested return p->instantiate(*this, args); } void pdecl_manager::del_decl_core(pdecl * p) { TRACE("pdecl_manager", tout << "del_decl_core:\n"; if (p->is_psort()) static_cast(p)->display(tout); else static_cast(p)->display(tout); tout << "\n";); size_t sz = p->obj_size(); m_id_gen.recycle(p->get_id()); p->finalize(*this); p->~pdecl(); m_allocator.deallocate(sz, p); } void pdecl_manager::del_decl(pdecl * p) { if (p->is_psort()) { psort * _p = static_cast(p); if (_p->is_sort_wrapper()) m_sort2psort.erase(static_cast(_p)->get_sort()); else m_table.erase(_p); } del_decl_core(p); } void pdecl_manager::del_decls() { while (!m_to_delete.empty()) { pdecl * p = m_to_delete.back(); m_to_delete.pop_back(); del_decl(p); } } psort_inst_cache * pdecl_manager::mk_inst_cache(unsigned num_params) { return new (a().allocate(sizeof(psort_inst_cache))) psort_inst_cache(num_params); } void pdecl_manager::del_inst_cache(psort_inst_cache * c) { if (c) { c->finalize(*this); c->~psort_inst_cache(); a().deallocate(sizeof(psort_inst_cache), c); } } datatype_decl_plugin * pdecl_manager::get_dt_plugin() const { return static_cast(m().get_plugin(m_datatype_fid)); } void pdecl_manager::save_info(sort * s, psort_decl * d, unsigned num_args, sort * const * args) { if (m_sort2info.contains(s)) return; sort_info * info = new (a().allocate(sizeof(app_sort_info))) app_sort_info(*this, d, num_args, args); m().inc_ref(s); m_sort2info.insert(s, info); } void pdecl_manager::save_info(sort * s, psort_decl * d, unsigned num_indices, unsigned const * indices) { if (m_sort2info.contains(s)) return; sort_info * info = new (a().allocate(sizeof(indexed_sort_info))) indexed_sort_info(*this, d, num_indices, indices); m().inc_ref(s); m_sort2info.insert(s, info); } void pdecl_manager::reset_sort_info() { obj_map::iterator it = m_sort2info.begin(); obj_map::iterator end = m_sort2info.end(); for (; it != end; ++it) { sort * s = (*it).m_key; sort_info * info = (*it).m_value; m().dec_ref(s); unsigned sz = info->obj_size(); info->finalize(*this); info->~sort_info(); m_allocator.deallocate(sz, info); } m_sort2info.reset(); } void pdecl_manager::display(std::ostream & out, sort * s) const { sort_info * info = 0; if (m_sort2info.find(s, info)) { info->display(out, *this); return; } out << s->get_name(); } format * pdecl_manager::pp(sort * s) const { sort_info * info = 0; if (m_sort2info.find(s, info)) { return info->pp(*this); } unsigned num_params = s->get_num_parameters(); if (s->get_family_id() != null_family_id && num_params > 0) { // Small hack to display FP and BitVec sorts that were not explicitly referenced by the user. unsigned i = 0; for (i = 0; i < num_params; i++) { if (!s->get_parameter(i).is_int()) break; } if (i == num_params) { // all parameters are integer ptr_buffer b; b.push_back(mk_string(m(), s->get_name().str().c_str())); for (unsigned i = 0; i < num_params; i++) b.push_back(mk_unsigned(m(), s->get_parameter(i).get_int())); return mk_seq1(m(), b.begin(), b.end(), f2f(), "_"); } } return mk_string(m(), s->get_name().str().c_str()); } z3-4.4.0/src/cmd_context/README0000644000175000017500000000027612540347414015630 0ustar michaelmichaelCommand context provides the infrastructure for executing commands in front-ends such as SMT-LIB 2.0. It is also provides the solver abstraction to plugin solvers in this kind of front-end. z3-4.4.0/src/cmd_context/eval_cmd.h0000644000175000017500000000042112540347414016663 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: eval_cmd.h Abstract: eval_cmd Author: Leonardo (leonardo) 2011-04-30 Notes: --*/ #ifndef _EVAL_CMD_H_ #define _EVAL_CMD_H_ class cmd_context; void install_eval_cmd(cmd_context & ctx); #endif z3-4.4.0/src/cmd_context/tactic_cmds.h0000644000175000017500000000244412540347414017375 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: tactic_cmds.h Abstract: Support for tactics in SMT 2.0 frontend. Author: Leonardo (leonardo) 2011-11-20 Notes: --*/ #ifndef _TACTIC_CMDS_H_ #define _TACTIC_CMDS_H_ #include"ast.h" #include"cmd_context_types.h" #include"ref.h" class tactic; class probe; class tactic_factory; class tactic_cmd { symbol m_name; char const * m_descr; tactic_factory * m_factory; public: tactic_cmd(symbol const & n, char const * d, tactic_factory * f): m_name(n), m_descr(d), m_factory(f) { SASSERT(m_factory); } ~tactic_cmd(); symbol get_name() const { return m_name; } char const * get_descr() const { return m_descr; } tactic * mk(ast_manager & m); }; void install_core_tactic_cmds(cmd_context & ctx); tactic * sexpr2tactic(cmd_context & ctx, sexpr * n); class probe_info { symbol m_name; char const * m_descr; ref m_probe; public: probe_info(symbol const & n, char const * d, probe * p); ~probe_info(); symbol get_name() const { return m_name; } char const * get_descr() const { return m_descr; } probe * get() const { return m_probe.get(); } }; probe * sexpr2probe(cmd_context & ctx, sexpr * n); #endif z3-4.4.0/src/cmd_context/eval_cmd.cpp0000644000175000017500000000440312540347414017222 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: eval_cmd.cpp Abstract: eval_cmd Author: Leonardo (leonardo) 2011-04-30 Notes: --*/ #include"cmd_context.h" #include"model_evaluator.h" #include"parametric_cmd.h" #include"scoped_timer.h" #include"scoped_ctrl_c.h" #include"cancel_eh.h" class eval_cmd : public parametric_cmd { expr * m_target; symbol m_last; public: eval_cmd():parametric_cmd("eval") {} virtual char const * get_usage() const { return " ( )*"; } virtual char const * get_main_descr() const { return "evaluate the given term in the current model."; } virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { model_evaluator::get_param_descrs(p); insert_timeout(p); } virtual void prepare(cmd_context & ctx) { parametric_cmd::prepare(ctx); m_target = 0; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { if (m_target == 0) return CPK_EXPR; return parametric_cmd::next_arg_kind(ctx); } virtual void set_next_arg(cmd_context & ctx, expr * arg) { m_target = arg; } virtual void execute(cmd_context & ctx) { if (!ctx.is_model_available()) throw cmd_exception("model is not available"); model_ref md; check_sat_result * last_result = ctx.get_check_sat_result(); SASSERT(last_result); last_result->get_model(md); expr_ref r(ctx.m()); unsigned timeout = m_params.get_uint("timeout", UINT_MAX); model_evaluator ev(*(md.get()), m_params); cancel_eh eh(ev); { scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); cmd_context::scoped_watch sw(ctx); try { ev(m_target, r); } catch (model_evaluator_exception & ex) { ctx.regular_stream() << "(error \"evaluator failed: " << ex.msg() << "\")" << std::endl; return; } } ctx.display(ctx.regular_stream(), r.get()); ctx.regular_stream() << std::endl; } }; void install_eval_cmd(cmd_context & ctx) { ctx.insert(alloc(eval_cmd)); } z3-4.4.0/src/cmd_context/tactic_manager.cpp0000644000175000017500000000243712540347414020416 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: tactic_manager.cpp Abstract: Collection of tactics & probes Author: Leonardo (leonardo) 2012-03-06 Notes: --*/ #include"tactic_manager.h" tactic_manager::~tactic_manager() { finalize_tactic_cmds(); finalize_probes(); } void tactic_manager::insert(tactic_cmd * c) { symbol const & s = c->get_name(); SASSERT(!m_name2tactic.contains(s)); m_name2tactic.insert(s, c); m_tactics.push_back(c); } void tactic_manager::insert(probe_info * p) { symbol const & s = p->get_name(); SASSERT(!m_name2probe.contains(s)); m_name2probe.insert(s, p); m_probes.push_back(p); } tactic_cmd * tactic_manager::find_tactic_cmd(symbol const & s) const { tactic_cmd * c = 0; m_name2tactic.find(s, c); return c; } probe_info * tactic_manager::find_probe(symbol const & s) const { probe_info * p = 0; m_name2probe.find(s, p); return p; } void tactic_manager::finalize_tactic_cmds() { std::for_each(m_tactics.begin(), m_tactics.end(), delete_proc()); m_tactics.reset(); m_name2tactic.reset(); } void tactic_manager::finalize_probes() { std::for_each(m_probes.begin(), m_probes.end(), delete_proc()); m_probes.reset(); m_name2probe.reset(); } z3-4.4.0/src/cmd_context/echo_tactic.cpp0000644000175000017500000000454612540347414017725 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: echo_tactic.h Abstract: Tactic and probe for dumping data. Author: Leonardo (leonardo) 2012-10-20 Notes: --*/ #include"tactic.h" #include"probe.h" #include"cmd_context.h" class echo_tactic : public skip_tactic { cmd_context & m_ctx; char const * m_msg; bool m_newline; public: echo_tactic(cmd_context & ctx, char const * msg, bool newline):m_ctx(ctx), m_msg(msg), m_newline(newline) {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { #pragma omp critical (echo_tactic) { m_ctx.regular_stream() << m_msg; if (m_newline) m_ctx.regular_stream() << std::endl; } skip_tactic::operator()(in, result, mc, pc, core); } }; tactic * mk_echo_tactic(cmd_context & ctx, char const * msg, bool newline) { return alloc(echo_tactic, ctx, msg, newline); } class probe_value_tactic : public skip_tactic { cmd_context & m_ctx; char const * m_msg; probe * m_p; bool m_newline; public: probe_value_tactic(cmd_context & ctx, char const * msg, probe * p, bool newline):m_ctx(ctx), m_msg(msg), m_p(p), m_newline(newline) { SASSERT(m_p); m_p->inc_ref(); } ~probe_value_tactic() { m_p->dec_ref(); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { double val = (*m_p)(*(in.get())).get_value(); #pragma omp critical (probe_value_tactic) { if (m_msg) m_ctx.diagnostic_stream() << m_msg << " "; m_ctx.diagnostic_stream() << val; if (m_newline) m_ctx.diagnostic_stream() << std::endl; } skip_tactic::operator()(in, result, mc, pc, core); } }; tactic * mk_probe_value_tactic(cmd_context & ctx, char const * msg, probe * p, bool newline) { return alloc(probe_value_tactic, ctx, msg, p, newline); } z3-4.4.0/src/cmd_context/parametric_cmd.cpp0000644000175000017500000000303712540347414020424 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: parametric_cmd.h Abstract: A generic parametric cmd. Author: Leonardo (leonardo) 2011-04-22 Notes: --*/ #include #include"cmd_context.h" #include"parametric_cmd.h" char const * parametric_cmd::get_descr(cmd_context & ctx) const { if (m_descr == 0) { const_cast(this)->m_descr = alloc(string_buffer<>); m_descr->append(get_main_descr()); m_descr->append("\nThe following options are available:\n"); std::ostringstream buf; pdescrs(ctx).display(buf, 2); m_descr->append(buf.str().c_str()); } return m_descr->c_str(); } cmd_arg_kind parametric_cmd::next_arg_kind(cmd_context & ctx) const { if (m_last == symbol::null) return CPK_KEYWORD; return pdescrs(ctx).get_kind(m_last); } void parametric_cmd::set_next_arg(cmd_context & ctx, symbol const & s) { if (m_last == symbol::null) { m_last = symbol(norm_param_name(s).c_str()); if (pdescrs(ctx).get_kind(m_last.bare_str()) == CPK_INVALID) throw cmd_exception("invalid keyword argument"); return; } else { m_params.set_sym(m_last.bare_str(), s); m_last = symbol::null; } } param_descrs const & parametric_cmd::pdescrs(cmd_context & ctx) const { if (!m_pdescrs) { parametric_cmd * _this = const_cast(this); _this->m_pdescrs = alloc(param_descrs); _this->init_pdescrs(ctx, *(_this->m_pdescrs)); } return *m_pdescrs; } z3-4.4.0/src/cmd_context/check_logic.cpp0000644000175000017500000003443512540347414017712 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: check_logic.cpp Abstract: Check whether a given assertion is in the correct logic or not Author: Leonardo de Moura (leonardo) 2011-08-11. Revision History: --*/ #include"check_logic.h" #include"arith_decl_plugin.h" #include"array_decl_plugin.h" #include"bv_decl_plugin.h" #include"ast_pp.h" #include"for_each_expr.h" struct check_logic::imp { ast_manager & m; symbol m_logic; arith_util m_a_util; bv_util m_bv_util; array_util m_ar_util; bool m_uf; // true if the logic supports uninterpreted functions bool m_arrays; // true if the logic supports arbitrary arrays bool m_bv_arrays; // true if the logic supports only bv arrays bool m_reals; // true if the logic supports reals bool m_ints; // true if the logic supports integers bool m_diff; // true if the logic supports difference logic only bool m_nonlinear; // true if the logic supports nonlinear arithmetic bool m_bvs; // true if the logic supports bit-vectors bool m_quantifiers; // true if the logic supports quantifiers bool m_unknown_logic; imp(ast_manager & _m):m(_m), m_a_util(m), m_bv_util(m), m_ar_util(m) { reset(); } void reset() { m_uf = false; m_arrays = false; m_bv_arrays = false; m_reals = false; m_ints = false; m_diff = false; m_nonlinear = false; m_bvs = false; m_quantifiers = false; m_unknown_logic = true; } void set_logic(symbol const & logic) { reset(); m_unknown_logic = false; if (logic == "AUFLIA") { m_uf = true; m_arrays = true; m_ints = true; m_quantifiers = true; } else if (logic == "AUFLIRA") { m_uf = true; m_arrays = true; m_reals = true; m_ints = true; m_quantifiers = true; } else if (logic == "AUFNIRA") { m_uf = true; m_arrays = true; m_reals = true; m_ints = true; m_nonlinear = true; m_quantifiers = true; } else if (logic == "LRA") { m_reals = true; m_quantifiers = true; } else if (logic == "QF_ABV") { m_bv_arrays = true; m_bvs = true; } else if (logic == "QF_AUFBV") { m_uf = true; m_bv_arrays = true; m_bvs = true; } else if (logic == "QF_UFBV") { m_uf = true; m_bvs = true; } else if (logic == "QF_AUFLIA") { m_uf = true; m_arrays = true; m_ints = true; } else if (logic == "QF_AX") { m_arrays = true; } else if (logic == "QF_BV") { m_bvs = true; } else if (logic == "QF_IDL") { m_ints = true; m_diff = true; } else if (logic == "QF_RDL") { m_reals = true; m_diff = true; } else if (logic == "QF_LIA") { m_ints = true; } else if (logic == "QF_LRA") { m_reals = true; } else if (logic == "QF_NIA") { m_ints = true; m_nonlinear = true; } else if (logic == "QF_NRA") { m_reals = true; m_nonlinear = true; } else if (logic == "QF_UF") { m_uf = true; } else if (logic == "QF_UFIDL") { m_uf = true; m_ints = true; m_diff = true; } else if (logic == "QF_UFLIA") { m_uf = true; m_ints = true; } else if (logic == "QF_UFLRA") { m_uf = true; m_reals = true; } else if (logic == "QF_UFNRA") { m_uf = true; m_reals = true; m_nonlinear = true; } else if (logic == "UFLRA") { m_uf = true; m_reals = true; m_quantifiers = true; } else if (logic == "UFNIA") { m_uf = true; m_ints = true; m_quantifiers = true; m_nonlinear = true; } else if (logic == "UFBV") { m_uf = true; m_bvs = true; m_quantifiers = true; } else { m_unknown_logic = true; } m_logic = logic; } struct failed {}; std::string m_last_error; void fail(char const * msg) { m_last_error = msg; throw failed(); } void check_sort(sort * s) { if (s->get_family_id() == null_family_id) { if (!m_uf) fail("logic does not support uninterpreted sorts"); } else if (m.is_bool(s)) { return; } else if (m_a_util.is_int(s)) { if (!m_ints) fail("logic does not support integers"); } else if (m_a_util.is_real(s)) { if (!m_reals) fail("logic does not support reals"); } else if (m_bv_util.is_bv_sort(s)) { if (!m_bvs) fail("logic does not support bitvectors"); } else if (m_ar_util.is_array(s)) { if (m_arrays) { return; } else if (m_bv_arrays) { if (get_array_arity(s) != 1) fail("logic supports only unidimensional arrays"); if (!m_bv_util.is_bv_sort(get_array_range(s)) || !m_bv_util.is_bv_sort(get_array_domain(s, 0))) fail("logic supports only arrays from bitvectors to bitvectors"); } else { fail("logic does not support arrays"); } } } void operator()(var * n) { if (!m_quantifiers) fail("logic does not support quantifiers"); check_sort(m.get_sort(n)); } bool is_int(expr * t) { if (m_a_util.is_uminus(t)) t = to_app(t)->get_arg(0); // Take care of coercions automatically added by Z3 if (m_a_util.is_to_real(t)) t = to_app(t)->get_arg(0); return m_a_util.is_numeral(t); } bool is_numeral(expr * t) { if (m_a_util.is_uminus(t)) t = to_app(t)->get_arg(0); // c if (is_int(t)) return true; // c1/c2 if (m_a_util.is_div(t) && is_int(to_app(t)->get_arg(0)) && is_int(to_app(t)->get_arg(1))) return true; return false; } // check if n has at most one argument that is not numeral. void check_mul(app * n) { if (m_nonlinear) return; // nothing to check unsigned num_args = n->get_num_args(); bool found_non_numeral = false; for (unsigned i = 0; i < num_args; i++) { if (!is_numeral(n->get_arg(i))) { if (found_non_numeral) fail("logic does not support nonlinear arithmetic"); else found_non_numeral = true; } } } // check if the divisor is a numeral void check_div(app * n) { SASSERT(n->get_num_args() == 2); if (!m_nonlinear && !is_numeral(n->get_arg(1))) fail("logic does not support nonlinear arithmetic"); } bool is_diff_var(expr * t) const { if (is_app(t) && to_app(t)->get_decl()->get_family_id() == null_family_id) return true; if (m.is_ite(t)) return true; return false; } void fail_non_diff(expr * t) { TRACE("check_logic", tout << mk_pp(t, m) << "\n";); fail("logic only supports difference arithmetic"); } bool same_args(app * t) { unsigned num_args = t->get_num_args(); if (num_args == 0) return false; expr * arg = t->get_arg(0); for (unsigned i = 1; i < num_args; i++) { if (t->get_arg(i) != arg) return false; } return true; } bool is_arith(expr * t) const { return m.get_sort(t)->get_family_id() == m_a_util.get_family_id(); } bool is_offset(app * t) { while (true) { expr * non_numeral = 0; unsigned num_args = t->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = t->get_arg(i); if (is_numeral(arg)) continue; if (non_numeral != 0) return false; non_numeral = arg; } if (non_numeral == 0) return true; if (is_diff_var(non_numeral)) return true; if (!m_a_util.is_add(non_numeral) && !m_a_util.is_sub(non_numeral)) return false; t = to_app(non_numeral); } return true; } bool is_diff_arg(expr * t) { if (is_diff_var(t)) return true; if (is_numeral(t)) return true; if (m_a_util.is_add(t) || m_a_util.is_sub(t)) return is_offset(to_app(t)); return false; } // Check if n is a diff logic predicate void check_diff_predicate(app * n) { expr * lhs = n->get_arg(0); expr * rhs = n->get_arg(1); if (!is_arith(lhs)) return; // formula is not in arithmetic if (is_diff_arg(lhs) && is_diff_arg(rhs)) return; if (is_numeral(lhs)) std::swap(lhs, rhs); if (!is_numeral(rhs)) fail_non_diff(n); if (!m_a_util.is_sub(lhs) || to_app(lhs)->get_num_args() != 2) fail_non_diff(n); expr * t1 = to_app(lhs)->get_arg(0); expr * t2 = to_app(lhs)->get_arg(1); if (is_diff_var(t1) && is_diff_var(t2)) return; if (m_a_util.is_add(t1) && m_a_util.is_add(t2)) { // QF_RDL supports (<= (- (+ x ... x) (+ y ... y)) c) if (to_app(t1)->get_num_args() != to_app(t2)->get_num_args()) fail_non_diff(n); if (!same_args(to_app(t1)) || !same_args(to_app(t2))) fail_non_diff(n); return; } fail_non_diff(n); } void check_diff_arg(expr * t) { if (!is_diff_arg(t)) fail_non_diff(t); } // Check if the arith args of n are of the form (t + k) where k is a numeral. void check_diff_args(app * n) { unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { if (is_arith(n)) check_diff_arg(n); } } void operator()(app * n) { sort * s = m.get_sort(n); check_sort(s); func_decl * f = n->get_decl(); family_id fid = f->get_family_id(); if (fid == null_family_id) { if (!m_uf && f->get_arity() > 0) fail("logic does not support uninterpreted functions"); if (m_diff) check_diff_args(n); } else if (fid == m_a_util.get_family_id()) { if (m_a_util.is_mul(n)) check_mul(n); else if (m_a_util.is_div(n) || m_a_util.is_idiv(n) || m_a_util.is_rem(n) || m_a_util.is_mod(n)) check_div(n); if (m_diff) { if (m_a_util.is_le(n) || m_a_util.is_lt(n) || m_a_util.is_ge(n) || m_a_util.is_gt(n)) check_diff_predicate(n); } if (!m_ints || !m_reals) { if (m_a_util.is_to_real(n) || m_a_util.is_to_int(n)) fail("logic does not support casting operators"); } } else if (fid == m_bv_util.get_family_id()) { // nothing to check... } else if (fid == m_ar_util.get_family_id()) { // nothing to check... if (m_diff) check_diff_args(n); } else if (fid == m.get_basic_family_id()) { // nothing to check... if (m_diff) { if (m.is_eq(n)) check_diff_predicate(n); else if (m.is_distinct(n) || m.is_ite(n)) check_diff_args(n); } } else if (m.is_builtin_family_id(fid)) { // nothing to check } else { fail("logic does not support theory"); } } void operator()(quantifier * n) { if (!m_quantifiers) fail("logic does not support quantifiers"); } bool operator()(expr * n) { if (m_unknown_logic) return true; try { quick_for_each_expr(*this, n); return true; } catch (failed) { return false; } } bool operator()(func_decl * f) { if (m_unknown_logic) return true; try { unsigned arity = f->get_arity(); if (arity > 0) { if (!m_uf) fail("logic does not support uninterpreted functions"); for (unsigned i = 0; i < arity; i++) check_sort(f->get_domain(i)); } check_sort(f->get_range()); return true; } catch (failed) { return false; } } }; check_logic::check_logic() { m_imp = 0; } check_logic::~check_logic() { if (m_imp) dealloc(m_imp); } void check_logic::reset() { if (m_imp) dealloc(m_imp); m_imp = 0; } void check_logic::set_logic(ast_manager & m, symbol const & logic) { reset(); m_imp = alloc(imp, m); m_imp->set_logic(logic); } bool check_logic::operator()(expr * n) { if (m_imp) return m_imp->operator()(n); return true; } bool check_logic::operator()(func_decl * f) { if (m_imp) return m_imp->operator()(f); return true; } char const * check_logic::get_last_error() const { if (m_imp) return m_imp->m_last_error.c_str(); return ""; } z3-4.4.0/src/cmd_context/interpolant_cmds.cpp0000644000175000017500000002032312540347414021014 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: interpolant_cmds.cpp Abstract: Commands for interpolation. Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #include #include"cmd_context.h" #include"cmd_util.h" #include"scoped_timer.h" #include"scoped_ctrl_c.h" #include"cancel_eh.h" #include"ast_pp.h" #include"ast_smt_pp.h" #include"ast_smt2_pp.h" #include"parametric_cmd.h" #include"mpq.h" #include"expr2var.h" #include"pp.h" #include"pp_params.hpp" #include"iz3interp.h" #include"iz3checker.h" #include"iz3profiling.h" #include"interp_params.hpp" #include"scoped_proof.h" static void show_interpolant_and_maybe_check(cmd_context & ctx, ptr_vector &cnsts, expr *t, ptr_vector &interps, params_ref &m_params, bool check) { if (m_params.get_bool("som", false)) m_params.set_bool("flat", true); th_rewriter s(ctx.m(), m_params); for(unsigned i = 0; i < interps.size(); i++){ expr_ref r(ctx.m()); proof_ref pr(ctx.m()); s(to_expr(interps[i]),r,pr); ctx.regular_stream() << mk_pp(r.get(), ctx.m()) << std::endl; #if 0 ast_smt_pp pp(ctx.m()); pp.set_logic(ctx.get_logic().str().c_str()); pp.display_smt2(ctx.regular_stream(), to_expr(interps[i])); ctx.regular_stream() << std::endl; #endif } s.cleanup(); // verify, for the paranoid... if(check || interp_params(m_params).check()){ std::ostringstream err; ast_manager &_m = ctx.m(); // need a solver -- make one here FIXME is this right? bool proofs_enabled, models_enabled, unsat_core_enabled; params_ref p; ctx.params().get_solver_params(_m, p, proofs_enabled, models_enabled, unsat_core_enabled); scoped_ptr sp = (ctx.get_solver_factory())(_m, p, false, true, false, ctx.get_logic()); if(iz3check(_m,sp.get(),err,cnsts,t,interps)) ctx.regular_stream() << "correct\n"; else ctx.regular_stream() << "incorrect: " << err.str().c_str() << "\n"; } for(unsigned i = 0; i < interps.size(); i++){ ctx.m().dec_ref(interps[i]); } interp_params itp_params(m_params); if(itp_params.profile()) profiling::print(ctx.regular_stream()); } static void check_can_interpolate(cmd_context & ctx){ if (!ctx.produce_interpolants()) throw cmd_exception("interpolation is not enabled, use command (set-option :produce-interpolants true)"); } static void get_interpolant_and_maybe_check(cmd_context & ctx, expr * t, params_ref &m_params, bool check) { check_can_interpolate(ctx); // get the proof, if there is one if (!ctx.has_manager() || ctx.cs_state() != cmd_context::css_unsat) throw cmd_exception("proof is not available"); expr_ref pr(ctx.m()); pr = ctx.get_check_sat_result()->get_proof(); if (pr == 0) throw cmd_exception("proof is not available"); // get the assertions from the context ptr_vector::const_iterator it = ctx.begin_assertions(); ptr_vector::const_iterator end = ctx.end_assertions(); ptr_vector cnsts((unsigned)(end - it)); for (int i = 0; it != end; ++it, ++i) cnsts[i] = *it; // compute an interpolant ptr_vector interps; try { iz3interpolate(ctx.m(),pr.get(),cnsts,t,interps,0); } catch (iz3_bad_tree &) { throw cmd_exception("interpolation pattern contains non-asserted formula"); } catch (iz3_incompleteness &) { throw cmd_exception("incompleteness in interpolator"); } show_interpolant_and_maybe_check(ctx, cnsts, t, interps, m_params, check); } static void get_interpolant(cmd_context & ctx, expr * t, params_ref &m_params) { get_interpolant_and_maybe_check(ctx,t,m_params,false); } #if 0 static void get_and_check_interpolant(cmd_context & ctx, params_ref &m_params, expr * t) { get_interpolant_and_maybe_check(ctx,t,m_params,true); } #endif static void compute_interpolant_and_maybe_check(cmd_context & ctx, expr * t, params_ref &m_params, bool check){ // create a fresh solver suitable for interpolation bool proofs_enabled, models_enabled, unsat_core_enabled; params_ref p; ast_manager &_m = ctx.m(); // TODO: the following is a HACK to enable proofs in the old smt solver // When we stop using that solver, this hack can be removed scoped_proof_mode spm(_m,PGM_FINE); ctx.params().get_solver_params(_m, p, proofs_enabled, models_enabled, unsat_core_enabled); p.set_bool("proof", true); scoped_ptr sp = (ctx.get_interpolating_solver_factory())(_m, p, true, models_enabled, false, ctx.get_logic()); ptr_vector cnsts; ptr_vector interps; model_ref m; // compute an interpolant lbool res; try { res = iz3interpolate(_m, *sp.get(), t, cnsts, interps, m, 0); } catch (iz3_incompleteness &) { throw cmd_exception("incompleteness in interpolator"); } switch(res){ case l_false: ctx.regular_stream() << "unsat\n"; show_interpolant_and_maybe_check(ctx, cnsts, t, interps, m_params, check); break; case l_true: ctx.regular_stream() << "sat\n"; // TODO: how to return the model to the context, if it exists? break; case l_undef: ctx.regular_stream() << "unknown\n"; // TODO: how to return the model to the context, if it exists? break; } for(unsigned i = 0; i < cnsts.size(); i++) ctx.m().dec_ref(cnsts[i]); } static expr *make_tree(cmd_context & ctx, const ptr_vector &exprs){ if(exprs.size() == 0) throw cmd_exception("not enough arguments"); expr *foo = exprs[0]; for(unsigned i = 1; i < exprs.size(); i++){ foo = ctx.m().mk_and(ctx.m().mk_interp(foo),exprs[i]); } return foo; } static void get_interpolant(cmd_context & ctx, const ptr_vector &exprs, params_ref &m_params) { expr *foo = make_tree(ctx,exprs); ctx.m().inc_ref(foo); get_interpolant(ctx,foo,m_params); ctx.m().dec_ref(foo); } static void compute_interpolant(cmd_context & ctx, const ptr_vector &exprs, params_ref &m_params) { expr *foo = make_tree(ctx, exprs); ctx.m().inc_ref(foo); compute_interpolant_and_maybe_check(ctx,foo,m_params,false); ctx.m().dec_ref(foo); } // UNARY_CMD(get_interpolant_cmd, "get-interpolant", "", "get interpolant for marked positions in fmla", CPK_EXPR, expr *, get_interpolant(ctx, arg);); // UNARY_CMD(get_and_check_interpolant_cmd, "get-and-check-interpolant", "", "get and check interpolant for marked positions in fmla", CPK_EXPR, expr *, get_and_check_interpolant(ctx, arg);); class get_interpolant_cmd : public parametric_cmd { protected: ptr_vector m_targets; public: get_interpolant_cmd(char const * name = "get-interpolant"):parametric_cmd(name) {} virtual char const * get_usage() const { return "+"; } virtual char const * get_main_descr() const { return "get interpolant for formulas"; } virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { } virtual void prepare(cmd_context & ctx) { parametric_cmd::prepare(ctx); m_targets.resize(0); } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_EXPR; } virtual void set_next_arg(cmd_context & ctx, expr * arg) { m_targets.push_back(arg); } virtual void execute(cmd_context & ctx) { get_interpolant(ctx,m_targets,m_params); } }; class compute_interpolant_cmd : public get_interpolant_cmd { public: compute_interpolant_cmd(char const * name = "compute-interpolant"):get_interpolant_cmd(name) {} virtual void execute(cmd_context & ctx) { compute_interpolant(ctx,m_targets,m_params); } }; void install_interpolant_cmds(cmd_context & ctx) { ctx.insert(alloc(get_interpolant_cmd)); ctx.insert(alloc(compute_interpolant_cmd)); // ctx.insert(alloc(get_and_check_interpolant_cmd)); } z3-4.4.0/src/cmd_context/pdecl.h0000644000175000017500000003306312540347414016210 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdecl.h Abstract: Parametric declarations for SMT-LIB 2.0 + inductive data-types. Author: Leonardo de Moura (leonardo) 2011-03-02. Revision History: --*/ #ifndef _PDECL_H_ #define _PDECL_H_ #include"ast.h" #include"obj_hashtable.h" #include"dictionary.h" #include"format.h" class pdecl_manager; class pdecl { protected: friend class pdecl_manager; unsigned m_id; unsigned m_num_params; unsigned m_ref_count; void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); --m_ref_count; } virtual bool is_psort() const { return false; } virtual size_t obj_size() const = 0; pdecl(unsigned id, unsigned num_params):m_id(id), m_num_params(num_params), m_ref_count(0) {} virtual void finalize(pdecl_manager & m) {} virtual ~pdecl() {} public: virtual bool check_num_params(pdecl * other) const { return m_num_params == other->m_num_params; } unsigned get_num_params() const { return m_num_params; } unsigned get_id() const { return m_id; } unsigned get_ref_count() const { return m_ref_count; } unsigned hash() const { return m_id; } virtual void display(std::ostream & out) const {} }; class psort_inst_cache; #if defined(__APPLE__) && defined(__MACH__) // CMW: for some unknown reason, llvm on OSX does not like the name `psort' #define psort Z3_psort #endif /** \brief Parametric sorts. */ class psort : public pdecl { protected: psort_inst_cache * m_inst_cache; friend class pdecl_manager; psort(unsigned id, unsigned num_params):pdecl(id, num_params), m_inst_cache(0) {} virtual bool is_psort() const { return true; } virtual void finalize(pdecl_manager & m); virtual ~psort() {} virtual void cache(pdecl_manager & m, sort * const * s, sort * r); virtual sort * find(sort * const * s) const; public: virtual bool is_sort_wrapper() const { return false; } virtual sort * instantiate(pdecl_manager & m, sort * const * s) { return 0; } // we use hash-consing for psorts. virtual char const * hcons_kind() const = 0; virtual unsigned hcons_hash() const = 0; virtual bool hcons_eq(psort const * other) const = 0; }; // for hash consing struct psort_hash_proc { unsigned operator()(psort * p) const { return p->hcons_hash(); } }; struct psort_eq_proc { bool operator()(psort * p1, psort * p2) const { return p1->hcons_eq(p2); } }; typedef ptr_hashtable psort_table; #define PSORT_DECL_VAR_PARAMS UINT_MAX class psort_decl : public pdecl { protected: friend class pdecl_manager; symbol m_name; psort_inst_cache * m_inst_cache; void cache(pdecl_manager & m, sort * const * s, sort * r); sort * find(sort * const * s); psort_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n); virtual void finalize(pdecl_manager & m); virtual ~psort_decl() {} public: virtual sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) = 0; virtual sort * instantiate(pdecl_manager & m, unsigned n, unsigned const * s) { return 0; } virtual sort * instantiate(pdecl_manager & m) { return instantiate(m, 0, static_cast(0)); } // return true if the declaration accepts a variable number of parameters. // Only builtin declarations can have a variable number of parameters. bool has_var_params() const { return m_num_params == PSORT_DECL_VAR_PARAMS; } symbol const & get_name() const { return m_name; } }; class psort_user_decl : public psort_decl { protected: friend class pdecl_manager; psort * m_def; psort_user_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, psort * p); virtual size_t obj_size() const { return sizeof(psort_user_decl); } virtual void finalize(pdecl_manager & m); virtual ~psort_user_decl() {} public: virtual sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s); virtual void display(std::ostream & out) const; }; class psort_builtin_decl : public psort_decl { protected: friend class pdecl_manager; family_id m_fid; decl_kind m_kind; psort_builtin_decl(unsigned id, pdecl_manager & m, symbol const & n, family_id fid, decl_kind k); virtual size_t obj_size() const { return sizeof(psort_builtin_decl); } virtual ~psort_builtin_decl() {} public: virtual sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s); virtual sort * instantiate(pdecl_manager & m, unsigned n, unsigned const * s); virtual void display(std::ostream & out) const; }; class datatype_decl_plugin; class datatype_decl; class constructor_decl; class accessor_decl; class pdatatypes_decl; class pdatatype_decl; class pconstructor_decl; class paccessor_decl; enum ptype_kind { PTR_PSORT, // psort PTR_REC_REF, // recursive reference PTR_MISSING_REF // a symbol, it is useful for building parsers. }; class ptype { ptype_kind m_kind; union { psort * m_sort; int m_idx; }; symbol m_missing_ref; public: ptype():m_kind(PTR_PSORT), m_sort(0) {} ptype(int idx):m_kind(PTR_REC_REF), m_idx(idx) {} ptype(psort * s):m_kind(PTR_PSORT), m_sort(s) {} ptype(symbol const & s):m_kind(PTR_MISSING_REF), m_missing_ref(s) {} ptype_kind kind() const { return m_kind; } psort * get_psort() const { SASSERT(kind() == PTR_PSORT); return m_sort; } int get_idx() const { SASSERT(kind() == PTR_REC_REF); return m_idx; } symbol const & get_missing_ref() const { SASSERT(kind() == PTR_MISSING_REF); return m_missing_ref; } void display(std::ostream & out, pdatatype_decl const * const * dts) const; }; class paccessor_decl : public pdecl { friend class pdecl_manager; friend class pconstructor_decl; friend class pdatatype_decl; symbol m_name; ptype m_type; paccessor_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, ptype const & r); virtual void finalize(pdecl_manager & m); virtual size_t obj_size() const { return sizeof(paccessor_decl); } bool has_missing_refs(symbol & missing) const; bool fix_missing_refs(dictionary const & symbol2idx, symbol & missing); accessor_decl * instantiate_decl(pdecl_manager & m, sort * const * s); symbol const & get_name() const { return m_name; } ptype const & get_type() const { return m_type; } virtual ~paccessor_decl() {} public: virtual void display(std::ostream & out) const { pdecl::display(out); } void display(std::ostream & out, pdatatype_decl const * const * dts) const; }; class pconstructor_decl : public pdecl { friend class pdecl_manager; friend class pdatatype_decl; symbol m_name; symbol m_recogniser_name; ptr_vector m_accessors; pconstructor_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, symbol const & r, unsigned num_accessors, paccessor_decl * const * accessors); virtual void finalize(pdecl_manager & m); virtual size_t obj_size() const { return sizeof(pconstructor_decl); } bool has_missing_refs(symbol & missing) const; bool fix_missing_refs(dictionary const & symbol2idx, symbol & missing); symbol const & get_name() const { return m_name; } symbol const & get_recognizer_name() const { return m_recogniser_name; } constructor_decl * instantiate_decl(pdecl_manager & m, sort * const * s); virtual ~pconstructor_decl() {} public: virtual void display(std::ostream & out) const { pdecl::display(out); } void display(std::ostream & out, pdatatype_decl const * const * dts) const; }; class pdatatype_decl : public psort_decl { friend class pdecl_manager; friend class pdatatypes_decl; ptr_vector m_constructors; pdatatypes_decl * m_parent; pdatatype_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, unsigned num_constructors, pconstructor_decl * const * constructors); virtual void finalize(pdecl_manager & m); virtual size_t obj_size() const { return sizeof(pdatatype_decl); } bool fix_missing_refs(dictionary const & symbol2idx, symbol & missing); datatype_decl * instantiate_decl(pdecl_manager & m, sort * const * s); virtual ~pdatatype_decl() {} public: sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s); virtual void display(std::ostream & out) const; bool has_missing_refs(symbol & missing) const; bool has_duplicate_accessors(symbol & repeated) const; }; /** \brief Represents a set of parametric mutually recursive inductive data-types. */ class pdatatypes_decl : public pdecl { friend class pdecl_manager; friend class pdatatype_decl; ptr_vector m_datatypes; pdatatypes_decl(unsigned id, unsigned num_params, pdecl_manager & m, unsigned num_datatypes, pdatatype_decl * const * dts); virtual void finalize(pdecl_manager & m); virtual size_t obj_size() const { return sizeof(pdatatypes_decl); } bool fix_missing_refs(symbol & missing); bool instantiate(pdecl_manager & m, sort * const * s); virtual ~pdatatypes_decl() {} public: pdatatype_decl const * const * children() const { return m_datatypes.c_ptr(); } }; class new_datatype_eh { public: virtual ~new_datatype_eh() {} virtual void operator()(sort * dt) = 0; }; class pdecl_manager { ast_manager & m_manager; small_object_allocator & m_allocator; id_gen m_id_gen; obj_map m_sort2psort; psort_table m_table; ptr_vector m_to_delete; pdatatype_decl * m_list; family_id m_datatype_fid; new_datatype_eh * m_new_dt_eh; struct sort_info; struct app_sort_info; struct indexed_sort_info; obj_map m_sort2info; // for pretty printing sorts void init_list(); void del_decl_core(pdecl * p); void del_decl(pdecl * p); void del_decls(); psort * register_psort(psort * n); void reset_sort_info(); public: pdecl_manager(ast_manager & m); ~pdecl_manager(); ast_manager & m() const { return m_manager; } small_object_allocator & a() const { return m_allocator; } family_id get_datatype_fid() const { return m_datatype_fid; } void set_new_datatype_eh(new_datatype_eh * eh) { m_new_dt_eh = eh; } psort * mk_psort_cnst(sort * s); psort * mk_psort_var(unsigned num_params, unsigned vidx); psort * mk_psort_app(unsigned num_params, psort_decl * d, unsigned num_args, psort * const * args); psort * mk_psort_app(psort_decl * d); psort_decl * mk_psort_user_decl(unsigned num_params, symbol const & n, psort * def); psort_decl * mk_psort_builtin_decl(symbol const & n, family_id fid, decl_kind k); paccessor_decl * mk_paccessor_decl(unsigned num_params, symbol const & s, ptype const & p); pconstructor_decl * mk_pconstructor_decl(unsigned num_params, symbol const & s, symbol const & r, unsigned num, paccessor_decl * const * as); pdatatype_decl * mk_pdatatype_decl(unsigned num_params, symbol const & s, unsigned num, pconstructor_decl * const * cs); pdatatypes_decl * mk_pdatatypes_decl(unsigned num_params, unsigned num, pdatatype_decl * const * dts); pdatatype_decl * mk_plist_decl() { if (!m_list) init_list(); return m_list; } bool fix_missing_refs(pdatatypes_decl * s, symbol & missing) { return s->fix_missing_refs(missing); } sort * instantiate(psort * s, unsigned num, sort * const * args); void lazy_dec_ref(pdecl * p) { p->dec_ref(); if (p->get_ref_count() == 0) m_to_delete.push_back(p); } template void lazy_dec_ref(unsigned num, T * const * ps) { for (unsigned i = 0; i < num; i++) lazy_dec_ref(ps[i]); } void inc_ref(pdecl * p) { if (p) { p->inc_ref(); } } void dec_ref(pdecl * p) { if (p) { lazy_dec_ref(p); del_decls(); } } template void inc_ref(unsigned num, T * const * ps) { for (unsigned i = 0; i < num; i++) inc_ref(ps[i]); } template void dec_ref(unsigned num, T * const * ps) { lazy_dec_ref(num, ps); del_decls(); } psort_inst_cache * mk_inst_cache(unsigned num_params); void del_inst_cache(psort_inst_cache * c); void notify_new_dt(sort * dt) { if (m_new_dt_eh) (*m_new_dt_eh)(dt); } datatype_decl_plugin * get_dt_plugin() const; void save_info(sort * s, psort_decl * d, unsigned num_args, sort * const * args); void save_info(sort * s, psort_decl * d, unsigned num_indices, unsigned const * indices); void display(std::ostream & out, sort * s) const; format_ns::format * pp(sort * s) const; }; typedef obj_ref pdecl_ref; typedef obj_ref psort_ref; typedef obj_ref paccessor_decl_ref; typedef obj_ref pconstructor_decl_ref; typedef obj_ref pdatatype_decl_ref; typedef obj_ref pdatatypes_decl_ref; typedef ref_vector pdecl_ref_vector; typedef ref_vector psort_decl_ref_vector; typedef ref_vector psort_ref_vector; typedef ref_buffer paccessor_decl_ref_buffer; typedef ref_buffer pconstructor_decl_ref_buffer; typedef ref_buffer pdatatype_decl_ref_buffer; typedef ref_buffer pdatatypes_decl_ref_buffer; #endif z3-4.4.0/src/cmd_context/simplify_cmd.cpp0000644000175000017500000001065212540347414020132 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: simplify_cmd.cpp Abstract: SMT2 front-end 'simplify' command. Author: Leonardo (leonardo) 2011-04-20 Notes: --*/ #include"cmd_context.h" #include"th_rewriter.h" #include"shared_occs.h" #include"ast_smt_pp.h" #include"for_each_expr.h" #include"parametric_cmd.h" #include"scoped_timer.h" #include"scoped_ctrl_c.h" #include"cancel_eh.h" #include class simplify_cmd : public parametric_cmd { expr * m_target; public: simplify_cmd(char const * name = "simplify"):parametric_cmd(name) {} virtual char const * get_usage() const { return " ( )*"; } virtual char const * get_main_descr() const { return "simplify the given term using builtin theory simplification rules."; } virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { th_rewriter::get_param_descrs(p); insert_timeout(p); p.insert("print", CPK_BOOL, "(default: true) print the simplified term."); p.insert("print_proofs", CPK_BOOL, "(default: false) print a proof showing the original term is equal to the resultant one."); p.insert("print_statistics", CPK_BOOL, "(default: false) print statistics."); } virtual ~simplify_cmd() { } virtual void prepare(cmd_context & ctx) { parametric_cmd::prepare(ctx); m_target = 0; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { if (m_target == 0) return CPK_EXPR; return parametric_cmd::next_arg_kind(ctx); } virtual void set_next_arg(cmd_context & ctx, expr * arg) { m_target = arg; } virtual void execute(cmd_context & ctx) { if (m_target == 0) throw cmd_exception("invalid simplify command, argument expected"); expr_ref r(ctx.m()); proof_ref pr(ctx.m()); if (m_params.get_bool("som", false)) m_params.set_bool("flat", true); th_rewriter s(ctx.m(), m_params); unsigned cache_sz; unsigned num_steps = 0; unsigned timeout = m_params.get_uint("timeout", UINT_MAX); bool failed = false; cancel_eh eh(s); { scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); cmd_context::scoped_watch sw(ctx); try { s(m_target, r, pr); } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { ctx.regular_stream() << "(error \"simplifier failed: " << ex.msg() << "\")" << std::endl; failed = true; r = m_target; } cache_sz = s.get_cache_size(); num_steps = s.get_num_steps(); s.cleanup(); } if (m_params.get_bool("print", true)) { ctx.display(ctx.regular_stream(), r); ctx.regular_stream() << std::endl; } if (!failed && m_params.get_bool("print_proofs", false)) { ast_smt_pp pp(ctx.m()); pp.set_logic(ctx.get_logic().str().c_str()); pp.display_expr_smt2(ctx.regular_stream(), pr.get()); ctx.regular_stream() << std::endl; } if (m_params.get_bool("print_statistics", false)) { shared_occs s1(ctx.m()); if (!failed) s1(r); unsigned long long max_mem = memory::get_max_used_memory(); unsigned long long mem = memory::get_allocation_size(); ctx.regular_stream() << "(:time " << std::fixed << std::setprecision(2) << ctx.get_seconds() << " :num-steps " << num_steps << " :memory " << std::fixed << std::setprecision(2) << static_cast(mem)/static_cast(1024*1024) << " :max-memory " << std::fixed << std::setprecision(2) << static_cast(max_mem)/static_cast(1024*1024) << " :cache-size: " << cache_sz << " :num-nodes-before " << get_num_exprs(m_target); if (!failed) ctx.regular_stream() << " :num-shared " << s1.num_shared() << " :num-nodes " << get_num_exprs(r); ctx.regular_stream() << ")" << std::endl; } } }; void install_simplify_cmd(cmd_context & ctx, char const * cmd_name) { ctx.insert(alloc(simplify_cmd, cmd_name)); } z3-4.4.0/src/cmd_context/interpolant_cmds.h0000644000175000017500000000051012540347414020455 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: interpolant_cmds.h Abstract: Commands for interpolation. Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #ifndef _INTERPOLANT_CMDS_H_ #define _INTERPOLANT_CMDS_H_ class cmd_context; void install_interpolant_cmds(cmd_context & ctx); #endif z3-4.4.0/src/cmd_context/check_logic.h0000644000175000017500000000113012540347414017341 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: check_logic.h Abstract: Check whether a given assertion is in the correct logic or not Author: Leonardo de Moura (leonardo) 2011-08-11. Revision History: --*/ #ifndef _CHECK_LOGIC_H_ #define _CHECK_LOGIC_H_ #include"ast.h" class check_logic { struct imp; imp * m_imp; public: check_logic(); ~check_logic(); void reset(); void set_logic(ast_manager & m, symbol const & logic); bool operator()(expr * n); bool operator()(func_decl * f); char const * get_last_error() const; }; #endif z3-4.4.0/src/cmd_context/parametric_cmd.h0000644000175000017500000000452312540347414020072 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: parametric_cmd.h Abstract: A generic parametric cmd. Author: Leonardo (leonardo) 2011-04-22 Notes: --*/ #ifndef _PARAMETRIC_CMD_H_ #define _PARAMETRIC_CMD_H_ #include"params.h" #include"symbol.h" #include"string_buffer.h" #include"cmd_context_types.h" class parametric_cmd : public cmd { public: symbol m_last; string_buffer<> * m_descr; params_ref m_params; scoped_ptr m_pdescrs; public: parametric_cmd(char const * name):cmd(name), m_descr(0) {} virtual ~parametric_cmd() { if (m_descr) dealloc(m_descr); } virtual void init_pdescrs(cmd_context & ctx, param_descrs & d) = 0; param_descrs const & pdescrs(cmd_context & ctx) const; params_ref const & ps() const { return m_params; } virtual char const * get_main_descr() const = 0; virtual char const * get_descr(cmd_context & ctx) const; virtual unsigned get_arity() const { return VAR_ARITY; } virtual void prepare(cmd_context & ctx) { m_last = symbol::null; m_params.reset(); } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const; virtual void set_next_arg(cmd_context & ctx, symbol const & s); virtual void set_next_arg(cmd_context & ctx, unsigned val) { m_params.set_uint(m_last, val); m_last = symbol::null; } virtual void set_next_arg(cmd_context & ctx, bool val) { m_params.set_bool(m_last, val); m_last = symbol::null; } virtual void set_next_arg(cmd_context & ctx, rational const & val) { m_params.set_rat(m_last, val); m_last = symbol::null; } virtual void set_next_arg(cmd_context & ctx, char const * val) { m_params.set_str(m_last, val); m_last = symbol::null; } virtual void set_next_arg(cmd_context & ctx, sort * s) { NOT_IMPLEMENTED_YET(); // m_params.set_sort(m_last, s); // m_last = symbol::null; } virtual void set_next_arg(cmd_context & ctx, expr * t) { NOT_IMPLEMENTED_YET(); // m_params.set_expr(m_last, t); // m_last = symbol::null; } virtual void set_next_arg(cmd_context & ctx, func_decl * f) { NOT_IMPLEMENTED_YET(); // m_params.set_func_decl(m_last, f); // m_last = symbol::null; } }; #endif z3-4.4.0/src/cmd_context/basic_cmds.cpp0000644000175000017500000007210612540347414017544 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: basic_cmds.cpp Abstract: Basic commands for SMT2 front end. Author: Leonardo (leonardo) 2011-03-30 Notes: --*/ #include"cmd_context.h" #include"version.h" #include"ast_smt_pp.h" #include"ast_smt2_pp.h" #include"ast_pp.h" #include"model_smt2_pp.h" #include"model_v2_pp.h" #include"array_decl_plugin.h" #include"pp.h" #include"cmd_util.h" #include"simplify_cmd.h" #include"eval_cmd.h" #include"gparams.h" #include"model_params.hpp" #include"env_params.h" class help_cmd : public cmd { svector m_cmds; void display_cmd(cmd_context & ctx, symbol const & s, cmd * c) { char const * usage = c->get_usage(); char const * descr = c->get_descr(ctx); ctx.regular_stream() << " (" << s; if (usage) ctx.regular_stream() << " " << escaped(usage, true) << ")\n"; else ctx.regular_stream() << ")\n"; if (descr) { ctx.regular_stream() << " " << escaped(descr, true, 4) << "\n"; } } public: help_cmd():cmd("help") {} virtual char const * get_usage() const { return "*"; } virtual char const * get_descr(cmd_context & ctx) const { return "print this help."; } virtual unsigned get_arity() const { return VAR_ARITY; } virtual void prepare(cmd_context & ctx) { m_cmds.reset(); } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_SYMBOL; } virtual void set_next_arg(cmd_context & ctx, symbol const & s) { cmd * c = ctx.find_cmd(s); if (c == 0) { std::string err_msg("unknown command '"); err_msg = err_msg + s.bare_str() + "'"; throw cmd_exception(err_msg); } m_cmds.push_back(s); } typedef std::pair named_cmd; struct named_cmd_lt { bool operator()(named_cmd const & c1, named_cmd const & c2) const { return c1.first.str() < c2.first.str(); } }; virtual void execute(cmd_context & ctx) { ctx.regular_stream() << "\""; if (m_cmds.empty()) { vector cmds; cmd_context::cmd_iterator it = ctx.begin_cmds(); cmd_context::cmd_iterator end = ctx.end_cmds(); for (; it != end; ++it) { cmds.push_back(named_cmd((*it).m_key, (*it).m_value)); } // named_cmd_lt is not a total order for commands, but this is irrelevant for Linux x Windows behavior std::sort(cmds.begin(), cmds.end(), named_cmd_lt()); vector::const_iterator it2 = cmds.begin(); vector::const_iterator end2 = cmds.end(); for (; it2 != end2; ++it2) { display_cmd(ctx, it2->first, it2->second); } } else { svector::const_iterator it = m_cmds.begin(); svector::const_iterator end = m_cmds.end(); for (; it != end; ++it) { cmd * c = ctx.find_cmd(*it); SASSERT(c); display_cmd(ctx, *it, c); } } ctx.regular_stream() << "\"\n"; } }; ATOMIC_CMD(exit_cmd, "exit", "exit.", ctx.print_success(); throw stop_parser_exception();); ATOMIC_CMD(get_model_cmd, "get-model", "retrieve model for the last check-sat command", { if (!ctx.is_model_available() || ctx.get_check_sat_result() == 0) throw cmd_exception("model is not available"); model_ref m; ctx.get_check_sat_result()->get_model(m); model_params p; if (p.v1() || p.v2()) { std::ostringstream buffer; model_v2_pp(buffer, *m, p.partial()); ctx.regular_stream() << "\"" << escaped(buffer.str().c_str(), true) << "\"" << std::endl; } else { ctx.regular_stream() << "(model " << std::endl; model_smt2_pp(ctx.regular_stream(), ctx, *(m.get()), 2); // m->display(ctx.regular_stream()); ctx.regular_stream() << ")" << std::endl; } }); ATOMIC_CMD(get_assignment_cmd, "get-assignment", "retrieve assignment", { if (!ctx.is_model_available() || ctx.get_check_sat_result() == 0) throw cmd_exception("model is not available"); model_ref m; ctx.get_check_sat_result()->get_model(m); ctx.regular_stream() << "("; dictionary const & macros = ctx.get_macros(); dictionary::iterator it = macros.begin(); dictionary::iterator end = macros.end(); for (bool first = true; it != end; ++it) { symbol const & name = (*it).m_key; cmd_context::macro const & _m = (*it).m_value; if (_m.first == 0 && ctx.m().is_bool(_m.second)) { expr_ref val(ctx.m()); m->eval(_m.second, val, true); if (ctx.m().is_true(val) || ctx.m().is_false(val)) { if (first) first = false; else ctx.regular_stream() << " "; ctx.regular_stream() << "(" << name << " " << (ctx.m().is_true(val) ? "true" : "false") << ")"; } } } ctx.regular_stream() << ")" << std::endl; }); class cmd_is_declared : public ast_smt_pp::is_declared { cmd_context& m_ctx; public: cmd_is_declared(cmd_context& ctx): m_ctx(ctx) {} virtual bool operator()(func_decl* d) const { return m_ctx.is_func_decl(d->get_name()); } virtual bool operator()(sort* s) const { return m_ctx.is_sort_decl(s->get_name()); } }; ATOMIC_CMD(get_proof_cmd, "get-proof", "retrieve proof", { if (!ctx.produce_proofs()) throw cmd_exception("proof construction is not enabled, use command (set-option :produce-proofs true)"); if (!ctx.has_manager() || ctx.cs_state() != cmd_context::css_unsat) throw cmd_exception("proof is not available"); expr_ref pr(ctx.m()); pr = ctx.get_check_sat_result()->get_proof(); if (pr == 0) throw cmd_exception("proof is not available"); // TODO: reimplement a new SMT2 pretty printer ast_smt_pp pp(ctx.m()); cmd_is_declared isd(ctx); pp.set_is_declared(&isd); pp.set_logic(ctx.get_logic().str().c_str()); pp.display_smt2(ctx.regular_stream(), pr); ctx.regular_stream() << std::endl; }); ATOMIC_CMD(get_unsat_core_cmd, "get-unsat-core", "retrieve unsat core", { if (!ctx.produce_unsat_cores()) throw cmd_exception("unsat core construction is not enabled, use command (set-option :produce-unsat-cores true)"); if (!ctx.has_manager() || ctx.cs_state() != cmd_context::css_unsat) throw cmd_exception("unsat core is not available"); ptr_vector core; ctx.get_check_sat_result()->get_unsat_core(core); ctx.regular_stream() << "("; ptr_vector::const_iterator it = core.begin(); ptr_vector::const_iterator end = core.end(); for (bool first = true; it != end; ++it) { if (first) first = false; else ctx.regular_stream() << " "; ctx.regular_stream() << mk_ismt2_pp(*it, ctx.m()); } ctx.regular_stream() << ")" << std::endl; }); ATOMIC_CMD(labels_cmd, "labels", "retrieve Simplify-like labels", { if (!ctx.has_manager() || (ctx.cs_state() != cmd_context::css_sat && ctx.cs_state() != cmd_context::css_unknown)) throw cmd_exception("labels are not available"); svector labels; ctx.get_check_sat_result()->get_labels(labels); ctx.regular_stream() << "(labels"; for (unsigned i = 0; i < labels.size(); i++) { ctx.regular_stream() << " " << labels[i]; } ctx.regular_stream() << ")" << std::endl; }); ATOMIC_CMD(get_assertions_cmd, "get-assertions", "retrieve asserted terms when in interactive mode", ctx.display_assertions();); UNARY_CMD(set_logic_cmd, "set-logic", "", "set the background logic.", CPK_SYMBOL, symbol const &, if (ctx.set_logic(arg)) ctx.print_success(); else ctx.print_unsupported(symbol::null); ); UNARY_CMD(pp_cmd, "display", "", "display the given term.", CPK_EXPR, expr *, { ctx.display(ctx.regular_stream(), arg); ctx.regular_stream() << std::endl; }); UNARY_CMD(echo_cmd, "echo", "", "display the given string", CPK_STRING, char const *, ctx.regular_stream() << arg << std::endl;); class set_get_option_cmd : public cmd { protected: symbol m_true; symbol m_false; symbol m_print_success; symbol m_print_warning; symbol m_expand_definitions; symbol m_interactive_mode; symbol m_produce_proofs; symbol m_produce_unsat_cores; symbol m_produce_models; symbol m_produce_assignments; symbol m_produce_interpolants; symbol m_regular_output_channel; symbol m_diagnostic_output_channel; symbol m_random_seed; symbol m_verbosity; symbol m_global_decls; symbol m_numeral_as_real; symbol m_error_behavior; symbol m_int_real_coercions; bool is_builtin_option(symbol const & s) const { return s == m_print_success || s == m_print_warning || s == m_expand_definitions || s == m_interactive_mode || s == m_produce_proofs || s == m_produce_unsat_cores || s == m_produce_models || s == m_produce_assignments || s == m_produce_interpolants || s == m_regular_output_channel || s == m_diagnostic_output_channel || s == m_random_seed || s == m_verbosity || s == m_global_decls; } public: set_get_option_cmd(char const * name): cmd(name), m_true("true"), m_false("false"), m_print_success(":print-success"), m_print_warning(":print-warning"), m_expand_definitions(":expand-definitions"), m_interactive_mode(":interactive-mode"), m_produce_proofs(":produce-proofs"), m_produce_unsat_cores(":produce-unsat-cores"), m_produce_models(":produce-models"), m_produce_assignments(":produce-assignments"), m_produce_interpolants(":produce-interpolants"), m_regular_output_channel(":regular-output-channel"), m_diagnostic_output_channel(":diagnostic-output-channel"), m_random_seed(":random-seed"), m_verbosity(":verbosity"), m_global_decls(":global-decls"), m_numeral_as_real(":numeral-as-real"), m_error_behavior(":error-behavior"), m_int_real_coercions(":int-real-coercions") { } virtual ~set_get_option_cmd() {} }; class set_option_cmd : public set_get_option_cmd { bool m_unsupported; symbol m_option; bool to_bool(symbol const & value) const { if (value != m_true && value != m_false) throw cmd_exception("invalid option value, true/false expected"); return value == m_true; } static unsigned to_unsigned(rational const & val) { if (!val.is_unsigned()) throw cmd_exception("option value is too big to fit in a machine integer."); return val.get_unsigned(); } static void check_not_initialized(cmd_context & ctx, symbol const & opt_name) { if (ctx.has_manager()) { std::string msg = "error setting '"; msg += opt_name.str(); msg += "', option value cannot be modified after initialization"; throw cmd_exception(msg); } } void set_param(cmd_context & ctx, char const * value) { try { gparams::set(m_option, value); env_params::updt_params(); ctx.global_params_updated(); } catch (gparams::exception ex) { throw cmd_exception(ex.msg()); } } void set_symbol(cmd_context & ctx, symbol const & value) { if (m_option == m_print_success) { ctx.set_print_success(to_bool(value)); } else if (m_option == m_print_warning) { enable_warning_messages(to_bool(value)); } else if (m_option == m_expand_definitions) { m_unsupported = true; } else if (m_option == m_interactive_mode) { check_not_initialized(ctx, m_interactive_mode); ctx.set_interactive_mode(to_bool(value)); } else if (m_option == m_produce_proofs) { check_not_initialized(ctx, m_produce_proofs); ctx.set_produce_proofs(to_bool(value)); } else if (m_option == m_produce_interpolants) { check_not_initialized(ctx, m_produce_interpolants); ctx.set_produce_interpolants(to_bool(value)); } else if (m_option == m_produce_unsat_cores) { check_not_initialized(ctx, m_produce_unsat_cores); ctx.set_produce_unsat_cores(to_bool(value)); } else if (m_option == m_produce_models) { ctx.set_produce_models(to_bool(value)); } else if (m_option == m_produce_assignments) { ctx.set_produce_assignments(to_bool(value)); } else if (m_option == m_global_decls) { check_not_initialized(ctx, m_global_decls); ctx.set_global_decls(to_bool(value)); } else if (m_option == m_numeral_as_real) { ctx.set_numeral_as_real(to_bool(value)); } else if (m_option == m_int_real_coercions) { ctx.m().enable_int_real_coercions(to_bool(value)); } #ifdef Z3DEBUG else if (m_option == ":enable-assertions") { enable_assertions(to_bool(value)); } #endif else if (m_option == m_error_behavior) { if (value == "immediate-exit") { ctx.set_exit_on_error(true); } else if (value == "continued-execution") { ctx.set_exit_on_error(false); } else { throw cmd_exception("error setting :error-behavior, 'immediate-execution' or 'continued-execution' expected"); } } else if (is_builtin_option(m_option)) { throw cmd_exception("option value is not a symbol"); } else { set_param(ctx, value.bare_str()); } } public: set_option_cmd(): set_get_option_cmd("set-option"), m_unsupported(false) { } virtual char const * get_usage() const { return " "; } virtual char const * get_descr(cmd_context & ctx) const { return "set configuration option."; } virtual unsigned get_arity() const { return 2; } virtual void prepare(cmd_context & ctx) { m_unsupported = false; m_option = symbol::null; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return m_option == symbol::null ? CPK_KEYWORD : CPK_OPTION_VALUE; } virtual void set_next_arg(cmd_context & ctx, symbol const & opt) { if (m_option == symbol::null) { m_option = opt; } else { set_symbol(ctx, opt); } } virtual void set_next_arg(cmd_context & ctx, rational const & val) { if (m_option == m_random_seed) { ctx.set_random_seed(to_unsigned(val)); } else if (m_option == m_verbosity) { set_verbosity_level(to_unsigned(val)); } else if (is_builtin_option(m_option)) { throw cmd_exception("option value is not a numeral"); } else { std::string str = val.to_string(); set_param(ctx, str.c_str()); } } virtual void set_next_arg(cmd_context & ctx, char const * value) { if (m_option == m_regular_output_channel) { ctx.set_regular_stream(value); } else if (m_option == m_diagnostic_output_channel) { ctx.set_diagnostic_stream(value); } else if (is_builtin_option(m_option)) { throw cmd_exception("option value is not a string"); } else { set_param(ctx, value); } } virtual void execute(cmd_context & ctx) { if (m_unsupported) ctx.print_unsupported(m_option); else ctx.print_success(); } }; class get_option_cmd : public set_get_option_cmd { static void print_bool(cmd_context & ctx, bool b) { ctx.regular_stream() << (b ? "true" : "false") << std::endl; } static void print_unsigned(cmd_context & ctx, unsigned v) { ctx.regular_stream() << v << std::endl; } static void print_string(cmd_context & ctx, char const * str) { ctx.regular_stream() << str << std::endl; } public: get_option_cmd(): set_get_option_cmd("get-option") { } virtual char const * get_usage() const { return ""; } virtual char const * get_descr(cmd_context & ctx) const { return "get configuration option."; } virtual unsigned get_arity() const { return 1; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_KEYWORD; } virtual void set_next_arg(cmd_context & ctx, symbol const & opt) { if (opt == m_print_success) { print_bool(ctx, ctx.print_success_enabled()); } // TODO: // else if (opt == m_print_warning) { // print_bool(ctx, ); // } else if (opt == m_expand_definitions) { ctx.print_unsupported(m_expand_definitions); } else if (opt == m_interactive_mode) { print_bool(ctx, ctx.interactive_mode()); } else if (opt == m_produce_proofs) { print_bool(ctx, ctx.produce_proofs()); } else if (opt == m_produce_interpolants) { print_bool(ctx, ctx.produce_interpolants()); } else if (opt == m_produce_unsat_cores) { print_bool(ctx, ctx.produce_unsat_cores()); } else if (opt == m_produce_models) { print_bool(ctx, ctx.produce_models()); } else if (opt == m_produce_assignments) { print_bool(ctx, ctx.produce_assignments()); } else if (opt == m_global_decls) { print_bool(ctx, ctx.global_decls()); } else if (opt == m_random_seed) { print_unsigned(ctx, ctx.random_seed()); } else if (opt == m_verbosity) { print_unsigned(ctx, get_verbosity_level()); } else if (opt == m_regular_output_channel) { print_string(ctx, ctx.get_regular_stream_name()); } else if (opt == m_diagnostic_output_channel) { print_string(ctx, ctx.get_diagnostic_stream_name()); } else if (opt == m_error_behavior) { if (ctx.exit_on_error()) { print_string(ctx, "immediate-exit"); } else { print_string(ctx, "continued-execution"); } } else if (opt == m_int_real_coercions) { print_bool(ctx, ctx.m().int_real_coercions()); } else { try { ctx.regular_stream() << gparams::get_value(opt) << std::endl; } catch (gparams::exception ex) { ctx.print_unsupported(opt); } } } }; #define STRINGIZE(x) #x #define STRINGIZE_VALUE_OF(x) STRINGIZE(x) class get_info_cmd : public cmd { symbol m_error_behavior; symbol m_name; symbol m_authors; symbol m_version; symbol m_status; symbol m_reason_unknown; symbol m_all_statistics; public: get_info_cmd(): cmd("get-info"), m_error_behavior(":error-behavior"), m_name(":name"), m_authors(":authors"), m_version(":version"), m_status(":status"), m_reason_unknown(":reason-unknown"), m_all_statistics(":all-statistics") { } virtual char const * get_usage() const { return ""; } virtual char const * get_descr(cmd_context & ctx) const { return "get information."; } virtual unsigned get_arity() const { return 1; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_KEYWORD; } virtual void set_next_arg(cmd_context & ctx, symbol const & opt) { if (opt == m_error_behavior) { if (ctx.exit_on_error()) ctx.regular_stream() << "(:error-behavior immediate-exit)" << std::endl; else ctx.regular_stream() << "(:error-behavior continued-execution)" << std::endl; } else if (opt == m_name) { ctx.regular_stream() << "(:name \"Z3\")" << std::endl; } else if (opt == m_authors) { ctx.regular_stream() << "(:authors \"Leonardo de Moura and Nikolaj Bjorner\")" << std::endl; } else if (opt == m_version) { ctx.regular_stream() << "(:version \"" << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER #ifdef Z3GITHASH << " - build hashcode " << STRINGIZE_VALUE_OF(Z3GITHASH) #endif << "\")" << std::endl; } else if (opt == m_status) { ctx.regular_stream() << "(:status " << ctx.get_status() << ")" << std::endl; } else if (opt == m_reason_unknown) { ctx.regular_stream() << "(:reason-unknown " << ctx.reason_unknown() << ")" << std::endl; } else if (opt == m_all_statistics) { ctx.display_statistics(); } else { ctx.print_unsupported(opt); } } }; class set_info_cmd : public cmd { symbol m_info; symbol m_status; symbol m_unsat; symbol m_sat; symbol m_unknown; public: set_info_cmd(): cmd("set-info"), m_status(":status"), m_unsat("unsat"), m_sat("sat"), m_unknown("unknown") { } virtual char const * get_usage() const { return " "; } virtual char const * get_descr(cmd_context & ctx) const { return "set information."; } virtual unsigned get_arity() const { return 2; } virtual void prepare(cmd_context & ctx) { m_info = symbol::null; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return m_info == symbol::null ? CPK_KEYWORD : CPK_OPTION_VALUE; } virtual void set_next_arg(cmd_context & ctx, rational const & val) {} virtual void set_next_arg(cmd_context & ctx, char const * val) {} virtual void set_next_arg(cmd_context & ctx, symbol const & s) { if (m_info == symbol::null) { m_info = s; } else { if (m_info == m_status) { if (s == m_unsat) { ctx.set_status(cmd_context::UNSAT); } else if (s == m_sat) { ctx.set_status(cmd_context::SAT); } else if (s == m_unknown) { ctx.set_status(cmd_context::UNKNOWN); } else { throw cmd_exception("invalid ':status' attribute"); } } } } virtual void execute(cmd_context & ctx) { ctx.print_success(); } }; class declare_map_cmd : public cmd { symbol m_array_sort; symbol m_name; ptr_vector m_domain; func_decl * m_f; family_id m_array_fid; public: declare_map_cmd(): cmd("declare-map"), m_array_sort("Array"), m_array_fid(null_family_id) {} family_id get_array_fid(cmd_context & ctx) { if (m_array_fid == null_family_id) { m_array_fid = ctx.m().mk_family_id("array"); } return m_array_fid; } virtual char const * get_usage() const { return " (+) "; } virtual char const * get_descr(cmd_context & ctx) const { return "declare a new array map operator with name using the given function declaration.\n ::= \n | ( (*) )\n | ((_ +) (*) )\nThe last two cases are used to disumbiguate between declarations with the same name and/or select (indexed) builtin declarations.\nFor more details about the the array map operator, see 'Generalized and Efficient Array Decision Procedures' (FMCAD 2009).\nExample: (declare-map set-union (Int) (or (Bool Bool) Bool))\nDeclares a new function (declare-fun set-union ((Array Int Bool) (Array Int Bool)) (Array Int Bool)).\nThe instance of the map axiom for this new declaration is:\n(forall ((a1 (Array Int Bool)) (a2 (Array Int Bool)) (i Int)) (= (select (set-union a1 a2) i) (or (select a1 i) (select a2 i))))"; } virtual unsigned get_arity() const { return 3; } virtual void prepare(cmd_context & ctx) { m_name = symbol::null; m_domain.reset(); } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { if (m_name == symbol::null) return CPK_SYMBOL; if (m_domain.empty()) return CPK_SORT_LIST; return CPK_FUNC_DECL; } virtual void set_next_arg(cmd_context & ctx, symbol const & s) { m_name = s; } virtual void set_next_arg(cmd_context & ctx, unsigned num, sort * const * slist) { if (num == 0) throw cmd_exception("invalid map declaration, empty sort list"); m_domain.append(num, slist); } virtual void set_next_arg(cmd_context & ctx, func_decl * f) { m_f = f; if (m_f->get_arity() == 0) throw cmd_exception("invalid map declaration, function declaration must have arity > 0"); } virtual void reset(cmd_context & ctx) { m_array_fid = null_family_id; } virtual void execute(cmd_context & ctx) { psort_decl * array_sort = ctx.find_psort_decl(m_array_sort); if (array_sort == 0) throw cmd_exception("Array sort is not available"); ptr_vector & array_sort_args = m_domain; sort_ref_buffer domain(ctx.m()); unsigned arity = m_f->get_arity(); for (unsigned i = 0; i < arity; i++) { array_sort_args.push_back(m_f->get_domain(i)); domain.push_back(array_sort->instantiate(ctx.pm(), array_sort_args.size(), array_sort_args.c_ptr())); array_sort_args.pop_back(); } sort_ref range(ctx.m()); array_sort_args.push_back(m_f->get_range()); range = array_sort->instantiate(ctx.pm(), array_sort_args.size(), array_sort_args.c_ptr()); parameter p[1] = { parameter(m_f) }; func_decl_ref new_map(ctx.m()); new_map = ctx.m().mk_func_decl(get_array_fid(ctx), OP_ARRAY_MAP, 1, p, domain.size(), domain.c_ptr(), range.get()); if (new_map == 0) throw cmd_exception("invalid array map operator"); ctx.insert(m_name, new_map); } }; // provides "help" for builtin cmds class builtin_cmd : public cmd { char const * m_usage; char const * m_descr; public: builtin_cmd(char const * name, char const * usage, char const * descr): cmd(name), m_usage(usage), m_descr(descr) {} virtual char const * get_usage() const { return m_usage; } virtual char const * get_descr(cmd_context & ctx) const { return m_descr; } }; void install_basic_cmds(cmd_context & ctx) { ctx.insert(alloc(set_logic_cmd)); ctx.insert(alloc(exit_cmd)); ctx.insert(alloc(get_assignment_cmd)); ctx.insert(alloc(get_assertions_cmd)); ctx.insert(alloc(get_proof_cmd)); ctx.insert(alloc(get_unsat_core_cmd)); ctx.insert(alloc(set_option_cmd)); ctx.insert(alloc(get_option_cmd)); ctx.insert(alloc(get_info_cmd)); ctx.insert(alloc(set_info_cmd)); ctx.insert(alloc(builtin_cmd, "assert", "", "assert term.")); ctx.insert(alloc(builtin_cmd, "check-sat", "*", "check if the current context is satisfiable. If a list of boolean constants B is provided, then check if the current context is consistent with assigning every constant in B to true.")); ctx.insert(alloc(builtin_cmd, "push", "?", "push 1 (or ) scopes.")); ctx.insert(alloc(builtin_cmd, "pop", "?", "pop 1 (or ) scopes.")); ctx.insert(alloc(builtin_cmd, "get-value", "(+)", "evaluate the given terms in the current model.")); ctx.insert(alloc(builtin_cmd, "declare-sort", " ?", "declare a new uninterpreted sort of arity , if arity is not provided, then it is assumed to be 0.")); ctx.insert(alloc(builtin_cmd, "define-sort", " (*) ", "define a new sort.")); ctx.insert(alloc(builtin_cmd, "declare-fun", " (*) ", "declare a new function/constant.")); ctx.insert(alloc(builtin_cmd, "declare-const", " ", "declare a new constant.")); ctx.insert(alloc(builtin_cmd, "declare-datatypes", "(*) (+)", "declare mutually recursive datatypes.\n ::= ( +)\n ::= ( *)\n ::= ( )\nexample: (declare-datatypes (T) ((BinTree (leaf (value T)) (node (left BinTree) (right BinTree)))))")); } void install_ext_basic_cmds(cmd_context & ctx) { ctx.insert(alloc(help_cmd)); ctx.insert(alloc(pp_cmd)); ctx.insert(alloc(get_model_cmd)); ctx.insert(alloc(echo_cmd)); ctx.insert(alloc(labels_cmd)); ctx.insert(alloc(declare_map_cmd)); ctx.insert(alloc(builtin_cmd, "reset", 0, "reset the shell (all declarations and assertions will be erased)")); install_simplify_cmd(ctx); install_eval_cmd(ctx); } z3-4.4.0/src/cmd_context/cmd_context.cpp0000644000175000017500000015731112540347414017766 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cmd_context.cpp Abstract: Command context. Author: Leonardo (leonardo) 2011-03-01 Notes: --*/ #include #include"tptr.h" #include"cmd_context.h" #include"func_decl_dependencies.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"array_decl_plugin.h" #include"datatype_decl_plugin.h" #include"seq_decl_plugin.h" #include"pb_decl_plugin.h" #include"fpa_decl_plugin.h" #include"ast_pp.h" #include"var_subst.h" #include"pp.h" #include"ast_smt2_pp.h" #include"basic_cmds.h" #include"cancel_eh.h" #include"scoped_ctrl_c.h" #include"dec_ref_util.h" #include"decl_collector.h" #include"well_sorted.h" #include"model_evaluator.h" #include"for_each_expr.h" #include"scoped_timer.h" #include"interpolant_cmds.h" #include"model_smt2_pp.h" func_decls::func_decls(ast_manager & m, func_decl * f): m_decls(TAG(func_decl*, f, 0)) { m.inc_ref(f); } void func_decls::finalize(ast_manager & m) { TRACE("cmd_context_detail", tout << "finalizing func_decls...\n";); if (GET_TAG(m_decls) == 0) { m.dec_ref(UNTAG(func_decl *, m_decls)); } else { TRACE("func_decls", tout << "finalize...\n";); func_decl_set * fs = UNTAG(func_decl_set *, m_decls); func_decl_set::iterator it = fs->begin(); func_decl_set::iterator end = fs->end(); for (; it != end; ++it) { TRACE("func_decls", tout << "dec_ref of " << (*it)->get_name() << " ref_count: " << (*it)->get_ref_count() << "\n";); m.dec_ref(*it); } dealloc(fs); } m_decls = 0; } bool func_decls::contains(func_decl * f) const { if (GET_TAG(m_decls) == 0) { return UNTAG(func_decl*, m_decls) == f; } else { func_decl_set * fs = UNTAG(func_decl_set *, m_decls); return fs->contains(f); } } bool func_decls::insert(ast_manager & m, func_decl * f) { if (contains(f)) return false; m.inc_ref(f); if (m_decls == 0) { m_decls = TAG(func_decl*, f, 0); } else if (GET_TAG(m_decls) == 0) { func_decl_set * new_fs = alloc(func_decl_set); new_fs->insert(UNTAG(func_decl*, m_decls)); new_fs->insert(f); m_decls = TAG(func_decl*, new_fs, 1); } else { func_decl_set * fs = UNTAG(func_decl_set*, m_decls); fs->insert(f); } return true; } void func_decls::erase(ast_manager & m, func_decl * f) { if (!contains(f)) return; if (GET_TAG(m_decls) == 0) { m.dec_ref(f); m_decls = 0; } else { func_decl_set * fs = UNTAG(func_decl_set *, m_decls); fs->erase(f); m.dec_ref(f); if (fs->empty()) { dealloc(fs); m_decls = 0; } } } /** \brief Return true if func_decls contains a declaration different from f, but with the same domain. */ bool func_decls::clash(func_decl * f) const { if (m_decls == 0) return false; if (GET_TAG(m_decls) == 0) return false; func_decl_set * fs = UNTAG(func_decl_set *, m_decls); func_decl_set::iterator it = fs->begin(); func_decl_set::iterator end = fs->end(); for (; it != end; ++it) { func_decl * g = *it; if (g == f) continue; if (g->get_arity() != f->get_arity()) continue; unsigned num = g->get_arity(); unsigned i; for (i = 0; i < num; i++) if (g->get_domain(i) != f->get_domain(i)) break; if (i == num) return true; } return false; } bool func_decls::more_than_one() const { if (m_decls == 0 || GET_TAG(m_decls) == 0) return false; func_decl_set * fs = UNTAG(func_decl_set *, m_decls); return fs->size() > 1; } func_decl * func_decls::first() const { if (m_decls == 0) return 0; if (GET_TAG(m_decls) == 0) return UNTAG(func_decl*, m_decls); func_decl_set * fs = UNTAG(func_decl_set *, m_decls); SASSERT(!fs->empty()); return *(fs->begin()); } func_decl * func_decls::find(unsigned arity, sort * const * domain, sort * range) const { if (!more_than_one()) return first(); func_decl_set * fs = UNTAG(func_decl_set *, m_decls); func_decl_set::iterator it = fs->begin(); func_decl_set::iterator end = fs->end(); for (; it != end; it++) { func_decl * f = *it; if (range != 0 && f->get_range() != range) continue; if (f->get_arity() != arity) continue; unsigned i = 0; for (i = 0; i < arity; i++) { if (f->get_domain(i) != domain[i]) break; } if (i == arity) return f; } return 0; } func_decl * func_decls::find(ast_manager & m, unsigned num_args, expr * const * args, sort * range) const { if (!more_than_one()) first(); ptr_buffer sorts; for (unsigned i = 0; i < num_args; i++) sorts.push_back(m.get_sort(args[i])); return find(num_args, sorts.c_ptr(), range); } ast_object_ref::ast_object_ref(cmd_context & ctx, ast * a):m_ast(a) { ctx.m().inc_ref(a); } void ast_object_ref::finalize(cmd_context & ctx) { ctx.m().dec_ref(m_ast); } void stream_ref::set(char const * name) { if (!name) { throw cmd_exception("invalid stream name"); } reset(); SASSERT(!m_owner); if (strcmp(name, "stdout") == 0) { m_name = "stdout"; m_stream = &std::cout; } else if (strcmp(name, "stderr") == 0) { m_name = "stderr"; m_stream = &std::cerr; } else { m_stream = alloc(std::ofstream, name, std::ios_base::app); m_name = name; m_owner = true; if (m_stream->bad() || m_stream->fail()) { reset(); std::string msg = "failed to set output stream '"; msg += name; msg += "'"; throw cmd_exception(msg); } SASSERT(m_stream); } } void stream_ref::reset() { if (m_owner) dealloc(m_stream); m_name = m_default_name; m_stream = &m_default; m_owner = false; } class cmd_context::pp_env : public smt2_pp_environment { protected: cmd_context & m_owner; arith_util m_autil; bv_util m_bvutil; array_util m_arutil; fpa_util m_futil; datalog::dl_decl_util m_dlutil; format_ns::format * pp_fdecl_name(symbol const & s, func_decls const & fs, func_decl * f, unsigned & len) { format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len); if (!fs.more_than_one()) return f_name; if (!fs.clash(f)) return f_name; return pp_as(f_name, f->get_range()); } format_ns::format * pp_fdecl_ref_core(symbol const & s, func_decls const & fs, func_decl * f) { unsigned len; format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len); if (!fs.more_than_one()) return f_name; return pp_signature(f_name, f); } public: pp_env(cmd_context & o):m_owner(o), m_autil(o.m()), m_bvutil(o.m()), m_arutil(o.m()), m_futil(o.m()), m_dlutil(o.m()) {} virtual ~pp_env() {} virtual ast_manager & get_manager() const { return m_owner.m(); } virtual arith_util & get_autil() { return m_autil; } virtual bv_util & get_bvutil() { return m_bvutil; } virtual array_util & get_arutil() { return m_arutil; } virtual fpa_util & get_futil() { return m_futil; } virtual datalog::dl_decl_util& get_dlutil() { return m_dlutil; } virtual bool uses(symbol const & s) const { return m_owner.m_builtin_decls.contains(s) || m_owner.m_func_decls.contains(s); } virtual format_ns::format * pp_sort(sort * s) { return m_owner.pp(s); } virtual format_ns::format * pp_fdecl(func_decl * f, unsigned & len) { symbol s = f->get_name(); func_decls fs; if (m_owner.m_func_decls.find(s, fs) && fs.contains(f)) { return pp_fdecl_name(s, fs, f, len); } if (m_owner.m_func_decl2alias.find(f, s) && m_owner.m_func_decls.find(s, fs) && fs.contains(f)) { return pp_fdecl_name(s, fs, f, len); } return smt2_pp_environment::pp_fdecl(f, len); } virtual format_ns::format * pp_fdecl_ref(func_decl * f) { symbol s = f->get_name(); func_decls fs; if (m_owner.m_func_decls.find(s, fs) && fs.contains(f)) { return pp_fdecl_ref_core(s, fs, f); } if (m_owner.m_func_decl2alias.find(f, s) && m_owner.m_func_decls.find(s, fs) && fs.contains(f)) { return pp_fdecl_ref_core(s, fs, f); } return smt2_pp_environment::pp_fdecl_ref(f); } }; cmd_context::cmd_context(bool main_ctx, ast_manager * m, symbol const & l): m_main_ctx(main_ctx), m_logic(l), m_interactive_mode(false), m_global_decls(false), m_print_success(m_params.m_smtlib2_compliant), m_random_seed(0), m_produce_unsat_cores(false), m_produce_assignments(false), m_status(UNKNOWN), m_numeral_as_real(false), m_ignore_check(false), m_exit_on_error(false), m_manager(m), m_own_manager(m == 0), m_manager_initialized(false), m_pmanager(0), m_sexpr_manager(0), m_regular("stdout", std::cout), m_diagnostic("stderr", std::cerr) { SASSERT(m != 0 || !has_manager()); install_basic_cmds(*this); install_ext_basic_cmds(*this); install_core_tactic_cmds(*this); install_interpolant_cmds(*this); SASSERT(m != 0 || !has_manager()); if (m_main_ctx) { set_verbose_stream(diagnostic_stream()); } } cmd_context::~cmd_context() { if (m_main_ctx) { set_verbose_stream(std::cerr); } finalize_cmds(); finalize_tactic_cmds(); finalize_probes(); reset(true); m_solver = 0; m_check_sat_result = 0; } void cmd_context::set_cancel(bool f) { if (m_solver) { if (f) { m_solver->cancel(); } else { m_solver->reset_cancel(); } } if (has_manager()) m().set_cancel(f); } opt_wrapper* cmd_context::get_opt() { return m_opt.get(); } void cmd_context::set_opt(opt_wrapper* opt) { m_opt = opt; for (unsigned i = 0; i < m_scopes.size(); ++i) { m_opt->push(); } m_opt->set_logic(m_logic); } void cmd_context::global_params_updated() { m_params.updt_params(); if (m_params.m_smtlib2_compliant) m_print_success = true; if (m_solver) { params_ref p; if (!m_params.m_auto_config) p.set_bool("auto_config", false); m_solver->updt_params(p); } } void cmd_context::set_produce_models(bool f) { if (m_solver) m_solver->set_produce_models(f); m_params.m_model = f; } void cmd_context::set_produce_unsat_cores(bool f) { // can only be set before initialization SASSERT(!has_manager()); m_params.m_unsat_core = f; } void cmd_context::set_produce_proofs(bool f) { // can only be set before initialization SASSERT(!has_manager()); m_params.m_proof = f; } void cmd_context::set_produce_interpolants(bool f) { // can only be set before initialization // FIXME currently synonym for produce_proofs // also sets the default solver to be simple smt SASSERT(!has_manager()); m_params.m_proof = f; // set_solver_factory(mk_smt_solver_factory()); } bool cmd_context::produce_models() const { return m_params.m_model; } bool cmd_context::produce_proofs() const { return m_params.m_proof; } bool cmd_context::produce_interpolants() const { // FIXME currently synonym for produce_proofs return m_params.m_proof; } bool cmd_context::produce_unsat_cores() const { return m_params.m_unsat_core; } bool cmd_context::well_sorted_check_enabled() const { return m_params.m_well_sorted_check; } bool cmd_context::validate_model_enabled() const { return m_params.m_model_validate; } cmd_context::check_sat_state cmd_context::cs_state() const { if (m_check_sat_result.get() == 0) return css_clear; switch (m_check_sat_result->status()) { case l_true: return css_sat; case l_false: return css_unsat; default: return css_unknown; } } void cmd_context::register_builtin_sorts(decl_plugin * p) { svector names; p->get_sort_names(names, m_logic); family_id fid = p->get_family_id(); svector::const_iterator it = names.begin(); svector::const_iterator end = names.end(); for (; it != end; ++it) { psort_decl * d = pm().mk_psort_builtin_decl((*it).m_name, fid, (*it).m_kind); insert(d); } } void cmd_context::register_builtin_ops(decl_plugin * p) { svector names; p->get_op_names(names, m_logic); family_id fid = p->get_family_id(); svector::const_iterator it = names.begin(); svector::const_iterator end = names.end(); for (; it != end; ++it) { if (m_builtin_decls.contains((*it).m_name)) { builtin_decl & d = m_builtin_decls.find((*it).m_name); builtin_decl * new_d = alloc(builtin_decl, fid, (*it).m_kind, d.m_next); d.m_next = new_d; m_extra_builtin_decls.push_back(new_d); } else { m_builtin_decls.insert((*it).m_name, builtin_decl(fid, (*it).m_kind)); } } } void cmd_context::register_plugin(symbol const & name, decl_plugin * p, bool install_names) { m_manager->register_plugin(name, p); if (install_names) { register_builtin_sorts(p); register_builtin_ops(p); } } void cmd_context::load_plugin(symbol const & name, bool install, svector& fids) { family_id id = m_manager->get_family_id(name); decl_plugin* p = m_manager->get_plugin(id); if (install && p && fids.contains(id)) { register_builtin_sorts(p); register_builtin_ops(p); } fids.erase(id); } bool cmd_context::logic_has_arith_core(symbol const & s) const { return s == "QF_LRA" || s == "QF_LIA" || s == "QF_RDL" || s == "QF_IDL" || s == "QF_AUFLIA" || s == "QF_ALIA" || s == "QF_AUFLIRA" || s == "QF_AUFNIA" || s == "QF_AUFNIRA" || s == "QF_UFLIA" || s == "QF_UFLRA" || s == "QF_UFIDL" || s == "QF_UFRDL" || s == "QF_NIA" || s == "QF_NRA" || s == "QF_NIRA" || s == "QF_UFNRA" || s == "QF_UFNIA" || s == "QF_UFNIRA" || s == "QF_BVRE" || s == "AUFLIA" || s == "AUFLIRA" || s == "AUFNIA" || s == "AUFNIRA" || s == "UFLIA" || s == "UFLRA" || s == "UFNRA" || s == "UFNIRA" || s == "UFNIA" || s == "LIA" || s == "LRA" || s == "QF_FP" || s == "QF_FPBV" || s == "QF_BVFP" || s == "HORN"; } bool cmd_context::logic_has_arith() const { return !has_logic() || logic_has_arith_core(m_logic); } bool cmd_context::logic_has_bv_core(symbol const & s) const { return s == "UFBV" || s == "AUFBV" || s == "ABV" || s == "BV" || s == "QF_BV" || s == "QF_UFBV" || s == "QF_ABV" || s == "QF_AUFBV" || s == "QF_BVRE" || s == "QF_FPBV" || s == "QF_BVFP" || s == "HORN"; } bool cmd_context::logic_has_horn(symbol const& s) const { return s == "HORN"; } bool cmd_context::logic_has_bv() const { return !has_logic() || logic_has_bv_core(m_logic); } bool cmd_context::logic_has_seq_core(symbol const& s) const { return s == "QF_BVRE"; } bool cmd_context::logic_has_seq() const { return !has_logic() || logic_has_seq_core(m_logic); } bool cmd_context::logic_has_fpa_core(symbol const& s) const { return s == "QF_FP" || s == "QF_FPBV" || s == "QF_BVFP"; } bool cmd_context::logic_has_fpa() const { return !has_logic() || logic_has_fpa_core(m_logic); } bool cmd_context::logic_has_array_core(symbol const & s) const { return s == "QF_AX" || s == "QF_AUFLIA" || s == "QF_ALIA" || s == "QF_AUFLIRA" || s == "QF_AUFNIA" || s == "QF_AUFNIRA" || s == "AUFLIA" || s == "AUFLIRA" || s == "AUFNIA" || s == "AUFNIRA" || s == "AUFBV" || s == "ABV" || s == "QF_ABV" || s == "QF_AUFBV" || s == "HORN"; } bool cmd_context::logic_has_array() const { return !has_logic() || logic_has_array_core(m_logic); } bool cmd_context::logic_has_datatype() const { return !has_logic(); } void cmd_context::init_manager_core(bool new_manager) { SASSERT(m_manager != 0); SASSERT(m_pmanager != 0); m_dt_eh = alloc(dt_eh, *this); m_pmanager->set_new_datatype_eh(m_dt_eh.get()); if (new_manager) { decl_plugin * basic = m_manager->get_plugin(m_manager->get_basic_family_id()); register_builtin_sorts(basic); register_builtin_ops(basic); // the manager was created by the command context. register_plugin(symbol("arith"), alloc(arith_decl_plugin), logic_has_arith()); register_plugin(symbol("bv"), alloc(bv_decl_plugin), logic_has_bv()); register_plugin(symbol("array"), alloc(array_decl_plugin), logic_has_array()); register_plugin(symbol("datatype"), alloc(datatype_decl_plugin), logic_has_datatype()); register_plugin(symbol("seq"), alloc(seq_decl_plugin), logic_has_seq()); register_plugin(symbol("pb"), alloc(pb_decl_plugin), !has_logic()); register_plugin(symbol("fpa"), alloc(fpa_decl_plugin), logic_has_fpa()); register_plugin(symbol("datalog_relation"), alloc(datalog::dl_decl_plugin), !has_logic()); } else { // the manager was created by an external module // we register all plugins available in the manager. // unless the logic specifies otherwise. svector fids; m_manager->get_range(fids); load_plugin(symbol("arith"), logic_has_arith(), fids); load_plugin(symbol("bv"), logic_has_bv(), fids); load_plugin(symbol("array"), logic_has_array(), fids); load_plugin(symbol("datatype"), logic_has_datatype(), fids); load_plugin(symbol("seq"), logic_has_seq(), fids); load_plugin(symbol("fpa"), logic_has_fpa(), fids); svector::iterator it = fids.begin(); svector::iterator end = fids.end(); for (; it != end; ++it) { decl_plugin * p = m_manager->get_plugin(*it); if (p) { register_builtin_sorts(p); register_builtin_ops(p); } } } if (!has_logic()) { // add list type only if the logic is not specified. // it prevents clashes with builtin types. insert(pm().mk_plist_decl()); } if (m_solver_factory) { mk_solver(); } m_check_logic.set_logic(m(), m_logic); } void cmd_context::init_manager() { if (m_manager_initialized) { // no-op } else if (m_manager) { m_manager_initialized = true; SASSERT(!m_own_manager); init_external_manager(); } else { m_manager_initialized = true; SASSERT(m_pmanager == 0); m_check_sat_result = 0; m_manager = m_params.mk_ast_manager(); m_pmanager = alloc(pdecl_manager, *m_manager); init_manager_core(true); } } void cmd_context::init_external_manager() { SASSERT(m_manager != 0); SASSERT(m_pmanager == 0); m_pmanager = alloc(pdecl_manager, *m_manager); init_manager_core(false); } bool cmd_context::supported_logic(symbol const & s) const { return s == "QF_UF" || s == "UF" || logic_has_arith_core(s) || logic_has_bv_core(s) || logic_has_array_core(s) || logic_has_seq_core(s) || logic_has_horn(s) || logic_has_fpa_core(s); } bool cmd_context::set_logic(symbol const & s) { if (has_logic()) throw cmd_exception("the logic has already been set"); if (has_manager() && m_main_ctx) throw cmd_exception("logic must be set before initialization"); if (!supported_logic(s)) { if (m_params.m_smtlib2_compliant) { return false; } else { warning_msg("unknown logic, ignoring set-logic command"); return true; } } m_logic = s; if (is_logic("QF_RDL") || is_logic("QF_LRA") || is_logic("UFLRA") || is_logic("LRA") || is_logic("RDL") || is_logic("QF_NRA") || is_logic("QF_UFNRA") || is_logic("QF_UFLRA")) m_numeral_as_real = true; return true; } std::string cmd_context::reason_unknown() const { if (m_check_sat_result.get() == 0) throw cmd_exception("state of the most recent check-sat command is not unknown"); return m_check_sat_result->reason_unknown(); } bool cmd_context::is_func_decl(symbol const & s) const { return m_builtin_decls.contains(s) || m_func_decls.contains(s); } void cmd_context::insert(symbol const & s, func_decl * f) { m_check_sat_result = 0; if (!m_check_logic(f)) { throw cmd_exception(m_check_logic.get_last_error()); } if (m_macros.contains(s)) { throw cmd_exception("invalid declaration, named expression already defined with this name ", s); } if (m_builtin_decls.contains(s)) { throw cmd_exception("invalid declaration, builtin symbol ", s); } dictionary::entry * e = m_func_decls.insert_if_not_there2(s, func_decls()); func_decls & fs = e->get_data().m_value; if (!fs.insert(m(), f)) { std::string msg = "invalid declaration, "; msg += f->get_arity() == 0 ? "constant" : "function"; msg += " '"; msg += s.str(); msg += "' (with the given signature) already declared"; throw cmd_exception(msg.c_str()); } if (s != f->get_name()) { TRACE("func_decl_alias", tout << "adding alias for: " << f->get_name() << ", alias: " << s << "\n";); m_func_decl2alias.insert(f, s); } if (!m_global_decls) { m_func_decls_stack.push_back(sf_pair(s, f)); } TRACE("cmd_context", tout << "new function decl\n" << mk_pp(f, m()) << "\n";); } void cmd_context::insert(symbol const & s, psort_decl * p) { m_check_sat_result = 0; if (m_psort_decls.contains(s)) { throw cmd_exception("sort already defined ", s); } pm().inc_ref(p); m_psort_decls.insert(s, p); if (!m_global_decls) { m_psort_decls_stack.push_back(s); } TRACE("cmd_context", tout << "new sort decl\n"; p->display(tout); tout << "\n";); } void cmd_context::insert(symbol const & s, unsigned arity, expr * t) { m_check_sat_result = 0; if (m_builtin_decls.contains(s)) { throw cmd_exception("invalid macro/named expression, builtin symbol ", s); } if (m_macros.contains(s)) { throw cmd_exception("named expression already defined"); } if (m_func_decls.contains(s)) { throw cmd_exception("invalid named expression, declaration already defined with this name ", s); } m().inc_ref(t); TRACE("insert_macro", tout << "new macro " << arity << "\n" << mk_pp(t, m()) << "\n";); m_macros.insert(s, macro(arity, t)); if (!m_global_decls) { m_macros_stack.push_back(s); } } void cmd_context::insert(cmd * c) { symbol const & s = c->get_name(); cmd * old_c; if (m_cmds.find(s, old_c) && c != old_c) { old_c->finalize(*this); dealloc(old_c); } m_cmds.insert(s, c); } void cmd_context::insert_user_tactic(symbol const & s, sexpr * d) { sm().inc_ref(d); sexpr * old_d; if (m_user_tactic_decls.find(s, old_d)) { sm().dec_ref(old_d); } m_user_tactic_decls.insert(s, d); } void cmd_context::insert(symbol const & s, object_ref * r) { r->inc_ref(*this); object_ref * old_r = 0; if (m_object_refs.find(s, old_r)) { old_r->dec_ref(*this); } m_object_refs.insert(s, r); } func_decl * cmd_context::find_func_decl(symbol const & s) const { builtin_decl d; if (m_builtin_decls.find(s, d)) { try { // Remark: ignoring m_next of d. We do not allow two different theories to define the same constant name. func_decl * f; f = m().mk_func_decl(d.m_fid, d.m_decl, 0, 0, 0, static_cast(0), 0); if (f != 0) return f; } catch (ast_exception &) { } throw cmd_exception("invalid function declaration reference, must provide signature for builtin symbol ", s); } if (m_macros.contains(s)) throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s); func_decls fs; if (m_func_decls.find(s, fs)) { if (fs.more_than_one()) throw cmd_exception("ambiguous function declaration reference, provide full signature to disumbiguate ( (*) ) ", s); return fs.first(); } throw cmd_exception("invalid function declaration reference, unknown function ", s); return 0; } /** \brief Select a builtin_decl from the list starting at first. We select the decl d s.t. d->m_fid == target_id If there is none that satisfies this condition, we return first. This is a HACK for supporting arithmetic and floating-point arithmetic. These are two different theories in Z3, but they share builtin symbol names: +, -, *, /, <, <=, >, >= */ static builtin_decl const & peek_builtin_decl(builtin_decl const & first, family_id target_id) { builtin_decl const * curr = &first; while (curr != 0) { if (curr->m_fid == target_id) return *curr; curr = curr->m_next; } return first; } func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices, unsigned arity, sort * const * domain, sort * range) const { builtin_decl d; if (m_builtin_decls.find(s, d)) { family_id fid = d.m_fid; decl_kind k = d.m_decl; // Hack: if d.m_next != 0, we use domain[0] (if available) to decide which plugin we use. if (d.m_decl != 0 && arity > 0) { builtin_decl const & d2 = peek_builtin_decl(d, domain[0]->get_family_id()); fid = d2.m_fid; k = d2.m_decl; } func_decl * f; if (num_indices == 0) { f = m().mk_func_decl(fid, k, 0, 0, arity, domain, range); } else { buffer ps; for (unsigned i = 0; i < num_indices; i++) ps.push_back(parameter(indices[i])); f = m().mk_func_decl(fid, k, num_indices, ps.c_ptr(), arity, domain, range); } if (f == 0) throw cmd_exception("invalid function declaration reference, invalid builtin reference ", s); return f; } if (m_macros.contains(s)) throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s); if (num_indices > 0) throw cmd_exception("invalid indexed function declaration reference, unknown builtin function ", s); func_decl * f = 0; func_decls fs; if (m_func_decls.find(s, fs)) { f = fs.find(arity, domain, range); } if (f == 0) throw cmd_exception("invalid function declaration reference, unknown function ", s); return f; } psort_decl * cmd_context::find_psort_decl(symbol const & s) const { psort_decl * p = 0; m_psort_decls.find(s, p); return p; } cmd_context::macro cmd_context::find_macro(symbol const & s) const { macro m; m_macros.find(s, m); return m; } cmd * cmd_context::find_cmd(symbol const & s) const { cmd * c = 0; m_cmds.find(s, c); return c; } sexpr * cmd_context::find_user_tactic(symbol const & s) const { sexpr * n = 0; m_user_tactic_decls.find(s, n); return n; } object_ref * cmd_context::find_object_ref(symbol const & s) const { object_ref * r = 0; m_object_refs.find(s, r); if (r == 0) throw cmd_exception("unknown global variable ", s); return r; } #define CHECK_SORT(T) if (well_sorted_check_enabled()) m().check_sorts_core(T) void cmd_context::mk_const(symbol const & s, expr_ref & result) const { mk_app(s, 0, 0, 0, 0, 0, result); } void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range, expr_ref & result) const { builtin_decl d; if (m_builtin_decls.find(s, d)) { family_id fid = d.m_fid; decl_kind k = d.m_decl; // Hack: if d.m_next != 0, we use the sort of args[0] (if available) to decide which plugin we use. if (d.m_decl != 0 && num_args > 0) { builtin_decl const & d2 = peek_builtin_decl(d, m().get_sort(args[0])->get_family_id()); fid = d2.m_fid; k = d2.m_decl; } if (num_indices == 0) { result = m().mk_app(fid, k, 0, 0, num_args, args, range); } else { result = m().mk_app(fid, k, num_indices, indices, num_args, args, range); } if (result.get() == 0) throw cmd_exception("invalid builtin application ", s); CHECK_SORT(result.get()); return; } if (num_indices > 0) throw cmd_exception("invalid use of indexed indentifier, unknown builtin function ", s); macro _m; if (m_macros.find(s, _m)) { if (num_args != _m.first) throw cmd_exception("invalid defined function application, incorrect number of arguments ", s); if (num_args == 0) { result = _m.second; return; } SASSERT(num_args > 0); TRACE("macro_bug", tout << "well_sorted_check_enabled(): " << well_sorted_check_enabled() << "\n"; tout << "s: " << s << "\n"; tout << "body:\n" << mk_ismt2_pp(_m.second, m()) << "\n"; tout << "args:\n"; for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n" << mk_pp(m().get_sort(args[i]), m()) << "\n";); var_subst subst(m()); subst(_m.second, num_args, args, result); if (well_sorted_check_enabled() && !is_well_sorted(m(), result)) throw cmd_exception("invalid macro application, sort mismatch ", s); return; } func_decls fs; if (!m_func_decls.find(s, fs)) { if (num_args == 0) { throw cmd_exception("unknown constant ", s); } else throw cmd_exception("unknown function/constant ", s); } if (num_args == 0 && range == 0) { if (fs.more_than_one()) throw cmd_exception("ambiguous constant reference, more than one constant with the same sort, use a qualified expression (as ) to disumbiguate ", s); func_decl * f = fs.first(); if (f == 0) throw cmd_exception("unknown constant ", s); if (f->get_arity() != 0) throw cmd_exception("invalid function application, missing arguments ", s); result = m().mk_const(f); return; } else { func_decl * f = fs.find(m(), num_args, args, range); if (f == 0) throw cmd_exception("unknown constant ", s); if (well_sorted_check_enabled()) m().check_sort(f, num_args, args); result = m().mk_app(f, num_args, args); return; } } void cmd_context::erase_func_decl(symbol const & s) { if (!global_decls()) { throw cmd_exception("function declarations can only be erased when global declarations (instead of scoped) are used"); } func_decls fs; m_func_decls.find(s, fs); while (!fs.empty()) { func_decl * f = fs.first(); if (s != f->get_name()) { SASSERT(m_func_decl2alias.contains(f)); m_func_decl2alias.erase(f); } fs.erase(m(), f); } fs.finalize(m()); m_func_decls.erase(s); } void cmd_context::erase_func_decl_core(symbol const & s, func_decl * f) { func_decls fs; m_func_decls.find(s, fs); if (fs.contains(f)) { if (s != f->get_name()) { SASSERT(m_func_decl2alias.contains(f)); m_func_decl2alias.erase(f); } fs.erase(m(), f); if (fs.empty()) m_func_decls.erase(s); } } void cmd_context::erase_func_decl(symbol const & s, func_decl * f) { if (!global_decls()) { throw cmd_exception("function declarations can only be erased when global (instead of scoped) declarations are used"); } erase_func_decl_core(s, f); } void cmd_context::erase_psort_decl_core(symbol const & s) { psort_decl * p; if (m_psort_decls.find(s, p)) { pm().dec_ref(p); m_psort_decls.erase(s); } } void cmd_context::erase_psort_decl(symbol const & s) { if (!global_decls()) { throw cmd_exception("sort declarations can only be erased when global (instead of scoped) declarations are used"); } erase_psort_decl_core(s); } void cmd_context::erase_macro_core(symbol const & s) { macro _m; if (m_macros.find(s, _m)) { m().dec_ref(_m.second); m_macros.erase(s); } } void cmd_context::erase_macro(symbol const & s) { if (!global_decls()) { throw cmd_exception("macros (aka named expressions) can only be erased when global (instead of scoped) declarations are used"); } erase_macro_core(s); } void cmd_context::erase_cmd(symbol const & s) { cmd * c; if (m_cmds.find(s, c)) { c->finalize(*this); m_cmds.erase(s); dealloc(c); } } void cmd_context::erase_user_tactic(symbol const & s) { sexpr * d; if (m_user_tactic_decls.find(s, d)) { m_user_tactic_decls.erase(s); sm().dec_ref(d); } } void cmd_context::erase_object_ref(symbol const & s) { object_ref * r = 0; if (m_object_refs.find(s, r)) { r->dec_ref(*this); m_object_refs.erase(s); } } void cmd_context::reset_func_decls() { dictionary::iterator it = m_func_decls.begin(); dictionary::iterator end = m_func_decls.end(); for (; it != end; ++it) { func_decls fs = (*it).m_value; fs.finalize(m()); } m_func_decls.reset(); m_func_decls_stack.reset(); m_func_decl2alias.reset(); } void cmd_context::reset_psort_decls() { dictionary::iterator it = m_psort_decls.begin(); dictionary::iterator end = m_psort_decls.end(); for (; it != end; ++it) { psort_decl * p = (*it).m_value; pm().dec_ref(p); } m_psort_decls.reset(); m_psort_decls_stack.reset(); } void cmd_context::reset_macros() { dictionary::iterator it = m_macros.begin(); dictionary::iterator end = m_macros.end(); for (; it != end; ++it) { expr * t = (*it).m_value.second; m().dec_ref(t); } m_macros.reset(); m_macros_stack.reset(); } void cmd_context::reset_cmds() { dictionary::iterator it = m_cmds.begin(); dictionary::iterator end = m_cmds.end(); for (; it != end; ++it) { cmd * c = (*it).m_value; c->reset(*this); } } void cmd_context::finalize_cmds() { dictionary::iterator it = m_cmds.begin(); dictionary::iterator end = m_cmds.end(); for (; it != end; ++it) { cmd * c = (*it).m_value; c->finalize(*this); dealloc(c); } m_cmds.reset(); } void cmd_context::reset_user_tactics() { dec_ref_values(sm(), m_user_tactic_decls); m_user_tactic_decls.reset(); } void cmd_context::reset_object_refs() { dictionary::iterator it = m_object_refs.begin(); dictionary::iterator end = m_object_refs.end(); for (; it != end; ++it) { object_ref * r = (*it).m_value; r->dec_ref(*this); } m_object_refs.reset(); } void cmd_context::insert_aux_pdecl(pdecl * p) { pm().inc_ref(p); m_aux_pdecls.push_back(p); } void cmd_context::reset(bool finalize) { m_logic = symbol::null; m_check_sat_result = 0; m_numeral_as_real = false; m_builtin_decls.reset(); m_extra_builtin_decls.reset(); m_check_logic.reset(); reset_object_refs(); reset_cmds(); reset_psort_decls(); restore_aux_pdecls(0); reset_macros(); reset_func_decls(); restore_assertions(0); if (m_solver) m_solver = 0; m_scopes.reset(); m_opt = 0; m_pp_env = 0; m_dt_eh = 0; if (m_manager) { dealloc(m_pmanager); m_pmanager = 0; if (m_own_manager) { dealloc(m_manager); m_manager = 0; m_manager_initialized = false; } else { // doesn't own manager... so it cannot be deleted // reinit cmd_context if this is not a finalization step if (!finalize) init_external_manager(); else m_manager_initialized = false; } } if (m_sexpr_manager) { dealloc(m_sexpr_manager); m_sexpr_manager = 0; } SASSERT(!m_own_manager || !has_manager()); } void cmd_context::assert_expr(expr * t) { if (!m_check_logic(t)) throw cmd_exception(m_check_logic.get_last_error()); m_check_sat_result = 0; m().inc_ref(t); m_assertions.push_back(t); if (produce_unsat_cores()) m_assertion_names.push_back(0); if (m_solver) m_solver->assert_expr(t); } void cmd_context::assert_expr(symbol const & name, expr * t) { if (!m_check_logic(t)) throw cmd_exception(m_check_logic.get_last_error()); if (!produce_unsat_cores() || name == symbol::null) { assert_expr(t); return; } m_check_sat_result = 0; m().inc_ref(t); m_assertions.push_back(t); expr * ans = m().mk_const(name, m().mk_bool_sort()); m().inc_ref(ans); m_assertion_names.push_back(ans); if (m_solver) m_solver->assert_expr(t, ans); } void cmd_context::push() { m_check_sat_result = 0; init_manager(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_func_decls_stack_lim = m_func_decls_stack.size(); s.m_psort_decls_stack_lim = m_psort_decls_stack.size(); s.m_macros_stack_lim = m_macros_stack.size(); s.m_aux_pdecls_lim = m_aux_pdecls.size(); s.m_assertions_lim = m_assertions.size(); if (m_solver) m_solver->push(); if (m_opt) m_opt->push(); } void cmd_context::push(unsigned n) { for (unsigned i = 0; i < n; i++) push(); } void cmd_context::restore_func_decls(unsigned old_sz) { SASSERT(old_sz <= m_func_decls_stack.size()); svector::iterator it = m_func_decls_stack.begin() + old_sz; svector::iterator end = m_func_decls_stack.end(); for (; it != end; ++it) { sf_pair const & p = *it; erase_func_decl_core(p.first, p.second); } m_func_decls_stack.shrink(old_sz); } void cmd_context::restore_psort_decls(unsigned old_sz) { SASSERT(old_sz <= m_psort_decls_stack.size()); svector::iterator it = m_psort_decls_stack.begin() + old_sz; svector::iterator end = m_psort_decls_stack.end(); for (; it != end; ++it) { symbol const & s = *it; psort_decl * d = 0; if (!m_psort_decls.find(s, d)) { UNREACHABLE(); } pm().dec_ref(d); m_psort_decls.erase(s); } m_psort_decls_stack.shrink(old_sz); } void cmd_context::restore_macros(unsigned old_sz) { SASSERT(old_sz <= m_macros_stack.size()); svector::iterator it = m_macros_stack.begin() + old_sz; svector::iterator end = m_macros_stack.end(); for (; it != end; ++it) { symbol const & s = *it; macro _m; if (!m_macros.find(s, _m)) { UNREACHABLE(); } m().dec_ref(_m.second); m_macros.erase(s); } m_macros_stack.shrink(old_sz); } void cmd_context::restore_aux_pdecls(unsigned old_sz) { SASSERT(old_sz <= m_aux_pdecls.size()); ptr_vector::iterator it = m_aux_pdecls.begin() + old_sz; ptr_vector::iterator end = m_aux_pdecls.end(); for (; it != end; ++it) { pm().dec_ref(*it); } m_aux_pdecls.shrink(old_sz); } static void restore(ast_manager & m, ptr_vector & c, unsigned old_sz) { ptr_vector::iterator it = c.begin() + old_sz; ptr_vector::iterator end = c.end(); for (; it != end; ++it) { m.dec_ref(*it); } c.shrink(old_sz); } void cmd_context::restore_assertions(unsigned old_sz) { if (!has_manager()) { // restore_assertions invokes m(), so if cmd_context does not have a manager, it will try to create one. SASSERT(old_sz == m_assertions.size()); SASSERT(m_assertions.empty()); return; } SASSERT(old_sz <= m_assertions.size()); SASSERT(!m_interactive_mode || m_assertions.size() == m_assertion_strings.size()); restore(m(), m_assertions, old_sz); if (produce_unsat_cores()) restore(m(), m_assertion_names, old_sz); if (m_interactive_mode) m_assertion_strings.shrink(old_sz); } void cmd_context::pop(unsigned n) { m_check_sat_result = 0; if (n == 0) return; unsigned lvl = m_scopes.size(); if (n > lvl) throw cmd_exception("invalid pop command, argument is greater than the current stack depth"); if (m_solver) { m_solver->pop(n); } if (m_opt) m_opt->pop(n); unsigned new_lvl = lvl - n; scope & s = m_scopes[new_lvl]; restore_func_decls(s.m_func_decls_stack_lim); restore_psort_decls(s.m_psort_decls_stack_lim); restore_macros(s.m_macros_stack_lim); restore_aux_pdecls(s.m_aux_pdecls_lim); restore_assertions(s.m_assertions_lim); m_scopes.shrink(new_lvl); } void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions) { if (m_ignore_check) return; IF_VERBOSE(100, verbose_stream() << "(started \"check-sat\")" << std::endl;); TRACE("before_check_sat", dump_assertions(tout);); init_manager(); unsigned timeout = m_params.m_timeout; scoped_watch sw(*this); lbool r; if (m_opt && !m_opt->empty()) { bool was_pareto = false; m_check_sat_result = get_opt(); cancel_eh eh(*get_opt()); scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); ptr_vector cnstr(m_assertions); cnstr.append(num_assumptions, assumptions); get_opt()->set_hard_constraints(cnstr); try { r = get_opt()->optimize(); while (r == l_true && get_opt()->is_pareto()) { was_pareto = true; get_opt()->display_assignment(regular_stream()); regular_stream() << "\n"; if (get_opt()->print_model()) { model_ref mdl; get_opt()->get_model(mdl); if (mdl) { regular_stream() << "(model " << std::endl; model_smt2_pp(regular_stream(), *this, *(mdl.get()), 2); regular_stream() << ")" << std::endl; } } r = get_opt()->optimize(); } } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { throw cmd_exception(ex.msg()); } if (was_pareto && r == l_false) { r = l_true; } get_opt()->set_status(r); if (r != l_false && !was_pareto) { get_opt()->display_assignment(regular_stream()); } } else if (m_solver) { m_check_sat_result = m_solver.get(); // solver itself stores the result. m_solver->set_progress_callback(this); cancel_eh eh(*m_solver); scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); try { r = m_solver->check_sat(num_assumptions, assumptions); } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { throw cmd_exception(ex.msg()); } m_solver->set_status(r); } else { // There is no solver installed in the command context. regular_stream() << "unknown" << std::endl; return; } display_sat_result(r); validate_check_sat_result(r); if (r == l_true) validate_model(); } void cmd_context::display_sat_result(lbool r) { switch (r) { case l_true: regular_stream() << "sat" << std::endl; break; case l_false: regular_stream() << "unsat" << std::endl; break; case l_undef: regular_stream() << "unknown" << std::endl; break; } } void cmd_context::validate_check_sat_result(lbool r) { switch (r) { case l_true: if (m_status == UNSAT) { #ifdef _EXTERNAL_RELEASE throw cmd_exception("check annotation that says unsat"); #else diagnostic_stream() << "BUG: incompleteness" << std::endl; exit(ERR_INCOMPLETENESS); #endif } break; case l_false: if (m_status == SAT) { #ifdef _EXTERNAL_RELEASE throw cmd_exception("check annotation that says sat"); #else diagnostic_stream() << "BUG: unsoundness" << std::endl; exit(ERR_UNSOUNDNESS); #endif } break; default: break; } } void cmd_context::set_diagnostic_stream(char const * name) { m_diagnostic.set(name); if (m_main_ctx) { set_warning_stream(&(*m_diagnostic)); set_verbose_stream(diagnostic_stream()); } } struct contains_array_op_proc { struct found {}; family_id m_array_fid; contains_array_op_proc(ast_manager & m):m_array_fid(m.mk_family_id("array")) {} void operator()(var * n) {} void operator()(app * n) { if (n->get_family_id() != m_array_fid) return; decl_kind k = n->get_decl_kind(); if (k == OP_AS_ARRAY || k == OP_STORE || k == OP_ARRAY_MAP || k == OP_CONST_ARRAY) throw found(); } void operator()(quantifier * n) {} }; /** \brief Check if the current model satisfies the quantifier free formulas. */ void cmd_context::validate_model() { if (!validate_model_enabled()) return; if (!is_model_available()) return; model_ref md; get_check_sat_result()->get_model(md); SASSERT(md.get() != 0); params_ref p; p.set_uint("max_degree", UINT_MAX); // evaluate algebraic numbers of any degree. p.set_uint("sort_store", true); p.set_bool("completion", true); model_evaluator evaluator(*(md.get()), p); contains_array_op_proc contains_array(m()); { cancel_eh eh(evaluator); expr_ref r(m()); scoped_ctrl_c ctrlc(eh); ptr_vector::const_iterator it = begin_assertions(); ptr_vector::const_iterator end = end_assertions(); for (; it != end; ++it) { expr * a = *it; if (is_ground(a)) { r = 0; evaluator(a, r); TRACE("model_validate", tout << "checking\n" << mk_ismt2_pp(a, m()) << "\nresult:\n" << mk_ismt2_pp(r, m()) << "\n";); if (m().is_true(r)) continue; // The evaluator for array expressions is not complete // If r contains as_array/store/map/const expressions, then we do not generate the error. // TODO: improve evaluator for model expressions. // Note that, if "a" evaluates to false, then the error will be generated. try { for_each_expr(contains_array, r); } catch (contains_array_op_proc::found) { continue; } throw cmd_exception("an invalid model was generated"); } } } } // FIXME: really interpolants_enabled ought to be a parameter to the solver factory, // but this is a global change, so for now, we use an alternate solver factory // for interpolation void cmd_context::mk_solver() { bool proofs_enabled, models_enabled, unsat_core_enabled; params_ref p; m_params.get_solver_params(m(), p, proofs_enabled, models_enabled, unsat_core_enabled); if(produce_interpolants()){ SASSERT(m_interpolating_solver_factory); m_solver = (*m_interpolating_solver_factory)(m(), p, true /* must have proofs */, models_enabled, unsat_core_enabled, m_logic); } else m_solver = (*m_solver_factory)(m(), p, proofs_enabled, models_enabled, unsat_core_enabled, m_logic); } void cmd_context::set_interpolating_solver_factory(solver_factory * f) { SASSERT(!has_manager()); m_interpolating_solver_factory = f; } void cmd_context::set_solver_factory(solver_factory * f) { m_solver_factory = f; m_check_sat_result = 0; if (has_manager() && f != 0) { mk_solver(); // assert formulas and create scopes in the new solver. unsigned lim = 0; svector::iterator it = m_scopes.begin(); svector::iterator end = m_scopes.end(); for (; it != end; ++it) { scope & s = *it; for (unsigned i = lim; i < s.m_assertions_lim; i++) { m_solver->assert_expr(m_assertions[i]); } lim = s.m_assertions_lim; m_solver->push(); } unsigned sz = m_assertions.size(); for (unsigned i = lim; i < sz; i++) { m_solver->assert_expr(m_assertions[i]); } } } void cmd_context::display_statistics(bool show_total_time, double total_time) { statistics st; unsigned long long mem = memory::get_max_used_memory(); if (show_total_time) st.update("total time", total_time); st.update("time", get_seconds()); st.update("memory", static_cast(mem)/static_cast(1024*1024)); if (m_check_sat_result) { m_check_sat_result->collect_statistics(st); } else if (m_solver) { m_solver->collect_statistics(st); } else if (m_opt) { m_opt->collect_statistics(st); } st.display_smt2(regular_stream()); } void cmd_context::display_assertions() { if (!m_interactive_mode) throw cmd_exception("command is only available in interactive mode, use command (set-option :interactive-mode true)"); vector::const_iterator it = m_assertion_strings.begin(); vector::const_iterator end = m_assertion_strings.end(); regular_stream() << "("; for (bool first = true; it != end; ++it) { std::string const & s = *it; if (first) first = false; else regular_stream() << "\n "; regular_stream() << s; } regular_stream() << ")" << std::endl; } bool cmd_context::is_model_available() const { if (produce_models() && has_manager() && (cs_state() == css_sat || cs_state() == css_unknown)) { model_ref md; get_check_sat_result()->get_model(md); return md.get() != 0; } return false; } format_ns::format * cmd_context::pp(sort * s) const { TRACE("cmd_context", tout << "pp(sort * s), s: " << mk_pp(s, m()) << "\n";); return pm().pp(s); } cmd_context::pp_env & cmd_context::get_pp_env() const { if (m_pp_env.get() == 0) { const_cast(this)->m_pp_env = alloc(pp_env, *const_cast(this)); } return *(m_pp_env.get()); } void cmd_context::pp(expr * n, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names) const { mk_smt2_format(n, get_pp_env(), params_ref(), num_vars, var_prefix, r, var_names); } void cmd_context::pp(expr * n, format_ns::format_ref & r) const { sbuffer buf; pp(n, 0, 0, r, buf); } void cmd_context::pp(func_decl * f, format_ns::format_ref & r) const { mk_smt2_format(f, get_pp_env(), params_ref(), r); } void cmd_context::display(std::ostream & out, sort * s, unsigned indent) const { format_ns::format_ref f(format_ns::fm(m())); f = pp(s); if (indent > 0) f = format_ns::mk_indent(m(), indent, f); ::pp(out, f.get(), m()); } void cmd_context::display(std::ostream & out, expr * n, unsigned indent, unsigned num_vars, char const * var_prefix, sbuffer & var_names) const { format_ns::format_ref f(format_ns::fm(m())); pp(n, num_vars, var_prefix, f, var_names); if (indent > 0) f = format_ns::mk_indent(m(), indent, f); ::pp(out, f.get(), m()); } void cmd_context::display(std::ostream & out, expr * n, unsigned indent) const { sbuffer buf; display(out, n, indent, 0, 0, buf); } void cmd_context::display(std::ostream & out, func_decl * d, unsigned indent) const { format_ns::format_ref f(format_ns::fm(m())); pp(d, f); if (indent > 0) f = format_ns::mk_indent(m(), indent, f); ::pp(out, f.get(), m()); } void cmd_context::dump_assertions(std::ostream & out) const { ptr_vector::const_iterator it = m_assertions.begin(); ptr_vector::const_iterator end = m_assertions.end(); for (; it != end; ++it) { display(out, *it); out << std::endl; } } void cmd_context::display_smt2_benchmark(std::ostream & out, unsigned num, expr * const * assertions, symbol const & logic) const { if (logic != symbol::null) out << "(set-logic " << logic << ")" << std::endl; // collect uninterpreted function declarations decl_collector decls(m(), false); for (unsigned i = 0; i < num; i++) { decls.visit(assertions[i]); } // TODO: display uninterpreted sort decls, and datatype decls. unsigned num_decls = decls.get_num_decls(); func_decl * const * fs = decls.get_func_decls(); for (unsigned i = 0; i < num_decls; i++) { display(out, fs[i]); out << std::endl; } for (unsigned i = 0; i < num; i++) { out << "(assert "; display(out, assertions[i], 8); out << ")" << std::endl; } out << "(check-sat)" << std::endl; } void cmd_context::slow_progress_sample() { SASSERT(m_solver); statistics st; regular_stream() << "(progress\n"; m_solver->collect_statistics(st); st.display_smt2(regular_stream()); svector labels; m_solver->get_labels(labels); regular_stream() << "(labels"; for (unsigned i = 0; i < labels.size(); i++) { regular_stream() << " " << labels[i]; } regular_stream() << "))" << std::endl; } void cmd_context::fast_progress_sample() { } cmd_context::dt_eh::dt_eh(cmd_context & owner): m_owner(owner), m_dt_util(owner.m()) { } cmd_context::dt_eh::~dt_eh() { } void cmd_context::dt_eh::operator()(sort * dt) { TRACE("new_dt_eh", tout << "new datatype: "; m_owner.pm().display(tout, dt); tout << "\n";); ptr_vector const * constructors = m_dt_util.get_datatype_constructors(dt); unsigned num_constructors = constructors->size(); for (unsigned j = 0; j < num_constructors; j++) { func_decl * c = constructors->get(j); m_owner.insert(c); TRACE("new_dt_eh", tout << "new constructor: " << c->get_name() << "\n";); func_decl * r = m_dt_util.get_constructor_recognizer(c); m_owner.insert(r); TRACE("new_dt_eh", tout << "new recognizer: " << r->get_name() << "\n";); ptr_vector const * accessors = m_dt_util.get_constructor_accessors(c); unsigned num_accessors = accessors->size(); for (unsigned k = 0; k < num_accessors; k++) { func_decl * a = accessors->get(k); m_owner.insert(a); TRACE("new_dt_eh", tout << "new accessor: " << a->get_name() << "\n";); } } } std::ostream & operator<<(std::ostream & out, cmd_context::status st) { switch (st) { case cmd_context::UNSAT: out << "unsat"; break; case cmd_context::SAT: out << "sat"; break; default: out << "unknown"; break; } return out; } z3-4.4.0/src/nlsat/0000755000175000017500000000000012540347414013555 5ustar michaelmichaelz3-4.4.0/src/nlsat/nlsat_interval_set.h0000644000175000017500000000647612540347414017643 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_interval_set.h Abstract: Sets of disjoint infeasible intervals. Author: Leonardo de Moura (leonardo) 2012-01-11. Revision History: --*/ #ifndef _NLSAT_INTERVAL_SET_H_ #define _NLSAT_INTERVAL_SET_H_ #include"nlsat_types.h" namespace nlsat { class interval_set; class interval_set_manager { anum_manager & m_am; small_object_allocator & m_allocator; svector m_already_visited; random_gen m_rand; void del(interval_set * s); public: interval_set_manager(anum_manager & m, small_object_allocator & a); ~interval_set_manager(); void set_seed(unsigned s) { m_rand.set_seed(s); } /** \brief Return the empty set. */ interval_set * mk_empty() { return 0; } /** \brief Return a set of composed of a single interval. */ interval_set * mk(bool lower_open, bool lower_inf, anum const & lower, bool upper_open, bool upper_inf, anum const & upper, literal justification); /** \brief Return the union of two sets. */ interval_set * mk_union(interval_set const * s1, interval_set const * s2); /** \brief Reference counting */ void dec_ref(interval_set * s); void inc_ref(interval_set * s); /** \brief Return true if s is the empty set. */ bool is_empty(interval_set const * s) { return s == 0; } /** \brief Return true if the set contains all real numbers. */ bool is_full(interval_set const * s); /** `\brief Return true if s1 is a subset of s2. */ bool subset(interval_set const * s1, interval_set const * s2); /** \brief Return true if s1 and s2 cover the same subset of R. The justifications are ignored */ bool set_eq(interval_set const * s1, interval_set const * s2); /** \brief Return true if s1 and s2 are the same (the justifications are taking into account). */ bool eq(interval_set const * s1, interval_set const * s2); /** \brief Return a set of literals that justify s. */ void get_justifications(interval_set const * s, literal_vector & js); void display(std::ostream & out, interval_set const * s) const; unsigned num_intervals(interval_set const * s) const; /** \brief (For debugging purposes) Return one of the intervals in s. \pre idx < num_intervals() */ interval_set * get_interval(interval_set const * s, unsigned idx) const; /** \brief Select a witness w in the complement of s. \pre !is_full(s) */ void peek_in_complement(interval_set const * s, anum & w, bool randomize); }; typedef obj_ref interval_set_ref; inline std::ostream & operator<<(std::ostream & out, interval_set_ref const & s) { s.m().display(out, s); return out; } }; #endif z3-4.4.0/src/nlsat/nlsat_assignment.h0000644000175000017500000000473212540347414017305 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_assignment.h Abstract: Assignment: Var -> Algebraic Number Author: Leonardo de Moura (leonardo) 2012-01-08. Revision History: --*/ #ifndef _NLSAT_ASSIGNMENT_H_ #define _NLSAT_ASSIGNMENT_H_ #include"nlsat_types.h" #include"algebraic_numbers.h" namespace nlsat { /** \brief A mapping from variables to values. This mapping is used to encode the current partial interpretation in nlsat. */ class assignment : public polynomial::var2anum { scoped_anum_vector m_values; svector m_assigned; public: assignment(anum_manager & _m):m_values(_m) {} virtual ~assignment() {} anum_manager & am() const { return m_values.m(); } void swap(assignment & other) { m_values.swap(other.m_values); m_assigned.swap(other.m_assigned); } void set_core(var x, anum & v) { m_values.reserve(x+1, anum()); m_assigned.reserve(x+1, false); m_assigned[x] = true; am().swap(m_values[x], v); } void set(var x, anum const & v) { m_values.reserve(x+1, anum()); m_assigned.reserve(x+1, false); m_assigned[x] = true; am().set(m_values[x], v); } void reset(var x) { if (x < m_assigned.size()) m_assigned[x] = false; } bool is_assigned(var x) const { return m_assigned.get(x, false); } anum const & value(var x) const { return m_values[x]; } virtual anum_manager & m() const { return am(); } virtual bool contains(var x) const { return is_assigned(x); } virtual anum const & operator()(var x) const { SASSERT(is_assigned(x)); return value(x); } }; /** \brief Wrapper for temporarily unassigning a given variable y. That is, given an assignment M, M' = undef_var_assignment(M, y) is identical to M, but y is unassigned in M' */ class undef_var_assignment : public polynomial::var2anum { assignment const & m_assignment; var m_y; public: undef_var_assignment(assignment const & a, var y):m_assignment(a), m_y(y) {} virtual anum_manager & m() const { return m_assignment.am(); } virtual bool contains(var x) const { return x != m_y && m_assignment.is_assigned(x); } virtual anum const & operator()(var x) const { return m_assignment.value(x); } }; }; #endif z3-4.4.0/src/nlsat/nlsat_evaluator.h0000644000175000017500000000256312540347414017137 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_evaluator.h Abstract: Helper class for computing the infeasible intervals of an arithmetic literal. Author: Leonardo de Moura (leonardo) 2012-01-12. Revision History: --*/ #ifndef _NLSAT_EVALUATOR_H_ #define _NLSAT_EVALUATOR_H_ #include"nlsat_types.h" #include"nlsat_assignment.h" #include"nlsat_interval_set.h" namespace nlsat { class evaluator { struct imp; imp * m_imp; public: evaluator(assignment const & x2v, pmanager & pm, small_object_allocator & allocator); ~evaluator(); interval_set_manager & ism() const; /** \brief Evaluate the given literal in the current model. All variables in the atom must be assigned. The result is true if the literal is satisfied, and false otherwise. */ bool eval(atom * a, bool neg); /** \brief Return the infeasible interval set for the given literal. All but the a->max_var() must be assigned in the current model. Let x be a->max_var(). Then, the resultant set specifies which values of x falsify the given literal. */ interval_set_ref infeasible_intervals(atom * a, bool neg); void push(); void pop(unsigned num_scopes); }; }; #endif z3-4.4.0/src/nlsat/tactic/0000755000175000017500000000000012540347414015024 5ustar michaelmichaelz3-4.4.0/src/nlsat/tactic/qfnra_nlsat_tactic.h0000644000175000017500000000116212540347414021034 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfnra_nlsat_tactic.h Abstract: Tactic based on nlsat for solving QF_NRA problems Author: Leonardo (leonardo) 2012-01-23 Notes: --*/ #ifndef _QFNRA_NLSAT_TACTIC_H_ #define _QFNRA_NLSAT_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_qfnra_nlsat_tactic(ast_manager & m, params_ref const & p = params_ref()); MK_SIMPLE_TACTIC_FACTORY(qfnra_nlsat_fct, mk_qfnra_nlsat_tactic(m, p)); /* ADD_TACTIC("qfnra-nlsat", "builtin strategy for solving QF_NRA problems using only nlsat.", "mk_qfnra_nlsat_tactic(m, p)") */ #endif z3-4.4.0/src/nlsat/tactic/nlsat_tactic.cpp0000644000175000017500000001735412540347414020212 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_tactic.cpp Abstract: Tactic for using nonlinear procedure. Author: Leonardo (leonardo) 2012-01-02 Notes: --*/ #include"tactical.h" #include"goal2nlsat.h" #include"nlsat_solver.h" #include"model.h" #include"expr2var.h" #include"arith_decl_plugin.h" #include"ast_smt2_pp.h" #include"z3_exception.h" #include"algebraic_numbers.h" class nlsat_tactic : public tactic { struct expr_display_var_proc : public nlsat::display_var_proc { ast_manager & m; expr_ref_vector m_var2expr; expr_display_var_proc(ast_manager & _m):m(_m), m_var2expr(_m) {} virtual void operator()(std::ostream & out, nlsat::var x) const { if (x < m_var2expr.size()) out << mk_ismt2_pp(m_var2expr.get(x), m); else out << "x!" << x; } }; struct imp { ast_manager & m; params_ref m_params; expr_display_var_proc m_display_var; nlsat::solver m_solver; goal2nlsat m_g2nl; imp(ast_manager & _m, params_ref const & p): m(_m), m_params(p), m_display_var(_m), m_solver(p) { } void updt_params(params_ref const & p) { m_params = p; m_solver.updt_params(p); } void set_cancel(bool f) { m_solver.set_cancel(f); m_g2nl.set_cancel(f); } bool contains_unsupported(expr_ref_vector & b2a, expr_ref_vector & x2t) { for (unsigned x = 0; x < x2t.size(); x++) { if (!is_uninterp_const(x2t.get(x))) { TRACE("unsupported", tout << "unsupported atom:\n" << mk_ismt2_pp(x2t.get(x), m) << "\n";); return true; } } for (unsigned b = 0; b < b2a.size(); b++) { expr * a = b2a.get(b); if (a == 0) continue; if (is_uninterp_const(a)) continue; if (m_solver.is_interpreted(b)) continue; // arithmetic atom TRACE("unsupported", tout << "unsupported atom:\n" << mk_ismt2_pp(a, m) << "\n";); return true; // unsupported } return false; } // Return false if nlsat assigned noninteger value to an integer variable. bool mk_model(expr_ref_vector & b2a, expr_ref_vector & x2t, model_converter_ref & mc) { bool ok = true; model_ref md = alloc(model, m); arith_util util(m); for (unsigned x = 0; x < x2t.size(); x++) { expr * t = x2t.get(x); if (!is_uninterp_const(t)) continue; expr * v; try { v = util.mk_numeral(m_solver.value(x), util.is_int(t)); } catch (z3_error & ex) { throw ex; } catch (z3_exception &) { v = util.mk_to_int(util.mk_numeral(m_solver.value(x), false)); ok = false; } md->register_decl(to_app(t)->get_decl(), v); } for (unsigned b = 0; b < b2a.size(); b++) { expr * a = b2a.get(b); if (a == 0 || !is_uninterp_const(a)) continue; lbool val = m_solver.bvalue(b); if (val == l_undef) continue; // don't care md->register_decl(to_app(a)->get_decl(), val == l_true ? m.mk_true() : m.mk_false()); } mc = model2model_converter(md.get()); return ok; } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("nlsat", *g); if (g->is_decided()) { result.push_back(g.get()); return; } fail_if_proof_generation("nlsat", g); expr2var a2b(m); expr2var t2x(m); m_g2nl(*g, m_params, m_solver, a2b, t2x); m_display_var.m_var2expr.reset(); t2x.mk_inv(m_display_var.m_var2expr); m_solver.set_display_var(m_display_var); lbool st = m_solver.check(); if (st == l_undef) { } else if (st == l_true) { expr_ref_vector x2t(m); expr_ref_vector b2a(m); a2b.mk_inv(b2a); t2x.mk_inv(x2t); if (!contains_unsupported(b2a, x2t)) { // If mk_model is false it means that the model produced by nlsat // assigns noninteger values to integer variables if (mk_model(b2a, x2t, mc)) { // result goal is trivially SAT g->reset(); } } } else { // TODO: extract unsat core g->assert_expr(m.mk_false(), 0, 0); } g->inc_depth(); result.push_back(g.get()); TRACE("nlsat", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; statistics m_stats; struct scoped_set_imp { nlsat_tactic & m_owner; scoped_set_imp(nlsat_tactic & o, imp & i):m_owner(o) { #pragma omp critical (tactic_cancel) { m_owner.m_imp = &i; } } ~scoped_set_imp() { m_owner.m_imp->m_solver.collect_statistics(m_owner.m_stats); #pragma omp critical (tactic_cancel) { m_owner.m_imp = 0; } } }; public: nlsat_tactic(params_ref const & p): m_params(p) { m_imp = 0; } virtual tactic * translate(ast_manager & m) { return alloc(nlsat_tactic, m_params); } virtual ~nlsat_tactic() { SASSERT(m_imp == 0); } virtual void updt_params(params_ref const & p) { m_params = p; } virtual void collect_param_descrs(param_descrs & r) { goal2nlsat::collect_param_descrs(r); nlsat::solver::collect_param_descrs(r); algebraic_numbers::manager::collect_param_descrs(r); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { try { imp local_imp(in->m(), m_params); scoped_set_imp setter(*this, local_imp); local_imp(in, result, mc, pc, core); } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { // convert all Z3 exceptions into tactic exceptions. throw tactic_exception(ex.msg()); } } virtual void cleanup() {} virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } virtual void collect_statistics(statistics & st) const { st.copy(m_stats); } virtual void reset_statistics() { m_stats.reset(); } }; tactic * mk_nlsat_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(nlsat_tactic, p)); } z3-4.4.0/src/nlsat/tactic/nlsat_tactic.h0000644000175000017500000000076312540347414017653 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_tactic.h Abstract: Tactic for using nonlinear procedure. Author: Leonardo (leonardo) 2012-01-02 Notes: --*/ #ifndef _NLSAT_TACTIC_H_ #define _NLSAT_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_nlsat_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC('nlsat', '(try to) solve goal using a nonlinear arithmetic solver.', 'mk_nlsat_tactic(m, p)') */ #endif z3-4.4.0/src/nlsat/tactic/goal2nlsat.cpp0000644000175000017500000002240212540347414017576 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: goal2nlsat.cpp Abstract: "Compile" a goal into the nonlinear arithmetic engine. Non-arithmetic atoms are "abstracted" into boolean variables. Non-supported terms are "abstracted" into variables. The mappings can be used to convert back the state of the engine into a goal. Author: Leonardo (leonardo) 2012-01-02 Notes: --*/ #include"goal2nlsat.h" #include"goal.h" #include"goal_util.h" #include"nlsat_solver.h" #include"expr2polynomial.h" #include"expr2var.h" #include"arith_decl_plugin.h" #include"tactic.h" #include"ast_smt2_pp.h" struct goal2nlsat::imp { struct nlsat_expr2polynomial : public expr2polynomial { nlsat::solver & m_solver; nlsat_expr2polynomial(nlsat::solver & s, ast_manager & m, polynomial::manager & pm, expr2var * e2v): expr2polynomial(m, pm, e2v), m_solver(s) { } virtual bool is_int(polynomial::var x) const { return m_solver.is_int(x); } virtual polynomial::var mk_var(bool is_int) { return m_solver.mk_var(is_int); } }; ast_manager & m; nlsat::solver & m_solver; polynomial::manager & m_pm; unsynch_mpq_manager & m_qm; arith_util m_util; expr2var & m_a2b; expr2var & m_t2x; nlsat_expr2polynomial m_expr2poly; polynomial::factor_params m_fparams; unsigned long long m_max_memory; bool m_factor; volatile bool m_cancel; imp(ast_manager & _m, params_ref const & p, nlsat::solver & s, expr2var & a2b, expr2var & t2x): m(_m), m_solver(s), m_pm(s.pm()), m_qm(s.qm()), m_util(m), m_a2b(a2b), m_t2x(t2x), m_expr2poly(m_solver, m, m_solver.pm(), &m_t2x) { updt_params(p); m_cancel = false; } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_factor = p.get_bool("factor", true); m_fparams.updt_params(p); } void set_cancel(bool f) { m_cancel = f; m_pm.set_cancel(f); } nlsat::atom::kind flip(nlsat::atom::kind k) { switch (k) { case nlsat::atom::EQ: return k; case nlsat::atom::LT: return nlsat::atom::GT; case nlsat::atom::GT: return nlsat::atom::LT; default: UNREACHABLE(); return k; } } nlsat::bool_var factor_atom(polynomial::polynomial * p, nlsat::atom::kind k) { sbuffer is_even; ptr_buffer ps; polynomial::factors fs(m_pm); m_pm.factor(p, fs, m_fparams); TRACE("goal2nlsat_bug", tout << "factors:\n" << fs << "\n";); SASSERT(fs.distinct_factors() > 0); for (unsigned i = 0; i < fs.distinct_factors(); i++) { ps.push_back(fs[i]); is_even.push_back(fs.get_degree(i) % 2 == 0); } if (m_qm.is_neg(fs.get_constant())) k = flip(k); return m_solver.mk_ineq_atom(k, ps.size(), ps.c_ptr(), is_even.c_ptr()); } nlsat::literal process_atom(app * f, nlsat::atom::kind k) { SASSERT(f->get_num_args() == 2); expr * lhs = f->get_arg(0); expr * rhs = f->get_arg(1); polynomial_ref p1(m_pm); polynomial_ref p2(m_pm); scoped_mpz d1(m_qm); scoped_mpz d2(m_qm); m_expr2poly.to_polynomial(lhs, p1, d1); m_expr2poly.to_polynomial(rhs, p2, d2); scoped_mpz lcm(m_qm); m_qm.lcm(d1, d2, lcm); m_qm.div(lcm, d1, d1); m_qm.div(lcm, d2, d2); m_qm.neg(d2); polynomial_ref p(m_pm); p = m_pm.addmul(d1, m_pm.mk_unit(), p1, d2, m_pm.mk_unit(), p2); TRACE("goal2nlsat_bug", tout << "p: " << p << "\nk: " << k << "\n";); if (is_const(p)) { int sign; if (is_zero(p)) sign = 0; else sign = m_qm.is_pos(m_pm.coeff(p, 0)) ? 1 : -1; switch (k) { case nlsat::atom::EQ: return sign == 0 ? nlsat::true_literal : nlsat::false_literal; case nlsat::atom::LT: return sign < 0 ? nlsat::true_literal : nlsat::false_literal; case nlsat::atom::GT: return sign > 0 ? nlsat::true_literal : nlsat::false_literal; default: UNREACHABLE(); return nlsat::true_literal; } } if (m_factor) { return nlsat::literal(factor_atom(p, k), false); } else { bool is_even = false; polynomial::polynomial * _p = p.get(); return nlsat::literal(m_solver.mk_ineq_atom(k, 1, &_p, &is_even), false); } } nlsat::literal process_eq(app * f) { return process_atom(f, nlsat::atom::EQ); } nlsat::literal process_le(app * f) { return ~process_atom(f, nlsat::atom::GT); } nlsat::literal process_ge(app * f) { return ~process_atom(f, nlsat::atom::LT); } // everything else is compiled as a boolean variable nlsat::bool_var process_bvar(expr * f) { if (m_a2b.is_var(f)) { return static_cast(m_a2b.to_var(f)); } else { nlsat::bool_var b = m_solver.mk_bool_var(); m_a2b.insert(f, b); return b; } } nlsat::literal process_atom(expr * f) { if (m.is_eq(f)) { if (m_util.is_int_real(to_app(f)->get_arg(0))) return process_eq(to_app(f)); else return nlsat::literal(process_bvar(f), false); } else if (m_util.is_le(f)) { return process_le(to_app(f)); } else if (m_util.is_ge(f)) { return process_ge(to_app(f)); } else if (is_app(f)) { if (to_app(f)->get_family_id() == m.get_basic_family_id()) { switch (to_app(f)->get_decl_kind()) { case OP_TRUE: case OP_FALSE: TRACE("goal2nlsat", tout << "f: " << mk_ismt2_pp(f, m) << "\n";); throw tactic_exception("apply simplify before applying nlsat"); case OP_AND: case OP_OR: case OP_IFF: case OP_XOR: case OP_NOT: case OP_IMPLIES: throw tactic_exception("convert goal into cnf before applying nlsat"); case OP_DISTINCT: throw tactic_exception("eliminate distinct operator (use tactic '(using-params simplify :blast-distinct true)') before applying nlsat"); default: UNREACHABLE(); return nlsat::literal(nlsat::null_bool_var, false); } } else if (to_app(f)->get_family_id() == m_util.get_family_id()) { throw tactic_exception("apply purify-arith before applying nlsat"); } else { return nlsat::literal(process_bvar(f), false); } } else { SASSERT(is_quantifier(f)); return nlsat::literal(process_bvar(f), false); } } nlsat::literal process_literal(expr * f) { bool neg = false; while (m.is_not(f, f)) neg = !neg; nlsat::literal l = process_atom(f); if (neg) l.neg(); return l; } void process(expr * f, expr_dependency * dep) { unsigned num_lits; expr * const * lits; if (m.is_or(f)) { num_lits = to_app(f)->get_num_args(); lits = to_app(f)->get_args(); } else { num_lits = 1; lits = &f; } sbuffer ls; for (unsigned i = 0; i < num_lits; i++) { ls.push_back(process_literal(lits[i])); } m_solver.mk_clause(ls.size(), ls.c_ptr(), dep); } void operator()(goal const & g) { if (has_term_ite(g)) throw tactic_exception("eliminate term-ite before applying nlsat"); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { process(g.form(i), g.dep(i)); } } }; struct goal2nlsat::scoped_set_imp { goal2nlsat & m_owner; scoped_set_imp(goal2nlsat & o, imp & i):m_owner(o) { #pragma omp critical (tactic_cancel) { m_owner.m_imp = &i; } } ~scoped_set_imp() { #pragma omp critical (tactic_cancel) { m_owner.m_imp = 0; } } }; goal2nlsat::goal2nlsat() { m_imp = 0; } goal2nlsat::~goal2nlsat() { SASSERT(m_imp == 0); } void goal2nlsat::collect_param_descrs(param_descrs & r) { insert_max_memory(r); r.insert("factor", CPK_BOOL, "(default: true) factor polynomials."); polynomial::factor_params::get_param_descrs(r); } void goal2nlsat::operator()(goal const & g, params_ref const & p, nlsat::solver & s, expr2var & a2b, expr2var & t2x) { imp local_imp(g.m(), p, s, a2b, t2x); scoped_set_imp setter(*this, local_imp); local_imp(g); } void goal2nlsat::set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } z3-4.4.0/src/nlsat/tactic/qfnra_nlsat_tactic.cpp0000644000175000017500000000416412540347414021374 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfnra_nlsat_tactic.h Abstract: Tactic based on nlsat for solving QF_NRA problems Author: Leonardo (leonardo) 2012-01-23 Notes: --*/ #include"tactical.h" #include"tseitin_cnf_tactic.h" #include"degree_shift_tactic.h" #include"purify_arith_tactic.h" #include"nlsat_tactic.h" #include"factor_tactic.h" #include"simplify_tactic.h" #include"elim_uncnstr_tactic.h" #include"propagate_values_tactic.h" #include"solve_eqs_tactic.h" #include"elim_term_ite_tactic.h" tactic * mk_qfnra_nlsat_tactic(ast_manager & m, params_ref const & p) { params_ref main_p = p; main_p.set_bool("elim_and", true); main_p.set_bool("blast_distinct", true); params_ref purify_p = p; purify_p.set_bool("complete", false); // temporary hack, solver does not support uninterpreted functions for encoding (div0 x) applications. So, we replace it application of this kind with an uninterpreted function symbol. tactic * factor; if (p.get_bool("factor", true)) factor = mk_factor_tactic(m, p); else factor = mk_skip_tactic(); return and_then(and_then(using_params(mk_simplify_tactic(m, p), main_p), using_params(mk_purify_arith_tactic(m, p), purify_p), mk_propagate_values_tactic(m, p), mk_solve_eqs_tactic(m, p), mk_elim_uncnstr_tactic(m, p), mk_elim_term_ite_tactic(m, p)), and_then(/* mk_degree_shift_tactic(m, p), */ // may affect full dimensionality detection factor, mk_solve_eqs_tactic(m, p), using_params(mk_simplify_tactic(m, p), main_p), mk_tseitin_cnf_core_tactic(m, p), using_params(mk_simplify_tactic(m, p), main_p), mk_nlsat_tactic(m, p))); } z3-4.4.0/src/nlsat/tactic/goal2nlsat.h0000644000175000017500000000323512540347414017246 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: goal2nlsat.h Abstract: "Compile" a goal into the nonlinear arithmetic engine. Non-arithmetic atoms are "abstracted" into boolean variables. Non-supported terms are "abstracted" into variables. The mappings can be used to convert back the state of the engine into a goal. Author: Leonardo (leonardo) 2012-01-02 Notes: --*/ #ifndef _GOAL2NLSAT_H_ #define _GOAL2NLSAT_H_ #include"nlsat_types.h" #include"model_converter.h" class goal; class expr2var; class goal2nlsat { struct imp; imp * m_imp; struct scoped_set_imp; public: goal2nlsat(); ~goal2nlsat(); static void collect_param_descrs(param_descrs & r); /** \brief "Compile" the goal into the given nlsat engine. Store a mapping from atoms to boolean variables into a2b. Store a mapping from terms into arithmetic variables into t2x. \remark a2b and t2x m don't need to be empty. The definitions there are reused. The input is expected to be in CNF */ void operator()(goal const & g, params_ref const & p, nlsat::solver & s, expr2var & a2b, expr2var & t2x); void set_cancel(bool f); }; class nlsat2goal { struct imp; imp * m_imp; public: nlsat2goal(); ~nlsat2goal(); static void collect_param_descrs(param_descrs & r); /** \brief Translate the state of the nlsat engine back into a goal. */ void operator()(nlsat::solver const & s, expr2var const & a2b, expr2var const & t2x, params_ref const & p, goal & g, model_converter_ref & mc); void set_cancel(bool f); }; #endif z3-4.4.0/src/nlsat/nlsat_types.cpp0000644000175000017500000000351412540347414016631 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_types.cpp Abstract: Basic types used in the nonlinear arithmetic satisfiability procedure. Author: Leonardo de Moura (leonardo) 2012-01-02. Revision History: --*/ #include"nlsat_types.h" #include"debug.h" #include"hash.h" #include"polynomial.h" namespace nlsat { ineq_atom::ineq_atom(kind k, unsigned sz, poly * const * ps, bool const * is_even, var max_var): atom(k, max_var), m_size(sz) { for (unsigned i = 0; i < m_size; i++) { m_ps[i] = TAG(poly *, ps[i], is_even[i] ? 1 : 0); } SASSERT(is_ineq_atom()); } unsigned ineq_atom::hash_proc::operator()(ineq_atom const * a) const { return get_composite_hash(a, a->m_size); } bool ineq_atom::eq_proc::operator()(ineq_atom const * a1, ineq_atom const * a2) const { if (a1->m_size != a2->m_size || a1->m_kind != a2->m_kind) return false; unsigned sz = a1->m_size; for (unsigned i = 0; i < sz; i++) { if (a1->m_ps[i] != a2->m_ps[i]) return false; } return true; } root_atom::root_atom(kind k, var x, unsigned i, poly * p): atom(k, x), m_x(x), m_i(i), m_p(p) { SASSERT(is_root_atom()); } unsigned root_atom::hash_proc::operator()(root_atom const * a) const { unsigned _a = a->m_x; unsigned _b = ((a->m_i << 2) | (a->m_kind)); unsigned _c = polynomial::manager::id(a->m_p); mix(_a, _b, _c); return _c; } bool root_atom::eq_proc::operator()(root_atom const * a1, root_atom const * a2) const { return a1->m_kind == a2->m_kind && a1->m_x == a2->m_x && a1->m_i == a2->m_i && a1->m_p == a2->m_p; } }; z3-4.4.0/src/nlsat/nlsat_types.h0000644000175000017500000001236212540347414016277 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_types.h Abstract: Basic types used in the nonlinear arithmetic satisfiability procedure. Author: Leonardo de Moura (leonardo) 2012-01-02. Revision History: --*/ #ifndef _NLSAT_TYPES_H_ #define _NLSAT_TYPES_H_ #include"polynomial.h" #include"buffer.h" #include"sat_types.h" #include"z3_exception.h" namespace algebraic_numbers { class anum; class manager; }; namespace nlsat { #define NLSAT_VB_LVL 10 typedef void * assumption; typedef void * assumption_set; typedef sat::bool_var bool_var; typedef sat::bool_var_vector bool_var_vector; const bool_var null_bool_var = sat::null_bool_var; typedef sat::literal literal; const literal null_literal = sat::null_literal; typedef sat::literal_vector literal_vector; inline literal to_literal(unsigned x) { return sat::to_literal(x); } typedef polynomial::var var; typedef polynomial::var_vector var_vector; typedef polynomial::manager pmanager; typedef polynomial::polynomial poly; const var null_var = polynomial::null_var; const var true_bool_var = 0; const literal true_literal(true_bool_var, false); const literal false_literal(true_bool_var, true); typedef polynomial::display_var_proc display_var_proc; class atom; class ineq_atom; // atoms of the form: p=0, p>0, p<0, where p is a product of polynomials class root_atom; // atoms of the form: x=root[i](p), xroot[i](p), where x is a variable and p a polynomial. class clause; class solver; class atom { public: enum kind { EQ=0, LT, GT, ROOT_EQ=10, ROOT_LT, ROOT_GT, ROOT_LE, ROOT_GE }; static kind flip(kind k) { SASSERT(k == EQ || k == LT || k == GT); if (k == LT) return GT; if (k == GT) return LT; return EQ; } protected: friend class solver; kind m_kind; unsigned m_ref_count; bool_var m_bool_var; var m_max_var; public: atom(kind k, var max_var):m_kind(k), m_ref_count(0), m_bool_var(null_bool_var), m_max_var(max_var) {} bool is_eq() const { return m_kind == EQ || m_kind == ROOT_EQ; } bool is_ineq_atom() const { return m_kind <= GT; } bool is_root_atom() const { return m_kind >= ROOT_EQ; } kind get_kind() const { return m_kind; } bool_var bvar() const { return m_bool_var; } var max_var() const { return m_max_var; } 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--; } }; typedef ptr_vector atom_vector; class ineq_atom : public atom { friend class solver; unsigned m_size; poly * m_ps[0]; ineq_atom(kind k, unsigned sz, poly * const * ps, bool const * is_even, var max_var); static unsigned get_obj_size(unsigned sz) { return sizeof(ineq_atom) + sizeof(poly*)*sz; } public: unsigned size() const { return m_size; } poly * p(unsigned i) const { SASSERT(i < size()); return UNTAG(poly*, m_ps[i]); } // Return true if i-th factor has odd degree bool is_odd(unsigned i) const { SASSERT(i < size()); return GET_TAG(m_ps[i]) == 0; } bool is_even(unsigned i) const { return !is_odd(i); } struct khasher { unsigned operator()(ineq_atom const * a) const { return a->m_kind; } }; struct chasher { unsigned operator()(ineq_atom const * a, unsigned idx) const { return polynomial::manager::id(a->p(idx)); } }; struct hash_proc { unsigned operator()(ineq_atom const * a) const; }; struct eq_proc { bool operator()(ineq_atom const * a1, ineq_atom const * a2) const; }; }; class root_atom : public atom { friend class solver; var m_x; unsigned m_i; poly * m_p; root_atom(kind k, var x, unsigned i, poly * p); public: var x() const { return m_x; } unsigned i() const { return m_i; } poly * p() const { return m_p; } struct hash_proc { unsigned operator()(root_atom const * a) const; }; struct eq_proc { bool operator()(root_atom const * a1, root_atom const * a2) const; }; }; inline ineq_atom * to_ineq_atom(atom * a) { SASSERT(a->is_ineq_atom()); return static_cast(a); } inline root_atom * to_root_atom(atom * a) { SASSERT(a->is_root_atom()); return static_cast(a); } inline ineq_atom const * to_ineq_atom(atom const * a) { SASSERT(a->is_ineq_atom()); return static_cast(a); } inline root_atom const * to_root_atom(atom const * a) { SASSERT(a->is_root_atom()); return static_cast(a); } typedef algebraic_numbers::anum anum; typedef algebraic_numbers::manager anum_manager; class solver_exception : public default_exception { public: solver_exception(char const * msg):default_exception(msg) {} }; class assignment; inline int normalize_sign(int s) { if (s < 0) return -1; if (s == 0) return 0; return 1; } }; #endif z3-4.4.0/src/nlsat/nlsat_solver.h0000644000175000017500000001104212540347414016437 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_solver.h Abstract: Nonlinear arithmetic satisfiability procedure. The procedure is complete for nonlinear real arithmetic, but it also has limited support for integers. Author: Leonardo de Moura (leonardo) 2012-01-02. Revision History: --*/ #ifndef _NLSAT_SOLVER_H_ #define _NLSAT_SOLVER_H_ #include"nlsat_types.h" #include"params.h" #include"statistics.h" namespace nlsat { class solver { struct imp; imp * m_imp; public: solver(params_ref const & p); ~solver(); /** \brief Return reference to rational manager. */ unsynch_mpq_manager & qm(); /** \brief Return reference to algebraic number manager. */ anum_manager & am(); /** \brief Return a reference to the polynomial manager used by the solver. */ pmanager & pm(); void set_display_var(display_var_proc const & proc); // ----------------------- // // Variable, Atoms, Clauses & Assumption creation // // ----------------------- /** \brief Create a fresh boolean variable that is not associated with any nonlinear arithmetic atom. */ bool_var mk_bool_var(); /** \brief Create a real/integer variable. */ var mk_var(bool is_int); /** \brief Create an atom of the form: p=0, p<0, p>0 Where p = ps[0]^e[0]*...*ps[sz-1]^e[sz-1] e[i] = 1 if is_even[i] is false e[i] = 2 if is_even[i] is true \pre sz > 0 */ bool_var mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even); /** \brief Create a literal for the p=0, p<0, p>0. Where p = ps[0]^e[0]*...*ps[sz-1]^e[sz-1] for sz > 0 p = 1 for sz = 0 e[i] = 1 if is_even[i] is false e[i] = 2 if is_even[i] is true */ literal mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even); /** \brief Create an atom of the form: x=root[i](p), xroot[i](p) */ bool_var mk_root_atom(atom::kind k, var x, unsigned i, poly * p); void inc_ref(bool_var b); void inc_ref(literal l) { inc_ref(l.var()); } void dec_ref(bool_var b); void dec_ref(literal l) { dec_ref(l.var()); } /** \brief Create a new clause. */ void mk_clause(unsigned num_lits, literal * lits, assumption a = 0); // ----------------------- // // Basic // // ----------------------- /** \brief Return the number of Boolean variables. */ unsigned num_bool_vars() const; /** \brief Get atom associated with Boolean variable. Return 0 if there is none. */ atom * bool_var2atom(bool_var b); /** \brief Return number of integer/real variables */ unsigned num_vars() const; bool is_int(var x) const; // ----------------------- // // Search // // ----------------------- lbool check(); // ----------------------- // // Model // // ----------------------- anum const & value(var x) const; lbool bvalue(bool_var b) const; bool is_interpreted(bool_var b) const; lbool value(literal l) const; // ----------------------- // // Misc // // ----------------------- void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); void set_cancel(bool f); void collect_statistics(statistics & st); void reset_statistics(); void display_status(std::ostream & out) const; // ----------------------- // // Pretty printing // // ----------------------- /** \brief Display solver's state. */ void display(std::ostream & out) const; /** \brief Display literal */ void display(std::ostream & out, literal l) const; /** \brief Display variable */ void display(std::ostream & out, var x) const; display_var_proc const & display_proc() const; }; }; #endif z3-4.4.0/src/nlsat/nlsat_interval_set.cpp0000644000175000017500000007205412540347414020171 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_interval_set.cpp Abstract: Sets of disjoint infeasible intervals. Author: Leonardo de Moura (leonardo) 2012-01-11. Revision History: --*/ #include"nlsat_interval_set.h" #include"algebraic_numbers.h" #include"buffer.h" namespace nlsat { struct interval { unsigned m_lower_open:1; unsigned m_upper_open:1; unsigned m_lower_inf:1; unsigned m_upper_inf:1; literal m_justification; anum m_lower; anum m_upper; }; class interval_set { public: static unsigned get_obj_size(unsigned num) { return sizeof(interval_set) + num*sizeof(interval); } unsigned m_num_intervals; unsigned m_ref_count:31; unsigned m_full:1; interval m_intervals[0]; }; void display(std::ostream & out, anum_manager & am, interval const & curr) { if (curr.m_lower_inf) { out << "(-oo, "; } else { if (curr.m_lower_open) out << "("; else out << "["; am.display_decimal(out, curr.m_lower); out << ", "; } if (curr.m_justification.sign()) out << "~"; out << "p"; out << curr.m_justification.var() << ", "; if (curr.m_upper_inf) { out << "oo)"; } else { am.display_decimal(out, curr.m_upper); if (curr.m_upper_open) out << ")"; else out << "]"; } } bool check_interval(anum_manager & am, interval const & i) { if (i.m_lower_inf) { SASSERT(i.m_lower_open); } if (i.m_upper_inf) { SASSERT(i.m_upper_open); } if (!i.m_lower_inf && !i.m_upper_inf) { int s = am.compare(i.m_lower, i.m_upper); TRACE("nlsat_interval", tout << "lower: "; am.display_decimal(tout, i.m_lower); tout << ", upper: "; am.display_decimal(tout, i.m_upper); tout << "\ns: " << s << "\n";); SASSERT(s <= 0); if (s == 0) { SASSERT(!i.m_lower_open && !i.m_upper_open); } } return true; } bool check_no_overlap(anum_manager & am, interval const & curr, interval const & next) { SASSERT(!curr.m_upper_inf); SASSERT(!next.m_lower_inf); int sign = am.compare(curr.m_upper, next.m_lower); CTRACE("nlsat", sign > 0, display(tout, am, curr); tout << " "; display(tout, am, next); tout << "\n";); SASSERT(sign <= 0); if (sign == 0) { SASSERT(curr.m_upper_open || next.m_lower_open); } return true; } // Check if the intervals are valid, ordered, and are disjoint. bool check_interval_set(anum_manager & am, unsigned sz, interval const * ints) { for (unsigned i = 0; i < sz; i++) { interval const & curr = ints[i]; SASSERT(check_interval(am, curr)); if (i < sz - 1) { interval const & next = ints[i+1]; SASSERT(check_no_overlap(am, curr, next)); } } return true; } interval_set_manager::interval_set_manager(anum_manager & m, small_object_allocator & a): m_am(m), m_allocator(a) { } interval_set_manager::~interval_set_manager() { } void interval_set_manager::del(interval_set * s) { if (s == 0) return; unsigned num = s->m_num_intervals; unsigned obj_sz = interval_set::get_obj_size(num); for (unsigned i = 0; i < num; i++) { m_am.del(s->m_intervals[i].m_lower); m_am.del(s->m_intervals[i].m_upper); } s->~interval_set(); m_allocator.deallocate(obj_sz, s); } void interval_set_manager::dec_ref(interval_set * s) { SASSERT(s->m_ref_count > 0); s->m_ref_count--; if (s->m_ref_count == 0) del(s); } void interval_set_manager::inc_ref(interval_set * s) { s->m_ref_count++; } interval_set * interval_set_manager::mk(bool lower_open, bool lower_inf, anum const & lower, bool upper_open, bool upper_inf, anum const & upper, literal justification) { void * mem = m_allocator.allocate(interval_set::get_obj_size(1)); interval_set * new_set = new (mem) interval_set(); new_set->m_num_intervals = 1; new_set->m_ref_count = 0; new_set->m_full = lower_inf && upper_inf; interval * i = new (new_set->m_intervals) interval(); i->m_lower_open = lower_open; i->m_lower_inf = lower_inf; i->m_upper_open = upper_open; i->m_upper_inf = upper_inf; i->m_justification = justification; if (!lower_inf) m_am.set(i->m_lower, lower); if (!upper_inf) m_am.set(i->m_upper, upper); SASSERT(check_interval_set(m_am, 1, new_set->m_intervals)); return new_set; } inline int compare_lower_lower(anum_manager & am, interval const & i1, interval const & i2) { if (i1.m_lower_inf && i2.m_lower_inf) return 0; if (i1.m_lower_inf) return -1; if (i2.m_lower_inf) return 1; SASSERT(!i1.m_lower_inf && !i2.m_lower_inf); int s = am.compare(i1.m_lower, i2.m_lower); if (s != 0) return s; if (i1.m_lower_open == i2.m_lower_open) return 0; if (i1.m_lower_open) return 1; else return -1; } inline int compare_upper_upper(anum_manager & am, interval const & i1, interval const & i2) { if (i1.m_upper_inf && i2.m_upper_inf) return 0; if (i1.m_upper_inf) return 1; if (i2.m_upper_inf) return -1; SASSERT(!i1.m_upper_inf && !i2.m_upper_inf); int s = am.compare(i1.m_upper, i2.m_upper); if (s != 0) return s; if (i1.m_upper_open == i2.m_upper_open) return 0; if (i1.m_upper_open) return -1; else return 1; } inline int compare_upper_lower(anum_manager & am, interval const & i1, interval const & i2) { if (i1.m_upper_inf || i2.m_lower_inf) return 1; SASSERT(!i1.m_upper_inf && !i2.m_lower_inf); int s = am.compare(i1.m_upper, i2.m_lower); if (s != 0) return s; if (!i1.m_upper_open && !i2.m_lower_open) return 0; return -1; } typedef sbuffer interval_buffer; // Given two interval in an interval set s.t. curr occurs before next. // We say curr and next are "adjacent" iff // there is no "space" between them. bool adjacent(anum_manager & am, interval const & curr, interval const & next) { SASSERT(!curr.m_upper_inf); SASSERT(!next.m_lower_inf); int sign = am.compare(curr.m_upper, next.m_lower); SASSERT(sign <= 0); if (sign == 0) { SASSERT(curr.m_upper_open || next.m_lower_open); return !curr.m_upper_open || !next.m_lower_open; } return false; } inline void push_back(anum_manager & am, interval_buffer & buf, bool lower_open, bool lower_inf, anum const & lower, bool upper_open, bool upper_inf, anum const & upper, literal justification) { buf.push_back(interval()); interval & i = buf.back(); i.m_lower_open = lower_open; i.m_lower_inf = lower_inf; am.set(i.m_lower, lower); i.m_upper_open = upper_open; i.m_upper_inf = upper_inf; am.set(i.m_upper, upper); i.m_justification = justification; SASSERT(check_interval(am, i)); } inline void push_back(anum_manager & am, interval_buffer & buf, interval const & i) { push_back(am, buf, i.m_lower_open, i.m_lower_inf, i.m_lower, i.m_upper_open, i.m_upper_inf, i.m_upper, i.m_justification); } inline interval_set * mk_interval(small_object_allocator & allocator, interval_buffer & buf, bool full) { unsigned sz = buf.size(); void * mem = allocator.allocate(interval_set::get_obj_size(sz)); interval_set * new_set = new (mem) interval_set(); new_set->m_full = full; new_set->m_ref_count = 0; new_set->m_num_intervals = sz; memcpy(new_set->m_intervals, buf.c_ptr(), sizeof(interval)*sz); return new_set; } interval_set * interval_set_manager::mk_union(interval_set const * s1, interval_set const * s2) { TRACE("nlsat_interval", tout << "mk_union\ns1: "; display(tout, s1); tout << "\ns2: "; display(tout, s2); tout << "\n";); if (s1 == 0 || s1 == s2) return const_cast(s2); if (s2 == 0) return const_cast(s1); if (s1->m_full) return const_cast(s1); if (s2->m_full) return const_cast(s2); interval_buffer result; unsigned sz1 = s1->m_num_intervals; unsigned sz2 = s2->m_num_intervals; unsigned i1 = 0; unsigned i2 = 0; while (true) { if (i1 >= sz1) { while (i2 < sz2) { TRACE("nlsat_interval", tout << "adding remaining intervals from s2: "; nlsat::display(tout, m_am, s2->m_intervals[i2]); tout << "\n";); push_back(m_am, result, s2->m_intervals[i2]); i2++; } break; } if (i2 >= sz2) { while (i1 < sz1) { TRACE("nlsat_interval", tout << "adding remaining intervals from s1: "; nlsat::display(tout, m_am, s1->m_intervals[i1]); tout << "\n";); push_back(m_am, result, s1->m_intervals[i1]); i1++; } break; } interval const & int1 = s1->m_intervals[i1]; interval const & int2 = s2->m_intervals[i2]; int l1_l2_sign = compare_lower_lower(m_am, int1, int2); int u1_u2_sign = compare_upper_upper(m_am, int1, int2); TRACE("nlsat_interval", tout << "i1: " << i1 << ", i2: " << i2 << "\n"; tout << "int1: "; nlsat::display(tout, m_am, int1); tout << "\n"; tout << "int2: "; nlsat::display(tout, m_am, int2); tout << "\n";); if (l1_l2_sign <= 0) { if (u1_u2_sign == 0) { // Cases: // 1) [ ] // [ ] // // 2) [ ] // [ ] // TRACE("nlsat_interval", tout << "l1_l2_sign <= 0, u1_u2_sign == 0\n";); push_back(m_am, result, int1); i1++; i2++; } else if (u1_u2_sign > 0) { // Cases: // // 1) [ ] // [ ] // // 2) [ ] // [ ] i2++; TRACE("nlsat_interval", tout << "l1_l2_sign <= 0, u1_u2_sign > 0\n";); // i1 may consume other intervals of s2 } else { SASSERT(u1_u2_sign < 0); int u1_l2_sign = compare_upper_lower(m_am, int1, int2); if (u1_l2_sign < 0) { SASSERT(l1_l2_sign < 0); // Cases: // 1) [ ] // [ ] TRACE("nlsat_interval", tout << "l1_l2_sign <= 0, u1_u2_sign < 0, u1_l2_sign < 0\n";); push_back(m_am, result, int1); i1++; } else if (u1_l2_sign == 0) { SASSERT(l1_l2_sign <= 0); SASSERT(!int1.m_upper_open && !int2.m_lower_open); SASSERT(!int2.m_lower_inf); TRACE("nlsat_interval", tout << "l1_l2_sign <= 0, u1_u2_sign < 0, u1_l2_sign == 0\n";); // Cases: if (l1_l2_sign != 0) { SASSERT(l1_l2_sign < 0); // 1) [ ] // [ ] SASSERT(!int2.m_lower_open); push_back(m_am, result, int1.m_lower_open, int1.m_lower_inf, int1.m_lower, true /* open */, false /* not +oo */, int1.m_upper, int1.m_justification); i1++; } else { SASSERT(l1_l2_sign == 0); // 2) u <<< int1 is a singleton // [ ] // just consume int1 i1++; } } else { SASSERT(l1_l2_sign <= 0); SASSERT(u1_u2_sign < 0); SASSERT(u1_l2_sign > 0); TRACE("nlsat_interval", tout << "l1_l2_sign <= 0, u1_u2_sign < 0, u1_l2_sign > 0\n";); if (l1_l2_sign == 0) { // Case: // 1) [ ] // [ ] // just consume int1 i1++; } else { SASSERT(l1_l2_sign < 0); SASSERT(u1_u2_sign < 0); SASSERT(u1_l2_sign > 0); // 2) [ ] // [ ] push_back(m_am, result, int1.m_lower_open, int1.m_lower_inf, int1.m_lower, !int2.m_lower_open, false /* not +oo */, int2.m_lower, int1.m_justification); i1++; } } } } else { SASSERT(l1_l2_sign > 0); if (u1_u2_sign == 0) { TRACE("nlsat_interval", tout << "l1_l2_sign > 0, u1_u2_sign == 0\n";); // Case: // 1) [ ] // [ ] // push_back(m_am, result, int2); i1++; i2++; } else if (u1_u2_sign < 0) { TRACE("nlsat_interval", tout << "l1_l2_sign > 0, u1_u2_sign > 0\n";); // Case: // 1) [ ] // [ ] i1++; // i2 may consume other intervals of s1 } else { int u2_l1_sign = compare_upper_lower(m_am, int2, int1); if (u2_l1_sign < 0) { TRACE("nlsat_interval", tout << "l1_l2_sign > 0, u1_u2_sign > 0, u2_l1_sign < 0\n";); // Case: // 1) [ ] // [ ] push_back(m_am, result, int2); i2++; } else if (u2_l1_sign == 0) { TRACE("nlsat_interval", tout << "l1_l2_sign > 0, u1_u2_sign > 0, u2_l1_sign == 0\n";); SASSERT(!int1.m_lower_open && !int2.m_upper_open); SASSERT(!int1.m_lower_inf); // Case: // [ ] // [ ] push_back(m_am, result, int2.m_lower_open, int2.m_lower_inf, int2.m_lower, true /* open */, false /* not +oo */, int2.m_upper, int2.m_justification); i2++; } else { TRACE("nlsat_interval", tout << "l1_l2_sign > 0, u1_u2_sign > 0, u2_l1_sign > 0\n";); SASSERT(l1_l2_sign > 0); SASSERT(u1_u2_sign > 0); SASSERT(u2_l1_sign > 0); // Case: // [ ] // [ ] push_back(m_am, result, int2.m_lower_open, int2.m_lower_inf, int2.m_lower, !int1.m_lower_open, false /* not +oo */, int1.m_lower, int2.m_justification); i2++; } } } SASSERT(result.size() <= 1 || check_no_overlap(m_am, result[result.size() - 2], result[result.size() - 1])); } SASSERT(!result.empty()); SASSERT(check_interval_set(m_am, result.size(), result.c_ptr())); // Compress // Remark: we only combine adjacent intervals when they have the same justification unsigned j = 0; unsigned sz = result.size(); for (unsigned i = 1; i < sz; i++) { interval & curr = result[j]; interval & next = result[i]; if (curr.m_justification == next.m_justification && adjacent(m_am, curr, next)) { // merge them curr.m_upper_inf = next.m_upper_inf; curr.m_upper_open = next.m_upper_open; m_am.swap(curr.m_upper, next.m_upper); } else { j++; if (i != j) { interval & next_curr = result[j]; next_curr.m_lower_inf = next.m_lower_inf; next_curr.m_lower_open = next.m_lower_open; m_am.swap(next_curr.m_lower, next.m_lower); next_curr.m_upper_inf = next.m_upper_inf; next_curr.m_upper_open = next.m_upper_open; m_am.swap(next_curr.m_upper, next.m_upper); next_curr.m_justification = next.m_justification; } } } j++; for (unsigned i = j; i < sz; i++) { interval & curr = result[i]; m_am.del(curr.m_lower); m_am.del(curr.m_upper); } result.shrink(j); SASSERT(check_interval_set(m_am, result.size(), result.c_ptr())); sz = j; SASSERT(sz >= 1); bool found_slack = !result[0].m_lower_inf || !result[sz-1].m_upper_inf; // Check if full for (unsigned i = 0; i < sz - 1 && !found_slack; i++) { if (!adjacent(m_am, result[i], result[i+1])) found_slack = true; } // Create new interval set interval_set * new_set = mk_interval(m_allocator, result, !found_slack); SASSERT(check_interval_set(m_am, sz, new_set->m_intervals)); return new_set; } bool interval_set_manager::is_full(interval_set const * s) { if (s == 0) return false; return s->m_full == 1; } unsigned interval_set_manager::num_intervals(interval_set const * s) const { if (s == 0) return 0; return s->m_num_intervals; } bool interval_set_manager::subset(interval_set const * s1, interval_set const * s2) { if (s1 == s2) return true; if (s1 == 0) return true; if (s2 == 0) return false; if (s2->m_full) return true; if (s1->m_full) return false; unsigned sz1 = s1->m_num_intervals; unsigned sz2 = s2->m_num_intervals; SASSERT(sz1 > 0 && sz2 > 0); unsigned i1 = 0; unsigned i2 = 0; while (i1 < sz1 && i2 < sz2) { interval const & int1 = s1->m_intervals[i1]; interval const & int2 = s2->m_intervals[i2]; TRACE("nlsat_interval", tout << "subset main loop, i1: " << i1 << ", i2: " << i2 << "\n"; tout << "int1: "; nlsat::display(tout, m_am, int1); tout << "\n"; tout << "int2: "; nlsat::display(tout, m_am, int2); tout << "\n";); if (compare_lower_lower(m_am, int1, int2) < 0) { TRACE("nlsat_interval", tout << "done\n";); // interval [int1.lower1, int2.lower2] is not in s2 // s1: [ ... // s2: [ ... return false; } while (i2 < sz2) { interval const & int2 = s2->m_intervals[i2]; TRACE("nlsat_interval", tout << "inner loop, i2: " << i2 << "\n"; tout << "int2: "; nlsat::display(tout, m_am, int2); tout << "\n";); int u1_u2_sign = compare_upper_upper(m_am, int1, int2); if (u1_u2_sign == 0) { TRACE("nlsat_interval", tout << "case 1, break\n";); // consume both // s1: ... ] // s2: ... ] i1++; i2++; break; } else if (u1_u2_sign < 0) { TRACE("nlsat_interval", tout << "case 2, break\n";); // consume only int1, int2 may cover other intervals of s1 // s1: ... ] // s2: ... ] i1++; break; } else { SASSERT(u1_u2_sign > 0); int u2_l1_sign = compare_upper_lower(m_am, int2, int1); TRACE("nlsat_interval", tout << "subset, u2_l1_sign: " << u2_l1_sign << "\n";); if (u2_l1_sign < 0) { TRACE("nlsat_interval", tout << "case 3, break\n";); // s1: [ ... // s2: [ ... ] ... i2++; break; } SASSERT(u2_l1_sign >= 0); // s1: [ ... ] // s2: [ ... ] if (i2 == sz2 - 1) { TRACE("nlsat_interval", tout << "case 4, done\n";); // s1: ... ] // s2: ...] // the interval [int2.upper, int1.upper] is not in s2 return false; // last interval of s2 } interval const & next2 = s2->m_intervals[i2+1]; if (!adjacent(m_am, int2, next2)) { TRACE("nlsat_interval", tout << "not adjacent, done\n";); // s1: ... ] // s2: ... ] [ // the interval [int2.upper, min(int1.upper, next2.lower)] is not in s2 return false; } TRACE("nlsat_interval", tout << "continue..\n";); // continue with adjacent interval of s2 // s1: ... ] // s2: ..][ ... i2++; } } } return i1 == sz1; } bool interval_set_manager::set_eq(interval_set const * s1, interval_set const * s2) { if (s1 == 0 || s2 == 0) return s1 == s2; if (s1->m_full || s2->m_full) return s1->m_full == s2->m_full; // TODO: check if bottleneck, then replace simple implementation return subset(s1, s2) && subset(s2, s1); } bool interval_set_manager::eq(interval_set const * s1, interval_set const * s2) { if (s1 == 0 || s2 == 0) return s1 == s2; if (s1->m_num_intervals != s2->m_num_intervals) return false; for (unsigned i = 0; i < s1->m_num_intervals; i++) { interval const & int1 = s1->m_intervals[i]; interval const & int2 = s2->m_intervals[i]; if (int1.m_lower_inf != int2.m_lower_inf || int1.m_lower_open != int2.m_lower_open || int1.m_upper_inf != int2.m_upper_inf || int1.m_upper_open != int2.m_upper_open || int1.m_justification != int2.m_justification || !m_am.eq(int1.m_lower, int2.m_lower) || !m_am.eq(int1.m_upper, int2.m_upper)) return false; } return true; } void interval_set_manager::get_justifications(interval_set const * s, literal_vector & js) { js.reset(); unsigned num = num_intervals(s); for (unsigned i = 0; i < num; i++) { literal l = s->m_intervals[i].m_justification; unsigned lidx = l.index(); if (m_already_visited.get(lidx, false)) continue; m_already_visited.setx(lidx, true, false); js.push_back(l); } for (unsigned i = 0; i < num; i++) { literal l = s->m_intervals[i].m_justification; unsigned lidx = l.index(); m_already_visited[lidx] = false; } } interval_set * interval_set_manager::get_interval(interval_set const * s, unsigned idx) const { SASSERT(idx < num_intervals(s)); interval_buffer result; push_back(m_am, result, s->m_intervals[idx]); bool found_slack = !result[0].m_lower_inf || !result[0].m_upper_inf; interval_set * new_set = mk_interval(m_allocator, result, !found_slack); SASSERT(check_interval_set(m_am, result.size(), new_set->m_intervals)); return new_set; } void interval_set_manager::peek_in_complement(interval_set const * s, anum & w, bool randomize) { SASSERT(!is_full(s)); if (s == 0) { if (randomize) { int num = m_rand() % 2 == 0 ? 1 : -1; #define MAX_RANDOM_DEN_K 4 int den_k = (m_rand() % MAX_RANDOM_DEN_K); int den = 1 << den_k; scoped_mpq _w(m_am.qm()); m_am.qm().set(_w, num, den); m_am.set(w, _w); return; } else { m_am.set(w, 0); return; } } unsigned n = 0; unsigned num = num_intervals(s); if (!s->m_intervals[0].m_lower_inf) { // lower is not -oo n++; m_am.int_lt(s->m_intervals[0].m_lower, w); if (!randomize) return; } if (!s->m_intervals[num-1].m_upper_inf) { // upper is not oo n++; if (n == 1 || m_rand()%n == 0) m_am.int_gt(s->m_intervals[num-1].m_upper, w); if (!randomize) return; } // Try to find a gap that is not an unit. for (unsigned i = 1; i < num; i++) { if (m_am.lt(s->m_intervals[i-1].m_upper, s->m_intervals[i].m_lower)) { n++; if (n == 1 || m_rand()%n == 0) m_am.select(s->m_intervals[i-1].m_upper, s->m_intervals[i].m_lower, w); if (!randomize) return; } } if (n > 0) return; // Try to find a rational unsigned irrational_i = UINT_MAX; for (unsigned i = 1; i < num; i++) { if (s->m_intervals[i-1].m_upper_open && s->m_intervals[i].m_lower_open) { SASSERT(m_am.eq(s->m_intervals[i-1].m_upper, s->m_intervals[i].m_lower)); // otherwise we would have found it in the previous step if (m_am.is_rational(s->m_intervals[i-1].m_upper)) { m_am.set(w, s->m_intervals[i-1].m_upper); return; } if (irrational_i == UINT_MAX) irrational_i = i-1; } } SASSERT(irrational_i != UINT_MAX); // Last option: peek irrational witness :-( SASSERT(s->m_intervals[irrational_i].m_upper_open && s->m_intervals[irrational_i+1].m_lower_open); m_am.set(w, s->m_intervals[irrational_i].m_upper); } void interval_set_manager::display(std::ostream & out, interval_set const * s) const { if (s == 0) { out << "{}"; return; } out << "{"; for (unsigned i = 0; i < s->m_num_intervals; i++) { if (i > 0) out << ", "; nlsat::display(out, m_am, s->m_intervals[i]); } out << "}"; if (s->m_full) out << "*"; } }; z3-4.4.0/src/nlsat/nlsat_explain.cpp0000644000175000017500000015424612540347414017136 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_explain.cpp Abstract: Functor that implements the "explain" procedure defined in Dejan and Leo's paper. Author: Leonardo de Moura (leonardo) 2012-01-13. Revision History: --*/ #include"nlsat_explain.h" #include"nlsat_assignment.h" #include"nlsat_evaluator.h" #include"algebraic_numbers.h" #include"ref_buffer.h" namespace nlsat { typedef polynomial::polynomial_ref_vector polynomial_ref_vector; typedef ref_buffer polynomial_ref_buffer; struct explain::imp { solver & m_solver; assignment const & m_assignment; atom_vector const & m_atoms; atom_vector const & m_x2eq; anum_manager & m_am; polynomial::cache & m_cache; pmanager & m_pm; polynomial_ref_vector m_ps; polynomial_ref_vector m_psc_tmp; polynomial_ref_vector m_factors; scoped_anum_vector m_roots_tmp; 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; svector m_in_set; todo_set(polynomial::cache & u):m_cache(u), m_set(u.pm()) {} void reset() { pmanager & pm = m_set.m(); unsigned sz = m_set.size(); for (unsigned i = 0; i < sz; i++) { m_in_set[pm.id(m_set.get(i))] = false; } m_set.reset(); } void insert(poly * p) { pmanager & pm = m_set.m(); p = m_cache.mk_unique(p); unsigned pid = pm.id(p); if (m_in_set.get(pid, false)) return; m_in_set.setx(pid, true, false); m_set.push_back(p); } bool empty() const { return m_set.empty(); } // Return max variable in todo_set var max_var() const { pmanager & pm = m_set.m(); var max = null_var; unsigned sz = m_set.size(); for (unsigned i = 0; i < sz; i++) { var x = pm.max_var(m_set.get(i)); SASSERT(x != null_var); if (max == null_var || x > max) max = x; } return max; } /** \brief Remove the maximal polynomials from the set and store them in max_polys. Return the maximal variable */ var remove_max_polys(polynomial_ref_vector & max_polys) { max_polys.reset(); var x = max_var(); pmanager & pm = m_set.m(); unsigned sz = m_set.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { poly * p = m_set.get(i); var y = pm.max_var(p); SASSERT(y <= x); if (y == x) { max_polys.push_back(p); m_in_set[pm.id(p)] = false; } else { m_set.set(j, p); j++; } } m_set.shrink(j); return x; } }; // temporary field for store todo set of polynomials todo_set m_todo; // temporary fields for preprocessing core scoped_literal_vector m_core1; scoped_literal_vector m_core2; // temporary fields for storing the result scoped_literal_vector * m_result; svector m_already_added_literal; evaluator & m_evaluator; imp(solver & s, assignment const & x2v, polynomial::cache & u, atom_vector const & atoms, atom_vector const & x2eq, evaluator & ev): m_solver(s), m_assignment(x2v), m_atoms(atoms), m_x2eq(x2eq), m_am(x2v.am()), m_cache(u), m_pm(u.pm()), m_ps(m_pm), m_psc_tmp(m_pm), m_factors(m_pm), m_roots_tmp(m_am), m_todo(u), m_core1(s), m_core2(s), m_result(0), m_evaluator(ev) { m_simplify_cores = false; m_full_dimensional = false; m_minimize_cores = false; } ~imp() { } void display(std::ostream & out, polynomial_ref const & p) const { m_pm.display(out, p, m_solver.display_proc()); } void display(std::ostream & out, polynomial_ref_vector const & ps, char const * delim = "\n") const { for (unsigned i = 0; i < ps.size(); i++) { if (i > 0) out << delim; m_pm.display(out, ps.get(i), m_solver.display_proc()); } } void display(std::ostream & out, literal l) const { m_solver.display(out, l); } void display_var(std::ostream & out, var x) const { m_solver.display(out, x); } void display(std::ostream & out, unsigned sz, literal const * ls) { for (unsigned i = 0; i < sz; i++) { display(out, ls[i]); out << "\n"; } } void display(std::ostream & out, literal_vector const & ls) { display(out, ls.size(), ls.c_ptr()); } void display(std::ostream & out, scoped_literal_vector const & ls) { display(out, ls.size(), ls.c_ptr()); } /** \brief Add literal to the result vector. */ void add_literal(literal l) { SASSERT(m_result != 0); SASSERT(l != true_literal); if (l == false_literal) return; unsigned lidx = l.index(); if (m_already_added_literal.get(lidx, false)) return; TRACE("nlsat_explain", tout << "adding literal: " << lidx << "\n"; m_solver.display(tout, l); tout << "\n";); m_already_added_literal.setx(lidx, true, false); m_result->push_back(l); } /** \brief Reset m_already_added vector using m_result */ void reset_already_added() { SASSERT(m_result != 0); unsigned sz = m_result->size(); for (unsigned i = 0; i < sz; i++) m_already_added_literal[(*m_result)[i].index()] = false; } /** \brief evaluate the given polynomial in the current interpretation. max_var(p) must be assigned in the current interpretation. */ int sign(polynomial_ref const & p) { TRACE("nlsat_explain", tout << "p: " << p << "\n";); SASSERT(max_var(p) == null_var || m_assignment.is_assigned(max_var(p))); return m_am.eval_sign_at(p, m_assignment); } /** \brief Wrapper for factorization */ void factor(polynomial_ref & p, polynomial_ref_vector & fs) { // TODO: add params, caching TRACE("nlsat_factor", tout << "factor\n" << p << "\n";); fs.reset(); m_cache.factor(p.get(), fs); } /** \brief Wrapper for psc chain computation */ void psc_chain(polynomial_ref & p, polynomial_ref & q, unsigned x, polynomial_ref_vector & result) { // TODO caching SASSERT(max_var(p) == max_var(q)); SASSERT(max_var(p) == x); m_cache.psc_chain(p, q, x, result); } /** \breif Store in ps the polynomials occurring in the given literals. */ void collect_polys(unsigned num, literal const * ls, polynomial_ref_vector & ps) { ps.reset(); for (unsigned i = 0; i < num; i++) { atom * a = m_atoms[ls[i].var()]; SASSERT(a != 0); if (a->is_ineq_atom()) { unsigned sz = to_ineq_atom(a)->size(); for (unsigned j = 0; j < sz; j++) ps.push_back(to_ineq_atom(a)->p(j)); } else { ps.push_back(to_root_atom(a)->p()); } } } /** \brief Add literal p != 0 into m_result. */ ptr_vector m_zero_fs; svector m_is_even; void add_zero_assumption(polynomial_ref & p) { // If p is of the form p1^n1 * ... * pk^nk, // then only the factors that are zero in the current interpretation needed to be considered. // I don't want to create a nested conjunction in the clause. // Then, I assert p_i1 * ... * p_im != 0 factor(p, m_factors); unsigned num_factors = m_factors.size(); m_zero_fs.reset(); m_is_even.reset(); polynomial_ref f(m_pm); for (unsigned i = 0; i < num_factors; i++) { f = m_factors.get(i); if (sign(f) == 0) { m_zero_fs.push_back(m_factors.get(i)); m_is_even.push_back(false); } } SASSERT(!m_zero_fs.empty()); // one of the factors must be zero in the current interpretation, since p is zero in it. literal l = m_solver.mk_ineq_literal(atom::EQ, m_zero_fs.size(), m_zero_fs.c_ptr(), m_is_even.c_ptr()); l.neg(); TRACE("nlsat_explain", tout << "adding (zero assumption) literal:\n"; display(tout, l); tout << "\n";); add_literal(l); } void add_simple_assumption(atom::kind k, poly * p, bool sign = false) { SASSERT(k == atom::EQ || k == atom::LT || k == atom::GT); bool is_even = false; bool_var b = m_solver.mk_ineq_atom(k, 1, &p, &is_even); literal l(b, !sign); add_literal(l); } void add_assumption(atom::kind k, poly * p, bool sign = false) { // TODO: factor add_simple_assumption(k, p, sign); } /** \brief Eliminate "vanishing leading coefficients" of p. That is, coefficients that vanish in the current interpretation. The resultant p is a reduct of p s.t. its leading coefficient does not vanish in the current interpretation. If all coefficients of p vanish, then the resultant p is the zero polynomial. */ void elim_vanishing(polynomial_ref & p) { SASSERT(!is_const(p)); var x = max_var(p); unsigned k = degree(p, x); SASSERT(k > 0); polynomial_ref lc(m_pm); polynomial_ref reduct(m_pm); while (true) { if (is_const(p)) return; if (k == 0) { // x vanished from p, peek next maximal variable x = max_var(p); SASSERT(x != null_var); k = degree(p, x); } if (m_pm.nonzero_const_coeff(p, x, k)) return; // lc is a nonzero constant lc = m_pm.coeff(p, x, k, reduct); if (!is_zero(lc)) { if (sign(lc) != 0) return; // lc is not the zero polynomial, but it vanished in the current interpretaion. // so we keep searching... add_zero_assumption(lc); } if (k == 0) { // all coefficients of p vanished in the current interpretation, // and were added as assumptions. p = m_pm.mk_zero(); return; } k--; p = reduct; } } /** Eliminate vanishing coefficients of polynomials in ps. The coefficients that are zero (i.e., vanished) are added as assumptions into m_result. */ void elim_vanishing(polynomial_ref_vector & ps) { unsigned j = 0; unsigned sz = ps.size(); polynomial_ref p(m_pm); for (unsigned i = 0; i < sz; i++) { p = ps.get(i); elim_vanishing(p); if (!is_const(p)) { ps.set(j, p); j++; } } ps.shrink(j); } /** Normalize literal with respect to given maximal variable. The basic idea is to eliminate vanishing (leading) coefficients from a (arithmetic) literal, and factors from lower stages. The vanishing coefficients and factors from lower stages are added as assumptions to the lemma being generated. Example 1) Assume - l is of the form (y^2 - 2)*x^3 + y*x + 1 > 0 - x is the maximal variable - y is assigned to sqrt(2) Thus, (y^2 - 2) the coefficient of x^3 vanished. This method returns y*x + 1 > 0 and adds the assumption (y^2 - 2) = 0 to the lemma Example 2) Assume - l is of the form (x + 2)*(y - 1) > 0 - x is the maximal variable - y is assigned to 0 (x + 2) < 0 is returned and assumption (y - 1) < 0 is added as an assumption. Remark: root atoms are not normalized */ literal normalize(literal l, var max) { bool_var b = l.var(); if (b == true_bool_var) return l; SASSERT(m_atoms[b] != 0); if (m_atoms[b]->is_ineq_atom()) { polynomial_ref_buffer ps(m_pm); sbuffer is_even; polynomial_ref p(m_pm); ineq_atom * a = to_ineq_atom(m_atoms[b]); int atom_sign = 1; unsigned sz = a->size(); bool normalized = false; // true if the literal needs to be normalized for (unsigned i = 0; i < sz; i++) { p = a->p(i); if (max_var(p) == max) elim_vanishing(p); // eliminate vanishing coefficients of max if (is_const(p) || max_var(p) < max) { int s = sign(p); if (!is_const(p)) { SASSERT(max_var(p) != null_var); SASSERT(max_var(p) < max); // factor p is a lower stage polynomial, so we should add assumption to justify p being eliminated if (s == 0) add_simple_assumption(atom::EQ, p); // add assumption p = 0 else if (a->is_even(i)) add_simple_assumption(atom::EQ, p, true); // add assumption p != 0 else if (s < 0) add_simple_assumption(atom::LT, p); // add assumption p < 0 else add_simple_assumption(atom::GT, p); // add assumption p > 0 } if (s == 0) { bool atom_val = a->get_kind() == atom::EQ; bool lit_val = l.sign() ? !atom_val : atom_val; return lit_val ? true_literal : false_literal; } else if (s == -1 && a->is_odd(i)) { atom_sign = -atom_sign; } normalized = true; } else { if (p != a->p(i)) { SASSERT(!m_pm.eq(p, a->p(i))); normalized = true; } is_even.push_back(a->is_even(i)); ps.push_back(p); } } if (ps.empty()) { SASSERT(atom_sign != 0); // LHS is positive or negative. It is positive if atom_sign > 0 and negative if atom_sign < 0 bool atom_val; if (a->get_kind() == atom::EQ) atom_val = false; else if (a->get_kind() == atom::LT) atom_val = atom_sign < 0; else atom_val = atom_sign > 0; bool lit_val = l.sign() ? !atom_val : atom_val; return lit_val ? true_literal : false_literal; } else if (normalized) { atom::kind new_k = a->get_kind(); if (atom_sign < 0) new_k = atom::flip(new_k); literal new_l = m_solver.mk_ineq_literal(new_k, ps.size(), ps.c_ptr(), is_even.c_ptr()); if (l.sign()) new_l.neg(); return new_l; } else { SASSERT(atom_sign > 0); return l; } } else { return l; } } /** Normalize literals (in the conflicting core) with respect to given maximal variable. The basic idea is to eliminate vanishing (leading) coefficients (and factors from lower stages) from (arithmetic) literals, */ void normalize(scoped_literal_vector & C, var max) { unsigned sz = C.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { literal new_l = normalize(C[i], max); if (new_l == true_literal) continue; if (new_l == false_literal) { // false literal was created. The assumptions added are sufficient for implying the conflict. C.reset(); return; } C.set(j, new_l); j++; } C.shrink(j); } var max_var(poly const * p) { return m_pm.max_var(p); } /** \brief Return the maximal variable in a set of nonconstant polynomials. */ var max_var(polynomial_ref_vector const & ps) { if (ps.empty()) return null_var; var max = max_var(ps.get(0)); SASSERT(max != null_var); // there are no constant polynomials in ps unsigned sz = ps.size(); for (unsigned i = 1; i < sz; i++) { var curr = m_pm.max_var(ps.get(i)); SASSERT(curr != null_var); if (curr > max) max = curr; } return max; } polynomial::var max_var(literal l) { atom * a = m_atoms[l.var()]; if (a != 0) return a->max_var(); else return null_var; } /** \brief Return the maximal variable in the given set of literals */ var max_var(unsigned sz, literal const * ls) { var max = null_var; for (unsigned i = 0; i < sz; i++) { literal l = ls[i]; atom * a = m_atoms[l.var()]; if (a != 0) { var x = a->max_var(); SASSERT(x != null_var); if (max == null_var || x > max) max = x; } } return max; } /** \brief Move the polynomials in q in ps that do not contain x to qs. */ void keep_p_x(polynomial_ref_vector & ps, var x, polynomial_ref_vector & qs) { unsigned sz = ps.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { poly * q = ps.get(i); if (max_var(q) != x) { qs.push_back(q); } else { ps.set(j, q); j++; } } ps.shrink(j); } /** \brief Add factors of p to todo */ void add_factors(polynomial_ref & p) { if (is_const(p)) return; elim_vanishing(p); if (is_const(p)) return; 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(p); } } /** \brief Add leading coefficients of the polynomials in ps. \pre all polynomials in ps contain x Remark: the leading coefficients do not vanish in the current model, since all polynomials in ps were pre-processed using elim_vanishing. */ void add_lc(polynomial_ref_vector & ps, var x) { polynomial_ref p(m_pm); polynomial_ref lc(m_pm); unsigned sz = ps.size(); for (unsigned i = 0; i < sz; i++) { p = ps.get(i); unsigned k = degree(p, x); SASSERT(k > 0); TRACE("nlsat_explain", tout << "add_lc, x: "; display_var(tout, x); tout << "\nk: " << k << "\n"; display(tout, p); tout << "\n";); if (m_pm.nonzero_const_coeff(p, x, k)) { TRACE("nlsat_explain", tout << "constant coefficient, skipping...\n";); continue; } lc = m_pm.coeff(p, x, k); SASSERT(sign(lc) != 0); SASSERT(!is_const(lc)); add_factors(lc); } } /** \brief Add v-psc(p, q, x) into m_todo */ void psc(polynomial_ref & p, polynomial_ref & q, var x) { polynomial_ref_vector & S = m_psc_tmp; polynomial_ref s(m_pm); TRACE("nlsat_explain", tout << "computing psc of\n"; display(tout, p); tout << "\n"; display(tout, q); tout << "\n";); psc_chain(p, q, x, S); unsigned sz = S.size(); for (unsigned i = 0; i < sz; i++) { s = S.get(i); TRACE("nlsat_explain", tout << "processing psc(" << i << ")\n"; display(tout, s); tout << "\n";); if (is_zero(s)) { TRACE("nlsat_explain", tout << "skipping psc is the zero polynomial\n";); continue; } if (is_const(s)) { TRACE("nlsat_explain", tout << "done, psc is a constant\n";); return; } if (sign(s) == 0) { TRACE("nlsat_explain", tout << "psc vanished, adding zero assumption\n";); add_zero_assumption(s); continue; } TRACE("nlsat_explain", tout << "adding v-psc of\n"; display(tout, p); tout << "\n"; display(tout, q); tout << "\n---->\n"; display(tout, s); tout << "\n";); // s did not vanish completely, but its leading coefficient may have vanished add_factors(s); return; } } /** \brief For each p in ps, add v-psc(x, p, p') into m_todo \pre all polynomials in ps contain x Remark: the leading coefficients do not vanish in the current model, since all polynomials in ps were pre-processed using elim_vanishing. */ void psc_discriminant(polynomial_ref_vector & ps, var x) { polynomial_ref p(m_pm); polynomial_ref p_prime(m_pm); unsigned sz = ps.size(); for (unsigned i = 0; i < sz; i++) { p = ps.get(i); if (degree(p, x) < 2) continue; p_prime = derivative(p, x); psc(p, p_prime, x); } } /** \brief For each p and q in ps, p != q, add v-psc(x, p, q) into m_todo \pre all polynomials in ps contain x Remark: the leading coefficients do not vanish in the current model, since all polynomials in ps were pre-processed using elim_vanishing. */ void psc_resultant(polynomial_ref_vector & ps, var x) { polynomial_ref p(m_pm); polynomial_ref q(m_pm); unsigned sz = ps.size(); for (unsigned i = 0; i < sz - 1; i++) { p = ps.get(i); for (unsigned j = i + 1; j < sz; j++) { q = ps.get(j); psc(p, q, x); } } } void add_root_literal(atom::kind k, var y, unsigned i, poly * p) { scoped_mpz c(m_pm.m()); bool_var b; bool lsign = false; if (m_pm.degree(p, y) == 1 && m_pm.const_coeff(p, y, 1, c)) { SASSERT(!m_pm.m().is_zero(c)); // literal can be expressed using a linear ineq_atom polynomial_ref p_prime(m_pm); p_prime = p; if (m_pm.m().is_neg(c)) p_prime = neg(p_prime); p = p_prime.get(); switch (k) { case atom::ROOT_EQ: k = atom::EQ; lsign = false; break; case atom::ROOT_LT: k = atom::LT; lsign = false; break; case atom::ROOT_GT: k = atom::GT; lsign = false; break; case atom::ROOT_LE: k = atom::GT; lsign = true; break; case atom::ROOT_GE: k = atom::LT; lsign = true; break; default: UNREACHABLE(); break; } bool is_even = false; b = m_solver.mk_ineq_atom(k, 1, &p, &is_even); } else { b = m_solver.mk_root_atom(k, y, i, p); lsign = false; } lsign = !lsign; // adding as an assumption literal l(b, lsign); TRACE("nlsat_explain", tout << "adding literal\n"; display(tout, l); tout << "\n";); add_literal(l); } /** Add one or two literals that specify in which cell of variable y the current interpretation is. One literal is added for the cases: - y in (-oo, min) where min is the minimal root of the polynomials p2 in ps We add literal ! (y < root_1(p2)) - y in (max, oo) where max is the maximal root of the polynomials p1 in ps We add literal ! (y > root_k(p1)) where k is the number of real roots of p - y = r where r is the k-th root of a polynomial p in ps We add literal ! (y = root_k(p)) Two literals are added when - y in (l, u) where (l, u) does not contain any root of polynomials p in ps, and l is the i-th root of a polynomial p1 in ps, and u is the j-th root of a polynomial p2 in ps. We add literals ! (y > root_i(p1)) or !(y < root_j(p2)) */ void add_cell_lits(polynomial_ref_vector & ps, var y) { SASSERT(m_assignment.is_assigned(y)); bool lower_inf = true; bool upper_inf = true; scoped_anum_vector & roots = m_roots_tmp; scoped_anum lower(m_am); scoped_anum upper(m_am); anum const & y_val = m_assignment.value(y); TRACE("nlsat_explain", tout << "adding literals for "; display_var(tout, y); tout << " -> "; m_am.display_decimal(tout, y_val); tout << "\n";); polynomial_ref p_lower(m_pm); unsigned i_lower; polynomial_ref p_upper(m_pm); unsigned i_upper; polynomial_ref p(m_pm); unsigned sz = ps.size(); for (unsigned k = 0; k < sz; k++) { p = ps.get(k); if (max_var(p) != y) continue; roots.reset(); // Variable y is assigned in m_assignment. We must temporarily unassign it. // Otherwise, the isolate_roots procedure will assume p is a constant polynomial. m_am.isolate_roots(p, undef_var_assignment(m_assignment, y), roots); unsigned num_roots = roots.size(); for (unsigned i = 0; i < num_roots; i++) { TRACE("nlsat_explain", tout << "comparing root: "; m_am.display_decimal(tout, roots[i]); tout << "\n";); int s = m_am.compare(y_val, roots[i]); if (s == 0) { // y_val == roots[i] // add literal // ! (y = root_i(p)) add_root_literal(atom::ROOT_EQ, y, i+1, p); return; } else if (s < 0) { // y_val < roots[i] // check if roots[i] is a better upper bound if (upper_inf || m_am.lt(roots[i], upper)) { upper_inf = false; m_am.set(upper, roots[i]); p_upper = p; i_upper = i+1; } } else if (s > 0) { // roots[i] < y_val // check if roots[i] is a better lower bound if (lower_inf || m_am.lt(lower, roots[i])) { lower_inf = false; m_am.set(lower, roots[i]); p_lower = p; i_lower = i+1; } } } } if (!lower_inf) add_root_literal(m_full_dimensional ? atom::ROOT_GE : atom::ROOT_GT, y, i_lower, p_lower); if (!upper_inf) add_root_literal(m_full_dimensional ? atom::ROOT_LE : atom::ROOT_LT, y, i_upper, p_upper); } /** \brief Return true if all polynomials in ps are univariate in x. */ bool all_univ(polynomial_ref_vector const & ps, var x) { unsigned sz = ps.size(); for (unsigned i = 0; i < sz; i++) { poly * p = ps.get(i); if (max_var(p) != x) return false; if (!m_pm.is_univariate(p)) return false; } return true; } /** \brief Apply model-based projection operation defined in our paper. */ void project(polynomial_ref_vector & ps, var max_x) { if (ps.empty()) return; m_todo.reset(); for (unsigned i = 0; i < ps.size(); i++) m_todo.insert(ps.get(i)); var x = m_todo.remove_max_polys(ps); // Remark: after vanishing coefficients are eliminated, ps may not contain max_x anymore if (x < max_x) add_cell_lits(ps, x); while (true) { if (all_univ(ps, x) && m_todo.empty()) { m_todo.reset(); break; } TRACE("nlsat_explain", tout << "project loop, processing var "; display_var(tout, x); tout << "\npolynomials\n"; display(tout, ps); tout << "\n";); add_lc(ps, x); psc_discriminant(ps, x); psc_resultant(ps, x); if (m_todo.empty()) break; x = m_todo.remove_max_polys(ps); add_cell_lits(ps, x); } } bool check_already_added() const { for (unsigned i = 0; i < m_already_added_literal.size(); i++) { SASSERT(m_already_added_literal[i] == false); } return true; } /* Conflicting core simplification using equations. The idea is to use equations to reduce the complexity of the conflicting core. Basic idea: Let l be of the form h > 0 and eq of the form p = 0 Using pseudo-division we have that: lc(p)^d h = q p + r where q and r are the pseudo-quotient and pseudo-remainder d is the integer returned by the pseudo-division algorithm. lc(p) is the leading coefficient of p If d is even or sign(lc(p)) > 0, we have that sign(h) = sign(r) Otherwise sign(h) = -sign(r) flipped the sign We have the following rules If (C and h > 0) implies false Then 1. (C and p = 0 and lc(p) != 0 and r > 0) implies false if d is even 2. (C and p = 0 and lc(p) > 0 and r > 0) implies false if lc(p) > 0 and d is odd 3. (C and p = 0 and lc(p) < 0 and r < 0) implies false if lc(p) < 0 and d is odd If (C and h = 0) implies false Then (C and p = 0 and lc(p) != 0 and r = 0) implies false If (C and h < 0) implies false Then 1. (C and p = 0 and lc(p) != 0 and r < 0) implies false if d is even 2. (C and p = 0 and lc(p) > 0 and r < 0) implies false if lc(p) > 0 and d is odd 3. (C and p = 0 and lc(p) < 0 and r > 0) implies false if lc(p) < 0 and d is odd Good cases: - lc(p) is a constant - p = 0 is already in the conflicting core - p = 0 is linear We only use equations from the conflicting core and lower stages. Equations from lower stages are automatically added to the lemma. */ struct eq_info { poly const * m_eq; polynomial::var m_x; unsigned m_k; poly * m_lc; int m_lc_sign; bool m_lc_const; bool m_lc_add; bool m_lc_add_ineq; void add_lc_ineq() { m_lc_add = true; m_lc_add_ineq = true; } void add_lc_diseq() { if (!m_lc_add) { m_lc_add = true; m_lc_add_ineq = false; } } }; void simplify(literal l, eq_info & info, var max, scoped_literal & new_lit) { bool_var b = l.var(); atom * a = m_atoms[b]; SASSERT(a != 0); if (a->is_root_atom()) { new_lit = l; return; } ineq_atom * _a = to_ineq_atom(a); unsigned num_factors = _a->size(); if (num_factors == 1 && _a->p(0) == info.m_eq) { new_lit = l; return; } TRACE("nlsat_simplify_core", tout << "trying to simplify literal\n"; display(tout, l); tout << "\nusing equation\n"; m_pm.display(tout, info.m_eq, m_solver.display_proc()); tout << "\n";); int atom_sign = 1; bool modified_lit = false; polynomial_ref_buffer new_factors(m_pm); sbuffer new_factors_even; polynomial_ref new_factor(m_pm); for (unsigned s = 0; s < num_factors; s++) { poly * f = _a->p(s); bool is_even = _a->is_even(s); if (m_pm.degree(f, info.m_x) < info.m_k) { new_factors.push_back(f); new_factors_even.push_back(is_even); continue; } modified_lit = true; unsigned d; m_pm.pseudo_remainder(f, info.m_eq, info.m_x, d, new_factor); // adjust sign based on sign of lc of eq if (d % 2 == 1 && // d is odd !is_even && // degree of the factor is odd info.m_lc_sign < 0 // lc of the eq is negative ) { atom_sign = -atom_sign; // flipped the sign of the current literal } if (is_const(new_factor)) { int s = sign(new_factor); if (s == 0) { bool atom_val = a->get_kind() == atom::EQ; bool lit_val = l.sign() ? !atom_val : atom_val; new_lit = lit_val ? true_literal : false_literal; if (!info.m_lc_const) { // We have essentially shown the current factor must be zero If the leading coefficient is not zero. // Note that, if the current factor is zero, then the whole polynomial is zero. // The atom is true if it is an equality, and false otherwise. // The sign of the leading coefficient (info.m_lc) of info.m_eq doesn't matter. // However, we have to store the fact it is not zero. info.add_lc_diseq(); } return; } else { // We have shown the current factor is a constant MODULO the sign of the leading coefficient (of the equation used to rewrite the factor). if (!info.m_lc_const) { // If the leading coefficient is not a constant, we must store this information as an extra assumption. if (d % 2 == 0 || // d is even is_even || // rewriting a factor of even degree, sign flip doesn't matter _a->get_kind() == atom::EQ) { // rewriting an equation, sign flip doesn't matter info.add_lc_diseq(); } else { info.add_lc_ineq(); } } if (s == -1 && !is_even) { atom_sign = -atom_sign; } } } else { new_factors.push_back(new_factor); new_factors_even.push_back(is_even); if (!info.m_lc_const) { if (d % 2 == 0 || // d is even is_even || // rewriting a factor of even degree, sign flip doesn't matter _a->get_kind() == atom::EQ) { // rewriting an equation, sign flip doesn't matter info.add_lc_diseq(); } else { info.add_lc_ineq(); } } } } if (modified_lit) { atom::kind new_k = _a->get_kind(); if (atom_sign < 0) new_k = atom::flip(new_k); new_lit = m_solver.mk_ineq_literal(new_k, new_factors.size(), new_factors.c_ptr(), new_factors_even.c_ptr()); if (l.sign()) new_lit.neg(); TRACE("nlsat_simplify_core", tout << "simplified literal:\n"; display(tout, new_lit); tout << "\n";); if (max_var(new_lit) < max) { // The conflicting core may have redundant literals. // We should check whether new_lit is true in the current model, and discard it if that is the case lbool val = m_solver.value(new_lit); SASSERT(val != l_undef); if (m_solver.value(new_lit) == l_false) add_literal(new_lit); new_lit = true_literal; return; } new_lit = normalize(new_lit, max); TRACE("nlsat_simplify_core", tout << "simplified literal after normalization:\n"; display(tout, new_lit); tout << "\n";); } else { new_lit = l; } } bool simplify(scoped_literal_vector & C, poly const * eq, var max) { bool modified_core = false; eq_info info; info.m_eq = eq; info.m_x = m_pm.max_var(info.m_eq); info.m_k = m_pm.degree(eq, info.m_x); polynomial_ref lc_eq(m_pm); lc_eq = m_pm.coeff(eq, info.m_x, info.m_k); info.m_lc = lc_eq.get(); info.m_lc_sign = sign(lc_eq); info.m_lc_add = false; info.m_lc_const = m_pm.is_const(lc_eq); SASSERT(info.m_lc != 0); scoped_literal new_lit(m_solver); unsigned sz = C.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { literal l = C[i]; new_lit = null_literal; simplify(l, info, max, new_lit); SASSERT(new_lit != null_literal); if (l == new_lit) { C.set(j, l); j++; continue; } modified_core = true; if (new_lit == true_literal) continue; if (new_lit == false_literal) { // false literal was created. The assumptions added are sufficient for implying the conflict. j = 0; // force core to be reset break; } C.set(j, new_lit); j++; } C.shrink(j); if (info.m_lc_add) { if (info.m_lc_add_ineq) add_assumption(info.m_lc_sign < 0 ? atom::LT : atom::GT, info.m_lc); else add_assumption(atom::EQ, info.m_lc, true); } return modified_core; } /** \brief (try to) Select an equation from C. Returns 0 if C does not contain any equality. This method selects the equation of minimal degree in max. */ poly * select_eq(scoped_literal_vector & C, var max) { poly * r = 0; unsigned min_d = UINT_MAX; unsigned sz = C.size(); for (unsigned i = 0; i < sz; i++) { literal l = C[i]; if (l.sign()) continue; bool_var b = l.var(); atom * a = m_atoms[b]; SASSERT(a != 0); if (a->get_kind() != atom::EQ) continue; ineq_atom * _a = to_ineq_atom(a); if (_a->size() > 1) continue; if (_a->is_even(0)) continue; unsigned d = m_pm.degree(_a->p(0), max); SASSERT(d > 0); if (d < min_d) { r = _a->p(0); min_d = d; if (min_d == 1) break; } } return r; } /** \brief Select an equation eq s.t. max_var(eq) < max, and it can be used to rewrite a literal in C. Return 0, if such equation was not found. */ var_vector m_select_tmp; ineq_atom * select_lower_stage_eq(scoped_literal_vector & C, var max) { var_vector & xs = m_select_tmp; unsigned sz = C.size(); for (unsigned i = 0; i < sz; i++) { literal l = C[i]; bool_var b = l.var(); atom * a = m_atoms[b]; if (a->is_root_atom()) continue; // we don't rewrite root atoms ineq_atom * _a = to_ineq_atom(a); unsigned num_factors = _a->size(); for (unsigned j = 0; j < num_factors; j++) { poly * p = _a->p(j); xs.reset(); m_pm.vars(p, xs); unsigned xs_sz = xs.size(); for (unsigned k = 0; k < xs_sz; k++) { var y = xs[k]; if (y >= max) continue; atom * eq = m_x2eq[y]; if (eq == 0) continue; SASSERT(eq->is_ineq_atom()); SASSERT(to_ineq_atom(eq)->size() == 1); SASSERT(!to_ineq_atom(eq)->is_even(0)); poly * eq_p = to_ineq_atom(eq)->p(0); SASSERT(m_pm.degree(eq_p, y) > 0); // TODO: create a parameter // In the current experiments, using equations with non constant coefficients produces a blowup if (!m_pm.nonzero_const_coeff(eq_p, y, m_pm.degree(eq_p, y))) continue; if (m_pm.degree(p, y) >= m_pm.degree(eq_p, y)) return to_ineq_atom(eq); } } } return 0; } /** \brief Simplify the core using equalities. */ void simplify(scoped_literal_vector & C, var max) { // Simplify using equations in the core while (!C.empty()) { poly * eq = select_eq(C, max); if (eq == 0) break; TRACE("nlsat_simplify_core", tout << "using equality for simplifying core\n"; m_pm.display(tout, eq, m_solver.display_proc()); tout << "\n";); if (!simplify(C, eq, max)) break; } // Simplify using equations using variables from lower stages. while (!C.empty()) { ineq_atom * eq = select_lower_stage_eq(C, max); if (eq == 0) break; SASSERT(eq->size() == 1); SASSERT(!eq->is_even(0)); poly * eq_p = eq->p(0); VERIFY(simplify(C, eq_p, max)); // add equation as an assumption add_literal(literal(eq->bvar(), true)); } } /** \brief Main procedure. The explain the given unsat core, and store the result in m_result */ void main(unsigned num, literal const * ls) { if (num == 0) return; collect_polys(num, ls, m_ps); var max_x = max_var(m_ps); TRACE("nlsat_explain", tout << "polynomials in the conflict:\n"; display(tout, m_ps); tout << "\n";); elim_vanishing(m_ps); project(m_ps, max_x); } void process2(unsigned num, literal const * ls) { if (m_simplify_cores) { m_core2.reset(); m_core2.append(num, ls); var max = max_var(num, ls); SASSERT(max != null_var); normalize(m_core2, max); TRACE("nlsat_explain", tout << "core after normalization\n"; display(tout, m_core2);); simplify(m_core2, max); TRACE("nlsat_explain", tout << "core after simplify\n"; display(tout, m_core2);); main(m_core2.size(), m_core2.c_ptr()); m_core2.reset(); } else { main(num, ls); } } // Auxiliary method for core minimization. literal_vector m_min_newtodo; bool minimize_core(literal_vector & todo, literal_vector & core) { SASSERT(!todo.empty()); literal_vector & new_todo = m_min_newtodo; new_todo.reset(); interval_set_manager & ism = m_evaluator.ism(); interval_set_ref r(ism); // Copy the union of the infeasible intervals of core into r. unsigned sz = core.size(); for (unsigned i = 0; i < sz; i++) { literal l = core[i]; atom * a = m_atoms[l.var()]; SASSERT(a != 0); interval_set_ref inf = m_evaluator.infeasible_intervals(a, l.sign()); r = ism.mk_union(inf, r); if (ism.is_full(r)) { // Done return false; } } TRACE("nlsat_mininize", tout << "interval set after adding partial core:\n" << r << "\n";); if (todo.size() == 1) { // Done core.push_back(todo[0]); return false; } // Copy the union of the infeasible intervals of todo into r until r becomes full. sz = todo.size(); for (unsigned i = 0; i < sz; i++) { literal l = todo[i]; atom * a = m_atoms[l.var()]; SASSERT(a != 0); interval_set_ref inf = m_evaluator.infeasible_intervals(a, l.sign()); r = ism.mk_union(inf, r); if (ism.is_full(r)) { // literal l must be in the core core.push_back(l); new_todo.swap(todo); return true; } else { new_todo.push_back(l); } } UNREACHABLE(); return true; } literal_vector m_min_todo; literal_vector m_min_core; void minimize(unsigned num, literal const * ls, scoped_literal_vector & r) { literal_vector & todo = m_min_todo; literal_vector & core = m_min_core; todo.reset(); core.reset(); todo.append(num, ls); while (true) { TRACE("nlsat_mininize", tout << "core minimization:\n"; display(tout, todo); tout << "\nCORE:\n"; display(tout, core);); if (!minimize_core(todo, core)) break; std::reverse(todo.begin(), todo.end()); TRACE("nlsat_mininize", tout << "core minimization:\n"; display(tout, todo); tout << "\nCORE:\n"; display(tout, core);); if (!minimize_core(todo, core)) break; } TRACE("nlsat_mininize", tout << "core:\n"; display(tout, core);); r.append(core.size(), core.c_ptr()); } void process(unsigned num, literal const * ls) { if (m_minimize_cores && num > 1) { m_core1.reset(); minimize(num, ls, m_core1); process2(m_core1.size(), m_core1.c_ptr()); m_core1.reset(); } else { process2(num, ls); } } void operator()(unsigned num, literal const * ls, scoped_literal_vector & result) { SASSERT(check_already_added()); SASSERT(num > 0); TRACE("nlsat_explain", tout << "[explain] set of literals is infeasible in the current interpretation\n"; display(tout, num, ls);); // exit(0); m_result = &result; process(num, ls); reset_already_added(); m_result = 0; TRACE("nlsat_explain", tout << "[explain] result\n"; display(tout, result);); CASSERT("nlsat", check_already_added()); } }; explain::explain(solver & s, assignment const & x2v, polynomial::cache & u, atom_vector const & atoms, atom_vector const & x2eq, evaluator & ev) { m_imp = alloc(imp, s, x2v, u, atoms, x2eq, ev); } explain::~explain() { dealloc(m_imp); } void explain::reset() { m_imp->m_core1.reset(); m_imp->m_core2.reset(); } void explain::set_simplify_cores(bool f) { m_imp->m_simplify_cores = f; } void explain::set_full_dimensional(bool f) { m_imp->m_full_dimensional = f; } void explain::set_minimize_cores(bool f) { 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); } }; #ifdef Z3DEBUG void pp(nlsat::explain::imp & ex, unsigned num, nlsat::literal const * ls) { ex.display(std::cout, num, ls); } void pp(nlsat::explain::imp & ex, nlsat::scoped_literal_vector & ls) { ex.display(std::cout, ls); } void pp(nlsat::explain::imp & ex, polynomial_ref const & p) { ex.display(std::cout, p); std::cout << std::endl; } void pp(nlsat::explain::imp & ex, polynomial::polynomial * p) { polynomial_ref _p(p, ex.m_pm); ex.display(std::cout, _p); std::cout << std::endl; } void pp(nlsat::explain::imp & ex, polynomial_ref_vector const & ps) { ex.display(std::cout, ps); } void pp_var(nlsat::explain::imp & ex, nlsat::var x) { ex.display(std::cout, x); std::cout << std::endl; } void pp_lit(nlsat::explain::imp & ex, nlsat::literal l) { ex.display(std::cout, l); std::cout << std::endl; } #endif z3-4.4.0/src/nlsat/nlsat_explain.h0000644000175000017500000000341012540347414016565 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_explain.h Abstract: Functor that implements the "explain" procedure defined in Dejan and Leo's paper. Author: Leonardo de Moura (leonardo) 2012-01-13. Revision History: --*/ #ifndef _NLSAT_EXPLAIN_H_ #define _NLSAT_EXPLAIN_H_ #include"nlsat_solver.h" #include"nlsat_scoped_literal_vector.h" #include"polynomial_cache.h" namespace nlsat { class evaluator; class explain { public: struct imp; private: imp * m_imp; public: explain(solver & s, assignment const & x2v, polynomial::cache & u, atom_vector const & atoms, atom_vector const & x2eq, evaluator & ev); ~explain(); void reset(); 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. - n > 0 - all of them are arithmetic literals. - all of them have the same maximal variable. - (ls[0] and ... and ls[n-1]) is infeasible in the current interpretation. Let x be the maximal variable in {ls[0], ..., ls[n-1]}. Remark: the current interpretation assigns all variables in ls[0], ..., ls[n-1] but x. This procedure stores in result a set of literals: s_1, ..., s_m s.t. - (s_1 or ... or s_m or ~ls[0] or ... or ~ls[n-1]) is a valid clause - s_1, ..., s_m do not contain variable x. - s_1, ..., s_m are false in the current interpretation */ void operator()(unsigned n, literal const * ls, scoped_literal_vector & result); }; }; #endif z3-4.4.0/src/nlsat/nlsat_params.pyg0000644000175000017500000000201412540347414016757 0ustar michaelmichael def_module_params('nlsat', description='nonlinear solver', export=True, params=(max_memory_param(), ('lazy', UINT, 0, "how lazy the solver is."), ('reorder', BOOL, True, "reorder variables."), ('simplify_conflicts', BOOL, True, "simplify conflicts using equalities before resolving them in nlsat solver."), ('minimize_conflicts', BOOL, False, "minimize conflicts"), ('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."), ('factor', BOOL, True, "factor polynomials produced during conflict resolution.") )) z3-4.4.0/src/nlsat/nlsat_justification.h0000644000175000017500000000634612540347414020013 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_justification.h Abstract: An explanation for a (Boolean) assignment in the nlsat procedure Author: Leonardo de Moura (leonardo) 2012-01-10. Revision History: --*/ #ifndef _NLSAT_JUSTIFICATION_H_ #define _NLSAT_JUSTIFICATION_H_ #include"nlsat_types.h" #include"tptr.h" namespace nlsat { // There are two kinds of justifications in nlsat: // // - clause // // - lazy_justification: it is a set of arithmetic literals s.t. // the maximal variable in each literal is the same. // The set is inconsistent in the current model. // Thus, our nonlinear procedure may be applied to it // to produce a clause. // class lazy_justification { unsigned m_num_literals; literal m_literals[0]; public: static unsigned get_obj_size(unsigned num) { return sizeof(lazy_justification) + sizeof(literal)*num; } lazy_justification(unsigned num, literal const * lits): m_num_literals(num) { memcpy(m_literals, lits, sizeof(literal)*num); } unsigned size() const { return m_num_literals; } literal operator[](unsigned i) const { SASSERT(i < size()); return m_literals[i]; } literal const * lits() const { return m_literals; } }; class justification { void * m_data; public: enum kind { NULL_JST = 0, DECISION, CLAUSE, LAZY }; justification():m_data(TAG(void *, static_cast(0), NULL_JST)) { SASSERT(is_null()); } justification(bool):m_data(TAG(void *, static_cast(0), DECISION)) { SASSERT(is_decision()); } justification(clause * c):m_data(TAG(void *, c, CLAUSE)) { SASSERT(is_clause()); } justification(lazy_justification * j):m_data(TAG(void *, j, LAZY)) { SASSERT(is_lazy()); } kind get_kind() const { return static_cast(GET_TAG(m_data)); } bool is_null() const { return get_kind() == NULL_JST; } bool is_decision() const { return get_kind() == DECISION; } bool is_clause() const { return get_kind() == CLAUSE; } bool is_lazy() const { return get_kind() == LAZY; } clause * get_clause() const { return UNTAG(clause*, m_data); } lazy_justification * get_lazy() const { return UNTAG(lazy_justification*, m_data); } bool operator==(justification other) const { return m_data == other.m_data; } bool operator!=(justification other) const { return m_data != other.m_data; } }; const justification null_justification; const justification decided_justification(true); inline justification mk_clause_jst(clause const * c) { return justification(const_cast(c)); } inline justification mk_lazy_jst(small_object_allocator & a, unsigned num, literal const * lits) { void * mem = a.allocate(lazy_justification::get_obj_size(num)); return justification(new (mem) lazy_justification(num, lits)); } inline void del_jst(small_object_allocator & a, justification jst) { if (jst.is_lazy()) { lazy_justification * ptr = jst.get_lazy(); unsigned obj_sz = lazy_justification::get_obj_size(ptr->size()); a.deallocate(obj_sz, ptr); } } }; #endif z3-4.4.0/src/nlsat/nlsat_scoped_literal_vector.h0000644000175000017500000000462012540347414021504 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_scoped_literal_vector.h Abstract: Scoped vector for nlsat literals. Just to be "cancel" safe. Author: Leonardo de Moura (leonardo) 2012-01-13. Revision History: --*/ #ifndef _NLSAT_SCOPED_LITERAL_VECTOR_H_ #define _NLSAT_SCOPED_LITERAL_VECTOR_H_ #include"nlsat_solver.h" namespace nlsat { class scoped_literal_vector { solver & m_solver; literal_vector m_lits; public: scoped_literal_vector(solver & s):m_solver(s) {} ~scoped_literal_vector() { reset(); } unsigned size() const { return m_lits.size(); } bool empty() const { return m_lits.empty(); } literal operator[](unsigned i) const { return m_lits[i]; } void reset() { unsigned sz = m_lits.size(); for (unsigned i = 0; i < sz; i++) { m_solver.dec_ref(m_lits[i]); } m_lits.reset(); } void push_back(literal l) { m_solver.inc_ref(l); m_lits.push_back(l); } void set(unsigned i, literal l) { m_solver.inc_ref(l); m_solver.dec_ref(m_lits[i]); m_lits[i] = l; } literal const * c_ptr() const { return m_lits.c_ptr(); } void shrink(unsigned new_sz) { SASSERT(new_sz <= m_lits.size()); unsigned sz = m_lits.size(); if (new_sz == sz) return; for (unsigned i = new_sz; i < sz; i++) { m_solver.dec_ref(m_lits[i]); } m_lits.shrink(new_sz); } void append(unsigned sz, literal const * ls) { for (unsigned i = 0; i < sz; i++) push_back(ls[i]); } }; class scoped_literal { solver & m_solver; literal m_lit; public: scoped_literal(solver & s):m_solver(s), m_lit(null_literal) {} ~scoped_literal() { m_solver.dec_ref(m_lit); } scoped_literal & operator=(literal l) { m_solver.inc_ref(l); m_solver.dec_ref(m_lit); m_lit = l; return *this; } scoped_literal & operator=(scoped_literal const & l) { return operator=(l.m_lit); } operator literal&() { return m_lit; } operator literal const &() const { return m_lit; } void neg() { m_lit.neg(); } }; }; #endif z3-4.4.0/src/nlsat/nlsat_clause.cpp0000644000175000017500000000174712540347414016747 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_clause.cpp Abstract: Clauses used in the Nonlinear arithmetic satisfiability procedure. Author: Leonardo de Moura (leonardo) 2012-01-02. Revision History: --*/ #include"nlsat_clause.h" namespace nlsat { clause::clause(unsigned id, unsigned sz, literal const * lits, bool learned, assumption_set as): m_id(id), m_size(sz), m_capacity(sz), m_learned(learned), m_activity(0), m_assumptions(as) { for (unsigned i = 0; i < sz; i++) { m_lits[i] = lits[i]; } } bool clause::contains(literal l) const { for (unsigned i = 0; i < m_size; i++) if (m_lits[i] == l) return true; return false; } bool clause::contains(bool_var v) const { for (unsigned i = 0; i < m_size; i++) if (m_lits[i].var() == v) return true; return false; } }; z3-4.4.0/src/nlsat/nlsat_solver.cpp0000644000175000017500000030333212540347414017000 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_solver.cpp Abstract: Nonlinear arithmetic satisfiability procedure. The procedure is complete for nonlinear real arithmetic, but it also has limited support for integers. Author: Leonardo de Moura (leonardo) 2012-01-02. Revision History: --*/ #include"nlsat_solver.h" #include"nlsat_clause.h" #include"nlsat_assignment.h" #include"nlsat_justification.h" #include"nlsat_evaluator.h" #include"nlsat_explain.h" #include"algebraic_numbers.h" #include"z3_exception.h" #include"chashtable.h" #include"id_gen.h" #include"dependency.h" #include"polynomial_cache.h" #include"permutation.h" #include"nlsat_params.hpp" #define NLSAT_EXTRA_VERBOSE #ifdef NLSAT_EXTRA_VERBOSE #define NLSAT_VERBOSE(CODE) IF_VERBOSE(10, CODE) #else #define NLSAT_VERBOSE(CODE) ((void)0) #endif namespace nlsat { typedef chashtable ineq_atom_table; typedef chashtable root_atom_table; // for apply_permutation procedure void swap(clause * & c1, clause * & c2) { std::swap(c1, c2); } struct solver::imp { struct dconfig { typedef imp value_manager; typedef small_object_allocator allocator; typedef void * value; static const bool ref_count = false; }; typedef dependency_manager assumption_manager; typedef assumption_manager::dependency * _assumption_set; typedef obj_ref assumption_set_ref; typedef polynomial::cache cache; typedef ptr_vector interval_set_vector; solver & m_solver; small_object_allocator m_allocator; unsynch_mpq_manager m_qm; pmanager m_pm; cache m_cache; anum_manager m_am; assumption_manager m_asm; assignment m_assignment; // partial interpretation evaluator m_evaluator; interval_set_manager & m_ism; ineq_atom_table m_ineq_atoms; root_atom_table m_root_atoms; id_gen m_cid_gen; clause_vector m_clauses; // set of clauses clause_vector m_learned; // set of learned clauses unsigned m_num_bool_vars; atom_vector m_atoms; // bool_var -> atom svector m_bvalues; // boolean assigment unsigned_vector m_levels; // bool_var -> level svector m_justifications; vector m_bwatches; // bool_var (that are not attached to atoms) -> clauses where it is maximal svector m_dead; // mark dead boolean variables id_gen m_bid_gen; svector m_is_int; // m_is_int[x] is true if variable is integer vector m_watches; // var -> clauses where variable is maximal interval_set_vector m_infeasible; // var -> to a set of interval where the variable cannot be assigned to. atom_vector m_var2eq; // var -> to asserted equality var_vector m_perm; // var -> var permutation of the variables var_vector m_inv_perm; // m_perm: internal -> external // m_inv_perm: external -> internal struct perm_display_var_proc : public display_var_proc { var_vector & m_perm; display_var_proc m_default_display_var; display_var_proc const * m_proc; // display external var ids perm_display_var_proc(var_vector & perm): m_perm(perm), m_proc(0) { } virtual void operator()(std::ostream & out, var x) const { if (m_proc == 0) m_default_display_var(out, x); else (*m_proc)(out, m_perm[x]); } }; perm_display_var_proc m_display_var; explain m_explain; bool_var m_bk; // current Boolean variable we are processing var m_xk; // current arith variable we are processing unsigned m_scope_lvl; struct trail { enum kind { BVAR_ASSIGNMENT, INFEASIBLE_UPDT, NEW_LEVEL, NEW_STAGE, UPDT_EQ }; kind m_kind; union { bool_var m_b; interval_set * m_old_set; atom * m_old_eq; }; trail(bool_var b):m_kind(BVAR_ASSIGNMENT), m_b(b) {} trail(interval_set * old_set):m_kind(INFEASIBLE_UPDT), m_old_set(old_set) {} trail(bool stage):m_kind(stage ? NEW_STAGE : NEW_LEVEL) {} trail(atom * a):m_kind(UPDT_EQ), m_old_eq(a) {} }; svector m_trail; anum m_zero; bool m_cancel; // configuration unsigned long long m_max_memory; unsigned m_lazy; // how lazy the solver is: 0 - satisfy all learned clauses, 1 - process only unit and empty learned clauses, 2 - use only conflict clauses for resolving conflicts bool m_simplify_cores; bool m_reorder; bool m_randomize; bool m_random_order; unsigned m_random_seed; unsigned m_max_conflicts; // statistics unsigned m_conflicts; unsigned m_propagations; unsigned m_decisions; unsigned m_stages; unsigned m_irrational_assignments; // number of irrational witnesses imp(solver & s, params_ref const & p): m_solver(s), m_allocator("nlsat"), m_pm(m_qm, &m_allocator), m_cache(m_pm), m_am(m_qm, p, &m_allocator), m_asm(*this, m_allocator), m_assignment(m_am), m_evaluator(m_assignment, m_pm, m_allocator), m_ism(m_evaluator.ism()), m_num_bool_vars(0), m_display_var(m_perm), m_explain(s, m_assignment, m_cache, m_atoms, m_var2eq, m_evaluator), m_scope_lvl(0), m_lemma(s), m_lazy_clause(s), m_lemma_assumptions(m_asm) { updt_params(p); reset_statistics(); m_cancel = false; mk_true_bvar(); } ~imp() { m_explain.reset(); m_lemma.reset(); m_lazy_clause.reset(); undo_until_size(0); del_clauses(); del_unref_atoms(); } void mk_true_bvar() { bool_var b = mk_bool_var(); SASSERT(b == true_bool_var); literal true_lit(b, false); mk_clause(1, &true_lit, false, 0); } void updt_params(params_ref const & _p) { nlsat_params p(_p); m_max_memory = p.max_memory(); m_lazy = p.lazy(); m_simplify_cores = p.simplify_conflicts(); bool min_cores = p.minimize_conflicts(); m_reorder = p.reorder(); m_randomize = p.randomize(); m_max_conflicts = p.max_conflicts(); m_random_order = p.shuffle_vars(); m_random_seed = p.seed(); 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); } void set_cancel(bool f) { m_pm.set_cancel(f); m_am.set_cancel(f); m_cancel = f; } void checkpoint() { if (m_cancel) throw solver_exception(Z3_CANCELED_MSG); if (memory::get_allocation_size() > m_max_memory) throw solver_exception(Z3_MAX_MEMORY_MSG); } // ----------------------- // // Basic // // ----------------------- unsigned num_bool_vars() const { return m_num_bool_vars; } unsigned num_vars() const { return m_is_int.size(); } bool is_int(var x) const { return m_is_int[x]; } void inc_ref(assumption) {} void dec_ref(assumption) {} void inc_ref(_assumption_set a) { if (a != 0) m_asm.inc_ref(a); } void dec_ref(_assumption_set a) { if (a != 0) m_asm.dec_ref(a); } void inc_ref(bool_var b) { if (b == null_bool_var) return; if (m_atoms[b] == 0) return; m_atoms[b]->inc_ref(); } void inc_ref(literal l) { inc_ref(l.var()); } void dec_ref(bool_var b) { if (b == null_bool_var) return; atom * a = m_atoms[b]; if (a == 0) return; SASSERT(a->ref_count() > 0); a->dec_ref(); if (a->ref_count() == 0) del(a); } void dec_ref(literal l) { dec_ref(l.var()); } bool is_arith_atom(bool_var b) const { return m_atoms[b] != 0; } bool is_arith_literal(literal l) const { return is_arith_atom(l.var()); } var max_var(poly const * p) const { return m_pm.max_var(p); } var max_var(bool_var b) const { if (!is_arith_atom(b)) return null_var; else return m_atoms[b]->max_var(); } var max_var(literal l) const { return max_var(l.var()); } /** \brief Return the maximum variable occurring in cls. */ var max_var(unsigned sz, literal const * cls) const { var x = null_var; for (unsigned i = 0; i < sz; i++) { literal l = cls[i]; if (is_arith_literal(l)) { var y = max_var(l); if (x == null_var || y > x) x = y; } } return x; } var max_var(clause const & cls) const { return max_var(cls.size(), cls.c_ptr()); } /** \brief Return the maximum Boolean variable occurring in cls. */ bool_var max_bvar(clause const & cls) const { bool_var b = null_bool_var; unsigned sz = cls.size(); for (unsigned i = 0; i < sz; i++) { literal l = cls[i]; if (b == null_bool_var || l.var() > b) b = l.var(); } return b; } /** \brief Return the degree of the maximal variable of the given atom */ unsigned degree(atom const * a) const { if (a->is_ineq_atom()) { unsigned max = 0; unsigned sz = to_ineq_atom(a)->size(); var x = a->max_var(); for (unsigned i = 0; i < sz; i++) { unsigned d = m_pm.degree(to_ineq_atom(a)->p(i), x); if (d > max) max = d; } return max; } else { return m_pm.degree(to_root_atom(a)->p(), a->max_var()); } } /** \brief Return the degree of the maximal variable in c */ unsigned degree(clause const & c) const { var x = max_var(c); if (x == null_var) return 0; unsigned max = 0; unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { literal l = c[i]; atom const * a = m_atoms[l.var()]; if (a == 0) continue; unsigned d = degree(a); if (d > max) max = d; } return max; } // ----------------------- // // Variable, Atoms, Clauses & Assumption creation // // ----------------------- bool_var mk_bool_var_core() { bool_var b = m_bid_gen.mk(); m_num_bool_vars++; m_atoms .setx(b, 0, 0); m_bvalues .setx(b, l_undef, l_undef); m_levels .setx(b, UINT_MAX, UINT_MAX); m_justifications.setx(b, null_justification, null_justification); m_bwatches .setx(b, clause_vector(), clause_vector()); m_dead .setx(b, false, true); return b; } bool_var mk_bool_var() { return mk_bool_var_core(); } var mk_var(bool is_int) { var x = m_pm.mk_var(); SASSERT(x == num_vars()); m_is_int. push_back(is_int); m_watches. push_back(clause_vector()); m_infeasible.push_back(0); m_var2eq. push_back(0); m_perm. push_back(x); m_inv_perm. push_back(x); SASSERT(m_is_int.size() == m_watches.size()); SASSERT(m_is_int.size() == m_infeasible.size()); SASSERT(m_is_int.size() == m_var2eq.size()); SASSERT(m_is_int.size() == m_perm.size()); SASSERT(m_is_int.size() == m_inv_perm.size()); return x; } void deallocate(ineq_atom * a) { unsigned obj_sz = ineq_atom::get_obj_size(a->size()); a->~ineq_atom(); m_allocator.deallocate(obj_sz, a); } void deallocate(root_atom * a) { a->~root_atom(); m_allocator.deallocate(sizeof(root_atom), a); } void del(bool_var b) { SASSERT(m_bwatches[b].empty()); SASSERT(m_bvalues[b] == l_undef); m_num_bool_vars--; m_dead[b] = true; m_atoms[b] = 0; m_bid_gen.recycle(b); } void del(ineq_atom * a) { SASSERT(a->ref_count() == 0); m_ineq_atoms.erase(a); del(a->bvar()); unsigned sz = a->size(); for (unsigned i = 0; i < sz; i++) m_pm.dec_ref(a->p(i)); deallocate(a); } void del(root_atom * a) { SASSERT(a->ref_count() == 0); m_root_atoms.erase(a); del(a->bvar()); m_pm.dec_ref(a->p()); deallocate(a); } void del(atom * a) { if (a == 0) return ; if (a->is_ineq_atom()) del(to_ineq_atom(a)); else del(to_root_atom(a)); } // Delete atoms with ref_count == 0 void del_unref_atoms() { unsigned sz = m_atoms.size(); for (unsigned i = 0; i < sz; i++) { del(m_atoms[i]); } } bool_var mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { SASSERT(sz >= 1); SASSERT(k == atom::LT || k == atom::GT || k == atom::EQ); int sign = 1; polynomial_ref p(m_pm); ptr_buffer uniq_ps; var max = null_var; for (unsigned i = 0; i < sz; i++) { p = m_pm.flip_sign_if_lm_neg(ps[i]); if (p.get() != ps[i]) sign = -sign; var curr_max = max_var(p.get()); if (curr_max > max || max == null_var) max = curr_max; uniq_ps.push_back(m_cache.mk_unique(p)); TRACE("nlsat_table_bug", tout << "p: " << p << ", uniq: " << uniq_ps.back() << "\n";); } void * mem = m_allocator.allocate(ineq_atom::get_obj_size(sz)); if (sign < 0) k = atom::flip(k); ineq_atom * new_atom = new (mem) ineq_atom(k, sz, uniq_ps.c_ptr(), is_even, max); TRACE("nlsat_table_bug", ineq_atom::hash_proc h; tout << "mk_ineq_atom hash: " << h(new_atom) << "\n"; display(tout, *new_atom, m_display_var); tout << "\n";); ineq_atom * old_atom = m_ineq_atoms.insert_if_not_there(new_atom); SASSERT(old_atom->max_var() == max); if (old_atom != new_atom) { deallocate(new_atom); return old_atom->bvar(); } bool_var b = mk_bool_var_core(); m_atoms[b] = new_atom; new_atom->m_bool_var = b; for (unsigned i = 0; i < sz; i++) { m_pm.inc_ref(new_atom->p(i)); } return b; } literal mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { SASSERT(k == atom::LT || k == atom::GT || k == atom::EQ); if (sz == 0) { switch (k) { case atom::LT: return false_literal; // 1 < 0 case atom::EQ: return false_literal; // 1 == 0 case atom::GT: return true_literal; // 1 > 0 default: UNREACHABLE(); return null_literal; } } else { return literal(mk_ineq_atom(k, sz, ps, is_even), false); } } bool_var mk_root_atom(atom::kind k, var x, unsigned i, poly * p) { SASSERT(i > 0); SASSERT(x >= max_var(p)); SASSERT(k == atom::ROOT_LT || k == atom::ROOT_GT || k == atom::ROOT_EQ || k == atom::ROOT_LE || k == atom::ROOT_GE); polynomial_ref p1(m_pm); p1 = m_pm.flip_sign_if_lm_neg(p); // flipping the sign of the polynomial will not change its roots. poly * uniq_p = m_cache.mk_unique(p1); void * mem = m_allocator.allocate(sizeof(root_atom)); root_atom * new_atom = new (mem) root_atom(k, x, i, uniq_p); root_atom * old_atom = m_root_atoms.insert_if_not_there(new_atom); SASSERT(old_atom->max_var() == x); if (old_atom != new_atom) { deallocate(new_atom); return old_atom->bvar(); } bool_var b = mk_bool_var_core(); m_atoms[b] = new_atom; new_atom->m_bool_var = b; m_pm.inc_ref(new_atom->p()); return b; } void attach_clause(clause & cls) { var x = max_var(cls); if (x != null_var) { m_watches[x].push_back(&cls); } else { bool_var b = max_bvar(cls); m_bwatches[b].push_back(&cls); } } void deattach_clause(clause & cls) { var x = max_var(cls); if (x != null_var) { m_watches[x].erase(&cls); } else { bool_var b = max_bvar(cls); m_bwatches[b].erase(&cls); } } void deallocate(clause * cls) { size_t obj_sz = clause::get_obj_size(cls->size()); cls->~clause(); m_allocator.deallocate(obj_sz, cls); } void del_clause(clause * cls) { deattach_clause(*cls); m_cid_gen.recycle(cls->id()); unsigned sz = cls->size(); for (unsigned i = 0; i < sz; i++) dec_ref((*cls)[i]); _assumption_set a = static_cast<_assumption_set>(cls->assumptions()); dec_ref(a); deallocate(cls); } void del_clauses(ptr_vector & cs) { unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) del_clause(cs[i]); } void del_clauses() { del_clauses(m_clauses); del_clauses(m_learned); } // We use a simple heuristic to sort literals // - bool literals < arith literals // - sort literals based on max_var // - sort literal with the same max_var using degree // break ties using the fact that ineqs are usually cheaper to process than eqs. struct lit_lt { imp & m; lit_lt(imp & _m):m(_m) {} bool operator()(literal l1, literal l2) const { atom * a1 = m.m_atoms[l1.var()]; atom * a2 = m.m_atoms[l2.var()]; if (a1 == 0 && a2 == 0) return l1.index() < l2.index(); if (a1 == 0) return true; if (a2 == 0) return false; var x1 = a1->max_var(); var x2 = a2->max_var(); if (x1 < x2) return true; if (x1 > x2) return false; SASSERT(x1 == x2); unsigned d1 = m.degree(a1); unsigned d2 = m.degree(a2); if (d1 < d2) return true; if (d1 > d2) return false; if (!a1->is_eq() && a2->is_eq()) return true; if (a1->is_eq() && !a2->is_eq()) return false; return l1.index() < l2.index(); } }; clause * mk_clause(unsigned num_lits, literal const * lits, bool learned, _assumption_set a) { SASSERT(num_lits > 0); unsigned cid = m_cid_gen.mk(); void * mem = m_allocator.allocate(clause::get_obj_size(num_lits)); clause * cls = new (mem) clause(cid, num_lits, lits, learned, a); for (unsigned i = 0; i < num_lits; i++) inc_ref(lits[i]); inc_ref(a); TRACE("nlsat_sort", tout << "mk_clause:\n"; display(tout, *cls); tout << "\n";); std::sort(cls->begin(), cls->end(), lit_lt(*this)); TRACE("nlsat_sort", tout << "after sort:\n"; display(tout, *cls); tout << "\n";); if (learned) m_learned.push_back(cls); else m_clauses.push_back(cls); attach_clause(*cls); return cls; } void mk_clause(unsigned num_lits, literal const * lits, assumption a) { SASSERT(num_lits > 0); _assumption_set as = 0; if (a != 0) as = m_asm.mk_leaf(a); mk_clause(num_lits, lits, false, as); } // ----------------------- // // Search // // ----------------------- void save_assign_trail(bool_var b) { m_trail.push_back(trail(b)); } void save_set_updt_trail(interval_set * old_set) { m_trail.push_back(trail(old_set)); } void save_updt_eq_trail(atom * old_eq) { m_trail.push_back(trail(old_eq)); } void save_new_stage_trail() { m_trail.push_back(trail(true)); } void save_new_level_trail() { m_trail.push_back(trail(false)); } void undo_bvar_assignment(bool_var b) { m_bvalues[b] = l_undef; m_levels[b] = UINT_MAX; del_jst(m_allocator, m_justifications[b]); m_justifications[b] = null_justification; if (m_atoms[b] == 0 && b < m_bk) m_bk = b; } void undo_set_updt(interval_set * old_set) { SASSERT(m_xk != null_var); var x = m_xk; m_ism.dec_ref(m_infeasible[x]); m_infeasible[x] = old_set; } void undo_new_stage() { SASSERT(m_xk != null_var); if (m_xk == 0) { m_xk = null_var; } else { m_xk--; m_assignment.reset(m_xk); } } void undo_new_level() { SASSERT(m_scope_lvl > 0); m_scope_lvl--; m_evaluator.pop(1); } void undo_updt_eq(atom * a) { SASSERT(m_xk != null_var); m_var2eq[m_xk] = a; } template void undo_until(Predicate const & pred) { while (pred()) { trail & t = m_trail.back(); switch (t.m_kind) { case trail::BVAR_ASSIGNMENT: undo_bvar_assignment(t.m_b); break; case trail::INFEASIBLE_UPDT: undo_set_updt(t.m_old_set); break; case trail::NEW_STAGE: undo_new_stage(); break; case trail::NEW_LEVEL: undo_new_level(); break; case trail::UPDT_EQ: undo_updt_eq(t.m_old_eq); break; default: break; } m_trail.pop_back(); } } struct size_pred { svector & m_trail; unsigned m_old_size; size_pred(svector & trail, unsigned old_size):m_trail(trail), m_old_size(old_size) {} bool operator()() const { return m_trail.size() > m_old_size; } }; // Keep undoing until trail has the given size void undo_until_size(unsigned old_size) { SASSERT(m_trail.size() >= old_size); undo_until(size_pred(m_trail, old_size)); } struct stage_pred { var const & m_xk; var m_target; stage_pred(var const & xk, var target):m_xk(xk), m_target(target) {} bool operator()() const { return m_xk != m_target; } }; // Keep undoing until stage is new_xk void undo_until_stage(var new_xk) { undo_until(stage_pred(m_xk, new_xk)); } struct level_pred { unsigned const & m_scope_lvl; unsigned m_new_lvl; level_pred(unsigned const & scope_lvl, unsigned new_lvl):m_scope_lvl(scope_lvl), m_new_lvl(new_lvl) {} bool operator()() const { return m_scope_lvl > m_new_lvl; } }; // Keep undoing until level is new_lvl void undo_until_level(unsigned new_lvl) { undo_until(level_pred(m_scope_lvl, new_lvl)); } struct unassigned_pred { bool_var m_b; svector const & m_bvalues; unassigned_pred(svector const & bvalues, bool_var b): m_b(b), m_bvalues(bvalues) {} bool operator()() const { return m_bvalues[m_b] != l_undef; } }; // Keep undoing until b is unassigned void undo_until_unassigned(bool_var b) { undo_until(unassigned_pred(m_bvalues, b)); SASSERT(m_bvalues[b] == l_undef); } /** \brief Create a new scope level */ void new_level() { m_evaluator.push(); m_scope_lvl++; save_new_level_trail(); } /** \brief Return the value of the given literal that was assigned by the search engine. */ lbool assigned_value(literal l) const { bool_var b = l.var(); if (l.sign()) return ~m_bvalues[b]; else return m_bvalues[b]; } /** \brief Assign literal using the given justification */ void assign(literal l, justification j) { TRACE("nlsat", tout << "assigning literal:\n"; display(tout, l); tout << "\njustification kind: " << j.get_kind() << "\n";); SASSERT(assigned_value(l) == l_undef); SASSERT(j != null_justification); SASSERT(!j.is_null()); if (j.is_decision()) m_decisions++; else m_propagations++; bool_var b = l.var(); m_bvalues[b] = to_lbool(!l.sign()); m_levels[b] = m_scope_lvl; m_justifications[b] = j; save_assign_trail(b); updt_eq(b); TRACE("nlsat_assign", tout << "b" << b << " -> " << m_bvalues[b] << " " << m_atoms[b] << "\n";); } /** \brief Create a "case-split" */ void decide(literal l) { new_level(); assign(l, decided_justification); } /** \brief Return the value of a literal as defined in Dejan and Leo's paper. */ lbool value(literal l) { lbool val = assigned_value(l); if (val != l_undef) return val; bool_var b = l.var(); atom * a = m_atoms[b]; if (a == 0) return l_undef; var max = a->max_var(); if (!m_assignment.is_assigned(max)) return l_undef; TRACE("value_bug", tout << "value of: "; display(tout, l); tout << "\n"; tout << "xk: " << m_xk << ", a->max_var(): " << a->max_var() << "\n"; display_assignment(tout); display_bool_assignment(tout);); return to_lbool(m_evaluator.eval(a, l.sign())); } /** \brief Return true if the given clause is already satisfied in the current partial interpretation. */ bool is_satisfied(clause const & cls) const { unsigned sz = cls.size(); for (unsigned i = 0; i < sz; i++) { if (const_cast(this)->value(cls[i]) == l_true) return true; } return false; } /** \brief Return true if the given clause is false in the current partial interpretation. */ bool is_inconsistent(unsigned sz, literal const * cls) { for (unsigned i = 0; i < sz; i++) { if (value(cls[i]) != l_false) { TRACE("is_inconsistent", tout << "literal is not false:\n"; display(tout, cls[i]); tout << "\n";); return false; } } return true; } /** \brief Process a clauses that contains only Boolean literals. */ bool process_boolean_clause(clause const & cls) { SASSERT(m_xk == null_var); unsigned num_undef = 0; unsigned first_undef = UINT_MAX; unsigned sz = cls.size(); for (unsigned i = 0; i < sz; i++) { literal l = cls[i]; SASSERT(m_atoms[l.var()] == 0); SASSERT(value(l) != l_true); if (value(l) == l_false) continue; SASSERT(value(l) == l_undef); num_undef++; if (first_undef == UINT_MAX) first_undef = i; } if (num_undef == 0) return false; SASSERT(first_undef != UINT_MAX); if (num_undef == 1) assign(cls[first_undef], mk_clause_jst(&cls)); // unit clause else decide(cls[first_undef]); return true; } /** \brief assign l to true, because l + (justification of) s is infeasible in RCF in the current interpretation. */ literal_vector core; void R_propagate(literal l, interval_set const * s, bool include_l = true) { m_ism.get_justifications(s, core); if (include_l) core.push_back(~l); assign(l, mk_lazy_jst(m_allocator, core.size(), core.c_ptr())); SASSERT(value(l) == l_true); } /** \brief m_infeasible[m_xk] <- m_infeasible[m_xk] Union s */ void updt_infeasible(interval_set const * s) { SASSERT(m_xk != null_var); interval_set * xk_set = m_infeasible[m_xk]; save_set_updt_trail(xk_set); interval_set_ref new_set(m_ism); TRACE("nlsat_inf_set", tout << "updating infeasible set\n"; m_ism.display(tout, xk_set); tout << "\n"; m_ism.display(tout, s); tout << "\n";); new_set = m_ism.mk_union(s, xk_set); TRACE("nlsat_inf_set", tout << "new infeasible set:\n"; m_ism.display(tout, new_set); tout << "\n";); SASSERT(!m_ism.is_full(new_set)); m_ism.inc_ref(new_set); m_infeasible[m_xk] = new_set; } /** \brief Update m_var2eq mapping. */ void updt_eq(bool_var b) { if (!m_simplify_cores) return; if (m_bvalues[b] != l_true) return; atom * a = m_atoms[b]; if (a == 0 || a->get_kind() != atom::EQ || to_ineq_atom(a)->size() > 1 || to_ineq_atom(a)->is_even(0)) return; var x = m_xk; SASSERT(a->max_var() == x); SASSERT(x != null_var); if (m_var2eq[x] != 0 && degree(m_var2eq[x]) <= degree(a)) return; // we only update m_var2eq if the new equality has smaller degree TRACE("simplify_core", tout << "Saving equality for "; m_display_var(tout, x); tout << " (x" << x << ") "; tout << "scope-lvl: " << scope_lvl() << "\n"; display(tout, literal(b, false)); tout << "\n";); save_updt_eq_trail(m_var2eq[x]); m_var2eq[x] = a; } /** \brief Process a clause that contains nonlinar arithmetic literals If satisfy_learned is true, then learned clauses are satisfied even if m_lazy > 0 */ bool process_arith_clause(clause const & cls, bool satisfy_learned) { if (!satisfy_learned && m_lazy >= 2 && cls.is_learned()) return true; // ignore lemmas in super lazy mode SASSERT(m_xk == max_var(cls)); unsigned num_undef = 0; // number of undefined literals unsigned first_undef = UINT_MAX; // position of the first undefined literal interval_set_ref first_undef_set(m_ism); // infeasible region of the first undefined literal interval_set * xk_set = m_infeasible[m_xk]; // current set of infeasible interval for current variable SASSERT(!m_ism.is_full(xk_set)); unsigned sz = cls.size(); for (unsigned i = 0; i < sz; i++) { checkpoint(); literal l = cls[i]; if (value(l) == l_false) continue; SASSERT(value(l) == l_undef); SASSERT(max_var(l) == m_xk); bool_var b = l.var(); atom * a = m_atoms[b]; SASSERT(a != 0); interval_set_ref curr_set(m_ism); TRACE("nlsat_inf_set", tout << "xk: " << m_xk << ", max_var(l): " << max_var(l) << ", l: "; display(tout, l); tout << "\n";); curr_set = m_evaluator.infeasible_intervals(a, l.sign()); TRACE("nlsat_inf_set", tout << "infeasible set for literal: "; display(tout, l); tout << "\n"; m_ism.display(tout, curr_set); tout << "\n";); if (m_ism.is_empty(curr_set)) { TRACE("nlsat_inf_set", tout << "infeasible set is empty, found literal\n";); R_propagate(l, 0); SASSERT(is_satisfied(cls)); return true; } if (m_ism.is_full(curr_set)) { TRACE("nlsat_inf_set", tout << "infeasible set is R, skip literal\n";); R_propagate(~l, 0); continue; } if (m_ism.subset(curr_set, xk_set)) { TRACE("nlsat_inf_set", tout << "infeasible set is a subset of current set, found literal\n";); R_propagate(l, xk_set); return true; } interval_set_ref tmp(m_ism); tmp = m_ism.mk_union(curr_set, xk_set); if (m_ism.is_full(tmp)) { TRACE("nlsat_inf_set", tout << "infeasible set + current set = R, skip literal\n";); R_propagate(~l, tmp, false); continue; } num_undef++; if (first_undef == UINT_MAX) { first_undef = i; first_undef_set = curr_set; } } TRACE("nlsat_inf_set", tout << "num_undef: " << num_undef << "\n";); if (num_undef == 0) return false; SASSERT(first_undef != UINT_MAX); if (num_undef == 1) { // unit clause assign(cls[first_undef], mk_clause_jst(&cls)); updt_infeasible(first_undef_set); } else if ( satisfy_learned || !cls.is_learned() /* must always satisfy input clauses */ || m_lazy == 0 /* if not in lazy mode, we also satiffy lemmas */) { decide(cls[first_undef]); updt_infeasible(first_undef_set); } else { TRACE("nlsat_lazy", tout << "skipping clause, satisfy_learned: " << satisfy_learned << ", cls.is_learned(): " << cls.is_learned() << ", lazy: " << m_lazy << "\n";); } return true; } /** \brief Try to satisfy the given clause. Return true if succeeded. If satisfy_learned is true, then (arithmetic) learned clauses are satisfied even if m_lazy > 0 */ bool process_clause(clause const & cls, bool satisfy_learned) { TRACE("nlsat", tout << "processing clause:\n"; display(tout, cls); tout << "\n";); if (is_satisfied(cls)) return true; if (m_xk == null_var) return process_boolean_clause(cls); else return process_arith_clause(cls, satisfy_learned); } /** \brief Try to satisfy the given "set" of clauses. Return 0, if the set was satisfied, or the violating clause otherwise */ clause * process_clauses(clause_vector const & cs) { unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { clause * c = cs[i]; if (!process_clause(*c, false)) return c; } return 0; // succeeded } /** \brief Make sure m_bk is the first unassigned pure Boolean variable. Set m_bk == null_bool_var if there is no unassigned pure Boolean variable. */ void peek_next_bool_var() { while (m_bk < m_atoms.size()) { if (!m_dead[m_bk] && m_atoms[m_bk] == 0 && m_bvalues[m_bk] == l_undef) { return; } m_bk++; } m_bk = null_bool_var; } /** \brief Create a new stage. See Dejan and Leo's paper. */ void new_stage() { m_stages++; save_new_stage_trail(); if (m_xk == null_var) m_xk = 0; else m_xk++; } /** \brief Assign m_xk */ void select_witness() { scoped_anum w(m_am); SASSERT(!m_ism.is_full(m_infeasible[m_xk])); m_ism.peek_in_complement(m_infeasible[m_xk], w, m_randomize); TRACE("nlsat", tout << "infeasible intervals: "; m_ism.display(tout, m_infeasible[m_xk]); tout << "\n"; tout << "assigning "; m_display_var(tout, m_xk); tout << "(x" << m_xk << ") -> " << w << "\n";); TRACE("nlsat_root", tout << "value as root object: "; m_am.display_root(tout, w); tout << "\n";); if (!m_am.is_rational(w)) m_irrational_assignments++; m_assignment.set_core(m_xk, w); } /** \brief main procedure */ lbool search() { TRACE("nlsat", tout << "starting search...\n"; display(tout); tout << "\nvar order:\n"; display_vars(tout);); TRACE("nlsat_proof", tout << "ASSERTED\n"; display(tout);); TRACE("nlsat_proof_sk", tout << "ASSERTED\n"; display_abst(tout);); TRACE("nlsat_mathematica", display_mathematica(tout);); m_bk = 0; m_xk = null_var; while (true) { CASSERT("nlsat", check_satisfied()); if (m_xk == null_var) { peek_next_bool_var(); if (m_bk == null_bool_var) new_stage(); // move to arith vars } else { new_stage(); // peek next arith var } TRACE("nlsat_bug", tout << "xk: x" << m_xk << " bk: b" << m_bk << "\n";); if (m_bk == null_bool_var && m_xk >= num_vars()) { TRACE("nlsat", tout << "found model\n"; display_assignment(tout); display_bool_assignment(tout);); return l_true; // all variables were assigned, and all clauses were satisfied. } TRACE("nlsat", tout << "processing variable "; if (m_xk != null_var) m_display_var(tout, m_xk); else tout << "boolean b" << m_bk; tout << "\n";); while (true) { checkpoint(); clause * conflict_clause; if (m_xk == null_var) conflict_clause = process_clauses(m_bwatches[m_bk]); else conflict_clause = process_clauses(m_watches[m_xk]); if (conflict_clause == 0) break; if (!resolve(*conflict_clause)) return l_false; if (m_conflicts >= m_max_conflicts) return l_undef; } if (m_xk == null_var) { if (m_bvalues[m_bk] == l_undef) { decide(literal(m_bk, true)); m_bk++; } } else { select_witness(); } } } lbool check() { TRACE("nlsat_smt2", display_smt2(tout);); TRACE("nlsat_fd", tout << "is_full_dimensional: " << is_full_dimensional() << "\n";); m_xk = null_var; m_explain.set_full_dimensional(is_full_dimensional()); if (m_random_order) { shuffle_vars(); } else if (m_reorder) { heuristic_reorder(); } sort_watched_clauses(); lbool r = search(); CTRACE("nlsat_model", r == l_true, tout << "model before restore order\n"; display_assignment(tout); display_bool_assignment(tout);); if (m_reorder) restore_order(); CTRACE("nlsat_model", r == l_true, tout << "model\n"; display_assignment(tout); display_bool_assignment(tout);); return r; } // ----------------------- // // Conflict Resolution // // ----------------------- svector m_marks; // bool_var -> bool temp mark used during conflict resolution unsigned m_num_marks; scoped_literal_vector m_lemma; scoped_literal_vector m_lazy_clause; assumption_set_ref m_lemma_assumptions; // assumption tracking // Conflict resolution invariant: a marked literal is in m_lemma or on the trail stack. bool check_marks() { for (unsigned i = 0; i < m_marks.size(); i++) { SASSERT(m_marks[i] == 0); } return true; } unsigned scope_lvl() const { return m_scope_lvl; } bool is_marked(bool_var b) const { return m_marks.get(b, 0) == 1; } void mark(bool_var b) { m_marks.setx(b, 1, 0); } void reset_mark(bool_var b) { m_marks[b] = 0; } void reset_marks() { unsigned sz = m_lemma.size(); for (unsigned i = 0; i < sz; i++) { reset_mark(m_lemma[i].var()); } } void process_antecedent(literal antecedent) { bool_var b = antecedent.var(); TRACE("nlsat_resolve", tout << "resolving antecedent, l:\n"; display(tout, antecedent); tout << "\n";); if (assigned_value(antecedent) == l_undef) { // antecedent must be false in the current arith interpretation SASSERT(value(antecedent) == l_false); if (!is_marked(b)) { SASSERT(is_arith_atom(b) && max_var(b) < m_xk); // must be in a previous stage TRACE("nlsat_resolve", tout << "literal is unassigned, but it is false in arithmetic interpretation, adding it to lemma\n";); mark(b); m_lemma.push_back(antecedent); } return; } unsigned b_lvl = m_levels[b]; TRACE("nlsat_resolve", tout << "b_lvl: " << b_lvl << ", is_marked(b): " << is_marked(b) << ", m_num_marks: " << m_num_marks << "\n";); if (!is_marked(b)) { mark(b); if (b_lvl == scope_lvl() /* same level */ && max_var(b) == m_xk /* same stage */) { TRACE("nlsat_resolve", tout << "literal is in the same level and stage, increasing marks\n";); m_num_marks++; } else { TRACE("nlsat_resolve", tout << "previous level or stage, adding literal to lemma\n"; tout << "max_var(b): " << max_var(b) << ", m_xk: " << m_xk << ", lvl: " << b_lvl << ", scope_lvl: " << scope_lvl() << "\n";); m_lemma.push_back(antecedent); } } } void resolve_clause(bool_var b, unsigned sz, literal const * c) { TRACE("nlsat_proof", tout << "resolving "; if (b != null_bool_var) display_atom(tout, b); tout << "\n"; display(tout, sz, c); tout << "\n";); TRACE("nlsat_proof_sk", tout << "resolving "; if (b != null_bool_var) tout << "b" << b; tout << "\n"; display_abst(tout, sz, c); tout << "\n";); for (unsigned i = 0; i < sz; i++) { if (c[i].var() != b) process_antecedent(c[i]); } } void resolve_clause(bool_var b, clause const & c) { TRACE("nlsat_resolve", tout << "resolving clause for b: " << b << "\n";); resolve_clause(b, c.size(), c.c_ptr()); m_lemma_assumptions = m_asm.mk_join(static_cast<_assumption_set>(c.assumptions()), m_lemma_assumptions); } void resolve_lazy_justification(bool_var b, lazy_justification const & jst) { TRACE("nlsat_resolve", tout << "resolving lazy_justification for b: " << b << "\n";); unsigned sz = jst.size(); // Dump lemma as Mathematica formula that must be true, // if the current interpretation (really) makes the core in jst infeasible. TRACE("nlsat_mathematica", tout << "assignment lemma\n"; literal_vector core; for (unsigned i = 0; i < sz; i++) { core.push_back(~jst[i]); } display_mathematica_lemma(tout, core.size(), core.c_ptr(), true);); m_lazy_clause.reset(); m_explain(jst.size(), jst.lits(), m_lazy_clause); for (unsigned i = 0; i < sz; i++) m_lazy_clause.push_back(~jst[i]); // lazy clause is a valid clause TRACE("nlsat_mathematica", display_mathematica_lemma(tout, m_lazy_clause.size(), m_lazy_clause.c_ptr());); TRACE("nlsat_proof_sk", tout << "theory lemma\n"; display_abst(tout, m_lazy_clause.size(), m_lazy_clause.c_ptr()); tout << "\n";); TRACE("nlsat_resolve", tout << "m_xk: " << m_xk << ", "; m_display_var(tout, m_xk); tout << "\n"; tout << "new valid clause:\n"; display(tout, m_lazy_clause.size(), m_lazy_clause.c_ptr()); tout << "\n";); DEBUG_CODE({ unsigned sz = m_lazy_clause.size(); for (unsigned i = 0; i < sz; i++) { literal l = m_lazy_clause[i]; if (l.var() != b) { SASSERT(value(l) == l_false); } else { SASSERT(value(l) == l_true); SASSERT(!l.sign() || m_bvalues[b] == l_false); SASSERT(l.sign() || m_bvalues[b] == l_true); } } }); resolve_clause(b, m_lazy_clause.size(), m_lazy_clause.c_ptr()); } /** \brief Return true if all literals in ls are from previous stages. */ bool only_literals_from_previous_stages(unsigned num, literal const * ls) const { for (unsigned i = 0; i < num; i++) { if (max_var(ls[i]) == m_xk) return false; } return true; } /** \brief Return the maximum scope level in ls. \pre This method assumes value(ls[i]) is l_false for i in [0, num) */ unsigned max_scope_lvl(unsigned num, literal const * ls) { unsigned max = 0; for (unsigned i = 0; i < num; i++) { literal l = ls[i]; bool_var b = l.var(); SASSERT(value(ls[i]) == l_false); if (assigned_value(l) == l_false) { unsigned lvl = m_levels[b]; if (lvl > max) max = lvl; } else { // l must be a literal from a previous stage that is false in the current intepretation SASSERT(assigned_value(l) == l_undef); SASSERT(max_var(b) != null_var); SASSERT(m_xk != null_var); SASSERT(max_var(b) < m_xk); } } return max; } /** \brief Remove literals of the given lvl (that are in the current stage) from lemma. \pre This method assumes value(ls[i]) is l_false for i in [0, num) */ void remove_literals_from_lvl(scoped_literal_vector & lemma, unsigned lvl) { TRACE("nlsat_resolve", tout << "removing literals from lvl: " << lvl << " and stage " << m_xk << "\n";); unsigned sz = lemma.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { literal l = lemma[i]; bool_var b = l.var(); SASSERT(is_marked(b)); SASSERT(value(lemma[i]) == l_false); if (assigned_value(l) == l_false && m_levels[b] == lvl && max_var(b) == m_xk) { m_num_marks++; continue; } lemma.set(j, l); j++; } lemma.shrink(j); } /** \brief Return true if it is a Boolean lemma. */ bool is_bool_lemma(unsigned sz, literal const * ls) const { for (unsigned i = 0; i < sz; i++) { if (m_atoms[ls[i].var()] != 0) return false; } return true; } /** Return the maximal decision level in lemma for literals in the first sz-1 positions that are at the same stage. If all these literals are from previous stages, we just backtrack the current level. */ unsigned find_new_level_arith_lemma(unsigned sz, literal const * lemma) { SASSERT(!is_bool_lemma(sz, lemma)); unsigned new_lvl = 0; bool found_lvl = false; for (unsigned i = 0; i < sz - 1; i++) { literal l = lemma[i]; if (max_var(l) == m_xk) { bool_var b = l.var(); if (!found_lvl) { found_lvl = true; new_lvl = m_levels[b]; } else { if (m_levels[b] > new_lvl) new_lvl = m_levels[b]; } } } SASSERT(!found_lvl || new_lvl < scope_lvl()); if (!found_lvl) { TRACE("nlsat_resolve", tout << "fail to find new lvl, using previous one\n";); new_lvl = scope_lvl() - 1; } return new_lvl; } /** \brief Return true if the conflict was solved. */ bool resolve(clause const & conflict) { clause const * conflict_clause = &conflict; start: SASSERT(check_marks()); TRACE("nlsat_proof", tout << "STARTING RESOLUTION\n";); TRACE("nlsat_proof_sk", tout << "STARTING RESOLUTION\n";); m_conflicts++; TRACE("nlsat", tout << "resolve, conflicting clause:\n"; display(tout, *conflict_clause); tout << "\n"; tout << "xk: "; if (m_xk != null_var) m_display_var(tout, m_xk); else tout << ""; tout << "\n"; tout << "scope_lvl: " << scope_lvl() << "\n"; tout << "current assignment\n"; display_assignment(tout); display_bool_assignment(tout);); // static unsigned counter = 0; // counter++; // if (counter > 6) // exit(0); m_num_marks = 0; m_lemma.reset(); m_lemma_assumptions = 0; resolve_clause(null_bool_var, *conflict_clause); unsigned top = m_trail.size(); bool found_decision; while (true) { found_decision = false; while (m_num_marks > 0) { SASSERT(top > 0); trail & t = m_trail[top-1]; SASSERT(t.m_kind != trail::NEW_STAGE); // we only mark literals that are in the same stage if (t.m_kind == trail::BVAR_ASSIGNMENT) { bool_var b = t.m_b; if (is_marked(b)) { TRACE("nlsat_resolve", tout << "found marked bool_var: " << b << "\n"; display_atom(tout, b); tout << "\n";); m_num_marks--; reset_mark(b); justification jst = m_justifications[b]; switch (jst.get_kind()) { case justification::CLAUSE: resolve_clause(b, *(jst.get_clause())); break; case justification::LAZY: resolve_lazy_justification(b, *(jst.get_lazy())); break; case justification::DECISION: SASSERT(m_num_marks == 0); found_decision = true; TRACE("nlsat_resolve", tout << "found decision\n";); m_lemma.push_back(literal(b, m_bvalues[b] == l_true)); break; default: UNREACHABLE(); break; } } } top--; } // m_lemma is an implicating clause after backtracking current scope level. if (found_decision) break; // If lemma only contains literals from previous stages, then we can stop. // We make progress by returning to a previous stage with additional information (new lemma) // that forces us to select a new partial interpretation if (only_literals_from_previous_stages(m_lemma.size(), m_lemma.c_ptr())) break; // Conflict does not depend on the current decision, and it is still in the current stage. // We should find // - the maximal scope level in the lemma // - remove literal assigned in the scope level from m_lemma // - backtrack to this level // - and continue conflict resolution from there // - we must bump m_num_marks for literals removed from m_lemma unsigned max_lvl = max_scope_lvl(m_lemma.size(), m_lemma.c_ptr()); TRACE("nlsat_resolve", tout << "conflict does not depend on current decision, backtracking to level: " << max_lvl << "\n";); SASSERT(max_lvl < scope_lvl()); remove_literals_from_lvl(m_lemma, max_lvl); undo_until_level(max_lvl); top = m_trail.size(); TRACE("nlsat_resolve", tout << "scope_lvl: " << scope_lvl() << " num marks: " << m_num_marks << "\n";); SASSERT(scope_lvl() == max_lvl); } TRACE("nlsat_proof", tout << "New lemma\n"; display(tout, m_lemma); tout << "\n=========================\n";); TRACE("nlsat_proof_sk", tout << "New lemma\n"; display_abst(tout, m_lemma); tout << "\n=========================\n";); if (m_lemma.empty()) { TRACE("nlsat", tout << "empty clause generated\n";); return false; // problem is unsat, empty clause was generated } reset_marks(); // remove marks from the literals in m_lemmas. TRACE("nlsat", tout << "new lemma:\n"; display(tout, m_lemma.size(), m_lemma.c_ptr()); tout << "\n"; tout << "found_decision: " << found_decision << "\n";); // There are two possibilities: // 1) m_lemma contains only literals from previous stages, and they // are false in the current interpretation. We make progress // by returning to a previous stage with additional information (new clause) // that forces us to select a new partial interpretation // >>> Return to some previous stage (we may also backjump many decisions and stages). // // 2) m_lemma contains at most one literal from the current level (the last literal). // Moreover, this literal was a decision, but the new lemma forces it to // be assigned to a different value. // >>> In this case, we remain in the same stage but, we add a new asserted literal // in a previous scope level. We may backjump many decisions. // unsigned sz = m_lemma.size(); clause * new_cls = 0; if (!found_decision) { // Case 1) // We just have to find the maximal variable in m_lemma, and return to that stage // Remark: the lemma may contain only boolean literals, in this case new_max_var == null_var; var new_max_var = max_var(sz, m_lemma.c_ptr()); TRACE("nlsat_resolve", tout << "backtracking to stage: " << new_max_var << ", curr: " << m_xk << "\n";); undo_until_stage(new_max_var); SASSERT(m_xk == new_max_var); new_cls = mk_clause(sz, m_lemma.c_ptr(), true, m_lemma_assumptions.get()); TRACE("nlsat", tout << "new_level: " << scope_lvl() << "\nnew_stage: " << new_max_var << " "; if (new_max_var != null_var) m_display_var(tout, new_max_var); tout << "\n";); } else { SASSERT(scope_lvl() >= 1); // Case 2) if (is_bool_lemma(m_lemma.size(), m_lemma.c_ptr())) { // boolean lemma, we just backtrack until the last literal is unassigned. bool_var max_bool_var = m_lemma[m_lemma.size()-1].var(); undo_until_unassigned(max_bool_var); } else { // We must find the maximal decision level in literals in the first sz-1 positions that // are at the same stage. If all these literals are from previous stages, // we just backtrack the current level. unsigned new_lvl = find_new_level_arith_lemma(m_lemma.size(), m_lemma.c_ptr()); TRACE("nlsat", tout << "backtracking to new level: " << new_lvl << ", curr: " << m_scope_lvl << "\n";); undo_until_level(new_lvl); } new_cls = mk_clause(sz, m_lemma.c_ptr(), true, m_lemma_assumptions.get()); } NLSAT_VERBOSE(display(verbose_stream(), *new_cls); verbose_stream() << "\n";); if (!process_clause(*new_cls, true)) { TRACE("nlsat", tout << "new clause triggered another conflict, restarting conflict resolution...\n";); // we are still in conflict conflict_clause = new_cls; goto start; } TRACE("nlsat_resolve_done", display_assignment(tout); display_bool_assignment(tout);); return true; } // ----------------------- // // Debugging // // ----------------------- bool check_watches() const { for (var x = 0; x < num_vars(); x++) { clause_vector const & cs = m_watches[x]; unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { clause const & c = *(cs[i]); SASSERT(max_var(c) == x); } } return true; } bool check_bwatches() const { for (bool_var b = 0; b < m_bwatches.size(); b++) { clause_vector const & cs = m_bwatches[b]; unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { clause const & c = *(cs[i]); SASSERT(max_var(c) == null_var); SASSERT(max_bvar(c) == b); } } return true; } bool check_invariant() const { SASSERT(check_watches()); SASSERT(check_bwatches()); return true; } bool check_satisfied(clause_vector const & cs) const { unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { clause const & c = *(cs[i]); if (!is_satisfied(c)) { TRACE("nlsat", tout << "not satisfied\n"; display(tout, c); tout << "\n";); UNREACHABLE(); } } return true; } bool check_satisfied() const { TRACE("nlsat", tout << "bk: b" << m_bk << ", xk: x" << m_xk << "\n"; if (m_xk != null_var) { m_display_var(tout, m_xk); tout << "\n"; }); unsigned num = m_atoms.size(); if (m_bk != null_bool_var) num = m_bk; for (bool_var b = 0; b < num; b++) { SASSERT(check_satisfied(m_bwatches[b])); } if (m_xk != null_var) { for (var x = 0; x < m_xk; x++) { SASSERT(check_satisfied(m_watches[x])); } } return true; } // ----------------------- // // Statistics // // ----------------------- void collect_statistics(statistics & st) { st.update("nlsat conflicts", m_conflicts); st.update("nlsat propagations", m_propagations); st.update("nlsat decisions", m_decisions); st.update("nlsat stages", m_stages); st.update("nlsat irrational assignments", m_irrational_assignments); } void reset_statistics() { m_conflicts = 0; m_propagations = 0; m_decisions = 0; m_stages = 0; m_irrational_assignments = 0; } // ----------------------- // // Variable reodering // // ----------------------- struct var_info_collector { pmanager & pm; atom_vector const & m_atoms; unsigned_vector m_max_degree; unsigned_vector m_num_occs; var_info_collector(pmanager & _pm, atom_vector const & atoms, unsigned num_vars): pm(_pm), m_atoms(atoms) { m_max_degree.resize(num_vars, 0); m_num_occs.resize(num_vars, 0); } var_vector m_vars; void collect(poly * p) { m_vars.reset(); pm.vars(p, m_vars); unsigned sz = m_vars.size(); for (unsigned i = 0; i < sz; i++) { var x = m_vars[i]; unsigned k = pm.degree(p, x); m_num_occs[x]++; if (k > m_max_degree[x]) m_max_degree[x] = k; } } void collect(literal l) { bool_var b = l.var(); atom * a = m_atoms[b]; if (a == 0) return; if (a->is_ineq_atom()) { unsigned sz = to_ineq_atom(a)->size(); for (unsigned i = 0; i < sz; i++) { collect(to_ineq_atom(a)->p(i)); } } else { collect(to_root_atom(a)->p()); } } void collect(clause const & c) { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) collect(c[i]); } void collect(clause_vector const & cs) { unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) collect(*(cs[i])); } void display(std::ostream & out, display_var_proc const & proc) { unsigned sz = m_num_occs.size(); for (unsigned i = 0; i < sz; i++) { proc(out, i); out << " -> " << m_max_degree[i] << " : " << m_num_occs[i] << "\n"; } } }; struct reorder_lt { var_info_collector const & m_info; reorder_lt(var_info_collector const & info):m_info(info) {} bool operator()(var x, var y) const { // high degree first if (m_info.m_max_degree[x] < m_info.m_max_degree[y]) return false; if (m_info.m_max_degree[x] > m_info.m_max_degree[y]) return true; // more constrained first if (m_info.m_num_occs[x] < m_info.m_num_occs[y]) return false; if (m_info.m_num_occs[x] > m_info.m_num_occs[y]) return true; return x < y; } }; // Order variables by degree and number of occurrences void heuristic_reorder() { unsigned num = num_vars(); var_info_collector collector(m_pm, m_atoms, num); collector.collect(m_clauses); collector.collect(m_learned); TRACE("nlsat_reorder", collector.display(tout, m_display_var);); var_vector new_order; for (var x = 0; x < num; x++) { new_order.push_back(x); } std::sort(new_order.begin(), new_order.end(), reorder_lt(collector)); TRACE("nlsat_reorder", tout << "new order: "; for (unsigned i = 0; i < num; i++) tout << new_order[i] << " "; tout << "\n";); var_vector perm; perm.resize(num, 0); for (var x = 0; x < num; x++) { perm[new_order[x]] = x; } reorder(perm.size(), perm.c_ptr()); SASSERT(check_invariant()); } void shuffle_vars() { var_vector p; unsigned num = num_vars(); for (var x = 0; x < num; x++) { p.push_back(x); } random_gen r(m_random_seed); shuffle(p.size(), p.c_ptr(), r); reorder(p.size(), p.c_ptr()); } /** \brief Reorder variables using the giving permutation. p maps internal variables to their new positions */ void reorder(unsigned sz, var const * p) { TRACE("nlsat_reorder", tout << "solver before variable reorder\n"; display(tout); display_vars(tout); tout << "\npermutation:\n"; for (unsigned i = 0; i < sz; i++) tout << p[i] << " "; tout << "\n"; ); SASSERT(num_vars() == sz); TRACE("nlsat_bool_assignment_bug", tout << "before reset watches\n"; display_bool_assignment(tout);); reset_watches(); assignment new_assignment(m_am); for (var x = 0; x < num_vars(); x++) { if (m_assignment.is_assigned(x)) new_assignment.set(p[x], m_assignment.value(x)); } var_vector new_inv_perm; new_inv_perm.resize(sz); // the undo_until_size(0) statement erases the Boolean assignment. // undo_until_size(0) undo_until_stage(null_var); m_cache.reset(); DEBUG_CODE({ for (var x = 0; x < num_vars(); x++) { SASSERT(m_watches[x].empty()); } }); // update m_perm mapping for (unsigned ext_x = 0; ext_x < sz; ext_x++) { // p: internal -> new pos // m_perm: internal -> external // m_inv_perm: external -> internal new_inv_perm[ext_x] = p[m_inv_perm[ext_x]]; m_perm.set(new_inv_perm[ext_x], ext_x); } svector is_int; is_int.swap(m_is_int); for (var x = 0; x < sz; x++) { m_is_int.setx(p[x], is_int[x], false); SASSERT(m_infeasible[x] == 0); } m_inv_perm.swap(new_inv_perm); DEBUG_CODE({ for (var x = 0; x < num_vars(); x++) { SASSERT(x == m_inv_perm[m_perm[x]]); SASSERT(m_watches[x].empty()); } }); m_pm.rename(sz, p); del_ill_formed_lemmas(); TRACE("nlsat_bool_assignment_bug", tout << "before reinit cache\n"; display_bool_assignment(tout);); reinit_cache(); m_assignment.swap(new_assignment); reattach_arith_clauses(m_clauses); reattach_arith_clauses(m_learned); TRACE("nlsat_reorder", tout << "solver after variable reorder\n"; display(tout); display_vars(tout);); } /** \brief Restore variable order. */ void restore_order() { // m_perm: internal -> external // m_inv_perm: external -> internal var_vector p; p.append(m_perm); reorder(p.size(), p.c_ptr()); DEBUG_CODE({ for (var x = 0; x < num_vars(); x++) { SASSERT(m_perm[x] == x); SASSERT(m_inv_perm[x] == x); } }); } /** \brief After variable reordering some lemmas containing root atoms may be ill-formed. */ void del_ill_formed_lemmas() { unsigned sz = m_learned.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { clause * c = m_learned[i]; if (ill_formed(*c)) { del_clause(c); } else { m_learned[j] = c; j++; } } m_learned.shrink(j); } /** \brief Return true if the clause contains an ill formed root atom */ bool ill_formed(clause const & c) { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { bool_var b = c[i].var(); atom * a = m_atoms[b]; if (a == 0) continue; if (a->is_ineq_atom()) continue; if (to_root_atom(a)->x() < max_var(to_root_atom(a)->p())) return true; } return false; } /** \brief reinsert all polynomials in the unique cache */ void reinit_cache() { reinit_cache(m_clauses); reinit_cache(m_learned); } void reinit_cache(clause_vector const & cs) { unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) reinit_cache(*(cs[i])); } void reinit_cache(clause const & c) { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) reinit_cache(c[i]); } void reinit_cache(literal l) { bool_var b = l.var(); atom * a = m_atoms[b]; if (a == 0) return; if (a->is_ineq_atom()) { var max = 0; unsigned sz = to_ineq_atom(a)->size(); for (unsigned i = 0; i < sz; i++) { poly * p = to_ineq_atom(a)->p(i); VERIFY(m_cache.mk_unique(p) == p); var x = m_pm.max_var(p); if (x > max) max = x; } a->m_max_var = max; } else { poly * p = to_root_atom(a)->p(); VERIFY(m_cache.mk_unique(p) == p); a->m_max_var = m_pm.max_var(p); } } void reset_watches() { unsigned num = num_vars(); for (var x = 0; x < num; x++) { m_watches[x].reset(); } } void reattach_arith_clauses(clause_vector const & cs) { unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { clause & c = *cs[i]; var x = max_var(c); if (x != null_var) m_watches[x].push_back(&c); } } // ----------------------- // // Solver initialization // // ----------------------- struct degree_lt { unsigned_vector & m_degrees; degree_lt(unsigned_vector & ds):m_degrees(ds) {} bool operator()(unsigned i1, unsigned i2) const { if (m_degrees[i1] < m_degrees[i2]) return true; if (m_degrees[i1] > m_degrees[i2]) return false; return i1 < i2; } }; unsigned_vector m_cs_degrees; unsigned_vector m_cs_p; void sort_clauses_by_degree(unsigned sz, clause ** cs) { if (sz <= 1) return; TRACE("nlsat_reorder_clauses", tout << "before:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); m_cs_degrees.reset(); m_cs_p.reset(); for (unsigned i = 0; i < sz; i++) { m_cs_p.push_back(i); m_cs_degrees.push_back(degree(*(cs[i]))); } std::sort(m_cs_p.begin(), m_cs_p.end(), degree_lt(m_cs_degrees)); TRACE("nlsat_reorder_clauses", tout << "permutation: "; ::display(tout, m_cs_p.begin(), m_cs_p.end()); tout << "\n";); apply_permutation(sz, cs, m_cs_p.c_ptr()); TRACE("nlsat_reorder_clauses", tout << "after:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); } void sort_watched_clauses() { unsigned num = num_vars(); for (unsigned i = 0; i < num; i++) { clause_vector & ws = m_watches[i]; sort_clauses_by_degree(ws.size(), ws.c_ptr()); } } // ----------------------- // // Full dimensional // // A problem is in the full dimensional fragment if it does // not contain equalities or non-strict inequalities. // // ----------------------- bool is_full_dimensional(literal l) const { atom * a = m_atoms[l.var()]; if (a == 0) return true; switch (a->get_kind()) { case atom::EQ: return l.sign(); case atom::LT: return !l.sign(); case atom::GT: return !l.sign(); case atom::ROOT_EQ: return l.sign(); case atom::ROOT_LT: return !l.sign(); case atom::ROOT_GT: return !l.sign(); case atom::ROOT_LE: return l.sign(); case atom::ROOT_GE: return l.sign(); default: UNREACHABLE(); return false; } } bool is_full_dimensional(clause const & c) const { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { if (!is_full_dimensional(c[i])) return false; } return true; } bool is_full_dimensional(clause_vector const & cs) const { unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { if (!is_full_dimensional(*(cs[i]))) return false; } return true; } bool is_full_dimensional() const { return is_full_dimensional(m_clauses); } // ----------------------- // // Pretty printing // // ----------------------- void display_assignment(std::ostream & out, display_var_proc const & proc) const { for (var x = 0; x < num_vars(); x++) { if (m_assignment.is_assigned(x)) { proc(out, x); out << " -> "; m_am.display_decimal(out, m_assignment.value(x)); out << "\n"; } } } void display_bool_assignment(std::ostream & out, display_var_proc const & proc) const { unsigned sz = m_atoms.size(); for (bool_var b = 0; b < sz; b++) { if (m_atoms[b] == 0 && m_bvalues[b] != l_undef) { out << "b" << b << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << "\n"; } } TRACE("nlsat_bool_assignment", for (bool_var b = 0; b < sz; b++) { out << "b" << b << " -> " << m_bvalues[b] << " " << m_atoms[b] << "\n"; }); } bool display_mathematica_assignment(std::ostream & out) const { bool first = true; for (var x = 0; x < num_vars(); x++) { if (m_assignment.is_assigned(x)) { if (first) first = false; else out << " && "; out << "x" << x << " == "; m_am.display_mathematica(out, m_assignment.value(x)); } } return !first; } void display_assignment(std::ostream & out) const { display_assignment(out, m_display_var); } void display_bool_assignment(std::ostream & out) const { display_bool_assignment(out, m_display_var); } void display(std::ostream & out, ineq_atom const & a, display_var_proc const & proc, bool use_star = false) const { unsigned sz = a.size(); for (unsigned i = 0; i < sz; i++) { if (use_star && i > 0) out << "*"; bool is_even = a.is_even(i); if (is_even || sz > 1) out << "("; m_pm.display(out, a.p(i), proc, use_star); if (is_even || sz > 1) out << ")"; if (is_even) out << "^2"; } switch (a.get_kind()) { case atom::LT: out << " < 0"; break; case atom::GT: out << " > 0"; break; case atom::EQ: out << " = 0"; break; default: UNREACHABLE(); break; } } void display_mathematica(std::ostream & out, ineq_atom const & a) const { unsigned sz = a.size(); for (unsigned i = 0; i < sz; i++) { if (i > 0) out << "*"; bool is_even = a.is_even(i); if (sz > 1) out << "("; if (is_even) out << "("; m_pm.display(out, a.p(i), display_var_proc(), true); if (is_even) out << "^2)"; if (sz > 1) out << ")"; } switch (a.get_kind()) { case atom::LT: out << " < 0"; break; case atom::GT: out << " > 0"; break; case atom::EQ: out << " == 0"; break; default: UNREACHABLE(); break; } } void display_smt2(std::ostream & out, ineq_atom const & a, display_var_proc const & proc) const { switch (a.get_kind()) { case atom::LT: out << "(< "; break; case atom::GT: out << "(> "; break; case atom::EQ: out << "(= "; break; default: UNREACHABLE(); break; } unsigned sz = a.size(); if (sz > 1) out << "(* "; for (unsigned i = 0; i < sz; i++) { if (i > 0) out << " "; if (a.is_even(i)) { out << "(* "; m_pm.display_smt2(out, a.p(i), proc); out << " "; m_pm.display_smt2(out, a.p(i), proc); out << ")"; } else { m_pm.display_smt2(out, a.p(i), proc); } } if (sz > 1) out << ")"; out << " 0)"; } void display(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { proc(out, a.x()); switch (a.get_kind()) { case atom::ROOT_LT: out << " < "; break; case atom::ROOT_GT: out << " > "; break; case atom::ROOT_LE: out << " <= "; break; case atom::ROOT_GE: out << " >= "; break; case atom::ROOT_EQ: out << " = "; break; default: UNREACHABLE(); break; } out << "root[" << a.i() << "]("; m_pm.display(out, a.p(), proc); out << ")"; } struct mathematica_var_proc : public display_var_proc { var m_x; public: mathematica_var_proc(var x):m_x(x) {} virtual void operator()(std::ostream & out, var x) const { if (m_x == x) out << "#1"; else out << "x" << x; } }; void display_mathematica(std::ostream & out, root_atom const & a) const { out << "x" << a.x(); switch (a.get_kind()) { case atom::ROOT_LT: out << " < "; break; case atom::ROOT_GT: out << " > "; break; case atom::ROOT_LE: out << " <= "; break; case atom::ROOT_GE: out << " >= "; break; case atom::ROOT_EQ: out << " == "; break; default: UNREACHABLE(); break; } out << "Root["; m_pm.display(out, a.p(), mathematica_var_proc(a.x()), true); out << " &, " << a.i() << "]"; } void display_smt2(std::ostream & out, root_atom const & a) const { NOT_IMPLEMENTED_YET(); } void display(std::ostream & out, atom const & a, display_var_proc const & proc) const { if (a.is_ineq_atom()) display(out, static_cast(a), proc); else display(out, static_cast(a), proc); } void display_mathematica(std::ostream & out, atom const & a) const { if (a.is_ineq_atom()) display_mathematica(out, static_cast(a)); else display_mathematica(out, static_cast(a)); } void display_smt2(std::ostream & out, atom const & a, display_var_proc const & proc) const { if (a.is_ineq_atom()) display_smt2(out, static_cast(a), proc); else display_smt2(out, static_cast(a), proc); } void display_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { if (b == 0) out << "true"; else if (m_atoms[b] == 0) out << "b" << b; else display(out, *(m_atoms[b]), proc); } void display_atom(std::ostream & out, bool_var b) const { display_atom(out, b, m_display_var); } void display_mathematica_atom(std::ostream & out, bool_var b) const { if (b == 0) out << "(0 < 1)"; else if (m_atoms[b] == 0) out << "b" << b; else display_mathematica(out, *(m_atoms[b])); } void display_smt2_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { if (b == 0) out << "true"; else if (m_atoms[b] == 0) out << "b" << b; else display_smt2(out, *(m_atoms[b]), proc); } void display(std::ostream & out, literal l, display_var_proc const & proc) const { if (l.sign()) { bool_var b = l.var(); out << "!"; if (m_atoms[b] != 0) out << "("; display_atom(out, b, proc); if (m_atoms[b] != 0) out << ")"; } else { display_atom(out, l.var(), proc); } } void display(std::ostream & out, literal l) const { display(out, l, m_display_var); } void display_mathematica(std::ostream & out, literal l) const { if (l.sign()) { bool_var b = l.var(); out << "!"; if (m_atoms[b] != 0) out << "("; display_mathematica_atom(out, b); if (m_atoms[b] != 0) out << ")"; } else { display_mathematica_atom(out, l.var()); } } void display_smt2(std::ostream & out, literal l, display_var_proc const & proc) const { if (l.sign()) { bool_var b = l.var(); out << "(not "; display_smt2_atom(out, b, proc); out << ")"; } else { display_smt2_atom(out, l.var(), proc); } } void display_assumptions(std::ostream & out, _assumption_set s) const { } void display(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { for (unsigned i = 0; i < num; i++) { if (i > 0) out << " or "; display(out, ls[i], proc); } } void display(std::ostream & out, unsigned num, literal const * ls) { display(out, num, ls, m_display_var); } void display(std::ostream & out, scoped_literal_vector const & cs) { display(out, cs.size(), cs.c_ptr(), m_display_var); } void display(std::ostream & out, clause const & c, display_var_proc const & proc) const { if (c.assumptions() != 0) { display_assumptions(out, static_cast<_assumption_set>(c.assumptions())); out << " |- "; } display(out, c.size(), c.c_ptr(), proc); } void display(std::ostream & out, clause const & c) const { display(out, c, m_display_var); } void display_smt2(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { if (num == 0) { out << "false"; } else if (num == 1) { display_smt2(out, ls[0], proc); } else { out << "(or"; for (unsigned i = 0; i < num; i++) { out << " "; display_smt2(out, ls[i], proc); } out << ")"; } } void display_smt2(std::ostream & out, clause const & c, display_var_proc const & proc = display_var_proc()) const { display_smt2(out, c.size(), c.c_ptr(), proc); } void display_abst(std::ostream & out, literal l) const { if (l.sign()) { bool_var b = l.var(); out << "!"; if (b == true_bool_var) out << "true"; else out << "b" << b; } else { out << "b" << l.var(); } } void display_abst(std::ostream & out, unsigned num, literal const * ls) const { for (unsigned i = 0; i < num; i++) { if (i > 0) out << " or "; display_abst(out, ls[i]); } } void display_abst(std::ostream & out, scoped_literal_vector const & cs) const { display_abst(out, cs.size(), cs.c_ptr()); } void display_abst(std::ostream & out, clause const & c) const { display_abst(out, c.size(), c.c_ptr()); } void display_mathematica(std::ostream & out, clause const & c) const { out << "("; unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { if (i > 0) out << " || "; display_mathematica(out, c[i]); } out << ")"; } // Debugging support: // Display generated lemma in Mathematica format. // Mathematica must reduce lemma to True (modulo resource constraints). void display_mathematica_lemma(std::ostream & out, unsigned num, literal const * ls, bool include_assignment = false) const { out << "Resolve[ForAll[{"; // var definition for (unsigned i = 0; i < num_vars(); i++) { if (i > 0) out << ", "; out << "x" << i; } out << "}, "; if (include_assignment) { out << "!("; if (!display_mathematica_assignment(out)) out << "0 < 1"; // nothing was printed out << ") || "; } for (unsigned i = 0; i < num; i++) { if (i > 0) out << " || "; display_mathematica(out, ls[i]); } out << "], Reals]\n"; // end of exists } void display(std::ostream & out, clause_vector const & cs, display_var_proc const & proc) const { unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { display(out, *(cs[i]), proc); out << "\n"; } } void display(std::ostream & out, clause_vector const & cs) const { display(out, cs, m_display_var); } void display_mathematica(std::ostream & out, clause_vector const & cs) const { unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { if (i > 0) out << ",\n"; out << " "; display_mathematica(out, *(cs[i])); } } void display_abst(std::ostream & out, clause_vector const & cs) const { unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { display_abst(out, *(cs[i])); out << "\n"; } } void display(std::ostream & out, display_var_proc const & proc) const { display(out, m_clauses, proc); if (!m_learned.empty()) { out << "Lemmas:\n"; display(out, m_learned, proc); } } void display_mathematica(std::ostream & out) const { out << "{\n"; display_mathematica(out, m_clauses); out << "}\n"; } void display_abst(std::ostream & out) const { display_abst(out, m_clauses); if (!m_learned.empty()) { out << "Lemmas:\n"; display_abst(out, m_learned); } } void display(std::ostream & out) const { display(out, m_display_var); } void display_vars(std::ostream & out) const { for (unsigned i = 0; i < num_vars(); i++) { out << i << " -> "; m_display_var(out, i); out << "\n"; } } void display_smt2_arith_decls(std::ostream & out) const { unsigned sz = m_is_int.size(); for (unsigned i = 0; i < sz; i++) { if (m_is_int[i]) out << "(declare-fun x" << i << " () Int)\n"; else out << "(declare-fun x" << i << " () Real)\n"; } } void display_smt2_bool_decls(std::ostream & out) const { unsigned sz = m_atoms.size(); for (unsigned i = 0; i < sz; i++) { if (m_atoms[i] == 0) out << "(declare-fun b" << i << " () Bool)\n"; } } void display_smt2(std::ostream & out) const { display_smt2_bool_decls(out); display_smt2_arith_decls(out); out << "(assert (and true\n"; unsigned sz = m_clauses.size(); for (unsigned i = 0; i < sz; i++) { display_smt2(out, *(m_clauses[i])); out << "\n"; } out << "))\n(check-sat)" << std::endl; } }; solver::solver(params_ref const & p) { m_imp = alloc(imp, *this, p); } solver::~solver() { dealloc(m_imp); } lbool solver::check() { return m_imp->check(); } void solver::set_cancel(bool f) { m_imp->set_cancel(f); } void solver::collect_param_descrs(param_descrs & d) { algebraic_numbers::manager::collect_param_descrs(d); nlsat_params::collect_param_descrs(d); } unsynch_mpq_manager & solver::qm() { return m_imp->m_qm; } anum_manager & solver::am() { return m_imp->m_am; } pmanager & solver::pm() { return m_imp->m_pm; } void solver::set_display_var(display_var_proc const & proc) { m_imp->m_display_var.m_proc = &proc; } bool solver::is_int(var x) const { return m_imp->is_int(x); } bool_var solver::mk_bool_var() { return m_imp->mk_bool_var(); } atom * solver::bool_var2atom(bool_var b) { return m_imp->m_atoms[b]; } var solver::mk_var(bool is_int) { return m_imp->mk_var(is_int); } bool_var solver::mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { return m_imp->mk_ineq_atom(k, sz, ps, is_even); } literal solver::mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { return m_imp->mk_ineq_literal(k, sz, ps, is_even); } bool_var solver::mk_root_atom(atom::kind k, var x, unsigned i, poly * p) { return m_imp->mk_root_atom(k, x, i, p); } void solver::inc_ref(bool_var b) { m_imp->inc_ref(b); } void solver::dec_ref(bool_var b) { m_imp->dec_ref(b); } void solver::mk_clause(unsigned num_lits, literal * lits, assumption a) { return m_imp->mk_clause(num_lits, lits, a); } void solver::display(std::ostream & out) const { m_imp->display(out); } void solver::display(std::ostream & out, literal l) const { m_imp->display(out, l); } void solver::display(std::ostream & out, var x) const { m_imp->m_display_var(out, x); } display_var_proc const & solver::display_proc() const { return m_imp->m_display_var; } anum const & solver::value(var x) const { if (m_imp->m_assignment.is_assigned(x)) return m_imp->m_assignment.value(x); return m_imp->m_zero; } lbool solver::bvalue(bool_var b) const { return m_imp->m_bvalues[b]; } lbool solver::value(literal l) const { return m_imp->value(l); } bool solver::is_interpreted(bool_var b) const { return m_imp->m_atoms[b] != 0; } void solver::reset_statistics() { return m_imp->reset_statistics(); } void solver::collect_statistics(statistics & st) { return m_imp->collect_statistics(st); } }; z3-4.4.0/src/nlsat/nlsat_evaluator.cpp0000644000175000017500000006734512540347414017503 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_evaluator.cpp Abstract: Helper class for computing the infeasible intervals of an arithmetic literal. Author: Leonardo de Moura (leonardo) 2012-01-12. Revision History: --*/ #include"nlsat_evaluator.h" namespace nlsat { struct evaluator::imp { assignment const & m_assignment; pmanager & m_pm; small_object_allocator & m_allocator; anum_manager & m_am; interval_set_manager m_ism; scoped_anum_vector m_tmp_values; scoped_anum_vector m_add_roots_tmp; scoped_anum_vector m_inf_tmp; // sign tables: light version struct sign_table { anum_manager & m_am; struct section { anum m_root; unsigned m_pos; }; svector
m_sections; unsigned_vector m_sorted_sections; // refs to m_sections unsigned_vector m_poly_sections; // refs to m_sections svector m_poly_signs; struct poly_info { unsigned m_num_roots; unsigned m_first_section; // idx in m_poly_sections; unsigned m_first_sign; // idx in m_poly_signs; poly_info(unsigned num, unsigned first_section, unsigned first_sign): m_num_roots(num), m_first_section(first_section), m_first_sign(first_sign) { } }; svector m_info; sign_table(anum_manager & am):m_am(am) {} ~sign_table() { reset(); } void reset() { unsigned sz = m_sections.size(); for (unsigned i = 0; i < sz; i++) m_am.del(m_sections[i].m_root); m_sections.reset(); m_sorted_sections.reset(); m_poly_sections.reset(); m_poly_signs.reset(); m_info.reset(); } unsigned mk_section(anum & v, unsigned pos) { unsigned new_id = m_sections.size(); m_sections.push_back(section()); section & new_s = m_sections.back(); m_am.set(new_s.m_root, v); new_s.m_pos = pos; return new_id; } // Merge the new roots of a polynomial p into m_sections & m_sorted_sections. // Store the section ids for the new roots in p_section_ids unsigned_vector new_sorted_sections; void merge(anum_vector & roots, unsigned_vector & p_section_ids) { new_sorted_sections.reset(); // new m_sorted_sections unsigned i1 = 0; unsigned sz1 = m_sorted_sections.size(); unsigned i2 = 0; unsigned sz2 = roots.size(); unsigned j = 0; while (i1 < sz1 && i2 < sz2) { unsigned s1_id = m_sorted_sections[i1]; section & s1 = m_sections[s1_id]; anum & r2 = roots[i2]; int c = m_am.compare(s1.m_root, r2); if (c == 0) { new_sorted_sections.push_back(s1_id); p_section_ids.push_back(s1_id); s1.m_pos = j; i1++; i2++; } else if (c < 0) { new_sorted_sections.push_back(s1_id); s1.m_pos = j; i1++; } else { // create new section unsigned new_id = mk_section(r2, j); // new_sorted_sections.push_back(new_id); p_section_ids.push_back(new_id); i2++; } j++; } SASSERT(i1 == sz1 || i2 == sz2); while (i1 < sz1) { unsigned s1_id = m_sorted_sections[i1]; section & s1 = m_sections[s1_id]; new_sorted_sections.push_back(s1_id); s1.m_pos = j; i1++; j++; } while (i2 < sz2) { anum & r2 = roots[i2]; // create new section unsigned new_id = mk_section(r2, j); // new_sorted_sections.push_back(new_id); p_section_ids.push_back(new_id); i2++; j++; } m_sorted_sections.swap(new_sorted_sections); } /** \brief Add polynomial with the given roots and signs. */ unsigned_vector p_section_ids; void add(anum_vector & roots, svector & signs) { p_section_ids.reset(); if (!roots.empty()) merge(roots, p_section_ids); unsigned first_sign = m_poly_signs.size(); unsigned first_section = m_poly_sections.size(); unsigned num_signs = signs.size(); // Must normalize signs since we use arithmetic operations such as * // during evaluation. // Without normalization, overflows may happen, and wrong results may be produced. for (unsigned i = 0; i < num_signs; i++) m_poly_signs.push_back(normalize_sign(signs[i])); m_poly_sections.append(p_section_ids); m_info.push_back(poly_info(roots.size(), first_section, first_sign)); SASSERT(check_invariant()); } /** \brief Add constant polynomial */ void add_const(int sign) { unsigned first_sign = m_poly_signs.size(); unsigned first_section = m_poly_sections.size(); m_poly_signs.push_back(normalize_sign(sign)); m_info.push_back(poly_info(0, first_section, first_sign)); } unsigned num_cells() const { return m_sections.size() * 2 + 1; } /** \brief Return true if the given cell is a section (i.e., root) */ bool is_section(unsigned c) const { SASSERT(c < num_cells()); return c % 2 == 1; } /** \brief Return true if the given cell is a sector (i.e., an interval between roots, or (-oo, min-root), or (max-root, +oo)). */ bool is_sector(unsigned c) const { SASSERT(c < num_cells()); return c % 2 == 0; } /** \brief Return the root id associated with the given section. \pre is_section(c) */ unsigned get_root_id(unsigned c) const { SASSERT(is_section(c)); return m_sorted_sections[c/2]; } // Return value of root idx. // If idx == UINT_MAX, it return zero (this is a hack to simplify the infeasible_intervals method anum const & get_root(unsigned idx) const { static anum zero; if (idx == UINT_MAX) return zero; SASSERT(idx < m_sections.size()); return m_sections[idx].m_root; } static unsigned section_id_to_cell_id(unsigned section_id) { return section_id*2 + 1; } // Return the cell_id of the root i of pinfo unsigned cell_id(poly_info const & pinfo, unsigned i) const { return section_id_to_cell_id(m_sections[m_poly_sections[pinfo.m_first_section + i]].m_pos); } // Return the sign idx of pinfo int sign(poly_info const & pinfo, unsigned i) const { return m_poly_signs[pinfo.m_first_sign + i]; } #define LINEAR_SEARCH_THRESHOLD 8 int sign_at(unsigned info_id, unsigned c) const { poly_info const & pinfo = m_info[info_id]; unsigned num_roots = pinfo.m_num_roots; if (num_roots < LINEAR_SEARCH_THRESHOLD) { unsigned i = 0; for (; i < num_roots; i++) { unsigned section_cell_id = cell_id(pinfo, i); if (section_cell_id == c) return 0; else if (section_cell_id > c) break; } return sign(pinfo, i); } else { if (num_roots == 0) return sign(pinfo, 0); unsigned root_1_cell_id = cell_id(pinfo, 0); unsigned root_n_cell_id = cell_id(pinfo, num_roots - 1); if (c < root_1_cell_id) return sign(pinfo, 0); else if (c == root_1_cell_id || c == root_n_cell_id) return 0; else if (c > root_n_cell_id) return sign(pinfo, num_roots); int low = 0; int high = num_roots-1; while (true) { SASSERT(0 <= low && high < static_cast(num_roots)); SASSERT(cell_id(pinfo, low) < c); SASSERT(c < cell_id(pinfo, high)); if (high == low + 1) { SASSERT(cell_id(pinfo, low) < c); SASSERT(c < cell_id(pinfo, low+1)); return sign(pinfo, low+1); } SASSERT(high > low + 1); int mid = low + ((high - low)/2); SASSERT(low < mid && mid < high); unsigned mid_cell_id = cell_id(pinfo, mid); if (mid_cell_id == c) { return 0; } if (c < mid_cell_id) { high = mid; } else { SASSERT(mid_cell_id < c); low = mid; } } } } bool check_invariant() const { SASSERT(m_sections.size() == m_sorted_sections.size()); for (unsigned i = 0; i < m_sorted_sections.size(); i++) { SASSERT(m_sorted_sections[i] < m_sections.size()); SASSERT(m_sections[m_sorted_sections[i]].m_pos == i); } unsigned total_num_sections = 0; unsigned total_num_signs = 0; for (unsigned i = 0; i < m_info.size(); i++) { SASSERT(m_info[i].m_first_section <= m_poly_sections.size()); SASSERT(m_info[i].m_num_roots == 0 || m_info[i].m_first_section < m_poly_sections.size()); SASSERT(m_info[i].m_first_sign < m_poly_signs.size()); total_num_sections += m_info[i].m_num_roots; total_num_signs += m_info[i].m_num_roots + 1; } SASSERT(total_num_sections == m_poly_sections.size()); SASSERT(total_num_signs == m_poly_signs.size()); return true; } // Display sign table for the given variable void display(std::ostream & out) const { out << "sections:\n "; for (unsigned i = 0; i < m_sections.size(); i++) { if (i > 0) out << " < "; m_am.display_decimal(out, m_sections[m_sorted_sections[i]].m_root); } out << "\n"; out << "sign variations:\n"; for (unsigned i = 0; i < m_info.size(); i++) { out << " "; for (unsigned j = 0; j < num_cells(); j++) { if (j > 0) out << " "; int s = sign_at(i, j); if (s < 0) out << "-"; else if (s == 0) out << "0"; else out << "+"; } out << "\n"; } } // Display sign table for the given variable void display_raw(std::ostream & out) const { out << "sections:\n "; for (unsigned i = 0; i < m_sections.size(); i++) { if (i > 0) out << " < "; m_am.display_decimal(out, m_sections[m_sorted_sections[i]].m_root); } out << "\n"; out << "poly_info:\n"; for (unsigned i = 0; i < m_info.size(); i++) { out << " roots:"; poly_info const & info = m_info[i]; for (unsigned j = 0; j < info.m_num_roots; j++) { out << " "; out << m_poly_sections[info.m_first_section+j]; } out << ", signs:"; for (unsigned j = 0; j < info.m_num_roots+1; j++) { out << " "; int s = m_poly_signs[info.m_first_sign+j]; if (s < 0) out << "-"; else if (s == 0) out << "0"; else out << "+"; } out << "\n"; } } }; sign_table m_sign_table_tmp; imp(assignment const & x2v, pmanager & pm, small_object_allocator & allocator): m_assignment(x2v), m_pm(pm), m_allocator(allocator), m_am(m_assignment.am()), m_ism(m_am, allocator), m_tmp_values(m_am), m_add_roots_tmp(m_am), m_inf_tmp(m_am), m_sign_table_tmp(m_am) { } var max_var(poly const * p) const { return m_pm.max_var(p); } /** \brief Return the sign of the polynomial in the current interpration. \pre All variables of p are assigned in the current interpretation. */ int eval_sign(poly * p) { // TODO: check if it is useful to cache results SASSERT(m_assignment.is_assigned(max_var(p))); return m_am.eval_sign_at(polynomial_ref(p, m_pm), m_assignment); } bool satisfied(int sign, atom::kind k) { return (sign == 0 && (k == atom::EQ || k == atom::ROOT_EQ || k == atom::ROOT_LE || k == atom::ROOT_GE)) || (sign < 0 && (k == atom::LT || k == atom::ROOT_LT || k == atom::ROOT_LE)) || (sign > 0 && (k == atom::GT || k == atom::ROOT_GT || k == atom::ROOT_GE)); } bool satisfied(int sign, atom::kind k, bool neg) { bool r = satisfied(sign, k); return neg ? !r : r; } bool eval_ineq(ineq_atom * a, bool neg) { SASSERT(m_assignment.is_assigned(a->max_var())); // all variables of a were already assigned... atom::kind k = a->get_kind(); unsigned sz = a->size(); int sign = 1; for (unsigned i = 0; i < sz; i++) { int curr_sign = eval_sign(a->p(i)); if (a->is_even(i) && curr_sign < 0) curr_sign = 1; sign = sign * curr_sign; if (sign == 0) break; } return satisfied(sign, k, neg); } bool eval_root(root_atom * a, bool neg) { SASSERT(m_assignment.is_assigned(a->max_var())); // all variables of a were already assigned... atom::kind k = a->get_kind(); scoped_anum_vector & roots = m_tmp_values; roots.reset(); m_am.isolate_roots(polynomial_ref(a->p(), m_pm), undef_var_assignment(m_assignment, a->x()), roots); SASSERT(a->i() > 0); if (a->i() > roots.size()) return false; // p does have sufficient roots int sign = m_am.compare(m_assignment.value(a->x()), roots[a->i() - 1]); return satisfied(sign, k, neg); } bool eval(atom * a, bool neg) { return a->is_ineq_atom() ? eval_ineq(to_ineq_atom(a), neg) : eval_root(to_root_atom(a), neg); } svector m_add_signs_tmp; void add(poly * p, var x, sign_table & t) { SASSERT(m_pm.max_var(p) <= x); if (m_pm.max_var(p) < x) { t.add_const(eval_sign(p)); } else { // isolate roots of p scoped_anum_vector & roots = m_add_roots_tmp; svector & signs = m_add_signs_tmp; roots.reset(); signs.reset(); TRACE("nlsat_evaluator", tout << "x: " << x << " max_var(p): " << m_pm.max_var(p) << "\n";); // Note: I added undef_var_assignment in the following statement, to allow us to obtain the infeasible interval sets // even when the maximal variable is assigned. I need this feature to minimize conflict cores. m_am.isolate_roots(polynomial_ref(p, m_pm), undef_var_assignment(m_assignment, x), roots, signs); t.add(roots, signs); } } // Evalute the sign of p1^e1*...*pn^en (of atom a) in cell c of table t. int sign_at(ineq_atom * a, sign_table const & t, unsigned c) const { int sign = 1; unsigned num_ps = a->size(); for (unsigned i = 0; i < num_ps; i++) { int curr_sign = t.sign_at(i, c); TRACE("nlsat_evaluator_bug", tout << "sign of i: " << i << " at cell " << c << "\n"; m_pm.display(tout, a->p(i)); tout << "\nsign: " << curr_sign << "\n";); if (a->is_even(i) && curr_sign < 0) curr_sign = 1; sign = sign * curr_sign; if (sign == 0) break; } return sign; } interval_set_ref infeasible_intervals(ineq_atom * a, bool neg) { sign_table & table = m_sign_table_tmp; table.reset(); unsigned num_ps = a->size(); var x = a->max_var(); for (unsigned i = 0; i < num_ps; i++) { add(a->p(i), x, table); TRACE("nlsat_evaluator_bug", tout << "table after:\n"; m_pm.display(tout, a->p(i)); tout << "\n"; table.display_raw(tout);); } TRACE("nlsat_evaluator", tout << "sign table for:\n"; for (unsigned i = 0; i < num_ps; i++) { m_pm.display(tout, a->p(i)); tout << "\n"; } table.display(tout);); interval_set_ref result(m_ism); interval_set_ref set(m_ism); literal jst(a->bvar(), neg); atom::kind k = a->get_kind(); anum dummy; bool prev_sat = true; bool prev_inf = true; bool prev_open = true; unsigned prev_root_id = UINT_MAX; unsigned num_cells = table.num_cells(); for (unsigned c = 0; c < num_cells; c++) { TRACE("nlsat_evaluator", tout << "cell: " << c << "\n"; tout << "prev_sat: " << prev_sat << "\n"; tout << "prev_inf: " << prev_inf << "\n"; tout << "prev_open: " << prev_open << "\n"; tout << "prev_root_id: " << prev_root_id << "\n"; tout << "processing cell: " << c << "\n"; tout << "interval_set so far:\n" << result << "\n";); int sign = sign_at(a, table, c); TRACE("nlsat_evaluator", tout << "sign: " << sign << "\n";); if (satisfied(sign, k, neg)) { // current cell is satisfied if (!prev_sat) { SASSERT(c > 0); // add interval bool curr_open; unsigned curr_root_id; if (table.is_section(c)) { curr_open = true; curr_root_id = table.get_root_id(c); } else { SASSERT(table.is_section(c-1)); curr_open = false; curr_root_id = table.get_root_id(c-1); } set = m_ism.mk(prev_open, prev_inf, table.get_root(prev_root_id), curr_open, false, table.get_root(curr_root_id), jst); result = m_ism.mk_union(result, set); prev_sat = true; } } else { // current cell is not satisfied if (prev_sat) { if (c == 0) { if (num_cells == 1) { // (-oo, oo) result = m_ism.mk(true, true, dummy, true, true, dummy, jst); } else { // save -oo as begining of infeasible interval prev_open = true; prev_inf = true; prev_root_id = UINT_MAX; } } else { SASSERT(c > 0); prev_inf = false; if (table.is_section(c)) { prev_open = false; prev_root_id = table.get_root_id(c); TRACE("nlsat_evaluator", tout << "updated prev_root_id: " << prev_root_id << " using cell: " << c << "\n";); } else { SASSERT(table.is_section(c-1)); prev_open = true; prev_root_id = table.get_root_id(c-1); TRACE("nlsat_evaluator", tout << "updated prev_root_id: " << prev_root_id << " using cell: " << (c - 1) << "\n";); } } prev_sat = false; } if (c == num_cells - 1) { // last cell add interval with (prev, oo) set = m_ism.mk(prev_open, prev_inf, table.get_root(prev_root_id), true, true, dummy, jst); result = m_ism.mk_union(result, set); } } } TRACE("nlsat_evaluator", tout << "interval_set: " << result << "\n";); return result; } interval_set_ref infeasible_intervals(root_atom * a, bool neg) { atom::kind k = a->get_kind(); unsigned i = a->i(); SASSERT(i > 0); literal jst(a->bvar(), neg); anum dummy; scoped_anum_vector & roots = m_tmp_values; roots.reset(); var x = a->max_var(); // Note: I added undef_var_assignment in the following statement, to allow us to obtain the infeasible interval sets // even when the maximal variable is assigned. I need this feature to minimize conflict cores. m_am.isolate_roots(polynomial_ref(a->p(), m_pm), undef_var_assignment(m_assignment, x), roots); interval_set_ref result(m_ism); if (i > roots.size()) { // p does have sufficient roots // atom is false by definition if (neg) { result = m_ism.mk_empty(); } else { result = m_ism.mk(true, true, dummy, true, true, dummy, jst); // (-oo, oo) } } else { anum const & r_i = roots[i-1]; switch (k) { case atom::ROOT_EQ: if (neg) { result = m_ism.mk(false, false, r_i, false, false, r_i, jst); // [r_i, r_i] } else { interval_set_ref s1(m_ism), s2(m_ism); s1 = m_ism.mk(true, true, dummy, true, false, r_i, jst); // (-oo, r_i) s2 = m_ism.mk(true, false, r_i, true, true, dummy, jst); // (r_i, oo) result = m_ism.mk_union(s1, s2); } break; case atom::ROOT_LT: if (neg) result = m_ism.mk(true, true, dummy, true, false, r_i, jst); // (-oo, r_i) else result = m_ism.mk(false, false, r_i, true, true, dummy, jst); // [r_i, oo) break; case atom::ROOT_GT: if (neg) result = m_ism.mk(true, false, r_i, true, true, dummy, jst); // (r_i, oo) else result = m_ism.mk(true, true, dummy, false, false, r_i, jst); // (-oo, r_i] break; case atom::ROOT_LE: if (neg) result = m_ism.mk(true, true, dummy, false, false, r_i, jst); // (-oo, r_i] else result = m_ism.mk(true, false, r_i, true, true, dummy, jst); // (r_i, oo) break; case atom::ROOT_GE: if (neg) result = m_ism.mk(false, false, r_i, true, true, dummy, jst); // [r_i, oo) else result = m_ism.mk(true, true, dummy, true, false, r_i, jst); // (-oo, r_i) break; default: UNREACHABLE(); break; } } TRACE("nlsat_evaluator", tout << "interval_set: " << result << "\n";); return result; } interval_set_ref infeasible_intervals(atom * a, bool neg) { return a->is_ineq_atom() ? infeasible_intervals(to_ineq_atom(a), neg) : infeasible_intervals(to_root_atom(a), neg); } }; evaluator::evaluator(assignment const & x2v, pmanager & pm, small_object_allocator & allocator) { m_imp = alloc(imp, x2v, pm, allocator); } evaluator::~evaluator() { dealloc(m_imp); } interval_set_manager & evaluator::ism() const { return m_imp->m_ism; } bool evaluator::eval(atom * a, bool neg) { return m_imp->eval(a, neg); } interval_set_ref evaluator::infeasible_intervals(atom * a, bool neg) { return m_imp->infeasible_intervals(a, neg); } void evaluator::push() { // do nothing } void evaluator::pop(unsigned num_scopes) { // do nothing } }; z3-4.4.0/src/nlsat/nlsat_clause.h0000644000175000017500000000362612540347414016412 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_clause.h Abstract: Clauses used in the Nonlinear arithmetic satisfiability procedure. Author: Leonardo de Moura (leonardo) 2012-01-02. Revision History: --*/ #ifndef _NLSAT_CLAUSE_H_ #define _NLSAT_CLAUSE_H_ #include"nlsat_types.h" #include"vector.h" namespace nlsat { class clause { friend class solver; unsigned m_id; unsigned m_size; unsigned m_capacity:31; unsigned m_learned:1; unsigned m_activity; assumption_set m_assumptions; literal m_lits[0]; static size_t get_obj_size(unsigned num_lits) { return sizeof(clause) + num_lits * sizeof(literal); } size_t get_size() const { return get_obj_size(m_capacity); } clause(unsigned id, unsigned sz, literal const * lits, bool learned, assumption_set as); public: unsigned size() const { return m_size; } unsigned id() const { return m_id; } literal & operator[](unsigned idx) { SASSERT(idx < m_size); return m_lits[idx]; } literal const & operator[](unsigned idx) const { SASSERT(idx < m_size); return m_lits[idx]; } bool is_learned() const { return m_learned; } literal * begin() { return m_lits; } literal * end() { return m_lits + m_size; } literal const * c_ptr() const { return m_lits; } void inc_activity() { m_activity++; } void set_activity(unsigned v) { m_activity = v; } unsigned get_activity() const { return m_activity; } bool contains(literal l) const; bool contains(bool_var v) const; void shrink(unsigned num_lits) { SASSERT(num_lits <= m_size); if (num_lits < m_size) { m_size = num_lits; } } assumption_set assumptions() const { return m_assumptions; } }; typedef ptr_vector clause_vector; }; #endif z3-4.4.0/src/opt/0000755000175000017500000000000012540347414013236 5ustar michaelmichaelz3-4.4.0/src/opt/wmax.cpp0000644000175000017500000000435612540347414014726 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: wmax.cpp Abstract: Theory based MaxSAT. Author: Nikolaj Bjorner (nbjorner) 2014-4-17 Notes: --*/ #include "wmax.h" #include "uint_set.h" #include "ast_pp.h" #include "model_smt2_pp.h" #include "smt_theory.h" #include "smt_context.h" #include "theory_wmaxsat.h" #include "opt_context.h" namespace opt { // ---------------------------------------------------------- // weighted max-sat using a custom theory solver for max-sat. // NB. it is quite similar to pseudo-Boolean propagation. class wmax : public maxsmt_solver_base { public: wmax(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): maxsmt_solver_base(c, ws, soft) {} virtual ~wmax() {} lbool operator()() { TRACE("opt", tout << "weighted maxsat\n";); scoped_ensure_theory wth(*this); lbool is_sat = l_true; bool was_sat = false; for (unsigned i = 0; i < m_soft.size(); ++i) { wth().assert_weighted(m_soft[i], m_weights[i]); } while (l_true == is_sat) { is_sat = s().check_sat(0,0); if (m_cancel) { is_sat = l_undef; } if (is_sat == l_true) { if (wth().is_optimal()) { m_upper = wth().get_min_cost(); s().get_model(m_model); } expr_ref fml = wth().mk_block(); s().assert_expr(fml); was_sat = true; } trace_bounds("wmax"); } if (was_sat) { wth().get_assignment(m_assignment); } if (is_sat == l_false && was_sat) { is_sat = l_true; } m_upper = wth().get_min_cost(); if (is_sat == l_true) { m_lower = m_upper; } TRACE("opt", tout << "min cost: " << m_upper << "\n";); return is_sat; } }; maxsmt_solver_base* mk_wmax(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { return alloc(wmax, c, ws, soft); } } z3-4.4.0/src/opt/opt_cmds.cpp0000644000175000017500000002304412540347414015555 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: opt_cmds.cpp Abstract: Commands for optimization benchmarks Author: Anh-Dung Phan (t-anphan) 2013-10-14 Notes: TODO: - Add appropriate statistics tracking to opt::context - Deal with push/pop (later) --*/ #include "opt_cmds.h" #include "cmd_context.h" #include "ast_pp.h" #include "opt_context.h" #include "cancel_eh.h" #include "scoped_ctrl_c.h" #include "scoped_timer.h" #include "parametric_cmd.h" #include "opt_params.hpp" #include "model_smt2_pp.h" static opt::context& get_opt(cmd_context& cmd) { if (!cmd.get_opt()) { cmd.set_opt(alloc(opt::context, cmd.m())); } return dynamic_cast(*cmd.get_opt()); } class assert_weighted_cmd : public cmd { unsigned m_idx; expr* m_formula; rational m_weight; symbol m_id; public: assert_weighted_cmd(): cmd("assert-weighted"), m_idx(0), m_formula(0), m_weight(0) {} virtual ~assert_weighted_cmd() { } virtual void reset(cmd_context & ctx) { m_idx = 0; m_formula = 0; m_id = symbol::null; } virtual char const * get_usage() const { return " "; } virtual char const * get_descr(cmd_context & ctx) const { return "assert soft constraint with weight"; } virtual unsigned get_arity() const { return VAR_ARITY; } // command invocation virtual void prepare(cmd_context & ctx) {} virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { switch(m_idx) { case 0: return CPK_EXPR; case 1: return CPK_NUMERAL; default: return CPK_SYMBOL; } } virtual void set_next_arg(cmd_context & ctx, rational const & val) { SASSERT(m_idx == 1); if (!val.is_pos()) { throw cmd_exception("Invalid weight. Weights must be positive."); } m_weight = val; ++m_idx; } virtual void set_next_arg(cmd_context & ctx, expr * t) { SASSERT(m_idx == 0); if (!ctx.m().is_bool(t)) { throw cmd_exception("Invalid type for expression. Expected Boolean type."); } m_formula = t; ++m_idx; } virtual void set_next_arg(cmd_context & ctx, symbol const& s) { SASSERT(m_idx > 1); m_id = s; ++m_idx; } virtual void failure_cleanup(cmd_context & ctx) { reset(ctx); } virtual void execute(cmd_context & ctx) { get_opt(ctx).add_soft_constraint(m_formula, m_weight, m_id); reset(ctx); } virtual void finalize(cmd_context & ctx) { } }; class assert_soft_cmd : public parametric_cmd { unsigned m_idx; expr* m_formula; public: assert_soft_cmd(): parametric_cmd("assert-soft"), m_idx(0), m_formula(0) {} virtual ~assert_soft_cmd() { } virtual void reset(cmd_context & ctx) { m_idx = 0; m_formula = 0; } virtual char const * get_usage() const { return " [:weight ] [:id ]"; } virtual char const * get_main_descr() const { return "assert soft constraint with optional weight and identifier"; } // command invocation virtual void prepare(cmd_context & ctx) { reset(ctx); } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { if (m_idx == 0) return CPK_EXPR; return parametric_cmd::next_arg_kind(ctx); } virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { p.insert("weight", CPK_NUMERAL, "(default: 1) penalty of not satisfying constraint."); p.insert("dweight", CPK_DECIMAL, "(default: 1.0) penalty as double of not satisfying constraint."); p.insert("id", CPK_SYMBOL, "(default: null) partition identifier for soft constraints."); } virtual void set_next_arg(cmd_context & ctx, expr * t) { SASSERT(m_idx == 0); if (!ctx.m().is_bool(t)) { throw cmd_exception("Invalid type for expression. Expected Boolean type."); } m_formula = t; ++m_idx; } virtual void failure_cleanup(cmd_context & ctx) { reset(ctx); } virtual void execute(cmd_context & ctx) { symbol w("weight"); rational weight = ps().get_rat(symbol("weight"), rational(0)); if (weight.is_zero()) { weight = ps().get_rat(symbol("dweight"), rational(0)); } if (weight.is_zero()) { weight = rational::one(); } symbol id = ps().get_sym(symbol("id"), symbol::null); get_opt(ctx).add_soft_constraint(m_formula, weight, id); reset(ctx); } virtual void finalize(cmd_context & ctx) { } }; class min_maximize_cmd : public cmd { bool m_is_max; public: min_maximize_cmd(bool is_max): cmd(is_max?"maximize":"minimize"), m_is_max(is_max) {} virtual void reset(cmd_context & ctx) { } virtual char const * get_usage() const { return ""; } virtual char const * get_descr(cmd_context & ctx) const { return "check sat modulo objective function";} virtual unsigned get_arity() const { return 1; } virtual void prepare(cmd_context & ctx) {} virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_EXPR; } virtual void set_next_arg(cmd_context & ctx, expr * t) { if (!is_app(t)) { throw cmd_exception("malformed objective term: it cannot be a quantifier or bound variable"); } get_opt(ctx).add_objective(to_app(t), m_is_max); } virtual void failure_cleanup(cmd_context & ctx) { reset(ctx); } virtual void execute(cmd_context & ctx) { } }; class optimize_cmd : public parametric_cmd { public: optimize_cmd(): parametric_cmd("optimize") {} virtual ~optimize_cmd() { } virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { insert_timeout(p); insert_max_memory(p); p.insert("print_statistics", CPK_BOOL, "(default: false) print statistics."); opt::context::collect_param_descrs(p); } virtual char const * get_main_descr() const { return "check sat modulo objective function";} virtual char const * get_usage() const { return "( )*"; } virtual void prepare(cmd_context & ctx) { parametric_cmd::prepare(ctx); } virtual void failure_cleanup(cmd_context & ctx) { reset(ctx); } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return parametric_cmd::next_arg_kind(ctx); } virtual void execute(cmd_context & ctx) { params_ref p = ctx.params().merge_default_params(ps()); opt::context& opt = get_opt(ctx); opt.updt_params(p); unsigned timeout = p.get_uint("timeout", UINT_MAX); ptr_vector::const_iterator it = ctx.begin_assertions(); ptr_vector::const_iterator end = ctx.end_assertions(); for (; it != end; ++it) { opt.add_hard_constraint(*it); } lbool r = l_undef; cancel_eh eh(opt); { scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); cmd_context::scoped_watch sw(ctx); try { r = opt.optimize(); if (r == l_true && opt.is_pareto()) { while (r == l_true) { display_result(ctx); ctx.regular_stream() << "\n"; r = opt.optimize(); } if (p.get_bool("print_statistics", false)) { display_statistics(ctx); } return; } } catch (z3_error& ex) { ctx.regular_stream() << "(error: " << ex.msg() << "\")" << std::endl; } catch (z3_exception& ex) { ctx.regular_stream() << "(error: " << ex.msg() << "\")" << std::endl; } } switch(r) { case l_true: { ctx.regular_stream() << "sat\n"; display_result(ctx); break; } case l_false: ctx.regular_stream() << "unsat\n"; break; case l_undef: ctx.regular_stream() << "unknown\n"; display_result(ctx); break; } if (p.get_bool("print_statistics", false)) { display_statistics(ctx); } } void display_result(cmd_context & ctx) { params_ref p = ctx.params().merge_default_params(ps()); opt::context& opt = get_opt(ctx); opt.display_assignment(ctx.regular_stream()); opt_params optp(p); if (optp.print_model()) { model_ref mdl; opt.get_model(mdl); if (mdl) { ctx.regular_stream() << "(model " << std::endl; model_smt2_pp(ctx.regular_stream(), ctx, *(mdl.get()), 2); ctx.regular_stream() << ")" << std::endl; } } } private: void display_statistics(cmd_context& ctx) { statistics stats; get_opt(ctx).collect_statistics(stats); stats.update("time", ctx.get_seconds()); stats.display_smt2(ctx.regular_stream()); } }; void install_opt_cmds(cmd_context & ctx) { ctx.insert(alloc(assert_weighted_cmd)); ctx.insert(alloc(assert_soft_cmd)); ctx.insert(alloc(min_maximize_cmd, true)); ctx.insert(alloc(min_maximize_cmd, false)); ctx.insert(alloc(optimize_cmd)); } z3-4.4.0/src/opt/mus.cpp0000644000175000017500000001412212540347414014546 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: mus.cpp Abstract: MUS extraction. Author: Nikolaj Bjorner (nbjorner) 2014-20-7 Notes: --*/ #include "solver.h" #include "smt_literal.h" #include "mus.h" #include "ast_pp.h" #include "ast_util.h" using namespace opt; // struct mus::imp { solver& m_s; ast_manager& m; expr_ref_vector m_cls2expr; obj_map m_expr2cls; volatile bool m_cancel; model_ref m_model; expr_ref_vector m_soft; vector m_weights; rational m_weight; imp(solver& s, ast_manager& m): m_s(s), m(m), m_cls2expr(m), m_cancel(false), m_soft(m) {} void reset() { m_cls2expr.reset(); m_expr2cls.reset(); } void set_cancel(bool f) { m_cancel = f; } unsigned add_soft(expr* cls) { SASSERT(is_uninterp_const(cls) || (m.is_not(cls) && is_uninterp_const(to_app(cls)->get_arg(0)))); unsigned idx = m_cls2expr.size(); m_expr2cls.insert(cls, idx); m_cls2expr.push_back(cls); TRACE("opt", tout << idx << ": " << mk_pp(cls, m) << "\n"; display_vec(tout, m_cls2expr);); return idx; } lbool get_mus(unsigned_vector& mus) { // SASSERT: mus does not have duplicates. m_model.reset(); unsigned_vector core; for (unsigned i = 0; i < m_cls2expr.size(); ++i) { core.push_back(i); } if (core.size() == 1) { mus.push_back(core.back()); return l_true; } mus.reset(); expr_ref_vector assumptions(m); ptr_vector core_exprs; while (!core.empty()) { IF_VERBOSE(2, verbose_stream() << "(opt.mus reducing core: " << core.size() << " new core: " << mus.size() << ")\n";); unsigned cls_id = core.back(); TRACE("opt", display_vec(tout << "core: ", core); display_vec(tout << "mus: ", mus); ); core.pop_back(); expr* cls = m_cls2expr[cls_id].get(); expr_ref not_cls(m); not_cls = mk_not(m, cls); unsigned sz = assumptions.size(); assumptions.push_back(not_cls); add_core(core, assumptions); lbool is_sat = m_s.check_sat(assumptions.size(), assumptions.c_ptr()); assumptions.resize(sz); switch (is_sat) { case l_undef: return is_sat; case l_true: assumptions.push_back(cls); mus.push_back(cls_id); update_model(); break; default: core_exprs.reset(); m_s.get_unsat_core(core_exprs); if (!core_exprs.contains(not_cls)) { // core := core_exprs \ mus core.reset(); for (unsigned i = 0; i < core_exprs.size(); ++i) { cls = core_exprs[i]; cls_id = m_expr2cls.find(cls); if (!mus.contains(cls_id)) { core.push_back(cls_id); } } TRACE("opt", display_vec(tout << "core exprs:", core_exprs); display_vec(tout << "core:", core); display_vec(tout << "mus:", mus); ); } break; } } #if 0 DEBUG_CODE( assumptions.reset(); for (unsigned i = 0; i < mus.size(); ++i) { assumptions.push_back(m_cls2expr[mus[i]].get()); } lbool is_sat = m_s.check_sat(assumptions.size(), assumptions.c_ptr()); SASSERT(is_sat == l_false); ); #endif return l_true; } void add_core(unsigned_vector const& core, expr_ref_vector& assumptions) { for (unsigned i = 0; i < core.size(); ++i) { assumptions.push_back(m_cls2expr[core[i]].get()); } } template void display_vec(std::ostream& out, T const& v) const { for (unsigned i = 0; i < v.size(); ++i) { out << v[i] << " "; } out << "\n"; } void display_vec(std::ostream& out, expr_ref_vector const& v) const { for (unsigned i = 0; i < v.size(); ++i) { out << mk_pp(v[i], m) << " "; } out << "\n"; } void display_vec(std::ostream& out, ptr_vector const& v) const { for (unsigned i = 0; i < v.size(); ++i) { out << mk_pp(v[i], m) << " "; } out << "\n"; } void set_soft(unsigned sz, expr* const* soft, rational const* weights) { m_model.reset(); m_weight.reset(); m_soft.append(sz, soft); m_weights.append(sz, weights); for (unsigned i = 0; i < sz; ++i) { m_weight += weights[i]; } } void update_model() { if (m_soft.empty()) return; model_ref mdl; expr_ref tmp(m); m_s.get_model(mdl); rational w; for (unsigned i = 0; i < m_soft.size(); ++i) { mdl->eval(m_soft[i].get(), tmp); if (!m.is_true(tmp)) { w += m_weights[i]; } } if (w < m_weight || !m_model.get()) { m_model = mdl; m_weight = w; } } rational get_best_model(model_ref& mdl) { mdl = m_model; return m_weight; } }; mus::mus(solver& s, ast_manager& m) { m_imp = alloc(imp, s, m); } mus::~mus() { dealloc(m_imp); } unsigned mus::add_soft(expr* cls) { return m_imp->add_soft(cls); } lbool mus::get_mus(unsigned_vector& mus) { return m_imp->get_mus(mus); } void mus::set_cancel(bool f) { m_imp->set_cancel(f); } void mus::reset() { m_imp->reset(); } void mus::set_soft(unsigned sz, expr* const* soft, rational const* weights) { m_imp->set_soft(sz, soft, weights); } rational mus::get_best_model(model_ref& mdl) { return m_imp->get_best_model(mdl); } z3-4.4.0/src/opt/maxhs.cpp0000644000175000017500000004603312540347414015070 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: maxhs.cpp Abstract: maxhs based MaxSAT. Author: Nikolaj Bjorner (nbjorner) 2014-4-17 Notes: --*/ #include "optsmt.h" #include "hitting_sets.h" #include "stopwatch.h" #include "ast_pp.h" #include "model_smt2_pp.h" #include "uint_set.h" #include "maxhs.h" #include "opt_context.h" namespace opt { class scoped_stopwatch { double& m_time; stopwatch m_watch; public: scoped_stopwatch(double& time): m_time(time) { m_watch.start(); } ~scoped_stopwatch() { m_watch.stop(); m_time += m_watch.get_seconds(); } }; // ---------------------------------- // MaxSatHS+MSS // variant of MaxSAT-HS (Algorithm 9) // that also refines upper bound during progressive calls // to the underlying optimization solver for the soft constraints. // class maxhs : public maxsmt_solver_base { struct stats { stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } unsigned m_num_iterations; unsigned m_num_core_reductions_success; unsigned m_num_core_reductions_failure; unsigned m_num_model_expansions_success; unsigned m_num_model_expansions_failure; double m_core_reduction_time; double m_model_expansion_time; double m_aux_sat_time; double m_disjoint_cores_time; }; hitting_sets m_hs; expr_ref_vector m_aux; // auxiliary (indicator) variables. obj_map m_aux2index; // expr |-> index unsigned_vector m_core_activity; // number of times soft constraint is used in a core. svector m_seed; // clause selected in current model. svector m_aux_active; // active soft clauses. ptr_vector m_asms; // assumptions (over aux) stats m_stats; bool m_at_lower_bound; public: maxhs(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): maxsmt_solver_base(c, ws, soft), m_aux(m), m_at_lower_bound(false) { } virtual ~maxhs() {} virtual void set_cancel(bool f) { maxsmt_solver_base::set_cancel(f); m_hs.set_cancel(f); } virtual void collect_statistics(statistics& st) const { maxsmt_solver_base::collect_statistics(st); m_hs.collect_statistics(st); st.update("maxhs-num-iterations", m_stats.m_num_iterations); st.update("maxhs-num-core-reductions-n", m_stats.m_num_core_reductions_failure); st.update("maxhs-num-core-reductions-y", m_stats.m_num_core_reductions_success); st.update("maxhs-num-model-expansions-n", m_stats.m_num_model_expansions_failure); st.update("maxhs-num-model-expansions-y", m_stats.m_num_model_expansions_success); st.update("maxhs-core-reduction-time", m_stats.m_core_reduction_time); st.update("maxhs-model-expansion-time", m_stats.m_model_expansion_time); st.update("maxhs-aux-sat-time", m_stats.m_aux_sat_time); st.update("maxhs-disj-core-time", m_stats.m_disjoint_cores_time); } lbool operator()() { ptr_vector hs; init(); init_local(); if (!disjoint_cores(hs)) { return l_undef; } seed2assumptions(); while (m_lower < m_upper) { ++m_stats.m_num_iterations; trace_bounds("maxhs"); TRACE("opt", tout << "(maxhs [" << m_lower << ":" << m_upper << "])\n";); if (m_cancel) { return l_undef; } lbool core_found = generate_cores(hs); switch(core_found) { case l_undef: return l_undef; case l_true: { lbool is_sat = next_seed(); switch(is_sat) { case l_true: seed2hs(false, hs); break; case l_false: TRACE("opt", tout << "no more seeds\n";); IF_VERBOSE(1, verbose_stream() << "(opt.maxhs.no-more-seeds)\n";); m_lower = m_upper; return l_true; case l_undef: return l_undef; } break; } case l_false: IF_VERBOSE(1, verbose_stream() << "(opt.maxhs.no-more-cores)\n";); TRACE("opt", tout << "no more cores\n";); m_lower = m_upper; return l_true; } } return l_true; } private: unsigned num_soft() const { return m_soft.size(); } void init_local() { unsigned sz = num_soft(); app_ref fml(m), obj(m); expr_ref_vector sum(m); m_asms.reset(); m_seed.reset(); m_aux.reset(); m_aux_active.reset(); m_aux2index.reset(); m_core_activity.reset(); for (unsigned i = 0; i < sz; ++i) { bool tt = is_true(m_model, m_soft[i]); m_seed.push_back(tt); m_aux. push_back(mk_fresh(m.mk_bool_sort())); m_aux_active.push_back(false); m_core_activity.push_back(0); m_aux2index.insert(m_aux.back(), i); if (tt) { m_asms.push_back(m_aux.back()); ensure_active(i); } } for (unsigned i = 0; i < m_weights.size(); ++i) { m_hs.add_weight(m_weights[i]); } TRACE("opt", print_seed(tout);); } void hs2seed(ptr_vector const& hs) { for (unsigned i = 0; i < num_soft(); ++i) { m_seed[i] = true; } for (unsigned i = 0; i < hs.size(); ++i) { m_seed[m_aux2index.find(hs[i])] = false; } TRACE("opt", print_asms(tout << "hitting set: ", hs); print_seed(tout);); } void seed2hs(bool pos, ptr_vector& hs) { hs.reset(); for (unsigned i = 0; i < num_soft(); ++i) { if (pos == m_seed[i]) { hs.push_back(m_aux[i].get()); } } TRACE("opt", print_asms(tout << "hitting set: ", hs); print_seed(tout);); } void seed2assumptions() { seed2hs(true, m_asms); } // // Find disjoint cores for soft constraints. // bool disjoint_cores(ptr_vector& hs) { scoped_stopwatch _sw(m_stats.m_disjoint_cores_time); m_asms.reset(); svector active(num_soft(), true); rational lower(0); update_assumptions(active, lower, hs); SASSERT(lower.is_zero()); while (true) { lbool is_sat = s().check_sat(m_asms.size(), m_asms.c_ptr()); switch (is_sat) { case l_true: if (lower > m_lower) { m_lower = lower; } return true; case l_false: if (!shrink()) return false; block_up(); update_assumptions(active, lower, hs); break; case l_undef: return false; } } } void update_assumptions(svector& active, rational& lower, ptr_vector& hs) { rational arg_min(0); expr* e = 0; for (unsigned i = 0; i < m_asms.size(); ++i) { unsigned index = m_aux2index.find(m_asms[i]); active[index] = false; if (arg_min.is_zero() || arg_min > m_weights[index]) { arg_min = m_weights[index]; e = m_asms[i]; } } if (e) { hs.push_back(e); lower += arg_min; } m_asms.reset(); for (unsigned i = 0; i < num_soft(); ++i) { if (active[i]) { m_asms.push_back(m_aux[i].get()); ensure_active(i); } } } // // Auxiliary Algorithm 10 for producing cores. // lbool generate_cores(ptr_vector& hs) { bool core = !m_at_lower_bound; while (true) { hs2seed(hs); lbool is_sat = check_subset(); switch(is_sat) { case l_undef: return l_undef; case l_true: if (!grow()) return l_undef; block_down(); return core?l_true:l_false; case l_false: core = true; if (!shrink()) return l_undef; block_up(); find_non_optimal_hitting_set(hs); break; } } } struct lt_activity { maxhs& hs; lt_activity(maxhs& hs):hs(hs) {} bool operator()(expr* a, expr* b) const { unsigned w1 = hs.m_core_activity[hs.m_aux2index.find(a)]; unsigned w2 = hs.m_core_activity[hs.m_aux2index.find(b)]; return w1 < w2; } }; // // produce the non-optimal hitting set by using the 10% heuristic. // of most active cores constraints. // m_asms contains the current core. // void find_non_optimal_hitting_set(ptr_vector& hs) { std::sort(m_asms.begin(), m_asms.end(), lt_activity(*this)); for (unsigned i = m_asms.size(); i > 9*m_asms.size()/10;) { --i; hs.push_back(m_asms[i]); } } // // retrieve the next seed that satisfies state of hs. // state of hs must be satisfiable before optimization is called. // lbool next_seed() { scoped_stopwatch _sw(m_stats.m_aux_sat_time); TRACE("opt", tout << "\n";); // min c_i*(not x_i) for x_i are soft clauses. // max c_i*x_i for x_i are soft clauses m_at_lower_bound = false; lbool is_sat = m_hs.compute_upper(); if (is_sat == l_true) { is_sat = m_hs.compute_lower(); } if (is_sat == l_true) { m_at_lower_bound = m_hs.get_upper() == m_hs.get_lower(); if (m_hs.get_lower() > m_lower) { m_lower = m_hs.get_lower(); } for (unsigned i = 0; i < num_soft(); ++i) { m_seed[i] = is_active(i) && !m_hs.get_value(i); } TRACE("opt", print_seed(tout);); } return is_sat; } // // check assignment returned by HS with the original // hard constraints. // lbool check_subset() { TRACE("opt", tout << "\n";); m_asms.reset(); for (unsigned i = 0; i < num_soft(); ++i) { if (m_seed[i]) { m_asms.push_back(m_aux[i].get()); ensure_active(i); } } return s().check_sat(m_asms.size(), m_asms.c_ptr()); } // // extend the current assignment to one that // satisfies as many soft constraints as possible. // update the upper bound based on this assignment // bool grow() { scoped_stopwatch _sw(m_stats.m_model_expansion_time); model_ref mdl; s().get_model(mdl); for (unsigned i = 0; i < num_soft(); ++i) { ensure_active(i); m_seed[i] = false; } for (unsigned i = 0; i < m_asms.size(); ++i) { m_seed[m_aux2index.find(m_asms[i])] = true; } for (unsigned i = 0; i < num_soft(); ++i) { if (m_seed[i]) { // already an assumption } else if (is_true(mdl, m_soft[i])) { m_seed[i] = true; m_asms.push_back(m_aux[i].get()); } else { m_asms.push_back(m_aux[i].get()); lbool is_sat = s().check_sat(m_asms.size(), m_asms.c_ptr()); switch(is_sat) { case l_undef: return false; case l_false: ++m_stats.m_num_model_expansions_failure; m_asms.pop_back(); break; case l_true: ++m_stats.m_num_model_expansions_success; s().get_model(mdl); m_seed[i] = true; break; } } } rational upper(0); for (unsigned i = 0; i < num_soft(); ++i) { if (!m_seed[i]) { upper += m_weights[i]; } } if (upper < m_upper) { m_upper = upper; m_hs.set_upper(upper); m_model = mdl; m_assignment.reset(); m_assignment.append(m_seed); TRACE("opt", tout << "new upper: " << m_upper << "\n"; model_smt2_pp(tout, m, *(mdl.get()), 0);); } DEBUG_CODE( for (unsigned i = 0; i < num_soft(); ++i) { SASSERT(is_true(mdl, m_soft[i]) == m_seed[i]); }); return true; } // // remove soft constraints from the current core. // bool shrink() { scoped_stopwatch _sw(m_stats.m_core_reduction_time); m_asms.reset(); s().get_unsat_core(m_asms); TRACE("opt", print_asms(tout, m_asms);); obj_map asm2index; for (unsigned i = 0; i < m_asms.size(); ++i) { asm2index.insert(m_asms[i], i); } obj_map::iterator it = asm2index.begin(), end = asm2index.end(); for (; it != end; ++it) { unsigned i = it->m_value; if (i < m_asms.size()) { expr* tmp = m_asms[i]; expr* back = m_asms.back(); m_asms[i] = back; m_asms.pop_back(); lbool is_sat = s().check_sat(m_asms.size(), m_asms.c_ptr()); TRACE("opt", tout << "checking: " << mk_pp(tmp, m) << ": " << is_sat << "\n";); switch(is_sat) { case l_true: ++m_stats.m_num_core_reductions_failure; // put back literal into core m_asms.push_back(back); m_asms[i] = tmp; break; case l_false: // update the core m_asms.reset(); ++m_stats.m_num_core_reductions_success; s().get_unsat_core(m_asms); TRACE("opt", print_asms(tout, m_asms);); update_index(asm2index); break; case l_undef: return false; } } } return true; } void print_asms(std::ostream& out, ptr_vector const& asms) { for (unsigned j = 0; j < asms.size(); ++j) { out << mk_pp(asms[j], m) << " "; } out << "\n"; } void print_seed(std::ostream& out) { out << "seed: "; for (unsigned i = 0; i < num_soft(); ++i) { out << (m_seed[i]?"1":"0"); } out << "\n"; } // // must include some literal not from asms. // (furthermore, update upper bound constraint in HS) // void block_down() { uint_set indices; unsigned_vector c_indices; for (unsigned i = 0; i < m_asms.size(); ++i) { indices.insert(m_aux2index.find(m_asms[i])); } for (unsigned i = 0; i < num_soft(); ++i) { if (!indices.contains(i)) { c_indices.push_back(i); } } m_hs.add_exists_false(c_indices.size(), c_indices.c_ptr()); } // should exclude some literal from core. void block_up() { unsigned_vector indices; for (unsigned i = 0; i < m_asms.size(); ++i) { unsigned index = m_aux2index.find(m_asms[i]); m_core_activity[index]++; indices.push_back(index); } m_hs.add_exists_true(indices.size(), indices.c_ptr()); } void update_index(obj_map& asm2index) { obj_map::iterator it = asm2index.begin(), end = asm2index.end(); for (; it != end; ++it) { it->m_value = UINT_MAX; } for (unsigned i = 0; i < m_asms.size(); ++i) { asm2index.find(m_asms[i]) = i; } } app_ref mk_fresh(sort* s) { app_ref r(m); r = m.mk_fresh_const("r", s); m_c.fm().insert(r->get_decl()); return r; } bool is_true(model_ref& mdl, expr* e) { expr_ref val(m); VERIFY(mdl->eval(e, val)); return m.is_true(val); } bool is_active(unsigned i) const { return m_aux_active[i]; } void ensure_active(unsigned i) { if (!is_active(i)) { expr_ref fml(m); fml = m.mk_implies(m_aux[i].get(), m_soft[i]); s().assert_expr(fml); m_aux_active[i] = true; } } }; maxsmt_solver_base* mk_maxhs( maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { return alloc(maxhs, c, ws, soft); } } z3-4.4.0/src/opt/mss.h0000644000175000017500000000256512540347414014221 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: mss.h Abstract: Maximal satisfying subset/minimal correction sets: MSS/MCS Author: Nikolaj Bjorner (nbjorner) 2014-2-8 Notes: --*/ #ifndef _MSS_H_ #define _MSS_H_ namespace opt { class mss { solver& m_s; ast_manager& m; volatile bool m_cancel; typedef ptr_vector exprs; typedef obj_hashtable expr_set; exprs m_mss; expr_set m_mcs; expr_set m_mss_set; vector m_cores; exprs m_todo; model_ref m_model; public: mss(solver& s, ast_manager& m); ~mss(); lbool operator()(model* initial_model, vector const& cores, exprs& literals, exprs& mcs); void set_cancel(bool f) { m_cancel = f; } void get_model(model_ref& mdl) { mdl = m_model; } private: void initialize(exprs& literals); bool check_result(); void add_mss(expr* n); void update_mss(); void update_core(exprs& core); lbool process_core(unsigned sz, exprs& core, bool& has_mcs, bool is_last); void display(std::ostream& out) const; void display_vec(std::ostream& out, unsigned sz, expr* const* args) const; bool check_invariant() const; }; }; #endif z3-4.4.0/src/opt/maxsls.h0000644000175000017500000000076412540347414014725 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: maxsls.h Abstract: Weighted SLS MAXSAT module Author: Nikolaj Bjorner (nbjorner) 2014-4-17 Notes: Partial, one-round SLS optimizer. Finds the first local maximum given a resource bound and returns. --*/ #ifndef _OPT_SLS_MAX_SAT_H_ #define _OPT_SLS_MAX_SAT_H_ #include "maxsmt.h" namespace opt { maxsmt_solver_base* mk_sls(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft); }; #endif z3-4.4.0/src/opt/opt_context.cpp0000644000175000017500000013232012540347414016311 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: opt_context.cpp Abstract: Facility for running optimization problem. Author: Anh-Dung Phan (t-anphan) 2013-10-16 Notes: --*/ #include "opt_context.h" #include "ast_pp.h" #include "opt_solver.h" #include "opt_params.hpp" #include "for_each_expr.h" #include "goal.h" #include "tactic.h" #include "lia2card_tactic.h" #include "elim01_tactic.h" #include "solve_eqs_tactic.h" #include "simplify_tactic.h" #include "propagate_values_tactic.h" #include "solve_eqs_tactic.h" #include "elim_uncnstr_tactic.h" #include "elim_term_ite_tactic.h" #include "tactical.h" #include "model_smt2_pp.h" #include "card2bv_tactic.h" #include "nnf_tactic.h" #include "inc_sat_solver.h" #include "bv_decl_plugin.h" #include "pb_decl_plugin.h" #include "ast_smt_pp.h" #include "filter_model_converter.h" namespace opt { void context::scoped_state::push() { m_hard_lim.push_back(m_hard.size()); m_objectives_lim.push_back(m_objectives.size()); m_objectives_term_trail_lim.push_back(m_objectives_term_trail.size()); } void context::scoped_state::pop() { m_hard.resize(m_hard_lim.back()); unsigned k = m_objectives_term_trail_lim.back(); while (m_objectives_term_trail.size() > k) { unsigned idx = m_objectives_term_trail.back(); m_objectives[idx].m_terms.pop_back(); m_objectives[idx].m_weights.pop_back(); m_objectives_term_trail.pop_back(); } m_objectives_term_trail_lim.pop_back(); k = m_objectives_lim.back(); while (m_objectives.size() > k) { objective& obj = m_objectives.back(); if (obj.m_type == O_MAXSMT) { m_indices.erase(obj.m_id); } m_objectives.pop_back(); } m_objectives_lim.pop_back(); m_hard_lim.pop_back(); } void context::scoped_state::add(expr* hard) { m_hard.push_back(hard); } bool context::scoped_state::set(ptr_vector & hard) { bool eq = hard.size() == m_hard.size(); for (unsigned i = 0; eq && i < hard.size(); ++i) { eq = hard[i] == m_hard[i].get(); } m_hard.reset(); m_hard.append(hard.size(), hard.c_ptr()); return !eq; } unsigned context::scoped_state::add(expr* f, rational const& w, symbol const& id) { if (w.is_neg()) { throw default_exception("Negative weight supplied. Weight should be positive"); } if (w.is_zero()) { throw default_exception("Zero weight supplied. Weight should be positive"); } if (!m.is_bool(f)) { throw default_exception("Soft constraint should be Boolean"); } if (!m_indices.contains(id)) { m_objectives.push_back(objective(m, id)); m_indices.insert(id, m_objectives.size() - 1); } SASSERT(m_indices.contains(id)); unsigned idx = m_indices[id]; m_objectives[idx].m_terms.push_back(f); m_objectives[idx].m_weights.push_back(w); m_objectives_term_trail.push_back(idx); return idx; } unsigned context::scoped_state::add(app* t, bool is_max) { app_ref tr(t, m); if (!m_bv.is_bv(t) && !m_arith.is_int_real(t)) { throw default_exception("Objective must be bit-vector, integer or real"); } unsigned index = m_objectives.size(); m_objectives.push_back(objective(is_max, tr, index)); return index; } context::context(ast_manager& m): m(m), m_arith(m), m_bv(m), m_hard_constraints(m), m_solver(0), m_box_index(UINT_MAX), m_optsmt(m), m_scoped_state(m), m_fm(m), m_objective_refs(m), m_enable_sat(false), m_enable_sls(false), m_is_clausal(false), m_pp_neat(false) { params_ref p; p.set_bool("model", true); p.set_bool("unsat_core", true); updt_params(p); } context::~context() { reset_maxsmts(); } void context::reset_maxsmts() { map_t::iterator it = m_maxsmts.begin(), end = m_maxsmts.end(); for (; it != end; ++it) { dealloc(it->m_value); } m_maxsmts.reset(); } void context::push() { m_scoped_state.push(); } void context::pop(unsigned n) { for (unsigned i = 0; i < n; ++i) { m_scoped_state.pop(); } clear_state(); reset_maxsmts(); m_optsmt.reset(); m_hard_constraints.reset(); } void context::set_hard_constraints(ptr_vector& fmls) { if (m_scoped_state.set(fmls)) { clear_state(); } } void context::add_hard_constraint(expr* f) { m_scoped_state.add(f); clear_state(); } unsigned context::add_soft_constraint(expr* f, rational const& w, symbol const& id) { clear_state(); return m_scoped_state.add(f, w, id); } unsigned context::add_objective(app* t, bool is_max) { clear_state(); return m_scoped_state.add(t, is_max); } void context::import_scoped_state() { m_optsmt.reset(); reset_maxsmts(); m_objectives.reset(); m_hard_constraints.reset(); scoped_state& s = m_scoped_state; for (unsigned i = 0; i < s.m_objectives.size(); ++i) { objective& obj = s.m_objectives[i]; m_objectives.push_back(obj); if (obj.m_type == O_MAXSMT) { add_maxsmt(obj.m_id); } } m_hard_constraints.append(s.m_hard); } lbool context::optimize() { if (m_pareto) { return execute_pareto(); } if (m_box_index != UINT_MAX) { return execute_box(); } clear_state(); init_solver(); import_scoped_state(); normalize(); internalize(); update_solver(); solver& s = get_solver(); for (unsigned i = 0; i < m_hard_constraints.size(); ++i) { TRACE("opt", tout << "Hard constraint: " << mk_ismt2_pp(m_hard_constraints[i].get(), m) << std::endl;); s.assert_expr(m_hard_constraints[i].get()); } IF_VERBOSE(1, verbose_stream() << "(optimize:check-sat)\n";); lbool is_sat = s.check_sat(0,0); TRACE("opt", tout << "initial search result: " << is_sat << "\n";); if (is_sat != l_true) { m_model = 0; return is_sat; } IF_VERBOSE(1, verbose_stream() << "(optimize:sat)\n";); s.get_model(m_model); TRACE("opt", model_smt2_pp(tout, m, *m_model, 0);); m_optsmt.setup(*m_opt_solver.get()); update_lower(); switch (m_objectives.size()) { case 0: return is_sat; case 1: return execute(m_objectives[0], true, false); default: { opt_params optp(m_params); symbol pri = optp.priority(); if (pri == symbol("pareto")) { return execute_pareto(); } else if (pri == symbol("box")) { return execute_box(); } else { return execute_lex(); } } } } bool context::print_model() const { opt_params optp(m_params); return optp.print_model(); } void context::get_base_model(model_ref& mdl) { mdl = m_model; } void context::fix_model(model_ref& mdl) { if (mdl) { if (m_model_converter) { (*m_model_converter)(mdl, 0); } m_fm(mdl, 0); } } void context::set_model(model_ref& mdl) { m_model = mdl; fix_model(mdl); } void context::get_model(model_ref& mdl) { mdl = m_model; fix_model(mdl); } lbool context::execute_min_max(unsigned index, bool committed, bool scoped, bool is_max) { if (scoped) get_solver().push(); lbool result = m_optsmt.lex(index, is_max); if (result == l_true) m_optsmt.get_model(m_model); if (scoped) get_solver().pop(1); if (result == l_true && committed) m_optsmt.commit_assignment(index); return result; } lbool context::execute_maxsat(symbol const& id, bool committed, bool scoped) { model_ref tmp; maxsmt& ms = *m_maxsmts.find(id); if (scoped) get_solver().push(); lbool result = ms(); if (result != l_false && (ms.get_model(tmp), tmp.get())) ms.get_model(m_model); if (scoped) get_solver().pop(1); if (result == l_true && committed) ms.commit_assignment(); return result; } lbool context::execute(objective const& obj, bool committed, bool scoped) { switch(obj.m_type) { case O_MAXIMIZE: return execute_min_max(obj.m_index, committed, scoped, true); case O_MINIMIZE: return execute_min_max(obj.m_index, committed, scoped, false); case O_MAXSMT: return execute_maxsat(obj.m_id, committed, scoped); default: UNREACHABLE(); return l_undef; } } lbool context::execute_lex() { lbool r = l_true; IF_VERBOSE(1, verbose_stream() << "(optsmt:lex)\n";); for (unsigned i = 0; r == l_true && i < m_objectives.size(); ++i) { bool is_last = i + 1 == m_objectives.size(); r = execute(m_objectives[i], i + 1 < m_objectives.size(), !is_last); if (r == l_true && !get_lower_as_num(i).is_finite()) { return r; } if (r == l_true && i + 1 < m_objectives.size()) { update_lower(); } } DEBUG_CODE(if (r == l_true) validate_lex();); return r; } lbool context::execute_box() { if (m_box_index < m_objectives.size()) { m_model = m_box_models[m_box_index]; ++m_box_index; return l_true; } if (m_box_index != UINT_MAX && m_box_index >= m_objectives.size()) { m_box_index = UINT_MAX; return l_false; } m_box_index = 1; m_box_models.reset(); lbool r = m_optsmt.box(); for (unsigned i = 0, j = 0; r == l_true && i < m_objectives.size(); ++i) { objective const& obj = m_objectives[i]; if (obj.m_type == O_MAXSMT) { solver::scoped_push _sp(get_solver()); r = execute(obj, false, false); if (r == l_true) m_box_models.push_back(m_model.get()); } else { m_box_models.push_back(m_optsmt.get_model(j)); ++j; } } if (r == l_true && m_objectives.size() > 0) { m_model = m_box_models[0]; } return r; } expr_ref context::mk_le(unsigned i, model_ref& mdl) { objective const& obj = m_objectives[i]; return mk_cmp(false, mdl, obj); } expr_ref context::mk_ge(unsigned i, model_ref& mdl) { objective const& obj = m_objectives[i]; return mk_cmp(true, mdl, obj); } expr_ref context::mk_gt(unsigned i, model_ref& mdl) { expr_ref result = mk_le(i, mdl); result = m.mk_not(result); return result; } expr_ref context::mk_cmp(bool is_ge, model_ref& mdl, objective const& obj) { rational k(0); expr_ref val(m), result(m); switch (obj.m_type) { case O_MINIMIZE: is_ge = !is_ge; case O_MAXIMIZE: VERIFY(mdl->eval(obj.m_term, val) && is_numeral(val, k)); if (is_ge) { result = mk_ge(obj.m_term, val); } else { result = mk_ge(val, obj.m_term); } break; case O_MAXSMT: { pb_util pb(m); unsigned sz = obj.m_terms.size(); ptr_vector terms; vector coeffs; for (unsigned i = 0; i < sz; ++i) { terms.push_back(obj.m_terms[i]); coeffs.push_back(obj.m_weights[i]); VERIFY(mdl->eval(obj.m_terms[i], val)); if (m.is_true(val)) { k += obj.m_weights[i]; } } if (is_ge) { result = pb.mk_ge(sz, coeffs.c_ptr(), terms.c_ptr(), k); } else { result = pb.mk_le(sz, coeffs.c_ptr(), terms.c_ptr(), k); } break; } } TRACE("opt", tout << (is_ge?">= ":"<= ") << k << "\n"; display_objective(tout, obj); tout << "\n"; tout << result << "\n";); return result; } expr_ref context::mk_ge(expr* t, expr* s) { expr_ref result(m); if (m_bv.is_bv(t)) { result = m_bv.mk_ule(s, t); } else { result = m_arith.mk_ge(t, s); } return result; } void context::yield() { m_pareto->get_model(m_model); update_bound(true); update_bound(false); } lbool context::execute_pareto() { if (!m_pareto) { set_pareto(alloc(gia_pareto, m, *this, m_solver.get(), m_params)); } lbool is_sat = (*(m_pareto.get()))(); if (is_sat != l_true) { set_pareto(0); } if (is_sat == l_true) { yield(); } return is_sat; } void context::display_bounds(std::ostream& out, bounds_t const& b) const { for (unsigned i = 0; i < m_objectives.size(); ++i) { objective const& obj = m_objectives[i]; display_objective(out, obj); if (obj.m_type == O_MAXIMIZE) { out << " |-> [" << b[i].first << ":" << b[i].second << "]\n"; } else { out << " |-> [" << -b[i].second << ":" << -b[i].first << "]\n"; } } } solver& context::get_solver() { return *m_solver.get(); } void context::init_solver() { setup_arith_solver(); #pragma omp critical (opt_context) { m_opt_solver = alloc(opt_solver, m, m_params, m_fm); m_opt_solver->set_logic(m_logic); m_solver = m_opt_solver.get(); } if (opt_params(m_params).priority() == symbol("pareto")) { m_opt_solver->ensure_pb(); } } void context::setup_arith_solver() { opt_params p(m_params); if (p.optsmt_engine() == symbol("symba") || p.optsmt_engine() == symbol("farkas")) { std::stringstream strm; strm << AS_OPTINF; gparams::set("smt.arith.solver", strm.str().c_str()); } } void context::update_solver() { if (!m_enable_sat || !probe_bv()) { return; } if (m_maxsat_engine != symbol("maxres") && m_maxsat_engine != symbol("pd-maxres") && m_maxsat_engine != symbol("bcd2") && m_maxsat_engine != symbol("sls")) { return; } if (opt_params(m_params).priority() == symbol("pareto")) { return; } m_params.set_bool("minimize_core_partial", true); // false); m_params.set_bool("minimize_core", true); m_sat_solver = mk_inc_sat_solver(m, m_params); unsigned sz = get_solver().get_num_assertions(); for (unsigned i = 0; i < sz; ++i) { m_sat_solver->assert_expr(get_solver().get_assertion(i)); } #pragma omp critical (opt_context) { m_solver = m_sat_solver.get(); } } void context::set_soft_assumptions() { if (m_sat_solver.get()) { m_params.set_bool("soft_assumptions", true); m_sat_solver->updt_params(m_params); } } void context::enable_sls(expr_ref_vector const& soft, vector const& weights) { SASSERT(soft.size() == weights.size()); if (m_sat_solver.get()) { set_soft_inc_sat(m_sat_solver.get(), soft.size(), soft.c_ptr(), weights.c_ptr()); } if (m_enable_sls && m_sat_solver.get()) { m_params.set_bool("optimize_model", true); m_sat_solver->updt_params(m_params); } } struct context::is_bv { struct found {}; ast_manager& m; pb_util pb; bv_util bv; is_bv(ast_manager& m): m(m), pb(m), bv(m) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } void operator()(app *n) { family_id fid = n->get_family_id(); if (fid != m.get_basic_family_id() && fid != pb.get_family_id() && fid != bv.get_family_id() && !is_uninterp_const(n)) { throw found(); } } }; bool context::probe_bv() { expr_fast_mark1 visited; is_bv proc(m); try { for (unsigned i = 0; i < m_objectives.size(); ++i) { objective & obj = m_objectives[i]; if (obj.m_type != O_MAXSMT) return false; maxsmt& ms = *m_maxsmts.find(obj.m_id); for (unsigned j = 0; j < ms.size(); ++j) { quick_for_each_expr(proc, visited, ms[j]); } } unsigned sz = get_solver().get_num_assertions(); for (unsigned i = 0; i < sz; i++) { quick_for_each_expr(proc, visited, get_solver().get_assertion(i)); } for (unsigned i = 0; i < m_hard_constraints.size(); ++i) { quick_for_each_expr(proc, visited, m_hard_constraints[i].get()); } } catch (is_bv::found) { return false; } return true; } struct context::is_propositional_fn { struct found {}; ast_manager& m; is_propositional_fn(ast_manager& m): m(m) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } void operator()(app *n) { family_id fid = n->get_family_id(); if (fid != m.get_basic_family_id() && !is_uninterp_const(n)) { throw found(); } } }; bool context::is_propositional(expr* p) { expr* np; if (is_uninterp_const(p) || (m.is_not(p, np) && is_uninterp_const(np))) { return true; } is_propositional_fn proc(m); expr_fast_mark1 visited; try { quick_for_each_expr(proc, visited, p); } catch (is_propositional_fn::found) { return false; } return true; } void context::add_maxsmt(symbol const& id) { maxsmt* ms = alloc(maxsmt, *this); ms->updt_params(m_params); #pragma omp critical (opt_context) { m_maxsmts.insert(id, ms); } } bool context::is_numeral(expr* e, rational & n) const { unsigned sz; return m_arith.is_numeral(e, n) || m_bv.is_numeral(e, n, sz); } void context::normalize() { expr_ref_vector fmls(m); to_fmls(fmls); simplify_fmls(fmls); from_fmls(fmls); } void context::simplify_fmls(expr_ref_vector& fmls) { if (m_is_clausal) { return; } goal_ref g(alloc(goal, m, true, false)); for (unsigned i = 0; i < fmls.size(); ++i) { g->assert_expr(fmls[i].get()); } tactic_ref tac0 = and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), mk_solve_eqs_tactic(m), mk_elim_term_ite_tactic(m), // NB: mk_elim_uncstr_tactic(m) is not sound with soft constraints mk_simplify_tactic(m)); opt_params optp(m_params); tactic_ref tac2, tac3, tac4; if (optp.elim_01()) { tac2 = mk_elim01_tactic(m); tac3 = mk_lia2card_tactic(m); tac4 = mk_elim_term_ite_tactic(m); params_ref lia_p; lia_p.set_bool("compile_equality", optp.pb_compile_equality()); tac3->updt_params(lia_p); set_simplify(and_then(tac0.get(), tac2.get(), tac3.get(), tac4.get())); } else { set_simplify(tac0.get()); } proof_converter_ref pc; expr_dependency_ref core(m); goal_ref_buffer result; (*m_simplify)(g, result, m_model_converter, pc, core); SASSERT(result.size() == 1); goal* r = result[0]; fmls.reset(); expr_ref tmp(m); for (unsigned i = 0; i < r->size(); ++i) { fmls.push_back(r->form(i)); } } bool context::is_maximize(expr* fml, app_ref& term, expr*& orig_term, unsigned& index) { if (is_app(fml) && m_objective_fns.find(to_app(fml)->get_decl(), index) && m_objectives[index].m_type == O_MAXIMIZE) { term = to_app(to_app(fml)->get_arg(0)); orig_term = m_objective_orig.find(to_app(fml)->get_decl()); return true; } return false; } bool context::is_minimize(expr* fml, app_ref& term, expr*& orig_term, unsigned& index) { if (is_app(fml) && m_objective_fns.find(to_app(fml)->get_decl(), index) && m_objectives[index].m_type == O_MINIMIZE) { term = to_app(to_app(fml)->get_arg(0)); orig_term = m_objective_orig.find(to_app(fml)->get_decl()); return true; } return false; } bool context::is_maxsat(expr* fml, expr_ref_vector& terms, vector& weights, rational& offset, bool& neg, symbol& id, unsigned& index) { if (!is_app(fml)) return false; neg = false; app* a = to_app(fml); if (m_objective_fns.find(a->get_decl(), index) && m_objectives[index].m_type == O_MAXSMT) { for (unsigned i = 0; i < a->get_num_args(); ++i) { expr* arg = a->get_arg(i); if (m.is_true(arg)) { // skip } else if (m.is_false(arg)) { offset += m_objectives[index].m_weights[i]; } else { terms.push_back(arg); weights.push_back(m_objectives[index].m_weights[i]); } } id = m_objectives[index].m_id; return true; } app_ref term(m); expr* orig_term; offset = rational::zero(); bool is_max = is_maximize(fml, term, orig_term, index); bool is_min = !is_max && is_minimize(fml, term, orig_term, index); if (is_min && get_pb_sum(term, terms, weights, offset)) { TRACE("opt", tout << "try to convert minimization" << mk_pp(term, m) << "\n";); // minimize 2*x + 3*y // <=> // (assert-soft (not x) 2) // (assert-soft (not y) 3) // for (unsigned i = 0; i < weights.size(); ++i) { if (weights[i].is_neg()) { offset += weights[i]; weights[i].neg(); } else { terms[i] = m.mk_not(terms[i].get()); } } TRACE("opt", tout << "Convert minimization " << mk_pp(orig_term, m) << "\n"; tout << "to maxsat: " << term << "\n"; for (unsigned i = 0; i < weights.size(); ++i) { tout << mk_pp(terms[i].get(), m) << ": " << weights[i] << "\n"; } tout << "offset: " << offset << "\n"; ); std::ostringstream out; out << mk_pp(orig_term, m); id = symbol(out.str().c_str()); return true; } if (is_max && get_pb_sum(term, terms, weights, offset)) { TRACE("opt", tout << "try to convert maximization" << mk_pp(term, m) << "\n";); // maximize 2*x + 3*y - z // <=> // (assert-soft x 2) // (assert-soft y 3) // (assert-soft (not z) 1) // offset := 6 // maximize = offset - penalty // for (unsigned i = 0; i < weights.size(); ++i) { if (weights[i].is_neg()) { weights[i].neg(); terms[i] = m.mk_not(terms[i].get()); } offset += weights[i]; } neg = true; std::ostringstream out; out << mk_pp(orig_term, m); id = symbol(out.str().c_str()); return true; } if ((is_max || is_min) && m_bv.is_bv(term)) { offset.reset(); unsigned bv_size = m_bv.get_bv_size(term); expr_ref val(m); val = m_bv.mk_numeral(is_max, 1); for (unsigned i = 0; i < bv_size; ++i) { rational w = power(rational(2),i); weights.push_back(w); terms.push_back(m.mk_eq(val, m_bv.mk_extract(i, i, term))); if (is_max) { offset += w; } } neg = is_max; std::ostringstream out; out << mk_pp(orig_term, m); id = symbol(out.str().c_str()); return true; } return false; } expr* context::mk_objective_fn(unsigned index, objective_t ty, unsigned sz, expr*const* args) { ptr_vector domain; for (unsigned i = 0; i < sz; ++i) { domain.push_back(m.get_sort(args[i])); } char const* name = ""; switch(ty) { case O_MAXIMIZE: name = "maximize"; break; case O_MINIMIZE: name = "minimize"; break; case O_MAXSMT: name = "maxsat"; break; default: break; } func_decl* f = m.mk_fresh_func_decl(name,"", domain.size(), domain.c_ptr(), m.mk_bool_sort()); m_objective_fns.insert(f, index); m_objective_refs.push_back(f); if (sz > 0) { m_objective_orig.insert(f, args[0]); } return m.mk_app(f, sz, args); } expr* context::mk_maximize(unsigned index, app* t) { expr* t_ = t; return mk_objective_fn(index, O_MAXIMIZE, 1, &t_); } expr* context::mk_minimize(unsigned index, app* t) { expr* t_ = t; return mk_objective_fn(index, O_MINIMIZE, 1, &t_); } expr* context::mk_maxsat(unsigned index, unsigned num_fmls, expr* const* fmls) { return mk_objective_fn(index, O_MAXSMT, num_fmls, fmls); } void context::from_fmls(expr_ref_vector const& fmls) { TRACE("opt", for (unsigned i = 0; i < fmls.size(); ++i) { tout << mk_pp(fmls[i], m) << "\n"; }); m_hard_constraints.reset(); expr* orig_term; for (unsigned i = 0; i < fmls.size(); ++i) { expr* fml = fmls[i]; app_ref tr(m); expr_ref_vector terms(m); vector weights; rational offset(0); unsigned index; symbol id; bool neg; if (is_maxsat(fml, terms, weights, offset, neg, id, index)) { objective& obj = m_objectives[index]; if (obj.m_type != O_MAXSMT) { // change from maximize/minimize. obj.m_id = id; obj.m_type = O_MAXSMT; obj.m_weights.append(weights); SASSERT(!m_maxsmts.contains(id)); add_maxsmt(id); } mk_atomic(terms); SASSERT(obj.m_id == id); obj.m_terms.reset(); obj.m_terms.append(terms); obj.m_adjust_value.set_offset(offset); obj.m_adjust_value.set_negate(neg); m_maxsmts.find(id)->set_adjust_value(obj.m_adjust_value); TRACE("opt", tout << "maxsat: " << id << " offset:" << offset << "\n";); } else if (is_maximize(fml, tr, orig_term, index)) { m_objectives[index].m_term = tr; } else if (is_minimize(fml, tr, orig_term, index)) { m_objectives[index].m_term = tr; m_objectives[index].m_adjust_value.set_negate(true); } else { m_hard_constraints.push_back(fml); } } } /** To select the proper theory solver we have to ensure that all theory symbols from soft constraints are reflected in the hard constraints. - filter "obj" from generated model. */ void context::mk_atomic(expr_ref_vector& terms) { ref fm; for (unsigned i = 0; i < terms.size(); ++i) { expr_ref p(terms[i].get(), m); app_ref q(m); if (is_propositional(p)) { terms[i] = p; } else { q = m.mk_fresh_const("obj", m.mk_bool_sort()); terms[i] = q; m_hard_constraints.push_back(m.mk_iff(p, q)); if (!fm) fm = alloc(filter_model_converter, m); fm->insert(q->get_decl()); } } if (fm) { m_model_converter = concat(m_model_converter.get(), fm.get()); } } void context::to_fmls(expr_ref_vector& fmls) { m_objective_fns.reset(); fmls.append(m_hard_constraints); for (unsigned i = 0; i < m_objectives.size(); ++i) { objective const& obj = m_objectives[i]; switch(obj.m_type) { case O_MINIMIZE: fmls.push_back(mk_minimize(i, obj.m_term)); break; case O_MAXIMIZE: fmls.push_back(mk_maximize(i, obj.m_term)); break; case O_MAXSMT: fmls.push_back(mk_maxsat(i, obj.m_terms.size(), obj.m_terms.c_ptr())); break; } } TRACE("opt", for (unsigned i = 0; i < fmls.size(); ++i) { tout << mk_pp(fmls[i].get(), m) << "\n"; }); } void context::internalize() { for (unsigned i = 0; i < m_objectives.size(); ++i) { objective & obj = m_objectives[i]; switch(obj.m_type) { case O_MINIMIZE: { app_ref tmp(m); tmp = m_arith.mk_uminus(obj.m_term); obj.m_index = m_optsmt.add(tmp); break; } case O_MAXIMIZE: obj.m_index = m_optsmt.add(obj.m_term); break; case O_MAXSMT: { maxsmt& ms = *m_maxsmts.find(obj.m_id); for (unsigned j = 0; j < obj.m_terms.size(); ++j) { ms.add(obj.m_terms[j].get(), obj.m_weights[j]); } break; } } } } void context::update_bound(bool is_lower) { expr_ref val(m); if (!m_model.get()) return; for (unsigned i = 0; i < m_objectives.size(); ++i) { objective const& obj = m_objectives[i]; rational r; switch(obj.m_type) { case O_MINIMIZE: { bool evaluated = m_model->eval(obj.m_term, val); TRACE("opt", tout << obj.m_term << " " << val << " " << evaluated << " " << is_numeral(val, r) << "\n";); if (evaluated && is_numeral(val, r)) { inf_eps val = inf_eps(obj.m_adjust_value(r)); TRACE("opt", tout << "adjusted value: " << val << "\n";); if (is_lower) { m_optsmt.update_lower(obj.m_index, val); } else { m_optsmt.update_upper(obj.m_index, val); } } break; } case O_MAXIMIZE: { bool evaluated = m_model->eval(obj.m_term, val); TRACE("opt", tout << obj.m_term << " " << val << "\n";); if (evaluated && is_numeral(val, r)) { inf_eps val = inf_eps(obj.m_adjust_value(r)); TRACE("opt", tout << "adjusted value: " << val << "\n";); if (is_lower) { m_optsmt.update_lower(obj.m_index, val); } else { m_optsmt.update_upper(obj.m_index, val); } } break; } case O_MAXSMT: { bool ok = true; for (unsigned j = 0; ok && j < obj.m_terms.size(); ++j) { bool evaluated = m_model->eval(obj.m_terms[j], val); TRACE("opt", tout << mk_pp(obj.m_terms[j], m) << " " << val << "\n";); if (evaluated) { if (!m.is_true(val)) { r += obj.m_weights[j]; } } else { ok = false; } } if (ok) { maxsmt& ms = *m_maxsmts.find(obj.m_id); if (is_lower) { ms.update_upper(r); TRACE("opt", tout << r << " " << ms.get_upper() << "\n";); } else { ms.update_lower(r); TRACE("opt", tout << r << " " << ms.get_lower() << "\n";); } } break; } } } } void context::display(std::ostream& out) { display_assignment(out); } void context::display_assignment(std::ostream& out) { for (unsigned i = 0; i < m_scoped_state.m_objectives.size(); ++i) { objective const& obj = m_scoped_state.m_objectives[i]; display_objective(out, obj); if (get_lower_as_num(i) != get_upper_as_num(i)) { out << " |-> [" << get_lower(i) << ":" << get_upper(i) << "]\n"; } else { out << " |-> " << get_lower(i) << "\n"; } } } void context::display_objective(std::ostream& out, objective const& obj) const { switch(obj.m_type) { case O_MAXSMT: { symbol s = obj.m_id; if (s != symbol::null) { out << s; } break; } default: out << obj.m_term; break; } } inf_eps context::get_lower_as_num(unsigned idx) { if (idx > m_objectives.size()) { throw default_exception("index out of bounds"); } objective const& obj = m_objectives[idx]; switch(obj.m_type) { case O_MAXSMT: return inf_eps(m_maxsmts.find(obj.m_id)->get_lower()); case O_MINIMIZE: return obj.m_adjust_value(m_optsmt.get_upper(obj.m_index)); case O_MAXIMIZE: return obj.m_adjust_value(m_optsmt.get_lower(obj.m_index)); default: UNREACHABLE(); return inf_eps(); } } inf_eps context::get_upper_as_num(unsigned idx) { if (idx > m_objectives.size()) { throw default_exception("index out of bounds"); } objective const& obj = m_objectives[idx]; switch(obj.m_type) { case O_MAXSMT: return inf_eps(m_maxsmts.find(obj.m_id)->get_upper()); case O_MINIMIZE: return obj.m_adjust_value(m_optsmt.get_lower(obj.m_index)); case O_MAXIMIZE: return obj.m_adjust_value(m_optsmt.get_upper(obj.m_index)); default: UNREACHABLE(); return inf_eps(); } } expr_ref context::get_lower(unsigned idx) { return to_expr(get_lower_as_num(idx)); } expr_ref context::get_upper(unsigned idx) { return to_expr(get_upper_as_num(idx)); } expr_ref context::to_expr(inf_eps const& n) { rational inf = n.get_infinity(); rational r = n.get_rational(); rational eps = n.get_infinitesimal(); expr_ref_vector args(m); if (!inf.is_zero()) { expr* oo = m.mk_const(symbol("oo"), m_arith.mk_int()); if (inf.is_one()) { args.push_back(oo); } else { args.push_back(m_arith.mk_mul(m_arith.mk_numeral(inf, inf.is_int()), oo)); } } if (!r.is_zero()) { args.push_back(m_arith.mk_numeral(r, r.is_int())); } if (!eps.is_zero()) { expr* ep = m.mk_const(symbol("epsilon"), m_arith.mk_real()); if (eps.is_one()) { args.push_back(ep); } else { args.push_back(m_arith.mk_mul(m_arith.mk_numeral(eps, eps.is_int()), ep)); } } switch(args.size()) { case 0: return expr_ref(m_arith.mk_numeral(rational(0), true), m); case 1: return expr_ref(args[0].get(), m); default: return expr_ref(m_arith.mk_add(args.size(), args.c_ptr()), m); } } void context::set_simplify(tactic* tac) { #pragma omp critical (opt_context) { m_simplify = tac; } } void context::clear_state() { set_pareto(0); m_box_index = UINT_MAX; m_model.reset(); } void context::set_pareto(pareto_base* p) { #pragma omp critical (opt_context) { m_pareto = p; } } void context::set_cancel(bool f) { #pragma omp critical (opt_context) { if (m_solver) { if (f) m_solver->cancel(); else m_solver->reset_cancel(); } if (m_pareto) { m_pareto->set_cancel(f); } if (m_simplify) { if (f) m_simplify->cancel(); else m_solver->reset_cancel(); } map_t::iterator it = m_maxsmts.begin(), end = m_maxsmts.end(); for (; it != end; ++it) { it->m_value->set_cancel(f); } } m_optsmt.set_cancel(f); } void context::collect_statistics(statistics& stats) const { if (m_solver) { m_solver->collect_statistics(stats); } if (m_simplify) { m_simplify->collect_statistics(stats); } map_t::iterator it = m_maxsmts.begin(), end = m_maxsmts.end(); for (; it != end; ++it) { it->m_value->collect_statistics(stats); } unsigned long long max_mem = memory::get_max_used_memory(); unsigned long long mem = memory::get_allocation_size(); stats.update("memory", static_cast(mem)/static_cast(1024*1024)); stats.update("max memory", static_cast(max_mem)/static_cast(1024*1024)); } void context::collect_param_descrs(param_descrs & r) { opt_params::collect_param_descrs(r); } void context::updt_params(params_ref& p) { m_params.append(p); if (m_solver) { m_solver->updt_params(m_params); } m_optsmt.updt_params(m_params); map_t::iterator it = m_maxsmts.begin(), end = m_maxsmts.end(); for (; it != end; ++it) { it->m_value->updt_params(m_params); } opt_params _p(p); m_enable_sat = _p.enable_sat(); m_enable_sls = _p.enable_sls(); m_maxsat_engine = _p.maxsat_engine(); m_pp_neat = _p.pp_neat(); } typedef obj_hashtable func_decl_set; struct context::free_func_visitor { ast_manager& m; func_decl_set m_funcs; obj_hashtable m_sorts; expr_mark m_visited; public: free_func_visitor(ast_manager& m): m(m) {} void operator()(var * n) { } void operator()(app * n) { if (n->get_family_id() == null_family_id) { m_funcs.insert(n->get_decl()); } sort* s = m.get_sort(n); if (s->get_family_id() == null_family_id) { m_sorts.insert(s); } } void operator()(quantifier * n) { } func_decl_set& funcs() { return m_funcs; } obj_hashtable& sorts() { return m_sorts; } void collect(expr* e) { for_each_expr(*this, m_visited, e); } }; std::string context::to_string() const { smt2_pp_environment_dbg env(m); ast_smt_pp ll_smt2_pp(m); free_func_visitor visitor(m); std::ostringstream out; #define PP(_e_) ast_smt2_pp(out, _e_, env); #define PPE(_e_) if (m_pp_neat) ast_smt2_pp(out, _e_, env); else ll_smt2_pp.display_expr_smt2(out, _e_); for (unsigned i = 0; i < m_scoped_state.m_hard.size(); ++i) { visitor.collect(m_scoped_state.m_hard[i]); } for (unsigned i = 0; i < m_scoped_state.m_objectives.size(); ++i) { objective const& obj = m_scoped_state.m_objectives[i]; switch(obj.m_type) { case O_MAXIMIZE: case O_MINIMIZE: visitor.collect(obj.m_term); break; case O_MAXSMT: for (unsigned j = 0; j < obj.m_terms.size(); ++j) { visitor.collect(obj.m_terms[j]); } break; default: UNREACHABLE(); break; } } obj_hashtable::iterator sit = visitor.sorts().begin(); obj_hashtable::iterator send = visitor.sorts().end(); for (; sit != send; ++sit) { PP(*sit); } func_decl_set::iterator it = visitor.funcs().begin(); func_decl_set::iterator end = visitor.funcs().end(); for (; it != end; ++it) { PP(*it); out << "\n"; } for (unsigned i = 0; i < m_scoped_state.m_hard.size(); ++i) { out << "(assert "; PPE(m_scoped_state.m_hard[i]); out << ")\n"; } for (unsigned i = 0; i < m_scoped_state.m_objectives.size(); ++i) { objective const& obj = m_scoped_state.m_objectives[i]; switch(obj.m_type) { case O_MAXIMIZE: out << "(maximize "; PP(obj.m_term); out << ")\n"; break; case O_MINIMIZE: out << "(minimize "; PP(obj.m_term); out << ")\n"; break; case O_MAXSMT: for (unsigned j = 0; j < obj.m_terms.size(); ++j) { out << "(assert-soft "; PP(obj.m_terms[j]); rational w = obj.m_weights[j]; if (w.is_int()) { out << " :weight " << w; } else { out << " :dweight " << w; } if (obj.m_id != symbol::null) { out << " :id " << obj.m_id; } out << ")\n"; } break; default: UNREACHABLE(); break; } } param_descrs descrs; collect_param_descrs(descrs); m_params.display_smt2(out, "opt", descrs); out << "(check-sat)\n"; return out.str(); } void context::validate_lex() { rational r1; expr_ref val(m); for (unsigned i = 0; i < m_objectives.size(); ++i) { objective const& obj = m_objectives[i]; switch(obj.m_type) { case O_MINIMIZE: case O_MAXIMIZE: { inf_eps n = m_optsmt.get_lower(obj.m_index); if (m_optsmt.objective_is_model_valid(obj.m_index) && n.get_infinity().is_zero() && n.get_infinitesimal().is_zero() && m_model->eval(obj.m_term, val) && is_numeral(val, r1)) { rational r2 = n.get_rational(); if (obj.m_type == O_MINIMIZE) { r1.neg(); } CTRACE("opt", r1 != r2, tout << obj.m_term << " evaluates to " << r1 << " but has objective " << r2 << "\n";); CTRACE("opt", r1 != r2, model_smt2_pp(tout, m, *m_model, 0);); SASSERT(r1 == r2); } break; } case O_MAXSMT: { rational value(0); for (unsigned i = 0; i < obj.m_terms.size(); ++i) { VERIFY(m_model->eval(obj.m_terms[i], val)); if (!m.is_true(val)) { value += obj.m_weights[i]; } // TBD: check that optimal was not changed. } TRACE("opt", tout << "value " << value << "\n";); break; } } } } } z3-4.4.0/src/opt/bcd2.cpp0000644000175000017500000003353312540347414014563 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: bcd2.cpp Abstract: bcd2 based MaxSAT. Author: Nikolaj Bjorner (nbjorner) 2014-4-17 Notes: --*/ #include "bcd2.h" #include "pb_decl_plugin.h" #include "uint_set.h" #include "ast_pp.h" namespace opt { // ------------------------------------------------------ // Morgado, Heras, Marques-Silva 2013 // (initial version without model-based optimizations) // class bcd2 : public maxsmt_solver_base { struct wcore { expr* m_r; unsigned_vector m_R; rational m_lower; rational m_mid; rational m_upper; }; typedef obj_hashtable expr_set; pb_util pb; expr_ref_vector m_soft_aux; obj_map m_relax2index; // expr |-> index obj_map m_soft2index; // expr |-> index expr_ref_vector m_trail; expr_ref_vector m_soft_constraints; expr_set m_asm_set; vector m_cores; vector m_sigmas; rational m_den; // least common multiplier of original denominators bool m_enable_lazy; // enable adding soft constraints lazily (called 'mgbcd2') unsigned_vector m_lazy_soft; // soft constraints to add lazily. void set2asms(expr_set const& set, expr_ref_vector & es) const { es.reset(); expr_set::iterator it = set.begin(), end = set.end(); for (; it != end; ++it) { es.push_back(m.mk_not(*it)); } } void bcd2_init_soft(weights_t& weights, expr_ref_vector const& soft) { // normalize weights to be integral: m_den = rational::one(); for (unsigned i = 0; i < m_weights.size(); ++i) { m_den = lcm(m_den, denominator(m_weights[i])); } if (!m_den.is_one()) { for (unsigned i = 0; i < m_weights.size(); ++i) { m_weights[i] = m_den*m_weights[i]; SASSERT(m_weights[i].is_int()); } } } void init_bcd() { m_trail.reset(); m_asm_set.reset(); m_cores.reset(); m_sigmas.reset(); m_lazy_soft.reset(); for (unsigned i = 0; i < m_soft.size(); ++i) { m_sigmas.push_back(m_weights[i]); m_soft_aux.push_back(mk_fresh()); if (m_enable_lazy) { m_lazy_soft.push_back(i); } else { enable_soft_constraint(i); } } m_upper += rational(1); } void process_sat() { svector assignment; update_assignment(assignment); if (check_lazy_soft(assignment)) { update_sigmas(); } } public: bcd2(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): maxsmt_solver_base(c, ws, soft), pb(m), m_soft_aux(m), m_trail(m), m_soft_constraints(m), m_enable_lazy(true) { bcd2_init_soft(ws, soft); } virtual ~bcd2() {} virtual lbool operator()() { expr_ref fml(m), r(m); lbool is_sat = l_undef; expr_ref_vector asms(m); init(); init_bcd(); if (m_cancel) { normalize_bounds(); return l_undef; } process_sat(); while (m_lower < m_upper) { trace_bounds("bcd2"); assert_soft(); solver::scoped_push _scope2(s()); TRACE("opt", display(tout);); assert_cores(); set2asms(m_asm_set, asms); if (m_cancel) { normalize_bounds(); return l_undef; } is_sat = s().check_sat(asms.size(), asms.c_ptr()); switch(is_sat) { case l_undef: normalize_bounds(); return l_undef; case l_true: process_sat(); break; case l_false: { ptr_vector unsat_core; uint_set subC, soft; s().get_unsat_core(unsat_core); core2indices(unsat_core, subC, soft); SASSERT(unsat_core.size() == subC.num_elems() + soft.num_elems()); if (soft.num_elems() == 0 && subC.num_elems() == 1) { unsigned s = *subC.begin(); wcore& c_s = m_cores[s]; c_s.m_lower = refine(c_s.m_R, c_s.m_mid); c_s.m_mid = div(c_s.m_lower + c_s.m_upper, rational(2)); } else { wcore c_s; rational delta = min_of_delta(subC); rational lower = sum_of_lower(subC); union_Rs(subC, c_s.m_R); r = mk_fresh(); relax(subC, soft, c_s.m_R, delta); c_s.m_lower = refine(c_s.m_R, lower + delta - rational(1)); c_s.m_upper = rational::one(); c_s.m_upper += sum_of_sigmas(c_s.m_R); c_s.m_mid = div(c_s.m_lower + c_s.m_upper, rational(2)); c_s.m_r = r; m_asm_set.insert(r); subtract(m_cores, subC); m_relax2index.insert(r, m_cores.size()); m_cores.push_back(c_s); } break; } } m_lower = compute_lower(); } normalize_bounds(); return l_true; } private: void enable_soft_constraint(unsigned i) { expr_ref fml(m); expr* r = m_soft_aux[i].get(); m_soft2index.insert(r, i); fml = m.mk_or(r, m_soft[i]); m_soft_constraints.push_back(fml); m_asm_set.insert(r); SASSERT(m_weights[i].is_int()); } void assert_soft() { for (unsigned i = 0; i < m_soft_constraints.size(); ++i) { s().assert_expr(m_soft_constraints[i].get()); } m_soft_constraints.reset(); } bool check_lazy_soft(svector const& assignment) { bool all_satisfied = true; for (unsigned i = 0; i < m_lazy_soft.size(); ++i) { unsigned j = m_lazy_soft[i]; if (!assignment[j]) { enable_soft_constraint(j); m_lazy_soft[i] = m_lazy_soft.back(); m_lazy_soft.pop_back(); --i; all_satisfied = false; } } return all_satisfied; } void normalize_bounds() { m_lower /= m_den; m_upper /= m_den; } expr* mk_fresh() { expr* r = mk_fresh_bool("r"); m_trail.push_back(r); return r; } void update_assignment(svector& new_assignment) { expr_ref val(m); rational new_upper(0); model_ref model; new_assignment.reset(); s().get_model(model); for (unsigned i = 0; i < m_soft.size(); ++i) { VERIFY(model->eval(m_soft[i], val)); new_assignment.push_back(m.is_true(val)); if (!new_assignment[i]) { new_upper += m_weights[i]; } } if (new_upper < m_upper) { m_upper = new_upper; m_model = model; m_assignment.reset(); m_assignment.append(new_assignment); } } void update_sigmas() { for (unsigned i = 0; i < m_cores.size(); ++i) { wcore& c_i = m_cores[i]; unsigned_vector const& R = c_i.m_R; c_i.m_upper.reset(); for (unsigned j = 0; j < R.size(); ++j) { unsigned r_j = R[j]; if (!m_assignment[r_j]) { c_i.m_upper += m_weights[r_j]; m_sigmas[r_j] = m_weights[r_j]; } else { m_sigmas[r_j].reset(); } } c_i.m_mid = div(c_i.m_lower + c_i.m_upper, rational(2)); } } /** * Minimum of two (positive) numbers. Zero is treated as +infinity. */ rational min_z(rational const& a, rational const& b) { if (a.is_zero()) return b; if (b.is_zero()) return a; if (a < b) return a; return b; } rational min_of_delta(uint_set const& subC) { rational delta(0); for (uint_set::iterator it = subC.begin(); it != subC.end(); ++it) { unsigned j = *it; wcore const& core = m_cores[j]; rational new_delta = rational(1) + core.m_upper - core.m_mid; SASSERT(new_delta.is_pos()); delta = min_z(delta, new_delta); } return delta; } rational sum_of_lower(uint_set const& subC) { rational lower(0); for (uint_set::iterator it = subC.begin(); it != subC.end(); ++it) { lower += m_cores[*it].m_lower; } return lower; } rational sum_of_sigmas(unsigned_vector const& R) { rational sum(0); for (unsigned i = 0; i < R.size(); ++i) { sum += m_sigmas[R[i]]; } return sum; } void union_Rs(uint_set const& subC, unsigned_vector& R) { for (uint_set::iterator it = subC.begin(); it != subC.end(); ++it) { R.append(m_cores[*it].m_R); } } rational compute_lower() { rational result(0); for (unsigned i = 0; i < m_cores.size(); ++i) { result += m_cores[i].m_lower; } return result; } void subtract(vector& cores, uint_set const& subC) { unsigned j = 0; for (unsigned i = 0; i < cores.size(); ++i) { if (subC.contains(i)) { m_asm_set.remove(cores[i].m_r); } else { if (j != i) { cores[j] = cores[i]; } ++j; } } cores.resize(j); for (unsigned i = 0; i < cores.size(); ++i) { m_relax2index.insert(cores[i].m_r, i); } } void core2indices(ptr_vector const& core, uint_set& subC, uint_set& soft) { for (unsigned i = 0; i < core.size(); ++i) { unsigned j; expr* a; VERIFY(m.is_not(core[i], a)); if (m_relax2index.find(a, j)) { subC.insert(j); } else { VERIFY(m_soft2index.find(a, j)); soft.insert(j); } } } rational refine(unsigned_vector const& idx, rational v) { return v + rational(1); } void relax(uint_set& subC, uint_set& soft, unsigned_vector& R, rational& delta) { for (uint_set::iterator it = soft.begin(); it != soft.end(); ++it) { R.push_back(*it); delta = min_z(delta, m_weights[*it]); m_asm_set.remove(m_soft_aux[*it].get()); } } void assert_cores() { for (unsigned i = 0; i < m_cores.size(); ++i) { assert_core(m_cores[i]); } } void assert_core(wcore const& core) { expr_ref fml(m); vector ws; ptr_vector rs; rational w(0); for (unsigned j = 0; j < core.m_R.size(); ++j) { unsigned idx = core.m_R[j]; ws.push_back(m_weights[idx]); w += ws.back(); rs.push_back(m_soft_aux[idx].get()); } w.neg(); w += core.m_mid; ws.push_back(w); rs.push_back(core.m_r); fml = pb.mk_le(ws.size(), ws.c_ptr(), rs.c_ptr(), core.m_mid); s().assert_expr(fml); } void display(std::ostream& out) { out << "[" << m_lower << ":" << m_upper << "]\n"; s().display(out); out << "\n"; for (unsigned i = 0; i < m_cores.size(); ++i) { wcore const& c = m_cores[i]; out << mk_pp(c.m_r, m) << ": "; for (unsigned j = 0; j < c.m_R.size(); ++j) { out << c.m_R[j] << " (" << m_sigmas[c.m_R[j]] << ") "; } out << "[" << c.m_lower << ":" << c.m_mid << ":" << c.m_upper << "]\n"; } for (unsigned i = 0; i < m_soft.size(); ++i) { out << mk_pp(m_soft[i], m) << " " << m_weights[i] << "\n"; } } }; maxsmt_solver_base* mk_bcd2( maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { return alloc(bcd2, c, ws, soft); } } z3-4.4.0/src/opt/bcd2.h0000644000175000017500000000053612540347414014225 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: bcd2.h Abstract: Bcd2 based MaxSAT. Author: Nikolaj Bjorner (nbjorner) 2014-4-17 Notes: --*/ #ifndef _BCD2_H_ #define _BCD2_H_ #include "maxsmt.h" namespace opt { maxsmt_solver_base* mk_bcd2(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft); } #endif z3-4.4.0/src/opt/opt_pareto.h0000644000175000017500000000547012540347414015571 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: opt_pareto.h Abstract: Pareto front utilities Author: Nikolaj Bjorner (nbjorner) 2014-4-24 Notes: --*/ #ifndef _OPT_PARETO_H_ #define _OPT_PARETO_H_ #include "solver.h" #include "model.h" namespace opt { class pareto_callback { public: virtual unsigned num_objectives() = 0; virtual expr_ref mk_gt(unsigned i, model_ref& model) = 0; virtual expr_ref mk_ge(unsigned i, model_ref& model) = 0; virtual expr_ref mk_le(unsigned i, model_ref& model) = 0; virtual void set_model(model_ref& m) = 0; virtual void fix_model(model_ref& m) = 0; }; class pareto_base { protected: ast_manager& m; pareto_callback& cb; volatile bool m_cancel; ref m_solver; params_ref m_params; model_ref m_model; public: pareto_base( ast_manager & m, pareto_callback& cb, solver* s, params_ref & p): m(m), cb(cb), m_cancel(false), m_solver(s), m_params(p) { } virtual ~pareto_base() {} virtual void updt_params(params_ref & p) { m_solver->updt_params(p); m_params.copy(p); } virtual void collect_param_descrs(param_descrs & r) { m_solver->collect_param_descrs(r); } virtual void collect_statistics(statistics & st) const { m_solver->collect_statistics(st); } virtual void set_cancel(bool f) { if (f) m_solver->cancel(); else m_solver->reset_cancel(); m_cancel = f; } virtual void display(std::ostream & out) const { m_solver->display(out); } virtual lbool operator()() = 0; virtual void get_model(model_ref& mdl) { mdl = m_model; } protected: void mk_dominates(); void mk_not_dominated_by(); }; class gia_pareto : public pareto_base { public: gia_pareto(ast_manager & m, pareto_callback& cb, solver* s, params_ref & p): pareto_base(m, cb, s, p) { } virtual ~gia_pareto() {} virtual lbool operator()(); }; // opportunistic improvement algorithm. class oia_pareto : public pareto_base { public: oia_pareto(ast_manager & m, pareto_callback& cb, solver* s, params_ref & p): pareto_base(m, cb, s, p) { } virtual ~oia_pareto() {} virtual lbool operator()(); }; } #endif z3-4.4.0/src/opt/maxsmt.cpp0000644000175000017500000002213412540347414015255 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: maxsmt.cpp Abstract: MaxSMT optimization context. Author: Nikolaj Bjorner (nbjorner) 2013-11-7 Notes: --*/ #include #include "maxsmt.h" #include "fu_malik.h" #include "maxres.h" #include "maxhs.h" #include "bcd2.h" #include "wmax.h" #include "maxsls.h" #include "ast_pp.h" #include "uint_set.h" #include "opt_context.h" #include "theory_wmaxsat.h" #include "ast_util.h" #include "pb_decl_plugin.h" namespace opt { maxsmt_solver_base::maxsmt_solver_base( maxsat_context& c, vector const& ws, expr_ref_vector const& soft): m(c.get_manager()), m_c(c), m_cancel(false), m_soft(soft), m_weights(ws), m_assertions(m) { c.get_base_model(m_model); SASSERT(m_model); updt_params(c.params()); } void maxsmt_solver_base::updt_params(params_ref& p) { m_params.copy(p); } solver& maxsmt_solver_base::s() { return m_c.get_solver(); } void maxsmt_solver_base::commit_assignment() { expr_ref tmp(m); rational k(0); for (unsigned i = 0; i < m_soft.size(); ++i) { if (get_assignment(i)) { k += m_weights[i]; } } pb_util pb(m); tmp = pb.mk_ge(m_weights.size(), m_weights.c_ptr(), m_soft.c_ptr(), k); TRACE("opt", tout << tmp << "\n";); s().assert_expr(tmp); } void maxsmt_solver_base::init() { m_lower.reset(); m_upper.reset(); m_assignment.reset(); for (unsigned i = 0; i < m_weights.size(); ++i) { expr_ref val(m); VERIFY(m_model->eval(m_soft[i], val)); m_assignment.push_back(m.is_true(val)); if (!m_assignment.back()) { m_upper += m_weights[i]; } } TRACE("opt", tout << "upper: " << m_upper << " assignments: "; for (unsigned i = 0; i < m_weights.size(); ++i) { tout << (m_assignment[i]?"T":"F"); } tout << "\n";); } void maxsmt_solver_base::set_mus(bool f) { params_ref p; p.set_bool("minimize_core", f); s().updt_params(p); } void maxsmt_solver_base::enable_sls(expr_ref_vector const& soft, vector const& ws) { m_c.enable_sls(soft, ws); } void maxsmt_solver_base::set_enable_sls(bool f) { m_c.set_enable_sls(f); } void maxsmt_solver_base::set_soft_assumptions() { m_c.set_soft_assumptions(); } app* maxsmt_solver_base::mk_fresh_bool(char const* name) { app* result = m.mk_fresh_const(name, m.mk_bool_sort()); m_c.fm().insert(result->get_decl()); return result; } smt::theory_wmaxsat* maxsmt_solver_base::get_wmax_theory() const { smt::theory_id th_id = m.get_family_id("weighted_maxsat"); smt::theory* th = m_c.smt_context().get_theory(th_id); if (th) { return dynamic_cast(th); } else { return 0; } } smt::theory_wmaxsat* maxsmt_solver_base::ensure_wmax_theory() { smt::theory_wmaxsat* wth = get_wmax_theory(); if (wth) { wth->reset_local(); } else { wth = alloc(smt::theory_wmaxsat, m, m_c.fm()); m_c.smt_context().register_plugin(wth); } return wth; } maxsmt_solver_base::scoped_ensure_theory::scoped_ensure_theory(maxsmt_solver_base& s) { m_wth = s.ensure_wmax_theory(); } maxsmt_solver_base::scoped_ensure_theory::~scoped_ensure_theory() { //m_wth->reset_local(); } smt::theory_wmaxsat& maxsmt_solver_base::scoped_ensure_theory::operator()() { return *m_wth; } void maxsmt_solver_base::trace_bounds(char const * solver) { IF_VERBOSE(1, rational l = m_adjust_value(m_lower); rational u = m_adjust_value(m_upper); if (l > u) std::swap(l, u); verbose_stream() << "(opt." << solver << " [" << l << ":" << u << "])\n";); } maxsmt::maxsmt(maxsat_context& c): m(c.get_manager()), m_c(c), m_cancel(false), m_soft_constraints(m), m_answer(m) {} lbool maxsmt::operator()() { lbool is_sat; m_msolver = 0; symbol const& maxsat_engine = m_c.maxsat_engine(); IF_VERBOSE(1, verbose_stream() << "(maxsmt)\n";); TRACE("opt", tout << "maxsmt\n";); if (m_soft_constraints.empty()) { TRACE("opt", tout << "no constraints\n";); m_msolver = 0; is_sat = s().check_sat(0, 0); } else if (maxsat_engine == symbol("maxres")) { m_msolver = mk_maxres(m_c, m_weights, m_soft_constraints); } else if (maxsat_engine == symbol("pd-maxres")) { m_msolver = mk_primal_dual_maxres(m_c, m_weights, m_soft_constraints); } else if (maxsat_engine == symbol("bcd2")) { m_msolver = mk_bcd2(m_c, m_weights, m_soft_constraints); } else if (maxsat_engine == symbol("maxhs")) { m_msolver = mk_maxhs(m_c, m_weights, m_soft_constraints); } else if (maxsat_engine == symbol("sls")) { // NB: this is experimental one-round version of SLS m_msolver = mk_sls(m_c, m_weights, m_soft_constraints); } else if (is_maxsat_problem(m_weights) && maxsat_engine == symbol("fu_malik")) { m_msolver = mk_fu_malik(m_c, m_weights, m_soft_constraints); } else { if (maxsat_engine != symbol::null && maxsat_engine != symbol("wmax")) { warning_msg("solver %s is not recognized, using default 'wmax'", maxsat_engine.str().c_str()); } m_msolver = mk_wmax(m_c, m_weights, m_soft_constraints); } if (m_msolver) { m_msolver->updt_params(m_params); m_msolver->set_adjust_value(m_adjust_value); is_sat = (*m_msolver)(); if (is_sat != l_false) { m_msolver->get_model(m_model); } } IF_VERBOSE(1, verbose_stream() << "is-sat: " << is_sat << "\n"; if (is_sat == l_true) { verbose_stream() << "Satisfying soft constraints\n"; display_answer(verbose_stream()); }); DEBUG_CODE(if (is_sat == l_true) verify_assignment();); return is_sat; } void maxsmt::verify_assignment() { // TBD: have to use a different solver // because we don't push local scope any longer. return; } bool maxsmt::get_assignment(unsigned idx) const { if (m_msolver) { return m_msolver->get_assignment(idx); } else { return true; } } rational maxsmt::get_lower() const { rational r = m_lower; if (m_msolver) { rational q = m_msolver->get_lower(); if (q > r) r = q; } return m_adjust_value(r); } rational maxsmt::get_upper() const { rational r = m_upper; if (m_msolver) { rational q = m_msolver->get_upper(); if (q < r) r = q; } return m_adjust_value(r); } void maxsmt::update_lower(rational const& r) { m_lower = r; } void maxsmt::update_upper(rational const& r) { m_upper = r; } void maxsmt::get_model(model_ref& mdl) { mdl = m_model.get(); } void maxsmt::commit_assignment() { if (m_msolver) { m_msolver->commit_assignment(); } } void maxsmt::add(expr* f, rational const& w) { TRACE("opt", tout << mk_pp(f, m) << " weight: " << w << "\n";); SASSERT(m.is_bool(f)); SASSERT(w.is_pos()); m_soft_constraints.push_back(f); m_weights.push_back(w); m_upper += w; } void maxsmt::display_answer(std::ostream& out) const { for (unsigned i = 0; i < m_soft_constraints.size(); ++i) { out << mk_pp(m_soft_constraints[i], m) << (get_assignment(i)?" |-> true\n":" |-> false\n"); } } void maxsmt::set_cancel(bool f) { m_cancel = f; if (m_msolver) { m_msolver->set_cancel(f); } } bool maxsmt::is_maxsat_problem(vector const& ws) const { for (unsigned i = 0; i < ws.size(); ++i) { if (!ws[i].is_one()) { return false; } } return true; } void maxsmt::updt_params(params_ref& p) { m_params.append(p); if (m_msolver) { m_msolver->updt_params(p); } } void maxsmt::collect_statistics(statistics& st) const { if (m_msolver) { m_msolver->collect_statistics(st); } } solver& maxsmt::s() { return m_c.get_solver(); } }; z3-4.4.0/src/opt/fu_malik.cpp0000644000175000017500000002032212540347414015530 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: fu_malik.cpp Abstract: Fu & Malik built-in optimization method. Adapted from sample code in C. Author: Anh-Dung Phan (t-anphan) 2013-10-15 Notes: --*/ #include "fu_malik.h" #include "qfbv_tactic.h" #include "tactic2solver.h" #include "goal.h" #include "probe.h" #include "tactic.h" #include "ast_pp.h" #include "model_smt2_pp.h" #include "opt_context.h" /** \brief Fu & Malik procedure for MaxSAT. This procedure is based on unsat core extraction and the at-most-one constraint. Return the number of soft-constraints that can be satisfied. Return -1 if the hard-constraints cannot be satisfied. That is, the formula cannot be satisfied even if all soft-constraints are ignored. For more information on the Fu & Malik procedure: Z. Fu and S. Malik, On solving the partial MAX-SAT problem, in International Conference on Theory and Applications of Satisfiability Testing, 2006. */ namespace opt { class fu_malik : public maxsmt_solver_base { filter_model_converter& m_fm; expr_ref_vector m_aux_soft; expr_ref_vector m_aux; model_ref m_model; public: fu_malik(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): maxsmt_solver_base(c, ws, soft), m_fm(c.fm()), m_aux_soft(soft), m_aux(m) { m_upper = rational(m_aux_soft.size() + 1); m_lower.reset(); m_assignment.resize(m_aux_soft.size(), false); } /** \brief One step of the Fu&Malik algorithm. Input: soft constraints + aux-vars (aka answer literals) Output: done/not-done when not done return updated set of soft-constraints and aux-vars. - if SAT --> terminates - if UNSAT * compute unsat core * add blocking variable to soft-constraints in the core - replace soft-constraint with the one with the blocking variable - we should also add an aux-var - replace aux-var with a new one * add at-most-one constraint with blocking */ typedef obj_hashtable expr_set; void set2vector(expr_set const& set, expr_ref_vector & es) const { es.reset(); expr_set::iterator it = set.begin(), end = set.end(); for (; it != end; ++it) { es.push_back(*it); } } void collect_statistics(statistics& st) const { st.update("opt-fm-num-steps", m_aux_soft.size() + 2 - m_upper.get_unsigned()); } void set_union(expr_set const& set1, expr_set const& set2, expr_set & set) const { set.reset(); expr_set::iterator it = set1.begin(), end = set1.end(); for (; it != end; ++it) { set.insert(*it); } it = set2.begin(); end = set2.end(); for (; it != end; ++it) { set.insert(*it); } } lbool step() { IF_VERBOSE(1, verbose_stream() << "(opt.max_sat step " << m_aux_soft.size() + 2 - m_upper.get_unsigned() << ")\n";); expr_ref_vector assumptions(m), block_vars(m); for (unsigned i = 0; i < m_aux_soft.size(); ++i) { assumptions.push_back(m.mk_not(m_aux[i].get())); } lbool is_sat = s().check_sat(assumptions.size(), assumptions.c_ptr()); if (is_sat != l_false) { return is_sat; } ptr_vector core; s().get_unsat_core(core); SASSERT(!core.empty()); // Update soft-constraints and aux_vars for (unsigned i = 0; i < m_aux_soft.size(); ++i) { bool found = false; for (unsigned j = 0; !found && j < core.size(); ++j) { found = assumptions[i].get() == core[j]; } if (!found) { continue; } app_ref block_var(m), tmp(m); block_var = m.mk_fresh_const("block_var", m.mk_bool_sort()); m_aux[i] = m.mk_fresh_const("aux", m.mk_bool_sort()); m_fm.insert(block_var->get_decl()); m_fm.insert(to_app(m_aux[i].get())->get_decl()); m_aux_soft[i] = m.mk_or(m_aux_soft[i].get(), block_var); block_vars.push_back(block_var); tmp = m.mk_or(m_aux_soft[i].get(), m_aux[i].get()); s().assert_expr(tmp); } SASSERT (!block_vars.empty()); assert_at_most_one(block_vars); IF_VERBOSE(1, verbose_stream() << "(opt.max_sat # of non-blocked soft constraints: " << m_aux_soft.size() - block_vars.size() << ")\n";); return l_false; } void assert_at_most_one(expr_ref_vector const& block_vars) { expr_ref has_one(m), has_zero(m), at_most_one(m); mk_at_most_one(block_vars.size(), block_vars.c_ptr(), has_one, has_zero); at_most_one = m.mk_or(has_one, has_zero); s().assert_expr(at_most_one); } void mk_at_most_one(unsigned n, expr* const * vars, expr_ref& has_one, expr_ref& has_zero) { SASSERT(n != 0); if (n == 1) { has_one = vars[0]; has_zero = m.mk_not(vars[0]); } else { unsigned mid = n/2; expr_ref has_one1(m), has_one2(m), has_zero1(m), has_zero2(m); mk_at_most_one(mid, vars, has_one1, has_zero1); mk_at_most_one(n-mid, vars+mid, has_one2, has_zero2); has_one = m.mk_or(m.mk_and(has_one1, has_zero2), m.mk_and(has_one2, has_zero1)); has_zero = m.mk_and(has_zero1, has_zero2); } } // TBD: bug when cancel flag is set, fu_malik returns is_sat == l_true instead of l_undef virtual lbool operator()() { lbool is_sat = l_true; if (m_aux_soft.empty()) { return is_sat; } solver::scoped_push _sp(s()); expr_ref tmp(m); TRACE("opt", tout << "soft constraints:\n"; for (unsigned i = 0; i < m_aux_soft.size(); ++i) { tout << mk_pp(m_aux_soft[i].get(), m) << "\n"; }); for (unsigned i = 0; i < m_aux_soft.size(); ++i) { m_aux.push_back(m.mk_fresh_const("p", m.mk_bool_sort())); m_fm.insert(to_app(m_aux.back())->get_decl()); tmp = m.mk_or(m_aux_soft[i].get(), m_aux[i].get()); s().assert_expr(tmp); } do { is_sat = step(); --m_upper; } while (is_sat == l_false); if (is_sat == l_true) { // Get a list satisfying m_aux_soft s().get_model(m_model); m_lower = m_upper; m_assignment.reset(); for (unsigned i = 0; i < m_soft.size(); ++i) { expr_ref val(m); VERIFY(m_model->eval(m_soft[i], val)); TRACE("opt", tout << val << "\n";); m_assignment.push_back(m.is_true(val)); } TRACE("opt", tout << "maxsat cost: " << m_upper << "\n"; model_smt2_pp(tout, m, *m_model, 0);); } // We are done and soft_constraints has // been updated with the max-sat assignment. return is_sat; } virtual void get_model(model_ref& mdl) { mdl = m_model.get(); } virtual rational get_lower() const { return rational(m_aux_soft.size())-m_upper; } virtual rational get_upper() const { return rational(m_aux_soft.size())-m_lower; } }; maxsmt_solver_base* mk_fu_malik(maxsat_context& c, weights_t & ws, expr_ref_vector const& soft) { return alloc(fu_malik, c, ws, soft); } }; z3-4.4.0/src/opt/mus.h0000644000175000017500000000215712540347414014220 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: mus.h Abstract: Basic MUS extraction Author: Nikolaj Bjorner (nbjorner) 2014-20-7 Notes: --*/ #ifndef _MUS_H_ #define _MUS_H_ namespace opt { class mus { struct imp; imp * m_imp; public: mus(solver& s, ast_manager& m); ~mus(); /** Add soft constraint. Assume that the solver context enforces that cls is equivalent to a disjunction of args. Assume also that cls is a literal. */ unsigned add_soft(expr* cls); lbool get_mus(unsigned_vector& mus); void reset(); void set_cancel(bool f); /** Instrument MUS extraction to also provide the minimal penalty model, if any is found. The minimal penalty model has the least weight for the supplied soft constraints. */ void set_soft(unsigned sz, expr* const* soft, rational const* weights); rational get_best_model(model_ref& mdl); }; }; #endif z3-4.4.0/src/opt/hitting_sets.h0000644000175000017500000000172512540347414016120 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: hitting_sets.h Abstract: Hitting set approximations. Author: Nikolaj Bjorner (nbjorner) 2014-06-06 Notes: --*/ #ifndef _HITTING_SETS_H_ #define _HITTING_SETS_H_ #include "rational.h" #include "statistics.h" #include "lbool.h" namespace opt { class hitting_sets { struct imp; imp* m_imp; public: hitting_sets(); ~hitting_sets(); void add_weight(rational const& w); void add_exists_true(unsigned sz, unsigned const* elems); void add_exists_false(unsigned sz, unsigned const* elems); lbool compute_lower(); lbool compute_upper(); void set_upper(rational const& r); rational get_lower(); rational get_upper(); bool get_value(unsigned idx); void set_cancel(bool f); void collect_statistics(::statistics& st) const; void reset(); }; }; #endif z3-4.4.0/src/opt/opt_solver.cpp0000644000175000017500000003207412540347414016144 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: opt_solver.cpp Abstract: Wraps smt::kernel as a solver for optimization Author: Anh-Dung Phan (t-anphan) 2013-10-16 Notes: Based directly on smt_solver. --*/ #include #include "reg_decl_plugins.h" #include "opt_solver.h" #include "smt_context.h" #include "theory_arith.h" #include "theory_diff_logic.h" #include "theory_dense_diff_logic.h" #include "theory_pb.h" #include "ast_pp.h" #include "ast_smt_pp.h" #include "pp_params.hpp" #include "opt_params.hpp" #include "model_smt2_pp.h" #include "stopwatch.h" namespace opt { opt_solver::opt_solver(ast_manager & mgr, params_ref const & p, filter_model_converter& fm): solver_na2as(mgr), m_params(p), m_context(mgr, m_params), m(mgr), m_fm(fm), m_objective_terms(m), m_dump_benchmarks(false), m_first(true) { m_params.updt_params(p); m_params.m_relevancy_lvl = 0; } unsigned opt_solver::m_dump_count = 0; opt_solver::~opt_solver() { } void opt_solver::updt_params(params_ref & _p) { opt_params p(_p); m_dump_benchmarks = p.dump_benchmarks(); m_params.updt_params(_p); m_context.updt_params(_p); } void opt_solver::collect_param_descrs(param_descrs & r) { m_context.collect_param_descrs(r); } void opt_solver::collect_statistics(statistics & st) const { m_context.collect_statistics(st); } void opt_solver::assert_expr(expr * t) { m_context.assert_expr(t); } void opt_solver::push_core() { m_context.push(); } void opt_solver::pop_core(unsigned n) { m_context.pop(n); } void opt_solver::set_logic(symbol const& logic) { m_logic = logic; m_context.set_logic(logic); } void opt_solver::ensure_pb() { smt::theory_id th_id = m.get_family_id("pb"); smt::theory* th = get_context().get_theory(th_id); if (!th) { get_context().register_plugin(alloc(smt::theory_pb, m, m_params)); } } smt::theory_opt& opt_solver::get_optimizer() { smt::context& ctx = m_context.get_context(); smt::theory_id arith_id = m_context.m().get_family_id("arith"); smt::theory* arith_theory = ctx.get_theory(arith_id); if (!arith_theory) { ctx.register_plugin(alloc(smt::theory_mi_arith, m, m_params)); arith_theory = ctx.get_theory(arith_id); SASSERT(arith_theory); } if (typeid(smt::theory_mi_arith) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_i_arith) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_inf_arith) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_rdl&) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_idl&) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_dense_mi&) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_dense_i&) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_dense_smi&) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_dense_si&) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else { UNREACHABLE(); return dynamic_cast(*arith_theory); } } bool opt_solver::dump_benchmarks() { return m_dump_benchmarks; } lbool opt_solver::check_sat_core(unsigned num_assumptions, expr * const * assumptions) { TRACE("opt_verbose", { tout << "context size: " << m_context.size() << "\n"; for (unsigned i = 0; i < m_context.size(); ++i) { tout << mk_pp(m_context.get_formulas()[i], m_context.m()) << "\n"; } }); stopwatch w; if (dump_benchmarks()) { w.start(); std::stringstream file_name; file_name << "opt_solver" << ++m_dump_count << ".smt2"; std::ofstream buffer(file_name.str().c_str()); to_smt2_benchmark(buffer, num_assumptions, assumptions, "opt_solver", ""); buffer.close(); IF_VERBOSE(1, verbose_stream() << "(created benchmark: " << file_name.str() << "..."; verbose_stream().flush();); } lbool r; if (m_first && num_assumptions == 0 && m_context.get_scope_level() == 0) { r = m_context.setup_and_check(); } else { r = m_context.check(num_assumptions, assumptions); } m_first = false; if (dump_benchmarks()) { w.stop(); IF_VERBOSE(1, verbose_stream() << ".. " << r << " " << std::fixed << w.get_seconds() << ")\n";); } return r; } void opt_solver::maximize_objectives(expr_ref_vector& blockers) { expr_ref blocker(m); for (unsigned i = 0; i < m_objective_vars.size(); ++i) { maximize_objective(i, blocker); blockers.push_back(blocker); } } /** \brief maximize the value of objective i in the current state. Return a predicate that blocks the current maximal value. The result of 'maximize' is post-processed. When maximization involves shared symbols the model produced by local optimization does not necessarily satisfy combination constraints (it may not be a real model). In this case, the model is post-processed (update_model causes an additional call to final_check to propagate theory equalities when 'has_shared' is true). */ void opt_solver::maximize_objective(unsigned i, expr_ref& blocker) { smt::theory_var v = m_objective_vars[i]; bool has_shared = false; inf_eps val = get_optimizer().maximize(v, blocker, has_shared); inf_eps val2; m_valid_objectives[i] = true; TRACE("opt", tout << (has_shared?"has shared":"non-shared") << "\n";); if (m_context.get_context().update_model(has_shared)) { if (has_shared && val != current_objective_value(i)) { decrement_value(i, val); } else { set_model(i); } } else { SASSERT(has_shared); decrement_value(i, val); } m_objective_values[i] = val; TRACE("opt", { tout << "objective: " << mk_pp(m_objective_terms[i].get(), m) << "\n"; tout << "maximal value: " << val << "\n"; tout << "new condition: " << blocker << "\n"; model_smt2_pp(tout << "update model:\n", m, *m_models[i], 0); }); } void opt_solver::set_model(unsigned i) { model_ref mdl; get_model(mdl); m_models.set(i, mdl.get()); } lbool opt_solver::decrement_value(unsigned i, inf_eps& val) { push_core(); expr_ref ge = mk_ge(i, val); TRACE("opt", tout << ge << "\n";); assert_expr(ge); lbool is_sat = m_context.check(0, 0); if (is_sat == l_true) { set_model(i); } pop_core(1); TRACE("opt", tout << is_sat << "\n";); if (is_sat != l_true) { // cop-out approximation if (arith_util(m).is_real(m_objective_terms[i].get())) { val -= inf_eps(inf_rational(rational(0), true)); } else { val -= inf_eps(inf_rational(rational(1))); } m_valid_objectives[i] = false; } return is_sat; } void opt_solver::get_unsat_core(ptr_vector & r) { unsigned sz = m_context.get_unsat_core_size(); for (unsigned i = 0; i < sz; i++) { r.push_back(m_context.get_unsat_core_expr(i)); } } void opt_solver::get_model(model_ref & m) { m_context.get_model(m); } proof * opt_solver::get_proof() { return m_context.get_proof(); } std::string opt_solver::reason_unknown() const { return m_context.last_failure_as_string(); } void opt_solver::get_labels(svector & r) { buffer tmp; m_context.get_relevant_labels(0, tmp); r.append(tmp.size(), tmp.c_ptr()); } void opt_solver::set_cancel(bool f) { m_context.set_cancel(f); } void opt_solver::set_progress_callback(progress_callback * callback) { m_callback = callback; m_context.set_progress_callback(callback); } unsigned opt_solver::get_num_assertions() const { return m_context.size(); } expr * opt_solver::get_assertion(unsigned idx) const { SASSERT(idx < get_num_assertions()); return m_context.get_formulas()[idx]; } void opt_solver::display(std::ostream & out) const { m_context.display(out); } smt::theory_var opt_solver::add_objective(app* term) { smt::theory_var v = get_optimizer().add_objective(term); m_objective_vars.push_back(v); m_objective_values.push_back(inf_eps(rational(-1), inf_rational())); m_objective_terms.push_back(term); m_valid_objectives.push_back(true); m_models.push_back(0); return v; } vector const& opt_solver::get_objective_values() { return m_objective_values; } inf_eps const& opt_solver::saved_objective_value(unsigned i) { return m_objective_values[i]; } inf_eps opt_solver::current_objective_value(unsigned i) { smt::theory_var v = m_objective_vars[i]; return get_optimizer().value(v); } expr_ref opt_solver::mk_ge(unsigned var, inf_eps const& val) { smt::theory_opt& opt = get_optimizer(); smt::theory_var v = m_objective_vars[var]; if (typeid(smt::theory_inf_arith) == typeid(opt)) { smt::theory_inf_arith& th = dynamic_cast(opt); return th.mk_ge(m_fm, v, val); } if (typeid(smt::theory_mi_arith) == typeid(opt)) { smt::theory_mi_arith& th = dynamic_cast(opt); SASSERT(val.is_finite()); return th.mk_ge(m_fm, v, val.get_numeral()); } if (typeid(smt::theory_i_arith) == typeid(opt)) { SASSERT(val.is_finite()); SASSERT(val.get_infinitesimal().is_zero()); smt::theory_i_arith& th = dynamic_cast(opt); return th.mk_ge(m_fm, v, val.get_rational()); } if (typeid(smt::theory_idl) == typeid(opt)) { smt::theory_idl& th = dynamic_cast(opt); return th.mk_ge(m_fm, v, val.get_rational()); } if (typeid(smt::theory_rdl) == typeid(opt) && val.get_infinitesimal().is_zero()) { smt::theory_rdl& th = dynamic_cast(opt); return th.mk_ge(m_fm, v, val.get_rational()); } // difference logic? return expr_ref(m.mk_true(), m); } void opt_solver::reset_objectives() { m_objective_vars.reset(); m_objective_values.reset(); m_objective_terms.reset(); m_valid_objectives.reset(); } opt_solver& opt_solver::to_opt(solver& s) { if (typeid(opt_solver) != typeid(s)) { throw default_exception("BUG: optimization context has not been initialized correctly"); } return dynamic_cast(s); } void opt_solver::to_smt2_benchmark( std::ofstream & buffer, unsigned num_assumptions, expr * const * assumptions, char const * name, char const * logic, char const * status, char const * attributes) { ast_smt_pp pp(m); pp.set_benchmark_name(name); pp.set_logic(logic); pp.set_status(status); pp.add_attributes(attributes); pp_params params; pp.set_simplify_implies(params.simplify_implies()); for (unsigned i = 0; i < num_assumptions; ++i) { pp.add_assumption(assumptions[i]); } for (unsigned i = 0; i < get_num_assertions(); ++i) { pp.add_assumption(get_assertion(i)); } pp.display_smt2(buffer, m.mk_true()); } } z3-4.4.0/src/opt/wmax.h0000644000175000017500000000055112540347414014364 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: wmax.h Abstract: Theory Solver based MaxSAT. Author: Nikolaj Bjorner (nbjorner) 2014-4-17 Notes: --*/ #ifndef _WMAX_H_ #define _WMAX_H_ #include "maxsmt.h" namespace opt { maxsmt_solver_base* mk_wmax(maxsat_context& c, weights_t & ws, expr_ref_vector const& soft); } #endif z3-4.4.0/src/opt/maxsls.cpp0000644000175000017500000000256012540347414015254 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: maxsls.cpp Abstract: Weighted SLS MAXSAT module Author: Nikolaj Bjorner (nbjorner) 2014-4-17 Notes: --*/ #include "maxsls.h" #include "ast_pp.h" #include "model_smt2_pp.h" namespace opt { class sls : public maxsmt_solver_base { public: sls(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): maxsmt_solver_base(c, ws, soft) { } virtual ~sls() {} lbool operator()() { IF_VERBOSE(1, verbose_stream() << "(opt.sls)\n";); init(); set_enable_sls(true); enable_sls(m_soft, m_weights); lbool is_sat = s().check_sat(0, 0); if (is_sat == l_true) { s().get_model(m_model); m_upper.reset(); for (unsigned i = 0; i < m_soft.size(); ++i) { expr_ref tmp(m); m_model->eval(m_soft[i], tmp, true); m_assignment[i] = m.is_true(tmp); if (!m_assignment[i]) { m_upper += m_weights[i]; } } } return is_sat; } }; maxsmt_solver_base* mk_sls( maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { return alloc(sls, c, ws, soft); } }; z3-4.4.0/src/opt/opt_context.h0000644000175000017500000002542412540347414015764 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: opt_context.h Abstract: Facility for running optimization problem. Author: Anh-Dung Phan (t-anphan) 2013-10-16 Notes: --*/ #ifndef _OPT_CONTEXT_H_ #define _OPT_CONTEXT_H_ #include "ast.h" #include "opt_solver.h" #include "opt_pareto.h" #include "optsmt.h" #include "maxsmt.h" #include "model_converter.h" #include "tactic.h" #include "arith_decl_plugin.h" #include "bv_decl_plugin.h" #include "cmd_context.h" namespace opt { class opt_solver; /** \brief base class required by MaxSMT solvers. By implementing a base class, you can invoke the MaxSMT solvers independent of the overall optimization infrastructure. The caller has to supply a solver object that encapsulates an incremental SAT or SMT solver. The MaxSMT solvers may assume that the solver object should be in a satisfiable state and contain an initial model. */ class maxsat_context { public: virtual filter_model_converter& fm() = 0; // converter that removes fresh names introduced by simplification. virtual bool sat_enabled() const = 0; // is using th SAT solver core enabled? virtual solver& get_solver() = 0; // retrieve solver object (SAT or SMT solver) virtual ast_manager& get_manager() = 0; virtual params_ref& params() = 0; virtual void enable_sls(expr_ref_vector const& soft, weights_t& weights) = 0; // stochastic local search virtual void set_enable_sls(bool f) = 0; // overwrite whether SLS is enabled. virtual void set_soft_assumptions() = 0; // configure SAT solver to skip assumptions assigned by unit-propagation virtual symbol const& maxsat_engine() const = 0; // retrieve maxsat engine configuration parameter. virtual void get_base_model(model_ref& _m) = 0; // retrieve model from initial satisfiability call. virtual smt::context& smt_context() = 0; // access SMT context for SMT based MaxSMT solver (wmax requires SMT core) }; /** \brief main context object for optimization. Hard and soft assertions, and objectives are registered with this context. It handles combinations of objectives. */ class context : public opt_wrapper, public pareto_callback, public maxsat_context { struct free_func_visitor; typedef map map_t; typedef map map_id; typedef vector > bounds_t; enum objective_t { O_MAXIMIZE, O_MINIMIZE, O_MAXSMT }; struct objective { objective_t m_type; app_ref m_term; // for maximize, minimize term expr_ref_vector m_terms; // for maxsmt vector m_weights; // for maxsmt adjust_value m_adjust_value; symbol m_id; // for maxsmt unsigned m_index; // for maximize/minimize index objective(bool is_max, app_ref& t, unsigned idx): m_type(is_max?O_MAXIMIZE:O_MINIMIZE), m_term(t), m_terms(t.get_manager()), m_id(), m_index(idx) { if (!is_max) { m_adjust_value.set_negate(true); } } objective(ast_manager& m, symbol id): m_type(O_MAXSMT), m_term(m), m_terms(m), m_id(id), m_index(0) {} }; class scoped_state { ast_manager& m; arith_util m_arith; bv_util m_bv; unsigned_vector m_hard_lim; unsigned_vector m_objectives_lim; unsigned_vector m_objectives_term_trail; unsigned_vector m_objectives_term_trail_lim; map_id m_indices; public: expr_ref_vector m_hard; vector m_objectives; scoped_state(ast_manager& m): m(m), m_arith(m), m_bv(m), m_hard(m) {} void push(); void pop(); void add(expr* hard); bool set(ptr_vector & hard); unsigned add(expr* soft, rational const& weight, symbol const& id); unsigned add(app* obj, bool is_max); }; ast_manager& m; arith_util m_arith; bv_util m_bv; expr_ref_vector m_hard_constraints; ref m_opt_solver; ref m_solver; ref m_sat_solver; scoped_ptr m_pareto; sref_vector m_box_models; unsigned m_box_index; params_ref m_params; optsmt m_optsmt; map_t m_maxsmts; scoped_state m_scoped_state; vector m_objectives; model_ref m_model; model_converter_ref m_model_converter; filter_model_converter m_fm; obj_map m_objective_fns; obj_map m_objective_orig; func_decl_ref_vector m_objective_refs; tactic_ref m_simplify; bool m_enable_sat; bool m_enable_sls; bool m_is_clausal; bool m_pp_neat; symbol m_maxsat_engine; symbol m_logic; public: context(ast_manager& m); virtual ~context(); unsigned add_soft_constraint(expr* f, rational const& w, symbol const& id); unsigned add_objective(app* t, bool is_max); void add_hard_constraint(expr* f); virtual void push(); virtual void pop(unsigned n); virtual bool empty() { return m_scoped_state.m_objectives.empty(); } virtual void set_cancel(bool f); virtual void reset_cancel() { set_cancel(false); } virtual void cancel() { set_cancel(true); } virtual void set_hard_constraints(ptr_vector & hard); virtual lbool optimize(); virtual bool print_model() const; virtual void get_model(model_ref& _m); virtual void set_model(model_ref& _m); virtual void fix_model(model_ref& _m); virtual void collect_statistics(statistics& stats) const; virtual proof* get_proof() { return 0; } virtual void get_labels(svector & r) {} virtual void get_unsat_core(ptr_vector & r) {} virtual std::string reason_unknown() const { return std::string("unknown"); } virtual void display_assignment(std::ostream& out); virtual bool is_pareto() { return m_pareto.get() != 0; } virtual void set_logic(symbol const& s) { m_logic = s; } void set_clausal(bool f) { m_is_clausal = f; } void display(std::ostream& out); static void collect_param_descrs(param_descrs & r); void updt_params(params_ref& p); params_ref& get_params() { return m_params; } expr_ref get_lower(unsigned idx); expr_ref get_upper(unsigned idx); std::string to_string() const; virtual unsigned num_objectives() { return m_objectives.size(); } virtual expr_ref mk_gt(unsigned i, model_ref& model); virtual expr_ref mk_ge(unsigned i, model_ref& model); virtual expr_ref mk_le(unsigned i, model_ref& model); virtual smt::context& smt_context() { return m_opt_solver->get_context(); } virtual filter_model_converter& fm() { return m_fm; } virtual bool sat_enabled() const { return 0 != m_sat_solver.get(); } virtual solver& get_solver(); virtual ast_manager& get_manager() { return this->m; } virtual params_ref& params() { return m_params; } virtual void enable_sls(expr_ref_vector const& soft, weights_t& weights); virtual void set_enable_sls(bool f) { m_enable_sls = f; } virtual void set_soft_assumptions(); virtual symbol const& maxsat_engine() const { return m_maxsat_engine; } virtual void get_base_model(model_ref& _m); private: void validate_feasibility(maxsmt& ms); lbool execute(objective const& obj, bool committed, bool scoped); lbool execute_min_max(unsigned index, bool committed, bool scoped, bool is_max); lbool execute_maxsat(symbol const& s, bool committed, bool scoped); lbool execute_lex(); lbool execute_box(); lbool execute_pareto(); expr_ref to_expr(inf_eps const& n); void reset_maxsmts(); void import_scoped_state(); void normalize(); void internalize(); bool is_maximize(expr* fml, app_ref& term, expr*& orig_term, unsigned& index); bool is_minimize(expr* fml, app_ref& term, expr*& orig_term, unsigned& index); bool is_maxsat(expr* fml, expr_ref_vector& terms, vector& weights, rational& offset, bool& neg, symbol& id, unsigned& index); expr* mk_maximize(unsigned index, app* t); expr* mk_minimize(unsigned index, app* t); expr* mk_maxsat(unsigned index, unsigned num_fmls, expr* const* fmls); expr* mk_objective_fn(unsigned index, objective_t ty, unsigned sz, expr*const* args); void to_fmls(expr_ref_vector& fmls); void from_fmls(expr_ref_vector const& fmls); void simplify_fmls(expr_ref_vector& fmls); void mk_atomic(expr_ref_vector& terms); void update_lower() { update_bound(true); } void update_bound(bool is_lower); inf_eps get_lower_as_num(unsigned idx); inf_eps get_upper_as_num(unsigned idx); struct is_bv; bool probe_bv(); struct is_propositional_fn; bool is_propositional(expr* e); void init_solver(); void update_solver(); void setup_arith_solver(); void add_maxsmt(symbol const& id); void set_simplify(tactic *simplify); void set_pareto(pareto_base* p); void clear_state(); bool is_numeral(expr* e, rational& n) const; void display_objective(std::ostream& out, objective const& obj) const; void display_bounds(std::ostream& out, bounds_t const& b) const; void validate_lex(); // pareto void yield(); expr_ref mk_ge(expr* t, expr* s); expr_ref mk_cmp(bool is_ge, model_ref& mdl, objective const& obj); }; } #endif z3-4.4.0/src/opt/hitting_sets.cpp0000644000175000017500000011531112540347414016450 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: hitting_sets.h Abstract: Hitting set approximations. Author: Nikolaj Bjorner (nbjorner) 2014-06-06 Notes: --*/ #include "vector.h" #include "util.h" #include "hitting_sets.h" #include "simplex.h" #include "sparse_matrix_def.h" #include "simplex_def.h" typedef simplex::simplex Simplex; typedef simplex::sparse_matrix sparse_matrix; namespace opt { struct hitting_sets::imp { class justification { public: enum kind_t { AXIOM, DECISION, CLAUSE }; private: kind_t m_kind; unsigned m_value; bool m_pos; public: explicit justification(kind_t k):m_kind(k), m_value(0), m_pos(false) {} explicit justification(unsigned v, bool pos):m_kind(CLAUSE), m_value(v), m_pos(pos) {} justification(justification const& other): m_kind(other.m_kind), m_value(other.m_value), m_pos(other.m_pos) {} justification& operator=(justification const& other) { m_kind = other.m_kind; m_value = other.m_value; m_pos = other.m_pos; return *this; } unsigned clause() const { return m_value; } bool is_axiom() const { return m_kind == AXIOM; } bool is_decision() const { return m_kind == DECISION; } bool is_clause() const { return m_kind == CLAUSE; } kind_t kind() const { return m_kind; } bool pos() const { return m_pos; } }; class set { unsigned m_num_elems; unsigned m_elems[0]; set(): m_num_elems(0) {} public: static set* mk(small_object_allocator& alloc, unsigned sz, unsigned const* elems) { unsigned size = (sz+1)*sizeof(unsigned); void * mem = alloc.allocate(size); set* result = new (mem) set(); result->m_num_elems = sz; memcpy(result->m_elems, elems, sizeof(unsigned)*sz); return result; } inline unsigned operator[](unsigned idx) const { SASSERT(idx < m_num_elems); return m_elems[idx]; } inline unsigned& operator[](unsigned idx) { SASSERT(idx < m_num_elems); return m_elems[idx]; } unsigned size() const { return m_num_elems; } unsigned alloc_size() const { return (m_num_elems + 1)*sizeof(unsigned); } bool empty() const { return 0 == size(); } }; volatile bool m_cancel; rational m_lower; rational m_upper; vector m_weights; vector m_weights_inv; rational m_max_weight; rational m_denominator; small_object_allocator m_alloc; ptr_vector m_T; ptr_vector m_F; svector m_value; svector m_model; vector m_tuse_list; vector m_fuse_list; // Custom CDCL solver. svector m_justification; vector m_twatch; vector m_fwatch; unsigned_vector m_level; unsigned_vector m_trail; // trail of assigned literals unsigned m_qhead; // queue head justification m_conflict_j; // conflict justification unsigned m_conflict_l; // conflict literal bool m_inconsistent; unsigned m_scope_lvl; rational m_weight; // current weight of assignment. unsigned_vector m_indices; unsigned_vector m_scores; vector m_scored_weights; svector m_score_updated; bool m_enable_simplex; struct compare_scores { imp* m_imp; compare_scores():m_imp(0) {} bool operator()(int v1, int v2) const { return m_imp->m_scored_weights[v1] > m_imp->m_scored_weights[v2]; } }; compare_scores m_compare_scores; heap m_heap; svector m_mark; struct scope { unsigned m_trail_lim; }; vector m_scopes; unsigned_vector m_lemma; unsigned m_conflict_lvl; // simplex unsynch_mpz_manager m; Simplex m_simplex; unsigned m_weights_var; static unsigned const null_idx = UINT_MAX; imp(): m_cancel(false), m_max_weight(0), m_denominator(1), m_alloc("hitting-sets"), m_qhead(0), m_conflict_j(justification(justification::AXIOM)), m_inconsistent(false), m_scope_lvl(0), m_compare_scores(), m_heap(0, m_compare_scores), m_weights_var(0) { m_enable_simplex = true; m_compare_scores.m_imp = this; } ~imp() { for (unsigned i = 0; i < m_T.size(); ++i) { m_alloc.deallocate(m_T[i]->alloc_size(), m_T[i]); } for (unsigned i = 0; i < m_F.size(); ++i) { m_alloc.deallocate(m_F[i]->alloc_size(), m_F[i]); } } void add_weight(rational const& w) { SASSERT(w.is_pos()); unsigned var = m_weights.size(); m_simplex.ensure_var(var); m_simplex.set_lower(var, mpq_inf(mpq(0),mpq(0))); m_simplex.set_upper(var, mpq_inf(mpq(1),mpq(0))); m_weights.push_back(w); m_weights_inv.push_back(rational::one()); m_value.push_back(l_undef); m_justification.push_back(justification(justification::DECISION)); m_tuse_list.push_back(unsigned_vector()); m_fuse_list.push_back(unsigned_vector()); m_twatch.push_back(unsigned_vector()); m_fwatch.push_back(unsigned_vector()); m_level.push_back(0); m_indices.push_back(var); m_model.push_back(l_undef); m_mark.push_back(false); m_scores.push_back(0); m_scored_weights.push_back(rational(0)); m_score_updated.push_back(true); m_max_weight += w; } justification add_exists_false(unsigned sz, unsigned const* S) { return add_exists(sz, S, true); } justification add_exists_true(unsigned sz, unsigned const* S) { return add_exists(sz, S, false); } justification add_exists(unsigned sz, unsigned const* S, bool sign) { vector& use_list = sign?m_fuse_list:m_tuse_list; lbool val = sign?l_false:l_true; justification j(justification::AXIOM); ptr_vector& Sets = sign?m_F:m_T; vector& watch = sign?m_fwatch:m_twatch; init_weights(); if (sz == 0) { set_conflict(0, justification(justification::AXIOM)); } else if (sz == 1) { IF_VERBOSE(2, verbose_stream() << "unit literal : " << S[0] << " " << val << "\n";); assign(S[0], val, justification(justification::AXIOM)); } else { unsigned clause_id = Sets.size(); for (unsigned i = 0; i < sz; ++i) { use_list[S[i]].push_back(clause_id); } j = justification(clause_id, !sign); watch[S[0]].push_back(clause_id); watch[S[1]].push_back(clause_id); Sets.push_back(set::mk(m_alloc, sz, S)); if (!sign) { pop(scope_lvl()); inc_score(clause_id); } TRACE("opt", display(tout, j);); IF_VERBOSE(2, if (!sign) display(verbose_stream(), j);); if (!sign && m_enable_simplex) { add_simplex_row(!sign, sz, S); } } return j; } lbool compute_lower() { m_lower.reset(); rational w1 = L1(); rational w2 = L2(); rational w3 = L3(); if (w1 > m_lower) m_lower = w1; if (w2 > m_lower) m_lower = w2; if (w3 > m_lower) m_lower = w3; return l_true; } lbool compute_upper() { m_upper = m_max_weight; unsigned fsz = m_F.size(); lbool r = search(); pop(scope_lvl()); IF_VERBOSE(1, verbose_stream() << "(hsmax.negated-size: " << fsz << ")\n";); #if 0 // garbage collect agressively on exit. // all learned clases for negative branches are // pruned. for (unsigned i = fsz; i < m_F.size(); ++i) { m_alloc.deallocate(m_F[i]->alloc_size(), m_F[i]); } m_F.resize(fsz); for (unsigned i = 0; i < m_fuse_list.size(); ++i) { unsigned_vector & uses = m_fuse_list[i]; while (!uses.empty() && uses.back() >= fsz) uses.pop_back(); unsigned_vector & watch = m_fwatch[i]; unsigned j = 0, k = 0; for (; j < watch.size(); ++j) { if (watch[j] < fsz) { watch[k] = watch[j]; ++k; } } watch.resize(k); } #endif return r; } rational get_lower() { return m_lower/m_denominator; } rational get_upper() { return m_upper/m_denominator; } void set_upper(rational const& r) { m_max_weight = r*m_denominator; } bool get_value(unsigned idx) { return idx < m_model.size() && m_model[idx] == l_true; } void set_cancel(bool f) { m_cancel = f; m_simplex.set_cancel(f); } void collect_statistics(::statistics& st) const { m_simplex.collect_statistics(st); } void reset() { m_lower.reset(); m_upper = m_max_weight; } void init_weights() { if (m_weights_var != 0) { return; } m_weights_var = m_weights.size(); unsigned_vector vars; scoped_mpz_vector coeffs(m); // normalize weights to integral. rational d(1); for (unsigned i = 0; i < m_weights.size(); ++i) { d = lcm(d, denominator(m_weights[i])); } m_denominator = d; if (!d.is_one()) { for (unsigned i = 0; i < m_weights.size(); ++i) { m_weights[i] *= d; } } rational lc(1); for (unsigned i = 0; i < m_weights.size(); ++i) { lc = lcm(lc, m_weights[i]); } for (unsigned i = 0; i < m_weights.size(); ++i) { m_weights_inv[i] = lc/m_weights[i]; } m_heap.set_bounds(m_weights.size()); for (unsigned i = 0; i < m_weights.size(); ++i) { m_heap.insert(i); } update_heap(); // set up Simplex objective function. for (unsigned i = 0; i < m_weights.size(); ++i) { vars.push_back(i); coeffs.push_back(m_weights[i].to_mpq().numerator()); } m_simplex.ensure_var(m_weights_var); vars.push_back(m_weights_var); coeffs.push_back(mpz(-1)); m_simplex.add_row(m_weights_var, coeffs.size(), vars.c_ptr(), coeffs.c_ptr()); } void display(std::ostream& out) const { out << "inconsistent: " << m_inconsistent << "\n"; out << "weight: " << m_weight << "\n"; for (unsigned i = 0; i < m_weights.size(); ++i) { out << i << ": " << value(i) << " w: " << m_weights[i] << " s: " << m_scores[i] << "\n"; } for (unsigned i = 0; i < m_T.size(); ++i) { display(out << "+" << i << ": ", *m_T[i]); } for (unsigned i = 0; i < m_F.size(); ++i) { display(out << "-" << i << ": ", *m_F[i]); } out << "watch lists:\n"; for (unsigned i = 0; i < m_fwatch.size(); ++i) { out << i << ": "; for (unsigned j = 0; j < m_twatch[i].size(); ++j) { out << "+" << m_twatch[i][j] << " "; } for (unsigned j = 0; j < m_fwatch[i].size(); ++j) { out << "-" << m_fwatch[i][j] << " "; } out << "\n"; } out << "trail\n"; for (unsigned i = 0; i < m_trail.size(); ++i) { unsigned idx = m_trail[i]; out << (m_justification[idx].is_decision()?"d":"") << idx << " "; } out << "\n"; } void display(std::ostream& out, set const& S) const { for (unsigned i = 0; i < S.size(); ++i) { out << S[i] << " "; } out << "\n"; } void display(std::ostream& out, justification const& j) const { switch(j.kind()) { case justification::AXIOM: out << "axiom\n"; break; case justification::DECISION: out << "decision\n"; break; case justification::CLAUSE: { out << "clause: "; set const& S = j.pos()?(*m_T[j.clause()]):(*m_F[j.clause()]); for (unsigned i = 0; i < S.size(); ++i) { out << S[i] << " "; } out << "\n"; } } } void display_lemma(std::ostream& out) { out << "lemma: "; for (unsigned i = 0; i < m_lemma.size(); ++i) { out << m_lemma[i] << " "; } out << "\n"; } struct scoped_push { imp& s; scoped_push(imp& s):s(s) { s.push(); } ~scoped_push() { s.pop(1); } }; struct value_lt { vector const& weights; value_lt(vector const& weights): weights(weights) {} bool operator()(int v1, int v2) const { return weights[v1] > weights[v2]; } }; void inc_score(unsigned clause_id) { set const& S = *m_T[clause_id]; if (!has_selected(S)) { for (unsigned j = 0; j < S.size(); ++j) { ++m_scores[S[j]]; m_score_updated[S[j]] = true; } } } void dec_score(unsigned clause_id) { set const& S = *m_T[clause_id]; if (!has_selected(S)) { for (unsigned j = 0; j < S.size(); ++j) { SASSERT(m_scores[S[j]] > 0); --m_scores[S[j]]; m_score_updated[S[j]] = true; } } } void update_score(unsigned idx, bool inc) { unsigned_vector const& uses = m_tuse_list[idx]; for (unsigned i = 0; i < uses.size(); ++i) { if (inc) { inc_score(uses[i]); } else { dec_score(uses[i]); } } } rational L1() { rational w(m_weight); scoped_push _sc(*this); for (unsigned i = 0; !canceled() && i < m_T.size(); ++i) { set const& S = *m_T[i]; SASSERT(!S.empty()); if (!has_selected(S)) { w += m_weights[select_min(S)]; for (unsigned j = 0; j < S.size(); ++j) { assign(S[j], l_true, justification(justification::DECISION)); } } } return w; } void update_heap() { for (unsigned i = 0; i < m_scored_weights.size(); ++i) { if (m_score_updated[i]) { rational const& old_w = m_scored_weights[i]; rational new_w = rational(m_scores[i])*m_weights_inv[i]; if (new_w > old_w) { m_scored_weights[i] = new_w; //m_heap.decreased(i); } else if (new_w < old_w) { m_scored_weights[i] = new_w; //m_heap.increased(i); } m_score_updated[i] = false; } } } rational L2() { rational w(m_weight); scoped_push _sc(*this); int n = 0; for (unsigned i = 0; i < m_T.size(); ++i) { if (!has_selected(*m_T[i])) ++n; } update_heap(); value_lt lt(m_scored_weights); std::sort(m_indices.begin(), m_indices.end(), lt); for(unsigned i = 0; i < m_indices.size() && n > 0; ++i) { // deg(c) = score(c) // wt(c) = m_weights[c] unsigned idx = m_indices[i]; if (m_scores[idx] == 0) { break; } if (m_scores[idx] < static_cast(n) || m_weights[idx].is_one()) { w += m_weights[idx]; } else { w += div((rational(n)*m_weights[idx]), rational(m_scores[idx])); } n -= m_scores[idx]; } return w; } rational L3() { TRACE("simplex", m_simplex.display(tout);); VERIFY(l_true == m_simplex.make_feasible()); TRACE("simplex", m_simplex.display(tout);); VERIFY(l_true == m_simplex.minimize(m_weights_var)); mpq_inf const& val = m_simplex.get_value(m_weights_var); unsynch_mpq_inf_manager mg; unsynch_mpq_manager& mq = mg.get_mpq_manager(); scoped_mpq c(mq); mg.ceil(val, c); rational w(c); CTRACE("simplex", w >= m_weight, tout << w << " " << m_weight << " !!!!\n"; display(tout);); SASSERT(w >= m_weight); return w; } void add_simplex_row(bool is_some_true, unsigned sz, unsigned const* S) { unsigned_vector vars; scoped_mpz_vector coeffs(m); for (unsigned i = 0; i < sz; ++i) { vars.push_back(S[i]); coeffs.push_back(mpz(1)); } unsigned base_var = m_F.size() + m_T.size() + m_weights.size(); m_simplex.ensure_var(base_var); vars.push_back(base_var); coeffs.push_back(mpz(-1)); // S - base_var = 0 if (is_some_true) { // base_var >= 1 m_simplex.set_lower(base_var, mpq_inf(mpq(1),mpq(0))); } else { // base_var <= sz-1 m_simplex.set_upper(base_var, mpq_inf(mpq(sz-1),mpq(0))); } m_simplex.add_row(base_var, coeffs.size(), vars.c_ptr(), coeffs.c_ptr()); } unsigned select_min(set const& S) { unsigned result = S[0]; for (unsigned i = 1; i < S.size(); ++i) { if (m_weights[result] > m_weights[S[i]]) { result = S[i]; } } return result; } bool have_selected(lbool val, ptr_vector const& Sets, unsigned& i) { for (i = 0; i < Sets.size(); ++i) { if (!has_selected(val, *Sets[i])) return false; } return true; } void set_undef_to_false() { for (unsigned i = 0; i < m_model.size(); ++i) { if (m_model[i] == l_undef) { m_model[i] = l_false; } } } bool values_satisfy_Fs(unsigned& i) { unsigned j = 0; for (i = 0; i < m_F.size(); ++i) { set const& F = *m_F[i]; for (j = 0; j < F.size(); ++j) { if (m_model[F[j]] == l_false) { break; } } if (F.size() == j) { break; } } return i == m_F.size(); } bool has_selected(set const& S) { return has_selected(l_true, S); } bool has_unselected(set const& S) { return has_selected(l_false, S); } bool has_unset(set const& S) { return has_selected(l_undef, S); } bool has_selected(lbool val, set const& S) { for (unsigned i = 0; i < S.size(); ++i) { if (val == value(S[i])) { return true; } } return false; } // (greedy) CDCL learner for hitting sets. inline unsigned scope_lvl() const { return m_scope_lvl; } inline bool inconsistent() const { return m_inconsistent; } inline bool canceled() const { return m_cancel; } inline unsigned lvl(unsigned idx) const { return m_level[idx]; } inline lbool value(unsigned idx) const { return m_value[idx]; } inline bool is_marked(unsigned v) const { return m_mark[v] != 0; } inline void mark(unsigned v) { SASSERT(!is_marked(v)); m_mark[v] = true; } inline void reset_mark(unsigned v) { SASSERT(is_marked(v)); m_mark[v] = false; } void push() { SASSERT(!inconsistent()); ++m_scope_lvl; m_scopes.push_back(scope()); scope& s = m_scopes.back(); s.m_trail_lim = m_trail.size(); } void pop(unsigned n) { if (n > 0) { m_inconsistent = false; m_scope_lvl = scope_lvl() - n; unassign(m_scopes[scope_lvl()].m_trail_lim); m_scopes.shrink(scope_lvl()); } } void assign(unsigned idx, lbool val, justification const& justification) { if (val == l_true) { m_weight += m_weights[idx]; update_score(idx, false); if (m_enable_simplex) { m_simplex.set_lower(idx, mpq_inf(mpq(1),mpq(0))); } } SASSERT(val != l_true || m_scores[idx] == 0); m_value[idx] = val; m_justification[idx] = justification; m_trail.push_back(idx); m_level[idx] = scope_lvl(); TRACE("opt", tout << idx << " := " << val << " scope: " << scope_lvl() << " w: " << m_weight << "\n";); } svector m_replay_idx; svector m_replay_val; void unassign(unsigned sz) { for (unsigned j = sz; j < m_trail.size(); ++j) { unsigned idx = m_trail[j]; lbool val = value(idx); m_value[idx] = l_undef; if (val == l_true) { m_weight -= m_weights[idx]; update_score(idx, true); if (m_enable_simplex) { m_simplex.set_lower(idx, mpq_inf(mpq(0),mpq(0))); } } if (m_justification[idx].is_axiom()) { m_replay_idx.push_back(idx); m_replay_val.push_back(val); } } TRACE("opt", tout << m_weight << "\n";); m_trail.shrink(sz); m_qhead = sz; for (unsigned i = m_replay_idx.size(); i > 0; ) { --i; unsigned idx = m_replay_idx[i]; lbool val = m_replay_val[i]; assign(idx, val, justification(justification::AXIOM)); } m_replay_idx.reset(); m_replay_val.reset(); } lbool search() { TRACE("opt", display(tout);); pop(scope_lvl()); while (true) { while (true) { propagate(); if (canceled()) return l_undef; if (!inconsistent()) break; if (!resolve_conflict()) return l_false; SASSERT(!inconsistent()); } if (!decide()) { SASSERT(validate_model()); m_model.reset(); m_model.append(m_value); m_upper = m_weight; // SASSERT(m_weight < m_max_weight); return l_true; } } } bool validate_model() { for (unsigned i = 0; i < m_T.size(); ++i) { set const& S = *m_T[i]; bool found = false; for (unsigned j = 0; !found && j < S.size(); ++j) { found = value(S[j]) == l_true; } CTRACE("opt", !found, display(tout << "not found: " << i << "\n", S); display(tout);); SASSERT(found); } for (unsigned i = 0; i < m_F.size(); ++i) { set const& S = *m_F[i]; bool found = false; for (unsigned j = 0; !found && j < S.size(); ++j) { found = value(S[j]) != l_true; } CTRACE("opt", !found, display(tout << "not found: " << i << "\n", S); display(tout);); SASSERT(found); } return true; } bool invariant() { for (unsigned i = 0; i < m_fwatch.size(); ++i) { for (unsigned j = 0; j < m_fwatch[i].size(); ++j) { set const& S = *m_F[m_fwatch[i][j]]; SASSERT(S[0] == i || S[1] == i); } } for (unsigned i = 0; i < m_twatch.size(); ++i) { for (unsigned j = 0; j < m_twatch[i].size(); ++j) { set const& S = *m_T[m_twatch[i][j]]; SASSERT(S[0] == i || S[1] == i); } } return true; } bool resolve_conflict() { while (true) { if (!resolve_conflict_core()) return false; if (!inconsistent()) return true; } } unsigned get_max_lvl(unsigned conflict_l, justification const& conflict_j) { if (scope_lvl() == 0) return 0; unsigned r = lvl(conflict_l); if (conflict_j.is_clause()) { unsigned clause = conflict_j.clause(); ptr_vector const& S = conflict_j.pos()?m_T:m_F; r = std::max(r, lvl((*S[clause])[0])); r = std::max(r, lvl((*S[clause])[1])); } return r; } bool resolve_conflict_core() { SASSERT(inconsistent()); TRACE("opt", display(tout);); unsigned conflict_l = m_conflict_l; justification conflict_j(m_conflict_j); if (conflict_j.is_axiom()) { return false; } m_conflict_lvl = get_max_lvl(conflict_l, conflict_j); if (m_conflict_lvl == 0) { return false; } unsigned idx = skip_above_conflict_level(); unsigned num_marks = 0; m_lemma.reset(); m_lemma.push_back(0); process_antecedent(conflict_l, num_marks); do { TRACE("opt", tout << "conflict literal: " << conflict_l << "\n"; display(tout, conflict_j);); if (conflict_j.is_clause()) { unsigned cl = conflict_j.clause(); unsigned i = 0; SASSERT(value(conflict_l) != l_undef); set const& T = conflict_j.pos()?(*m_T[cl]):(*m_F[cl]); if (T[0] == conflict_l) { i = 1; } else { SASSERT(T[1] == conflict_l); process_antecedent(T[0], num_marks); i = 2; } unsigned sz = T.size(); for (; i < sz; ++i) { process_antecedent(T[i], num_marks); } } else if (conflict_j.is_decision()) { --num_marks; SASSERT(num_marks == 0); break; } else if (conflict_j.is_axiom()) { IF_VERBOSE(0, verbose_stream() << "axiom " << conflict_l << " " << value(conflict_l) << " " << num_marks << "\n";); --num_marks; SASSERT(num_marks == 0); break; } while (true) { unsigned l = m_trail[idx]; if (is_marked(l)) break; SASSERT(idx > 0); --idx; } conflict_l = m_trail[idx]; conflict_j = m_justification[conflict_l]; --idx; --num_marks; if (num_marks == 0 && value(conflict_l) == l_false) { ++num_marks; } reset_mark(conflict_l); } while (num_marks > 0); m_lemma[0] = conflict_l; TRACE("opt", display_lemma(tout);); SASSERT(value(conflict_l) == l_true); unsigned new_scope_lvl = 0; for (unsigned i = 1; i < m_lemma.size(); ++i) { SASSERT(l_true == value(m_lemma[i])); new_scope_lvl = std::max(new_scope_lvl, lvl(m_lemma[i])); reset_mark(m_lemma[i]); } pop(scope_lvl() - new_scope_lvl); SASSERT(l_undef == value(conflict_l)); justification j = add_exists_false(m_lemma.size(), m_lemma.c_ptr()); if (!j.is_axiom()) assign(conflict_l, l_false, j); return true; } void process_antecedent(unsigned antecedent, unsigned& num_marks) { unsigned alvl = lvl(antecedent); SASSERT(alvl <= m_conflict_lvl); if (!is_marked(antecedent) && alvl > 0 && !m_justification[antecedent].is_axiom()) { mark(antecedent); if (alvl == m_conflict_lvl || value(antecedent) == l_false) { ++num_marks; } else { m_lemma.push_back(antecedent); } } } unsigned skip_above_conflict_level() { unsigned idx = m_trail.size(); if (idx == 0) { return idx; } idx--; // skip literals from levels above the conflict level while (lvl(m_trail[idx]) > m_conflict_lvl) { SASSERT(idx > 0); idx--; } return idx; } void set_conflict(unsigned idx, justification const& justification) { if (!inconsistent()) { TRACE("opt", tout << "conflict: " << idx << "\n";); m_inconsistent = true; m_conflict_j = justification; m_conflict_l = idx; } } unsigned next_var() { update_heap(); value_lt lt(m_scored_weights); std::sort(m_indices.begin(), m_indices.end(), lt); unsigned idx = m_indices[0]; if (m_scores[idx] == 0) return UINT_MAX; return idx; #if 0 int min_val = m_heap.min_value(); if (min_val == -1) { return UINT_MAX; } SASSERT(0 <= min_val && static_cast(min_val) < m_weights.size()); if (m_scores[min_val] == 0) { return UINT_MAX; } return static_cast(min_val); #endif } bool decide() { unsigned idx = next_var(); if (idx == UINT_MAX) { return false; } else { push(); TRACE("opt", tout << "decide " << idx << "\n";); assign(idx, l_true, justification(justification::DECISION)); return true; } } void propagate() { TRACE("opt", display(tout);); SASSERT(invariant()); while (m_qhead < m_trail.size() && !inconsistent() && !canceled()) { unsigned idx = m_trail[m_qhead]; ++m_qhead; switch (value(idx)) { case l_undef: UNREACHABLE(); break; case l_true: propagate(idx, l_false, m_fwatch, m_F); break; case l_false: propagate(idx, l_true, m_twatch, m_T); break; } } prune_branch(); } void propagate(unsigned idx, lbool good_val, vector& watch, ptr_vector& Fs) { TRACE("opt", tout << idx << " " << value(idx) << "\n";); unsigned_vector& w = watch[idx]; unsigned sz = w.size(); lbool bad_val = ~good_val; SASSERT(value(idx) == bad_val); unsigned l = 0; for (unsigned i = 0; i < sz && !canceled(); ++i, ++l) { unsigned clause_id = w[i]; set& F = *Fs[clause_id]; SASSERT(F.size() >= 2); bool k1 = (F[0] != idx); bool k2 = !k1; SASSERT(F[k1] == idx); SASSERT(value(F[k1]) == bad_val); if (value(F[k2]) == good_val) { w[l] = w[i]; continue; } bool found = false; unsigned sz2 = F.size(); for (unsigned j = 2; !found && j < sz2; ++j) { unsigned idx2 = F[j]; if (value(idx2) != bad_val) { found = true; std::swap(F[k1], F[j]); --l; watch[idx2].push_back(clause_id); } } if (!found) { if (value(F[k2]) == bad_val) { set_conflict(F[k2], justification(clause_id, good_val == l_true)); if (i == l) { l = sz; } else { for (; i < sz; ++i, ++l) { w[l] = w[i]; } } break; } else { SASSERT(value(F[k2]) == l_undef); assign(F[k2], good_val, justification(clause_id, good_val == l_true)); w[l] = w[i]; } } } watch[idx].shrink(l); SASSERT(invariant()); TRACE("opt", tout << idx << " " << value(idx) << "\n";); SASSERT(value(idx) == bad_val); } bool infeasible_lookahead() { if (m_enable_simplex && L3() >= m_max_weight) { return true; } return (L1() >= m_max_weight) || (L2() >= m_max_weight); } void prune_branch() { if (inconsistent() || !infeasible_lookahead()) { return; } IF_VERBOSE(4, verbose_stream() << "(hs.prune-branch " << m_weight << ")\n";); m_lemma.reset(); unsigned i = 0; rational w(0); for (; i < m_trail.size() && w < m_max_weight; ++i) { unsigned idx = m_trail[i]; if (m_justification[idx].is_decision()) { SASSERT(value(idx) == l_true); m_lemma.push_back(idx); w += m_weights[idx]; } } // undo the lower bounds. TRACE("opt", tout << "prune branch: " << m_weight << " "; display_lemma(tout); display(tout); ); justification j = add_exists_false(m_lemma.size(), m_lemma.c_ptr()); unsigned idx = m_lemma.empty()?0:m_lemma[0]; set_conflict(idx, j); } // TBD: derive strong inequalities and add them to Simplex. // x_i1 + .. + x_ik >= k-1 for each subset k from set n: x_1 + .. + x_n >= k }; hitting_sets::hitting_sets() { m_imp = alloc(imp); } hitting_sets::~hitting_sets() { dealloc(m_imp); } void hitting_sets::add_weight(rational const& w) { m_imp->add_weight(w); } void hitting_sets::add_exists_true(unsigned sz, unsigned const* elems) { m_imp->add_exists_true(sz, elems); } void hitting_sets::add_exists_false(unsigned sz, unsigned const* elems) { m_imp->add_exists_false(sz, elems); } lbool hitting_sets::compute_lower() { return m_imp->compute_lower(); } lbool hitting_sets::compute_upper() { return m_imp->compute_upper(); } rational hitting_sets::get_lower() { return m_imp->get_lower(); } rational hitting_sets::get_upper() { return m_imp->get_upper(); } void hitting_sets::set_upper(rational const& r) { return m_imp->set_upper(r); } bool hitting_sets::get_value(unsigned idx) { return m_imp->get_value(idx); } void hitting_sets::set_cancel(bool f) { m_imp->set_cancel(f); } void hitting_sets::collect_statistics(::statistics& st) const { m_imp->collect_statistics(st); } void hitting_sets::reset() { m_imp->reset(); } }; z3-4.4.0/src/opt/opt_pareto.cpp0000644000175000017500000000561012540347414016120 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: opt_pareto.cpp Abstract: Pareto front utilities Author: Nikolaj Bjorner (nbjorner) 2014-4-24 Notes: --*/ #include "opt_pareto.h" #include "ast_pp.h" #include "model_smt2_pp.h" namespace opt { // --------------------- // GIA pareto algorithm lbool gia_pareto::operator()() { expr_ref fml(m); lbool is_sat = m_solver->check_sat(0, 0); if (is_sat == l_true) { { solver::scoped_push _s(*m_solver.get()); while (is_sat == l_true) { if (m_cancel) { return l_undef; } m_solver->get_model(m_model); IF_VERBOSE(1, model_ref mdl(m_model); cb.fix_model(mdl); model_smt2_pp(verbose_stream() << "new model:\n", m, *mdl, 0);); // TBD: we can also use local search to tune solution coordinate-wise. mk_dominates(); is_sat = m_solver->check_sat(0, 0); } } if (is_sat == l_undef) { return l_undef; } SASSERT(is_sat == l_false); is_sat = l_true; mk_not_dominated_by(); } return is_sat; } void pareto_base::mk_dominates() { unsigned sz = cb.num_objectives(); expr_ref fml(m); expr_ref_vector gt(m), fmls(m); for (unsigned i = 0; i < sz; ++i) { fmls.push_back(cb.mk_ge(i, m_model)); gt.push_back(cb.mk_gt(i, m_model)); } fmls.push_back(m.mk_or(gt.size(), gt.c_ptr())); fml = m.mk_and(fmls.size(), fmls.c_ptr()); IF_VERBOSE(10, verbose_stream() << "dominates: " << fml << "\n";); TRACE("opt", tout << fml << "\n"; model_smt2_pp(tout, m, *m_model, 0);); m_solver->assert_expr(fml); } void pareto_base::mk_not_dominated_by() { unsigned sz = cb.num_objectives(); expr_ref fml(m); expr_ref_vector le(m); for (unsigned i = 0; i < sz; ++i) { le.push_back(cb.mk_le(i, m_model)); } fml = m.mk_not(m.mk_and(le.size(), le.c_ptr())); IF_VERBOSE(10, verbose_stream() << "not dominated by: " << fml << "\n";); TRACE("opt", tout << fml << "\n";); m_solver->assert_expr(fml); } // --------------------------------- // OIA algorithm (without filtering) lbool oia_pareto::operator()() { solver::scoped_push _s(*m_solver.get()); lbool is_sat = m_solver->check_sat(0, 0); if (m_cancel) { is_sat = l_undef; } if (is_sat == l_true) { m_solver->get_model(m_model); mk_not_dominated_by(); } return is_sat; } } z3-4.4.0/src/opt/maxhs.h0000644000175000017500000000060712540347414014532 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: maxhs.h Abstract: HS-max based MaxSAT. Author: Nikolaj Bjorner (nbjorner) 2014-4-17 Notes: --*/ #ifndef _HS_MAX_H_ #define _HS_MAX_H_ #include "maxsmt.h" namespace opt { maxsmt_solver_base* mk_maxhs(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft); } #endif z3-4.4.0/src/opt/optsmt.cpp0000644000175000017500000003433512540347414015300 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: optsmt.cpp Abstract: Objective optimization method. Author: Anh-Dung Phan (t-anphan) 2013-10-16 Notes: Suppose we obtain solution t1 = k1, ..., tn = kn-epsilon Assert: t1 > k1 \/ t2 > k2 \/ ... \/ tn >= kn If this solution is satisfiable, then for each t_i, maximize the assignment and assert the new frontier. Claim: we don't necessarily have to freeze assignments of t_i when optimizing assignment for t_j because the state will always satisfy the disjunction. If one of the k_i is unbounded, then omit a disjunction for it. --*/ #include #include "optsmt.h" #include "opt_solver.h" #include "arith_decl_plugin.h" #include "theory_arith.h" #include "ast_pp.h" #include "model_pp.h" #include "th_rewriter.h" #include "opt_params.hpp" namespace opt { void optsmt::set_cancel(bool f) { TRACE("opt", tout << "set cancel: " << f << "\n";); m_cancel = f; } void optsmt::set_max(vector& dst, vector const& src, expr_ref_vector& fmls) { for (unsigned i = 0; i < src.size(); ++i) { if (src[i] >= dst[i]) { dst[i] = src[i]; m_models.set(i, m_s->get_model(i)); m_lower_fmls[i] = fmls[i].get(); if (dst[i].is_pos() && !dst[i].is_finite()) { // review: likely done already. m_lower_fmls[i] = m.mk_false(); fmls[i] = m.mk_false(); } } else if (src[i] < dst[i] && !m.is_true(m_lower_fmls[i].get())) { fmls[i] = m_lower_fmls[i].get(); } } } /* Enumerate locally optimal assignments until fixedpoint. */ lbool optsmt::basic_opt() { lbool is_sat = l_true; expr_ref bound(m.mk_true(), m), tmp(m); expr* vars[1]; solver::scoped_push _push(*m_s); while (is_sat == l_true && !m_cancel) { tmp = m.mk_fresh_const("b", m.mk_bool_sort()); vars[0] = tmp; bound = m.mk_implies(tmp, bound); m_s->assert_expr(bound); is_sat = m_s->check_sat(1, vars); if (is_sat == l_true) { bound = update_lower(); } } if (m_cancel || is_sat == l_undef) { return l_undef; } // set the solution tight. for (unsigned i = 0; i < m_lower.size(); ++i) { m_upper[i] = m_lower[i]; } return l_true; } /* Enumerate locally optimal assignments until fixedpoint. */ lbool optsmt::farkas_opt() { smt::theory_opt& opt = m_s->get_optimizer(); if (typeid(smt::theory_inf_arith) != typeid(opt)) { return l_undef; } lbool is_sat = l_true; while (is_sat == l_true && !m_cancel) { is_sat = update_upper(); } if (m_cancel || is_sat == l_undef) { return l_undef; } // set the solution tight. for (unsigned i = 0; i < m_lower.size(); ++i) { m_upper[i] = m_lower[i]; } return l_true; } lbool optsmt::symba_opt() { smt::theory_opt& opt = m_s->get_optimizer(); if (typeid(smt::theory_inf_arith) != typeid(opt)) { return l_undef; } expr_ref_vector ors(m), disj(m); expr_ref fml(m), bound(m.mk_true(), m), tmp(m); expr* vars[1]; { for (unsigned i = 0; i < m_upper.size(); ++i) { ors.push_back(m_s->mk_ge(i, m_upper[i])); } fml = m.mk_or(ors.size(), ors.c_ptr()); tmp = m.mk_fresh_const("b", m.mk_bool_sort()); fml = m.mk_implies(tmp, fml); vars[0] = tmp; lbool is_sat = l_true; solver::scoped_push _push(*m_s); while (!m_cancel) { m_s->assert_expr(fml); TRACE("opt", tout << fml << "\n";); is_sat = m_s->check_sat(1,vars); if (is_sat == l_true) { disj.reset(); m_s->maximize_objectives(disj); m_s->get_model(m_model); for (unsigned i = 0; i < ors.size(); ++i) { expr_ref tmp(m); m_model->eval(ors[i].get(), tmp); if (m.is_true(tmp)) { m_lower[i] = m_upper[i]; ors[i] = m.mk_false(); disj[i] = m.mk_false(); } } set_max(m_lower, m_s->get_objective_values(), disj); fml = m.mk_or(ors.size(), ors.c_ptr()); tmp = m.mk_fresh_const("b", m.mk_bool_sort()); fml = m.mk_implies(tmp, fml); vars[0] = tmp; } else if (is_sat == l_undef) { return l_undef; } else { break; } } } bound = m.mk_or(m_lower_fmls.size(), m_lower_fmls.c_ptr()); m_s->assert_expr(bound); if (m_cancel) { return l_undef; } return basic_opt(); } void optsmt::update_lower(unsigned idx, inf_eps const& v) { TRACE("opt", tout << "v" << idx << " >= " << v << "\n";); m_lower_fmls[idx] = m_s->mk_ge(idx, v); m_lower[idx] = v; } void optsmt::update_upper(unsigned idx, inf_eps const& v) { TRACE("opt", tout << "v" << idx << " <= " << v << "\n";); m_upper[idx] = v; } expr_ref optsmt::update_lower() { expr_ref_vector disj(m); m_s->get_model(m_model); m_s->maximize_objectives(disj); set_max(m_lower, m_s->get_objective_values(), disj); TRACE("opt", for (unsigned i = 0; i < m_lower.size(); ++i) { tout << m_lower[i] << " "; } tout << "\n"; model_pp(tout, *m_model); ); IF_VERBOSE(2, verbose_stream() << "(optsmt.lower "; for (unsigned i = 0; i < m_lower.size(); ++i) { verbose_stream() << m_lower[i] << " "; } verbose_stream() << ")\n";); IF_VERBOSE(3, verbose_stream() << disj << "\n";); IF_VERBOSE(3, model_pp(verbose_stream(), *m_model);); return expr_ref(m.mk_or(disj.size(), disj.c_ptr()), m); } lbool optsmt::update_upper() { smt::theory_opt& opt = m_s->get_optimizer(); SASSERT(typeid(smt::theory_inf_arith) == typeid(opt)); smt::theory_inf_arith& th = dynamic_cast(opt); expr_ref bound(m); expr_ref_vector bounds(m); solver::scoped_push _push(*m_s); // // NB: we have to create all bound expressions before calling check_sat // because the state after check_sat is not at base level. // vector mid; for (unsigned i = 0; i < m_lower.size() && !m_cancel; ++i) { if (m_lower[i] < m_upper[i]) { mid.push_back((m_upper[i]+m_lower[i])/rational(2)); bound = m_s->mk_ge(i, mid[i]); bounds.push_back(bound); } else { bounds.push_back(0); mid.push_back(inf_eps()); } } bool progress = false; for (unsigned i = 0; i < m_lower.size() && !m_cancel; ++i) { if (m_lower[i] <= mid[i] && mid[i] <= m_upper[i] && m_lower[i] < m_upper[i]) { th.enable_record_conflict(bounds[i].get()); lbool is_sat = m_s->check_sat(1, bounds.c_ptr() + i); switch(is_sat) { case l_true: IF_VERBOSE(2, verbose_stream() << "(optsmt lower bound for v" << m_vars[i] << " := " << m_upper[i] << ")\n";); m_lower[i] = mid[i]; th.enable_record_conflict(0); m_s->assert_expr(update_lower()); break; case l_false: IF_VERBOSE(2, verbose_stream() << "(optsmt conflict: " << th.conflict_minimize() << ") \n";); if (!th.conflict_minimize().is_finite()) { // bounds is not in the core. The context is unsat. m_upper[i] = m_lower[i]; return l_false; } else { m_upper[i] = std::min(m_upper[i], th.conflict_minimize()); } break; default: th.enable_record_conflict(0); return l_undef; } th.enable_record_conflict(0); progress = true; } } if (m_cancel) { return l_undef; } if (!progress) { return l_false; } return l_true; } void optsmt::setup(opt_solver& solver) { m_s = &solver; solver.reset_objectives(); m_vars.reset(); // force base level { solver::scoped_push _push(solver); } for (unsigned i = 0; i < m_objs.size(); ++i) { smt::theory_var v = solver.add_objective(m_objs[i].get()); if (v == smt::null_theory_var) { std::ostringstream out; out << "Objective function '" << mk_pp(m_objs[i].get(), m) << "' is not supported"; throw default_exception(out.str()); } m_vars.push_back(v); } } lbool optsmt::lex(unsigned obj_index, bool is_maximize) { TRACE("opt", tout << "optsmt:lex\n";); solver::scoped_push _push(*m_s); SASSERT(obj_index < m_vars.size()); return basic_lex(obj_index, is_maximize); } lbool optsmt::basic_lex(unsigned obj_index, bool is_maximize) { lbool is_sat = l_true; expr_ref block(m), tmp(m); for (unsigned i = 0; i < obj_index; ++i) { commit_assignment(i); } while (is_sat == l_true && !m_cancel) { is_sat = m_s->check_sat(0, 0); if (is_sat != l_true) break; m_s->maximize_objective(obj_index, block); m_s->get_model(m_model); inf_eps obj = m_s->saved_objective_value(obj_index); if (obj > m_lower[obj_index]) { m_lower[obj_index] = obj; IF_VERBOSE(1, if (is_maximize) verbose_stream() << "(optsmt lower bound: " << obj << ")\n"; else verbose_stream() << "(optsmt upper bound: " << (-obj) << ")\n"; ); for (unsigned i = obj_index+1; i < m_vars.size(); ++i) { m_s->maximize_objective(i, tmp); m_lower[i] = m_s->saved_objective_value(i); } } TRACE("opt", tout << "strengthen bound: " << block << "\n";); m_s->assert_expr(block); // TBD: only works for simplex // blocking formula should be extracted based // on current state. } if (m_cancel || is_sat == l_undef) { TRACE("opt", tout << "undef: " << m_cancel << " " << is_sat << "\n";); return l_undef; } // set the solution tight. m_upper[obj_index] = m_lower[obj_index]; for (unsigned i = obj_index+1; i < m_lower.size(); ++i) { m_lower[i] = inf_eps(rational(-1), inf_rational(0)); } return l_true; } /** Takes solver with hard constraints added. Returns an optimal assignment to objective functions. */ lbool optsmt::box() { lbool is_sat = l_true; if (m_vars.empty()) { return is_sat; } // assertions added during search are temporary. solver::scoped_push _push(*m_s); if (m_optsmt_engine == symbol("farkas")) { is_sat = farkas_opt(); } else if (m_optsmt_engine == symbol("symba")) { is_sat = symba_opt(); } else { is_sat = basic_opt(); } return is_sat; } inf_eps optsmt::get_lower(unsigned i) const { if (i >= m_lower.size()) return inf_eps(); return m_lower[i]; } bool optsmt::objective_is_model_valid(unsigned index) const { return m_s->objective_is_model_valid(index); } inf_eps optsmt::get_upper(unsigned i) const { if (i >= m_upper.size()) return inf_eps(); return m_upper[i]; } void optsmt::get_model(model_ref& mdl) { mdl = m_model.get(); } // force lower_bound(i) <= objective_value(i) void optsmt::commit_assignment(unsigned i) { inf_eps lo = m_lower[i]; TRACE("opt", tout << "set lower bound of " << mk_pp(m_objs[i].get(), m) << " to: " << lo << "\n"; tout << get_lower(i) << ":" << get_upper(i) << "\n";); // Only assert bounds for bounded objectives if (lo.is_finite()) { m_s->assert_expr(m_s->mk_ge(i, lo)); } } unsigned optsmt::add(app* t) { expr_ref t1(t, m), t2(m); th_rewriter rw(m); rw(t1, t2); SASSERT(is_app(t2)); m_objs.push_back(to_app(t2)); m_lower.push_back(inf_eps(rational(-1),inf_rational(0))); m_upper.push_back(inf_eps(rational(1), inf_rational(0))); m_lower_fmls.push_back(m.mk_true()); m_models.push_back(0); return m_objs.size()-1; } void optsmt::updt_params(params_ref& p) { opt_params _p(p); m_optsmt_engine = _p.optsmt_engine(); } void optsmt::reset() { m_lower.reset(); m_upper.reset(); m_objs.reset(); m_vars.reset(); m_model.reset(); m_lower_fmls.reset(); m_s = 0; } } z3-4.4.0/src/opt/inc_sat_solver.h0000644000175000017500000000067112540347414016425 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: inc_sat_solver.h Abstract: incremental solver based on SAT core. Author: Nikolaj Bjorner (nbjorner) 2014-7-30 Notes: --*/ #ifndef _HS_INC_SAT_SOLVER_H_ #define _HS_INC_SAT_SOLVER_H_ #include "solver.h" solver* mk_inc_sat_solver(ast_manager& m, params_ref& p); void set_soft_inc_sat(solver* s, unsigned sz, expr*const* soft, rational const* weights); #endif z3-4.4.0/src/opt/pb_sls.cpp0000644000175000017500000006435712540347414015243 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: pb_sls.cpp Abstract: SLS for PB optimization. Author: Nikolaj Bjorner (nbjorner) 2014-03-18 Notes: --*/ #include "pb_sls.h" #include "smt_literal.h" #include "ast_pp.h" #include "th_rewriter.h" #include "sat_sls.h" namespace smt { struct pb_sls::imp { struct clause { literal_vector m_lits; scoped_mpz_vector m_weights; scoped_mpz m_k; scoped_mpz m_value; bool m_eq; clause(unsynch_mpz_manager& m): m_weights(m), m_k(m), m_value(m), m_eq(true) {} clause(clause const& cls): m_lits(cls.m_lits), m_weights(cls.m_weights.m()), m_k(cls.m_k), m_value(cls.m_value), m_eq(cls.m_eq) { for (unsigned i = 0; i < cls.m_weights.size(); ++i) { m_weights.push_back(cls.m_weights[i]); } } }; struct stats { stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } unsigned m_num_flips; unsigned m_num_improvements; }; ast_manager& m; pb_util pb; unsynch_mpz_manager mgr; th_rewriter m_rewrite; volatile bool m_cancel; vector m_clauses; // clauses to be satisfied expr_ref_vector m_orig_clauses; // for debugging model_ref m_orig_model; // for debugging vector m_soft; // soft constraints vector m_weights; // weights of soft constraints rational m_penalty; // current penalty of soft constraints rational m_best_penalty; vector m_hard_occ, m_soft_occ; // variable occurrence svector m_assignment; // current assignment. svector m_best_assignment; expr_ref_vector m_trail; obj_map m_decl2var; // map declarations to Boolean variables. ptr_vector m_var2decl; // reverse map sat::index_set m_hard_false; // list of hard clauses that are false. sat::index_set m_soft_false; // list of soft clauses that are false. unsigned m_max_flips; // maximal number of flips unsigned m_non_greedy_percent; // percent of moves to do non-greedy style random_gen m_rng; scoped_mpz one; stats m_stats; imp(ast_manager& m): m(m), pb(m), m_rewrite(m), m_cancel(false), m_orig_clauses(m), m_trail(m), one(mgr) { reset(); one = mpz(1); } ~imp() { } void reset() { init_max_flips(); m_non_greedy_percent = 30; m_decl2var.reset(); m_var2decl.reset(); m_assignment.reset(); m_hard_occ.reset(); m_soft_occ.reset(); m_clauses.reset(); m_orig_clauses.reset(); m_soft.reset(); m_weights.reset(); m_trail.reset(); m_decl2var.insert(m.mk_true(), 0); m_var2decl.push_back(m.mk_true()); m_assignment.push_back(true); m_hard_occ.push_back(unsigned_vector()); m_soft_occ.push_back(unsigned_vector()); } void init_max_flips() { m_max_flips = 200; } void add(expr* f) { clause cls(mgr); if (compile_clause(f, cls)) { m_clauses.push_back(cls); m_orig_clauses.push_back(f); } } void add(expr* f, rational const& w) { clause cls(mgr); if (compile_clause(f, cls)) { m_soft.push_back(cls); m_weights.push_back(w); } } void set_model(model_ref & mdl) { m_orig_model = mdl; for (unsigned i = 0; i < m_var2decl.size(); ++i) { expr_ref tmp(m); VERIFY(mdl->eval(m_var2decl[i], tmp)); m_assignment[i] = m.is_true(tmp); } } lbool operator()() { init(); IF_VERBOSE(1, verbose_stream() << "(pb.sls initial penalty: " << m_best_penalty << ")\n"; verbose_stream() << "(pb.sls violated: " << m_hard_false.num_elems() << " penalty: " << m_penalty << ")\n";); svector assignment(m_assignment); for (unsigned round = 0; round < 40; ++round) { init_max_flips(); while (m_max_flips > 0) { --m_max_flips; literal lit = flip(); if (m_cancel) { return l_undef; } IF_VERBOSE(3, verbose_stream() << "(pb.sls violated: " << m_hard_false.num_elems() << " penalty: " << m_penalty << " " << lit << ")\n";); if (m_hard_false.empty() && m_best_penalty.is_zero()) { break; } } if (m_hard_false.empty() && m_best_penalty.is_zero()) { break; } IF_VERBOSE(1, verbose_stream() << "(pb.sls best penalty " << m_best_penalty << ")\n";); if (!m_best_assignment.empty()) { assignment.reset(); assignment.append(m_best_assignment); round = 0; } m_assignment.reset(); m_assignment.append(assignment); m_best_assignment.reset(); m_soft_false.reset(); m_hard_false.reset(); m_penalty.reset(); for (unsigned i = 0; i < m_soft.size(); ++i) { if (!eval(m_soft[i])) { m_soft_false.insert(i); m_penalty += m_weights[i]; } } for (unsigned i = 0; i < m_clauses.size(); ++i) { if (!eval(m_clauses[i])) { m_hard_false.insert(i); } } } m_assignment.reset(); m_assignment.append(assignment); m_penalty = m_best_penalty; return l_true; } bool get_value(literal l) { return l.sign() ^ m_assignment[l.var()]; } void set_cancel(bool f) { m_cancel = f; } void get_model(model_ref& mdl) { mdl = alloc(model, m); for (unsigned i = 1; i < m_var2decl.size(); ++i) { expr* d = m_var2decl[i]; if (is_uninterp_const(d)) { mdl->register_decl(to_app(d)->get_decl(), m_assignment[i] ? m.mk_true() : m.mk_false()); } } } void collect_statistics(::statistics& st) const { st.update("sls.num_flips", m_stats.m_num_flips); st.update("sls.num_improvements", m_stats.m_num_improvements); } void updt_params(params_ref& p) { } bool soft_holds(unsigned index) { return eval(m_soft[index]); } void display(std::ostream& out, clause const& cls) { scoped_mpz w(mgr); for (unsigned i = 0; i < cls.m_lits.size(); ++i) { w = cls.m_weights[i]; out << w << "*" << cls.m_lits[i] << " "; out << "(" << mk_pp(m_var2decl[cls.m_lits[i].var()], m) << ") "; if (i + 1 < cls.m_lits.size()) { out << "+ "; } } out << "(" << cls.m_value << ") "; if (cls.m_eq) { out << "= "; } else { out << ">= "; } out << cls.m_k << "\n"; } void display(std::ostream& out) { for (unsigned i = 0; i < m_clauses.size(); ++i) { display(out, m_clauses[i]); } out << "soft:\n"; for (unsigned i = 0; i < m_soft.size(); ++i) { display(out << m_weights[i] << ": ", m_soft[i]); } for (unsigned i = 0; i < m_assignment.size(); ++i) { out << literal(i) << ": " << mk_pp(m_var2decl[i], m) << " |-> " << (m_assignment[i]?"true":"false") << "\n"; } } bool eval(clause& cls) { unsigned sz = cls.m_lits.size(); cls.m_value.reset(); for (unsigned i = 0; i < sz; ++i) { if (get_value(cls.m_lits[i])) { cls.m_value += cls.m_weights[i]; } } if (cls.m_eq) { return cls.m_value == cls.m_k; } else { return cls.m_value >= cls.m_k; } } void init_occ(vector const& clauses, vector & occ) { for (unsigned i = 0; i < clauses.size(); ++i) { clause const& cls = clauses[i]; for (unsigned j = 0; j < cls.m_lits.size(); ++j) { literal lit = cls.m_lits[j]; if (occ.size() <= static_cast(lit.var())) occ.resize(lit.var() + 1); occ[lit.var()].push_back(i); } } } void init() { m_best_assignment.reset(); m_best_penalty.reset(); m_hard_false.reset(); m_hard_occ.reset(); m_soft_false.reset(); m_soft_occ.reset(); m_penalty.reset(); for (unsigned i = 0; i <= m_var2decl.size(); ++i) { m_soft_occ.push_back(unsigned_vector()); m_hard_occ.push_back(unsigned_vector()); } // initialize the occurs vectors. init_occ(m_clauses, m_hard_occ); init_occ(m_soft, m_soft_occ); // add clauses that are false. for (unsigned i = 0; i < m_clauses.size(); ++i) { if (!eval(m_clauses[i])) { m_hard_false.insert(i); expr_ref tmp(m); VERIFY(m_orig_model->eval(m_orig_clauses[i].get(), tmp)); IF_VERBOSE(0, verbose_stream() << "original evaluation: " << tmp << "\n"; verbose_stream() << mk_pp(m_orig_clauses[i].get(), m) << "\n"; display(verbose_stream(), m_clauses[i]);); } } for (unsigned i = 0; i < m_soft.size(); ++i) { if (!eval(m_soft[i])) { m_soft_false.insert(i); m_penalty += m_weights[i]; } } m_best_penalty = m_penalty; TRACE("opt", display(tout);); } literal flip() { m_stats.m_num_flips++; literal result; if (m_hard_false.empty()) { result = flip_soft(); } else { result = flip_hard(); } if (m_hard_false.empty() && m_best_penalty > m_penalty) { IF_VERBOSE(1, verbose_stream() << "(pb.sls improved bound " << m_penalty << ")\n";); m_best_assignment.reset(); m_best_assignment.append(m_assignment); m_best_penalty = m_penalty; m_stats.m_num_improvements++; init_max_flips(); } if (!m_assignment[result.var()]) { result.neg(); } return result; } literal flip_hard() { SASSERT(!m_hard_false.empty()); literal lit; clause const& cls = pick_hard_clause(); // IF_VERBOSE(1, display(verbose_stream(), cls);); int break_count; int min_bc = INT_MAX; unsigned min_bc_index = 0; for (unsigned i = 0; i < cls.m_lits.size(); ++i) { lit = cls.m_lits[i]; break_count = flip(lit); if (break_count < min_bc) { min_bc = break_count; min_bc_index = i; } else if (break_count == min_bc && m_rng(5) == 1) { min_bc_index = i; } int new_break_count = flip(~lit); if (-break_count != new_break_count) { verbose_stream() << lit << "\n"; IF_VERBOSE(0, display(verbose_stream(), cls);); display(verbose_stream()); exit(0); } // VERIFY(-break_count == flip(~lit)); } if (m_rng(100) <= m_non_greedy_percent) { lit = cls.m_lits[m_rng(cls.m_lits.size())]; } else { lit = cls.m_lits[min_bc_index]; } flip(lit); return lit; } literal flip_soft() { literal lit; clause const& cls = pick_soft_clause(); int break_count; int min_bc = INT_MAX; unsigned min_bc_index = 0; rational penalty = m_penalty; rational min_penalty = penalty; for (unsigned i = 0; i < cls.m_lits.size(); ++i) { lit = cls.m_lits[i]; break_count = flip(lit); SASSERT(break_count >= 0); if (break_count == 0 && penalty > m_penalty) { return lit; } if ((break_count < min_bc) || (break_count == min_bc && m_penalty < min_penalty)) { min_bc = break_count; min_bc_index = i; min_penalty = m_penalty; } VERIFY(-break_count == flip(~lit)); } if (m_rng(100) <= m_non_greedy_percent) { lit = cls.m_lits[m_rng(cls.m_lits.size())]; } else { // just do a greedy move: lit = cls.m_lits[min_bc_index]; } flip(lit); return lit; } // // TODO: alternate version: loop over soft clauses and see if there is a flip that // reduces the penalty while preserving the hard constraints. // // crude selection strategy. clause const& pick_hard_clause() { SASSERT(!m_hard_false.empty()); return m_clauses[m_hard_false.choose(m_rng)]; } clause const& pick_soft_clause() { SASSERT(!m_soft_false.empty()); return m_soft[m_soft_false.choose(m_rng)]; } int flip(literal l) { m_assignment[l.var()] = !m_assignment[l.var()]; int break_count = 0; unsigned_vector const& occh = m_hard_occ[l.var()]; scoped_mpz value(mgr); for (unsigned i = 0; i < occh.size(); ++i) { unsigned j = occh[i]; clause& cls = m_clauses[j]; value = cls.m_value; if (eval(cls)) { if (m_hard_false.contains(j)) { break_count--; m_hard_false.remove(j); } } else { if (!m_hard_false.contains(j)) { break_count++; m_hard_false.insert(j); } else if (value < m_clauses[j].m_value) { } } } unsigned_vector const& occs = m_soft_occ[l.var()]; for (unsigned i = 0; i < occs.size(); ++i) { unsigned j = occs[i]; if (eval(m_soft[j])) { if (m_soft_false.contains(j)) { m_penalty -= m_weights[j]; m_soft_false.remove(j); } } else { if (!m_soft_false.contains(j)) { m_penalty += m_weights[j]; m_soft_false.insert(j); } } } TRACE("opt", tout << "flip: " << l << " num false: " << m_hard_false.num_elems() << " penalty: " << m_penalty << " break count: " << break_count << "\n";); return break_count; } literal mk_aux_literal(expr* f) { unsigned var; expr_ref tmp(m); if (!m_decl2var.find(f, var)) { var = m_hard_occ.size(); SASSERT(m_var2decl.size() == var); SASSERT(m_soft_occ.size() == var); m_hard_occ.push_back(unsigned_vector()); m_soft_occ.push_back(unsigned_vector()); VERIFY(m_orig_model->eval(f, tmp)); m_assignment.push_back(m.is_true(tmp)); m_decl2var.insert(f, var); m_var2decl.push_back(f); } return literal(var); } void pad(scoped_mpz_vector& vec, unsigned sz, mpz& val) { for (unsigned i = 0; i < sz; ++i) { vec.push_back(val); } } literal mk_literal(expr* f) { if (m.is_not(f, f)) { literal result = mk_literal(f); if (result != null_literal) { result.neg(); } return result; } if (is_uninterp_const(f)) return mk_aux_literal(to_app(f)); if (m.is_true(f)) return true_literal; if (m.is_false(f)) return false_literal; if (m.is_and(f)) { literal_vector lits; app* g = to_app(f); for (unsigned i = 0; i < g->get_num_args(); ++i) { lits.push_back(mk_literal(g->get_arg(i))); } literal result = mk_aux_literal(f); for (unsigned i = 0; i < lits.size(); ++i) { clause cls(mgr); cls.m_lits.push_back(~result); cls.m_weights.push_back(one); cls.m_lits.push_back(lits[i]); cls.m_weights.push_back(one); cls.m_k = one; cls.m_eq = false; m_clauses.push_back(cls); m_orig_clauses.push_back(f); lits[i].neg(); } lits.push_back(result); clause cls(mgr); cls.m_lits.append(lits); pad(cls.m_weights, lits.size(), one); cls.m_k = one; cls.m_eq = false; m_clauses.push_back(cls); m_orig_clauses.push_back(f); return result; } if (m.is_or(f)) { literal_vector lits; app* g = to_app(f); for (unsigned i = 0; i < g->get_num_args(); ++i) { lits.push_back(mk_literal(g->get_arg(i))); } literal result = mk_aux_literal(f); for (unsigned i = 0; i < lits.size(); ++i) { clause cls(mgr); cls.m_lits.push_back(result); cls.m_weights.push_back(one); cls.m_lits.push_back(~lits[i]); cls.m_weights.push_back(one); cls.m_k = one; cls.m_eq = false; m_clauses.push_back(cls); m_orig_clauses.push_back(f); } lits.push_back(~result); clause cls(mgr); cls.m_lits.append(lits); pad(cls.m_weights, lits.size(), one); cls.m_k = one; cls.m_eq = false; m_clauses.push_back(cls); m_orig_clauses.push_back(f); return result; } expr* x, *y; if ((m.is_eq(f, x, y) && m.is_bool(x)) || m.is_iff(f, x, y)) { literal a = mk_literal(x); literal b = mk_literal(y); literal result = mk_aux_literal(f); clause cls(mgr); cls.m_lits.push_back(~result); cls.m_lits.push_back(~a); cls.m_lits.push_back(b); pad(cls.m_weights, 3, one); cls.m_k = one; cls.m_eq = false; m_clauses.push_back(cls); m_orig_clauses.push_back(f); // actually, the clause that defines f cls.m_lits[0] = ~result; cls.m_lits[1] = a; cls.m_lits[2] = ~b; m_clauses.push_back(cls); m_orig_clauses.push_back(f); cls.m_lits[0] = result; cls.m_lits[1] = a; cls.m_lits[2] = b; m_clauses.push_back(cls); m_orig_clauses.push_back(f); cls.m_lits[0] = result; cls.m_lits[1] = ~a; cls.m_lits[2] = ~b; m_clauses.push_back(cls); m_orig_clauses.push_back(f); return result; } if (pb.is_ge(f)) { } IF_VERBOSE(0, verbose_stream() << "not handled: " << mk_pp(f, m) << "\n";); return mk_aux_literal(f); } bool compile_clause(expr* _f, clause& cls) { expr_ref tmp(m); m_rewrite(_f, tmp); if (!is_app(tmp)) return false; app* f = to_app(tmp); expr* f2; unsigned sz = f->get_num_args(); expr* const* args = f->get_args(); literal lit; rational coeff, k; if (m.is_not(f, f2) && pb.is_ge(f2)) { // ~(ax+by >= k) // <=> // ax + by < k // <=> // -ax - by >= -k + 1 // <=> // a(1-x) + b(1-y) >= -k + a + b + 1 sz = to_app(f2)->get_num_args(); args = to_app(f2)->get_args(); k = pb.get_k(f2); SASSERT(k.is_int()); k.neg(); k += rational::one(); expr_ref_vector args(m); vector coeffs; for (unsigned i = 0; i < sz; ++i) { args.push_back(m.mk_not(to_app(f2)->get_arg(i))); coeffs.push_back(pb.get_coeff(f2, i)); k += pb.get_coeff(f2, i); } tmp = pb.mk_ge(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), k); return compile_clause(tmp, cls); } else if (pb.is_ge(f) || pb.is_eq(f)) { k = pb.get_k(f); SASSERT(k.is_int()); cls.m_k = k.to_mpq().numerator(); for (unsigned i = 0; i < sz; ++i) { coeff = pb.get_coeff(f, i); SASSERT(coeff.is_int()); lit = mk_literal(args[i]); if (lit == null_literal) return false; if (lit == false_literal) continue; if (lit == true_literal) { cls.m_k -= coeff.to_mpq().numerator(); continue; } cls.m_lits.push_back(lit); cls.m_weights.push_back(coeff.to_mpq().numerator()); if (get_value(lit)) { cls.m_value += coeff.to_mpq().numerator(); } } cls.m_eq = pb.is_eq(f); } else if (m.is_or(f)) { for (unsigned i = 0; i < sz; ++i) { lit = mk_literal(args[i]); if (lit == null_literal) return false; if (lit == false_literal) continue; if (lit == true_literal) return false; cls.m_lits.push_back(lit); cls.m_weights.push_back(mpz(1)); if (get_value(lit)) { cls.m_value += mpz(1); } } cls.m_eq = false; cls.m_k = mpz(1); } else if (m.is_true(f)) { return false; } else { lit = mk_literal(f); if (lit == null_literal) return false; SASSERT(lit != false_literal && lit != true_literal); cls.m_lits.push_back(lit); cls.m_weights.push_back(mpz(1)); cls.m_eq = true; cls.m_k = mpz(1); } return true; } }; pb_sls::pb_sls(ast_manager& m) { m_imp = alloc(imp, m); } pb_sls::~pb_sls() { dealloc(m_imp); } void pb_sls::add(expr* f) { m_imp->add(f); } void pb_sls::add(expr* f, rational const& w) { m_imp->add(f, w); } void pb_sls::set_model(model_ref& mdl) { m_imp->set_model(mdl); } lbool pb_sls::operator()() { return (*m_imp)(); } void pb_sls::set_cancel(bool f) { m_imp->set_cancel(f); } void pb_sls::collect_statistics(statistics& st) const { m_imp->collect_statistics(st); } void pb_sls::get_model(model_ref& mdl) { m_imp->get_model(mdl); } void pb_sls::reset() { m_imp->reset(); } bool pb_sls::soft_holds(unsigned index) { return m_imp->soft_holds(index); } void pb_sls::updt_params(params_ref& p) { m_imp->updt_params(p); } } z3-4.4.0/src/opt/opt_sls_solver.h0000644000175000017500000001720712540347414016473 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: opt_sls_solver.h Abstract: Wraps a solver with SLS for improving a solution using an objective function. Author: Nikolaj Bjorner (nbjorner) 2014-4-18 Notes: --*/ #ifndef _OPT_SLS_SOLVER_H_ #define _OPT_SLS_SOLVER_H_ #include "solver_na2as.h" #include "card2bv_tactic.h" #include "nnf_tactic.h" #include "pb_sls.h" #include "bvsls_opt_engine.h" namespace opt { class sls_solver : public solver_na2as { ast_manager& m; ref m_solver; scoped_ptr m_bvsls; scoped_ptr m_pbsls; pb::card_pb_rewriter m_pb2bv; vector m_weights; expr_ref_vector m_soft; model_ref m_model; params_ref m_params; symbol m_engine; public: sls_solver(ast_manager & m, solver* s, expr_ref_vector const& soft, vector const& weights, params_ref & p): solver_na2as(m), m(m), m_solver(s), m_bvsls(0), m_pbsls(0), m_pb2bv(m), m_weights(weights), m_soft(soft) { updt_params(p); } virtual ~sls_solver() {} virtual void updt_params(params_ref & p) { m_solver->updt_params(p); m_params.copy(p); opt_params _p(p); m_engine = _p.sls_engine(); } virtual void collect_param_descrs(param_descrs & r) { m_solver->collect_param_descrs(r); } virtual void collect_statistics(statistics & st) const { m_solver->collect_statistics(st); if (m_bvsls) m_bvsls->collect_statistics(st); if (m_pbsls) m_pbsls->collect_statistics(st); } virtual void assert_expr(expr * t) { m_solver->assert_expr(t); } virtual void get_unsat_core(ptr_vector & r) { m_solver->get_unsat_core(r); } virtual void get_model(model_ref & m) { m = m_model; } virtual proof * get_proof() { return m_solver->get_proof(); } virtual std::string reason_unknown() const { return m_solver->reason_unknown(); } virtual void get_labels(svector & r) { m_solver->get_labels(r); } virtual void set_cancel(bool f) { m_solver->set_cancel(f); m_pb2bv.set_cancel(f); #pragma omp critical (sls_solver) { if (m_bvsls) { m_bvsls->set_cancel(f); } if (m_pbsls) { m_pbsls->set_cancel(f); } } } virtual void set_progress_callback(progress_callback * callback) { m_solver->set_progress_callback(callback); } virtual unsigned get_num_assertions() const { return m_solver->get_num_assertions(); } virtual expr * get_assertion(unsigned idx) const { return m_solver->get_assertion(idx); } virtual void display(std::ostream & out) const { m_solver->display(out); // if (m_bvsls) m_bvsls->display(out); } void opt(model_ref& mdl) { if (m_engine == symbol("pb")) { pbsls_opt(mdl); } else { bvsls_opt(mdl); } } static expr_ref soft2bv(expr_ref_vector const& soft, vector const& weights) { ast_manager& m = soft.get_manager(); pb::card_pb_rewriter pb2bv(m); rational upper(1); expr_ref objective(m); for (unsigned i = 0; i < weights.size(); ++i) { upper += weights[i]; } expr_ref zero(m), tmp(m); bv_util bv(m); expr_ref_vector es(m); rational num = numerator(upper); rational den = denominator(upper); rational maxval = num*den; unsigned bv_size = maxval.get_num_bits(); zero = bv.mk_numeral(rational(0), bv_size); for (unsigned i = 0; i < soft.size(); ++i) { pb2bv(soft[i], tmp); es.push_back(m.mk_ite(tmp, bv.mk_numeral(den*weights[i], bv_size), zero)); } if (es.empty()) { objective = bv.mk_numeral(0, bv_size); } else { objective = es[0].get(); for (unsigned i = 1; i < es.size(); ++i) { objective = bv.mk_bv_add(objective, es[i].get()); } } return objective; } protected: typedef bvsls_opt_engine::optimization_result opt_result; virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) { lbool r = m_solver->check_sat(num_assumptions, assumptions); if (r == l_true) { m_solver->get_model(m_model); opt(m_model); } return r; } virtual void push_core() { m_solver->push(); } virtual void pop_core(unsigned n) { m_solver->pop(n); } private: // convert soft constraints to bit-vector objective. void assertions2sls() { expr_ref tmp(m); goal_ref g(alloc(goal, m, true, false)); for (unsigned i = 0; i < m_solver->get_num_assertions(); ++i) { m_pb2bv(m_solver->get_assertion(i), tmp); g->assert_expr(tmp); } tactic_ref simplify = mk_nnf_tactic(m); proof_converter_ref pc; expr_dependency_ref core(m); goal_ref_buffer result; model_converter_ref model_converter; (*simplify)(g, result, model_converter, pc, core); SASSERT(result.size() == 1); goal* r = result[0]; for (unsigned i = 0; i < r->size(); ++i) { m_bvsls->assert_expr(r->form(i)); } } void pbsls_opt(model_ref& mdl) { #pragma omp critical (sls_solver) { if (m_pbsls) { m_pbsls->reset(); } else { m_pbsls = alloc(smt::pb_sls, m); } } m_pbsls->set_model(mdl); m_pbsls->updt_params(m_params); for (unsigned i = 0; i < m_solver->get_num_assertions(); ++i) { m_pbsls->add(m_solver->get_assertion(i)); } for (unsigned i = 0; i < m_soft.size(); ++i) { m_pbsls->add(m_soft[i].get(), m_weights[i]); } (*m_pbsls.get())(); m_pbsls->get_model(m_model); mdl = m_model.get(); } void bvsls_opt(model_ref& mdl) { #pragma omp critical (sls_solver) { m_bvsls = alloc(bvsls_opt_engine, m, m_params); } assertions2sls(); expr_ref objective = soft2bv(m_soft, m_weights); opt_result res(m); res.is_sat = l_undef; try { res = m_bvsls->optimize(objective, mdl, true); } catch (...) { } SASSERT(res.is_sat == l_true || res.is_sat == l_undef); if (res.is_sat == l_true) { m_bvsls->get_model(m_model); mdl = m_model.get(); } } }; } #endif z3-4.4.0/src/opt/maxsmt.h0000644000175000017500000001155512540347414014727 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: maxsmt.h Abstract: MaxSMT optimization context. Author: Nikolaj Bjorner (nbjorner) 2013-11-7 Notes: --*/ #ifndef _OPT_MAXSMT_H_ #define _OPT_MAXSMT_H_ #include"ast.h" #include"params.h" #include"solver.h" #include"filter_model_converter.h" #include"statistics.h" #include"smt_context.h" #include"smt_theory.h" #include"theory_wmaxsat.h" #include"opt_solver.h" namespace opt { typedef vector const weights_t; class maxsat_context; class maxsmt_solver { protected: adjust_value m_adjust_value; public: virtual ~maxsmt_solver() {} virtual lbool operator()() = 0; virtual rational get_lower() const = 0; virtual rational get_upper() const = 0; virtual bool get_assignment(unsigned index) const = 0; virtual void set_cancel(bool f) = 0; virtual void collect_statistics(statistics& st) const = 0; virtual void get_model(model_ref& mdl) = 0; virtual void updt_params(params_ref& p) = 0; void set_adjust_value(adjust_value& adj) { m_adjust_value = adj; } }; // --------------------------------------------- // base class with common utilities used // by maxsmt solvers // class maxsmt_solver_base : public maxsmt_solver { protected: ast_manager& m; maxsat_context& m_c; volatile bool m_cancel; const expr_ref_vector m_soft; vector m_weights; expr_ref_vector m_assertions; rational m_lower; rational m_upper; model_ref m_model; svector m_assignment; // truth assignment to soft constraints params_ref m_params; // config public: maxsmt_solver_base(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft); virtual ~maxsmt_solver_base() {} virtual rational get_lower() const { return m_lower; } virtual rational get_upper() const { return m_upper; } virtual bool get_assignment(unsigned index) const { return m_assignment[index]; } virtual void set_cancel(bool f) { m_cancel = f; if (f) s().cancel(); else s().reset_cancel(); } virtual void collect_statistics(statistics& st) const { } virtual void get_model(model_ref& mdl) { mdl = m_model.get(); } virtual void commit_assignment(); void set_model() { s().get_model(m_model); } virtual void updt_params(params_ref& p); solver& s(); void init(); void set_mus(bool f); app* mk_fresh_bool(char const* name); class smt::theory_wmaxsat* get_wmax_theory() const; smt::theory_wmaxsat* ensure_wmax_theory(); class scoped_ensure_theory { smt::theory_wmaxsat* m_wth; public: scoped_ensure_theory(maxsmt_solver_base& s); ~scoped_ensure_theory(); smt::theory_wmaxsat& operator()(); }; protected: void enable_sls(expr_ref_vector const& soft, weights_t& ws); void set_enable_sls(bool f); void set_soft_assumptions(); void trace_bounds(char const* solver); }; /** Takes solver with hard constraints added. Returns modified soft constraints that are maximal assignments. */ class maxsmt { ast_manager& m; maxsat_context& m_c; scoped_ptr m_msolver; volatile bool m_cancel; expr_ref_vector m_soft_constraints; expr_ref_vector m_answer; vector m_weights; rational m_lower; rational m_upper; adjust_value m_adjust_value; model_ref m_model; params_ref m_params; public: maxsmt(maxsat_context& c); lbool operator()(); void set_cancel(bool f); void updt_params(params_ref& p); void add(expr* f, rational const& w); void set_adjust_value(adjust_value& adj) { m_adjust_value = adj; } unsigned size() const { return m_soft_constraints.size(); } expr* operator[](unsigned idx) const { return m_soft_constraints[idx]; } rational weight(unsigned idx) const { return m_weights[idx]; } void commit_assignment(); rational get_value() const; rational get_lower() const; rational get_upper() const; void update_lower(rational const& r); void update_upper(rational const& r); void get_model(model_ref& mdl); bool get_assignment(unsigned index) const; void display_answer(std::ostream& out) const; void collect_statistics(statistics& st) const; private: bool is_maxsat_problem(weights_t& ws) const; void verify_assignment(); solver& s(); }; }; #endif z3-4.4.0/src/opt/opt_cmds.h0000644000175000017500000000050412540347414015216 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: opt_cmds.h Abstract: Commands for optimization benchmarks Author: Anh-Dung Phan (t-anphan) 2013-10-14 Notes: --*/ #ifndef _OPT_CMDS_H_ #define _OPT_CMDS_H_ #include "ast.h" class cmd_context; void install_opt_cmds(cmd_context & ctx); #endif z3-4.4.0/src/opt/pb_sls.h0000644000175000017500000000156412540347414014677 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: pb_sls.h Abstract: SLS for PB optimization. Author: Nikolaj Bjorner (nbjorner) 2014-03-18 Notes: --*/ #ifndef _PB_SLS_H_ #define _PB_SLS_H_ #include "pb_decl_plugin.h" #include "model.h" #include "lbool.h" #include "params.h" #include "statistics.h" namespace smt { class pb_sls { struct imp; imp* m_imp; public: pb_sls(ast_manager& m); ~pb_sls(); void add(expr* f); void add(expr* f, rational const& w); bool soft_holds(unsigned index); void set_model(model_ref& mdl); lbool operator()(); void set_cancel(bool f); void collect_statistics(::statistics& st) const; void get_model(model_ref& mdl); void updt_params(params_ref& p); void reset(); }; }; #endif z3-4.4.0/src/opt/mss.cpp0000644000175000017500000002105712540347414014551 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: mss.cpp Abstract: MSS/MCS extraction. Author: Nikolaj Bjorner (nbjorner) 2014-2-8 Notes: --*/ #include "solver.h" #include "mss.h" #include "ast_pp.h" #include "model_smt2_pp.h" namespace opt { mss::mss(solver& s, ast_manager& m): m_s(s), m(m), m_cancel(false) { } mss::~mss() { } bool mss::check_result() { lbool is_sat = m_s.check_sat(m_mss.size(), m_mss.c_ptr()); if (is_sat == l_undef) return true; SASSERT(m_mss.empty() || is_sat == l_true); if (is_sat == l_false) return false; expr_set::iterator it = m_mcs.begin(), end = m_mcs.end(); for (; it != end; ++it) { m_mss.push_back(*it); is_sat = m_s.check_sat(m_mss.size(), m_mss.c_ptr()); m_mss.pop_back(); if (is_sat == l_undef) return true; SASSERT(is_sat == l_false); if (is_sat == l_true) return false; } return true; } void mss::initialize(exprs& literals) { expr* n; expr_set lits, core_lits; for (unsigned i = 0; i < literals.size(); ++i) { n = literals[i]; lits.insert(n); m.is_not(n, n); if (!is_uninterp_const(n)) { throw default_exception("arguments have to be uninterpreted literals"); } } exprs rest_core; expr_ref tmp(m); // // the last core is a dummy core. It contains literals that // did not occur in previous cores and did not evaluate to true // in the current model. // for (unsigned i = 0; i < m_cores.size(); ++i) { exprs const& core = m_cores[i]; for (unsigned j = 0; j < core.size(); ++j) { expr* n = core[j]; if (!core_lits.contains(n)) { core_lits.insert(n); VERIFY(m_model->eval(n, tmp)); if (m.is_true(tmp)) { add_mss(n); } else { m_todo.push_back(n); } } } } for (unsigned i = 0; i < literals.size(); ++i) { expr* n = literals[i]; if (!core_lits.contains(n)) { VERIFY(m_model->eval(n, tmp)); if (m.is_true(tmp)) { m_mss.push_back(n); } else { rest_core.push_back(n); core_lits.insert(n); m_todo.push_back(n); } } } m_cores.push_back(rest_core); } void mss::add_mss(expr* n) { if (!m_mss_set.contains(n)) { m_mss_set.insert(n); m_mss.push_back(n); } } void mss::update_core(exprs& core) { unsigned j = 0; for (unsigned i = 0; i < core.size(); ++i) { expr* n = core[i]; if (!m_mss_set.contains(n)) { if (i != j) { core[j] = core[i]; } ++j; } } core.resize(j); } void mss::update_mss() { expr_ref tmp(m); unsigned j = 0; for (unsigned i = 0; i < m_todo.size(); ++i) { expr* n = m_todo[i]; SASSERT(!m_mss_set.contains(n)); if (m_mcs.contains(n)) { continue; // remove from cores. } VERIFY(m_model->eval(n, tmp)); if (m.is_true(tmp)) { add_mss(n); } else { if (j != i) { m_todo[j] = m_todo[i]; } ++j; } } m_todo.resize(j); } lbool mss::operator()(model* initial_model, vector const& _cores, exprs& literals, exprs& mcs) { m_mss.reset(); m_todo.reset(); m_model = initial_model; m_cores.reset(); SASSERT(m_model); m_cores.append(_cores); initialize(literals); TRACE("opt", display_vec(tout << "lits: ", literals.size(), literals.c_ptr()); display(tout);); lbool is_sat = l_true; for (unsigned i = 0; is_sat == l_true && i < m_cores.size(); ++i) { bool has_mcs = false; bool is_last = i + 1 < m_cores.size(); SASSERT(check_invariant()); update_core(m_cores[i]); // remove members of mss is_sat = process_core(1, m_cores[i], has_mcs, is_last); } if (is_sat == l_true) { SASSERT(check_invariant()); TRACE("opt", display(tout);); literals.reset(); literals.append(m_mss); mcs.reset(); expr_set::iterator it = m_mcs.begin(), end = m_mcs.end(); for (; it != end; ++it) { mcs.push_back(*it); } SASSERT(check_result()); } m_mcs.reset(); m_mss_set.reset(); IF_VERBOSE(2, display_vec(verbose_stream() << "mcs: ", mcs.size(), mcs.c_ptr());); return is_sat; } // // at least one literal in core is false in current model. // pick literals in core that are not yet in mss. // lbool mss::process_core(unsigned sz, exprs& core, bool& has_mcs, bool is_last) { SASSERT(sz > 0); if (core.empty()) { return l_true; } if (m_cancel) { return l_undef; } if (sz == 1 && core.size() == 1 && is_last && !has_mcs) { // there has to be at least one false // literal in the core. TRACE("opt", tout << "mcs: " << mk_pp(core[0], m) << "\n";); m_mcs.insert(core[0]); return l_true; } sz = std::min(sz, core.size()); TRACE("opt", display_vec(tout << "process (total " << core.size() << ") :", sz, core.c_ptr());); unsigned sz_save = m_mss.size(); m_mss.append(sz, core.c_ptr()); lbool is_sat = m_s.check_sat(m_mss.size(), m_mss.c_ptr()); IF_VERBOSE(3, display_vec(verbose_stream() << "mss: ", m_mss.size(), m_mss.c_ptr());); m_mss.resize(sz_save); switch (is_sat) { case l_true: m_s.get_model(m_model); update_mss(); DEBUG_CODE( for (unsigned i = 0; i < sz; ++i) { SASSERT(m_mss_set.contains(core[i])); }); update_core(core); return process_core(2*sz, core, has_mcs, is_last); case l_false: if (sz == 1) { has_mcs = true; m_mcs.insert(core[0]); core[0] = core.back(); core.pop_back(); } else { exprs core2; core2.append(core.size()-sz, core.c_ptr()+sz); core.resize(sz); is_sat = process_core(sz, core2, has_mcs, false); if (is_sat != l_true) { return is_sat; } update_core(core); } return process_core(1, core, has_mcs, is_last); case l_undef: return l_undef; } return l_true; } void mss::display_vec(std::ostream& out, unsigned sz, expr* const* args) const { for (unsigned i = 0; i < sz; ++i) { out << mk_pp(args[i], m) << " "; } out << "\n"; } void mss::display(std::ostream& out) const { for (unsigned i = 0; i < m_cores.size(); ++i) { display_vec(out << "core: ", m_cores[i].size(), m_cores[i].c_ptr()); } expr_set::iterator it = m_mcs.begin(), end = m_mcs.end(); out << "mcs:\n"; for (; it != end; ++it) { out << mk_pp(*it, m) << "\n"; } out << "\n"; out << "mss:\n"; for (unsigned i = 0; i < m_mss.size(); ++i) { out << mk_pp(m_mss[i], m) << "\n"; } out << "\n"; if (m_model) { model_smt2_pp(out, m, *(m_model.get()), 0); } } bool mss::check_invariant() const { if (!m_model) return true; expr_ref tmp(m); for (unsigned i = 0; i < m_mss.size(); ++i) { expr* n = m_mss[i]; VERIFY(m_model->eval(n, tmp)); CTRACE("opt", !m.is_true(tmp), tout << mk_pp(n, m) << " |-> " << mk_pp(tmp, m) << "\n";); SASSERT(!m.is_false(tmp)); } return true; } } z3-4.4.0/src/opt/optsmt.h0000644000175000017500000000376712540347414014752 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: optsmt.h Abstract: Objective optimization method. Author: Anh-Dung Phan (t-anphan) 2013-10-16 Notes: --*/ #ifndef _OPTSMT_H_ #define _OPTSMT_H_ #include "opt_solver.h" namespace opt { /** Takes solver with hard constraints added. Returns an optimal assignment to objective functions. */ class optsmt { ast_manager& m; opt_solver* m_s; volatile bool m_cancel; vector m_lower; vector m_upper; app_ref_vector m_objs; expr_ref_vector m_lower_fmls; svector m_vars; symbol m_optsmt_engine; model_ref m_model; sref_vector m_models; public: optsmt(ast_manager& m): m(m), m_s(0), m_cancel(false), m_objs(m), m_lower_fmls(m) {} void setup(opt_solver& solver); lbool box(); lbool lex(unsigned obj_index, bool is_maximize); unsigned add(app* t); void set_cancel(bool f); void updt_params(params_ref& p); unsigned get_num_objectives() const { return m_objs.size(); } void commit_assignment(unsigned index); inf_eps get_lower(unsigned index) const; inf_eps get_upper(unsigned index) const; bool objective_is_model_valid(unsigned index) const; void get_model(model_ref& mdl); model* get_model(unsigned index) const { return m_models[index]; } void update_lower(unsigned idx, inf_eps const& r); void update_upper(unsigned idx, inf_eps const& r); void reset(); private: lbool basic_opt(); lbool symba_opt(); lbool basic_lex(unsigned idx, bool is_maximize); lbool farkas_opt(); void set_max(vector& dst, vector const& src, expr_ref_vector& fmls); expr_ref update_lower(); lbool update_upper(); }; }; #endif z3-4.4.0/src/opt/opt_solver.h0000644000175000017500000001115612540347414015607 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: opt_solver.h Abstract: Wraps smt::kernel as a solver for optimization Author: Anh-Dung Phan (t-anphan) 2013-10-16 Notes: Based directly on smt_solver. --*/ #ifndef _OPT_SOLVER_H_ #define _OPT_SOLVER_H_ #include"inf_rational.h" #include"inf_eps_rational.h" #include"ast.h" #include"params.h" #include"solver_na2as.h" #include"smt_kernel.h" #include"smt_params.h" #include"smt_types.h" #include"theory_opt.h" #include"filter_model_converter.h" namespace opt { typedef inf_eps_rational inf_eps; // Adjust bound bound |-> m_offset + (m_negate?-1:1)*bound class adjust_value { rational m_offset; bool m_negate; public: adjust_value(rational const& offset, bool neg): m_offset(offset), m_negate(neg) {} adjust_value(): m_offset(0), m_negate(false) {} void set_offset(rational const& o) { m_offset = o; } void set_negate(bool neg) { m_negate = neg; } rational const& get_offset() const { return m_offset; } bool get_negate() { return m_negate; } inf_eps operator()(inf_eps const& r) const { inf_eps result = r; if (m_negate) result.neg(); result += m_offset; return result; } rational operator()(rational const& r) const { rational result = r; if (m_negate) result.neg(); result += m_offset; return result; } }; class opt_solver : public solver_na2as { private: smt_params m_params; smt::kernel m_context; ast_manager& m; filter_model_converter& m_fm; progress_callback * m_callback; symbol m_logic; svector m_objective_vars; vector m_objective_values; sref_vector m_models; expr_ref_vector m_objective_terms; svector m_valid_objectives; bool m_dump_benchmarks; static unsigned m_dump_count; statistics m_stats; bool m_first; public: opt_solver(ast_manager & m, params_ref const & p, filter_model_converter& fm); virtual ~opt_solver(); virtual void updt_params(params_ref & p); virtual void collect_param_descrs(param_descrs & r); virtual void collect_statistics(statistics & st) const; virtual void assert_expr(expr * t); virtual void push_core(); virtual void pop_core(unsigned n); virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions); virtual void get_unsat_core(ptr_vector & r); virtual void get_model(model_ref & _m); virtual proof * get_proof(); virtual std::string reason_unknown() const; virtual void get_labels(svector & r); virtual void set_cancel(bool f); virtual void set_progress_callback(progress_callback * callback); virtual unsigned get_num_assertions() const; virtual expr * get_assertion(unsigned idx) const; virtual void display(std::ostream & out) const; void set_logic(symbol const& logic); smt::theory_var add_objective(app* term); void reset_objectives(); void maximize_objective(unsigned i, expr_ref& blocker); void maximize_objectives(expr_ref_vector& blockers); inf_eps const & saved_objective_value(unsigned obj_index); inf_eps current_objective_value(unsigned obj_index); model* get_model(unsigned obj_index) { return m_models[obj_index]; } bool objective_is_model_valid(unsigned obj_index) const { return m_valid_objectives[obj_index]; } vector const& get_objective_values(); expr_ref mk_ge(unsigned obj_index, inf_eps const& val); static opt_solver& to_opt(solver& s); bool dump_benchmarks(); smt::context& get_context() { return m_context.get_context(); } // used by weighted maxsat. void ensure_pb(); smt::theory_opt& get_optimizer(); void to_smt2_benchmark(std::ofstream & buffer, unsigned num_assumptions, expr * const * assumptions, char const * name = "benchmarks", char const * logic = "", char const * status = "unknown", char const * attributes = ""); private: lbool decrement_value(unsigned i, inf_eps& val); void set_model(unsigned i); }; } #endif z3-4.4.0/src/opt/opt_params.pyg0000644000175000017500000000431512540347414016127 0ustar michaelmichaeldef_module_params('opt', description='optimization parameters', export=True, params=(('timeout', UINT, UINT_MAX, 'set timeout'), ('optsmt_engine', SYMBOL, 'basic', "select optimization engine: 'basic', 'farkas', 'symba'"), ('maxsat_engine', SYMBOL, 'maxres', "select engine for maxsat: 'fu_malik', 'core_maxsat', 'wmax', 'pbmax', 'maxres', 'pd-maxres', 'bcd2', 'wpm2', 'sls', 'maxhs'"), ('priority', SYMBOL, 'lex', "select how to priortize objectives: 'lex' (lexicographic), 'pareto', or 'box'"), ('dump_benchmarks', BOOL, False, 'dump benchmarks for profiling'), ('print_model', BOOL, False, 'display model for satisfiable constraints'), ('enable_sls', BOOL, False, 'enable SLS tuning during weighted maxsast'), ('enable_sat', BOOL, True, 'enable the new SAT core for propositional constraints'), ('elim_01', BOOL, True, 'eliminate 01 variables'), ('pp.neat', BOOL, True, 'use neat (as opposed to less readable, but faster) pretty printer when displaying context'), ('pb.compile_equality', BOOL, False, 'compile arithmetical equalities into pseudo-Boolean equality (instead of two inequalites)'), ('maxres.hill_climb', BOOL, True, 'give preference for large weight cores'), ('maxres.add_upper_bound_block', BOOL, False, 'restict upper bound with constraint'), ('maxres.max_num_cores', UINT, UINT_MAX, 'maximal number of cores per round'), ('maxres.max_core_size', UINT, 3, 'break batch of generated cores if size reaches this number'), ('maxres.maximize_assignment', BOOL, False, 'find an MSS/MCS to improve current assignment'), ('maxres.max_correction_set_size', UINT, 3, 'allow generating correction set constraints up to maximal size'), ('maxres.wmax', BOOL, False, 'use weighted theory solver to constrain upper bounds') )) z3-4.4.0/src/opt/inc_sat_solver.cpp0000644000175000017500000003441512540347414016763 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: inc_sat_solver.cpp Abstract: incremental solver based on SAT core. Author: Nikolaj Bjorner (nbjorner) 2014-7-30 Notes: --*/ #include "solver.h" #include "tactical.h" #include "sat_solver.h" #include "tactic2solver.h" #include "nnf_tactic.h" #include "aig_tactic.h" #include "propagate_values_tactic.h" #include "max_bv_sharing_tactic.h" #include "card2bv_tactic.h" #include "bit_blaster_tactic.h" #include "simplify_tactic.h" #include "goal2sat.h" #include "ast_pp.h" #include "model_smt2_pp.h" // incremental SAT solver. class inc_sat_solver : public solver { ast_manager& m; sat::solver m_solver; goal2sat m_goal2sat; params_ref m_params; bool m_optimize_model; // parameter expr_ref_vector m_fmls; expr_ref_vector m_asmsf; unsigned_vector m_fmls_lim; unsigned_vector m_asms_lim; unsigned_vector m_fmls_head_lim; unsigned m_fmls_head; expr_ref_vector m_core; atom2bool_var m_map; model_ref m_model; model_converter_ref m_mc; tactic_ref m_preprocess; unsigned m_num_scopes; sat::literal_vector m_asms; goal_ref_buffer m_subgoals; proof_converter_ref m_pc; model_converter_ref m_mc2; expr_dependency_ref m_dep_core; expr_ref_vector m_soft; vector m_weights; bool m_soft_assumptions; typedef obj_map dep2asm_t; public: inc_sat_solver(ast_manager& m, params_ref const& p): m(m), m_solver(p,0), m_params(p), m_optimize_model(false), m_fmls(m), m_asmsf(m), m_fmls_head(0), m_core(m), m_map(m), m_num_scopes(0), m_dep_core(m), m_soft(m), m_soft_assumptions(false) { m_params.set_bool("elim_vars", false); m_solver.updt_params(m_params); params_ref simp2_p = p; simp2_p.set_bool("som", true); simp2_p.set_bool("pull_cheap_ite", true); simp2_p.set_bool("push_ite_bv", false); simp2_p.set_bool("local_ctx", true); simp2_p.set_uint("local_ctx_limit", 10000000); simp2_p.set_bool("flat", true); // required by som simp2_p.set_bool("hoist_mul", false); // required by som m_preprocess = and_then(mk_card2bv_tactic(m, m_params), //mk_simplify_tactic(m), //mk_propagate_values_tactic(m), using_params(mk_simplify_tactic(m), simp2_p), mk_max_bv_sharing_tactic(m), mk_bit_blaster_tactic(m), mk_aig_tactic(), using_params(mk_simplify_tactic(m), simp2_p)); } virtual ~inc_sat_solver() {} virtual void set_progress_callback(progress_callback * callback) {} virtual lbool check_sat(unsigned num_assumptions, expr * const * assumptions) { m_solver.pop_to_base_level(); dep2asm_t dep2asm; m_model = 0; lbool r = internalize_formulas(); if (r != l_true) return r; r = internalize_assumptions(num_assumptions, assumptions, dep2asm); if (r != l_true) return r; extract_assumptions(dep2asm, m_asms); r = initialize_soft_constraints(); if (r != l_true) return r; r = m_solver.check(m_asms.size(), m_asms.c_ptr()); switch (r) { case l_true: if (num_assumptions > 0) { check_assumptions(dep2asm); } break; case l_false: // TBD: expr_dependency core is not accounted for. if (num_assumptions > 0) { extract_core(dep2asm); } break; default: break; } return r; } virtual void set_cancel(bool f) { m_goal2sat.set_cancel(f); m_solver.set_cancel(f); if (f) m_preprocess->cancel(); else m_preprocess->reset_cancel(); } virtual void push() { internalize_formulas(); m_solver.user_push(); ++m_num_scopes; m_fmls_lim.push_back(m_fmls.size()); m_asms_lim.push_back(m_asmsf.size()); m_fmls_head_lim.push_back(m_fmls_head); } virtual void pop(unsigned n) { if (n < m_num_scopes) { // allow inc_sat_solver to n = m_num_scopes; // take over for another solver. } SASSERT(n >= m_num_scopes); m_solver.user_pop(n); m_num_scopes -= n; while (n > 0) { m_fmls_head = m_fmls_head_lim.back(); m_fmls.resize(m_fmls_lim.back()); m_fmls_lim.pop_back(); m_fmls_head_lim.pop_back(); m_asmsf.resize(m_asms_lim.back()); m_asms_lim.pop_back(); --n; } } virtual unsigned get_scope_level() const { return m_num_scopes; } virtual void assert_expr(expr * t, expr * a) { if (a) { m_asmsf.push_back(a); assert_expr(m.mk_implies(a, t)); } else { assert_expr(t); } } virtual void assert_expr(expr * t) { TRACE("opt", tout << mk_pp(t, m) << "\n";); m_fmls.push_back(t); } virtual void set_produce_models(bool f) {} virtual void collect_param_descrs(param_descrs & r) { goal2sat::collect_param_descrs(r); sat::solver::collect_param_descrs(r); } virtual void updt_params(params_ref const & p) { m_params = p; m_params.set_bool("elim_vars", false); m_solver.updt_params(m_params); m_soft_assumptions = m_params.get_bool("soft_assumptions", false); m_optimize_model = m_params.get_bool("optimize_model", false); } virtual void collect_statistics(statistics & st) const { m_preprocess->collect_statistics(st); m_solver.collect_statistics(st); } virtual void get_unsat_core(ptr_vector & r) { r.reset(); r.append(m_core.size(), m_core.c_ptr()); } virtual void get_model(model_ref & mdl) { if (!m_model.get()) { extract_model(); } mdl = m_model; } virtual proof * get_proof() { UNREACHABLE(); return 0; } virtual std::string reason_unknown() const { return "no reason given"; } virtual void get_labels(svector & r) { UNREACHABLE(); } virtual unsigned get_num_assertions() const { return m_fmls.size(); } virtual expr * get_assertion(unsigned idx) const { return m_fmls[idx]; } virtual unsigned get_num_assumptions() const { return m_asmsf.size(); } virtual expr * get_assumption(unsigned idx) const { return m_asmsf[idx]; } void set_soft(unsigned sz, expr*const* soft, rational const* weights) { m_soft.reset(); m_weights.reset(); m_soft.append(sz, soft); m_weights.append(sz, weights); } private: lbool initialize_soft_constraints() { dep2asm_t dep2asm; if (m_soft.empty()) { return l_true; } expr_ref_vector soft(m_soft); for (unsigned i = 0; i < soft.size(); ++i) { expr* e = soft[i].get(), *e1; if (is_uninterp_const(e) || (m.is_not(e, e1) && is_uninterp_const(e1))) { continue; } expr_ref asum(m), fml(m); asum = m.mk_fresh_const("soft", m.mk_bool_sort()); fml = m.mk_iff(asum, e); m_fmls.push_back(fml); soft[i] = asum; } m_soft.reset(); lbool r = internalize_formulas(); if (r != l_true) return r; r = internalize_assumptions(soft.size(), soft.c_ptr(), dep2asm); if (r != l_true) return r; sat::literal_vector lits; svector weights; sat::literal lit; for (unsigned i = 0; i < soft.size(); ++i) { weights.push_back(m_weights[i].get_double()); expr* s = soft[i].get(); if (!dep2asm.find(s, lit)) { IF_VERBOSE(0, verbose_stream() << "not found: " << mk_pp(s, m) << "\n"; dep2asm_t::iterator it = dep2asm.begin(); dep2asm_t::iterator end = dep2asm.end(); for (; it != end; ++it) { verbose_stream() << mk_pp(it->m_key, m) << " " << it->m_value << "\n"; } UNREACHABLE();); } lits.push_back(lit); } m_solver.initialize_soft(lits.size(), lits.c_ptr(), weights.c_ptr()); return r; } lbool internalize_goal(goal_ref& g, dep2asm_t& dep2asm) { m_mc2.reset(); m_pc.reset(); m_dep_core.reset(); m_subgoals.reset(); SASSERT(g->models_enabled()); SASSERT(!g->proofs_enabled()); TRACE("opt", g->display(tout);); try { (*m_preprocess)(g, m_subgoals, m_mc2, m_pc, m_dep_core); } catch (tactic_exception & ex) { IF_VERBOSE(0, verbose_stream() << "exception in tactic " << ex.msg() << "\n";); return l_undef; } m_mc = concat(m_mc.get(), m_mc2.get()); if (m_subgoals.size() != 1) { IF_VERBOSE(0, verbose_stream() << "size of subgoals is not 1, it is: " << m_subgoals.size() << "\n";); return l_undef; } g = m_subgoals[0]; TRACE("opt", g->display_with_dependencies(tout);); m_goal2sat(*g, m_params, m_solver, m_map, dep2asm, true); return l_true; } lbool internalize_assumptions(unsigned sz, expr* const* asms, dep2asm_t& dep2asm) { if (sz == 0) { return l_true; } goal_ref g = alloc(goal, m, true, true); // models and cores are enabled. for (unsigned i = 0; i < sz; ++i) { g->assert_expr(asms[i], m.mk_leaf(asms[i])); } return internalize_goal(g, dep2asm); } lbool internalize_formulas() { if (m_fmls_head == m_fmls.size()) { return l_true; } dep2asm_t dep2asm; goal_ref g = alloc(goal, m, true, false); // models, maybe cores are enabled for (; m_fmls_head < m_fmls.size(); ++m_fmls_head) { g->assert_expr(m_fmls[m_fmls_head].get()); } return internalize_goal(g, dep2asm); } void extract_assumptions(dep2asm_t& dep2asm, sat::literal_vector& asms) { asms.reset(); dep2asm_t::iterator it = dep2asm.begin(), end = dep2asm.end(); for (; it != end; ++it) { asms.push_back(it->m_value); } //IF_VERBOSE(0, verbose_stream() << asms << "\n";); } void extract_core(dep2asm_t& dep2asm) { u_map asm2dep; dep2asm_t::iterator it = dep2asm.begin(), end = dep2asm.end(); for (; it != end; ++it) { asm2dep.insert(it->m_value.index(), it->m_key); } sat::literal_vector const& core = m_solver.get_core(); TRACE("opt", dep2asm_t::iterator it2 = dep2asm.begin(); dep2asm_t::iterator end2 = dep2asm.end(); for (; it2 != end2; ++it2) { tout << mk_pp(it2->m_key, m) << " |-> " << sat::literal(it2->m_value) << "\n"; } tout << "core: "; for (unsigned i = 0; i < core.size(); ++i) { tout << core[i] << " "; } tout << "\n"; ); m_core.reset(); for (unsigned i = 0; i < core.size(); ++i) { expr* e; VERIFY(asm2dep.find(core[i].index(), e)); m_core.push_back(e); } } void check_assumptions(dep2asm_t& dep2asm) { sat::model const & ll_m = m_solver.get_model(); dep2asm_t::iterator it = dep2asm.begin(), end = dep2asm.end(); for (; it != end; ++it) { sat::literal lit = it->m_value; if (!m_soft_assumptions && sat::value_at(lit, ll_m) != l_true) { IF_VERBOSE(0, verbose_stream() << mk_pp(it->m_key, m) << " does not evaluate to true\n"; verbose_stream() << m_asms << "\n"; m_solver.display_assignment(verbose_stream()); m_solver.display(verbose_stream());); throw default_exception("bad state"); } } } void extract_model() { TRACE("sat", tout << "retrieve model\n";); if (!m_solver.model_is_current()) { m_model = 0; return; } sat::model const & ll_m = m_solver.get_model(); model_ref md = alloc(model, m); atom2bool_var::iterator it = m_map.begin(); atom2bool_var::iterator end = m_map.end(); for (; it != end; ++it) { expr * n = it->m_key; if (is_app(n) && to_app(n)->get_num_args() > 0) { continue; } sat::bool_var v = it->m_value; switch (sat::value_at(v, ll_m)) { case l_true: md->register_decl(to_app(n)->get_decl(), m.mk_true()); break; case l_false: md->register_decl(to_app(n)->get_decl(), m.mk_false()); break; default: break; } } m_model = md; if (m_mc) { (*m_mc)(m_model); } SASSERT(m_model); DEBUG_CODE( for (unsigned i = 0; i < m_fmls.size(); ++i) { expr_ref tmp(m); VERIFY(m_model->eval(m_fmls[i].get(), tmp)); CTRACE("opt", !m.is_true(tmp), tout << "Evaluation failed: " << mk_pp(m_fmls[i].get(), m) << " to " << tmp << "\n"; model_smt2_pp(tout, m, *(m_model.get()), 0);); SASSERT(m.is_true(tmp)); }); } }; solver* mk_inc_sat_solver(ast_manager& m, params_ref& p) { return alloc(inc_sat_solver, m, p); } void set_soft_inc_sat(solver* _s, unsigned sz, expr*const* soft, rational const* weights) { inc_sat_solver* s = dynamic_cast(_s); s->set_soft(sz, soft, weights); } z3-4.4.0/src/opt/maxres.cpp0000644000175000017500000006077612540347414015261 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: maxsres.cpp Abstract: MaxRes (weighted) max-sat algorithms: - mus: max-sat algorithm by Nina and Bacchus, AAAI 2014. - mus-mss: based on dual refinement of bounds. MaxRes is a core-guided approach to maxsat. MusMssMaxRes extends the core-guided approach by leveraging both cores and satisfying assignments to make progress towards a maximal satisfying assignment. Given a (minimal) unsatisfiable core for the soft constraints the approach works like max-res. Given a (maximal) satisfying subset of the soft constraints the approach updates the upper bound if the current assignment improves the current best assignmet. Furthermore, take the soft constraints that are complements to the current satisfying subset. E.g, if F are the hard constraints and s1,...,sn, t1,..., tm are the soft clauses and F & s1 & ... & sn is satisfiable, then the complement of of the current satisfying subset is t1, .., tm. Update the hard constraint: F := F & (t1 or ... or tm) Replace t1, .., tm by m-1 new soft clauses: t1 & t2, t3 & (t1 or t2), t4 & (t1 or t2 or t3), ..., tn & (t1 or ... t_{n-1}) Claim: If k of these soft clauses are satisfied, then k+1 of the previous soft clauses are satisfied. If k of these soft clauses are false in the satisfying assignment for the updated F, then k of the original soft clauses are also false under the assignment. In summary: any assignment to the new clauses that satsfies F has the same cost. Claim: If there are no satisfying assignments to F, then the current best assignment is the optimum. Author: Nikolaj Bjorner (nbjorner) 2014-20-7 Notes: --*/ #include "solver.h" #include "maxsmt.h" #include "maxres.h" #include "ast_pp.h" #include "mus.h" #include "mss.h" #include "inc_sat_solver.h" #include "opt_context.h" #include "pb_decl_plugin.h" #include "opt_params.hpp" #include "ast_util.h" #include "smt_solver.h" using namespace opt; class maxres : public maxsmt_solver_base { public: enum strategy_t { s_primal, s_primal_dual }; private: struct stats { unsigned m_num_cores; unsigned m_num_cs; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; stats m_stats; expr_ref_vector m_B; expr_ref_vector m_asms; expr_ref_vector m_defs; obj_map m_asm2weight; ptr_vector m_new_core; mus m_mus; mss m_mss; expr_ref_vector m_trail; strategy_t m_st; rational m_max_upper; bool m_found_feasible_optimum; bool m_hill_climb; // prefer large weight soft clauses for cores unsigned m_last_index; // last index used during hill-climbing bool m_add_upper_bound_block; // restrict upper bound with constraint unsigned m_max_num_cores; // max number of cores per round. unsigned m_max_core_size; // max core size per round. bool m_maximize_assignment; // maximize assignment to find MCS unsigned m_max_correction_set_size;// maximal set of correction set that is tolerated. bool m_wmax; // Block upper bound using wmax // this option is disabled if SAT core is used. typedef ptr_vector exprs; public: maxres(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft, strategy_t st): maxsmt_solver_base(c, ws, soft), m_B(m), m_asms(m), m_defs(m), m_mus(c.get_solver(), m), m_mss(c.get_solver(), m), m_trail(m), m_st(st), m_found_feasible_optimum(false), m_hill_climb(true), m_last_index(0), m_add_upper_bound_block(false), m_max_num_cores(UINT_MAX), m_max_core_size(3), m_maximize_assignment(false), m_max_correction_set_size(3) { } virtual ~maxres() {} bool is_literal(expr* l) { return is_uninterp_const(l) || (m.is_not(l, l) && is_uninterp_const(l)); } void add_soft(expr* e, rational const& w) { TRACE("opt", tout << mk_pp(e, m) << "\n";); expr_ref asum(m), fml(m); app_ref cls(m); rational weight(0); if (m_asm2weight.find(e, weight)) { weight += w; m_asm2weight.insert(e, weight); m_upper += w; return; } if (is_literal(e)) { asum = e; } else { asum = mk_fresh_bool("soft"); fml = m.mk_iff(asum, e); s().assert_expr(fml); } new_assumption(asum, w); m_upper += w; } void new_assumption(expr* e, rational const& w) { IF_VERBOSE(3, verbose_stream() << "new assumption " << mk_pp(e, m) << " " << w << "\n";); TRACE("opt", tout << "insert: " << mk_pp(e, m) << " : " << w << "\n";); m_asm2weight.insert(e, w); m_asms.push_back(e); m_trail.push_back(e); } lbool mus_solver() { init(); init_local(); trace_bounds("maxres"); while (m_lower < m_upper) { TRACE("opt", display_vec(tout, m_asms); s().display(tout); tout << "\n"; display(tout); ); lbool is_sat = check_sat_hill_climb(m_asms); if (m_cancel) { return l_undef; } switch (is_sat) { case l_true: found_optimum(); return l_true; case l_false: is_sat = process_unsat(); if (is_sat != l_true) return is_sat; break; case l_undef: return l_undef; default: break; } } trace_bounds("maxres"); return l_true; } lbool primal_dual_solver() { init(); init_local(); set_soft_assumptions(); lbool is_sat = l_true; trace_bounds("max_res"); exprs cs; while (m_lower < m_upper) { #if 0 expr_ref_vector asms(m_asms); sort_assumptions(asms); is_sat = s().check_sat(asms.size(), asms.c_ptr()); #else is_sat = check_sat_hill_climb(m_asms); #endif if (m_cancel) { return l_undef; } switch (is_sat) { case l_true: get_current_correction_set(cs); IF_VERBOSE(2, display_vec(verbose_stream() << "correction set: ", cs);); if (cs.empty()) { m_found_feasible_optimum = m_model.get() != 0; m_lower = m_upper; } else { process_sat(cs); } break; case l_false: is_sat = process_unsat(); if (is_sat != l_true) return is_sat; break; case l_undef: return l_undef; default: break; } } trace_bounds("maxres"); return l_true; } lbool check_sat_hill_climb(expr_ref_vector& asms1) { expr_ref_vector asms(asms1); lbool is_sat = l_true; if (m_hill_climb) { /** Give preference to cores that have large minmal values. */ sort_assumptions(asms); m_last_index = std::min(m_last_index, asms.size()-1); m_last_index = 0; unsigned index = m_last_index>0?m_last_index-1:0; m_last_index = 0; bool first = index > 0; SASSERT(index < asms.size() || asms.empty()); while (index < asms.size() && is_sat == l_true) { while (!first && asms.size() > 20*(index - m_last_index) && index < asms.size()) { index = next_index(asms, index); } first = false; IF_VERBOSE(3, verbose_stream() << "weight: " << get_weight(asms[0].get()) << " " << get_weight(asms[index-1].get()) << " num soft: " << index << "\n";); m_last_index = index; is_sat = s().check_sat(index, asms.c_ptr()); } } else { is_sat = s().check_sat(asms.size(), asms.c_ptr()); } return is_sat; } void found_optimum() { IF_VERBOSE(1, verbose_stream() << "found optimum\n";); s().get_model(m_model); DEBUG_CODE( for (unsigned i = 0; i < m_asms.size(); ++i) { SASSERT(is_true(m_asms[i].get())); }); rational upper(0); for (unsigned i = 0; i < m_soft.size(); ++i) { m_assignment[i] = is_true(m_soft[i]); if (!m_assignment[i]) upper += m_weights[i]; } SASSERT(upper == m_lower); m_upper = m_lower; m_found_feasible_optimum = true; } virtual lbool operator()() { m_defs.reset(); switch(m_st) { case s_primal: return mus_solver(); case s_primal_dual: return primal_dual_solver(); } return l_undef; } virtual void collect_statistics(statistics& st) const { st.update("maxres-cores", m_stats.m_num_cores); st.update("maxres-correction-sets", m_stats.m_num_cs); } lbool get_cores(vector& cores) { // assume m_s is unsat. lbool is_sat = l_false; expr_ref_vector asms(m_asms); cores.reset(); exprs core; while (is_sat == l_false) { core.reset(); s().get_unsat_core(core); //verify_core(core); model_ref mdl; get_mus_model(mdl); is_sat = minimize_core(core); ++m_stats.m_num_cores; if (is_sat != l_true) { break; } if (core.empty()) { cores.reset(); m_lower = m_upper; return l_true; } cores.push_back(core); if (core.size() >= m_max_core_size) { break; } if (cores.size() >= m_max_num_cores) { break; } remove_soft(core, asms); is_sat = check_sat_hill_climb(asms); } TRACE("opt", tout << "num cores: " << cores.size() << "\n"; for (unsigned i = 0; i < cores.size(); ++i) { display_vec(tout, cores[i]); } tout << "num satisfying: " << asms.size() << "\n";); return is_sat; } void get_current_correction_set(exprs& cs) { model_ref mdl; s().get_model(mdl); update_assignment(mdl.get()); get_current_correction_set(mdl.get(), cs); } void get_current_correction_set(model* mdl, exprs& cs) { ++m_stats.m_num_cs; cs.reset(); if (!mdl) return; for (unsigned i = 0; i < m_asms.size(); ++i) { if (is_false(mdl, m_asms[i].get())) { cs.push_back(m_asms[i].get()); } } TRACE("opt", display_vec(tout << "new correction set: ", cs);); } struct compare_asm { maxres& mr; compare_asm(maxres& mr):mr(mr) {} bool operator()(expr* a, expr* b) const { return mr.get_weight(a) > mr.get_weight(b); } }; void sort_assumptions(expr_ref_vector& _asms) { compare_asm comp(*this); exprs asms(_asms.size(), _asms.c_ptr()); expr_ref_vector trail(_asms); std::sort(asms.begin(), asms.end(), comp); _asms.reset(); _asms.append(asms.size(), asms.c_ptr()); DEBUG_CODE( for (unsigned i = 0; i + 1 < asms.size(); ++i) { SASSERT(get_weight(asms[i]) >= get_weight(asms[i+1])); }); } unsigned next_index(expr_ref_vector const& asms, unsigned index) { if (index < asms.size()) { rational w = get_weight(asms[index]); ++index; for (; index < asms.size() && w == get_weight(asms[index]); ++index); } return index; } void process_sat(exprs const& corr_set) { expr_ref fml(m), tmp(m); TRACE("opt", display_vec(tout << "corr_set: ", corr_set);); remove_core(corr_set); rational w = split_core(corr_set); cs_max_resolve(corr_set, w); IF_VERBOSE(2, verbose_stream() << "(opt.maxres.correction-set " << corr_set.size() << ")\n";); } lbool process_unsat() { vector cores; lbool is_sat = get_cores(cores); if (is_sat != l_true) { return is_sat; } if (cores.empty()) { return l_false; } else { process_unsat(cores); return l_true; } } unsigned max_core_size(vector const& cores) { unsigned result = 0; for (unsigned i = 0; i < cores.size(); ++i) { result = std::max(cores[i].size(), result); } return result; } void process_unsat(vector const& cores) { for (unsigned i = 0; i < cores.size(); ++i) { process_unsat(cores[i]); } } void process_unsat(exprs const& core) { expr_ref fml(m); remove_core(core); SASSERT(!core.empty()); rational w = split_core(core); TRACE("opt", display_vec(tout << "minimized core: ", core);); max_resolve(core, w); fml = mk_not(m, mk_and(m, m_B.size(), m_B.c_ptr())); s().assert_expr(fml); m_lower += w; trace_bounds("maxres"); } bool get_mus_model(model_ref& mdl) { rational w(0); if (m_c.sat_enabled()) { // SAT solver core extracts some model // during unsat core computation. mdl = 0; s().get_model(mdl); } else { w = m_mus.get_best_model(mdl); } if (mdl.get() && w < m_upper) { update_assignment(mdl.get()); } return 0 != mdl.get(); } lbool minimize_core(exprs& core) { if (m_c.sat_enabled() || core.empty()) { return l_true; } m_mus.reset(); for (unsigned i = 0; i < core.size(); ++i) { m_mus.add_soft(core[i]); } unsigned_vector mus_idx; lbool is_sat = m_mus.get_mus(mus_idx); if (is_sat != l_true) { return is_sat; } m_new_core.reset(); for (unsigned i = 0; i < mus_idx.size(); ++i) { m_new_core.push_back(core[mus_idx[i]]); } core.reset(); core.append(m_new_core); return l_true; } rational get_weight(expr* e) const { return m_asm2weight.find(e); } void sls() { vector ws; for (unsigned i = 0; i < m_asms.size(); ++i) { ws.push_back(get_weight(m_asms[i].get())); } enable_sls(m_asms, ws); } rational split_core(exprs const& core) { if (core.empty()) return rational(0); // find the minimal weight: rational w = get_weight(core[0]); for (unsigned i = 1; i < core.size(); ++i) { w = std::min(w, get_weight(core[i])); } // add fresh soft clauses for weights that are above w. for (unsigned i = 0; i < core.size(); ++i) { rational w2 = get_weight(core[i]); if (w2 > w) { rational w3 = w2 - w; new_assumption(core[i], w3); } } return w; } void display_vec(std::ostream& out, exprs const& exprs) { display_vec(out, exprs.size(), exprs.c_ptr()); } void display_vec(std::ostream& out, expr_ref_vector const& exprs) { display_vec(out, exprs.size(), exprs.c_ptr()); } void display_vec(std::ostream& out, unsigned sz, expr* const* args) const { for (unsigned i = 0; i < sz; ++i) { out << mk_pp(args[i], m) << " : " << get_weight(args[i]) << " "; } out << "\n"; } void display(std::ostream& out) { for (unsigned i = 0; i < m_asms.size(); ++i) { expr* a = m_asms[i].get(); out << mk_pp(a, m) << " : " << get_weight(a) << "\n"; } } void max_resolve(exprs const& core, rational const& w) { SASSERT(!core.empty()); expr_ref fml(m), asum(m); app_ref cls(m), d(m), dd(m); m_B.reset(); m_B.append(core.size(), core.c_ptr()); // // d_0 := true // d_i := b_{i-1} and d_{i-1} for i = 1...sz-1 // soft (b_i or !d_i) // == (b_i or !(!b_{i-1} or d_{i-1})) // == (b_i or b_0 & b_1 & ... & b_{i-1}) // // Soft constraint is satisfied if previous soft constraint // holds or if it is the first soft constraint to fail. // // Soundness of this rule can be established using MaxRes // for (unsigned i = 1; i < core.size(); ++i) { expr* b_i = m_B[i-1].get(); expr* b_i1 = m_B[i].get(); if (i == 1) { d = to_app(b_i); } else if (i == 2) { d = m.mk_and(b_i, d); m_trail.push_back(d); } else { dd = mk_fresh_bool("d"); fml = m.mk_implies(dd, d); s().assert_expr(fml); m_defs.push_back(fml); fml = m.mk_implies(dd, b_i); s().assert_expr(fml); m_defs.push_back(fml); d = dd; } asum = mk_fresh_bool("a"); cls = m.mk_or(b_i1, d); fml = m.mk_implies(asum, cls); new_assumption(asum, w); s().assert_expr(fml); m_defs.push_back(fml); } } // cs is a correction set (a complement of a (maximal) satisfying assignment). void cs_max_resolve(exprs const& cs, rational const& w) { if (cs.empty()) return; TRACE("opt", display_vec(tout << "correction set: ", cs);); expr_ref fml(m), asum(m); app_ref cls(m), d(m), dd(m); m_B.reset(); m_B.append(cs.size(), cs.c_ptr()); d = m.mk_false(); // // d_0 := false // d_i := b_{i-1} or d_{i-1} for i = 1...sz-1 // soft (b_i and d_i) // == (b_i and (b_0 or b_1 or ... or b_{i-1})) // // asm => b_i // asm => d_{i-1} or b_{i-1} // d_i => d_{i-1} or b_{i-1} // for (unsigned i = 1; i < cs.size(); ++i) { expr* b_i = m_B[i-1].get(); expr* b_i1 = m_B[i].get(); cls = m.mk_or(b_i, d); if (i > 2) { d = mk_fresh_bool("d"); fml = m.mk_implies(d, cls); s().assert_expr(fml); m_defs.push_back(fml); } else { d = cls; } asum = mk_fresh_bool("a"); fml = m.mk_implies(asum, b_i1); s().assert_expr(fml); m_defs.push_back(fml); fml = m.mk_implies(asum, cls); s().assert_expr(fml); m_defs.push_back(fml); new_assumption(asum, w); } fml = m.mk_or(m_B.size(), m_B.c_ptr()); s().assert_expr(fml); } void update_assignment(model* mdl) { rational upper(0); expr_ref tmp(m); for (unsigned i = 0; i < m_soft.size(); ++i) { if (!is_true(mdl, m_soft[i])) { upper += m_weights[i]; } } if (upper >= m_upper) { return; } m_model = mdl; for (unsigned i = 0; i < m_soft.size(); ++i) { m_assignment[i] = is_true(m_soft[i]); } m_upper = upper; DEBUG_CODE(verify_assignment();); trace_bounds("maxres"); add_upper_bound_block(); } void add_upper_bound_block() { if (!m_add_upper_bound_block) return; pb_util u(m); expr_ref_vector nsoft(m); expr_ref fml(m); for (unsigned i = 0; i < m_soft.size(); ++i) { nsoft.push_back(mk_not(m, m_soft[i])); } fml = u.mk_lt(nsoft.size(), m_weights.c_ptr(), nsoft.c_ptr(), m_upper); s().assert_expr(fml); } bool is_true(model* mdl, expr* e) { expr_ref tmp(m); VERIFY(mdl->eval(e, tmp)); return m.is_true(tmp); } bool is_false(model* mdl, expr* e) { expr_ref tmp(m); VERIFY(mdl->eval(e, tmp)); return m.is_false(tmp); } bool is_true(expr* e) { return is_true(m_model.get(), e); } void remove_soft(exprs const& core, expr_ref_vector& asms) { for (unsigned i = 0; i < asms.size(); ++i) { if (core.contains(asms[i].get())) { asms[i] = asms.back(); asms.pop_back(); --i; } } } void remove_core(exprs const& core) { remove_soft(core, m_asms); } virtual void set_cancel(bool f) { maxsmt_solver_base::set_cancel(f); m_mus.set_cancel(f); } virtual void updt_params(params_ref& p) { maxsmt_solver_base::updt_params(p); opt_params _p(p); m_hill_climb = _p.maxres_hill_climb(); m_add_upper_bound_block = _p.maxres_add_upper_bound_block(); m_max_num_cores = _p.maxres_max_num_cores(); m_max_core_size = _p.maxres_max_core_size(); m_maximize_assignment = _p.maxres_maximize_assignment(); m_max_correction_set_size = _p.maxres_max_correction_set_size(); m_wmax = _p.maxres_wmax(); } void init_local() { m_upper.reset(); m_lower.reset(); m_trail.reset(); for (unsigned i = 0; i < m_soft.size(); ++i) { add_soft(m_soft[i], m_weights[i]); } m_max_upper = m_upper; m_found_feasible_optimum = false; m_last_index = 0; add_upper_bound_block(); } virtual void commit_assignment() { if (m_found_feasible_optimum) { TRACE("opt", tout << "Committing feasible solution\n"; tout << m_defs; tout << m_asms; ); for (unsigned i = 0; i < m_defs.size(); ++i) { s().assert_expr(m_defs[i].get()); } for (unsigned i = 0; i < m_asms.size(); ++i) { s().assert_expr(m_asms[i].get()); } } else { maxsmt_solver_base::commit_assignment(); } } void verify_core(exprs const& core) { IF_VERBOSE(3, verbose_stream() << "verify core\n";); ref smt_solver = mk_smt_solver(m, m_params, symbol()); for (unsigned i = 0; i < s().get_num_assertions(); ++i) { smt_solver->assert_expr(s().get_assertion(i)); } for (unsigned i = 0; i < core.size(); ++i) { smt_solver->assert_expr(core[i]); } lbool is_sat = smt_solver->check_sat(0, 0); if (is_sat == l_true) { IF_VERBOSE(0, verbose_stream() << "not a core\n";); } } void verify_assignment() { IF_VERBOSE(1, verbose_stream() << "verify assignment\n";); ref smt_solver = mk_smt_solver(m, m_params, symbol()); for (unsigned i = 0; i < s().get_num_assertions(); ++i) { smt_solver->assert_expr(s().get_assertion(i)); } expr_ref n(m); for (unsigned i = 0; i < m_soft.size(); ++i) { n = m_soft[i]; if (!m_assignment[i]) { n = mk_not(m, n); } smt_solver->assert_expr(n); } lbool is_sat = smt_solver->check_sat(0, 0); if (is_sat == l_false) { IF_VERBOSE(0, verbose_stream() << "assignment is infeasible\n";); } } }; opt::maxsmt_solver_base* opt::mk_maxres( maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { return alloc(maxres, c, ws, soft, maxres::s_primal); } opt::maxsmt_solver_base* opt::mk_primal_dual_maxres( maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { return alloc(maxres, c, ws, soft, maxres::s_primal_dual); } z3-4.4.0/src/opt/maxres.h0000644000175000017500000000077312540347414014715 0ustar michaelmichael/*++ Copyright (c) 2014 Microsoft Corporation Module Name: maxsres.h Abstract: MaxRes (weighted) max-sat algorithm by Nina and Bacchus, AAAI 2014. Author: Nikolaj Bjorner (nbjorner) 2014-20-7 Notes: --*/ #ifndef _MAXRES_H_ #define _MAXRES_H_ namespace opt { maxsmt_solver_base* mk_maxres(maxsat_context& c, weights_t & ws, expr_ref_vector const& soft); maxsmt_solver_base* mk_primal_dual_maxres(maxsat_context& c, weights_t & ws, expr_ref_vector const& soft); }; #endif z3-4.4.0/src/opt/fu_malik.h0000644000175000017500000000115512540347414015200 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: fu_malik.h Abstract: Fu&Malik built-in optimization method. Adapted from sample code in C. Author: Anh-Dung Phan (t-anphan) 2013-10-15 Notes: Takes solver with hard constraints added. Returns a maximal satisfying subset of soft_constraints that are still consistent with the solver state. --*/ #ifndef _OPT_FU_MALIK_H_ #define _OPT_FU_MALIK_H_ #include "opt_solver.h" #include "maxsmt.h" namespace opt { maxsmt_solver_base* mk_fu_malik(maxsat_context& c, weights_t & ws, expr_ref_vector const& soft); }; #endif z3-4.4.0/src/qe/0000755000175000017500000000000012540347414013041 5ustar michaelmichaelz3-4.4.0/src/qe/qe_arith.cpp0000644000175000017500000002422212540347414015343 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: qe_arith.cpp Abstract: Simple projection function for real arithmetic based on Loos-W. Author: Nikolaj Bjorner (nbjorner) 2013-09-12 Revision History: --*/ #include "qe_arith.h" #include "qe_util.h" #include "arith_decl_plugin.h" #include "ast_pp.h" #include "th_rewriter.h" #include "expr_functors.h" namespace qe { class arith_project_util { ast_manager& m; arith_util a; th_rewriter m_rw; expr_ref_vector m_ineq_terms; vector m_ineq_coeffs; svector m_ineq_strict; scoped_ptr m_var; struct cant_project {}; void is_linear(rational const& mul, expr* t, rational& c, expr_ref_vector& ts) { expr* t1, *t2; rational mul1; if (t == m_var->x()) { c += mul; } else if (a.is_mul(t, t1, t2) && a.is_numeral(t1, mul1)) { is_linear(mul* mul1, t2, c, ts); } else if (a.is_mul(t, t1, t2) && a.is_numeral(t2, mul1)) { is_linear(mul* mul1, t1, c, ts); } else if (a.is_add(t)) { app* ap = to_app(t); for (unsigned i = 0; i < ap->get_num_args(); ++i) { is_linear(mul, ap->get_arg(i), c, ts); } } else if (a.is_sub(t, t1, t2)) { is_linear(mul, t1, c, ts); is_linear(-mul, t2, c, ts); } else if (a.is_uminus(t, t1)) { is_linear(-mul, t1, c, ts); } else if (a.is_numeral(t, mul1)) { ts.push_back(a.mk_numeral(mul*mul1, m.get_sort(t))); } else if ((*m_var)(t)) { IF_VERBOSE(1, verbose_stream() << "can't project:" << mk_pp(t, m) << "\n";); throw cant_project(); } else if (mul.is_one()) { ts.push_back(t); } else { ts.push_back(a.mk_mul(a.mk_numeral(mul, m.get_sort(t)), t)); } } bool is_linear(expr* lit, rational& c, expr_ref& t, bool& is_strict) { if (!(*m_var)(lit)) { return false; } expr* e1, *e2; c.reset(); sort* s; expr_ref_vector ts(m); bool is_not = m.is_not(lit, lit); rational mul(1); if (is_not) { mul.neg(); } SASSERT(!m.is_not(lit)); if (a.is_le(lit, e1, e2) || a.is_ge(lit, e2, e1)) { is_linear( mul, e1, c, ts); is_linear(-mul, e2, c, ts); s = m.get_sort(e1); is_strict = is_not; } else if (a.is_lt(lit, e1, e2) || a.is_gt(lit, e2, e1)) { is_linear( mul, e1, c, ts); is_linear(-mul, e2, c, ts); s = m.get_sort(e1); is_strict = !is_not; } else if (m.is_eq(lit, e1, e2) && !is_not) { is_linear( mul, e1, c, ts); is_linear(-mul, e2, c, ts); s = m.get_sort(e1); is_strict = false; } else { IF_VERBOSE(1, verbose_stream() << "can't project:" << mk_pp(lit, m) << "\n";); throw cant_project(); } if (ts.empty()) { t = a.mk_numeral(rational(0), s); } else { t = a.mk_add(ts.size(), ts.c_ptr()); } return true; } void project(model& model, expr_ref_vector& lits) { unsigned num_pos = 0; unsigned num_neg = 0; m_ineq_terms.reset(); m_ineq_coeffs.reset(); m_ineq_strict.reset(); expr_ref_vector new_lits(m); for (unsigned i = 0; i < lits.size(); ++i) { rational c(0); expr_ref t(m); bool is_strict; if (is_linear(lits[i].get(), c, t, is_strict)) { m_ineq_coeffs.push_back(c); m_ineq_terms.push_back(t); m_ineq_strict.push_back(is_strict); if (c.is_zero()) { m_rw(lits[i].get(), t); new_lits.push_back(t); } else if (c.is_pos()) { ++num_pos; } else { ++num_neg; } } else { new_lits.push_back(lits[i].get()); } } lits.reset(); lits.append(new_lits); if (num_pos == 0 || num_neg == 0) { return; } bool use_pos = num_pos < num_neg; unsigned max_t = find_max(model, use_pos); for (unsigned i = 0; i < m_ineq_terms.size(); ++i) { if (i != max_t) { if (m_ineq_coeffs[i].is_pos() == use_pos) { lits.push_back(mk_le(i, max_t)); } else { lits.push_back(mk_lt(i, max_t)); } } } } unsigned find_max(model& mdl, bool do_pos) { unsigned result; bool found = false; rational found_val(0), r, found_c; expr_ref val(m); for (unsigned i = 0; i < m_ineq_terms.size(); ++i) { rational const& ac = m_ineq_coeffs[i]; if (ac.is_pos() == do_pos) { VERIFY(mdl.eval(m_ineq_terms[i].get(), val)); VERIFY(a.is_numeral(val, r)); r /= abs(ac); IF_VERBOSE(1, verbose_stream() << "max: " << mk_pp(m_ineq_terms[i].get(), m) << " " << r << " " << (!found || r > found_val) << "\n";); if (!found || r > found_val) { result = i; found_val = r; found_c = ac; found = true; } } } SASSERT(found); if (a.is_int(m_var->x()) && !found_c.is_one()) { throw cant_project(); } return result; } // ax + t <= 0 // bx + s <= 0 // a and b have different signs. // Infer: a|b|x + |b|t + |a|bx + |a|s <= 0 // e.g. |b|t + |a|s <= 0 expr_ref mk_lt(unsigned i, unsigned j) { rational const& ac = m_ineq_coeffs[i]; rational const& bc = m_ineq_coeffs[j]; SASSERT(ac.is_pos() != bc.is_pos()); SASSERT(ac.is_neg() != bc.is_neg()); expr* t = m_ineq_terms[i].get(); expr* s = m_ineq_terms[j].get(); expr_ref bt = mk_mul(abs(bc), t); expr_ref as = mk_mul(abs(ac), s); expr_ref ts = mk_add(bt, as); expr* z = a.mk_numeral(rational(0), m.get_sort(t)); expr_ref result1(m), result2(m); if (m_ineq_strict[i] || m_ineq_strict[j]) { result1 = a.mk_lt(ts, z); } else { result1 = a.mk_le(ts, z); } m_rw(result1, result2); return result2; } // ax + t <= 0 // bx + s <= 0 // a and b have same signs. // encode:// t/|a| <= s/|b| // e.g. |b|t <= |a|s expr_ref mk_le(unsigned i, unsigned j) { rational const& ac = m_ineq_coeffs[i]; rational const& bc = m_ineq_coeffs[j]; SASSERT(ac.is_pos() == bc.is_pos()); SASSERT(ac.is_neg() == bc.is_neg()); expr* t = m_ineq_terms[i].get(); expr* s = m_ineq_terms[j].get(); expr_ref bt = mk_mul(abs(bc), t); expr_ref as = mk_mul(abs(ac), s); expr_ref result1(m), result2(m); if (m_ineq_strict[j] && !m_ineq_strict[i]) { result1 = a.mk_lt(bt, as); } else { result1 = a.mk_le(bt, as); } m_rw(result1, result2); return result2; } expr_ref mk_add(expr* t1, expr* t2) { return expr_ref(a.mk_add(t1, t2), m); } expr_ref mk_mul(rational const& r, expr* t2) { expr* t1 = a.mk_numeral(r, m.get_sort(t2)); return expr_ref(a.mk_mul(t1, t2), m); } public: arith_project_util(ast_manager& m): m(m), a(m), m_rw(m), m_ineq_terms(m) {} expr_ref operator()(model& model, app_ref_vector& vars, expr_ref_vector const& lits) { app_ref_vector new_vars(m); expr_ref_vector result(lits); for (unsigned i = 0; i < vars.size(); ++i) { app* v = vars[i].get(); m_var = alloc(contains_app, m, v); try { project(model, result); TRACE("qe", tout << "projected: " << mk_pp(v, m) << " "; for (unsigned i = 0; i < result.size(); ++i) { tout << mk_pp(result[i].get(), m) << "\n"; }); } catch (cant_project) { IF_VERBOSE(1, verbose_stream() << "can't project:" << mk_pp(v, m) << "\n";); new_vars.push_back(v); } } vars.reset(); vars.append(new_vars); return qe::mk_and(result); } }; expr_ref arith_project(model& model, app_ref_vector& vars, expr_ref_vector const& lits) { ast_manager& m = vars.get_manager(); arith_project_util ap(m); return ap(model, vars, lits); } expr_ref arith_project(model& model, app_ref_vector& vars, expr* fml) { ast_manager& m = vars.get_manager(); arith_project_util ap(m); expr_ref_vector lits(m); qe::flatten_and(fml, lits); return ap(model, vars, lits); } } z3-4.4.0/src/qe/nlarith_util.cpp0000644000175000017500000022004712540347414016250 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ast.h" #include "nlarith_util.h" #include "arith_decl_plugin.h" #include "ast_pp.h" #include "qe.h" #include "expr_replacer.h" #include "arith_rewriter.h" #include "arith_simplifier_plugin.h" #include "expr_functors.h" namespace nlarith { typedef app_ref_vector poly; enum comp { LE, LT, EQ, NE}; typedef vector polys; typedef svector comps; class util::literal_set { app_ref m_inf; app_ref m_sup; app* m_x; app_ref_vector m_lits; vector m_polys; svector m_comps; public: literal_set(ast_manager& m) : m_inf(m), m_sup(m), m_x(0), m_lits(m) {} unsigned size() const { return m_lits.size(); } app_ref_vector& lits() { return m_lits; } vector& polys() { return m_polys; } svector& comps() { return m_comps; } poly const& get_poly(unsigned i) const { return m_polys[i]; } // Note: comp comp(unsigned i) is not valid C++. // it works on VC++, but it is rejected by gcc. comp compare(unsigned i) const { return m_comps[i]; } app* literal(unsigned i) const { return m_lits[i]; } app* x() const { return m_x; } void set_x(app* x) { SASSERT(!m_x); m_x = x; } app* x_inf() { if (!m_inf) { mk_const("inf", m_inf); } return m_inf; } app* x_sup() { if (!m_sup) { mk_const("sup", m_sup); } return m_sup; } private: void mk_const(char const* suffix, app_ref& v) { ast_manager& m = m_lits.get_manager(); std::string name = m_x->get_decl()->get_name().str(); name += suffix; sort* r = m.get_sort(m_x); v= m.mk_const(symbol(name.c_str()), r); } }; class util::imp { ast_manager& m_manager; arith_util m_arith; bool m_enable_linear; app_ref m_zero; app_ref m_one; smt_params m_params; basic_simplifier_plugin m_bs; arith_simplifier_plugin m_rw; arith_rewriter m_rw1; expr_ref_vector m_trail; ast_manager& m() const { return m_manager; } arith_util& a() { return m_arith; } app* z() { return m_zero.get();} app* one() { return m_one.get(); } std::ostream& display(std::ostream& out, comp c) { switch(c) { case LE: out << "<="; return out; case LT: out << "<"; return out; case EQ: out << "="; return out; case NE: out << "!="; return out; } return out; } public: imp(ast_manager& m) : m_manager(m), m_arith(m), m_enable_linear(false), m_zero(num(0),m), m_one(num(1),m), m_bs(m), m_rw(m, m_bs, m_params), m_rw1(m), m_trail(m) { } // // create branches and substitutions according to case analysis. // bool create_branches(app* x, unsigned num_lits, expr* const* lits, branch_conditions& branch_conds) { polys polys; comps comps; contains_app contains_x(m(), x); branch_conds.reset(); m_trail.reset(); // use scope? if (!a().is_real(x)) { return false; } if (!get_polys(contains_x, num_lits, lits, polys, comps, &branch_conds, 0)) { TRACE("nlarith", tout << "could not extract polynomials " << mk_pp(x, m()) << "\n"; for (unsigned i = 0; i < num_lits; ++i) { tout << mk_pp(lits[i], m()) << " "; } tout << "\n"; ); return false; } if (is_degree_two_plus(polys)) { return false; } if (!m_enable_linear && is_linear(polys)) { TRACE("nlarith", tout << "this is a linear problem " << mk_pp(x,m()) << "\n"; display(tout, polys);); return false; } unsigned idx; if (has_single_degree2(polys, comps, idx)) { for (unsigned i = 0; i < polys.size(); ++i) { create_branch_l(idx, i, polys, comps, branch_conds); } } else { for (unsigned i = 0; i < polys.size(); ++i) { create_branch(i, polys, comps, branch_conds); } } inf_branch(polys, comps, branch_conds); TRACE("nlarith", for (unsigned i = 0; i < num_lits; ++i) { tout << mk_pp(lits[i], m()) << " "; } tout << "\n"; display_branching(tout, x, branch_conds); ); return true; } void set_enable_linear(bool enable_linear) { m_enable_linear = enable_linear; } void extract_non_linear(unsigned sz, app* const* es, ptr_vector& nl_vars) { ast_mark visit; for (unsigned i = 0; i < sz; ++i) { extract_non_linear(es[i], visit, nl_vars); } } void extract_non_linear(expr* e, ptr_vector& nl_vars) { ast_mark visit; extract_non_linear(e, visit, nl_vars); } void extract_non_linear(expr* e, ast_mark& visit, ptr_vector& nl_vars) { if (visit.is_marked(e)) { return; } ast_mark nonlin; ptr_vector todo; todo.push_back(e); while(!todo.empty()) { e = todo.back(); todo.pop_back(); if (is_var(e)) { continue; } if (is_quantifier(e)) { e = to_quantifier(e)->get_expr(); if (!visit.is_marked(e)) { todo.push_back(e); } } SASSERT(is_app(e)); app* a = to_app(e); bool nl = m_enable_linear || nonlin.is_marked(e) || is_nonlinear(a); if (is_arithmetical(a)) { // TBD: overshoots in the case of 'ite' expressions. for (unsigned i = 0; i < a->get_num_args(); ++i) { expr* arg = a->get_arg(i); bool nl2 = nonlin.is_marked(arg); if (!visit.is_marked(arg) || (nl && !nl2)) { todo.push_back(to_app(arg)); visit.mark(arg, true); if (nl) { nonlin.mark(arg, true); } } } } else if (is_variable(a)) { if (nl) { nl_vars.push_back(a); } } else { for (unsigned i = 0; i < a->get_num_args(); ++i) { expr* arg = a->get_arg(i); if (!visit.is_marked(arg) || !nonlin.is_marked(arg)) { todo.push_back(to_app(arg)); visit.mark(arg, true); nonlin.mark(arg, true); } } } } TRACE("nlarith", tout << "Non-linear variables: "; for (unsigned i = 0; i < nl_vars.size(); ++i) { tout << mk_pp(nl_vars[i], m()) << " "; } tout << "\n";); } private: void track(expr* e) { m_trail.push_back(e); } app* mk_lt(expr* p) { expr_ref r(m()); m_rw.mk_lt(p, z(), r); track(r); return to_app(r); } app* mk_le(expr* p) { expr_ref r(m()); m_rw.mk_le(p, z(), r); track(r); return to_app(r); } app* mk_gt(expr* p) { return mk_lt(mk_uminus(p)); } app* mk_ge(expr* p) { return mk_le(mk_uminus(p)); } app* mk_eq(expr* p) { expr_ref r(m()); m_bs.mk_eq(p, z(), r); track(r); return to_app(r); } app* mk_ne(expr* p) { expr_ref r(m()); m_bs.mk_eq(p, z(), r); m_bs.mk_not(r, r); track(r); return to_app(r); } app* num(int i) { return a().mk_numeral(rational(i), false); } // // TBD: perhaps merge with arith_rewriter using a app_ref buffer? // app* mk_uminus(expr* e) { expr_ref r(m()); m_rw.mk_uminus(e, r); track(r); return to_app(r); } app* mk_add(unsigned sz, expr* const* args) { expr_ref r(m()); m_rw.mk_add(sz, args, r); track(r); return to_app(r); } app* mk_add(expr* t, expr* s) { expr_ref r(m()); expr* args[2] = { t, s}; m_rw.mk_add(2, args, r); track(r); return to_app(r); } app* mk_add(expr* t, expr* s, expr* u) { return mk_add(t, mk_add(s, u)); } app* mk_mul(expr* t, expr* s) { expr_ref r(m()); expr* args[2] = { t, s}; m_rw.mk_mul(2, args, r); track(r); return to_app(r); } app* mk_sub(expr* t, expr* s) { expr_ref r(m()); expr* args[2] = { t, s}; m_rw.mk_sub(2, args, r); track(r); return to_app(r); } app* mk_mul(expr* t, expr* s, expr* u) { return mk_mul(t, mk_mul(s, u)); } app* mk_and(unsigned num_args, expr* const* args) { expr_ref r(m()); m_bs.mk_and(num_args, args, r); track(r); return to_app(r); } app* mk_and(expr* a, expr* b) { expr* args[2] = { a, b }; return mk_and(2, args); } app* mk_or(unsigned num_args, expr* const* args) { expr_ref r(m()); m_bs.mk_or(num_args, args, r); track(r); return to_app(r); } app* mk_or(expr* a, expr* b) { expr* args[2] = { a, b }; return mk_or(2, args); } void display_branching( std::ostream& out, app* x, branch_conditions const& bc) const { out << mk_pp(x, m()) << ":\n"; for (unsigned i = 0; i < bc.preds().size(); ++i) { out << "Pred: " << mk_pp(bc.preds()[i], m()) << "\n"; } for (unsigned i = 0; i < bc.branches().size(); ++i) { out << "Branch:\n" << mk_pp(bc.branches()[i], m()) << "\n"; for (unsigned j = 0; j < bc.subst()[i].size(); ++j) { out << mk_pp(bc.preds()[j], m()) << " |-> " << mk_pp(bc.subst(i)[j], m()) << "\n"; } out << "Def: " << mk_pp(bc.def(i), m()) << "\n"; } } struct abc_poly { app_ref m_a; app_ref m_b; app_ref m_c; abc_poly(imp& I, app* a, app* b, app* c): m_a(a, I.m()), m_b(b, I.m()), m_c(c, I.m()) {} }; struct sqrt_form { app_ref m_a; int m_b; app_ref m_c; app_ref m_d; sqrt_form(imp& I, app* a, int b, app* c, app* d) : m_a(a, I.m()), m_b(b), m_c(c, I.m()), m_d(d, I.m()) { SASSERT(d != I.z()); } void display(std::ostream& out) const { ast_manager& m = m_a.get_manager(); out << "(/ (+ " << mk_pp(m_a, m) << " (* " << m_b << " (sqrt " << mk_pp(m_c, m) << "))) " << mk_pp(m_d, m) << ")"; } }; expr* mk_abs(expr* e) { return m().mk_ite(mk_lt(e), mk_uminus(e), e); } // // result = (a + b*sqrt(c))/d // expr* to_expr(sqrt_form const& s) { arith_util& A = a(); expr* result; // result = (a + b*sqrt(c))/d if (s.m_c == z() || s.m_b == 0) { result = A.mk_div(s.m_a, s.m_d); } else { expr* half = A.mk_numeral(rational(1,2), false); result = A.mk_div(mk_add(s.m_a, mk_mul(num(s.m_b), A.mk_power(mk_abs(s.m_c), half))), s.m_d); } return result; } // // // Given p(x): ax^2 + bx + c < 0 // then the derivative is d p(x)/dx = 2ax + b // cases: // 1. a != 0, b != 0: // zero: (- b +- sqrt(b^2 - 4ac))/ 2a // then slope of x at zero is: // 2a*zero + b = +- sqrt(..), // so the slope is given by the sign of the solution. // // return zero + epsilon * (if sign > 0 then -1 else 1) // // 2. a = 0, b != 0: // zero : -c/b // slope is b. // return -c/b + epsilon * (if b > 0 then -1 else 1) // // Given p(x): ax^2 + bx + c <= 0, ax^2 + bx + c = 0, // use epsilon = 0. // Given p(x): ax^2 + bx + c <= 0, ax^2 + bx + c = 0, // use epsilon as in < case. // expr* mk_def(comp cmp, abc_poly const& p, sqrt_form const& s) { expr* result = to_expr(s); if (is_strict(cmp)) { if (p.m_a == z()) { result = mk_add(result, mk_mul(mk_epsilon(), m().mk_ite(mk_lt(p.m_b),num(1),num(-1)))); } else { if (s.m_b > 0) { result = mk_add(result, mk_mul(num(-1),mk_epsilon())); } else { result = mk_add(result, mk_epsilon()); } } } return result; } // // TBD: Compute an espilon based on the terms // used in the constraints. // expr* mk_epsilon() { return a().mk_numeral(rational(1,10000), false); } // // TBD: Compute an inf based on the terms // used in the constraints. Eg., use a symbolic // constant for epsilon and inf and then solve for // it postiori. // expr* mk_inf() { return a().mk_numeral(rational(-10000), false); } // lower bounds for each case: // a*x^2 + b*x + c + eps = 0 & a = 0 & b = 0 => x < 0 // a*x^2 + b*x + c + eps = 0 & a = 0 & b != 0 => x < - (c / b) < - (c^2 +1) * (1 + 1/b^2) // a*x^2 + b*x + c + eps = 0 & a != 0 => x < (-|b| - sqrt(b^2 - 4ac))/2|a| < - (b^2*(1 + 1/a^2) + (c^2+1)) app* sq(expr* e) { return mk_mul(e,e); } app* sq1(expr * e) { return mk_add(num(1), sq(e)); } app* inv(expr * e) { return a().mk_div(num(1), e); } expr* mk_inf(branch_conditions const& bc) { return mk_inf(); #if 0 if (bc.size() > 0) { // pick a number lower than the symbolic lower bounds. for(unsigned i = 0; i < bc.size(); ++i) { expr * a = bc.a(i); expr * b = bc.b(i); expr * c = bc.c(i); expr * e = m().mk_ite( mk_eq(a), m().mk_ite( mk_eq(b), num(0), mk_mul(mk_add(sq(c),num(1)), sq1(inv(b)))), mk_add(mk_mul(sq(b),sq1(inv(a))), sq1(c))); r = mk_add(e, r); } return mk_uminus(r); } #endif } void inf_branch( polys const& polys, comps const& comps, branch_conditions& bc) { // /\_j p_j -> p_j[-oo / x] app_ref t1(m()); expr_ref_vector es(m()), subst(m()); for (unsigned j = 0; j < polys.size(); ++j) { minus_inf_subst sub(*this); apply_subst(sub, comps[j], polys[j], t1); es.push_back(m().mk_implies(bc.preds(j), t1)); subst.push_back(t1); TRACE("nlarith_verbose", display(tout << "inf", polys[j]); display(tout, comps[j]); tout << " 0 [-oo] --> " << mk_pp(t1.get(), m()) << "\n";); } TRACE("nlarith", tout << "inf-branch\n";); bc.add_branch(mk_and(es.size(), es.c_ptr()), m().mk_true(), subst, mk_inf(bc), z(), z(), z()); } void create_branch_l(unsigned j, unsigned i, polys const& polys, comps const& comps, branch_conditions& bc) { comp cmp = comps[i]; poly const& p = polys[i]; if (i == j) cmp = LE; // non-strict to avoid epsilon substitution mode. app* a, *b, *c; get_coefficients(p, a, b, c); app_ref t1(m()); expr_ref a2(m()), d(m()), t2(m()), cond(m()); expr_ref_vector es(m()), subst(m()); if (b != z()) { sqrt_form e0(*this, mk_uminus(c), 0, z(), b); // a_i = 0 /\ b_i != 0 /\ phi[e_i/x] TRACE("nlarith", display(tout << "a_i != 0 & b_i != 0 & hi[e_i / x]", p);tout<<"\n";); scoped_ptr rp = mk_default_expr_replacer(m()); expr_substitution sub(m()); sub.insert(a, z()); rp->set_substitution(&sub); if (a != z()) es.push_back(mk_eq(a)); es.push_back(mk_ne(b)); cond = mk_and(es.size(), es.c_ptr()); es.push_back(bc.preds(i)); for (unsigned k = 0; k < polys.size(); ++k) { mk_subst(cmp, polys[k], comps[k], e0, t1); (*rp)(t1, t2); es.push_back(m().mk_implies(bc.preds(k), t2)); subst.push_back(t1); } bc.add_branch(mk_and(es.size(), es.c_ptr()), cond, subst, mk_def(cmp, abc_poly(*this, z(), b, c), e0), a, b, c); } if (i == j && a != z()) { // a != 0 & phi[-b/(2a)/x] TRACE("nlarith", display(tout << "a != 0 & phi[-b/2a / x]", p);tout<<"\n";); app* a2 = mk_mul(num(2), a); sqrt_form e1(*this, mk_uminus(b), 0, z(), a2); es.reset(); subst.reset(); cond = mk_ne(a); es.push_back(cond); es.push_back(bc.preds(i)); for (unsigned k = 0; k < polys.size(); ++k) { mk_subst(cmp, polys[k], comps[k], e1, t1); es.push_back(m().mk_implies(bc.preds(k), t1)); subst.push_back(t1); } bc.add_branch(mk_and(es.size(), es.c_ptr()), cond, subst, mk_def(cmp, abc_poly(*this, a2, b, z()),e1), a, b, c); } } void create_branch(unsigned i, polys const& polys, comps const& comps, branch_conditions& bc) { comp cmp = comps[i]; poly const& p = polys[i]; app* a, *b, *c; get_coefficients(p, a, b, c); app_ref t1(m()), a2(m()), d(m()); expr_ref cond(m()), t2(m()), branch(m()); expr_ref_vector es(m()), subst(m()); d = mk_sub(mk_mul(b,b), mk_mul(num(4), a, c)); a2 = mk_mul(a, num(2)); TRACE("nlarith", display(tout, p); tout << "\n"; tout << "a:" << mk_pp(a, m()) << "\n"; tout << "b:" << mk_pp(b,m()) << "\n"; tout << "c:" << mk_pp(c,m()) << "\n"; tout << "d:" << mk_pp(d, m()) << "\n";); // p & a = 0 & b != 0 & /\_j p_j -> p_j[e0/x] // p & a != 0 & 0 <= d & /\_j p_j -> (p_j[e1/x] \/ p_j[e2/x]) // or: // p & a = 0 & b != 0 /\_j p_j -> p[e0+eps/x] // p & a != 0 & 0 <= d /\_j p_j -> p[e1+eps/x] \/ p[e2+eps/x] if (b != z()) { sqrt_form e0(*this, mk_uminus(c), 0, z(), b); es.reset(); subst.reset(); scoped_ptr rp = mk_default_expr_replacer(m()); expr_substitution sub(m()); sub.insert(a, z()); rp->set_substitution(&sub); if (a != z()) es.push_back(mk_eq(a)); es.push_back(mk_ne(b)); cond = mk_and(es.size(), es.c_ptr()); es.push_back(bc.preds(i)); for (unsigned j = 0; j < polys.size(); ++j) { mk_subst(cmp, polys[j], comps[j], e0, t1); (*rp)(t1, t2); es.push_back(m().mk_implies(bc.preds(j), t2)); subst.push_back(t2); } branch = mk_and(es.size(), es.c_ptr()); bc.add_branch(branch, cond, subst, mk_def(cmp, abc_poly(*this, z(), b, c), e0), a, b, c); } if (a != z()) { sqrt_form e1(*this, mk_uminus(b), 1, d, a2); sqrt_form e2(*this, mk_uminus(b), -1, d, a2); es.reset(); subst.reset(); es.push_back(mk_ne(a)); es.push_back(mk_ge(d)); cond = mk_and(es.size(), es.c_ptr()); es.push_back(bc.preds(i)); for (unsigned j = 0; j < polys.size(); ++j) { mk_subst(cmp, polys[j], comps[j], e1, t1); es.push_back(m().mk_implies(bc.preds(j), t1)); subst.push_back(t1); } branch = mk_and(es.size(), es.c_ptr()); bc.add_branch(branch, cond, subst, mk_def(cmp, abc_poly(*this, a, b, c), e1), a, b, c); TRACE("nlarith", tout << mk_pp(branch,m()) << "\n";); TRACE("nlarith", tout << "0 <= " << mk_pp(d,m()) << "\n"; tout << mk_pp(mk_ge(d), m()) << "\n";); es.resize(3); subst.reset(); for (unsigned j = 0; j < polys.size(); ++j) { mk_subst(cmp, polys[j], comps[j], e2, t1); es.push_back(m().mk_implies(bc.preds(j), t1)); subst.push_back(t1); } branch = mk_and(es.size(), es.c_ptr()); bc.add_branch(branch, cond, subst, mk_def(cmp, abc_poly(*this, a, b, c), e2), a, b, c); TRACE("nlarith", tout << mk_pp(branch,m()) << "\n";); } } bool is_strict(comp c) const { return c == LT || c == NE; } void mk_subst(comp c1, poly const& p, comp c, sqrt_form const& e, app_ref& r) { sqrt_subst sub(*this, e); if (is_strict(c1)) { plus_eps_subst sub2(*this, sub); apply_subst(sub2, c, p, r); } else { apply_subst(sub, c, p, r); } TRACE("nlarith_verbose", display(tout, p); display(tout, c); e.display(tout << " 0 "); tout << " --> " << mk_pp(r.get(), m()) << "\n";); } void get_coefficients(poly const& p, app*& a, app*& b, app*& c) { a = b = c = z(); if (p.size() > 0) c = p[0]; if (p.size() > 1) b = p[1]; if (p.size() > 2) a = p[2]; SASSERT(p.size() <= 3); } bool is_le(expr* e, expr*& s, expr*& t) { return a().is_ge(e, t, s) || a().is_le(e, s, t); } bool is_lt(expr* e, expr*& s, expr*& t) { return a().is_gt(e, t, s) || a().is_lt(e, s, t); } bool is_degree_two_plus(polys const& ps) { for (unsigned i = 0; i < ps.size(); ++i) { if (ps[i].size() > 3) { TRACE("nlarith", tout << "not second-degree: "; display(tout, ps[i]); tout <<"\n"; ); return true; } } return false; } bool is_linear(polys& ps) const { rational n; for (unsigned i = 0; i < ps.size(); ++i) { if (ps[i].size() > 2) return false; if (ps[i].size() == 2) { if (is_numeral(ps[i][1].get(), n)) { ps[i][1] = m_arith.mk_numeral(n, false); } else { return false; } } } return true; } bool has_single_degree2(polys const& ps, comps const& comps, unsigned& idx) const { unsigned n = 0; for (unsigned i = 0; i < ps.size(); ++i) { if (ps[i].size() == 3) { ++n; idx = i; if (comps[i] == EQ) { return false; } } } return n == 1; } /** \brief Create branch conditions for each atomic formulI. */ bool get_polys(contains_app& contains_x, unsigned num_lits, expr* const* lits, polys& polys, comps& comps, branch_conditions* bc, app_ref_vector* literals) { ast_manager& M = m(); expr* e1, *e2, *e3; app_ref t(M); poly p(M); comp c; for (unsigned i = 0; i < num_lits; ++i) { if (!contains_x(lits[i])) { continue; } // e1 <= e2 if (is_le(lits[i], e1, e2)) { t = mk_sub(e1, e2); c = LE; } // ! (e2 <= e3) <=> e3 < e2 else if (M.is_not(lits[i], e1) && is_le(e1, e2, e3)) { t = mk_sub(e3, e2); c = LT; } // e1 < e2 else if (is_lt(lits[i], e1, e2)) { t = mk_sub(e1, e2); c = LT; } // ! (e2 < e3) <=> e3 <= e2 else if (M.is_not(lits[i], e1) && is_lt(e1, e2, e3)) { t = mk_sub(e3, e2); c = LE; } else if (M.is_eq(lits[i], e1, e2)) { t = mk_sub(e1, e2); c = EQ; } else if (M.is_not(lits[i], e1) && M.is_eq(e1, e2, e3)) { t = mk_sub(e2, e3); c = NE; } else { return false; } if (!get_decomposition(t, contains_x, p)) { return false; } polys.push_back(p); comps.push_back(c); if (bc) { bc->add_pred(lits[i]); } if (literals) { literals->push_back(to_app(lits[i])); } TRACE("nlarith_verbose", tout << mk_pp(lits[i], m()) << " -> "; display(tout, p); tout << "\n"; ); } return true; } void display(std::ostream& out, poly const& p) { out << "("; for (unsigned i = 0; i < p.size(); ++i) { out << i << ": " << mk_pp(p[i], m()); if (i + 1 < p.size()) out << ", "; } out << ")"; } void display(std::ostream& out, polys const& ps) { for (unsigned i = 0; i < ps.size(); ++i) { display(out, ps[i]); out << " "; } } bool is_numeral(expr* t, rational& n) const { if (!is_app(t)) return false; app* e = to_app(t); func_decl* f = e->get_decl(); if (f->get_family_id() != m_arith.get_family_id()) { return false; } rational m; switch(f->get_decl_kind()) { case OP_ADD: #define MK_AOP(_mk_op_) \ SASSERT(e->get_num_args() > 0); \ if (!is_numeral(e->get_arg(0), n)) return false; \ for (unsigned i = 1; i < e->get_num_args(); ++i) { \ if (!is_numeral(e->get_arg(i), m)) return false; \ n = n _mk_op_ m; \ } MK_AOP(+); return true; case OP_MUL: MK_AOP(*); return true; case OP_SUB: MK_AOP(-); return true; case OP_UMINUS: if (!is_numeral(e->get_arg(0), n)) return false; n.neg(); return true; case OP_NUM: return m_arith.is_numeral(e, n); default: return false; } } /** \brief Decompose polynomial into sum of powers of 'x'. p = result[0] + x*result[1] + x*x*result[2] + ... */ bool get_decomposition(expr* t, contains_app& contains_x, poly& result) { result.reset(); if (!is_app(t)) { return false; } app* e = to_app(t); if (!contains_x(e)) { result.push_back(e); return true; } if (contains_x.x() == e) { result.push_back(z()); result.push_back(one()); return true; } func_decl* f = e->get_decl(); if (f->get_family_id() != a().get_family_id()) { return false; } poly r(m()); switch(f->get_decl_kind()) { case OP_ADD: #define MK_OP(_mk_op_) \ SASSERT(e->get_num_args() > 0); \ if (!get_decomposition(e->get_arg(0), contains_x, result)) return false;\ for (unsigned i = 1; i < e->get_num_args(); ++i) { \ if (!get_decomposition(e->get_arg(i), contains_x, r)) return false; \ _mk_op_(result, r); \ } MK_OP(mk_add); return true; case OP_MUL: MK_OP(mk_mul); return true; case OP_SUB: MK_OP(mk_sub); return true; case OP_UMINUS: if(!get_decomposition(e->get_arg(0), contains_x, result)) return false; mk_uminus(result); return true; default: TRACE("nlarith", tout << "Cannot decompose " << mk_pp(f, m()) << "\n" << mk_pp(e, m()) << "\n";); return false; } } void mk_uminus(poly& p) { for (unsigned i = 0; i < p.size(); ++i) { p[i] = mk_uminus(p[i].get()); } } void mk_sub(poly& r, poly const& other) { for (unsigned i = 0; i < r.size() && i < other.size(); ++i) { r[i] = mk_sub(r[i].get(), other[i]); } for (unsigned i = r.size(); i < other.size(); ++i) { r.push_back(mk_uminus(other[i])); } } void mk_add(poly& r, poly const& other) { for (unsigned i = 0; i < r.size() && i < other.size(); ++i) { r[i] = mk_add(r[i].get(), other[i]); } for (unsigned i = r.size(); i < other.size(); ++i) { r.push_back(other[i]); } } // r[0]*o[0] // r[0]*o[1] + r[1]*o[0] // r[0]*o[2] + r[1]*o[1] + r[2]*o[0] // // r[sz-1]*o[sz-2] + r[sz-2]*o[sz-1] // r[sz-1]*o[sz-1] void mk_mul(poly& r, poly const& other) { poly result(m()); for (unsigned i = 0; i + 1 < r.size() + other.size(); ++i) { app_ref t(z(), m()); for (unsigned j = 0; j <= i && j < r.size(); ++j) { unsigned k = i - j; if (k < other.size()) { t = mk_add(t, mk_mul(r[j].get(),other[k])); } } result.push_back(t); } TRACE("nlarith_verbose", display(tout, r); display(tout <<" * ", other); display(tout << " = ", result); tout <<"\n";); r.reset(); r.append(result.size(), result.c_ptr()); } void mk_mul(poly& p, expr* e) { for (unsigned i = 0; i < p.size(); ++i) { p[i] = mk_mul(p[i].get(), e); } } void mk_add(poly& p, unsigned shift, expr* e) { while (p.size() <= shift) { p.push_back(z()); } p[shift] = mk_add(p[shift].get(), e); } /** \brief Symbolic differentiation with respect to 'x'. result = [p[1], 2*p[2], 3*p[3],..,(num_terms-1)*p[num_terms-1]] */ void mk_differentiate(poly const& p, app_ref_vector& result) { for (unsigned i = 1; i < p.size(); ++i) { result.push_back(mk_mul(num(i), p[i])); } } class isubst { protected: imp& m_imp; public: isubst(imp& i) : m_imp(i) {} virtual void mk_lt(poly const& p, app_ref& r) = 0; virtual void mk_eq(poly const& p, app_ref& r) = 0; virtual void mk_le(poly const& p, app_ref& r) { imp& I = m_imp; app_ref r1(I.m()), r2(I.m()); mk_lt(p, r1); mk_eq(p, r2); r = I.mk_or(r1, r2); } virtual void mk_ne(poly const& p, app_ref& r) { imp& I = m_imp; mk_eq(p, r); r = I.m().mk_not(r); } }; void apply_subst(isubst& sub, comp c, poly const& p, app_ref& r) { switch(c) { case EQ: sub.mk_eq(p, r); return; case LE: sub.mk_le(p, r); return; case LT: sub.mk_lt(p, r); return; case NE: sub.mk_ne(p, r); return; } } class basic_subst : public isubst { app* m_x; public: basic_subst(imp& i, app* x) : isubst(i), m_x(x) {} virtual void mk_lt(poly const& p, app_ref& r) { imp& I = m_imp; app_ref result(I.m()); I.mk_polynomial(m_x, p, result); r = I.mk_lt(result); } virtual void mk_eq(poly const& p, app_ref& r) { imp& I = m_imp; app_ref result(I.m()); I.mk_polynomial(m_x, p, result); r = I.mk_eq(result); } }; class sqrt_subst : public isubst { sqrt_form const& m_s; public: sqrt_subst(imp& i, sqrt_form const& s): isubst(i), m_s(s) {} // p[e/x] < 0: (a*parity(d) < 0 /\ 0 < a*a - b*b*c) \/ // (b*parity(d) <= 0 /\ (a*parity(d) < 0 \/ a*a - b*b*c < 0)) virtual void mk_lt(poly const& p, app_ref& r) { imp& I = m_imp; ast_manager& m = I.m(); app_ref a(m), b(m), c(m_s.m_c), d(m); I.mk_instantiate(p, m_s, a, b, d); app_ref ad(a, m), bd(b, m), aabbc(m); if (is_even(p.size())) { ad = I.mk_mul(a, d); bd = I.mk_mul(b, d); } if (m_s.m_b == 0) { r = I.mk_lt(ad); } else { aabbc = I.mk_sub(I.mk_mul(a,a), I.mk_mul(b,b,c)); r = I.mk_or(I.mk_and(I.mk_lt(ad), I.mk_gt(aabbc)), I.mk_and(I.mk_le(bd), I.mk_or(I.mk_lt(ad), I.mk_lt(aabbc)))); } } // p[e/x] = 0: a*b <= 0 & a*a - b*b*c = 0 virtual void mk_eq(poly const& p, app_ref& r) { imp& I = m_imp; ast_manager& m = I.m(); app_ref a(m), b(m), c(m_s.m_c),d(m), aabbc(m); I.mk_instantiate(p, m_s, a, b, d); if (m_s.m_b == 0) { r = I.mk_eq(a); } else { aabbc = I.mk_sub(I.mk_mul(a, a), I.mk_mul(b, b, c)); r = I.mk_and(I.mk_le(I.mk_mul(a, b)), I.mk_eq(aabbc)); } } // p[e/x] <= 0: a*parity(d) <= 0 /\ 0 <= a*a - b*b*c \/ b*parity(d) <= 0 /\ a*a - b*b*c <= 0 virtual void mk_le(poly const& p, app_ref& r) { imp& I = m_imp; ast_manager& m = I.m(); app_ref a(m), b(m), c(m_s.m_c), d(m); I.mk_instantiate(p, m_s, a, b, d); app_ref ad(a, m), bd(b, m), aabbc(m); if (is_even(p.size())) { ad = I.mk_mul(a, d); bd = I.mk_mul(b, d); } if (m_s.m_b == 0) { r = I.mk_le(ad); } else { aabbc = I.mk_sub(I.mk_mul(a, a), I.mk_mul(b, b, c)); r = I.mk_or(I.mk_and(I.mk_le(ad), I.mk_ge(aabbc)), I.mk_and(I.mk_le(bd), I.mk_le(aabbc))); } } }; class plus_eps_subst : public isubst { isubst& m_s; /** \brief compute nu(p): nu(p) = p < 0 if degree(x) = 0 nu(p) = p < 0 \/ (p = 0 /\ nu(p')) Then p(x+epsilon) < 0 iff nu(p(x)) */ void mk_nu(poly const& p, app_ref& r) { imp& I = m_imp; ast_manager& m = I.m(); app_ref_vector t1(m); app_ref t3(m), t4(m); m_s.mk_lt(p, r); if (p.size() > 1) { m_s.mk_eq(p, t3); I.mk_differentiate(p, t1); mk_nu(t1, t4); // p < 0 \/ (p = 0 /\ nu(p')) r = I.mk_or(r, I.mk_and(t3, t4)); } } public: plus_eps_subst(imp& i, isubst& s) : isubst(i), m_s(s) {} virtual void mk_lt(poly const& p, app_ref& r) { mk_nu(p, r); } // /\ p[i] = 0 virtual void mk_eq(poly const& p, app_ref& r) { r = m_imp.mk_zero(p); } }; class minus_eps_subst : public isubst { isubst& m_s; /** \brief compute nu(p): nu(p, t) = p < 0 if degree(x) = 0 nu(p, f) = p > 0 if degree(x) = 0 nu(p, t) = p < 0 \/ (p = 0 /\ nu(p', f)) nu(p, f) = p > 0 \/ (p = 0 /\ nu(p', t)) Then p(x-epsilon) < 0 iff nu(p(x), t) */ void mk_lt(poly const& p, bool even, app_ref& r) { imp& I = m_imp; if (even) { m_s.mk_lt(p, r); } else { poly p1(p); I.mk_uminus(p1); m_s.mk_lt(p1, r); } } void mk_nu(poly const& p, bool even, app_ref& r) { imp& I = m_imp; ast_manager& m = I.m(); app_ref_vector t1(m); app_ref t3(m), t4(m); mk_lt(p, even, r); if (p.size() > 1) { m_s.mk_eq(p, t3); I.mk_differentiate(p, t1); mk_nu(t1, !even, t4); // p < 0 \/ (p = 0 /\ nu(p')) r = I.mk_or(r, I.mk_and(t3, t4)); } } public: minus_eps_subst(imp& i, isubst& s) : isubst(i), m_s(s) {} virtual void mk_lt(poly const& p, app_ref& r) { mk_nu(p, true, r); } // /\ p[i] = 0 virtual void mk_eq(poly const& p, app_ref& r) { r = m_imp.mk_zero(p); } }; class minus_inf_subst : public isubst { /** \brief compute mu(p) given by. p = p[0] + x*p[1] + x*x*p[2] + ... mu(p) = p[num_terms-1]*(-1)^(parity num_terms-1) < 0 \/ p[num_terms-1] = 0 /\ mu(num_terms-1, terms) */ app* mk_lt(poly const& p, unsigned i) { imp& I = m_imp; ast_manager& m = I.m(); if (i == 0) { return m.mk_false(); } --i; expr* t = p[i]; app* e = is_even(i)?I.mk_lt(t):I.mk_gt(t); if (i == 0) { return e; } else { return I.mk_or(e, I.mk_and(I.mk_eq(t), mk_lt(p, i))); } } public: minus_inf_subst(imp& i) : isubst(i) {} virtual void mk_lt(poly const& p, app_ref& r) { r = mk_lt(p, p.size()); } // /\ p[i] = 0 virtual void mk_eq(poly const& p, app_ref& r) { r = m_imp.mk_zero(p); } }; class plus_inf_subst : public isubst { app* mk_lt(poly const& p, unsigned i) { imp& I = m_imp; ast_manager& m = I.m(); if (i == 0) { return m.mk_false(); } --i; expr* t = p[i]; app* e = I.mk_lt(t); if (i == 0) { return e; } else { return I.mk_or(e, I.mk_and(I.mk_eq(t), mk_lt(p, i))); } } public: plus_inf_subst(imp& i) : isubst(i) {} virtual void mk_lt(poly const& p, app_ref& r) { r = mk_lt(p, p.size()); } // /\ p[i] = 0 virtual void mk_eq(poly const& p, app_ref& r) { r = m_imp.mk_zero(p); } }; /** \brief create polynomail expression. result = p[0] + x*p[1] + x*x*p[2] + ... */ void mk_polynomial(app* x, poly const& p, app_ref& result) { if (p.empty()) { result = z(); return; } app_ref xx(x, m()); expr_ref_vector tmp(m()); tmp.push_back(p[0]); for (unsigned i = 1; i < p.size(); ++i) { tmp.push_back(mk_mul(xx.get(), p[i])); xx = mk_mul(x, xx.get()); } result = mk_add(tmp.size(), tmp.c_ptr()); } app* mk_zero(poly const& p) { app_ref_vector tmp(m()); mk_zero(p, tmp); return mk_and(tmp.size(), reinterpret_cast(tmp.c_ptr())); } void mk_zero(poly const& p, app_ref_vector& zeros) { for (unsigned i = 0; i < p.size(); ++i) { zeros.push_back(mk_eq(p[i])); } } /** \brief Formal replacement of x by (a + b*sqrt(c))/d in p. where: p = p[0] + x*p[1] + x*x*p[2] + ... The result is an expression (a' + b'*sqrt(c))/d' */ void mk_instantiate(poly const& p, sqrt_form const& s, app_ref& ar, app_ref& br, app_ref& dr) { app* a = s.m_a, *c = s.m_c, *d = s.m_d; app_ref b(num(s.m_b), m()); br = z(); dr = one(); if (p.empty()) { ar = z(); return; } unsigned i = p.size() - 1; ar = p[i]; while (i > 0) { --i; // compute // p[i] + x * (ar + br*sqrt(c))/dr // = // p[i] + (a + b*sqrt(c))/d * (ar + br*sqrt(c))/dr // = // p[i] + (a*ar + b*br*c + (a*br + ar*b)*sqrt(c))/d*dr // = // (d*dr*p[i] + a*ar + b*br*c + (a*br + ar*b)*sqrt(c))/d*dr // app_ref tmp1(mk_add(mk_mul(d, dr, p[i]), mk_mul(a, ar), mk_mul(b, br, c)), m()); br = mk_add(mk_mul(a, br), mk_mul(ar, b)); dr = mk_mul(d, dr); ar = tmp1; } TRACE("nlarith_verbose", display(tout, p); s.display(tout << " "); tout << " " << mk_pp(ar, m()) << " " << mk_pp(br, m()) << " " << mk_pp(dr, m()) << "\n";); } static bool is_even(unsigned n) { return 0 == (n&0x1); } bool is_variable(app* e) { return a().is_real(e) && e->get_family_id() == null_family_id && e->get_num_args() == 0; } bool is_arithmetical(app* e) { if (e->get_family_id() == m().get_basic_family_id()) { return true; } if (e->get_family_id() == a().get_family_id()) { return true; } return false; } bool is_nonlinear(app* e) { if (a().is_mul(e)) { unsigned n = 0; for (unsigned i = 0; n < 2 && i < e->get_num_args(); ++i) { if (!a().is_numeral(e->get_arg(i))) { ++n; } } return n == 2; } return false; } private: // u = v*q + r void quot_rem(poly const& u, poly const& v, poly& q, poly& r, app_ref& lc, unsigned& power) { lc = v.empty()?num(0):v[v.size()-1]; power = 0; if (u.size() < v.size() || v.size() == 0) { q.reset(); r.reset(); r.append(u); return; } SASSERT(u.size() >= v.size() && v.size() > 0); unsigned n = v.size()-1; if (a().is_numeral(v[n])) { numeric_quot_rem(u, v, q, r); } else { pseudo_quot_rem(u, v, q, r, power); } } // // Compute q and r such that // u = v*q + r, // assuming the leading coefficient of v is a numeral. // void numeric_quot_rem(poly const& u, poly const& v, poly& q, poly& r) { SASSERT(u.size() > 0 && v.size() > 0); unsigned m = u.size()-1, n = v.size()-1; q.reset(); r.reset(); r.append(u); rational v_n; VERIFY(a().is_numeral(v[n], v_n)); app_ref v_inv(a().mk_numeral(rational(1)/v_n, false), m_manager); bool is_one = v_n.is_one(); for (int k = m-n+1; k > 0; ) { --k; if (is_one) { q[k] = u[n+k]; } else { q[k] = mk_mul(u[n+k], v_inv.get()); } for (int j = n + k - 1; j >= k; --j) { r[j] = mk_sub(r[j].get(), mk_mul(q[k].get(), v[j-k])); } } SASSERT(test_quot_rem(u, v, q, r)); } // // Compute q and r such that // lc(v)^{m-n+1}*u = v*q + r, // where lc(v) is the leading coefficient of v // of degree 'n' and the most significant coefficient // in u has degree 'm'. // void pseudo_quot_rem(poly const& u, poly const& v, poly& q, poly& r, unsigned& power) { SASSERT(u.size() > 0 && v.size() > 0); unsigned m = u.size()-1, n = v.size()-1; app* v_n = v[n]; power = m- n + 1; q.reset(); r.reset(); r.append(u); q.resize(m-n+1); poly powers_v(m_manager); powers_v.push_back(num(1)); for (unsigned i = 1; i < m-n+2; ++i) { powers_v[i] = mk_mul(powers_v[i-1].get(), v_n); } for (int k = m-n+1; k > 0; ) { --k; q[k] = mk_mul(u[n+k], powers_v[k].get()); for (int j = n + k; j > 0; ) { --j; r[j] = mk_mul(v_n, r[j].get()); // n + k != j if (j >= k) { r[j] = mk_sub(r[j].get(), mk_mul(r[n+k].get(), v[j-k])); } } } DEBUG_CODE( poly u1(u); mk_mul(u1, powers_v[m-n+1].get()); SASSERT(test_quot_rem(u1, v, q, r)); ); } // validate: u = q*v + r bool test_quot_rem(poly const& u, poly const& v, poly const& q, poly const& r) { poly u1(u), q1(q); mk_mul(q1, v); mk_add(q1, r); mk_sub(q1, u); for (unsigned i = 0; i < q1.size(); ++i) { if (z() != q1[i].get()) { TRACE("nlarith", display(tout, q1);); return false; } } return true; } /** \brief create case split predicates for polynomial elimination. */ void mk_derivative(poly& p) { if(p.empty()) { return; } if (p.size() > 1) { p[0] = p[1].get(); for (unsigned i = 1; i + 1 < p.size(); ++i) { p[i] = mk_mul(num(i), p[i+1].get()); } } p.resize(p.size()-1); } void mk_derivative(unsigned k, poly& p) { for (unsigned i = 0; i < k; ++i) { mk_derivative(p); } } void mk_inf_sign(isubst& sub, util::literal_set const& literals, app_ref& fml, app_ref_vector& new_atoms) { new_atoms.reset(); expr_ref_vector equivs(m()); app_ref tmp(m()); for (unsigned i = 0; i < literals.size(); ++i) { if (literals.compare(i) == EQ) { continue; } apply_subst(sub, literals.compare(i), literals.get_poly(i), tmp); equivs.push_back(m().mk_implies(literals.literal(i), tmp)); new_atoms.push_back(tmp); } fml = mk_and(equivs.size(), equivs.c_ptr()); } void mk_plus_inf_sign(util::literal_set const& literals, app_ref& fml, app_ref_vector& new_atoms) { plus_inf_subst sub(*this); mk_inf_sign(sub, literals, fml, new_atoms); } void mk_minus_inf_sign(util::literal_set const& literals, app_ref& fml, app_ref_vector& new_atoms) { minus_inf_subst sub(*this); mk_inf_sign(sub, literals, fml, new_atoms); } // one of the literals is 0 at x_sup and x_inf, other // literals have their derivative close. void mk_bound(util::literal_set& literals, app_ref& fml, app_ref_vector& new_atoms) { new_atoms.reset(); app_ref tmp(m()); expr_ref_vector conjs(m()); mk_exists_zero(literals, true, 0, conjs, new_atoms); mk_same_sign (literals, true, conjs, new_atoms); mk_exists_zero(literals, false, 0, conjs, new_atoms); mk_same_sign (literals, false, conjs, new_atoms); mk_lt(literals.x(), literals.x_inf(), conjs, new_atoms); mk_lt(literals.x_sup(), literals.x(), conjs, new_atoms); fml = mk_and(conjs.size(), conjs.c_ptr()); } void mk_lt(app* x, app* y, expr_ref_vector& conjs, app_ref_vector& new_atoms) { app* atm = mk_lt(mk_sub(x,y)); new_atoms.push_back(atm); conjs.push_back(atm); } void mk_exists_zero(util::literal_set& literals, bool is_sup, poly const* p1, expr_ref_vector& conjs, app_ref_vector& new_atoms) { app* x = is_sup?literals.x_sup():literals.x_inf(); expr_ref_vector ors(m()); app_ref fml(m()); basic_subst sub(*this, x); for (unsigned i = 0; i < literals.size(); ++i) { if (literals.compare(i) == EQ) { continue; } apply_subst(sub, EQ, literals.get_poly(i), fml); new_atoms.push_back(fml); ors.push_back(fml); } if (p1) { apply_subst(sub, EQ, *p1, fml); new_atoms.push_back(fml); ors.push_back(fml); } conjs.push_back(mk_or(ors.size(), ors.c_ptr())); } /* z < x < y: z is sup, y is inf /\_j (p_j(x) < 0 -> p_j(z) < 0 \/ p_j(z) = 0 /\ p'_j(z) < 0) /\ /\_j (p_j(x) < 0 -> p_j(y) < 0 \/ p'_j(y) = 0 /\ -p'_j(y) < 0) */ void mk_same_sign(util::literal_set& literals, bool is_sup, expr_ref_vector& conjs, app_ref_vector& new_atoms) { app* x = is_sup?literals.x_sup():literals.x_inf(); app_ref fml(m()); for (unsigned i = 0; i < literals.size(); ++i) { switch(literals.compare(i)) { case EQ: break; case LT: mk_same_sign( x, is_sup, literals.get_poly(i), literals.literal(i), fml, new_atoms); conjs.push_back(fml); break; default: UNREACHABLE(); break; } } } void mk_same_sign(app* x, bool is_sup, poly const& p, app* l, app_ref& fml, app_ref_vector& new_atoms) { basic_subst sub0(*this, x); if (is_sup) { plus_eps_subst sub(*this, sub0); apply_subst(sub, LT, p, fml); } else { minus_eps_subst sub(*this, sub0); apply_subst(sub, LT, p, fml); } collect_atoms(fml, new_atoms); fml = m().mk_implies(l, fml); } void collect_atoms(app* fml, app_ref_vector& atoms) { ptr_vector todo; todo.push_back(fml); while (!todo.empty()) { fml = todo.back(); todo.pop_back(); if (m().is_and(fml) || m().is_or(fml)) { unsigned sz = fml->get_num_args(); for (unsigned i = 0; i < sz; ++i) { todo.push_back(to_app(fml->get_arg(i))); } } else { atoms.push_back(fml); } } } class simple_branch : public util::branch { app_ref m_cnstr; app_ref_vector m_atoms; svector m_updates; public: simple_branch(ast_manager& m, app* cnstr): m_cnstr(cnstr, m), m_atoms(m) {} virtual ~simple_branch() {} virtual app* get_constraint() { return m_cnstr.get(); } virtual void get_updates(ptr_vector& atoms, svector& updates) { for (unsigned i = 0; i < m_atoms.size(); ++i) { atoms.push_back(m_atoms[i].get()); updates.push_back(m_updates[i]); } } void update(app* a, util::atom_update u) { m_atoms.push_back(a); m_updates.push_back(u); } void insert(app* a) { update(a, util::INSERT); } void remove(app* a) { update(a, util::REMOVE); } }; class ins_rem_branch : public simple_branch { public: ins_rem_branch(ast_manager& m, app* a, app* r, app* cnstr): simple_branch(m, cnstr) { insert(a); remove(r); } virtual ~ins_rem_branch() {} }; /** \brief Compute branches given u = 0 & v = 0. u has degree m, v has degree n. m >= n 1. u = 0 & lc(v) = 0 & v' = 0 remove v = 0, add v' = 0 2. let q, r be such that, m >= n lc(v)^{m-n+1}*u = v*q + r then v = 0 & r = 0 remove u = 0, add r = 0 */ void get_sign_branches_eq(util::literal_set& lits, unsigned i, unsigned j, ptr_vector& branches) { SASSERT(lits.compare(i) == EQ); SASSERT(lits.compare(j) == EQ); poly const* u = &lits.get_poly(i); poly const* v = &lits.get_poly(j); app* l0 = lits.literal(i); app* l1 = lits.literal(j); if (u->size() < v->size()) { std::swap(u, v); std::swap(l0, l1); } app_ref lc_v0(m()), v2_eq(m()), r_eq(m()), lc(m()); poly v2(m()), q(m()), r(m()); unsigned n = v->size()-1; basic_subst sub(*this, lits.x()); unsigned power; v2.set(*v); v2.resize(n); quot_rem(*u, *v, q, r, lc_v0, power); lc_v0 = mk_eq(lc); sub.mk_eq(v2, v2_eq); sub.mk_eq(r, r_eq); branches.push_back(alloc(ins_rem_branch, m(), v2_eq, l1, mk_and(lc_v0, v2_eq))); branches.push_back(alloc(ins_rem_branch, m(), r_eq, l0, r_eq)); // TBD: add constraints that coefficients to l0 are non-zero? branches.push_back(alloc(simple_branch, m(), m().mk_not(l0))); branches.push_back(alloc(simple_branch, m(), m().mk_not(l1))); } /** \brief Compute branch where all predicates are non-zero. p_infty \/ p_minus_infty \/ mk_bound where mk_bound = z < x < y /\ (\/_j p_j(z) = 0) /\_j (p_j(x) < 0 -> p_j(z) < 0 \/ p_j(z) = 0 /\ p'_j(z) < 0) /\ (\/_j p_j(y) = 0) /\_j (p_j(x) < 0 -> p_j(y) < 0 \/ p'_j(y) = 0 /\ -p'_j(z) < 0) p_j ranges over predicates 'p_j(x) < 0' */ void get_sign_branches_neq(util::literal_set& lits, ptr_vector& branches) { app_ref_vector new_atoms(m()); app_ref fml(m()); branches.push_back(mk_inf_branch(lits, true)); branches.push_back(mk_inf_branch(lits, false)); mk_bound(lits, fml, new_atoms); simple_branch* br = alloc(simple_branch, m(), fml); swap_atoms(br, lits.lits(), new_atoms); branches.push_back(br); } util::branch* mk_inf_branch(util::literal_set& literals, bool is_pos) { app_ref fml(m()); app_ref_vector new_atoms(m()); if (is_pos) { mk_plus_inf_sign(literals, fml, new_atoms); } else { mk_minus_inf_sign(literals, fml, new_atoms); } simple_branch* br = alloc(simple_branch, m(), fml); swap_atoms(br, literals.lits(), new_atoms); return br; } void swap_atoms(simple_branch* br, app_ref_vector const& old_atoms, app_ref_vector const& new_atoms) { for (unsigned i = 0; i < old_atoms.size(); ++i) { br->remove(old_atoms[i]); } for (unsigned i = 0; i < new_atoms.size(); ++i) { br->insert(new_atoms[i]); } } /** \brief Compute branches where one equality holds. p != 0 \/ lc(p) = 0 \/ p' = 0 \/ p_j(x) < 0 -> p_j(infty) < 0 \/ p_j(x) < 0 -> p_j(-infty) < 0 \/ p(z) < 0 < p(y) /\ p'(x) > 0 /\ m_bound(-p') \/ p(y) < 0 < p(z) /\ p'(x) < 0 /\ m_bound(p') where mk_bound(q) = z < x < y /\ /\_j p_j(x) < 0 -> r_j(x) < 0 (\/_j r_j(z) = 0) /\_j (p_j(x) < 0 -> r_j(x) < 0 /\ r_j(z-epsilon) < 0 (\/_j r_j(y) = 0) /\_j (p_j(x) < 0 -> r_j(x) < 0 /\ r_j(y+epsilon) < 0 p_j ranges over predicates 'p_j(x) < 0', q(x) < 0 r_j is obtained by quot_rem(p, p_j, q_j, r_j) z < x < y: z is sup, y is inf */ void get_sign_branches_eq_neq(util::literal_set& lits, unsigned i, ptr_vector& branches) { SASSERT(lits.size() > i); SASSERT(lits.compare(i) == EQ); poly const& p = lits.get_poly(i); poly p1(m()); mk_differentiate(p, p1); app_ref eq(m()), lc_p0(m()), l1(m()); basic_subst sub_x(*this, lits.x()); apply_subst(sub_x, EQ, p1, eq); lc_p0 = mk_eq(p[p.size()-1]); poly p2(p); p2.resize(p.size()-1); apply_subst(sub_x, EQ, p2, l1); branches.push_back(alloc(simple_branch, m(), m().mk_not(lits.literal(i)))); branches.push_back(alloc(simple_branch, m(), eq)); branches.push_back(alloc(ins_rem_branch, m(), l1, lits.literal(i), lc_p0)); branches.push_back(mk_inf_branch(lits, true)); branches.push_back(mk_inf_branch(lits, false)); branches.push_back(mk_bound_ext(lits, p, p1, lits.x())); } simple_branch* mk_bound_ext(util::literal_set& lits, poly const& p, poly const& p1, app* x) { // // Assuming p(x) = 0, p'(x) != 0, lc(p) != 0 // x < y < z // (p'(x) < 0 & p(y) < 0 < p(z) | p'(x) > 0 & p(y) > 0 > p(z)) // \/ p'(y) = 0 \/_j p_j(y) = 0 // \/ p'(z) = 0 \/_j p_j(z) = 0 // /\_j p_j(x) < 0 -> sign_adjust(lc, parity, r_j(y+epsilon)) // /\_j p_j(x) < 0 -> sign_adjust(lc, parity, r_j(z-epsilon)) // p'(x) < 0 -> r(y+epsilon) < 0 & r(z-epsilon) < 0 // p'(x) > 0 -> r(y+epsilon) > 0 & r(z-epsilon) > 0 // sign_adjust(lc, even, r) = r < 0 // sign_adjust(lc, odd, r) = (lc > 0 -> r < 0) & (lc < 0 -> r > 0) // app_ref eq(m()), fml(m()), l1(m()), l2(m()), l3(m()); app_ref p1_lt0(m()), p1_gt0(m()); app_ref_vector new_atoms(m()); expr_ref_vector conjs(m()); poly p_m(p), p1_m(p1); mk_uminus(p_m); mk_uminus(p1_m); mk_lt(lits.x(), lits.x_inf(), conjs, new_atoms); // y < x < z mk_lt(lits.x_sup(), lits.x(), conjs, new_atoms); basic_subst sub_x(*this, x); basic_subst sub_y(*this, lits.x_sup()); basic_subst sub_z(*this, lits.x_inf()); apply_subst(sub_y, LT, p, l1); // p(y) < 0 apply_subst(sub_z, LT, p_m,l2); // 0 < p(z) apply_subst(sub_x, LT, p1_m, p1_gt0); // p1(x) > 0 new_atoms.push_back(l1); new_atoms.push_back(l2); new_atoms.push_back(p1_gt0); conjs.push_back(m().mk_implies(p1_gt0, mk_and(l1, l2))); // p'(x) > 0 -> p(y) < 0 < p(z) apply_subst(sub_y, LT, p_m, l1); // p(y) > 0 apply_subst(sub_z, LT, p, l2); // 0 > p(z) apply_subst(sub_x, LT, p1, p1_lt0); // p1(x) < 0 new_atoms.push_back(l1); new_atoms.push_back(l2); new_atoms.push_back(p1_lt0); conjs.push_back(m().mk_implies(p1_lt0, mk_and(l1, l2))); // p'(x) < 0 -> p(y) > 0 > p(z) conjs.push_back(fml); mk_exists_zero(lits, true, &p1, conjs, new_atoms); // p'(z) = 0 \/_j p_j(z) = 0 mk_exists_zero(lits, false, &p1, conjs, new_atoms); // p'(y) = 0 \/_j p_j(y) = 0 for (unsigned i = 0; i < lits.size(); ++i) { if (lits.compare(i) == LT) { mk_bound_ext(lits.literal(i), lits.get_poly(i), p, lits.x_sup(), lits.x_inf(), conjs, new_atoms); } } // p'(x) < 0 -> r(y+epsilon) < 0 & r(z-epsilon) < 0 // p'(x) > 0 -> r(y+epsilon) > 0 & r(z-epsilon) > 0 mk_bound_ext(p1_lt0, p1, p, lits.x_sup(), lits.x_inf(), conjs, new_atoms); mk_bound_ext(p1_gt0, p1_m, p, lits.x_sup(), lits.x_inf(), conjs, new_atoms); fml = mk_and(conjs.size(), conjs.c_ptr()); simple_branch* br = alloc(simple_branch, m(), fml); swap_atoms(br, lits.lits(), new_atoms); return br; } void mk_bound_ext(app* l_j, poly const& p_j, poly const& p, app* y, app* z, expr_ref_vector& conjs, app_ref_vector& new_atoms) { poly q(m()), r(m()); app_ref eq(m()), fml(m()), l1(m()), l2(m()), l3(m()), l4(m()); // lc(p)^{m-n+1}*p_i = p*q + r app_ref lc(m()), lc_m(m()); basic_subst sub_y(*this, y); basic_subst sub_z(*this, z); unsigned power; quot_rem(p_j, p, q, r, lc, power); poly r_m(r); mk_uminus(r_m); lc_m = mk_uminus(lc); plus_eps_subst sub_ye(*this, sub_y); minus_eps_subst sub_ze(*this, sub_z); // p_j(x) < 0 -> sign_adjust(lc, parity, r_j(y+epsilon)) // p_j(x) < 0 -> sign_adjust(lc, parity, r_j(z-epsilon)) if ((power % 2) == 0) { apply_subst(sub_ye, LT, r, l1); apply_subst(sub_ze, LT, r, l2); fml = mk_and(l1, l2); } else { apply_subst(sub_ye, LT, r, l1); apply_subst(sub_ye, LT, r_m, l2); l1 = m().mk_implies(mk_lt(lc_m), l1); l2 = m().mk_implies(mk_lt(lc), l2); apply_subst(sub_ze, LT, r_m, l3); apply_subst(sub_ze, LT, r_m, l4); l3 = m().mk_implies(mk_lt(lc_m), l3); l4 = m().mk_implies(mk_lt(lc), l4); expr* args[4] = { l1, l2, l3, l4 }; fml = mk_and(4, args); } collect_atoms(fml, new_atoms); fml = m().mk_implies(l_j, fml); conjs.push_back(fml); } public: /** \brief Generate branch formulas depending on the current evaluation of literals. There are 3 cases: 1. Two or more equalities are true 2. Precisely one equality is true 3. No equality is true */ void get_sign_branches(util::literal_set& lits, util::eval& eval, ptr_vector& branches) { m_trail.reset(); unsigned z1 = UINT_MAX, z2 = UINT_MAX; for (unsigned i = 0; i < lits.size(); ++i) { if (lits.compare(i) == EQ && l_true == eval(lits.literal(i))) { if (z1 == UINT_MAX) { z1 = i; } else { SASSERT(z2 == UINT_MAX); z2 = i; break; } } } if (z1 == UINT_MAX) { get_sign_branches_neq(lits, branches); } else if (z2 == UINT_MAX) { get_sign_branches_eq_neq(lits, z1, branches); } else { get_sign_branches_eq(lits, z1, z2, branches); } } bool get_sign_literals(util::atoms const& atoms, util::eval& eval, util::literal_set*& lits) { // TBD: use 'eval' to select non-linear literals that are relevant. m_trail.reset(); ptr_vector nlvars, atms; util::atoms::iterator it = atoms.begin(), end = atoms.end(); for (; it != end; ++it) { atms.push_back(*it); } extract_non_linear(atms.size(), atms.begin(), nlvars); if (nlvars.empty()) { lits = 0; return true; } app* x = nlvars.back(); contains_app contains_x(m(), x); expr* const* _atoms = (expr*const*)atms.begin(); lits = alloc(util::literal_set, m()); lits->set_x(x); if (get_polys(contains_x, atms.size(), _atoms, lits->polys(), lits->comps(), 0, &lits->lits())) { return true; } dealloc(lits); lits = 0; return false; } // Sign matrix algorithm (Cohen-Hormander) public: enum sign { Negative, Zero, NonZero, Positive, Unknown }; typedef svector sign_vector; typedef vector sign_matrix; void mk_sign_matrix(vector const& polys, sign_matrix& result) { } private: // remove points that don't contain Zero void condense(sign_matrix& mat) { unsigned i = 0, j = 0; SASSERT(mat.size() % 2 == 0); for (; i + 1 < mat.size(); i += 2) { if (mat[i+1].contains(Zero)) { if (i != j) { mat[j] = mat[i]; mat[j+1] = mat[i+1]; } j += 2; } } mat.resize(j); } // set sign of p(x) to sign of q_i(x) where p_i(x) = 0 void infer_psign(sign_vector& pqs) { unsigned n = pqs.size()/2; for (unsigned i = 0; i < n; ++i) { if (Zero == pqs[i]) { sign s = pqs[i+n]; pqs.resize(n); cons(s, pqs); return; } } pqs.resize(n); cons(Unknown, pqs); } void cons(sign s, sign_vector& v) { for (unsigned i = 0; i < v.size(); ++i) { std::swap(s, v[i]); } v.push_back(s); } // Deduce matrix for p, p1, .., pn from p', p1, .., pn, q0, .., qn void deduce_matrix(sign_matrix& m) { for (unsigned i = 0; i < m.size(); ++i) { infer_psign(m[i]); } condense(m); } }; util::util(ast_manager& m) { m_imp = alloc(imp, m); } util::~util() { dealloc(m_imp); } bool util::create_branches(app* x, unsigned num_lits, expr* const* lits, branch_conditions& bc) { return m_imp->create_branches(x, num_lits, lits, bc); } void util::set_enable_linear(bool enable_linear) { m_imp->set_enable_linear(enable_linear); } void util::extract_non_linear(expr* e, ptr_vector& nl_vars) { m_imp->extract_non_linear(e, nl_vars); } void util::deallocate(literal_set* lits) { dealloc(lits); } bool util::get_sign_literals(atoms const& atoms, eval& ev, literal_set*& lits) { return m_imp->get_sign_literals(atoms, ev, lits); } void util::get_sign_branches(literal_set& lits, eval& ev, ptr_vector& branches) { m_imp->get_sign_branches(lits, ev, branches); } }; z3-4.4.0/src/qe/qe_cmd.h0000644000175000017500000000051712540347414014445 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: qe_cmd.h Abstract: SMT2 front-end 'qe' command. Author: Nikolaj Bjorner (nbjorner) 2011-01-11 Notes: --*/ #ifndef _QE_CMD_H_ #define _QE_CMD_H_ class cmd_context; void install_qe_cmd(cmd_context & ctx, char const * cmd_name = "elim-quantifiers"); #endif z3-4.4.0/src/qe/vsubst_tactic.cpp0000644000175000017500000001214712540347414016427 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: vsubst_tactic.cpp Abstract: Check satisfiability of QF_NRA problems using virtual subsititution quantifier-elimination. Author: Nikolaj (nbjorner) 2011-05-16 Notes: Ported to tactic framework on 2012-02-28 It was qfnra_vsubst.cpp This goal transformation checks satsifiability of quantifier-free non-linear constraints using virtual substitutions (applies to second-degree polynomials). . identify non-linear variables . use the identified variables as non-linear variables. . give up if there are non-linear variables under uninterpreted scope. give up if there are no non-linear variables. . call quantifier elimination with - non-linear elimination option. - get-first-branch option. . if the first branch is linear, then done. if the result is unsat, then done. if the first branch is non-linear then, check candidate model, perhaps iterate using rewriting or just give up. . helpful facilities: . linearize_rewriter a*a*b + a*b = 0 <=> (b+1) = 0 \/ a = 0 \/ b = 0 . sign analysis: a*a + b*b + c < 0 => c < 0 --*/ #include"tactic.h" #include"qe.h" #include"arith_decl_plugin.h" #include"for_each_expr.h" #include"extension_model_converter.h" #include"ast_smt2_pp.h" class vsubst_tactic : public tactic { params_ref m_params; class get_var_proc { arith_util m_arith; ptr_vector& m_vars; public: get_var_proc(ast_manager & m, ptr_vector& vars) : m_arith(m), m_vars(vars) {} void operator()(expr* e) { if (is_app(e)) { app* a = to_app(e); if (m_arith.is_real(e) && a->get_num_args() == 0 && a->get_family_id() == null_family_id) { m_vars.push_back(a); } } } }; void get_vars(ast_manager & m, expr* fml, ptr_vector& vars) { get_var_proc proc(m, vars); for_each_expr(proc, fml); } void main(goal & s, model_converter_ref & mc, params_ref const & p) { ast_manager & m = s.m(); ptr_vector fs; for (unsigned i = 0; i < s.size(); ++i) { fs.push_back(s.form(i)); } app_ref f(m.mk_and(fs.size(), fs.c_ptr()), m); TRACE("vsubst", s.display(tout); tout << "goal: " << mk_ismt2_pp(f.get(), m) << "\n";); ptr_vector vars; get_vars(m, f.get(), vars); if (vars.empty()) { TRACE("vsubst", tout << "no real variables\n";); throw tactic_exception("there are no real variables"); } smt_params params; params.updt_params(p); params.m_model = false; flet fl1(params.m_nlquant_elim, true); flet fl2(params.m_nl_arith_gb, false); TRACE("quant_elim", tout << "Produce models: " << params.m_model << "\n";); qe::expr_quant_elim_star1 qelim(m, params); expr_ref g(f, m); qe::def_vector defs(m); lbool is_sat = qelim.first_elim(vars.size(), vars.c_ptr(), g, defs); if (is_sat == l_undef) { TRACE("vsubst", tout << mk_ismt2_pp(g, m) << "\n";); throw tactic_exception("elimination was not successful"); } if (!defs.empty()) { extension_model_converter * ev = alloc(extension_model_converter, m); mc = ev; for (unsigned i = defs.size(); i > 0; ) { --i; ev->insert(defs.var(i), defs.def(i)); } } s.reset(); // TBD: wasteful as we already know it is sat or unsat. // TBD: extract model from virtual substitution. s.assert_expr(g); TRACE("qfnra_vsubst", tout << "v-subst result:\n"; s.display(tout);); } public: vsubst_tactic(params_ref const & p):m_params(p) {} virtual tactic * translate(ast_manager & m) { return alloc(vsubst_tactic, m_params); } virtual ~vsubst_tactic() {} virtual void updt_params(params_ref const & p) { m_params = p; } /** \brief Check satisfiability of an assertion set of QF_NRA by using virtual substitutions. */ virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("vsubst", g); fail_if_unsat_core_generation("vsubst", g); fail_if_model_generation("vsubst", g); // disable for now due to problems with infinitesimals. mc = 0; pc = 0; core = 0; result.reset(); main(*(g.get()), mc, m_params); result.push_back(g.get()); SASSERT(g->is_well_sorted()); } virtual void cleanup(void) {} }; tactic * mk_vsubst_tactic(ast_manager & m, params_ref const & p) { return alloc(vsubst_tactic, p); } z3-4.4.0/src/qe/qe_tactic.h0000644000175000017500000000075212540347414015152 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: qe_tactic.h Abstract: Quantifier elimination front-end for tactic framework. Author: Leonardo de Moura (leonardo) 2011-12-28. Revision History: --*/ #ifndef _QE_TACTIC_H_ #define _QE_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_qe_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qe", "apply quantifier elimination.", "mk_qe_tactic(m, p)") */ #endif z3-4.4.0/src/qe/qe_arith.h0000644000175000017500000000077012540347414015012 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifndef __QE_ARITH_H_ #define __QE_ARITH_H_ #include "model.h" namespace qe { /** Loos-Weispfenning model-based projection for a basic conjunction. Lits is a vector of literals. return vector of variables that could not be projected. */ expr_ref arith_project(model& model, app_ref_vector& vars, expr_ref_vector const& lits); expr_ref arith_project(model& model, app_ref_vector& vars, expr* fml); }; #endif z3-4.4.0/src/qe/qe_util.cpp0000644000175000017500000001114112540347414015205 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "qe_util.h" #include "bool_rewriter.h" namespace qe { void flatten_and(expr_ref_vector& result) { ast_manager& m = result.get_manager(); expr* e1, *e2, *e3; for (unsigned i = 0; i < result.size(); ++i) { if (m.is_and(result[i].get())) { app* a = to_app(result[i].get()); unsigned num_args = a->get_num_args(); for (unsigned j = 0; j < num_args; ++j) { result.push_back(a->get_arg(j)); } result[i] = result.back(); result.pop_back(); --i; } else if (m.is_not(result[i].get(), e1) && m.is_not(e1, e2)) { result[i] = e2; --i; } else if (m.is_not(result[i].get(), e1) && m.is_or(e1)) { app* a = to_app(e1); unsigned num_args = a->get_num_args(); for (unsigned j = 0; j < num_args; ++j) { result.push_back(m.mk_not(a->get_arg(j))); } result[i] = result.back(); result.pop_back(); --i; } else if (m.is_not(result[i].get(), e1) && m.is_implies(e1,e2,e3)) { result.push_back(e2); result[i] = m.mk_not(e3); --i; } else if (m.is_true(result[i].get()) || (m.is_not(result[i].get(), e1) && m.is_false(e1))) { result[i] = result.back(); result.pop_back(); --i; } else if (m.is_false(result[i].get()) || (m.is_not(result[i].get(), e1) && m.is_true(e1))) { result.reset(); result.push_back(m.mk_false()); return; } } } void flatten_and(expr* fml, expr_ref_vector& result) { SASSERT(result.get_manager().is_bool(fml)); result.push_back(fml); flatten_and(result); } void flatten_or(expr_ref_vector& result) { ast_manager& m = result.get_manager(); expr* e1, *e2, *e3; for (unsigned i = 0; i < result.size(); ++i) { if (m.is_or(result[i].get())) { app* a = to_app(result[i].get()); unsigned num_args = a->get_num_args(); for (unsigned j = 0; j < num_args; ++j) { result.push_back(a->get_arg(j)); } result[i] = result.back(); result.pop_back(); --i; } else if (m.is_not(result[i].get(), e1) && m.is_not(e1, e2)) { result[i] = e2; --i; } else if (m.is_not(result[i].get(), e1) && m.is_and(e1)) { app* a = to_app(e1); unsigned num_args = a->get_num_args(); for (unsigned j = 0; j < num_args; ++j) { result.push_back(m.mk_not(a->get_arg(j))); } result[i] = result.back(); result.pop_back(); --i; } else if (m.is_implies(result[i].get(),e2,e3)) { result.push_back(e3); result[i] = m.mk_not(e2); --i; } else if (m.is_false(result[i].get()) || (m.is_not(result[i].get(), e1) && m.is_true(e1))) { result[i] = result.back(); result.pop_back(); --i; } else if (m.is_true(result[i].get()) || (m.is_not(result[i].get(), e1) && m.is_false(e1))) { result.reset(); result.push_back(m.mk_true()); return; } } } void flatten_or(expr* fml, expr_ref_vector& result) { SASSERT(result.get_manager().is_bool(fml)); result.push_back(fml); flatten_or(result); } expr_ref mk_and(expr_ref_vector const& fmls) { ast_manager& m = fmls.get_manager(); expr_ref result(m); bool_rewriter(m).mk_and(fmls.size(), fmls.c_ptr(), result); return result; } expr_ref mk_or(expr_ref_vector const& fmls) { ast_manager& m = fmls.get_manager(); expr_ref result(m); bool_rewriter(m).mk_or(fmls.size(), fmls.c_ptr(), result); return result; } } z3-4.4.0/src/qe/qe_bool_plugin.cpp0000644000175000017500000001346412540347414016553 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: qe_bool_plugin.cpp Abstract: Eliminate Boolean variable from formula Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: Notes: The procedure is a bit naive. Consider a co-factoring approach that eliminates all Boolean variables in scope in one shot, similar to what we do with BDDs. --*/ #include "qe.h" #include "expr_safe_replace.h" #include "ast_pp.h" #include "model_evaluator.h" namespace qe { class bool_plugin : public qe_solver_plugin { expr_safe_replace m_replace; public: bool_plugin(i_solver_context& ctx, ast_manager& m): qe_solver_plugin(m, m.get_basic_family_id(), ctx), m_replace(m) {} virtual void assign(contains_app& x, expr* fml, rational const& vl) { SASSERT(vl.is_zero() || vl.is_one()); } virtual bool get_num_branches(contains_app& x, expr* fml, rational& nb) { nb = rational(2); return true; } virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) { SASSERT(vl.is_one() || vl.is_zero()); expr* tf = (vl.is_one())?m.mk_true():m.mk_false(); m_replace.apply_substitution(x.x(), tf, fml); if (def) { *def = tf; } } virtual bool project(contains_app& x, model_ref& model, expr_ref& fml) { model_evaluator model_eval(*model); expr_ref val_x(m); rational val; model_eval(x.x(), val_x); CTRACE("qe", (!m.is_true(val_x) && !m.is_false(val_x)), tout << "Boolean is a don't care: " << mk_pp(x.x(), m) << "\n";); val = m.is_true(val_x)?rational::one():rational::zero(); subst(x, val, fml, 0); return true; } virtual unsigned get_weight(contains_app& contains_x, expr* fml) { app* x = contains_x.x(); bool p = m_ctx.pos_atoms().contains(x); bool n = m_ctx.neg_atoms().contains(x); if (p && n) { return 3; } return 0; } virtual bool solve(conj_enum& conjs,expr* fml) { return solve_units(conjs, fml) || solve_polarized(fml); } virtual bool is_uninterpreted(app* a) { return false; } private: bool solve_units(conj_enum& conjs, expr* _fml) { expr_ref fml(_fml, m); conj_enum::iterator it = conjs.begin(), end = conjs.end(); unsigned idx; for (; it != end; ++it) { expr* e = *it; if (!is_app(e)) { continue; } app* a = to_app(e); expr* e1; if (m_ctx.is_var(a, idx)) { m_replace.apply_substitution(a, m.mk_true(), fml); m_ctx.elim_var(idx, fml, m.mk_true()); return true; } else if (m.is_not(e, e1) && m_ctx.is_var(e1, idx)) { m_replace.apply_substitution(to_app(e1), m.mk_false(), fml); m_ctx.elim_var(idx, fml, m.mk_false()); return true; } } return false; } bool solve_polarized(expr* _fml) { unsigned num_vars = m_ctx.get_num_vars(); expr_ref fml(_fml, m), def(m); for (unsigned i = 0; i < num_vars; ++i) { if (m.is_bool(m_ctx.get_var(i)) && solve_polarized(m_ctx.contains(i), fml, def)) { m_ctx.elim_var(i, fml, def); return true; } } return false; } bool solve_polarized( contains_app& contains_x, expr_ref& fml, expr_ref& def) { app* x = contains_x.x(); bool p = m_ctx.pos_atoms().contains(x); bool n = m_ctx.neg_atoms().contains(x); TRACE("quant_elim", tout << mk_pp(x, m) << " " << mk_pp(fml, m) << "\n";); if (p && n) { return false; } else if (p && !n) { atom_set::iterator it = m_ctx.pos_atoms().begin(), end = m_ctx.pos_atoms().end(); for (; it != end; ++it) { if (x != *it && contains_x(*it)) return false; } it = m_ctx.neg_atoms().begin(), end = m_ctx.neg_atoms().end(); for (; it != end; ++it) { if (contains_x(*it)) return false; } // only occurrences of 'x' must be in positive atoms def = m.mk_true(); m_replace.apply_substitution(x, def, fml); return true; } else if (!p && n) { atom_set::iterator it = m_ctx.pos_atoms().begin(), end = m_ctx.pos_atoms().end(); for (; it != end; ++it) { if (contains_x(*it)) return false; } it = m_ctx.neg_atoms().begin(), end = m_ctx.neg_atoms().end(); for (; it != end; ++it) { if (x != *it && contains_x(*it)) return false; } def = m.mk_false(); m_replace.apply_substitution(x, def, fml); return true; } else if (contains_x(fml)) { return false; } else { def = m.mk_false(); return true; } } }; qe_solver_plugin* mk_bool_plugin(i_solver_context& ctx) { return alloc(bool_plugin, ctx, ctx.get_manager()); } } z3-4.4.0/src/qe/nlarith_util.h0000644000175000017500000001134612540347414015715 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: nlarith_util.h Abstract: Utilities for nln-linear arithmetic quantifier elimination and solving. Author: Nikolaj (nbjorner) 2011-05-13 Notes: --*/ #ifndef _NLARITH_UTIL_H_ #define _NLARITH_UTIL_H_ #include "ast.h" #include "lbool.h" namespace nlarith { /** \brief A summary for branch side conditions and substitutions. Each branch in a split comprises of: - preds - a sequence of predicates used for the branching. - branches - a sequence of branch side conditions - subst - a sequence of substitutions that replace 'preds' by formulas not containing the eliminated variable - constraints - a sequence of side constraints to add to the main formula. */ class branch_conditions { expr_ref_vector m_branches; expr_ref_vector m_preds; vector m_subst; expr_ref_vector m_constraints; expr_ref_vector m_defs; expr_ref_vector m_a; expr_ref_vector m_b; expr_ref_vector m_c; public: branch_conditions(ast_manager& m) : m_branches(m), m_preds(m), m_constraints(m), m_defs(m), m_a(m), m_b(m), m_c(m) {} void add_pred(expr* p) { m_preds.push_back(p); } void add_branch(expr* branch, expr* cond, expr_ref_vector const& subst, expr* def, expr* a, expr* b, expr* c) { m_branches.push_back(branch); m_constraints.push_back(cond); m_subst.push_back(subst); m_defs.push_back(def); m_a.push_back(a); m_b.push_back(b); m_c.push_back(c); } expr* preds(unsigned i) const { return m_preds[i]; } expr* branches(unsigned i) const { return m_branches[i]; } expr* constraints(unsigned i) const { return m_constraints[i]; } expr* def(unsigned i) const { return m_defs[i]; } expr* a(unsigned i) const { return m_a[i]; } expr* b(unsigned i) const { return m_b[i]; } expr* c(unsigned i) const { return m_c[i]; } expr_ref_vector const& subst(unsigned i) const { return m_subst[i]; } expr_ref_vector const& branches() const { return m_branches; } expr_ref_vector const& preds() const { return m_preds; } vector const& subst() const { return m_subst; } expr_ref_vector const& constraints() const { return m_constraints; } void reset() { m_branches.reset(); m_preds.reset(); m_subst.reset(); m_constraints.reset(); m_defs.reset(); m_a.reset(); m_b.reset(); m_c.reset(); } unsigned size() const { return branches().size(); } unsigned num_preds() const { return preds().size(); } }; class util { class imp; imp* m_imp; public: util(ast_manager& m); ~util(); /** \brief Enable handling of linear variables. */ void set_enable_linear(bool enable_linear); /** \brief Create branches for non-linear variable x. */ bool create_branches(app* x, unsigned nl, expr* const* lits, branch_conditions& bc); /** \brief Extract non-linear variables from ground formula. \requires a ground formula. */ void extract_non_linear(expr* e, ptr_vector& nl_vars); /** \brief literal sets. Opaque state. */ class literal_set; static void deallocate(literal_set* lits); /** \brief Sign-based branching. v2. */ typedef obj_hashtable atoms; class eval { public: virtual ~eval() {} virtual lbool operator()(app* a) = 0; }; enum atom_update { INSERT, REMOVE }; class branch { public: virtual ~branch() {} virtual app* get_constraint() = 0; virtual void get_updates(ptr_vector& atoms, svector& updates) = 0; }; /** \brief select literals that contain non-linear variables. */ bool get_sign_literals(atoms const& atoms, eval& eval, literal_set*& lits); /** \brief given selected literals, generate branch conditions. */ void get_sign_branches(literal_set& lits, eval& eval, ptr_vector& branches); }; }; #endif z3-4.4.0/src/qe/qe_tactic.cpp0000644000175000017500000001003712540347414015502 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: qe_tactic.cpp Abstract: Quantifier elimination front-end for tactic framework. Author: Leonardo de Moura (leonardo) 2011-12-28. Revision History: --*/ #include"tactical.h" #include"filter_model_converter.h" #include"cooperate.h" #include"qe.h" class qe_tactic : public tactic { struct imp { ast_manager & m; smt_params m_fparams; volatile bool m_cancel; qe::expr_quant_elim m_qe; imp(ast_manager & _m, params_ref const & p): m(_m), m_qe(m, m_fparams) { updt_params(p); m_cancel = false; } void updt_params(params_ref const & p) { m_fparams.updt_params(p); m_fparams.m_nlquant_elim = p.get_bool("qe_nonlinear", false); m_qe.updt_params(p); } void collect_param_descrs(param_descrs & r) { m_qe.collect_param_descrs(r); } void set_cancel(bool f) { m_cancel = f; m_qe.set_cancel(f); } void checkpoint() { if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); cooperate("qe"); } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("qe", *g); m_fparams.m_model = g->models_enabled(); proof_ref new_pr(m); expr_ref new_f(m); bool produce_proofs = g->proofs_enabled(); unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { checkpoint(); if (g->inconsistent()) break; expr * f = g->form(i); if (!has_quantifiers(f)) continue; m_qe(m.mk_true(), f, new_f); new_pr = 0; if (produce_proofs) { new_pr = m.mk_modus_ponens(g->pr(i), new_pr); } g->update(i, new_f, new_pr, g->dep(i)); } g->inc_depth(); result.push_back(g.get()); TRACE("qe", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: qe_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual tactic * translate(ast_manager & m) { return alloc(qe_tactic, m, m_params); } virtual ~qe_tactic() { dealloc(m_imp); } virtual void updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { r.insert("qe_nonlinear", CPK_BOOL, "(default: false) enable virtual term substitution."); m_imp->collect_param_descrs(r); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } virtual void cleanup() { ast_manager & m = m_imp->m; imp * d = m_imp; #pragma omp critical (tactic_cancel) { m_imp = 0; } dealloc(d); d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { m_imp = d; } } protected: virtual void set_cancel(bool f) { if (m_imp) m_imp->set_cancel(f); } }; tactic * mk_qe_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(qe_tactic, m, p)); } z3-4.4.0/src/qe/qe_array_plugin.cpp0000644000175000017500000002427112540347414016734 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "qe.h" #include "array_decl_plugin.h" #include "expr_safe_replace.h" #include "ast_pp.h" #include "arith_decl_plugin.h" namespace qe { // --------------------- // arrays class array_plugin : public qe_solver_plugin { expr_safe_replace m_replace; public: array_plugin(i_solver_context& ctx, ast_manager& m) : qe_solver_plugin(m, m.mk_family_id("array"), ctx), m_replace(m) { } virtual ~array_plugin() {} virtual void assign(contains_app& x, expr* fml, rational const& vl) { UNREACHABLE(); } virtual bool get_num_branches( contains_app& x, expr* fml, rational& num_branches) { return false; } virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) { UNREACHABLE(); } virtual bool solve(conj_enum& conjs, expr* fml) { conj_enum::iterator it = conjs.begin(), end = conjs.end(); for (; it != end; ++it) { expr* e = *it; if (m.is_eq(e) && solve_eq(to_app(e), fml)) { return true; } } expr_ref_vector eqs(m); conjs.extract_equalities(eqs); for (unsigned i = 0; i < eqs.size(); ++i) { TRACE("qe_verbose", tout << mk_pp(eqs[i].get(), m) << "\n";); expr* e = eqs[i].get(); if (solve_eq_zero(e, fml)) { return true; } } return false; } virtual bool is_uninterpreted(app* f) { return true; } private: bool solve_eq(app* eq, expr* fml) { SASSERT(m.is_eq(eq)); expr* arg1 = eq->get_arg(0); expr* arg2 = eq->get_arg(1); return solve_eq(arg1, arg2, fml) || solve_eq(arg2, arg1, fml); } bool solve_eq_zero(expr* e, expr* fml) { arith_util arith(m); if (arith.is_add(e)) { app* a = to_app(e); expr* e1, *e2; unsigned sz = a->get_num_args(); expr_ref_vector args(m); expr_ref lhs(m), rhs(m); rational r; args.append(sz, a->get_args()); for (unsigned i = 0; i < sz; ++i) { expr_ref save(m); save = lhs = args[i].get(); args[i] = arith.mk_numeral(rational(0), m.get_sort(lhs)); rhs = arith.mk_uminus(arith.mk_add(args.size(), args.c_ptr())); if (arith.is_mul(lhs, e1, e2) && arith.is_numeral(e1, r) && r.is_minus_one()) { lhs = to_app(e2); rhs = arith.mk_uminus(rhs); } if (solve_eq(lhs, rhs, fml)) { return true; } args[i] = save; } } return false; } bool solve_eq(expr* lhs, expr* rhs, expr* fml) { if (!is_app(lhs)) { return false; } TRACE("qe_verbose", tout << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << "\n";); expr_ref tmp(m); app* a = to_app(lhs); // // A = t, A not in t. // unsigned idx = 0; if (m_ctx.is_var(a, idx) && !m_ctx.contains(idx)(rhs)) { expr_ref result(fml, m); m_replace.apply_substitution(a, rhs, result); m_ctx.elim_var(idx, result, rhs); return true; } if (solve_store(a, rhs, fml)) { return true; } if (solve_select(a, rhs, fml)) { return true; } return false; } bool solve_select2(app* lhs, expr* rhs, expr* fml) { // // (select (select A i) j) = t, A not in i, j, t // A |-> (store B' j (store B i t)), where B, B' are fresh. // // TBD return false; } bool solve_select(app* lhs, expr* rhs, expr* fml) { // // (select A i) = t, A not in i, v, t // A |-> (store B i t), where B is fresh. // unsigned idx = 0; vector > args; if (is_select(lhs, idx, rhs, args) && args.size() == 1) { contains_app& contains_A = m_ctx.contains(idx); app* A = contains_A.x(); app_ref B(m); expr_ref store_B_i_t(m); unsigned num_args = args[0].size(); B = m.mk_fresh_const("B", m.get_sort(A)); ptr_buffer args2; args2.push_back(B); for (unsigned i = 0; i < num_args; ++i) { args2.push_back(args[0][i]); } args2.push_back(rhs); store_B_i_t = m.mk_app(m_fid, OP_STORE, args2.size(), args2.c_ptr()); TRACE("qe", tout << "fml: " << mk_pp(fml, m) << "\n"; tout << "solved form: " << mk_pp(store_B_i_t, m) << "\n"; tout << "eq: " << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << "\n"; ); expr_ref result(fml, m); m_replace.apply_substitution(A, store_B_i_t, result); m_ctx.add_var(B); m_ctx.elim_var(idx, result, store_B_i_t); return true; } return false; } bool is_array_app_of(app* a, unsigned& idx, expr* t, decl_kind k) { if (!is_app_of(a, m_fid, k)) { return false; } expr* v = a->get_arg(0); if (!m_ctx.is_var(v, idx)) { return false; } contains_app& contains_v = m_ctx.contains(idx); for (unsigned i = 1; i < a->get_num_args(); ++i) { if (contains_v(a->get_arg(i))) { return false; } } if (contains_v(t)) { return false; } return true; } bool solve_store(app* lhs, expr* rhs, expr* fml) { // // store(store(A, j, u), i, v) = t, A not in i, j, u, v, t // -> // A |-> store(store(t, i, w), j, w') where w, w' are fresh. // t[i] = v // store(t, i, v)[j] = u // unsigned idx = 0; vector > args; if (is_store_update(lhs, idx, rhs, args)) { contains_app& contains_A = m_ctx.contains(idx); app* A = contains_A.x(); app_ref w(m); expr_ref store_t(rhs, m), store_T(rhs, m), select_t(m); ptr_vector args2; for (unsigned i = args.size(); i > 0; ) { --i; args2.reset(); w = m.mk_fresh_const("w", m.get_sort(args[i].back())); args2.push_back(store_T); args2.append(args[i]); select_t = m.mk_app(m_fid, OP_SELECT, args2.size()-1, args2.c_ptr()); fml = m.mk_and(fml, m.mk_eq(select_t, args2.back())); store_T = m.mk_app(m_fid, OP_STORE, args2.size(), args2.c_ptr()); args2[0] = store_t; args2.back() = w; store_t = m.mk_app(m_fid, OP_STORE, args2.size(), args2.c_ptr()); m_ctx.add_var(w); } TRACE("qe", tout << "Variable: " << mk_pp(A, m) << "\n"; tout << "fml: " << mk_pp(fml, m) << "\n"; tout << "solved form: " << mk_pp(store_t, m) << "\n"; tout << "eq: " << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << "\n"; ); expr_ref result(fml, m); m_replace.apply_substitution(A, store_t, result); m_ctx.elim_var(idx, result, store_t); return true; } return false; } bool is_array_app_of(app* a, unsigned& idx, expr* t, decl_kind k, vector >& args) { if (m_ctx.is_var(a, idx)) { contains_app& contains_v = m_ctx.contains(idx); if (args.empty() || contains_v(t)) { return false; } for (unsigned i = 0; i < args.size(); ++i) { for (unsigned j = 0; j < args[i].size(); ++j) { if (contains_v(args[i][j])) { return false; } } } return true; } if (!is_app_of(a, m_fid, k)) { return false; } args.push_back(ptr_vector()); for (unsigned i = 1; i < a->get_num_args(); ++i) { args.back().push_back(a->get_arg(i)); } if (!is_app(a->get_arg(0))) { return false; } return is_array_app_of(to_app(a->get_arg(0)), idx, t, k, args); } bool is_store_update(app* a, unsigned& idx, expr* t, vector >& args) { return is_array_app_of(a, idx, t, OP_STORE, args); } bool is_select(app* a, unsigned& idx, expr* t, vector >& args) { return is_array_app_of(a, idx, t, OP_SELECT, args); } }; qe_solver_plugin* mk_array_plugin(i_solver_context& ctx) { return alloc(array_plugin, ctx, ctx.get_manager()); } } z3-4.4.0/src/qe/qe_arith_plugin.cpp0000644000175000017500000027273612540347414016740 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: arith_plugin.cpp Abstract: Eliminate Arithmetical variable from formula Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: --*/ #include "qe.h" #include "ast_pp.h" #include "expr_safe_replace.h" #include "bool_rewriter.h" #include "bv_decl_plugin.h" #include "arith_decl_plugin.h" #include "arith_eq_solver.h" #include "arith_rewriter.h" #include "th_rewriter.h" #include "factor_rewriter.h" #include "obj_pair_hashtable.h" #include "nlarith_util.h" #include "model_evaluator.h" #include "smt_kernel.h" namespace qe { class bound { rational m_coeff; expr_ref m_term; bool m_is_strict; public: bound(ast_manager& m, rational const& n, expr* t, bool is_strict) : m_coeff(n), m_term(t, m), m_is_strict(is_strict) { } bool is_strict() const { return m_is_strict; } expr* term() const { return m_term.get(); } rational const& coeff() const { return m_coeff; } void update(rational const& k, expr* t) { m_coeff = k; m_term = t; } void pp(std::ostream& out, app* x) { ast_manager& m = m_term.get_manager(); out << "(<= (+ (* " << coeff() << " " << mk_pp(x, m) << ") " << mk_pp(term(), m) << ") 0)"; } }; typedef rational numeral; // m_k | (m_a * x + m_term) class div_constraint { numeral m_k; numeral m_a; expr* m_term; public: div_constraint(numeral const& k, numeral const& a, expr* t): m_k(k), m_a(a), m_term(t) {} numeral const& a() const { return m_a; } numeral const& k() const { return m_k; } expr* t() const { return m_term; } numeral& a_ref() { return m_a; } numeral& k_ref() { return m_k; } expr*& t_ref() { return m_term; } }; typedef vector div_constraints; class arith_qe_util { ast_manager& m; i_solver_context& m_ctx; public: arith_util m_arith; // initialize before m_zero_i, etc. th_rewriter simplify; private: arith_eq_solver m_arith_solver; bv_util m_bv; expr_ref m_zero_i; expr_ref m_one_i; expr_ref m_minus_one_i; expr_ref m_zero_r; expr_ref m_one_r; expr_ref m_tmp; public: expr_safe_replace m_replace; bool_rewriter m_bool_rewriter; arith_rewriter m_arith_rewriter; arith_qe_util(ast_manager& m, smt_params& p, i_solver_context& ctx) : m(m), m_ctx(ctx), m_arith(m), simplify(m), m_arith_solver(m), m_bv(m), m_zero_i(m_arith.mk_numeral(numeral(0), true), m), m_one_i(m_arith.mk_numeral(numeral(1), true), m), m_minus_one_i(m_arith.mk_numeral(numeral(-1), true), m), m_zero_r(m_arith.mk_numeral(numeral(0), false), m), m_one_r(m_arith.mk_numeral(numeral(1), false), m), m_tmp(m), m_replace(m), m_bool_rewriter(m), m_arith_rewriter(m) { } ast_manager& get_manager() { return m; } // // match e := k*x + rest, where k != 0. // bool get_coeff(contains_app& contains_x, expr* p, rational& k, expr_ref& rest) { app* x = contains_x.x(); ptr_vector restl, todo; todo.push_back(p); bool found = false; expr* e1, *e2; while (!todo.empty()) { expr* e = todo.back(); todo.pop_back(); if (m_arith.is_add(e)) { for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { todo.push_back(to_app(e)->get_arg(i)); } } else if (e == x) { k = numeral(1); found = true; break; } else if (m_arith.is_mul(e, e1, e2) && e1 == x && m_arith.is_numeral(e2, k)) { found = true; break; } else if (m_arith.is_mul(e, e1, e2) && e2 == x && m_arith.is_numeral(e1, k)) { found = true; break; } else { restl.push_back(e); } } if (!found) { TRACE("qe_verbose", tout << "Did not find: " << mk_pp(x, m) << " in " << mk_pp(p, m) << "\n"; ); return false; } while (!todo.empty()) { restl.push_back(todo.back()); todo.pop_back(); } if (restl.empty()) { rest = mk_zero(x); } else { rest = m_arith.mk_add(restl.size(), restl.c_ptr()); } if (contains_x(rest)) { return false; } TRACE("qe_verbose", tout << mk_pp(p, m) << " = " << "(+ (* " << k << " " << mk_pp(x, m) << ") " << mk_pp(rest, m) << ")\n"; ); return true; } // // match p := k + rest // where k is a numeral and rest does not contain numerals. // void get_const(expr* p, rational& k, expr_ref& rest) { ptr_vector todo, restl; todo.push_back(p); k = numeral(0); while(!todo.empty()) { p = todo.back(); todo.pop_back(); if (m_arith.is_add(p)) { for (unsigned i = 0; i < to_app(p)->get_num_args(); ++i) { todo.push_back(to_app(p)->get_arg(i)); } } else if (m_arith.is_numeral(p, k)) { break; } else { restl.push_back(p); } } while (!todo.empty()) { restl.push_back(todo.back()); todo.pop_back(); } if (restl.empty()) { rest = mk_zero(p); } else { rest = m_arith.mk_add(restl.size(), restl.c_ptr()); } } // // match (not ne) bool is_neg(app* e, expr_ref& ne) { if (m.is_not(e)) { ne = to_app(e)->get_arg(0); return true; } return false; } bool is_le(app* e, expr_ref& p) { return is_le_ge_core<1>(e, p); } bool is_ge(app* e, expr_ref& p) { return is_le_ge_core<0>(e, p); } // match e = p < 0 or p > 0 bool is_lt(app* e, expr_ref& p) { numeral k; expr* a1, *a2; if (m_arith.is_lt(e, a1, a2) || m_arith.is_gt(e, a2, a1)) { p = a1; if (m_arith.is_numeral(a2, k) && k.is_zero()) { return true; } } else { return false; } p = mk_sub(p, a2); simplify(p); return true; } // // match 0 == p mod k, p mod k == 0 // bool is_divides(app* e, numeral& k, expr_ref& p) { expr* e1, *e2; if (!m.is_eq(e, e1, e2)) { return false; } return is_divides(e1, e2, k, p) || is_divides(e2, e1, k, p); } bool is_divides(expr* e1, expr* e2, numeral& k, expr_ref& p) { if (m_arith.is_mod(e2) && m_arith.is_numeral(e1, k) && k.is_zero() && m_arith.is_numeral(to_app(e2)->get_arg(1), k)) { p = to_app(e2)->get_arg(0); return true; } return false; } bool is_not_divides(app* e, app_ref& n, numeral& k, expr_ref& p) { if (!m.is_not(e)) { return false; } if (!is_app(to_app(e)->get_arg(0))) { return false; } n = to_app(to_app(e)->get_arg(0)); return is_divides(n, k, p); } bool is_real(app* x) const { return m_arith.is_real(x); } // // b*t <= a*s // template void mk_bound_aux(rational const& a, expr* t, rational const& b, expr* s, expr_ref& result) { SASSERT(a.is_neg() == b.is_neg()); expr_ref tt(t, m), ss(s, m), e(m); // hack to fix wierd gcc compilation error rational abs_a(a); rational abs_b(b); if (abs_a.is_neg()) abs_a.neg(); if (abs_b.is_neg()) abs_b.neg(); ss = mk_mul(abs_a, ss); tt = mk_mul(abs_b, tt); if(a.is_neg()) { e = mk_sub(tt, ss); if (is_strict) { if (m_arith.is_int(e)) { e = mk_add(e, m_one_i); mk_le(e, result); } else { mk_lt(e, result); } } else { mk_le(e, result); } } else { e = mk_sub(ss, tt); if (is_strict) { if (m_arith.is_int(e)) { e = mk_add(e, m_one_i); mk_le(e, result); } else { mk_lt(e, result); } } else { mk_le(e, result); } } } void mk_bound(rational const& a, expr* t, rational const& b, expr* s, expr_ref& result) { mk_bound_aux(a, t, b, s, result); } void mk_strict_bound(rational const& a, expr* t, rational const& b, expr* s, expr_ref& result) { mk_bound_aux(a, t, b, s, result); } void mk_divides(numeral n, expr* e, expr_ref& result) { SASSERT(n.is_int()); expr_ref tmp1(e, m), tmp2(m); simplify(tmp1); m_arith_rewriter.mk_mod(tmp1, mk_numeral(n), tmp2); m_bool_rewriter.mk_eq(m_zero_i, tmp2, result); } void mk_div(expr* a, numeral const & k, expr_ref& result) { result = m_arith.mk_div(a, m_arith.mk_numeral(k, false)); simplify(result); } expr* mk_numeral(numeral const& k, bool is_int = true) { return m_arith.mk_numeral(k, is_int); } expr* mk_numeral(int k, bool is_int) { return mk_numeral(numeral(k),is_int); } expr* mk_uminus(expr* e) { return m_arith.mk_uminus(e); } expr* mk_abs(expr* e) { rational val; if (m_arith.is_numeral(e, val)) { if (val.is_neg()) { return m_arith.mk_uminus(e); } else { return e; } } else { return m.mk_ite(m_arith.mk_le(mk_zero(e), e), e, m_arith.mk_uminus(e)); } } template expr_ref mk_min_max(unsigned num_args, expr* const* args) { SASSERT(num_args > 0); if (num_args == 1) { return expr_ref(args[0], m); } else { expr_ref e2 = mk_min_max(num_args-1,args+1); expr* e1 = args[0]; expr* cmp = is_max?m_arith.mk_le(e1, e2):m_arith.mk_le(e2, e1); return expr_ref(m.mk_ite(cmp, e2, e1), m); } } expr_ref mk_max(unsigned num_args, expr* const* args) { return mk_min_max(num_args, args); } expr_ref mk_min(unsigned num_args, expr* const* args) { return mk_min_max(num_args, args); } expr* mk_mul(expr* a, expr* b) { return m_arith.mk_mul(a,b); } expr* mk_add(expr* a, expr* b) { return m_arith.mk_add(a,b); } expr* mk_sub(expr* a, expr* b) { return m_arith.mk_sub(a,b); } expr* mk_mul(numeral const& a, expr* b) { if (a.is_one()) return b; return m_arith.mk_mul(mk_numeral(a, m_arith.is_int(b)),b); } expr* mk_zero(sort* s) { return m_arith.is_int(s)?m_zero_i:m_zero_r; } expr* mk_zero(expr* e) { return m_arith.is_int(e)?m_zero_i:m_zero_r; } expr* mk_one(sort* s) { return m_arith.is_int(s)?m_one_i:m_one_r; } expr* mk_one(expr* e) { return m_arith.is_int(e)?m_one_i:m_one_r; } void mk_le(expr* e, expr_ref& result) { expr_ref tmp(e, m); simplify(tmp); m_arith_rewriter.mk_le(tmp, mk_zero(e), result); } void mk_lt(expr* e, expr_ref& result) { rational r; if (m_arith.is_numeral(e, r)) { if (r.is_neg()) { result = m.mk_true(); } else { result = m.mk_false(); } } else if (m_arith.is_int(e)) { result = m_arith.mk_le(e, m_minus_one_i); } else { result = m.mk_not(m_arith.mk_le(mk_zero(e), e)); } simplify(result); TRACE("qe_verbose", tout << "mk_lt " << mk_pp(result, m) << "\n";); } // ax + t = 0 void mk_eq(rational const& a, app* x, expr* t, expr_ref& result) { result = m_arith.mk_eq(mk_add(mk_mul(a, x), t), mk_zero(x)); } void mk_and(unsigned sz, expr*const* args, expr_ref& result) { m_bool_rewriter.mk_and(sz, args, result); } void mk_and(expr* e1, expr* e2, expr_ref& result) { m_bool_rewriter.mk_and(e1, e2, result); } void add_and(expr* e, ptr_vector& conjs) { if (m.is_and(e)) { conjs.append(to_app(e)->get_num_args(), to_app(e)->get_args()); } else { conjs.push_back(e); } } void mk_flat_and(expr* e1, expr* e2, expr_ref& result) { ptr_vector conjs; add_and(e1, conjs); add_and(e2, conjs); m_bool_rewriter.mk_and(conjs.size(), conjs.c_ptr(), result); } void mk_or(unsigned sz, expr*const* args, expr_ref& result) { m_bool_rewriter.mk_or(sz, args, result); } void mk_or(expr* e1, expr* e2, expr_ref& result) { m_bool_rewriter.mk_or(e1, e2, result); } // // b*t <= a*s // void mk_resolve(app* x, bool is_strict, rational const& a, expr* t, rational const& b, expr* s, expr_ref& result) { rational abs_a(abs(a)), abs_b(abs(b)); SASSERT(a.is_neg() == b.is_pos()); SASSERT(!is_strict || (abs_a.is_one() && abs_b.is_one())); expr_ref bt(mk_mul(abs_b, t), m); expr_ref as(mk_mul(abs_a, s), m); expr_ref as_bt(mk_add(as, bt), m); if(is_strict) { mk_lt(as_bt, result); } else { mk_le(as_bt, result); } if (!abs_a.is_one() && !abs_b.is_one()) { // integer resolution case. SASSERT(!is_strict); SASSERT(abs_a > rational::one() && abs_b > rational::one()); expr_ref slack(mk_numeral((abs_a-numeral(1))*(abs_b-numeral(1)), true), m); expr_ref result1(m), result2(m); // a*s + b*t <= 0 expr_ref as_bt_le_0(result, m), tmp2(m), asz_bt_le_0(m), tmp3(m), tmp4(m); expr_ref b_divides_sz(m); // a*s + b*t + (a-1)(b-1) <= 0 tmp2 = m_arith.mk_add(as_bt, slack); mk_le(tmp2, result1); rational a1 = a, b1 = b; if (abs_a < abs_b) { std::swap(abs_a, abs_b); std::swap(a1, b1); std::swap(s, t); std::swap(as, bt); } SASSERT(abs_a >= abs_b); // create finite disjunction for |b|. // exists x, z in [0 .. |b|-2] . b*x + s + z = 0 && ax + t <= 0 && bx + s <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && ax + t <= 0 && bx + s <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && bx + s <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && -z - s + s <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && -z <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && a*n_sign(b)(s + z) + |b|t <= 0 // <=> // exists z in [0 .. |b|-2] . |b| | (z + s) && a*n_sign(b)(s + z) + |b|t <= 0 // expr_ref sz(mk_add(s, x), m); if (b1.is_pos()) { sz = m_arith.mk_uminus(sz); } tmp4 = mk_add(mk_mul(a1, sz), bt); mk_le(tmp4, asz_bt_le_0); if (to_app(asz_bt_le_0)->get_arg(0) == x && m_arith.is_zero(to_app(asz_bt_le_0)->get_arg(1))) { // exists z in [0 .. |b|-2] . |b| | (z + s) && z <= 0 // <=> // |b| | s mk_divides(abs_b, s, tmp2); } else { mk_divides(abs_b, sz, b_divides_sz); mk_and(b_divides_sz, asz_bt_le_0, tmp4); mk_big_or(abs_b - numeral(2), x, tmp4, tmp2); TRACE("qe", tout << "b | s + z: " << mk_pp(b_divides_sz, m) << "\n"; tout << "a(s+z) + bt <= 0: " << mk_pp(asz_bt_le_0, m) << "\n"; ); } mk_flat_and(as_bt_le_0, tmp2, result2); mk_or(result1, result2, result); simplify(result); // a*s + b*t + (a-1)(b-1) <= 0 // or exists z in [0 .. |b|-2] . |b| | (z + s) && a*n_sign(b)(s + z) + |b|t <= 0 } TRACE("qe", { tout << (is_strict?"strict":"non-strict") << "\n"; bound(m, a, t, false).pp(tout, x); tout << "\n"; bound(m, b, s, false).pp(tout, x); tout << "\n"; tout << mk_pp(result, m) << "\n"; }); } struct mul_lt { arith_util& u; mul_lt(arith_qe_util& u): u(u.m_arith) {} bool operator()(expr* n1, expr* n2) const { expr* x, *y; if (u.is_mul(n1, x, y) && u.is_numeral(x)) { n1 = y; } if (u.is_mul(n2, x, y) && u.is_numeral(x)) { n2 = y; } return n1->get_id() < n2->get_id(); } }; void normalize_sum(expr_ref& p) { simplify(p); if (!m_arith.is_add(p)) { return; } unsigned sz = to_app(p)->get_num_args(); ptr_buffer args; for (unsigned i = 0; i < sz; ++i) { args.push_back(to_app(p)->get_arg(i)); } std::sort(args.begin(), args.end(), mul_lt(*this)); p = m_arith.mk_add(args.size(), args.c_ptr()); } void pp_div(std::ostream& out, app* x, div_constraint const& div) { out << div.k() << " | (" << div.a() << "*" << mk_pp(x, m) << " + " << mk_pp(div.t(), m) << ") "; } void pp_divs(std::ostream& out, app* x, div_constraints const& divs) { for (unsigned i = 0; i < divs.size(); ++i) { pp_div(out, x, divs[i]); out << " "; } } bool mk_atom(expr* e, bool p, expr_ref& result) { // retain equalities. if (!is_app(e)) { return false; } app* a = to_app(e); expr_ref t1(m), t2(m); expr_ref tmp1(m), tmp2(m); rational k; expr* a0, *a1; if (p && is_divides(a, k, tmp1)) { result = e; } else if (!p && is_divides(a, k, tmp1)) { m_bool_rewriter.mk_not(e, result); } else if (p && m.is_eq(e, a0, a1) && is_arith(a0)) { t1 = mk_sub(a0, a1); simplify(t1); t2 = mk_sub(a1, a0); simplify(t2); mk_le(t1, tmp1); mk_le(t2, tmp2); mk_and(tmp1, tmp2, result); } else if (!p && m.is_eq(e, a0, a1) && m_arith.is_int(a0)) { tmp1 = mk_sub(a0, a1); t1 = mk_add(mk_one(a0), tmp1); simplify(t1); t2 = mk_sub(mk_one(a0), tmp1); simplify(t2); mk_le(t1, tmp1); // a0 < a1 <=> 1 + a0 - a1 <= 0 mk_le(t2, tmp2); // a0 > a1 <=> 1 - a0 + a1 <= 0 mk_or(tmp1, tmp2, result); } else if (!p && m.is_eq(e, a0, a1) && m_arith.is_real(a0)) { t1 = mk_sub(a0, a1); simplify(t1); t2 = mk_sub(a1, a0); simplify(t2); mk_lt(t1, tmp1); mk_lt(t2, tmp2); mk_or(tmp1, tmp2, result); } else if (!p && (m_arith.is_le(e, a0, a1) || m_arith.is_ge(e, a1, a0))) { tmp1 = mk_sub(a1, a0); mk_lt(tmp1, result); } else if (p && (m_arith.is_le(e) || m_arith.is_ge(e))) { result = e; } else if (p && (m_arith.is_lt(e, a0, a1) || m_arith.is_gt(e, a1, a0))) { tmp1 = mk_sub(a0, a1); mk_lt(tmp1, result); } else if (!p && (m_arith.is_lt(e, a0, a1) || m_arith.is_gt(e, a1, a0))) { tmp1 = mk_sub(a1, a0); mk_le(tmp1, result); } else { return false; } TRACE("qe_verbose", tout << "Atom: " << mk_pp(result, m) << "\n";); return true; } void mk_bounded_var(rational const& n, app_ref& z_bv, app_ref& z) { rational two(2), b(n); unsigned sz = 0; do { ++sz; b = div(b, two); } while (b.is_pos()); sort* s = m_bv.mk_sort(sz); z_bv = m.mk_fresh_const("z", s); expr_ref tmp(m); z = m_bv.mk_bv2int(z_bv); } bool solve(conj_enum& conjs, expr* fml) { expr_ref_vector eqs(m); extract_equalities(conjs, eqs); return reduce_equations(eqs.size(), eqs.c_ptr(), fml); } // ---------------------------------------------------------------------- // // Equation solving features. // // Extract equalities from current goal. // void extract_equalities(conj_enum& conjs, expr_ref_vector& eqs) { obj_hashtable leqs; expr_ref_vector trail(m); expr_ref tmp1(m), tmp2(m); expr *a0, *a1; eqs.reset(); conj_enum::iterator it = conjs.begin(), end = conjs.end(); for (; it != end; ++it) { expr* e = *it; bool is_leq = false; if (m.is_eq(e, a0, a1) && is_arith(a0)) { m_arith_rewriter.mk_sub(a0, a1, tmp1); simplify(tmp1); eqs.push_back(tmp1); } else if (m_arith.is_le(e, a0, a1) || m_arith.is_ge(e, a1, a0)) { m_arith_rewriter.mk_sub(a0, a1, tmp1); is_leq = true; } else { // drop equality. } if (is_leq) { normalize_sum(tmp1); tmp2 = m_arith.mk_uminus(tmp1); normalize_sum(tmp2); if (leqs.contains(tmp2)) { eqs.push_back(tmp1); TRACE("qe", tout << "found: " << mk_pp(tmp1, m) << "\n";); } else { trail.push_back(tmp1); leqs.insert(tmp1); TRACE("qe_verbose", tout << "insert: " << mk_pp(tmp1, m) << "\n";); } } } } private: // // match p <= 0 or p >= 0 // template bool is_le_ge_core(app* e, expr_ref& p) { numeral k; expr_ref tmp(m); expr* a2; if (m_arith.is_le(e)) { p = e->get_arg(1-IS_LE); a2 = e->get_arg(IS_LE); if (m_arith.is_numeral(a2, k) && k.is_zero()) { return true; } } else if (m_arith.is_ge(e)) { p = e->get_arg(IS_LE); a2 = e->get_arg(1-IS_LE); if (m_arith.is_numeral(a2, k) && k.is_zero()) { return true; } } else { return false; } p = mk_sub(p, a2); simplify(p); return true; } bool is_arith(expr* e) { return m_arith.is_int(e) || m_arith.is_real(e); } void mk_big_or(numeral up, app* x, expr* body, expr_ref& result) { TRACE("qe", tout << mk_pp(x, m) << " " << mk_pp(body, m) << "\n";); if (numeral(1) >= up) { mk_big_or_blast(up, x, body, result); } else { mk_big_or_symbolic_blast(up, x, body, result); } } void mk_big_or_blast(numeral up, app* x, expr* body, expr_ref& result) { expr_ref_vector ors(m); numeral index(0); while (index <= up) { expr* n = mk_numeral(index); result = body; m_replace.apply_substitution(x, n, result); ors.push_back(result); ++index; } mk_or(ors.size(), ors.c_ptr(), result); TRACE("qe", tout << "[0 " << up << "] " << mk_pp(x, m) << "\n" << mk_pp(body, m) << "\n" << mk_pp(result, m) << "\n";); } void mk_big_or_symbolic(numeral up, app* x, expr* body, expr_ref& result) { app_ref z_bv(m); mk_big_or_symbolic(up, x, body, z_bv, result); m_ctx.add_var(z_bv); } void mk_big_or_symbolic_blast(numeral up, app* x, expr* body, expr_ref& result) { app_ref z_bv(m); mk_big_or_symbolic(up, x, body, z_bv, result); m_ctx.blast_or(z_bv, result); } void mk_big_or_symbolic(numeral up, app* x, expr* body, app_ref& z_bv, expr_ref& result) { expr* e1 = m_arith.mk_le(x, m_arith.mk_numeral(up, true)); mk_flat_and(e1, body, result); app_ref z(m); mk_bounded_var(up, z_bv, z); m_replace.apply_substitution(x, z, result); } // // Determine if 'x' can be isolated. // Return the coefficient if found. // bool isolate_x(expr* p, app* x, contains_app& contains_x, numeral& coeff) { numeral k; while (m_arith.is_add(p)) { bool found_x = false; expr* next_p = 0; for (unsigned i = 0; i < to_app(p)->get_num_args(); ++i) { expr* arg = to_app(p)->get_arg(i); if (contains_x(arg)) { if (found_x) { return false; } found_x = true; next_p = arg; } } if (!next_p) { return false; } p = next_p; } expr *e1, *e2; if (p == x) { coeff = numeral(1); return true; } else if (m_arith.is_mul(p, e1, e2) && m_arith.is_numeral(e1, k) && e2 == x) { coeff = k; return true; } else if (m_arith.is_mul(p, e1, e2) && m_arith.is_numeral(e2, k) && e1 == x) { coeff = k; return true; } return false; } // // Reduce equations. // Singular equations eliminate variables directly. // Linear equations eliminate original variables and introduce auxiliary variables. // bool reduce_equations(unsigned num_eqs, expr * const* eqs, expr* fml) { for (unsigned i = 0; i < num_eqs; ++i) { if (reduce_equation(eqs[i], fml)) { return true; } } return false; } bool solve_singular(unsigned var_idx, expr* p, expr* fml) { rational k; expr_ref e(m), tmp(m); app* x = m_ctx.get_var(var_idx); if (!isolate_x(p, x, m_ctx.contains(var_idx), k)) { return false; } if (m_arith.is_int(x) && !(abs(k).is_one())) { return false; } if (abs(k).is_one()) { if (k.is_neg()) { e = m_arith.mk_add(p, x); } else { e = m_arith.mk_sub(x, p); } } else { SASSERT(!m_arith.is_int(x)); // p = p' + k*x = 0 // <=> // -k*x = p' = p - k*x // => // x = (p - k*x)/ -k expr* ke = m_arith.mk_numeral(-k, false); tmp = m_arith.mk_mul(ke, x); tmp = m_arith.mk_add(p, tmp); e = m_arith.mk_div(tmp, ke); } TRACE("qe", tout << "is singular:\n" << mk_pp(p, m) << "\n" << mk_pp(fml, m) << "\n" << mk_pp(x, m) << " = " << mk_pp(e, m) << "\n"; ); expr_ref result(fml, m); m_replace.apply_substitution(x, e, result); simplify(result); TRACE("qe", tout << "singular solved:\n" << mk_pp(result, m) << "\n"; ); m_ctx.elim_var(var_idx, result, e); return true; } bool solve_singular(expr* p, expr* fml) { unsigned num_vars = m_ctx.get_num_vars(); for (unsigned i = 0; i < num_vars; ++i) { if (solve_singular(i, p, fml)) { return true; } } return false; } bool solve_linear(expr* p, expr* fml) { vector values; unsigned num_vars = m_ctx.get_num_vars(); app*const* vars_ptr = m_ctx.get_vars(); if (!is_linear(p, num_vars, vars_ptr, values)) { return false; } TRACE("qe", tout << "is linear: " << mk_pp(p, m) << "\n";); SASSERT(values.size() == num_vars + 1); SASSERT(num_vars > 0); unsigned index; bool is_aux; // // The first entry in values is the constant. // VERIFY(m_arith_solver.solve_integer_equation(values, index, is_aux)); SASSERT(1 <= index && index <= num_vars); app_ref x(m_ctx.get_var(index-1), m); app_ref z(m); expr_ref p1(m); if (is_aux) { // An auxiliary variable was introduced in lieu of 'x'. // it has coefficient 'm' = values[index]. SASSERT(values[index] >= rational(3)); z = m.mk_fresh_const("x", m_arith.mk_int()); m_ctx.add_var(z); p1 = m_arith.mk_mul(m_arith.mk_numeral(values[index], true), z); } else { // the coefficient to 'x' is -1. p1 = m_arith.mk_numeral(numeral(0), true); } for (unsigned i = 1; i <= num_vars; ++i) { numeral k = values[i]; if (!k.is_zero() && i != index) { p1 = m_arith.mk_add(p1, m_arith.mk_mul(m_arith.mk_numeral(k, true), m_ctx.get_var(i-1))); } } p1 = m_arith.mk_add(p1, m_arith.mk_numeral(values[0], true)); TRACE("qe", tout << "is linear:\n" << mk_pp(fml, m) << "\n" << mk_pp(p, m) << "\n" << mk_pp(x, m) << " = " << mk_pp(p1, m) << "\n"; tout << values[0] << " + "; for (unsigned i = 0; i < num_vars; ++i) { tout << " + " << values[i+1] << " * " << mk_pp(m_ctx.get_var(i), m) << " "; } tout << " = 0\n"; ); expr_ref result(fml, m); m_replace.apply_substitution(x, p1, result); simplify(result); m_ctx.elim_var(index-1, result, p1); TRACE("qe", tout << "Reduced: " << mk_pp(result, m) << "\n";); return true; } bool reduce_equation(expr* p, expr* fml) { numeral k; if (m_arith.is_numeral(p, k) && k.is_zero()) { return false; } return solve_singular(p, fml) || solve_linear(p, fml); } bool find_variable(expr* p, unsigned num_vars, app* const* vars, numeral* values, numeral const& k) { if (!is_app(p) || to_app(p)->get_num_args() > 0) { return false; } for (unsigned i = 0; i < num_vars; ++i) { if (p == vars[i]) { values[i] += k; return true; } } return false; } bool is_linear(expr* p, unsigned num_vars, app* const* vars, vector& values) { if (num_vars == 0) { return false; } values.reset(); for (unsigned i = 0; i <= num_vars; ++i) { values.push_back(numeral(0)); } numeral* vars_ptr = values.c_ptr() + 1; ptr_vector todo; numeral k; expr* e1, *e2; todo.push_back(p); while (!todo.empty()) { p = todo.back(); todo.pop_back(); if (m_arith.is_add(p)) { for (unsigned i = 0; i < to_app(p)->get_num_args(); ++i) { todo.push_back(to_app(p)->get_arg(i)); } } else if (m_arith.is_mul(p, e1, e2) && m_arith.is_numeral(e1, k) && find_variable(e2, num_vars, vars, vars_ptr, k)) { // ok } else if (m_arith.is_mul(p, e1, e2) && m_arith.is_numeral(e2, k) && find_variable(e1, num_vars, vars, vars_ptr, k)) { // ok } else if (find_variable(p, num_vars, vars, vars_ptr, k)) { // ok } else if (m_arith.is_numeral(p, k)) { values[0] += k; } else { TRACE("qe_verbose", tout << "non-linear " << mk_pp(p, m) << "\n";); return false; } } return true; } }; class bounds_proc { arith_qe_util& m_util; ast_mark m_mark; expr_ref_vector m_le_terms, m_ge_terms, m_lt_terms, m_gt_terms; vector m_le_coeffs, m_ge_coeffs, m_lt_coeffs, m_gt_coeffs; app_ref_vector m_le_atoms, m_ge_atoms, m_lt_atoms, m_gt_atoms; expr_ref_vector m_div_terms; vector m_div_coeffs, m_div_divisors; app_ref_vector m_div_atoms; app_ref m_div_z; expr_ref_vector m_nested_div_terms; vector m_nested_div_coeffs, m_nested_div_divisors; app_ref_vector m_nested_div_atoms; app_ref_vector m_nested_div_z; rational m_d; public: bounds_proc(arith_qe_util& u): m_util(u), m_le_terms(u.get_manager()), m_ge_terms(u.get_manager()), m_lt_terms(u.get_manager()), m_gt_terms(u.get_manager()), m_le_atoms(u.get_manager()), m_ge_atoms(u.get_manager()), m_lt_atoms(u.get_manager()), m_gt_atoms(u.get_manager()), m_div_terms(u.get_manager()), m_div_atoms(u.get_manager()), m_div_z(u.get_manager()), m_nested_div_terms(u.get_manager()), m_nested_div_atoms(u.get_manager()), m_nested_div_z(u.get_manager()) { reset(); } bool get_bound(contains_app& contains_x, app* a) { ast_manager& m = m_util.get_manager(); app* x = contains_x.x(); if (m_mark.is_marked(a) || get_le_bound(contains_x, a) || get_lt_bound(contains_x, a) || get_divides(contains_x, a) || get_nested_divs(contains_x, a)) { TRACE("qe_verbose", tout << "Bound for " << mk_pp(x, m) << " within " << mk_pp(a, m) << "\n";); m_mark.mark(a, true); return true; } else { TRACE("qe", tout << "No bound for " << mk_pp(x, m) << " within " << mk_pp(a, m) << "\n";); return false; } } unsigned lt_size() { return m_lt_terms.size(); } unsigned le_size() { return m_le_terms.size(); } unsigned gt_size() { return m_gt_terms.size(); } unsigned ge_size() { return m_ge_terms.size(); } unsigned t_size(bool is_l) { return is_l?lt_size():gt_size(); } unsigned e_size(bool is_l) { return is_l?le_size():ge_size(); } unsigned size(bool is_strict, bool is_l) { return is_strict?t_size(is_l):e_size(is_l); } expr* const* lt() { return m_lt_terms.c_ptr(); } expr* const* le() { return m_le_terms.c_ptr(); } expr* const* gt() { return m_gt_terms.c_ptr(); } expr* const* ge() { return m_ge_terms.c_ptr(); } expr* const* t(bool is_l) { return is_l?lt():gt(); } expr* const* e(bool is_l) { return is_l?le():ge(); } expr* const* exprs(bool is_strict, bool is_l) { return is_strict?t(is_l):e(is_l);} rational const* lt_coeffs() { return m_lt_coeffs.c_ptr(); } rational const* le_coeffs() { return m_le_coeffs.c_ptr(); } rational const* gt_coeffs() { return m_gt_coeffs.c_ptr(); } rational const* ge_coeffs() { return m_ge_coeffs.c_ptr(); } rational const* t_coeffs(bool is_l) { return is_l?lt_coeffs():gt_coeffs(); } rational const* e_coeffs(bool is_l) { return is_l?le_coeffs():ge_coeffs(); } rational const* coeffs(bool is_strict, bool is_l) { return is_strict?t_coeffs(is_l):e_coeffs(is_l); } app* const* lt_atoms() { return m_lt_atoms.c_ptr(); } app* const* le_atoms() { return m_le_atoms.c_ptr(); } app* const* gt_atoms() { return m_gt_atoms.c_ptr(); } app* const* ge_atoms() { return m_ge_atoms.c_ptr(); } app* const* t_atoms(bool is_l) { return is_l?lt_atoms():gt_atoms(); } app* const* e_atoms(bool is_l) { return is_l?le_atoms():ge_atoms(); } app* const* atoms(bool is_strict, bool is_l) { return is_strict?t_atoms(is_l):e_atoms(is_l); } unsigned div_size() const { return m_div_terms.size(); } app* const* div_atoms() { return m_div_atoms.c_ptr(); } rational const* div_coeffs() { return m_div_coeffs.c_ptr(); } expr* const* div_terms() { return m_div_terms.c_ptr(); } rational const* divisors() { return m_div_divisors.c_ptr(); } bool div_z(rational & d, app_ref& z_bv, app_ref& z) { if (m_div_z.get()) { z = m_div_z; z_bv = to_app(z->get_arg(0)); d = m_d; return true; } if (m_div_terms.empty() && m_nested_div_terms.empty()) { return false; } m_d = rational(1); for (unsigned i = 0; i < m_div_divisors.size(); ++i) { m_d = lcm(m_div_divisors[i], m_d); } for (unsigned i = 0; i < m_nested_div_divisors.size(); ++i) { m_d = lcm(m_nested_div_divisors[i], m_d); } if (abs(m_d).is_one()) { return false; } m_util.mk_bounded_var(m_d, z_bv, m_div_z); z = m_div_z; d = m_d; return true; } unsigned nested_div_size() const { return m_nested_div_terms.size(); } app* nested_div_atom(unsigned idx) { return m_nested_div_atoms[idx].get(); } rational const& nested_div_coeff(unsigned idx) { return m_nested_div_coeffs[idx]; } expr* nested_div_term(unsigned idx) { return m_nested_div_terms[idx].get(); } rational const& nested_divisor(unsigned idx) { return m_nested_div_divisors[idx]; } app* nested_div_z(unsigned idx) { return m_nested_div_z[idx].get(); } app* nested_div_z_bv(unsigned idx) { return to_app(m_nested_div_z[idx]->get_arg(0)); } void reset() { m_lt_terms.reset(); m_gt_terms.reset(); m_ge_terms.reset(); m_le_terms.reset(); m_gt_coeffs.reset(); m_lt_coeffs.reset(); m_ge_coeffs.reset(); m_le_coeffs.reset(); m_lt_atoms.reset(); m_gt_atoms.reset(); m_le_atoms.reset(); m_ge_atoms.reset(); m_div_terms.reset(); m_div_coeffs.reset(); m_div_divisors.reset(); m_div_atoms.reset(); m_div_z = 0; m_nested_div_terms.reset(); m_nested_div_coeffs.reset(); m_nested_div_divisors.reset(); m_nested_div_atoms.reset(); m_nested_div_z.reset(); } private: bool get_nested_divs(contains_app& contains_x, app* a) { ast_manager& m = m_util.get_manager(); ptr_vector todo; todo.push_back(a); rational k1, k2; expr_ref rest(m); while (!todo.empty()) { expr* e = todo.back(); todo.pop_back(); if (m_mark.is_marked(e)) { continue; } m_mark.mark(e, true); if (!contains_x(e)) { continue; } if (contains_x.x() == e) { return false; } if (!is_app(e)) { return false; } a = to_app(e); if (m_util.m_arith.is_mod(e) && m_util.m_arith.is_numeral(to_app(e)->get_arg(1), k1) && m_util.get_coeff(contains_x, to_app(e)->get_arg(0), k2, rest)) { app_ref z(m), z_bv(m); m_util.mk_bounded_var(k1, z_bv, z); m_nested_div_terms.push_back(rest); m_nested_div_divisors.push_back(k1); m_nested_div_coeffs.push_back(k2); m_nested_div_atoms.push_back(a); m_nested_div_z.push_back(z); continue; } unsigned num_args = a->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { todo.push_back(a->get_arg(i)); } } return true; } bool get_le_bound(contains_app& contains_x, app* a) { ast_manager& m = m_util.get_manager(); expr_ref p(m), rest(m); rational k; if (m_util.is_le(a, p) && m_util.get_coeff(contains_x, p, k, rest)) { // k*x + rest <= 0 if (m_util.is_real(contains_x.x())) { m_util.mk_div(rest, abs(k), rest); k = k.is_pos()?rational::one():rational::minus_one(); } if (k.is_neg()) { m_le_terms.push_back(rest); m_le_coeffs.push_back(k); m_le_atoms.push_back(a); } else { m_ge_terms.push_back(rest); m_ge_coeffs.push_back(k); m_ge_atoms.push_back(a); } return true; } return false; } bool get_lt_bound(contains_app& contains_x, app* a) { ast_manager& m = m_util.get_manager(); expr_ref p(m), rest(m), na(m); rational k; if (m_util.is_lt(a, p) && m_util.get_coeff(contains_x, p, k, rest)) { // k*x + rest < 0 } else if (m_util.is_neg(a, na) && is_app(na) && m_util.is_ge(to_app(na), p) && m_util.get_coeff(contains_x, p, k, rest)) { // // not (k*x + rest >= 0) // <=> // k*x + rest < 0 // } else { return false; } SASSERT(m_util.is_real(contains_x.x())); m_util.mk_div(rest, abs(k), rest); if (k.is_neg()) { m_lt_terms.push_back(rest); m_lt_coeffs.push_back(rational::minus_one()); m_lt_atoms.push_back(a); } else { m_gt_terms.push_back(rest); m_gt_coeffs.push_back(rational::one()); m_gt_atoms.push_back(a); } return true; } bool get_divides(contains_app& contains_x, app* a) { ast_manager& m = m_util.get_manager(); expr_ref p(m), rest(m); app_ref a2(m); numeral k, k2; if (m_util.is_divides(a, k, p) && m_util.get_coeff(contains_x, p, k2, rest)) { m_div_terms.push_back(rest); m_div_divisors.push_back(k); m_div_coeffs.push_back(k2); m_div_atoms.push_back(a); return true; } if (m_util.is_not_divides(a, a2, k, p) && m_util.get_coeff(contains_x, p, k2, rest)) { m_div_terms.push_back(rest); m_div_divisors.push_back(k); m_div_coeffs.push_back(k2); m_div_atoms.push_back(a2); return true; } return false; } public: void display(std::ostream& out) { ast_manager& m = m_util.get_manager(); for (unsigned i = 0; i < lt_size(); ++i) { out << mk_pp(lt()[i], m) << " < 0\n"; } for (unsigned i = 0; i < le_size(); ++i) { out << mk_pp(le()[i], m) << " < 0\n"; } for (unsigned i = 0; i < gt_size(); ++i) { out << mk_pp(gt()[i], m) << " < 0\n"; } for (unsigned i = 0; i < ge_size(); ++i) { out << mk_pp(ge()[i], m) << " < 0\n"; } } }; class x_subst { arith_qe_util& m_super; expr_ref m_t; rational m_coeff; public: x_subst(arith_qe_util& s): m_super(s), m_t(s.get_manager()), m_coeff(rational::one()) {} void set_term(expr* t) { m_t = t; } void set_coeff(rational const& k) { m_coeff = k; } expr* get_term() const { return m_t; } rational get_coeff() const { return m_coeff; } expr_ref mk_term(rational const& c, expr* t) { // return t + c*m_t ast_manager& m = m_super.get_manager(); if (m_t.get()) { return expr_ref(m_super.mk_add(m_super.mk_mul(c, m_t), t), m); } else { return expr_ref(t, m); } } rational mk_coeff(rational const& k) { return k * m_coeff; } }; struct branch_formula { expr* m_fml; app* m_var; unsigned m_branch; expr* m_result; rational m_coeff; expr* m_term; branch_formula(): m_fml(0), m_var(0), m_branch(0), m_result(0), m_term(0) {} branch_formula(expr* fml, app* var, unsigned b, expr* r, rational coeff, expr* term): m_fml(fml), m_var(var), m_branch(b), m_result(r), m_coeff(coeff), m_term(term) {} unsigned mk_hash() const { return mk_mix(m_fml?m_fml->hash():0, m_var?m_var->hash():0, m_branch); } bool mk_eq(branch_formula const& other) const { return m_fml == other.m_fml && m_var == other.m_var && m_branch == other.m_branch; } struct hash { typedef branch_formula data; unsigned operator()(data const& d) const { return d.mk_hash(); } }; struct eq { typedef branch_formula data; bool operator()(data const& x, data const& y) const { return x.mk_eq(y); } }; }; class arith_plugin : public qe_solver_plugin { typedef obj_pair_map bounds_cache; typedef obj_pair_map resolve_cache; typedef hashtable subst_cache; arith_qe_util m_util; expr_ref_vector m_trail; bounds_cache m_bounds_cache; subst_cache m_subst; public: arith_plugin(i_solver_context& ctx, ast_manager& m, smt_params& p): qe_solver_plugin(m, m.mk_family_id("arith"), ctx), m_util(m, p, ctx), m_trail(m) {} ~arith_plugin() { bounds_cache::iterator it = m_bounds_cache.begin(), end = m_bounds_cache.end(); for (; it != end; ++it) { dealloc(it->get_value()); } } virtual void assign(contains_app& contains_x, expr* fml, rational const& vl) { SASSERT(vl.is_unsigned()); app* x = contains_x.x(); unsigned v = vl.get_unsigned(); expr_ref result(fml, m); unsigned t_size, e_size; x_subst x_t(m_util); if (get_cache(x, fml, v, result)) { return; } bounds_proc& bounds = get_bounds(x, fml); bool is_lower = get_bound_sizes(bounds, x, t_size, e_size); assign_nested_divs(contains_x, bounds, result); assign_divs(contains_x, bounds, x_t, result); //assign_all(contains_x, fml); if (v == 0) { // // index is for the infinity case. // assert v => ~(x <= t) each t // assert v => (x >= s) each s // mk_non_bounds(bounds, true, is_lower, result); mk_non_bounds(bounds, false, is_lower, result); mk_non_resolve(bounds, true, is_lower, result); mk_non_resolve(bounds, false, is_lower, result); m_util.simplify(result); add_cache(x, fml, v, result, x_t.get_coeff(), x_t.get_term()); TRACE("qe", tout << vl << " " << mk_pp(x, m) << " infinite case\n"; tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); return; } unsigned index = v-1; bool is_strict = e_size <= index; bool is_eq = false; SASSERT(index < t_size + e_size); if (is_strict) { index -= e_size; TRACE("qe_verbose", bounds.display(tout); ); } else if (m_util.is_real(x)) { SASSERT(0 == (e_size & 0x1)); is_eq = (0 == (index & 0x1)); index /= 2; e_size /= 2; } SASSERT(is_strict || index < e_size); SASSERT(!is_strict || index < t_size); // // index is for the upper/lower-bound case. // assert v => (x <= t_i) // assert v => (x <= t_j => t_i <= t_j), add new atom to stack. // assert v => (x >= s => s <= t_i), add new atom to stack. // // assert v => (x < t_j => t_i < t_j) // SASSERT(index < bounds.size(is_strict, is_lower)); expr_ref t(bounds.exprs(is_strict, is_lower)[index], m); rational a = bounds.coeffs(is_strict, is_lower)[index]; mk_bounds(bounds, x, true, is_eq, is_strict, is_lower, index, a, t, result); mk_bounds(bounds, x, false, is_eq, is_strict, is_lower, index, a, t, result); t = x_t.mk_term(a, t); a = x_t.mk_coeff(a); mk_resolve(bounds, x, x_t, true, is_eq, is_strict, is_lower, index, a, t, result); mk_resolve(bounds, x, x_t, false, is_eq, is_strict, is_lower, index, a, t, result); m_util.simplify(result); add_cache(x, fml, v, result, x_t.get_coeff(), x_t.get_term()); TRACE("qe", { tout << vl << " " << mk_pp(bounds.atoms(is_strict, is_lower)[index],m) << "\n"; tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n"; } ); } virtual bool get_num_branches(contains_app& contains_x, expr* fml, rational& nb) { app* x = contains_x.x(); if (!update_bounds(contains_x, fml)) { return false; } bounds_proc& bounds = get_bounds(x, fml); unsigned t_size, e_size; get_bound_sizes(bounds, x, t_size, e_size); nb = rational(t_size + e_size + 1); return true; } virtual void subst(contains_app& contains_x, rational const& vl, expr_ref& fml, expr_ref* def) { SASSERT(vl.is_unsigned()); if (def) { get_def(contains_x, vl.get_unsigned(), fml, *def); } VERIFY(get_cache(contains_x.x(), fml, vl.get_unsigned(), fml)); TRACE("qe", tout << mk_pp(contains_x.x(), m) << " " << vl << "\n" << mk_pp(fml, m) << "\n";); } virtual bool project(contains_app& x, model_ref& model, expr_ref& fml) { if (!update_bounds(x, fml)) { TRACE("qe", tout << mk_pp(x.x(), m) << " failed to update bounds\n";); return false; } if (m_util.m_arith.is_real(x.x())) { return project_real(x, model, fml); } else { return project_int(x, model, fml); } } virtual unsigned get_weight(contains_app& contains_x, expr* fml) { return 2; } virtual bool solve(conj_enum& conjs, expr* fml) { return m_util.solve(conjs, fml); } virtual bool mk_atom(expr* e, bool p, expr_ref& result) { return m_util.mk_atom(e, p, result); } virtual bool is_uninterpreted(app* f) { switch(f->get_decl_kind()) { case OP_NUM: case OP_LE: case OP_LT: case OP_GE: case OP_GT: case OP_ADD: case OP_SUB: case OP_UMINUS: return false; case OP_MOD: if(m_util.m_arith.is_numeral(f->get_arg(1))) { return false; } return true; case OP_MUL: { arith_util& a = m_util.m_arith; expr* m, *n; if (a.is_mul(f, m, n) && (a.is_numeral(m) || a.is_numeral(n))) { return false; } return true; } default: return true; } } private: /** \brief Compute least upper/greatest lower bounds for x. Assume: (not (= k 0)) (<= 0 (mod m k)) (< (mod m k) (abs k)) (= m (+ (* k (div m k)) (mod m k))) i.e. k * (e div k) + (e mod k) = e When k is positive, least upper bound for x such that: k*x <= e is e div k When k is negative, greatest lower bound for x such that k*x <= e is e div k k * (e div k) + (e mod k) = e */ expr_ref mk_idiv(expr* e, numeral k) { SASSERT(!k.is_zero()); arith_util& a = m_util.m_arith; if (k.is_one()) { return expr_ref(e, m); } if (k.is_minus_one()) { return expr_ref(a.mk_uminus(e), m); } SASSERT(a.is_int(e)); return expr_ref(a.mk_idiv(e, a.mk_numeral(k, true)), m); } void get_def(contains_app& contains_x, unsigned v, expr* fml, expr_ref& def) { app* x = contains_x.x(); x_subst x_t(m_util); bounds_proc& bounds = get_bounds(x, fml); branch_formula bf; VERIFY (m_subst.find(branch_formula(fml, x, v, 0, rational::zero(), 0), bf)); x_t.set_term(bf.m_term); x_t.set_coeff(bf.m_coeff); // x is of the form: x_t.get_coeff()*x' + x_t.get_term() CTRACE("qe", x_t.get_term(), tout << x_t.get_coeff() << " " << mk_pp(x_t.get_term(), m) << "\n";); // // a*x + t <= 0 // a*(c*x' + s) + t <= 0 // a*c*x' + a*s + t <= 0 // unsigned t_size, e_size, sz; bool is_lower = get_bound_sizes(bounds, x, t_size, e_size); bool is_strict; if (v == 0) { is_strict = false; sz = bounds.size(is_strict, !is_lower); expr_ref_vector terms(m); if (sz == 0) { terms.push_back(m_util.mk_zero(x)); } for (unsigned i = 0; i < sz; ++i) { // a*x + term <= 0 expr_ref term(bounds.exprs(is_strict, !is_lower)[i], m); rational a = bounds.coeffs(is_strict, !is_lower)[i]; if (x_t.get_term()) { // x := coeff * x' + s // solve instead for // a*coeff*x' + term + a*s <= 0 TRACE("qe", tout << x_t.get_coeff() << "* " << mk_pp(x,m) << " + " << mk_pp(x_t.get_term(), m) << "\n";); SASSERT(x_t.get_coeff().is_pos()); term = m_util.mk_add(term, m_util.mk_mul(a, x_t.get_term())); a = a * x_t.get_coeff(); } TRACE("qe", tout << a << "* " << mk_pp(x,m) << " + " << mk_pp(term, m) << " <= 0\n";); SASSERT(a.is_int()); SASSERT(is_lower == a.is_pos()); // a*x + t <= 0 // <= // x <= -t div a + 1 term = m_util.mk_uminus(term); term = mk_idiv(term, a); terms.push_back(term); TRACE("qe", tout << "a: " << a << " term: " << mk_pp(term, m) << "\n";); } is_strict = true; sz = bounds.size(is_strict, !is_lower); for (unsigned i = 0; i < sz; ++i) { expr_ref term(bounds.exprs(is_strict, !is_lower)[i], m); SASSERT(abs(bounds.coeffs(is_strict, !is_lower)[i]).is_one()); // if (is_lower) { // x + t < 0 // <= // x <= -t -1 term = m_util.mk_uminus(m_util.mk_add(term, m_util.mk_one(x))); } else { // -x + t < 0 // <= // t + 1 <= x term = m_util.mk_add(term, m_util.mk_one(x)); } terms.push_back(term); } if (is_lower) { def = m_util.mk_min(terms.size(), terms.c_ptr()); } else { def = m_util.mk_max(terms.size(), terms.c_ptr()); } if (x_t.get_term()) { // x := coeff * x + s TRACE("qe", tout << x_t.get_coeff() << "* " << mk_pp(x,m) << " + " << mk_pp(x_t.get_term(), m) << "\n";); def = m_util.mk_add(m_util.mk_mul(x_t.get_coeff(), def), x_t.get_term()); } m_util.simplify(def); return; } --v; is_strict = e_size <= v; SASSERT(v < t_size + e_size); if (is_strict) { v -= e_size; TRACE("qe_verbose", bounds.display(tout); ); } else if (m_util.is_real(x)) { SASSERT(0 == (e_size & 0x1)); v /= 2; e_size /= 2; } SASSERT(is_strict || v < e_size); SASSERT(!is_strict || v < t_size); // // index is for the upper/lower-bound case. // assert v => (x <= t_i) // SASSERT(v < bounds.size(is_strict, is_lower)); def = bounds.exprs(is_strict, is_lower)[v]; rational a = bounds.coeffs(is_strict, is_lower)[v]; if (x_t.get_term()) { // x := coeff * x' + s // solve instead for // a*coeff*x' + term + a*s <= 0 TRACE("qe", tout << x_t.get_coeff() << "* " << mk_pp(x,m) << " + " << mk_pp(x_t.get_term(), m) << "\n";); SASSERT(x_t.get_coeff().is_pos()); def = m_util.mk_add(def, m_util.mk_mul(a, x_t.get_term())); a = a * x_t.get_coeff(); } SASSERT(a.is_int()); SASSERT(is_lower != a.is_pos()); // a*x + t <= 0 // <= // x <= -t div a def = m_util.mk_uminus(def); def = mk_idiv(def, a); if (x_t.get_term()) { // x := coeff * x + s def = m_util.mk_add(m_util.mk_mul(x_t.get_coeff(), def), x_t.get_term()); } if (is_strict) { SASSERT(m_util.m_arith.is_real(x)); // We actually want a supremum, such that dual inequalities are satisfied. // i.e. for every dual inequality , if the dual bound is feasible, make sure to // choose a value in the feasible range. def = m_util.mk_sub(def, m_util.mk_one(x)); } m_util.simplify(def); TRACE("qe", tout << "TBD (for Real): " << a << " " << mk_pp(def, m) << "\n";); } expr_ref mk_not(expr* e) { expr* r; if (m.is_not(e,r)) { return expr_ref(r, m); } return expr_ref(m.mk_not(e), m); } // // Projection function for x of type real. // TBD: model-based selection soundness/completeness? // when model selects bound different from what is the smaller half, what then? // shouldn't we find candidate among either lt or gt, and then if both can be found // only then select which one to go with. Then assign has to be context-aware. // Perhaps not really: the model is used as a hint. // bool project_real(contains_app& x, model_ref& model, expr_ref& fml) { SASSERT(m_util.m_arith.is_real(x.x())); model_evaluator model_eval(*model); bounds_proc& bounds = get_bounds(x.x(), fml); bool is_lower = bounds.le_size() + bounds.lt_size() < bounds.ge_size() + bounds.gt_size(); unsigned e_size = bounds.e_size(is_lower); numeral bound1, bound2, vl, x_val; unsigned idx1, idx2; bool found1 = find_min_max(is_lower, false, bounds, model_eval, bound1, idx1); bool found2 = find_min_max(is_lower, true, bounds, model_eval, bound2, idx2); if (!found1 && !found2) { vl = numeral(0); } else if (found2 && (!found1 || bound2 <= bound1)) { // strict indices come after non-strict indices. There // is a pair of index values for non-strict inequalities // corresponding to the disjunction (x < t || x = t) vl = numeral(1 + 2*e_size + idx2); } else if (found1 && (!found2 || bound1 < bound2)) { expr_ref val_x(m); model_eval(x.x(), val_x); VERIFY(m_util.m_arith.is_numeral(val_x, x_val)); if (x_val == bound1) { vl = numeral(1 + 2*idx1); // even indicates equality between x and bound. } else { vl = numeral(1 + 2*idx1 + 1); // odd indicates strict bound. } } assign(x, fml, vl); subst(x, vl, fml, 0); TRACE("qe", tout << mk_pp(fml, m) << "\n";); return true; } bool project_int(contains_app& x, model_ref& model, expr_ref& fml) { model_evaluator model_eval(*model); bounds_proc& bounds = get_bounds(x.x(), fml); SASSERT(m_util.m_arith.is_int(x.x())); SASSERT(bounds.lt_size() == 0 && bounds.gt_size() == 0); bool is_lower = bounds.le_size() < bounds.ge_size(); numeral bound, vl, x_val; unsigned idx = bounds.le_size() + bounds.ge_size(); bool found = find_min_max(is_lower, false, bounds, model_eval, bound, idx); if (found) { SASSERT(idx < bounds.size(false, is_lower)); vl = numeral(1 + idx); } else { vl = numeral(0); } assign(x, fml, vl); subst(x, vl, fml, 0); TRACE("qe", tout << mk_pp(fml, m) << "\n";); return true; } bool find_min_max(bool is_lower, bool is_strict, bounds_proc& bounds, model_evaluator& eval, rational& bound, unsigned& idx) { bool found = false; unsigned num_bounds = bounds.size(is_strict, is_lower); rational num; for (unsigned i = 0; i < num_bounds; ++i) { expr_ref vl(m); eval(bounds.atoms(is_strict, is_lower)[i], vl); if (!m.is_true(vl)) { continue; } eval(bounds.exprs(is_strict, is_lower)[i], vl); VERIFY(m_util.m_arith.is_numeral(vl, num)); num /= abs(bounds.coeffs(is_strict,is_lower)[i]); if (found) { if (is_lower?(num < bound):(num > bound)) { bound = num; idx = i; } } else { found = true; idx = i; bound = num; } } return found; } bool get_bound_sizes(bounds_proc& bounds, app* x, unsigned& t_size, unsigned& e_size) { unsigned le_size = bounds.le_size(); unsigned ge_size = bounds.ge_size(); if (m_util.is_real(x)) { le_size *= 2; ge_size *= 2; } if (le_size + bounds.lt_size() < ge_size + bounds.gt_size()) { e_size = le_size; t_size = bounds.lt_size(); return true; } else { e_size = ge_size; t_size = bounds.gt_size(); return false; } } void add_cache(app* x, expr* fml, unsigned v, expr* result, rational coeff, expr* term) { m_trail.push_back(x); m_trail.push_back(fml); m_trail.push_back(result); if (term) m_trail.push_back(term); m_subst.insert(branch_formula(fml, x, v, result, coeff, term)); } bool get_cache(app* x, expr* fml, unsigned v, expr_ref& result) { branch_formula bf; if (!m_subst.find(branch_formula(fml, x, v, 0, rational::zero(), 0), bf)) { return false; } SASSERT(bf.m_result); result = bf.m_result; return true; } void assign_divs(contains_app& contains_x, bounds_proc& bounds, x_subst& x_t, expr_ref& result) { app* x = contains_x.x(); app_ref z(m), z_bv(m); rational d; if (!bounds.div_z(d, z_bv, z)) { return; } m_ctx.add_var(z_bv); // // assert // z < d // d | (x - z) // (c | ax + t <-> c | az + t) for each divisor. // // z < d expr* z_lt_d = m_util.m_arith.mk_le(z, m_util.m_arith.mk_numeral(d-rational(1), true)); m_ctx.add_constraint(false, z_lt_d); TRACE("qe", tout << mk_pp(z_lt_d, m) << "\n";); // result <- result & z <= d - 1 SASSERT(!abs(d).is_one()); rational d1 = d - rational(1); expr_ref tmp(m); m_util.m_arith_rewriter.mk_le(z, m_util.m_arith.mk_numeral(d1, true), tmp); m_util.m_bool_rewriter.mk_and(result, tmp, result); // d | (x - z) expr_ref t1(m), new_atom(m); t1 = m_util.mk_sub(x, z); m_util.mk_divides(d, t1, new_atom); m_ctx.add_constraint(false, new_atom); TRACE("qe", tout << mk_pp(new_atom, m) << "\n";); // (c | ax + t <-> c | az + t) for each divisor. mk_div_equivs(bounds, z, result); TRACE("qe", tout << mk_pp(result, m) << "\n";); // update x_t to map x |-> dx + z x_t.set_term(z); x_t.set_coeff(d); } // // (c | ax + t <-> c | az + t) for each divisor. // void mk_div_equivs(bounds_proc& bounds, expr* z, expr_ref& result) { unsigned sz = bounds.div_size(); app* const* atoms = bounds.div_atoms(); rational const* coeffs = bounds.div_coeffs(); expr* const* terms = bounds.div_terms(); rational const* divisors = bounds.divisors(); expr_ref new_atom(m), t1(m); for (unsigned i = 0; i < sz; ++i) { app* atm = atoms[i]; t1 = m_util.mk_add(m_util.mk_mul(coeffs[i], z), terms[i]); m_util.mk_divides(divisors[i], t1, new_atom); m_util.m_replace.apply_substitution(atm, new_atom.get(), result); m_ctx.add_constraint(false, mk_not(atm), new_atom); m_ctx.add_constraint(false, mk_not(new_atom), atm); } } void assign_nested_divs(contains_app& contains_x, bounds_proc& bounds, expr_ref& result) { unsigned num_nested_divs = bounds.nested_div_size(); if (num_nested_divs == 0) { return; } app_ref z(m), z_bv(m); rational d; VERIFY (bounds.div_z(d, z_bv, z)); for (unsigned i = 0; i < num_nested_divs; ++i) { // // mod_term = arg_0 mod k // app* atm = bounds.nested_div_atom(i); rational const& k = bounds.nested_divisor(i); app* z1_bv = bounds.nested_div_z_bv(i); app* z1 = bounds.nested_div_z(i); m_ctx.add_var(z1_bv); // // assert // z < k // (coeff*x + rest - z) mod k == 0 // expr* z_lt_k = m_util.m_arith.mk_le(z1, m_util.m_arith.mk_numeral(k-rational(1), true)); m_ctx.add_constraint(false, z_lt_k); expr* e1 = m_util.m_arith.mk_sub(atm->get_arg(0), z1); expr* e2 = atm->get_arg(1); expr_ref mod_term2(m_util.m_arith.mk_mod(e1, e2), m); m_util.simplify(mod_term2); m_ctx.add_constraint(false, m.mk_eq(mod_term2, m_util.mk_zero(mod_term2))); m_util.m_replace.apply_substitution(atm, z1, result); // // conjoin (coeff*z + rest - z1) mod k == 0 to result // expr_ref mod_eq(m), tmp1(m), tmp2(m); tmp2 = m_util.mk_numeral(bounds.nested_div_coeff(i), true); tmp1 = m_util.m_arith.mk_mul(tmp2, z1); tmp2 = m_util.m_arith.mk_sub(bounds.nested_div_term(i), z); tmp2 = m_util.m_arith.mk_add(tmp1, tmp2); tmp1 = m_util.m_arith.mk_mod(tmp2, bounds.nested_div_atom(i)->get_arg(1)); mod_eq = m.mk_eq(tmp1, m_util.mk_zero(z)); m_util.simplify(mod_eq); result = m.mk_and(result, mod_eq); TRACE("qe", tout << mk_pp(mod_eq, m) << "\n";); } } bounds_proc& get_bounds(app* x, expr* fml) { bounds_proc* result = 0; VERIFY (m_bounds_cache.find(x, fml, result)); return *result; } void mk_non_bounds(bounds_proc& bounds, bool is_strict, bool is_lower, expr_ref& result) { unsigned sz = bounds.size(is_strict, is_lower); for (unsigned i = 0; i < sz; ++i) { app* e = bounds.atoms(is_strict, is_lower)[i]; m_ctx.add_constraint(true, mk_not(e)); m_util.m_replace.apply_substitution(e, m.mk_false(), result); } } void mk_non_resolve(bounds_proc& bounds, bool is_strict, bool is_lower, expr_ref& result) { unsigned sz = bounds.size(is_strict, !is_lower); for (unsigned i = 0; i < sz; ++i) { app* e = bounds.atoms(is_strict, !is_lower)[i]; m_ctx.add_constraint(true, e); m_util.m_replace.apply_substitution(e, m.mk_true(), result); } } // // phi[x < t, x <= s, x >= u, x > v] // // x = +oo: phi[false, false, true, true] // x < t: phi[true, t-e < s, t - e >= u, t - e > v] == phi[true, t <= s, t > u, t > v] // x < s: phi[s-e < t, true, s - e >= u, s - e > v] == phi[s <= t, true, s > u, s > v] // x = s: phi[s < t, true, s >= u, s > v] // // assert // path1 => x < t // bounds: // path1 => x < t' => t < t' when index(t') < index(t) // path1 => x < t' => t <= t' when index(t') >= index(t) // path1 => x <= s => t <= s // resolve: // path1 => x >= u => t > u // path1 => x > v => t > v // symmetry reduction: // // // path2 => x <= s // bounds: // path2 => x < s => x < t => s <= t // path2 => x = s => x < t => s < t // path2 => x <= s => x <= s' => s < s' when index(s') < index(s) // path2 => x <= s => x <= s' => s <= s' when index(s') >= index(s) // resolve: // path2 => x < s => x >= u => s > u // path2 => x = s => x >= u => s >= u // path2 => x <= s => x > v => s > v // void mk_bound(bool is_strict, bool is_lower, rational const& a, expr* t, rational const& b, expr* s, expr_ref& result) { if (is_strict) { if (is_lower) { // b*t > a*s m_util.mk_strict_bound(b, s, a, t, result); } else { // b*t < a*s m_util.mk_strict_bound(a, t, b, s, result); } } else { if (is_lower) { // b*t >= a*s m_util.mk_bound(b, s, a, t, result); } else { // b*t <= a*s m_util.mk_bound(a, t, b, s, result); } } m_util.simplify(result); TRACE("qe", tout << (is_strict?"strict":"non-strict") << "\n"; tout << (is_lower?"is-lower":"is-upper") << "\n"; tout << "a: " << a << " " << mk_pp(t, m) << "\n"; tout << "b: " << b << " " << mk_pp(s, m) << "\n"; tout << mk_pp(result, m) << "\n";); } // // a*x <= t, a*x < t // /* - bounds - add_assertion - flag whether to add side-effect to state - x - the variable to be eliminated - is_strict - whether to loop over strict inequalities - is_eq_ctx - whether non-strict inequality is to be treated as equality case. - is_strict_ctx - whether 'atm' is a strict inequality - is_lower - whether 'x' is given a lower-bound in 'atm' - index - index of 'atm' in 'bounds' 'atm = bounds[index]' - a - coefficient to 'x' in 'atm' - t - upper/lower bound to 'x' in 'atm' */ void mk_bounds(bounds_proc& bounds, app* x, bool is_strict, bool is_eq_ctx, bool is_strict_ctx, bool is_lower, unsigned index, rational const& a, expr* t, expr_ref& result) { TRACE("qe", tout << mk_pp(t, m) << "\n";); SASSERT(!is_eq_ctx || !is_strict_ctx); unsigned sz = bounds.size(is_strict, is_lower); expr_ref tmp(m), eq(m); bool same_strict = (is_strict == is_strict_ctx); bool non_strict_real = m_util.is_real(x) && !is_strict_ctx; app* atm = bounds.atoms(is_strict_ctx, is_lower)[index]; for (unsigned i = 0; i < sz; ++i) { app* e = bounds.atoms(is_strict, is_lower)[i]; expr_ref s(bounds.exprs(is_strict, is_lower)[i], m); rational b = bounds.coeffs(is_strict, is_lower)[i]; if (same_strict && i == index) { if (non_strict_real) { m_util.mk_eq(a, x, t, eq); TRACE("qe", tout << "a:" << a << " x: " << mk_pp(x, m) << "t: " << mk_pp(t, m) << " eq: " << mk_pp(eq, m) << "\n";); if (is_eq_ctx) { m_ctx.add_constraint(true, eq); } else { m_ctx.add_constraint(true, mk_not(eq)); m_ctx.add_constraint(true, e); } } else { m_ctx.add_constraint(true, e); } m_util.m_replace.apply_substitution(atm, m.mk_true(), result); continue; } // // Break symmetries by using index: // bounds before me are strictly larger. // Cases: // ax <= t & ax != t & bx < s => bt <= as // ax <= t & ax = t & bx < s => bt < as // bx <= s => bt < as or bt <= as depending on symmetry // bool result_is_strict = (non_strict_real && is_eq_ctx && is_strict) || (same_strict && i < index); mk_bound(result_is_strict, is_lower, a, t, b, s, tmp); m_util.m_replace.apply_substitution(e, tmp.get(), result); TRACE("qe", tout << (result_is_strict?"strict result":"non-strict result") << "\n"; tout << (is_strict?"strict":"non-strict") << "\n"; tout << mk_pp(atm, m) << " & "; tout << mk_pp(e, m) << " --> "; tout << mk_pp(tmp.get(), m) << "\n";); m_ctx.add_constraint(true, mk_not(e), tmp); } } // x <= t // x != t => x >= u => t > u // x = t => x >= u => t >= u void mk_resolve(bounds_proc& bounds, app* x, x_subst& x_t, bool is_strict, bool is_eq_ctx, bool is_strict_ctx, bool is_lower, unsigned index, rational const& a, expr* t, expr_ref& result) { expr_ref tmp(m); unsigned sz = bounds.size(is_strict, !is_lower); bool is_strict_real = !is_eq_ctx && m_util.is_real(x) && !is_strict_ctx; bool strict_resolve = is_strict || is_strict_ctx || is_strict_real; app* atm = bounds.atoms(is_strict_ctx, is_lower)[index]; for (unsigned i = 0; i < sz; ++i) { app* e = bounds.atoms(is_strict, !is_lower)[i]; expr_ref s(bounds.exprs(is_strict, !is_lower)[i], m); rational b = bounds.coeffs(is_strict, !is_lower)[i]; SASSERT(!b.is_zero()); SASSERT(b.is_pos() != a.is_pos()); s = x_t.mk_term(b, s); b = x_t.mk_coeff(b); m_util.mk_resolve(x, strict_resolve, a, t, b, s, tmp); expr_ref save_result(result); m_util.m_replace.apply_substitution(e, tmp.get(), result); m_ctx.add_constraint(true, mk_not(e), tmp); TRACE("qe_verbose", tout << mk_pp(atm, m) << " "; tout << mk_pp(e, m) << " ==>\n"; tout << mk_pp(tmp, m) << "\n"; tout << "old fml: " << mk_pp(save_result, m) << "\n"; tout << "new fml: " << mk_pp(result, m) << "\n"; ); } } bool update_bounds(bounds_proc& bounds, contains_app& contains_x, expr* fml, atom_set const& tbl, bool is_pos) { app_ref tmp(m); atom_set::iterator it = tbl.begin(), end = tbl.end(); for (; it != end; ++it) { app* e = *it; if (!contains_x(e)) { continue; } if (!is_pos) { SASSERT(!m.is_not(e)); tmp = m.mk_not(e); e = tmp; } if (!bounds.get_bound(contains_x, e)) { return false; } } return true; } bool update_bounds(contains_app& contains_x, expr* fml) { bounds_proc* bounds = 0; if (m_bounds_cache.find(contains_x.x(), fml, bounds)) { return true; } bounds = alloc(bounds_proc, m_util); if (!update_bounds(*bounds, contains_x, fml, m_ctx.pos_atoms(), true)) { dealloc(bounds); return false; } if (!update_bounds(*bounds, contains_x, fml, m_ctx.neg_atoms(), false)) { dealloc(bounds); return false; } m_trail.push_back(contains_x.x()); m_trail.push_back(fml); m_bounds_cache.insert(contains_x.x(), fml, bounds); return true; } }; // --------------------- // non-linear arithmetic class nlarith_plugin : public qe_solver_plugin { typedef obj_map weight_m; typedef obj_pair_map bcs_t; typedef obj_map weights_t; bcs_t m_cache; weights_t m_weights; th_rewriter m_rewriter; nlarith::util m_util; expr_safe_replace m_replace; expr_ref_vector m_trail; factor_rewriter_star m_factor_rw; bool m_produce_models; public: nlarith_plugin(i_solver_context& ctx, ast_manager& m, bool produce_models) : qe_solver_plugin(m, m.mk_family_id("arith"), ctx), m_rewriter(m), m_util(m), m_replace(m), m_trail(m), m_factor_rw(m), m_produce_models(produce_models) { TRACE("qe", tout << "produce models: " << produce_models << "\n";); m_util.set_enable_linear(true); // (produce_models); } virtual ~nlarith_plugin() { bcs_t::iterator it = m_cache.begin(), end = m_cache.end(); for (; it != end; ++it) { dealloc(it->get_value()); } weights_t::iterator it2 = m_weights.begin(), e2 = m_weights.end(); for (; it2 != e2; ++it2) { dealloc(it2->get_value()); } } virtual bool simplify(expr_ref& fml) { expr_ref tmp(m), tmp2(m); m_factor_rw(fml, tmp); m_rewriter(tmp, tmp2); if (fml.get() != tmp2.get()) { fml = tmp2; return true; } return false; } virtual void assign(contains_app& x, expr* fml, rational const& vl) { nlarith::branch_conditions *brs; VERIFY (m_cache.find(x.x(), fml, brs)); SASSERT(vl.is_unsigned()); SASSERT(vl.get_unsigned() < brs->size()); expr* branch_fml = brs->branches(vl.get_unsigned()); expr_ref result(m), tmp(m); m_factor_rw(branch_fml, tmp); m_rewriter(tmp, result); TRACE("qe", tout << vl << " " << mk_pp(result.get(), m) << "\n";); m_ctx.add_constraint(true, result); } virtual bool get_num_branches(contains_app& x, expr* fml, rational& num_branches) { nlarith::branch_conditions *brs; if (m_cache.find(x.x(), fml, brs)) { num_branches = rational(brs->size()); return true; } expr_ref_vector lits(m); update_bounds(lits, m_ctx.pos_atoms(), true); update_bounds(lits, m_ctx.neg_atoms(), false); brs = alloc(nlarith::branch_conditions, m); TRACE("nlarith", tout << mk_pp(fml, m) << "\n";); if (!m_util.create_branches(x.x(), lits.size(), lits.c_ptr(), *brs)) { TRACE("nlarith", tout << "no branches for " << mk_pp(x.x(), m) << "\n";); dealloc(brs); return false; } num_branches = rational(brs->size()); insert_cache(x.x(), fml, brs); return true; } virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) { nlarith::branch_conditions *brs; VERIFY (m_cache.find(x.x(), fml, brs)); SASSERT(vl.is_unsigned()); SASSERT(vl.get_unsigned() < brs->size()); unsigned j = vl.get_unsigned(); m_replace.reset(); for (unsigned i = 0; i < brs->preds().size(); ++i) { m_replace.insert(brs->preds(i), brs->subst(j)[i]); } m_replace(fml); expr_ref tmp(m.mk_and(brs->constraints(j), fml), m); m_factor_rw(tmp, fml); if (def) { m_factor_rw(brs->def(j), *def); } } virtual unsigned get_weight(contains_app& x, expr* fml) { obj_map* weights = 0; unsigned weight = 0; if (!m_weights.find(fml, weights)) { weights = alloc(weight_m); m_weights.insert(fml, weights); m_trail.push_back(fml); ptr_vector nl_vars; m_util.extract_non_linear(to_app(fml), nl_vars); for (unsigned i = 0; i < nl_vars.size(); ++i) { weights->insert(nl_vars[i], 100); } } if (weights->find(x.x(), weight)) { return weight; } return UINT_MAX; } virtual bool solve(conj_enum& conjs, expr* fml) { return false; } // we don't need to modify the atom. virtual bool mk_atom(expr* e, bool p, expr_ref& result) { return false; } virtual bool is_uninterpreted(app* f) { if (m_produce_models) { return true; } switch(f->get_decl_kind()) { case OP_NUM: case OP_LE: case OP_LT: case OP_GE: case OP_GT: case OP_ADD: case OP_SUB: case OP_UMINUS: return false; case OP_MUL: { arith_util a(m); expr* m, *n; if (a.is_mul(f, m, n) && (a.is_numeral(m) || a.is_numeral(n))) { return false; } return true; } default: return true; } return true; } private: void insert_cache(app* x, expr* e, nlarith::branch_conditions* brs) { m_trail.push_back(x); m_trail.push_back(e); m_cache.insert(x, e, brs); } void update_bounds(expr_ref_vector& lits, atom_set const& tbl, bool is_pos) { atom_set::iterator it = tbl.begin(), end = tbl.end(); for (; it != end; ++it) { app* e = *it; lits.push_back(is_pos?e:m.mk_not(e)); } } }; qe_solver_plugin* mk_arith_plugin(i_solver_context& ctx, bool produce_models, smt_params& p) { if (p.m_nlquant_elim) { return alloc(nlarith_plugin, ctx, ctx.get_manager(), produce_models); } else { return alloc(arith_plugin, ctx, ctx.get_manager(), p); } } } z3-4.4.0/src/qe/qe.cpp0000644000175000017500000026364112540347414014166 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: qe.cpp Abstract: Quantifier elimination procedures Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: --*/ #include "qe.h" #include "smt_theory.h" #include "bv_decl_plugin.h" #include "smt_context.h" #include "theory_bv.h" #include "ast_ll_pp.h" #include "ast_pp.h" #include "ast_smt_pp.h" #include "expr_abstract.h" #include "var_subst.h" #include "for_each_expr.h" #include "dl_decl_plugin.h" #include "nlarith_util.h" #include "expr_replacer.h" #include "factor_rewriter.h" #include "expr_functors.h" #include "quant_hoist.h" #include "bool_rewriter.h" #include "qe_util.h" #include "th_rewriter.h" #include "smt_kernel.h" #include "model_evaluator.h" #include "has_free_vars.h" #include "rewriter_def.h" #include "cooperate.h" #include "tactical.h" #include "model_v2_pp.h" #include "obj_hashtable.h" namespace qe { class conjunctions { ast_manager& m; ptr_vector m_plugins; // family_id -> plugin public: conjunctions(ast_manager& m) : m(m) {} void add_plugin(qe_solver_plugin* p) { family_id fid = p->get_family_id(); if (static_cast(m_plugins.size()) <= fid) { m_plugins.resize(fid + 1); } m_plugins[fid] = p; } void get_partition( expr* fml, unsigned num_vars, app* const* vars, expr_ref& fml_closed, // conjuncts that don't contain any variables from vars. expr_ref& fml_mixed, // conjuncts that contain terms from vars and non-vars. expr_ref& fml_open // conjuncts that contain vars (mixed or pure). ) { expr_ref_vector conjs(m); ast_mark visited; ast_mark contains_var; ast_mark contains_uf; ptr_vector todo; ptr_vector conjs_closed, conjs_mixed, conjs_open; qe::flatten_and(fml, conjs); for (unsigned i = 0; i < conjs.size(); ++i) { todo.push_back(conjs[i].get()); } while (!todo.empty()) { expr* e = todo.back(); if (visited.is_marked(e)) { todo.pop_back(); continue; } if (is_var(to_app(e), num_vars, vars)) { contains_var.mark(e, true); visited.mark(e, true); todo.pop_back(); continue; } if (!is_app(e)) { visited.mark(e, true); todo.pop_back(); continue; } bool all_visited = true; app* a = to_app(e); if (is_uninterpreted(a)) { contains_uf.mark(e, true); } for (unsigned i = 0; i < a->get_num_args(); ++i) { expr* arg = a->get_arg(i); if (!visited.is_marked(arg)) { all_visited = false; todo.push_back(arg); } else { if (contains_var.is_marked(arg)) { contains_var.mark(e, true); } if (contains_uf.is_marked(arg)) { contains_uf.mark(e, true); } } } if (all_visited) { todo.pop_back(); visited.mark(e, true); } } for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); bool cv = contains_var.is_marked(e); bool cu = contains_uf.is_marked(e); if (cv && cu) { conjs_mixed.push_back(e); conjs_open.push_back(e); } else if (cv) { conjs_open.push_back(e); } else { conjs_closed.push_back(e); } } bool_rewriter rewriter(m); rewriter.mk_and(conjs_closed.size(), conjs_closed.c_ptr(), fml_closed); rewriter.mk_and(conjs_mixed.size(), conjs_mixed.c_ptr(), fml_mixed); rewriter.mk_and(conjs_open.size(), conjs_open.c_ptr(), fml_open); TRACE("qe", tout << "closed\n" << mk_ismt2_pp(fml_closed, m) << "\n"; tout << "open\n" << mk_ismt2_pp(fml_open, m) << "\n"; tout << "mixed\n" << mk_ismt2_pp(fml_mixed, m) << "\n"; ); } // // Partition variables into buckets. // The var_paritions buckets covering disjoint subsets of // the conjuncts. The remaining variables in vars are non-partioned. // bool partition_vars( unsigned num_vars, contains_app** vars, unsigned num_args, expr* const* args, vector& partition ) { unsigned_vector contains_index; unsigned_vector non_shared; unsigned_vector non_shared_vars; union_find_default_ctx df; union_find uf(df); partition.reset(); for (unsigned v = 0; v < num_vars; ++v) { contains_app& contains_x = *vars[v]; contains_index.reset(); for (unsigned i = 0; i < num_args; ++i) { if (contains_x(args[i])) { contains_index.push_back(i); TRACE("qe_verbose", tout << "var " << v << " in " << i << "\n";); } } // // x occurs in more than half of conjuncts. // Mark it as shared. // if (2*contains_index.size() > num_args) { if (partition.empty()) { partition.push_back(unsigned_vector()); } partition.back().push_back(v); TRACE("qe_verbose", tout << "majority " << v << "\n";); continue; } // // Create partition of variables that share conjuncts. // unsigned var_x = uf.mk_var(); SASSERT(var_x == non_shared_vars.size()); non_shared_vars.push_back(v); for (unsigned i = 0; i < contains_index.size(); ++i) { unsigned idx = contains_index[i]; if (non_shared.size() <= idx) { non_shared.resize(idx+1,UINT_MAX); } unsigned var_y = non_shared[idx]; if (var_y != UINT_MAX) { uf.merge(var_x, var_y); } else { non_shared[idx] = var_x; } } } if (non_shared_vars.empty()) { return false; } unsigned root0 = uf.find(0); bool is_partitioned = false; for (unsigned idx = 1; !is_partitioned && idx < non_shared_vars.size(); ++idx) { is_partitioned = (uf.find(idx) != root0); } if (!is_partitioned) { return false; } // // variables are partitioned into more than one // class. // unsigned_vector roots; if (!partition.empty()) { roots.push_back(UINT_MAX); } for (unsigned idx = 0; idx < non_shared_vars.size(); ++idx) { unsigned x = non_shared_vars[idx]; unsigned r = non_shared_vars[uf.find(idx)]; TRACE("qe_verbose", tout << "x: " << x << " r: " << r << "\n";); bool found = false; for (unsigned i = 0; !found && i < roots.size(); ++i) { if (roots[i] == r) { found = true; partition[i].push_back(x); } } if (!found) { roots.push_back(r); partition.push_back(unsigned_vector()); partition.back().push_back(x); } } TRACE("qe_verbose", for (unsigned i = 0; i < partition.size(); ++i) { for (unsigned j = 0; j < partition[i].size(); ++j) { tout << " " << mk_ismt2_pp(vars[partition[i][j]]->x(), m);; } tout << "\n"; }); SASSERT(partition.size() != 1); SASSERT(!partition.empty() || roots.size() > 1); return true; } private: bool is_var(app* x, unsigned num_vars, app* const* vars) { for (unsigned i = 0; i < num_vars; ++i) { if (vars[i] == x) { return true; } } return false; } bool is_uninterpreted(app* a) { family_id fid = a->get_family_id(); if (null_family_id == fid) { return true; } if (static_cast(fid) >= m_plugins.size()) { return true; } qe_solver_plugin* p = m_plugins[fid]; if (!p) { return true; } return p->is_uninterpreted(a); } }; // --------------- // conj_enum conj_enum::conj_enum(ast_manager& m, expr* e): m(m), m_conjs(m) { qe::flatten_and(e, m_conjs); } void conj_enum::extract_equalities(expr_ref_vector& eqs) { arith_util arith(m); obj_hashtable leqs; expr_ref_vector trail(m); expr_ref tmp1(m), tmp2(m); expr *a0, *a1; eqs.reset(); conj_enum::iterator it = begin(); for (; it != end(); ++it) { expr* e = *it; if (m.is_eq(e, a0, a1) && (arith.is_int(a0) || arith.is_real(a0))) { tmp1 = arith.mk_sub(a0, a1); eqs.push_back(tmp1); } else if (arith.is_le(e, a0, a1) || arith.is_ge(e, a1, a0)) { tmp1 = arith.mk_sub(a0, a1); tmp2 = arith.mk_sub(a1, a0); if (leqs.contains(tmp2)) { eqs.push_back(tmp1); TRACE("qe", tout << "found: " << mk_ismt2_pp(tmp1, m) << "\n";); } else { trail.push_back(tmp1); leqs.insert(tmp1); TRACE("qe_verbose", tout << "insert: " << mk_ismt2_pp(tmp1, m) << "\n";); } } else { // drop equality. } } } // ----------------- // Lift ite from sub-formulas. // class lift_ite { ast_manager& m; i_expr_pred& m_is_relevant; th_rewriter m_rewriter; scoped_ptr m_replace; public: lift_ite(ast_manager& m, i_expr_pred& is_relevant) : m(m), m_is_relevant(is_relevant), m_rewriter(m), m_replace(mk_default_expr_replacer(m)) {} bool operator()(expr* fml, expr_ref& result) { if (m.is_ite(fml)) { result = fml; return true; } app* ite; if (find_ite(fml, ite)) { expr* cond, *th, *el; VERIFY(m.is_ite(ite, cond, th, el)); expr_ref tmp1(fml, m), tmp2(fml, m); m_replace->apply_substitution(ite, th, tmp1); m_replace->apply_substitution(ite, el, tmp2); result = m.mk_ite(cond, tmp1, tmp2); m_rewriter(result); return true; } else { return false; } } private: bool find_ite(expr* e, app*& ite) { ptr_vector todo; ast_mark visited; todo.push_back(e); while(!todo.empty()) { e = todo.back(); todo.pop_back(); if (visited.is_marked(e)) { continue; } visited.mark(e, true); if (!m_is_relevant(e)) { continue; } if (m.is_ite(e)) { ite = to_app(e); return true; } if (is_app(e)) { app* a = to_app(e); unsigned num_args = a->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { todo.push_back(a->get_arg(i)); } } } return false; } }; // convert formula to NNF. class nnf { ast_manager& m; i_expr_pred& m_is_relevant; lift_ite m_lift_ite; obj_map m_pos, m_neg; // memoize positive/negative sub-formulas expr_ref_vector m_trail; // trail for generated terms expr_ref_vector m_args; ptr_vector m_todo; // stack of formulas to visit svector m_pols; // stack of polarities bool_rewriter m_rewriter; public: nnf(ast_manager& m, i_expr_pred& is_relevant): m(m), m_is_relevant(is_relevant), m_lift_ite(m, is_relevant), m_trail(m), m_args(m), m_rewriter(m) {} void operator()(expr_ref& fml) { reset(); get_nnf(fml, true); } void reset() { m_todo.reset(); m_trail.reset(); m_pols.reset(); m_pos.reset(); m_neg.reset(); } private: bool contains(expr* e, bool p) { return p?m_pos.contains(e):m_neg.contains(e); } expr* lookup(expr* e, bool p) { expr* r = 0; if (p && m_pos.find(e, r)) { return r; } if (!p && m_neg.find(e, r)) { return r; } m_todo.push_back(e); m_pols.push_back(p); return 0; } void insert(expr* e, bool p, expr* r) { if (p) { m_pos.insert(e, r); } else { m_neg.insert(e, r); } TRACE("nnf", tout << mk_ismt2_pp(e, m) << " " << p << "\n"; tout << mk_ismt2_pp(r, m) << "\n";); m_trail.push_back(r); } void pop() { m_todo.pop_back(); m_pols.pop_back(); } void nnf_iff(app* a, bool p) { SASSERT(m.is_iff(a) || m.is_xor(a) || m.is_eq(a)); expr* a0 = a->get_arg(0); expr* a1 = a->get_arg(1); expr* r1 = lookup(a0, true); expr* r2 = lookup(a0, false); expr* p1 = lookup(a1, true); expr* p2 = lookup(a1, false); if (r1 && r2 && p1 && p2) { expr_ref tmp1(m), tmp2(m), tmp(m); pop(); if (p) { m_rewriter.mk_and(r1, p1, tmp1); m_rewriter.mk_and(r2, p2, tmp2); m_rewriter.mk_or(tmp1, tmp2, tmp); } else { m_rewriter.mk_or(r1, p1, tmp1); m_rewriter.mk_or(r2, p2, tmp2); m_rewriter.mk_and(tmp1, tmp2, tmp); } insert(a, p, tmp); } } void nnf_ite(app* a, bool p) { SASSERT(m.is_ite(a)); expr* r1 = lookup(a->get_arg(0), true); expr* r2 = lookup(a->get_arg(0), false); expr* th = lookup(a->get_arg(1), p); expr* el = lookup(a->get_arg(2), p); if (r1 && r2 && th && el) { expr_ref tmp1(m), tmp2(m), tmp(m); pop(); m_rewriter.mk_and(r1, th, tmp1); m_rewriter.mk_and(r2, el, tmp2); m_rewriter.mk_or(tmp1, tmp2, tmp); TRACE("nnf", tout << mk_ismt2_pp(a, m) << "\n"; tout << mk_ismt2_pp(tmp, m) << "\n";); insert(a, p, tmp); } } void nnf_implies(app* a, bool p) { SASSERT(m.is_implies(a)); expr* r1 = lookup(a->get_arg(0), !p); expr* r2 = lookup(a->get_arg(1), p); if (r1 && r2) { expr_ref tmp(m); if (p) { m_rewriter.mk_or(r1, r2, tmp); } else { m_rewriter.mk_and(r1, r2, tmp); } insert(a, p, tmp); } } void nnf_not(app* a, bool p) { SASSERT(m.is_not(a)); expr* arg = a->get_arg(0); expr* e = lookup(arg, !p); if (e) { insert(a, p, e); } } void nnf_and_or(bool is_and, app* a, bool p) { m_args.reset(); unsigned num_args = a->get_num_args(); expr_ref tmp(m); bool visited = true; for (unsigned i = 0; i < num_args; ++i) { expr* arg = a->get_arg(i); expr* r = lookup(arg, p); if (r) { m_args.push_back(r); } else { visited = false; } } if (visited) { pop(); if ((p && is_and) || (!p && !is_and)) { m_rewriter.mk_and(num_args, m_args.c_ptr(), tmp); } else { m_rewriter.mk_or(num_args, m_args.c_ptr(), tmp); } insert(a, p, tmp); } } bool get_nnf(expr_ref& fml, bool p0) { TRACE("nnf", tout << mk_ismt2_pp(fml.get(), m) << "\n";); bool p = p0; unsigned sz = m_todo.size(); expr_ref tmp(m); expr* e = lookup(fml, p); if (e) { fml = e; return true; } m_trail.push_back(fml); while (sz < m_todo.size()) { e = m_todo.back(); p = m_pols.back(); if (!m_is_relevant(e)) { pop(); insert(e, p, p?e:m.mk_not(e)); continue; } if (!is_app(e)) { return false; } if (contains(e, p)) { pop(); continue; } app* a = to_app(e); if (m.is_and(e) || m.is_or(e)) { nnf_and_or(m.is_and(a), a, p); } else if (m.is_not(a)) { nnf_not(a, p); } else if (m.is_ite(a)) { nnf_ite(a, p); } else if (m.is_iff(a) || (m.is_eq(a) && m.is_bool(a->get_arg(0)))) { nnf_iff(a, p); } else if (m.is_xor(a)) { nnf_iff(a, !p); } else if (m.is_implies(a)) { nnf_implies(a, p); } else if (m_lift_ite(e, tmp)) { if (!get_nnf(tmp, p)) { return false; } pop(); insert(e, p, tmp); } else { pop(); insert(e, p, p?e:m.mk_not(e)); } } fml = lookup(fml, p0); SASSERT(fml.get()); return true; } }; // ---------------------------------- // Normalize literals in NNF formula. class nnf_normalize_literals { ast_manager& m; i_expr_pred& m_is_relevant; i_nnf_atom& m_mk_atom; obj_map m_cache; ptr_vector m_todo; expr_ref_vector m_trail; ptr_vector m_args; public: nnf_normalize_literals(ast_manager& m, i_expr_pred& is_relevant, i_nnf_atom& mk_atom): m(m), m_is_relevant(is_relevant), m_mk_atom(mk_atom), m_trail(m) {} void operator()(expr_ref& fml) { SASSERT(m_todo.empty()); m_todo.push_back(fml); while (!m_todo.empty()) { expr* e = m_todo.back(); if (m_cache.contains(e)) { m_todo.pop_back(); } else if (!is_app(e)) { m_todo.pop_back(); m_cache.insert(e, e); } else if (visit(to_app(e))) { m_todo.pop_back(); } } fml = m_cache.find(fml); reset(); } void reset() { m_cache.reset(); m_todo.reset(); m_trail.reset(); } private: bool visit(app* e) { bool all_visit = true; expr* f = 0; expr_ref tmp(m); if (!m_is_relevant(e)) { m_cache.insert(e, e); } else if (m.is_and(e) || m.is_or(e)) { m_args.reset(); for (unsigned i = 0; i < e->get_num_args(); ++i) { if (m_cache.find(e->get_arg(i), f)) { m_args.push_back(f); } else { all_visit = false; m_todo.push_back(e->get_arg(i)); } } if (all_visit) { m_cache.insert(e, m.mk_app(e->get_decl(), m_args.size(), m_args.c_ptr())); } } else if (m.is_not(e, f)) { SASSERT(!m.is_not(f) && !m.is_and(f) && !m.is_or(f)); m_mk_atom(f, false, tmp); m_cache.insert(e, tmp); m_trail.push_back(tmp); } else { m_mk_atom(e, true, tmp); m_trail.push_back(tmp); m_cache.insert(e, tmp); } return all_visit; } }; // ---------------------------- // def_vector void def_vector::normalize() { // apply nested definitions into place. ast_manager& m = m_vars.get_manager(); expr_substitution sub(m); scoped_ptr rep = mk_expr_simp_replacer(m); if (size() <= 1) { return; } for (unsigned i = size(); i > 0; ) { --i; expr_ref e(m); e = def(i); rep->set_substitution(&sub); (*rep)(e); sub.insert(m.mk_const(var(i)), e); def_ref(i) = e; } } void def_vector::project(unsigned num_vars, app* const* vars) { obj_hashtable fns; for (unsigned i = 0; i < num_vars; ++i) { fns.insert(vars[i]->get_decl()); } for (unsigned i = 0; i < size(); ++i) { if (fns.contains(m_vars[i].get())) { // // retain only first occurrence of eliminated variable. // later occurrences are just recycling the name. // fns.remove(m_vars[i].get()); } else { for (unsigned j = i+1; j < size(); ++j) { m_vars.set(j-1, m_vars.get(j)); m_defs.set(j-1, m_defs.get(j)); } m_vars.pop_back(); m_defs.pop_back(); --i; } } } // ---------------------------- // guarded_defs std::ostream& guarded_defs::display(std::ostream& out) const { ast_manager& m = m_guards.get_manager(); for (unsigned i = 0; i < size(); ++i) { for (unsigned j = 0; j < defs(i).size(); ++j) { out << defs(i).var(j)->get_name() << " := " << mk_pp(defs(i).def(j), m) << "\n"; } out << "if " << mk_pp(guard(i), m) << "\n"; } return out; } bool guarded_defs::inv() { return m_defs.size() == m_guards.size(); } void guarded_defs::add(expr* guard, def_vector const& defs) { SASSERT(inv()); m_defs.push_back(defs); m_guards.push_back(guard); m_defs.back().normalize(); SASSERT(inv()); } void guarded_defs::project(unsigned num_vars, app* const* vars) { for (unsigned i = 0; i < size(); ++i) { m_defs[i].project(num_vars, vars); } } // ---------------------------- // Obtain atoms in NNF formula. class nnf_collect_atoms { ast_manager& m; i_expr_pred& m_is_relevant; ptr_vector m_todo; ast_mark m_visited; public: nnf_collect_atoms(ast_manager& m, i_expr_pred& is_relevant): m(m), m_is_relevant(is_relevant) {} void operator()(expr* fml, atom_set& pos, atom_set& neg) { m_todo.push_back(fml); while (!m_todo.empty()) { expr* e = m_todo.back(); m_todo.pop_back(); if (m_visited.is_marked(e)) { continue; } m_visited.mark(e, true); if (!is_app(e) || !m_is_relevant(e)) { continue; } app* a = to_app(e); if (m.is_and(a) || m.is_or(a)) { for (unsigned i = 0; i < a->get_num_args(); ++i) { m_todo.push_back(a->get_arg(i)); } } else if (m.is_not(a, e) && is_app(e)) { neg.insert(to_app(e)); } else { pos.insert(a); } } SASSERT(m_todo.empty()); m_visited.reset(); } }; // -------------------------------- // Bring formula to NNF, normalize atoms, collect literals. class nnf_normalizer { nnf m_nnf_core; nnf_collect_atoms m_collect_atoms; nnf_normalize_literals m_normalize_literals; public: nnf_normalizer(ast_manager& m, i_expr_pred& is_relevant, i_nnf_atom& mk_atom): m_nnf_core(m, is_relevant), m_collect_atoms(m, is_relevant), m_normalize_literals(m, is_relevant, mk_atom) {} void operator()(expr_ref& fml, atom_set& pos, atom_set& neg) { expr_ref orig(fml); ast_manager& m = fml.get_manager(); m_nnf_core(fml); m_normalize_literals(fml); m_collect_atoms(fml, pos, neg); TRACE("qe", tout << mk_ismt2_pp(orig, m) << "\n-->\n" << mk_ismt2_pp(fml, m) << "\n";); } void reset() { m_nnf_core.reset(); m_normalize_literals.reset(); } }; void get_nnf(expr_ref& fml, i_expr_pred& pred, i_nnf_atom& mk_atom, atom_set& pos, atom_set& neg) { nnf_normalizer nnf(fml.get_manager(), pred, mk_atom); nnf(fml, pos, neg); } // // Theory plugin for quantifier elimination. // class quant_elim { public: virtual ~quant_elim() {} virtual lbool eliminate_exists( unsigned num_vars, app* const* vars, expr_ref& fml, app_ref_vector& free_vars, bool get_first, guarded_defs* defs) = 0; virtual void set_assumption(expr* fml) = 0; virtual void collect_statistics(statistics & st) const = 0; virtual void eliminate(bool is_forall, unsigned num_vars, app* const* vars, expr_ref& fml) = 0; virtual void set_cancel(bool f) = 0; virtual void updt_params(params_ref const& p) {} }; class search_tree { typedef map branch_map; ast_manager& m; app_ref_vector m_vars; // free variables app_ref m_var; // 0 or selected free variable def_vector m_def; // substitution for the variable eliminated relative to the parent. expr_ref m_fml; // formula whose variables are to be eliminated app_ref m_assignment; // assignment that got us here. search_tree* m_parent; // parent pointer rational m_num_branches; // number of possible branches ptr_vector m_children; // list of children branch_map m_branch_index; // branch_id -> child search tree index atom_set m_pos; atom_set m_neg; bool m_pure; // is the node pure (no variables deleted). // The invariant captures that search tree nodes are either // - unit branches (with only one descendant), or // - unassigned variable and formula // - assigned formula, but unassigned variable for branching // - assigned variable and formula with 0 or more branches. // #define CHECK_COND(_cond_) if (!(_cond_)) { TRACE("qe", tout << "violated: " << #_cond_ << "\n";); return false; } bool invariant() const { CHECK_COND(assignment()); CHECK_COND(m_children.empty() || fml()); CHECK_COND(!is_root() || fml()); CHECK_COND(!fml() || has_var() || m_num_branches.is_zero() || is_unit()); branch_map::iterator it = m_branch_index.begin(), end = m_branch_index.end(); for (; it != end; ++it) { CHECK_COND(it->m_value < m_children.size()); CHECK_COND(it->m_key < get_num_branches()); } for (unsigned i = 0; i < m_children.size(); ++i) { CHECK_COND(m_children[i]); CHECK_COND(this == m_children[i]->m_parent); CHECK_COND(m_children[i]->invariant()); } return true; } #undef CHECKC_COND public: search_tree(search_tree* parent, ast_manager& m, app* assignment): m(m), m_vars(m), m_var(m), m_def(m), m_fml(m), m_assignment(assignment, m), m_parent(parent), m_pure(true) {} ~search_tree() { reset(); } expr* fml() const { return m_fml; } expr_ref& fml_ref() { return m_fml; } def_vector const& def() const { return m_def; } app* assignment() const { return m_assignment; } app* var() const { SASSERT(has_var()); return m_var; } bool has_var() const { return 0 != m_var.get(); } search_tree* parent() const { return m_parent; } bool is_root() const { return !parent(); } rational const& get_num_branches() const { SASSERT(has_var()); return m_num_branches; } // extract disjunctions void get_leaves(expr_ref_vector& result) { SASSERT(is_root()); ptr_vector todo; todo.push_back(this); while (!todo.empty()) { search_tree* st = todo.back(); todo.pop_back(); if (st->m_children.empty() && st->fml() && st->m_vars.empty() && !st->has_var()) { result.push_back(st->fml()); } for (unsigned i = 0; i < st->m_children.size(); ++i) { todo.push_back(st->m_children[i]); } } } void get_leaves_rec(def_vector& defs, guarded_defs& gdefs) { expr* f = this->fml(); unsigned sz = defs.size(); defs.append(def()); if (m_children.empty() && f && !m.is_false(f) && m_vars.empty() && !has_var()) { gdefs.add(f, defs); } else { for (unsigned i = 0; i < m_children.size(); ++i) { m_children[i]->get_leaves_rec(defs, gdefs); } } defs.shrink(sz); } void get_leaves(guarded_defs& gdefs) { def_vector defs(m); get_leaves_rec(defs, gdefs); } void reset() { TRACE("qe",tout << "resetting\n";); for (unsigned i = 0; i < m_children.size(); ++i) { dealloc(m_children[i]); } m_pos.reset(); m_neg.reset(); m_children.reset(); m_vars.reset(); m_branch_index.reset(); m_var = 0; m_def.reset(); m_num_branches = rational::zero(); m_pure = true; } void init(expr* fml) { m_fml = fml; SASSERT(invariant()); } void add_def(app* v, expr* def) { if (v && def) { m_def.push_back(v->get_decl(), def); } } unsigned num_free_vars() const { return m_vars.size(); } app* const* free_vars() const { return m_vars.c_ptr(); } app* free_var(unsigned i) const { return m_vars[i]; } void reset_free_vars() { m_vars.reset(); } atom_set const& pos_atoms() const { return m_pos; } atom_set const& neg_atoms() const { return m_neg; } atom_set& pos_atoms() { return m_pos; } atom_set& neg_atoms() { return m_neg; } // set the branch variable. void set_var(app* x, rational const& num_branches) { SASSERT(!m_var.get()); SASSERT(m_vars.contains(x)); m_var = x; m_vars.erase(x); m_num_branches = num_branches; SASSERT(invariant()); } // include new variables. void consume_vars(app_ref_vector& vars) { while (!vars.empty()) { m_vars.push_back(vars.back()); vars.pop_back(); } } bool is_pure() const { search_tree const* node = this; while (node) { if (!node->m_pure) return false; node = node->parent(); } return true; } bool is_unit() const { return m_children.size() == 1 && m_branch_index.empty(); } bool has_branch(rational const& branch_id) const { return m_branch_index.contains(branch_id); } search_tree* child(rational const& branch_id) const { unsigned idx = m_branch_index.find(branch_id); return m_children[idx]; } search_tree* child() const { SASSERT(is_unit()); return m_children[0]; } // remove variable from branch. void del_var(app* x) { SASSERT(m_children.empty()); SASSERT(m_vars.contains(x)); m_vars.erase(x); m_pure = false; } // add branch with a given formula search_tree* add_child(expr* fml) { SASSERT(m_branch_index.empty()); SASSERT(m_children.empty()); m_num_branches = rational(1); search_tree* st = alloc(search_tree, this, m, m.mk_true()); m_children.push_back(st); st->init(fml); st->m_vars.append(m_vars.size(), m_vars.c_ptr()); SASSERT(invariant()); TRACE("qe", tout << mk_pp(m_fml, m) << " child: " << mk_pp(fml, m) << "\n";); return st; } search_tree* add_child(rational const& branch_id, app* assignment) { SASSERT(!m_branch_index.contains(branch_id)); SASSERT(has_var()); SASSERT(branch_id.is_nonneg() && branch_id < m_num_branches); unsigned index = m_children.size(); search_tree* st = alloc(search_tree, this, m, assignment); m_children.push_back(st); m_branch_index.insert(branch_id, index); st->m_vars.append(m_vars.size(), m_vars.c_ptr()); SASSERT(invariant()); //TRACE("qe", tout << mk_pp(m_fml, m) << " child: " << mk_pp(st->fml(), m) << "\n";); return st; } void display(std::ostream& out) const { display(out, ""); } void display(std::ostream& out, char const* indent) const { out << indent << "node\n"; if (m_var) { out << indent << " var: " << mk_ismt2_pp(m_var.get(), m) << "\n"; } for (unsigned i = 0; i < m_vars.size(); ++i) { out << indent << " free: " << mk_ismt2_pp(m_vars[i], m) << "\n"; } if (m_fml) { out << indent << " fml: " << mk_ismt2_pp(m_fml.get(), m) << "\n"; } for (unsigned i = 0; i < m_def.size(); ++i) { out << indent << " def: " << m_def.var(i)->get_name() << " = " << mk_ismt2_pp(m_def.def(i), m) << "\n"; } out << indent << " branches: " << m_num_branches << "\n"; std::string new_indent(indent); new_indent += " "; for (unsigned i = 0; i < m_children.size(); ++i) { m_children[i]->display(out, new_indent.c_str()); } } expr_ref abstract_variable(app* x, expr* fml) const { expr_ref result(m); expr* y = x; expr_abstract(m, 0, 1, &y, fml, result); symbol X(x->get_decl()->get_name()); sort* s = m.get_sort(x); result = m.mk_exists(1, &s, &X, result); return result; } void display_validate(std::ostream& out) const { if (m_children.empty()) { return; } expr_ref q(m); expr* x = m_var; if (x) { q = abstract_variable(m_var, m_fml); expr_ref_vector fmls(m); expr_ref fml(m); for (unsigned i = 0; i < m_children.size(); ++i) { search_tree const& child = *m_children[i]; fml = child.fml(); if (fml) { // abstract free variables in children. ptr_vector child_vars, new_vars; child_vars.append(child.m_vars.size(), child.m_vars.c_ptr()); if (child.m_var) { child_vars.push_back(child.m_var); } for (unsigned j = 0; j < child_vars.size(); ++j) { if (!m_vars.contains(child_vars[j]) && !new_vars.contains(child_vars[j])) { fml = abstract_variable(child_vars[j], fml); new_vars.push_back(child_vars[j]); } } fmls.push_back(fml); } } bool_rewriter(m).mk_or(fmls.size(), fmls.c_ptr(), fml); fml = m.mk_not(m.mk_iff(q, fml)); ast_smt_pp pp(m); out << "; eliminate " << mk_pp(m_var, m) << "\n"; out << "(push)\n"; pp.display_smt2(out, fml); out << "(pop)\n\n"; DEBUG_CODE( smt_params params; params.m_simplify_bit2int = true; params.m_arith_enum_const_mod = true; params.m_bv_enable_int2bv2int = true; params.m_relevancy_lvl = 0; smt::kernel ctx(m, params); ctx.assert_expr(fml); lbool is_sat = ctx.check(); if (is_sat == l_true) { std::cout << "; Validation failed:\n"; std::cout << mk_pp(fml, m) << "\n"; } ); } for (unsigned i = 0; i < m_children.size(); ++i) { if (m_children[i]->fml()) { m_children[i]->display_validate(out); } } } }; // ------------------------- // i_solver_context i_solver_context::~i_solver_context() { for (unsigned i = 0; i < m_plugins.size(); ++i) { dealloc(m_plugins[i]); } } bool i_solver_context::is_relevant::operator()(expr* e) { for (unsigned i = 0; i < m_s.get_num_vars(); ++i) { if (m_s.contains(i)(e)) { return true; } } TRACE("qe_verbose", tout << "Not relevant: " << mk_ismt2_pp(e, m_s.get_manager()) << "\n";); return false; } bool i_solver_context::is_var(expr* x, unsigned& idx) const { unsigned nv = get_num_vars(); for (unsigned i = 0; i < nv; ++i) { if (get_var(i) == x) { idx = i; return true; } } return false; } void i_solver_context::add_plugin(qe_solver_plugin* p) { family_id fid = p->get_family_id(); SASSERT(fid != null_family_id); if (static_cast(m_plugins.size()) <= fid) { m_plugins.resize(fid+1,0); } SASSERT(!m_plugins[fid]); m_plugins[fid] = p; } bool i_solver_context::has_plugin(app* x) { ast_manager& m = get_manager(); family_id fid = m.get_sort(x)->get_family_id(); return 0 <= fid && fid < static_cast(m_plugins.size()) && m_plugins[fid] != 0; } qe_solver_plugin& i_solver_context::plugin(app* x) { ast_manager& m = get_manager(); SASSERT(has_plugin(x)); return *(m_plugins[m.get_sort(x)->get_family_id()]); } void i_solver_context::mk_atom(expr* e, bool p, expr_ref& result) { ast_manager& m = get_manager(); TRACE("qe_verbose", tout << mk_ismt2_pp(e, m) << "\n";); for (unsigned i = 0; i < m_plugins.size(); ++i) { qe_solver_plugin* pl = m_plugins[i]; if (pl && pl->mk_atom(e, p, result)) { return; } } TRACE("qe_verbose", tout << "No plugin for " << mk_ismt2_pp(e, m) << "\n";); if (p || m.is_not(e, e)) { result = e; } else { result = m.mk_not(e); } } void i_solver_context::mk_atom_fn::operator()(expr* e, bool p, expr_ref& result) { m_s.mk_atom(e, p, result); } typedef ref_vector_ptr_hash expr_ref_vector_hash; typedef ref_vector_ptr_eq expr_ref_vector_eq; typedef hashtable clause_table; typedef value_trail _value_trail; class quant_elim_plugin : public i_solver_context { ast_manager& m; quant_elim& m_qe; th_rewriter m_rewriter; smt::kernel m_solver; bv_util m_bv; expr_ref_vector m_literals; bool_rewriter m_bool_rewriter; conjunctions m_conjs; // maintain queue for variables. app_ref_vector m_free_vars; // non-quantified variables app_ref_vector m_trail; expr_ref m_fml; expr_ref m_subfml; obj_map m_var2branch; // var -> bv-var, identify explored branch. obj_map m_var2contains; // var -> contains_app obj_map > m_children; // var -> list of dependent children search_tree m_root; search_tree* m_current; // current branch vector m_partition; // cached latest partition of variables. app_ref_vector m_new_vars; // variables added by solvers bool m_get_first; // get first satisfying branch. guarded_defs* m_defs; nnf_normalizer m_nnf; // nnf conversion public: quant_elim_plugin(ast_manager& m, quant_elim& qe, smt_params& p): m(m), m_qe(qe), m_rewriter(m), m_solver(m, p), m_bv(m), m_literals(m), m_bool_rewriter(m), m_conjs(m), m_free_vars(m), m_trail(m), m_fml(m), m_subfml(m), m_root(0, m, m.mk_true()), m_current(0), m_new_vars(m), m_get_first(false), m_defs(0), m_nnf(m, get_is_relevant(), get_mk_atom()) { params_ref params; params.set_bool("gcd_rounding", true); m_rewriter.updt_params(params); } virtual ~quant_elim_plugin() { reset(); } void reset() { m_free_vars.reset(); m_trail.reset(); obj_map::iterator it = m_var2contains.begin(), end = m_var2contains.end(); for (; it != end; ++it) { dealloc(it->m_value); } m_var2contains.reset(); m_var2branch.reset(); m_root.reset(); m_new_vars.reset(); m_fml = 0; m_defs = 0; m_nnf.reset(); } void add_plugin(qe_solver_plugin* p) { i_solver_context::add_plugin(p); m_conjs.add_plugin(p); } void set_cancel(bool f) { m_solver.set_cancel(f); m_rewriter.set_cancel(f); } void check(unsigned num_vars, app* const* vars, expr* assumption, expr_ref& fml, bool get_first, app_ref_vector& free_vars, guarded_defs* defs) { reset(); m_solver.push(); m_get_first = get_first; m_defs = defs; for (unsigned i = 0; i < num_vars; ++i) { if (has_plugin(vars[i])) { add_var(vars[i]); } else { m_free_vars.push_back(vars[i]); } } m_root.consume_vars(m_new_vars); m_current = &m_root; // set sub-formula m_fml = fml; normalize(m_fml, m_root.pos_atoms(), m_root.neg_atoms()); expr_ref f(m_fml); get_max_relevant(get_is_relevant(), f, m_subfml); if (f.get() != m_subfml.get()) { m_fml = f; f = m_subfml; m_solver.assert_expr(f); } m_root.init(f); TRACE("qe", for (unsigned i = 0; i < num_vars; ++i) tout << mk_ismt2_pp(vars[i], m) << "\n"; tout << mk_ismt2_pp(f, m) << "\n";); m_solver.assert_expr(m_fml); if (assumption) m_solver.assert_expr(assumption); bool is_sat = false; lbool res = l_true; while (res == l_true) { res = m_solver.check(); if (res == l_true) { is_sat = true; final_check(); } else { break; } } if (res == l_undef) { free_vars.append(num_vars, vars); reset(); m_solver.pop(1); return; } if (!is_sat) { fml = m.mk_false(); reset(); m_solver.pop(1); return; } if (!get_first) { expr_ref_vector result(m); m_root.get_leaves(result); m_bool_rewriter.mk_or(result.size(), result.c_ptr(), fml); } if (defs) { m_root.get_leaves(*defs); defs->project(num_vars, vars); } TRACE("qe", tout << "result:" << mk_ismt2_pp(fml, m) << "\n"; tout << "input: " << mk_ismt2_pp(m_fml, m) << "\n"; tout << "subformula: " << mk_ismt2_pp(m_subfml, m) << "\n"; m_root.display(tout); m_root.display_validate(tout); for (unsigned i = 0; i < m_free_vars.size(); ++i) tout << mk_ismt2_pp(m_free_vars[i].get(), m) << " "; tout << "\n"; ); free_vars.append(m_free_vars); if (!m_free_vars.empty() || m_solver.inconsistent()) { if (m_fml.get() != m_subfml.get()) { scoped_ptr rp = mk_default_expr_replacer(m); rp->apply_substitution(to_app(m_subfml.get()), fml, m_fml); fml = m_fml; } } reset(); m_solver.pop(1); f = 0; } void collect_statistics(statistics& st) { m_solver.collect_statistics(st); } private: void final_check() { model_ref model; m_solver.get_model(model); scoped_ptr model_eval = alloc(model_evaluator, *model); while (true) { TRACE("qe", model_v2_pp(tout, *model);); while (can_propagate_assignment(*model_eval)) { propagate_assignment(*model_eval); } VERIFY(CHOOSE_VAR == update_current(*model_eval, true)); SASSERT(m_current->fml()); if (l_true != m_solver.check()) { return; } m_solver.get_model(model); model_eval = alloc(model_evaluator, *model); search_tree* st = m_current; update_current(*model_eval, false); if (st == m_current) { break; } } pop(*model_eval); } ast_manager& get_manager() { return m; } atom_set const& pos_atoms() const { return m_current->pos_atoms(); } atom_set const& neg_atoms() const { return m_current->neg_atoms(); } unsigned get_num_vars() const { return m_current->num_free_vars(); } app* get_var(unsigned idx) const { return m_current->free_var(idx); } app* const* get_vars() const { return m_current->free_vars(); } contains_app& contains(unsigned idx) { return contains(get_var(idx)); } // // The variable at idx is eliminated (without branching). // void elim_var(unsigned idx, expr* _fml, expr* def) { app* x = get_var(idx); expr_ref fml(_fml, m); TRACE("qe", tout << mk_pp(x,m) << " " << mk_pp(def, m) << "\n";); m_current->set_var(x, rational(1)); m_current = m_current->add_child(fml); m_current->add_def(x, def); m_current->consume_vars(m_new_vars); normalize(*m_current); } void add_var(app* x) { m_new_vars.push_back(x); if (m_var2branch.contains(x)) { return; } contains_app* ca = alloc(contains_app, m, x); m_var2contains.insert(x, ca); app* bv = 0; if (m.is_bool(x) || m_bv.is_bv(x)) { bv = x; } else { bv = m.mk_fresh_const("b", m_bv.mk_sort(20)); m_trail.push_back(bv); } TRACE("qe", tout << "Add branch var: " << mk_ismt2_pp(x, m) << " " << mk_ismt2_pp(bv, m) << "\n";); m_var2branch.insert(x, bv); } virtual void add_constraint(bool use_current_val, expr* l1 = 0, expr* l2 = 0, expr* l3 = 0) { search_tree* node = m_current; if (!use_current_val) { node = m_current->parent(); } m_literals.reset(); while (node) { m_literals.push_back(m.mk_not(node->assignment())); node = node->parent(); } add_literal(l1); add_literal(l2); add_literal(l3); expr_ref fml(m); fml = m.mk_or(m_literals.size(), m_literals.c_ptr()); TRACE("qe", tout << mk_ismt2_pp(fml, m) << "\n";); m_solver.assert_expr(fml); } void blast_or(app* var, expr_ref& fml) { m_qe.eliminate_exists(1, &var, fml, m_free_vars, false, 0); } lbool eliminate_exists(unsigned num_vars, app* const* vars, expr_ref& fml, bool get_first, guarded_defs* defs) { return m_qe.eliminate_exists(num_vars, vars, fml, m_free_vars, get_first, defs); } private: void add_literal(expr* l) { if (l != 0) { m_literals.push_back(l); } } void get_max_relevant(i_expr_pred& is_relevant, expr_ref& fml, expr_ref& subfml) { if (m.is_and(fml) || m.is_or(fml)) { app* a = to_app(fml); unsigned num_args = a->get_num_args(); ptr_buffer r_args; ptr_buffer i_args; for (unsigned i = 0; i < num_args; ++i) { expr* arg = a->get_arg(i); if (is_relevant(arg)) { r_args.push_back(arg); } else { i_args.push_back(arg); } } if (r_args.empty() || i_args.empty()) { subfml = fml; } else if (r_args.size() == 1) { expr_ref tmp(r_args[0], m); get_max_relevant(is_relevant, tmp, subfml); i_args.push_back(tmp); fml = m.mk_app(a->get_decl(), i_args.size(), i_args.c_ptr()); } else { subfml = m.mk_app(a->get_decl(), r_args.size(), r_args.c_ptr()); i_args.push_back(subfml); fml = m.mk_app(a->get_decl(), i_args.size(), i_args.c_ptr()); } } else { subfml = fml; } } app* get_branch_id(app* x) { app* result = 0; VERIFY (m_var2branch.find(x, result)); return result; } bool extract_partition(ptr_vector& vars) { if (m_partition.empty()) { return false; } unsigned_vector& vec = m_partition.back();; for (unsigned i = 0; i < vec.size(); ++i) { vars.push_back(m_current->free_var(vec[i])); } m_partition.pop_back(); return true; } enum update_status { CHOOSE_VAR, NEED_PROPAGATION }; update_status update_current(model_evaluator& model_eval, bool apply) { SASSERT(m_fml); m_current = &m_root; rational branch, nb; while (true) { SASSERT(m_current->fml()); if (m_current->is_unit()) { m_current = m_current->child(); continue; } if (!m_current->has_var()) { return CHOOSE_VAR; } app* x = m_current->var(); app* b = get_branch_id(x); nb = m_current->get_num_branches(); expr_ref fml(m_current->fml(), m); if (!eval(model_eval, b, branch) || branch >= nb) { TRACE("qe", tout << "evaluation failed: setting branch to 0\n";); branch = rational::zero(); } SASSERT(!branch.is_neg()); if (!m_current->has_branch(branch)) { if (apply) { app_ref assignment(mk_eq_value(b, branch), m); m_current = m_current->add_child(branch, assignment); plugin(x).assign(contains(x), fml, branch); m_current->consume_vars(m_new_vars); } return NEED_PROPAGATION; } m_current = m_current->child(branch); if (m_current->fml() == 0) { SASSERT(!m_current->has_var()); if (apply) { expr_ref def(m); plugin(x).subst(contains(x), branch, fml, m_defs?&def:0); SASSERT(!contains(x)(fml)); m_current->consume_vars(m_new_vars); m_current->init(fml); m_current->add_def(x, def); normalize(*m_current); } return CHOOSE_VAR; } } } void pop(model_evaluator& model_eval) { // // Eliminate trivial quantifiers by solving // variables that can be eliminated. // solve_vars(); expr* fml = m_current->fml(); // we are done splitting. if (m_current->num_free_vars() == 0) { block_assignment(); return; } expr_ref fml_closed(m), fml_open(m), fml_mixed(m); unsigned num_vars = m_current->num_free_vars(); ptr_vector cont; ptr_vector vars; for (unsigned i = 0; i < num_vars; ++i) { cont.push_back(&contains(i)); vars.push_back(m_current->free_var(i)); } m_conjs.get_partition(fml, num_vars, vars.c_ptr(), fml_closed, fml_mixed, fml_open); if (m.is_and(fml_open) && m_conjs.partition_vars( num_vars, cont.c_ptr(), to_app(fml_open)->get_num_args(), to_app(fml_open)->get_args(), m_partition)) { process_partition(); return; } // // The closed portion of the formula // can be used as the quantifier-free portion, // unless the current state is unsatisfiable. // if (m.is_true(fml_mixed)) { SASSERT(l_true == m_solver.check()); m_current = m_current->add_child(fml_closed); for (unsigned i = 0; m_defs && i < m_current->num_free_vars(); ++i) { expr_ref val(m); app* x = m_current->free_var(i); model_eval(x, val); // hack: assign may add new variables to the branch. if (val == x) { model_ref model; lbool is_sat = m_solver.check(); if (is_sat == l_undef) return; m_solver.get_model(model); SASSERT(is_sat == l_true); model_evaluator model_eval2(*model); model_eval2.set_model_completion(true); model_eval2(x, val); } TRACE("qe", tout << mk_pp(x,m) << " " << mk_pp(val, m) << "\n";); m_current->add_def(x, val); } m_current->reset_free_vars(); block_assignment(); return; } // // one variable is to be processed. // constrain_assignment(); } void normalize(search_tree& st) { normalize(st.fml_ref(), st.pos_atoms(), st.neg_atoms()); } void normalize(expr_ref& result, atom_set& pos, atom_set& neg) { m_rewriter(result); bool simplified = true; while (simplified) { simplified = false; for (unsigned i = 0; !simplified && i < m_plugins.size(); ++i) { qe_solver_plugin* pl = m_plugins[i]; simplified = pl && pl->simplify(result); } } TRACE("qe_verbose", tout << "simp: " << mk_ismt2_pp(result.get(), m) << "\n";); m_nnf(result, pos, neg); TRACE("qe", tout << "nnf: " << mk_ismt2_pp(result.get(), m) << "\n";); } // // variable queuing. // // ------------------------------------------------ // propagate the assignments to branch // literals to implied constraints on the // variable. // bool get_propagate_value(model_evaluator& model_eval, search_tree* node, app*& b, rational& b_val) { return node->has_var() && eval(model_eval, get_branch_id(node->var()), b_val); } bool can_propagate_assignment(model_evaluator& model_eval) { return m_fml && NEED_PROPAGATION == update_current(model_eval, false); } void propagate_assignment(model_evaluator& model_eval) { if (m_fml) { update_current(model_eval, true); } } // // evaluate the Boolean or bit-vector 'b' in // the current model // bool eval(model_evaluator& model_eval, app* b, rational& val) { unsigned bv_size; expr_ref res(m); model_eval(b, res); SASSERT(m.is_bool(b) || m_bv.is_bv(b)); if (m.is_true(res)) { val = rational::one(); return true; } else if (m.is_false(res)) { val = rational::zero(); return true; } else if (m_bv.is_numeral(res, val, bv_size)) { return true; } else { return false; } } // // create literal 'b = r' // app* mk_eq_value(app* b, rational const& vl) { if (m.is_bool(b)) { if (vl.is_zero()) return m.mk_not(b); if (vl.is_one()) return b; UNREACHABLE(); } SASSERT(m_bv.is_bv(b)); app_ref value(m_bv.mk_numeral(vl, m_bv.get_bv_size(b)), m); return m.mk_eq(b, value); } bool is_eq_value(app* e, app*& bv, rational& v) { unsigned sz = 0; if (!m.is_eq(e)) return false; expr* e0 = e->get_arg(0), *e1 = e->get_arg(1); if (!m_bv.is_bv(e0)) return false; if (m_bv.is_numeral(e0, v, sz) && is_app(e1)) { bv = to_app(e1); return true; } if (m_bv.is_numeral(e1, v, sz) && is_app(e0)) { bv = to_app(e0); return true; } return false; } // // the current state is satisfiable. // all bound decisions have been made. // void block_assignment() { TRACE("qe_verbose", m_solver.display(tout);); add_constraint(true); } // // The variable v is to be assigned a value in a range. // void constrain_assignment() { expr* fml = m_current->fml(); SASSERT(fml); rational k; app* x; if (!find_min_weight(x, k)) { return; } m_current->set_var(x, k); if (m_bv.is_bv(x)) { return; } app* b = get_branch_id(x); // // Create implication: // // assign_0 /\ ... /\ assign_{v-1} => b(v) <= k-1 // where // - assign_i is the current assignment: i = b(i) // - k is the number of cases for v // if (m.is_bool(b)) { SASSERT(k <= rational(2)); return; } SASSERT(m_bv.is_bv(b)); SASSERT(k.is_pos()); app_ref max_val(m_bv.mk_numeral(k-rational::one(), m_bv.get_bv_size(b)), m); expr_ref ub(m_bv.mk_ule(b, max_val), m); add_constraint(true, ub); } // // Process the partition stored in m_vars. // void process_partition() { expr_ref fml(m_current->fml(), m); ptr_vector vars; bool closed = true; while (extract_partition(vars)) { lbool r = m_qe.eliminate_exists(vars.size(), vars.c_ptr(), fml, m_free_vars, m_get_first, m_defs); vars.reset(); closed = closed && (r != l_undef); } TRACE("qe", tout << mk_pp(fml, m) << "\n";); m_current->add_child(fml)->reset_free_vars(); block_assignment(); } // variable queueing. contains_app& contains(app* x) { contains_app* result = 0; VERIFY(m_var2contains.find(x, result)); return *result; } bool find_min_weight(app*& x, rational& num_branches) { SASSERT(!m_current->has_var()); while (m_current->num_free_vars() > 0) { unsigned weight = UINT_MAX; unsigned nv = m_current->num_free_vars(); expr* fml = m_current->fml(); unsigned index = 0; for (unsigned i = 0; i < nv; ++i) { x = get_var(i); if (!has_plugin(x)) { break; } unsigned weight1 = plugin(get_var(i)).get_weight(contains(i), fml); if (weight1 < weight) { index = i; } } x = get_var(index); if (has_plugin(x) && plugin(x).get_num_branches(contains(x), fml, num_branches)) { return true; } TRACE("qe", tout << "setting variable " << mk_pp(x, m) << " free\n";); m_free_vars.push_back(x); m_current->del_var(x); } return false; } // // Solve for variables in fml. // Add a unit branch when variables are solved. // void solve_vars() { bool solved = true; while (solved) { expr_ref fml(m_current->fml(), m); conj_enum conjs(m, fml); solved = false; for (unsigned i = 0; !solved && i < m_plugins.size(); ++i) { qe_solver_plugin* p = m_plugins[i]; solved = p && p->solve(conjs, fml); SASSERT(m_new_vars.empty()); } } } }; // ------------------------------------------------ // quant_elim class quant_elim_new : public quant_elim { ast_manager& m; smt_params& m_fparams; expr_ref m_assumption; bool m_produce_models; ptr_vector m_plugins; volatile bool m_cancel; bool m_eliminate_variables_as_block; public: quant_elim_new(ast_manager& m, smt_params& p) : m(m), m_fparams(p), m_assumption(m), m_produce_models(m_fparams.m_model), m_cancel(false), m_eliminate_variables_as_block(true) { } virtual ~quant_elim_new() { reset(); } void reset() { for (unsigned i = 0; i < m_plugins.size(); ++i) { dealloc(m_plugins[i]); } } void set_cancel(bool f) { for (unsigned i = 0; i < m_plugins.size(); ++i) { m_plugins[i]->set_cancel(f); } m_cancel = f; } void checkpoint() { if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); cooperate("qe"); } void collect_statistics(statistics & st) const { for (unsigned i = 0; i < m_plugins.size(); ++i) { m_plugins[i]->collect_statistics(st); } } void updt_params(params_ref const& p) { m_eliminate_variables_as_block = p.get_bool("eliminate_variables_as_block", m_eliminate_variables_as_block); } void eliminate(bool is_forall, unsigned num_vars, app* const* vars, expr_ref& fml) { if (is_forall) { eliminate_forall_bind(num_vars, vars, fml); } else { eliminate_exists_bind(num_vars, vars, fml); } } virtual void bind_variables(unsigned num_vars, app* const* vars, expr_ref& fml) { if (num_vars > 0) { ptr_vector sorts; vector names; ptr_vector free_vars; for (unsigned i = 0; i < num_vars; ++i) { contains_app contains_x(m, vars[i]); if (contains_x(fml)) { sorts.push_back(m.get_sort(vars[i])); names.push_back(vars[i]->get_decl()->get_name()); free_vars.push_back(vars[i]); } } if (!free_vars.empty()) { expr_ref tmp(m); expr_abstract(m, 0, free_vars.size(), (expr*const*)free_vars.c_ptr(), fml, tmp); fml = m.mk_exists(free_vars.size(), sorts.c_ptr(), names.c_ptr(), tmp, 1); } } } virtual void set_assumption(expr* fml) { m_assumption = fml; } virtual lbool eliminate_exists( unsigned num_vars, app* const* vars, expr_ref& fml, app_ref_vector& free_vars, bool get_first, guarded_defs* defs) { if (get_first) { return eliminate_block(num_vars, vars, fml, free_vars, get_first, defs); } if (m_eliminate_variables_as_block) { return eliminate_block(num_vars, vars, fml, free_vars, get_first, defs); } for (unsigned i = 0; i < num_vars; ++i) { lbool r = eliminate_block(1, vars+i, fml, free_vars, get_first, defs); switch(r) { case l_false: return l_false; case l_undef: free_vars.append(num_vars-i-1,vars+1+i); return l_undef; default: break; } } return l_true; } private: lbool eliminate_block( unsigned num_vars, app* const* vars, expr_ref& fml, app_ref_vector& free_vars, bool get_first, guarded_defs* defs) { checkpoint(); if (has_quantifiers(fml)) { free_vars.append(num_vars, vars); return l_undef; } flet fl1(m_fparams.m_model, true); flet fl2(m_fparams.m_simplify_bit2int, true); flet fl3(m_fparams.m_arith_enum_const_mod, true); flet fl4(m_fparams.m_bv_enable_int2bv2int, true); flet fl5(m_fparams.m_array_canonize_simplify, true); flet fl6(m_fparams.m_relevancy_lvl, 0); TRACE("qe", for (unsigned i = 0; i < num_vars; ++i) { tout << mk_ismt2_pp(vars[i], m) << " "; } tout << "\n"; tout << mk_ismt2_pp(fml, m) << "\n"; ); expr_ref fml0(fml, m); quant_elim_plugin* th; pop_context(th); th->check(num_vars, vars, m_assumption, fml, get_first, free_vars, defs); push_context(th); TRACE("qe", for (unsigned i = 0; i < num_vars; ++i) { tout << mk_ismt2_pp(vars[i], m) << " "; } tout << "\n"; tout << "Input:\n" << mk_ismt2_pp(fml0, m) << "\n"; tout << "Result:\n" << mk_ismt2_pp(fml, m) << "\n";); if (m.is_false(fml)) { return l_false; } if (free_vars.empty()) { return l_true; } return l_undef; } void pop_context(quant_elim_plugin*& th) { if (m_plugins.empty()) { th = alloc(quant_elim_plugin, m, *this, m_fparams); th->add_plugin(mk_bool_plugin(*th)); th->add_plugin(mk_bv_plugin(*th)); th->add_plugin(mk_arith_plugin(*th, m_produce_models, m_fparams)); th->add_plugin(mk_array_plugin(*th)); th->add_plugin(mk_datatype_plugin(*th)); th->add_plugin(mk_dl_plugin(*th)); } else { th = m_plugins.back(); m_plugins.pop_back(); } } void push_context(quant_elim_plugin* th) { m_plugins.push_back(th); th->reset(); } void eliminate_exists_bind(unsigned num_vars, app* const* vars, expr_ref& fml) { checkpoint(); app_ref_vector free_vars(m); eliminate_exists(num_vars, vars, fml, free_vars, false, 0); bind_variables(free_vars.size(), free_vars.c_ptr(), fml); } void eliminate_forall_bind(unsigned num_vars, app* const* vars, expr_ref& fml) { expr_ref tmp(m); bool_rewriter rw(m); rw.mk_not(fml, tmp); eliminate_exists_bind(num_vars, vars, tmp); rw.mk_not(tmp, fml); } }; // ------------------------------------------------ // expr_quant_elim expr_quant_elim::expr_quant_elim(ast_manager& m, smt_params const& fp, params_ref const& p): m(m), m_fparams(fp), m_params(p), m_trail(m), m_qe(0), m_assumption(m.mk_true()) { } expr_quant_elim::~expr_quant_elim() { dealloc(m_qe); } void expr_quant_elim::operator()(expr* assumption, expr* fml, expr_ref& result) { TRACE("qe", tout << "elim input\n" << mk_ismt2_pp(fml, m) << "\n";); expr_ref_vector bound(m); result = fml; m_assumption = assumption; instantiate_expr(bound, result); elim(result); m_trail.reset(); m_visited.reset(); abstract_expr(bound.size(), bound.c_ptr(), result); TRACE("qe", tout << "elim result\n" << mk_ismt2_pp(result, m) << "\n";); } void expr_quant_elim::updt_params(params_ref const& p) { init_qe(); m_qe->updt_params(p); } void expr_quant_elim::collect_param_descrs(param_descrs& r) { r.insert("eliminate_variables_as_block", CPK_BOOL, "(default: true) eliminate variables as a block (true) or one at a time (false)"); } void expr_quant_elim::init_qe() { if (!m_qe) { m_qe = alloc(quant_elim_new, m, const_cast(m_fparams)); } } void expr_quant_elim::instantiate_expr(expr_ref_vector& bound, expr_ref& fml) { expr_free_vars fv; fv(fml); fv.set_default_sort(m.mk_bool_sort()); if (!fv.empty()) { expr_ref tmp(m); for (unsigned i = fv.size(); i > 0;) { --i; bound.push_back(m.mk_fresh_const("bound", fv[i])); } var_subst subst(m); subst(fml, bound.size(), bound.c_ptr(), tmp); fml = tmp; } } void expr_quant_elim::abstract_expr(unsigned sz, expr* const* bound, expr_ref& fml) { if (sz > 0) { expr_ref tmp(m); expr_abstract(m, 0, sz, bound, fml, tmp); fml = tmp; } } static void extract_vars(quantifier* q, expr_ref& new_body, app_ref_vector& vars) { ast_manager& m = new_body.get_manager(); expr_ref tmp(m); unsigned nd = q->get_num_decls(); for (unsigned i = 0; i < nd; ++i) { vars.push_back(m.mk_fresh_const("x",q->get_decl_sort(i))); } expr* const* exprs = (expr* const*)(vars.c_ptr()); var_subst subst(m); subst(new_body, vars.size(), exprs, tmp); inv_var_shifter shift(m); shift(tmp, vars.size(), new_body); } void expr_quant_elim::elim(expr_ref& result) { expr_ref tmp(m); ptr_vector todo; m_trail.push_back(result); todo.push_back(result); expr* e = 0, *r = 0; while (!todo.empty()) { e = todo.back(); if (m_visited.contains(e)) { todo.pop_back(); continue; } switch(e->get_kind()) { case AST_APP: { app* a = to_app(e); expr_ref_vector args(m); unsigned num_args = a->get_num_args(); bool all_visited = true; for (unsigned i = 0; i < num_args; ++i) { if (m_visited.find(a->get_arg(i), r)) { args.push_back(r); } else { todo.push_back(a->get_arg(i)); all_visited = false; } } if (all_visited) { r = m.mk_app(a->get_decl(), args.size(), args.c_ptr()); todo.pop_back(); m_trail.push_back(r); m_visited.insert(e, r); } break; } case AST_QUANTIFIER: { app_ref_vector vars(m); quantifier* q = to_quantifier(e); bool is_fa = q->is_forall(); tmp = q->get_expr(); extract_vars(q, tmp, vars); elim(tmp); init_qe(); m_qe->set_assumption(m_assumption); m_qe->eliminate(is_fa, vars.size(), vars.c_ptr(), tmp); m_trail.push_back(tmp); m_visited.insert(e, tmp); todo.pop_back(); break; } default: UNREACHABLE(); break; } } VERIFY (m_visited.find(result, e)); result = e; } void expr_quant_elim::collect_statistics(statistics & st) const { if (m_qe) { m_qe->collect_statistics(st); } } lbool expr_quant_elim::first_elim(unsigned num_vars, app* const* vars, expr_ref& fml, def_vector& defs) { app_ref_vector fvs(m); init_qe(); guarded_defs gdefs(m); lbool res = m_qe->eliminate_exists(num_vars, vars, fml, fvs, true, &gdefs); if (gdefs.size() > 0) { defs.reset(); defs.append(gdefs.defs(0)); fml = gdefs.guard(0); } return res; } bool expr_quant_elim::solve_for_var(app* var, expr* fml, guarded_defs& defs) { return solve_for_vars(1,&var, fml, defs); } bool expr_quant_elim::solve_for_vars(unsigned num_vars, app* const* vars, expr* _fml, guarded_defs& defs) { app_ref_vector fvs(m); expr_ref fml(_fml, m); TRACE("qe", tout << mk_pp(fml, m) << "\n";); init_qe(); lbool is_sat = m_qe->eliminate_exists(num_vars, vars, fml, fvs, false, &defs); return is_sat != l_undef; } void expr_quant_elim::set_cancel(bool f) { if (m_qe) { m_qe->set_cancel(f); } } // ------------------------------------------------ // expr_quant_elim_star1 bool expr_quant_elim_star1::visit_quantifier(quantifier * q) { if (!is_target(q)) { return true; } bool visited = true; visit(q->get_expr(), visited); return visited; } void expr_quant_elim_star1::reduce1_quantifier(quantifier * q) { if (!is_target(q)) { cache_result(q, q, 0); return; } quantifier_ref new_q(m); expr * new_body = 0; proof * new_pr; get_cached(q->get_expr(), new_body, new_pr); new_q = m.update_quantifier(q, new_body); expr_ref r(m); m_quant_elim(m_assumption, new_q, r); if (q == r.get()) { cache_result(q, q, 0); return; } proof_ref pr(m); if (m.proofs_enabled()) { pr = m.mk_rewrite(q, r); // TODO: improve justification } cache_result(q, r, pr); } expr_quant_elim_star1::expr_quant_elim_star1(ast_manager& m, smt_params const& p): simplifier(m), m_quant_elim(m, p), m_assumption(m.mk_true()) { } void expr_quant_elim_star1::reduce_with_assumption(expr* ctx, expr* fml, expr_ref& result) { proof_ref pr(m); m_assumption = ctx; (*this)(fml, result, pr); m_assumption = m.mk_true(); } void hoist_exists(expr_ref& fml, app_ref_vector& vars) { quantifier_hoister hoister(fml.get_manager()); hoister.pull_exists(fml, vars, fml); } void mk_exists(unsigned num_bound, app* const* vars, expr_ref& fml) { ast_manager& m = fml.get_manager(); expr_ref tmp(m); expr_abstract(m, 0, num_bound, (expr*const*)vars, fml, tmp); ptr_vector sorts; svector names; for (unsigned i = 0; i < num_bound; ++i) { sorts.push_back(vars[i]->get_decl()->get_range()); names.push_back(vars[i]->get_decl()->get_name()); } if (num_bound > 0) { tmp = m.mk_exists(num_bound, sorts.c_ptr(), names.c_ptr(), tmp, 1); } fml = tmp; } class simplify_solver_context : public i_solver_context { ast_manager& m; smt_params m_fparams; app_ref_vector* m_vars; expr_ref* m_fml; ptr_vector m_contains; atom_set m_pos; atom_set m_neg; public: simplify_solver_context(ast_manager& m): m(m), m_vars(0), m_fml(0) { add_plugin(mk_bool_plugin(*this)); add_plugin(mk_arith_plugin(*this, false, m_fparams)); } void updt_params(params_ref const& p) { m_fparams.updt_params(p); } virtual ~simplify_solver_context() { reset(); } void solve(expr_ref& fml, app_ref_vector& vars) { init(fml, vars); bool solved = true; do { conj_enum conjs(m, fml); solved = false; for (unsigned i = 0; !solved && i < m_plugins.size(); ++i) { qe_solver_plugin* p = m_plugins[i]; solved = p && p->solve(conjs, fml); } TRACE("qe", tout << (solved?"solved":"not solved") << "\n"; if (solved) tout << mk_ismt2_pp(fml, m) << "\n";); } while (solved); } virtual ast_manager& get_manager() { return m; } virtual atom_set const& pos_atoms() const { return m_pos; } virtual atom_set const& neg_atoms() const { return m_neg; } // Access current set of variables to solve virtual unsigned get_num_vars() const { return m_vars->size(); } virtual app* get_var(unsigned idx) const { return (*m_vars)[idx].get(); } virtual app*const* get_vars() const { return m_vars->c_ptr(); } virtual bool is_var(expr* e, unsigned& idx) const { for (unsigned i = 0; i < m_vars->size(); ++i) { if ((*m_vars)[i].get() == e) { idx = i; return true; } } return false; } virtual contains_app& contains(unsigned idx) { return *m_contains[idx]; } // callback to replace variable at index 'idx' with definition 'def' and updated formula 'fml' virtual void elim_var(unsigned idx, expr* fml, expr* def) { TRACE("qe", tout << mk_pp(m_vars->get(idx), m) << " " << mk_pp(fml, m) << "\n";); *m_fml = fml; m_vars->set(idx, m_vars->get(m_vars->size()-1)); m_vars->pop_back(); dealloc(m_contains[idx]); m_contains[idx] = m_contains[m_contains.size()-1]; m_contains.pop_back(); } // callback to add new variable to branch. virtual void add_var(app* x) { m_vars->push_back(x); } // callback to add constraints in branch. virtual void add_constraint(bool use_var, expr* l1 = 0, expr* l2 = 0, expr* l3 = 0) { UNREACHABLE(); } // eliminate finite domain variable 'var' from fml. virtual void blast_or(app* var, expr_ref& fml) { UNREACHABLE(); } private: void reset() { for (unsigned i = 0; i < m_contains.size(); ++i) { dealloc (m_contains[i]); } m_contains.reset(); } void init(expr_ref& fml, app_ref_vector& vars) { reset(); m_fml = &fml; m_vars = &vars; for (unsigned i = 0; i < vars.size(); ++i) { m_contains.push_back(alloc(contains_app, m, vars[i].get())); } } }; class simplify_rewriter_cfg::impl { ast_manager& m; simplify_solver_context m_ctx; public: impl(ast_manager& m) : m(m), m_ctx(m) {} void updt_params(params_ref const& p) { m_ctx.updt_params(p); } bool reduce_quantifier( quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr ) { // bool is_forall = old_q->is_forall(); app_ref_vector vars(m); TRACE("qe", tout << "simplifying" << mk_pp(new_body, m) << "\n";); result = new_body; extract_vars(old_q, result, vars); TRACE("qe", tout << "variables extracted" << mk_pp(result, m) << "\n";); if (old_q->is_forall()) { result = m.mk_not(result); } m_ctx.solve(result, vars); if (old_q->is_forall()) { expr* e = 0; result = m.is_not(result, e)?e:m.mk_not(result); } var_shifter shift(m); shift(result, vars.size(), result); expr_abstract(m, 0, vars.size(), (expr*const*)vars.c_ptr(), result, result); TRACE("qe", tout << "abstracted" << mk_pp(result, m) << "\n";); ptr_vector sorts; svector names; for (unsigned i = 0; i < vars.size(); ++i) { sorts.push_back(vars[i]->get_decl()->get_range()); names.push_back(vars[i]->get_decl()->get_name()); } if (!vars.empty()) { result = m.mk_quantifier(old_q->is_forall(), vars.size(), sorts.c_ptr(), names.c_ptr(), result, 1); } result_pr = 0; return true; } }; simplify_rewriter_cfg::simplify_rewriter_cfg(ast_manager& m) { imp = alloc(simplify_rewriter_cfg::impl, m); } simplify_rewriter_cfg::~simplify_rewriter_cfg() { dealloc(imp); } bool simplify_rewriter_cfg::reduce_quantifier( quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr ) { return imp->reduce_quantifier(old_q, new_body, new_patterns, new_no_patterns, result, result_pr); } void simplify_rewriter_cfg::updt_params(params_ref const& p) { imp->updt_params(p); } bool simplify_rewriter_cfg::pre_visit(expr* e) { if (!is_quantifier(e)) return true; quantifier * q = to_quantifier(e); return (q->get_num_patterns() == 0 && q->get_num_no_patterns() == 0); } void simplify_exists(app_ref_vector& vars, expr_ref& fml) { ast_manager& m = fml.get_manager(); simplify_solver_context ctx(m); ctx.solve(fml, vars); } } template class rewriter_tpl; z3-4.4.0/src/qe/qe_cmd.cpp0000644000175000017500000000360712540347414015003 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "qe_cmd.h" #include "qe.h" #include "cmd_context.h" #include "parametric_cmd.h" class qe_cmd : public parametric_cmd { expr * m_target; public: qe_cmd(char const* name = "elim-quantifiers"):parametric_cmd(name) {} virtual char const * get_usage() const { return " ( )*"; } virtual char const * get_main_descr() const { return "apply quantifier elimination to the supplied expression"; } virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { insert_timeout(p); p.insert("print", CPK_BOOL, "(default: true) print the simplified term."); p.insert("print_statistics", CPK_BOOL, "(default: false) print statistics."); } virtual ~qe_cmd() { } virtual void prepare(cmd_context & ctx) { parametric_cmd::prepare(ctx); m_target = 0; } virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { if (m_target == 0) return CPK_EXPR; return parametric_cmd::next_arg_kind(ctx); } virtual void set_next_arg(cmd_context & ctx, expr * arg) { m_target = arg; } virtual void execute(cmd_context & ctx) { smt_params par; proof_ref pr(ctx.m()); qe::expr_quant_elim_star1 qe(ctx.m(), par); expr_ref result(ctx.m()); qe(m_target, result, pr); if (m_params.get_bool("print", true)) { ctx.display(ctx.regular_stream(), result); ctx.regular_stream() << std::endl; } if (m_params.get_bool("print_statistics", false)) { statistics st; qe.collect_statistics(st); st.display(ctx.regular_stream()); } } }; void install_qe_cmd(cmd_context & ctx, char const * cmd_name) { ctx.insert(alloc(qe_cmd, cmd_name)); } z3-4.4.0/src/qe/vsubst_tactic.h0000644000175000017500000000112212540347414016063 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: vsubst_tactic.h Abstract: Check satisfiability of QF_NRA problems using virtual subsititution quantifier-elimination. Author: Nikolaj (nbjorner) 2011-05-16 Notes: --*/ #ifndef _VSUBST_TACTIC_H_ #define _VSUBST_TACTIC_H_ #include"params.h" class ast_manager; class tactic; tactic * mk_vsubst_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("vsubst", "checks satsifiability of quantifier-free non-linear constraints using virtual substitution.", "mk_vsubst_tactic(m, p)") */ #endif z3-4.4.0/src/qe/qe_bv_plugin.cpp0000644000175000017500000000471312540347414016224 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: bv_plugin.cpp Abstract: Eliminate Bit-vector variable from formula Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: Notes: --*/ #include "qe.h" #include "expr_safe_replace.h" #include "bv_decl_plugin.h" #include "model_evaluator.h" namespace qe { class bv_plugin : public qe_solver_plugin { expr_safe_replace m_replace; bv_util m_bv; public: bv_plugin(i_solver_context& ctx, ast_manager& m): qe_solver_plugin(m, m.mk_family_id("bv"), ctx), m_replace(m), m_bv(m) {} virtual void assign(contains_app& x, expr* fml, rational const& vl) { } virtual bool get_num_branches(contains_app& x, expr* fml, rational& nb) { unsigned sz = m_bv.get_bv_size(x.x()); nb = power(rational(2), sz); return true; } virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) { app_ref c(m_bv.mk_numeral(vl, m_bv.get_bv_size(x.x())), m); m_replace.apply_substitution(x.x(), c, fml); if (def) { *def = m_bv.mk_numeral(vl, m_bv.get_bv_size(x.x())); } } virtual bool project(contains_app& x, model_ref& model, expr_ref& fml) { model_evaluator model_eval(*model); expr_ref val_x(m); rational val(0); unsigned bv_size; model_eval(x.x(), val_x); m_bv.is_numeral(val_x, val, bv_size); subst(x, val, fml, 0); return true; } virtual unsigned get_weight(contains_app& contains_x, expr* fml) { return 2; } bool solve(conj_enum& conjs, expr* fml) { return false; } virtual bool is_uninterpreted(app* f) { switch(f->get_decl_kind()) { case OP_BSDIV0: case OP_BUDIV0: case OP_BSREM0: case OP_BUREM0: case OP_BSMOD0: case OP_BSDIV_I: case OP_BUDIV_I: case OP_BSREM_I: case OP_BUREM_I: case OP_BSMOD_I: return true; default: return false; } return false; } }; qe_solver_plugin* mk_bv_plugin(i_solver_context& ctx) { return alloc(bv_plugin, ctx, ctx.get_manager()); } } z3-4.4.0/src/qe/qe_sat_tactic.h0000644000175000017500000000106312540347414016015 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qe_sat_tactic.h Abstract: Procedure for quantifier satisfiability using quantifier elimination. Author: Nikolaj Bjorner (nbjorner) 2012-02-24 Revision History: --*/ #ifndef __QE_SAT_H__ #define __QE_SAT_H__ #include"tactic.h" namespace qe { tactic * mk_sat_tactic(ast_manager& m, params_ref const& p = params_ref()); }; /* ADD_TACTIC("qe-sat", "check satisfiability of quantified formulas using quantifier elimination.", "qe::mk_sat_tactic(m, p)") */ #endif z3-4.4.0/src/qe/qe_sat_tactic.cpp0000644000175000017500000006444612540347414016366 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qe_sat_tactic.cpp Abstract: Procedure for quantifier satisfiability using quantifier projection. Based on generalizations by Bjorner & Monniaux (see tvm\papers\z3qe\altqe.tex) Author: Nikolaj Bjorner (nbjorner) 2012-02-24 Revision History: --*/ #include "qe_sat_tactic.h" #include "quant_hoist.h" #include "ast_pp.h" #include "smt_kernel.h" #include "qe.h" #include "cooperate.h" #include "model_v2_pp.h" #include "expr_replacer.h" #include "th_rewriter.h" #include "expr_context_simplifier.h" // plugin registration. // solver specific projection operators. // connect goals to tactic namespace qe { class is_relevant_default : public i_expr_pred { public: bool operator()(expr* e) { return true; } }; class mk_atom_default : public i_nnf_atom { public: virtual void operator()(expr* e, bool pol, expr_ref& result) { if (pol) result = e; else result = result.get_manager().mk_not(e); } }; class sat_tactic : public tactic { // forall x . not forall y . not forall z . not forall u . fml. ast_manager& m; expr_ref m_false; volatile bool m_cancel; smt_params m_fparams; params_ref m_params; unsigned m_extrapolate_strategy_param; bool m_projection_mode_param; bool m_strong_context_simplify_param; bool m_ctx_simplify_local_param; vector m_vars; ptr_vector m_solvers; smt::kernel m_solver; expr_ref m_fml; expr_ref_vector m_Ms; expr_ref_vector m_assignments; is_relevant_default m_is_relevant; mk_atom_default m_mk_atom; th_rewriter m_rewriter; simplify_rewriter_star m_qe_rw; expr_strong_context_simplifier m_ctx_rewriter; class solver_context : public i_solver_context { ast_manager& m; sat_tactic& m_super; smt::kernel& m_solver; atom_set m_pos; atom_set m_neg; app_ref_vector m_vars; expr_ref m_fml; ptr_vector m_contains_app; bool m_projection_mode_param; public: solver_context(sat_tactic& s, unsigned idx): m(s.m), m_super(s), m_solver(*s.m_solvers[idx+1]), m_vars(m), m_fml(m), m_projection_mode_param(true) {} virtual ~solver_context() { std::for_each(m_contains_app.begin(), m_contains_app.end(), delete_proc()); } void init(expr* fml, unsigned idx) { m_fml = fml; for (unsigned j = 0; j < m_super.vars(idx).size(); ++j) { add_var(m_super.vars(idx)[j]); } get_nnf(m_fml, get_is_relevant(), get_mk_atom(), m_pos, m_neg); } void set_projection_mode(bool p) { m_projection_mode_param = p; } ast_manager& get_manager() { return m; } expr* fml() { return m_fml; } // set of atoms in current formula. virtual atom_set const& pos_atoms() const { return m_pos; } virtual atom_set const& neg_atoms() const { return m_neg; } // Access current set of variables to solve virtual unsigned get_num_vars() const { return m_vars.size(); } virtual app* get_var(unsigned idx) const { return m_vars[idx]; } virtual app*const* get_vars() const { return m_vars.c_ptr(); } virtual bool is_var(expr* e, unsigned& idx) const { for (unsigned i = 0; i < m_vars.size(); ++i) { if (e == m_vars[i]) return (idx = i, true); } return false; } virtual contains_app& contains(unsigned idx) { return *m_contains_app[idx]; } // callback to replace variable at index 'idx' with definition 'def' and updated formula 'fml' virtual void elim_var(unsigned idx, expr* fml, expr* def) { m_fml = fml; m_pos.reset(); m_neg.reset(); get_nnf(m_fml, get_is_relevant(), get_mk_atom(), m_pos, m_neg); m_vars.erase(idx); dealloc(m_contains_app[idx]); m_contains_app.erase(m_contains_app.c_ptr() + idx); } // callback to add new variable to branch. virtual void add_var(app* x) { m_vars.push_back(x); m_contains_app.push_back(alloc(contains_app, m, x)); } // callback to add constraints in branch. virtual void add_constraint(bool use_var, expr* l1 = 0, expr* l2 = 0, expr* l3 = 0) { ptr_buffer args; if (l1) args.push_back(l1); if (l2) args.push_back(l2); if (l3) args.push_back(l3); expr_ref cnstr(m.mk_or(args.size(), args.c_ptr()), m); m_solver.assert_expr(cnstr); TRACE("qe", tout << "add_constraint " << mk_pp(cnstr,m) << "\n";); } // eliminate finite domain variable 'var' from fml. virtual void blast_or(app* var, expr_ref& fml) { expr_ref result(m); expr_quant_elim qelim(m, m_super.m_fparams); qe::mk_exists(1, &var, fml); qelim(m.mk_true(), fml, result); fml = result; TRACE("qe", tout << mk_pp(var, m) << " " << mk_pp(fml, m) << "\n";); } void project_var_partial(unsigned i) { app* x = get_var(i); m_super.check_success(has_plugin(x)); qe_solver_plugin& p = plugin(m.get_sort(x)->get_family_id()); model_ref model; m_solver.get_model(model); m_super.check_success(p.project(contains(i), model, m_fml)); m_super.m_rewriter(m_fml); TRACE("qe", model_v2_pp(tout, *model); tout << "\n"; tout << mk_pp(m_fml, m) << "\n";); elim_var(i, m_fml, 0); } void project_var_full(unsigned i) { expr_ref result(m); app* x = get_var(i); expr_quant_elim qelim(m, m_super.m_fparams); qe::mk_exists(1, &x, m_fml); qelim(m.mk_true(), m_fml, result); m_fml = result; m_super.m_rewriter(m_fml); TRACE("qe", tout << mk_pp(m_fml, m) << "\n";); elim_var(i, m_fml, 0); } void project_var(unsigned i) { if (m_projection_mode_param) { project_var_full(i); } else { project_var_partial(i); } } }; public: sat_tactic(ast_manager& m, params_ref const& p = params_ref()): m(m), m_false(m.mk_false(), m), m_cancel(false), m_params(p), m_extrapolate_strategy_param(0), m_projection_mode_param(true), m_strong_context_simplify_param(true), m_ctx_simplify_local_param(false), m_solver(m, m_fparams), m_fml(m), m_Ms(m), m_assignments(m), m_rewriter(m), m_qe_rw(m), m_ctx_rewriter(m_fparams, m) { m_fparams.m_model = true; } virtual tactic * translate(ast_manager & m) { return alloc(sat_tactic, m); } virtual ~sat_tactic() { for (unsigned i = 0; i < m_solvers.size(); ++i) { dealloc(m_solvers[i]); } } virtual void set_cancel(bool f) { m_cancel = f; // not thread-safe when solvers are reset. // TBD: lock - this, reset() and init_Ms. for (unsigned i = 0; i < m_solvers.size(); ++i) { m_solvers[i]->set_cancel(f); } m_solver.set_cancel(f); m_ctx_rewriter.set_cancel(f); } virtual void operator()( goal_ref const& goal, goal_ref_buffer& result, model_converter_ref& mc, proof_converter_ref & pc, expr_dependency_ref& core) { try { checkpoint(); reset(); ptr_vector fmls; goal->get_formulas(fmls); m_fml = m.mk_and(fmls.size(), fmls.c_ptr()); TRACE("qe", tout << "input: " << mk_pp(m_fml,m) << "\n";); expr_ref tmp(m); m_qe_rw(m_fml, tmp); m_fml = tmp; TRACE("qe", tout << "reduced: " << mk_pp(m_fml,m) << "\n";); skolemize_existential_prefix(); extract_alt_form(m_fml); model_ref model; expr_ref res = qt(0, m.mk_true(), model); goal->inc_depth(); if (m.is_false(res)) { goal->assert_expr(res); } else { goal->reset(); // equi-satisfiable. What to do with model? mc = model2model_converter(&*model); } result.push_back(goal.get()); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } virtual void collect_statistics(statistics & st) const { for (unsigned i = 0; i < m_solvers.size(); ++i) { m_solvers[i]->collect_statistics(st); } m_solver.collect_statistics(st); m_ctx_rewriter.collect_statistics(st); } virtual void reset_statistics() { for (unsigned i = 0; i < m_solvers.size(); ++i) { m_solvers[i]->reset_statistics(); } m_solver.reset_statistics(); m_ctx_rewriter.reset_statistics(); } virtual void cleanup() {} virtual void updt_params(params_ref const & p) { m_extrapolate_strategy_param = p.get_uint("extrapolate_strategy", m_extrapolate_strategy_param); m_projection_mode_param = p.get_bool("projection_mode", m_projection_mode_param); m_strong_context_simplify_param = p.get_bool("strong_context_simplify", m_strong_context_simplify_param); m_ctx_simplify_local_param = p.get_bool("strong_context_simplify_local", m_ctx_simplify_local_param); m_fparams.updt_params(p); m_qe_rw.updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { r.insert("extrapolate_strategy",CPK_UINT, "(default: 0 trivial extrapolation) 1 - nnf strengthening 2 - smt-test 3 - nnf_weakening"); r.insert("projection_mode", CPK_BOOL, "(default: true - full) false - partial quantifier instantiation"); r.insert("strong_context_simplify", CPK_BOOL, "(default: true) use strong context simplifier on result of quantifier elimination"); r.insert("strong_context_simplify_local", CPK_BOOL, "(default: false) use strong context simplifier locally on the new formula only"); } private: unsigned num_alternations() const { return m_vars.size(); } void init_Ms() { for (unsigned i = 0; i < num_alternations(); ++i) { m_Ms.push_back(m.mk_true()); m_solvers.push_back(alloc(smt::kernel, m, m_fparams, m_params)); } m_Ms.push_back(m_fml); m_solvers.push_back(alloc(smt::kernel, m, m_fparams, m_params)); m_solvers.back()->assert_expr(m_fml); } expr* M(unsigned i) { return m_Ms[i].get(); } app_ref_vector const& vars(unsigned i) { return m_vars[i]; } smt::kernel& solver(unsigned i) { return *m_solvers[i]; } void reset() { m_fml = 0; m_Ms.reset(); m_vars.reset(); } void skolemize_existential_prefix() { quantifier_hoister hoist(m); expr_ref result(m); app_ref_vector vars(m); hoist.pull_exists(m_fml, vars, result); m_fml = result; } // // fa x ex y fa z . phi // fa x ! fa y ! fa z ! (!phi) // void extract_alt_form(expr* fml) { quantifier_hoister hoist(m); expr_ref result(m); bool is_fa; unsigned parity = 0; m_fml = fml; while (true) { app_ref_vector vars(m); hoist(m_fml, vars, is_fa, result); if (vars.empty()) { break; } SASSERT(((parity & 0x1) == 0) == is_fa); ++parity; TRACE("qe", tout << "Hoist " << (is_fa?"fa":"ex") << "\n"; for (unsigned i = 0; i < vars.size(); ++i) { tout << mk_pp(vars[i].get(), m) << " "; } tout << "\n"; tout << mk_pp(result, m) << "\n";); m_vars.push_back(vars); m_fml = result; } // // negate formula if the last quantifier is universal. // so that normal form fa x ! fa y ! fa z ! psi // is obtained. // if ((parity & 0x1) == 1) { m_fml = m.mk_not(m_fml); } init_Ms(); checkpoint(); } /** \brief Main quantifier test algorithm loop. */ expr_ref qt(unsigned i, expr* ctx, model_ref& model) { model_ref model1; while (true) { IF_VERBOSE(1, verbose_stream() << "(qt " << i << ")\n";); TRACE("qe", tout << i << " " << mk_pp(ctx, m) << "\n"; display(tout);); checkpoint(); if (is_sat(i, ctx, model)) { expr_ref ctxM = extrapolate(i, ctx, M(i)); if (i == num_alternations()) { return ctxM; } expr_ref res = qt(i+1, ctxM, model1); if (m.is_false(res)) { return ctxM; } project(i, res); } else { return m_false; } } UNREACHABLE(); return expr_ref(m); } /** \brief compute extrapolant Assume A & B is sat. Compute C, such that 1. A & C is sat, 2. not B & C is unsat. (C strengthens B and is still satisfiable with A). */ expr_ref extrapolate(unsigned i, expr* A, expr* B) { switch(m_extrapolate_strategy_param) { case 0: return trivial_extrapolate(i, A, B); case 1: return nnf_strengthening_extrapolate(i, A, B); case 2: return smt_test_extrapolate(i, A, B); case 3: return nnf_weakening_extrapolate(i, A, B); default: return trivial_extrapolate(i, A, B); } } expr_ref trivial_extrapolate(unsigned i, expr* A, expr* B) { return expr_ref(B, m); } expr_ref conjunction_extrapolate(unsigned i, expr* A, expr* B) { return expr_ref(m.mk_and(A, B), m); } /** Set C = nnf(B), That is, C is the NNF version of B. For each literal in C in order, replace the literal by False and check the conditions for extrapolation: 1. not B & C is unsat, 2. A & C is sat. The first check holds by construction, so it is redundant. The second is not redundant. Instead of replacing literals in an NNF formula, one simply asserts the negation of that literal. */ expr_ref nnf_strengthening_extrapolate(unsigned i, expr* A, expr* B) { expr_ref Bnnf(B, m); atom_set pos, neg; get_nnf(Bnnf, m_is_relevant, m_mk_atom, pos, neg); expr_substitution sub(m); remove_duplicates(pos, neg); // Assumption: B is already asserted in solver[i]. smt::kernel& solver = *m_solvers[i]; solver.push(); solver.assert_expr(A); nnf_strengthen(solver, pos, m.mk_false(), sub); nnf_strengthen(solver, neg, m.mk_true(), sub); solver.pop(1); scoped_ptr replace = mk_default_expr_replacer(m); replace->set_substitution(&sub); (*replace)(Bnnf); m_rewriter(Bnnf); TRACE("qe", tout << "A: " << mk_pp(A, m) << "\n"; tout << "B: " << mk_pp(B, m) << "\n"; tout << "B': " << mk_pp(Bnnf.get(), m) << "\n"; // solver.display(tout); ); DEBUG_CODE( solver.push(); solver.assert_expr(m.mk_not(B)); solver.assert_expr(Bnnf); lbool is_sat = solver.check(); TRACE("qe", tout << "is sat: " << is_sat << "\n";); SASSERT(is_sat == l_false); solver.pop(1);); DEBUG_CODE( solver.push(); solver.assert_expr(A); solver.assert_expr(Bnnf); lbool is_sat = solver.check(); TRACE("qe", tout << "is sat: " << is_sat << "\n";); SASSERT(is_sat == l_true); solver.pop(1);); return Bnnf; } void nnf_strengthen(smt::kernel& solver, atom_set& atoms, expr* value, expr_substitution& sub) { atom_set::iterator it = atoms.begin(), end = atoms.end(); for (; it != end; ++it) { solver.push(); solver.assert_expr(m.mk_iff(*it, value)); lbool is_sat = solver.check(); solver.pop(1); if (is_sat == l_true) { TRACE("qe", tout << "consistent with: " << mk_pp(*it, m) << " " << mk_pp(value, m) << "\n";); sub.insert(*it, value); solver.assert_expr(m.mk_iff(*it, value)); } checkpoint(); } } void remove_duplicates(atom_set& pos, atom_set& neg) { ptr_vector to_delete; atom_set::iterator it = pos.begin(), end = pos.end(); for (; it != end; ++it) { if (neg.contains(*it)) { to_delete.push_back(*it); } } for (unsigned j = 0; j < to_delete.size(); ++j) { pos.remove(to_delete[j]); neg.remove(to_delete[j]); } } /** Set C = nnf(B), That is, C is the NNF version of B. For each literal in C in order, replace the literal by True and check the conditions for extrapolation 1. not B & C is unsat, 2. A & C is sat. The second holds by construction and is redundant. The first is not redundant. */ expr_ref nnf_weakening_extrapolate(unsigned i, expr* A, expr* B) { expr_ref Bnnf(B, m); atom_set pos, neg; get_nnf(Bnnf, m_is_relevant, m_mk_atom, pos, neg); remove_duplicates(pos, neg); expr_substitution sub(m); m_solver.push(); m_solver.assert_expr(A); m_solver.assert_expr(m.mk_not(B)); nnf_weaken(m_solver, Bnnf, pos, m.mk_true(), sub); nnf_weaken(m_solver, Bnnf, neg, m.mk_false(), sub); scoped_ptr replace = mk_default_expr_replacer(m); replace->set_substitution(&sub); (*replace)(Bnnf); m_rewriter(Bnnf); m_solver.pop(1); return Bnnf; } void nnf_weaken(smt::kernel& solver, expr_ref& B, atom_set& atoms, expr* value, expr_substitution& sub) { atom_set::iterator it = atoms.begin(), end = atoms.end(); for (; it != end; ++it) { solver.push(); expr_ref fml = B; mk_default_expr_replacer(m)->apply_substitution(*it, value, fml); solver.assert_expr(fml); if (solver.check() == l_false) { sub.insert(*it, value); B = fml; } solver.pop(1); checkpoint(); } } /** Use the model for A & B to extrapolate. Initially, C is conjunction of literals from B that are in model of A & B. The model is a conjunction of literals. Let us denote this set $C$. We see that the conditions for extrapolation are satisfied. Furthermore, C can be generalized by removing literals from C as long as !B & A & C is unsatisfiable. */ expr_ref smt_test_extrapolate(unsigned i, expr* A, expr* B) { expr_ref_vector proxies(m), core(m); obj_map proxy_map; checkpoint(); m_solver.push(); m_solver.assert_expr(m.mk_not(B)); for (unsigned i = 0; i < m_assignments.size(); ++i) { proxies.push_back(m.mk_fresh_const("proxy",m.mk_bool_sort())); proxy_map.insert(proxies.back(), m_assignments[i].get()); m_solver.assert_expr(m.mk_iff(proxies.back(), m_assignments[i].get())); TRACE("qe", tout << "assignment: " << mk_pp(m_assignments[i].get(), m) << "\n";); } lbool is_sat = m_solver.check(proxies.size(), proxies.c_ptr()); SASSERT(is_sat == l_false); unsigned core_size = m_solver.get_unsat_core_size(); for (unsigned i = 0; i < core_size; ++i) { core.push_back(proxy_map.find(m_solver.get_unsat_core_expr(i))); } expr_ref result(m.mk_and(core.size(), core.c_ptr()), m); TRACE("qe", tout << "core: " << mk_pp(result, m) << "\n"; tout << mk_pp(A, m) << "\n"; tout << mk_pp(B, m) << "\n";); m_solver.pop(1); return result; } /** \brief project vars(idx) from fml relative to M(idx). */ void project(unsigned idx, expr* _fml) { SASSERT(idx < num_alternations()); expr_ref fml(_fml, m); if (m_strong_context_simplify_param && m_ctx_simplify_local_param) { m_ctx_rewriter.push(); m_ctx_rewriter.assert_expr(M(idx)); m_ctx_rewriter(fml); m_ctx_rewriter.pop(); TRACE("qe", tout << mk_pp(_fml, m) << "\n-- context simplify -->\n" << mk_pp(fml, m) << "\n";); } solver_context ctx(*this, idx); ctx.add_plugin(mk_arith_plugin(ctx, false, m_fparams)); ctx.add_plugin(mk_bool_plugin(ctx)); ctx.add_plugin(mk_bv_plugin(ctx)); ctx.init(fml, idx); ctx.set_projection_mode(m_projection_mode_param); m_solvers[idx+1]->push(); while (ctx.get_num_vars() > 0) { VERIFY(l_true == m_solvers[idx+1]->check()); ctx.project_var(ctx.get_num_vars()-1); } m_solvers[idx+1]->pop(1); expr_ref not_fml(m.mk_not(ctx.fml()), m); m_rewriter(not_fml); if (m_strong_context_simplify_param && !m_ctx_simplify_local_param) { m_ctx_rewriter.push(); m_ctx_rewriter.assert_expr(M(idx)); m_ctx_rewriter(not_fml); m_ctx_rewriter.pop(); } expr_ref tmp(m.mk_and(M(idx), not_fml), m); m_rewriter(tmp); m_Ms[idx] = tmp; m_solvers[idx]->assert_expr(not_fml); TRACE("qe", tout << mk_pp(fml, m) << "\n--->\n"; tout << mk_pp(tmp, m) << "\n";); } void checkpoint() { if (m_cancel) { throw tactic_exception(TACTIC_CANCELED_MSG); } cooperate("qe-sat"); } void check_success(bool ok) { if (!ok) { TRACE("qe", tout << "check failed\n";); throw tactic_exception(TACTIC_CANCELED_MSG); } } bool is_sat(unsigned i, expr* ctx, model_ref& model) { smt::kernel& solver = *m_solvers[i]; solver.push(); solver.assert_expr(ctx); lbool r = solver.check(); m_assignments.reset(); solver.get_assignments(m_assignments); solver.pop(1); check_success(r != l_undef); if (r == l_true && i == 0) solver.get_model(model); return r == l_true; } void display(std::ostream& out) { bool is_fa = true; for (unsigned i = 0; i < num_alternations(); ++i) { out << (is_fa?"fa ":"ex "); for (unsigned j = 0; j < vars(i).size(); ++j) { out << mk_pp(m_vars[i][j].get(), m) << " "; } out << "\n"; out << mk_pp(m_Ms[i+1].get(), m) << "\n"; is_fa = !is_fa; } } }; tactic * mk_sat_tactic(ast_manager& m, params_ref const& p) { return alloc(sat_tactic, m, p); } }; z3-4.4.0/src/qe/qe_lite.h0000644000175000017500000000321312540347414014633 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qe_lite.h Abstract: Light weight partial quantifier-elimination procedures Author: Nikolaj Bjorner (nbjorner) 2012-10-17 Revision History: --*/ #ifndef __QE_LITE_H__ #define __QE_LITE_H__ #include "ast.h" #include "uint_set.h" #include "params.h" class tactic; class qe_lite { class impl; impl * m_impl; public: qe_lite(ast_manager& m); ~qe_lite(); /** \brief Apply light-weight quantifier elimination on constants provided as vector of variables. Return the updated formula and updated set of variables that were not eliminated. */ void operator()(app_ref_vector& vars, expr_ref& fml); /** \brief Apply light-weight quantifier elimination to variables present/absent in the index set. If 'index_of_bound' is true, then the index_set is treated as the set of bound variables. if 'index_of_bound' is false, then index_set is treated as the set of variables that are not bound (variables that are not in the index set are bound). */ void operator()(uint_set const& index_set, bool index_of_bound, expr_ref& fml); void operator()(uint_set const& index_set, bool index_of_bound, expr_ref_vector& conjs); /** \brief full rewriting based light-weight quantifier elimination round. */ void operator()(expr_ref& fml, proof_ref& pr); void set_cancel(bool f); }; tactic * mk_qe_lite_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qe-light", "apply light-weight quantifier elimination.", "mk_qe_lite_tactic(m, p)") */ #endif z3-4.4.0/src/qe/qe_dl_plugin.cpp0000644000175000017500000001742612540347414016221 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "qe.h" #include "expr_safe_replace.h" #include "dl_decl_plugin.h" #include "obj_pair_hashtable.h" #include "ast_pp.h" namespace qe { // --------------------- // dl_plugin class eq_atoms { expr_ref_vector m_eqs; expr_ref_vector m_neqs; app_ref_vector m_eq_atoms; app_ref_vector m_neq_atoms; public: eq_atoms(ast_manager& m): m_eqs(m), m_neqs(m), m_eq_atoms(m), m_neq_atoms(m) {} unsigned num_eqs() const { return m_eqs.size(); } expr* eq(unsigned i) const { return m_eqs[i]; } app* eq_atom(unsigned i) const { return m_eq_atoms[i]; } unsigned num_neqs() const { return m_neqs.size(); } app* neq_atom(unsigned i) const { return m_neq_atoms[i]; } expr* neq(unsigned i) const { return m_neqs[i]; } void add_eq(app* atom, expr * e) { m_eq_atoms.push_back(atom); m_eqs.push_back(e); } void add_neq(app* atom, expr* e) { m_neq_atoms.push_back(atom); m_neqs.push_back(e); } }; class dl_plugin : public qe_solver_plugin { typedef obj_pair_map eqs_cache; expr_safe_replace m_replace; datalog::dl_decl_util m_util; expr_ref_vector m_trail; eqs_cache m_eqs_cache; public: dl_plugin(i_solver_context& ctx, ast_manager& m) : qe_solver_plugin(m, m.mk_family_id("datalog_relation"), ctx), m_replace(m), m_util(m), m_trail(m) { } virtual ~dl_plugin() { eqs_cache::iterator it = m_eqs_cache.begin(), end = m_eqs_cache.end(); for (; it != end; ++it) { dealloc(it->get_value()); } } bool get_num_branches(contains_app & x,expr * fml,rational & num_branches) { if (!update_eqs(x, fml)) { return false; } eq_atoms& eqs = get_eqs(x.x(), fml); uint64 domain_size; if (is_small_domain(x, eqs, domain_size)) { num_branches = rational(domain_size, rational::ui64()); } else { num_branches = rational(eqs.num_eqs() + 1); } return true; } void assign(contains_app & x,expr * fml,const rational & v) { SASSERT(v.is_unsigned()); eq_atoms& eqs = get_eqs(x.x(), fml); unsigned uv = v.get_unsigned(); uint64 domain_size; if (is_small_domain(x, eqs, domain_size)) { SASSERT(v < rational(domain_size, rational::ui64())); assign_small_domain(x, eqs, uv); } else { assign_large_domain(x, eqs, uv); } } void subst(contains_app & x,const rational & v,expr_ref & fml, expr_ref* def) { SASSERT(v.is_unsigned()); eq_atoms& eqs = get_eqs(x.x(), fml); unsigned uv = v.get_unsigned(); uint64 domain_size; if (is_small_domain(x, eqs, domain_size)) { SASSERT(uv < domain_size); subst_small_domain(x, eqs, uv, fml); } else { subst_large_domain(x, eqs, uv, fml); } if (def) { *def = 0; // TBD } } virtual bool solve(conj_enum& conjs, expr* fml) { return false; } private: bool is_small_domain(contains_app& x, eq_atoms& eqs, uint64& domain_size) { VERIFY(m_util.try_get_size(m.get_sort(x.x()), domain_size)); return domain_size < eqs.num_eqs() + eqs.num_neqs(); } void assign_small_domain(contains_app & x,eq_atoms& eqs, unsigned value) { expr_ref vl(m_util.mk_numeral(value, m.get_sort(x.x())), m); expr_ref eq(m.mk_eq(x.x(), vl), m); m_ctx.add_constraint(true, eq); } void assign_large_domain(contains_app & x,eq_atoms& eqs, unsigned v) { if (v < eqs.num_eqs()) { m_ctx.add_constraint(true, eqs.eq_atom(v)); } else { SASSERT(v == eqs.num_eqs()); for (unsigned i = 0; i < eqs.num_eqs(); ++i) { expr_ref neq(m.mk_not(eqs.eq_atom(i)), m); m_ctx.add_constraint(true, neq); } for (unsigned i = 0; i < eqs.num_neqs(); ++i) { expr_ref neq(m.mk_not(eqs.neq_atom(i)), m); m_ctx.add_constraint(true, neq); } } } void subst_small_domain(contains_app & x,eq_atoms& eqs, unsigned v,expr_ref & fml) { expr_ref vl(m_util.mk_numeral(v, m.get_sort(x.x())), m); m_replace.apply_substitution(x.x(), vl, fml); } // assumes that all disequalities can be satisfied. void subst_large_domain(contains_app & x,eq_atoms& eqs, unsigned w,expr_ref & fml) { SASSERT(w <= eqs.num_eqs()); if (w < eqs.num_eqs()) { expr* e = eqs.eq(w); m_replace.apply_substitution(x.x(), e, fml); } else { for (unsigned i = 0; i < eqs.num_eqs(); ++i) { m_replace.apply_substitution(eqs.eq_atom(i), m.mk_false(), fml); } for (unsigned i = 0; i < eqs.num_neqs(); ++i) { m_replace.apply_substitution(eqs.neq_atom(i), m.mk_true(), fml); } } } eq_atoms& get_eqs(app* x, expr* fml) { eq_atoms* eqs = 0; VERIFY(m_eqs_cache.find(x, fml, eqs)); return *eqs; } bool update_eqs(contains_app& contains_x, expr* fml) { eq_atoms* eqs = 0; if (m_eqs_cache.find(contains_x.x(), fml, eqs)) { return true; } eqs = alloc(eq_atoms, m); if (!update_eqs(*eqs, contains_x, fml, m_ctx.pos_atoms(), true)) { dealloc(eqs); return false; } if (!update_eqs(*eqs, contains_x, fml, m_ctx.neg_atoms(), false)) { dealloc(eqs); return false; } m_trail.push_back(contains_x.x()); m_trail.push_back(fml); m_eqs_cache.insert(contains_x.x(), fml, eqs); return true; } bool update_eqs(eq_atoms& eqs, contains_app& contains_x, expr* fml, atom_set const& tbl, bool is_pos) { atom_set::iterator it = tbl.begin(), end = tbl.end(); expr* x = contains_x.x(); for (; it != end; ++it) { app* e = *it; if (!contains_x(e)) { continue; } if (m_util.is_lt(e)) { NOT_IMPLEMENTED_YET(); } expr* e1, *e2; if (!m.is_eq(e, e1, e2)) { TRACE("quant_elim", tout << "Cannot handle: " << mk_pp(e, m) << "\n";); return false; } if (x == e2) { std::swap(e1, e2); } if (contains_x(e2) || x != e1) { TRACE("quant_elim", tout << "Cannot handle: " << mk_pp(e, m) << "\n";); return false; } if (is_pos) { eqs.add_eq(e, e2); } else { eqs.add_neq(e, e2); } } return true; } }; qe_solver_plugin* mk_dl_plugin(i_solver_context& ctx) { return alloc(dl_plugin, ctx, ctx.get_manager()); } } z3-4.4.0/src/qe/qe_util.h0000644000175000017500000000123512540347414014655 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: qe_util.h Abstract: Utilities for quantifiers. Author: Nikolaj Bjorner (nbjorner) 2013-08-28 Revision History: --*/ #ifndef _QE_UTIL_H_ #define _QE_UTIL_H_ #include "ast.h" namespace qe { /** \brief Collect top-level conjunctions and disjunctions. */ void flatten_and(expr_ref_vector& result); void flatten_and(expr* fml, expr_ref_vector& result); void flatten_or(expr_ref_vector& result); void flatten_or(expr* fml, expr_ref_vector& result); expr_ref mk_and(expr_ref_vector const& fmls); expr_ref mk_or(expr_ref_vector const& fmls); } #endif z3-4.4.0/src/qe/qe.h0000644000175000017500000003072212540347414013623 0ustar michaelmichael/*++ Copyright (c) 2010 Microsoft Corporation Module Name: qe.h Abstract: Quantifier-elimination procedures Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: --*/ #ifndef __QE_H__ #define __QE_H__ #include "ast.h" #include "smt_params.h" #include "statistics.h" #include "lbool.h" #include "expr_functors.h" #include "simplifier.h" #include "rewriter.h" #include "model.h" #include "params.h" namespace qe { class i_nnf_atom { public: virtual void operator()(expr* e, bool pol, expr_ref& result) = 0; }; typedef obj_hashtable atom_set; class qe_solver_plugin; class i_solver_context { protected: class is_relevant : public i_expr_pred { i_solver_context& m_s; public: is_relevant(i_solver_context& s):m_s(s) {} virtual bool operator()(expr* e); }; class mk_atom_fn : public i_nnf_atom { i_solver_context& m_s; public: mk_atom_fn(i_solver_context& s) : m_s(s) {} void operator()(expr* e, bool p, expr_ref& result); }; is_relevant m_is_relevant; mk_atom_fn m_mk_atom; ptr_vector m_plugins; // fid -> plugin public: i_solver_context():m_is_relevant(*this), m_mk_atom(*this) {} virtual ~i_solver_context(); void add_plugin(qe_solver_plugin* p); bool has_plugin(app* x); qe_solver_plugin& plugin(app* x); qe_solver_plugin& plugin(unsigned fid) { return *m_plugins[fid]; } void mk_atom(expr* e, bool p, expr_ref& result); virtual ast_manager& get_manager() = 0; // set of atoms in current formula. virtual atom_set const& pos_atoms() const = 0; virtual atom_set const& neg_atoms() const = 0; // Access current set of variables to solve virtual unsigned get_num_vars() const = 0; virtual app* get_var(unsigned idx) const = 0; virtual app*const* get_vars() const = 0; virtual bool is_var(expr* e, unsigned& idx) const; virtual contains_app& contains(unsigned idx) = 0; // callback to replace variable at index 'idx' with definition 'def' and updated formula 'fml' virtual void elim_var(unsigned idx, expr* fml, expr* def) = 0; // callback to add new variable to branch. virtual void add_var(app* x) = 0; // callback to add constraints in branch. virtual void add_constraint(bool use_var, expr* l1 = 0, expr* l2 = 0, expr* l3 = 0) = 0; // eliminate finite domain variable 'var' from fml. virtual void blast_or(app* var, expr_ref& fml) = 0; i_expr_pred& get_is_relevant() { return m_is_relevant; } i_nnf_atom& get_mk_atom() { return m_mk_atom; } }; class conj_enum { ast_manager& m; expr_ref_vector m_conjs; public: conj_enum(ast_manager& m, expr* e); class iterator { conj_enum* m_super; unsigned m_index; public: iterator(conj_enum& c, bool first) : m_super(&c), m_index(first?0:c.m_conjs.size()) {} expr* operator*() { return m_super->m_conjs[m_index].get(); } iterator& operator++() { m_index++; return *this; } bool operator==(iterator const& it) const { return m_index == it.m_index; } bool operator!=(iterator const& it) const { return m_index != it.m_index; } }; void extract_equalities(expr_ref_vector& eqs); iterator begin() { return iterator(*this, true); } iterator end() { return iterator(*this, false); } }; // // interface for plugins to eliminate quantified variables. // class qe_solver_plugin { protected: ast_manager& m; family_id m_fid; i_solver_context& m_ctx; public: qe_solver_plugin(ast_manager& m, family_id fid, i_solver_context& ctx) : m(m), m_fid(fid), m_ctx(ctx) {} virtual ~qe_solver_plugin() {} family_id get_family_id() { return m_fid; } /** \brief Return number of case splits for variable x in fml. */ virtual bool get_num_branches(contains_app& x, expr* fml, rational& num_branches) = 0; /** \brief Given a case split index 'vl' assert the constraints associated with it. */ virtual void assign(contains_app& x, expr* fml, rational const& vl) = 0; /** \brief The case splits associated with 'vl' are satisfiable. Apply the elimination for fml corresponding to case split. If def is non-null, then retrieve the realizer corresponding to the case split. */ virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) = 0; /** \brief solve quantified variable. */ virtual bool solve(conj_enum& conjs, expr* fml) = 0; /** \brief project variable x for fml given model. Assumption: model |= fml[x] Projection updates fml to fml', such that: - fml' -> fml - model |= fml' - fml' does not contain x return false if the method is not implemented. */ virtual bool project(contains_app& x, model_ref& model, expr_ref& fml) { return false; } /** \brief assign branching penalty to variable x for 'fml'. */ virtual unsigned get_weight(contains_app& x, expr* fml) { return UINT_MAX; } /** \brief simplify formula. */ virtual bool simplify(expr_ref& fml) { return false; } /** \brief Normalize literal during NNF conversion. */ virtual bool mk_atom(expr* e, bool p, expr_ref& result) { return false; } /** \brief Determine whether sub-term is uninterpreted with respect to quantifier elimination. */ virtual bool is_uninterpreted(app* f) { return true; } }; qe_solver_plugin* mk_datatype_plugin(i_solver_context& ctx); qe_solver_plugin* mk_bool_plugin(i_solver_context& ctx); qe_solver_plugin* mk_bv_plugin(i_solver_context& ctx); qe_solver_plugin* mk_dl_plugin(i_solver_context& ctx); qe_solver_plugin* mk_array_plugin(i_solver_context& ctx); qe_solver_plugin* mk_arith_plugin(i_solver_context& ctx, bool produce_models, smt_params& p); class def_vector { func_decl_ref_vector m_vars; expr_ref_vector m_defs; def_vector& operator=(def_vector const& other); public: def_vector(ast_manager& m): m_vars(m), m_defs(m) {} def_vector(def_vector const& other): m_vars(other.m_vars), m_defs(other.m_defs) {} void push_back(func_decl* v, expr* e) { m_vars.push_back(v); m_defs.push_back(e); } void reset() { m_vars.reset(); m_defs.reset(); } void append(def_vector const& o) { m_vars.append(o.m_vars); m_defs.append(o.m_defs); } unsigned size() const { return m_defs.size(); } void shrink(unsigned sz) { m_vars.shrink(sz); m_defs.shrink(sz); } bool empty() const { return m_defs.empty(); } func_decl* var(unsigned i) const { return m_vars[i]; } expr* def(unsigned i) const { return m_defs[i]; } expr_ref_vector::element_ref def_ref(unsigned i) { return m_defs[i]; } void normalize(); void project(unsigned num_vars, app* const* vars); }; /** \brief Guarded definitions. A realizer to a an existential quantified formula is a disjunction together with a substitution from the existentially quantified variables to terms such that: 1. The original formula (exists (vars) fml) is equivalent to the disjunction of guards. 2. Each guard is equivalent to fml where 'vars' are replaced by the substitution associated with the guard. */ class guarded_defs { expr_ref_vector m_guards; vector m_defs; bool inv(); public: guarded_defs(ast_manager& m): m_guards(m) { SASSERT(inv()); } void add(expr* guard, def_vector const& defs); unsigned size() const { return m_guards.size(); } def_vector const& defs(unsigned i) const { return m_defs[i]; } expr* guard(unsigned i) const { return m_guards[i]; } std::ostream& display(std::ostream& out) const; void project(unsigned num_vars, app* const* vars); }; class quant_elim; class expr_quant_elim { ast_manager& m; smt_params const& m_fparams; params_ref m_params; expr_ref_vector m_trail; obj_map m_visited; quant_elim* m_qe; expr* m_assumption; public: expr_quant_elim(ast_manager& m, smt_params const& fp, params_ref const& p = params_ref()); ~expr_quant_elim(); void operator()(expr* assumption, expr* fml, expr_ref& result); void collect_statistics(statistics & st) const; void updt_params(params_ref const& p); void collect_param_descrs(param_descrs& r); /** \brief try to eliminate 'vars' and find first satisfying assignment. return l_true if satisfiable, l_false if unsatisfiable, l_undef if the formula could not be satisfied modulo eliminating the quantified variables. */ lbool first_elim(unsigned num_vars, app* const* vars, expr_ref& fml, def_vector& defs); /** \brief solve for (exists (var) fml). Return false if operation failed. Return true and list of pairs (t_i, fml_i) in such that fml_1 \/ ... \/ fml_n == (exists (var) fml) and fml_i => fml[t_i] */ bool solve_for_var(app* var, expr* fml, guarded_defs& defs); bool solve_for_vars(unsigned num_vars, app* const* vars, expr* fml, guarded_defs& defs); void set_cancel(bool f); private: void instantiate_expr(expr_ref_vector& bound, expr_ref& fml); void abstract_expr(unsigned sz, expr* const* bound, expr_ref& fml); void elim(expr_ref& result); void init_qe(); }; class expr_quant_elim_star1 : public simplifier { protected: expr_quant_elim m_quant_elim; expr* m_assumption; virtual bool visit_quantifier(quantifier * q); virtual void reduce1_quantifier(quantifier * q); virtual bool is_target(quantifier * q) const { return q->get_num_patterns() == 0 && q->get_num_no_patterns() == 0; } public: expr_quant_elim_star1(ast_manager & m, smt_params const& p); virtual ~expr_quant_elim_star1() {} void collect_statistics(statistics & st) const { m_quant_elim.collect_statistics(st); } void reduce_with_assumption(expr* ctx, expr* fml, expr_ref& result); lbool first_elim(unsigned num_vars, app* const* vars, expr_ref& fml, def_vector& defs) { return m_quant_elim.first_elim(num_vars, vars, fml, defs); } void set_cancel(bool f) {} // TBD: replace simplifier by rewriter }; void hoist_exists(expr_ref& fml, app_ref_vector& vars); void mk_exists(unsigned num_vars, app* const* vars, expr_ref& fml); void get_nnf(expr_ref& fml, i_expr_pred& pred, i_nnf_atom& mk_atom, atom_set& pos, atom_set& neg); class simplify_rewriter_cfg : public default_rewriter_cfg { class impl; impl* imp; public: simplify_rewriter_cfg(ast_manager& m); ~simplify_rewriter_cfg(); bool reduce_quantifier( quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr); bool pre_visit(expr* e); void updt_params(params_ref const& p); }; class simplify_rewriter_star : public rewriter_tpl { simplify_rewriter_cfg m_cfg; public: simplify_rewriter_star(ast_manager& m): rewriter_tpl(m, false, m_cfg), m_cfg(m) {} void updt_params(params_ref const& p) { m_cfg.updt_params(p); } }; }; #endif z3-4.4.0/src/qe/qe_lite.cpp0000644000175000017500000026444112540347414015202 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qe_lite.cpp Abstract: Light weight partial quantifier-elimination procedure Author: Nikolaj Bjorner (nbjorner) 2012-10-17 Revision History: --*/ #include "qe_lite.h" #include "expr_abstract.h" #include "used_vars.h" #include "occurs.h" #include "for_each_expr.h" #include "rewriter_def.h" #include "ast_pp.h" #include "ast_ll_pp.h" #include "ast_smt2_pp.h" #include "tactical.h" #include "bool_rewriter.h" #include "var_subst.h" #include "uint_set.h" #include "qe_util.h" #include "th_rewriter.h" #include "for_each_expr.h" #include "expr_safe_replace.h" #include "cooperate.h" #include "datatype_decl_plugin.h" class is_variable_proc { public: virtual bool operator()(expr* e) const = 0; }; class is_variable_test : public is_variable_proc { enum is_var_kind { BY_VAR_SET, BY_VAR_SET_COMPLEMENT, BY_NUM_DECLS }; uint_set m_var_set; unsigned m_num_decls; is_var_kind m_var_kind; public: is_variable_test(uint_set const& vars, bool index_of_bound) : m_var_set(vars), m_num_decls(0), m_var_kind(index_of_bound?BY_VAR_SET:BY_VAR_SET_COMPLEMENT) {} is_variable_test(unsigned num_decls) : m_num_decls(num_decls), m_var_kind(BY_NUM_DECLS) {} virtual bool operator()(expr* e) const { if (!is_var(e)) { return false; } unsigned idx = to_var(e)->get_idx(); switch(m_var_kind) { case BY_VAR_SET: return m_var_set.contains(idx); case BY_VAR_SET_COMPLEMENT: return !m_var_set.contains(idx); case BY_NUM_DECLS: return idx < m_num_decls; } UNREACHABLE(); return false; } }; namespace eq { class der { ast_manager & m; arith_util a; datatype_util dt; is_variable_proc* m_is_variable; var_subst m_subst; expr_ref_vector m_new_exprs; ptr_vector m_map; int_vector m_pos2var; ptr_vector m_inx2var; unsigned_vector m_order; expr_ref_vector m_subst_map; expr_ref_buffer m_new_args; th_rewriter m_rewriter; volatile bool m_cancel; void der_sort_vars(ptr_vector & vars, ptr_vector & definitions, unsigned_vector & order) { order.reset(); // eliminate self loops, and definitions containing quantifiers. bool found = false; for (unsigned i = 0; i < definitions.size(); i++) { var * v = vars[i]; expr * t = definitions[i]; if (t == 0 || has_quantifiers(t) || occurs(v, t)) definitions[i] = 0; else found = true; // found at least one candidate } if (!found) return; typedef std::pair frame; svector todo; expr_fast_mark1 visiting; expr_fast_mark2 done; unsigned vidx, num; for (unsigned i = 0; i < definitions.size(); i++) { if (definitions[i] == 0) continue; var * v = vars[i]; SASSERT(v->get_idx() == i); SASSERT(todo.empty()); todo.push_back(frame(v, 0)); while (!todo.empty()) { start: frame & fr = todo.back(); expr * t = fr.first; if (t->get_ref_count() > 1 && done.is_marked(t)) { todo.pop_back(); continue; } switch (t->get_kind()) { case AST_VAR: vidx = to_var(t)->get_idx(); if (fr.second == 0) { CTRACE("der_bug", vidx >= definitions.size(), tout << "vidx: " << vidx << "\n";); // Remark: The size of definitions may be smaller than the number of variables occuring in the quantified formula. if (definitions.get(vidx, 0) != 0) { if (visiting.is_marked(t)) { // cycle detected: remove t visiting.reset_mark(t); definitions[vidx] = 0; } else { visiting.mark(t); fr.second = 1; todo.push_back(frame(definitions[vidx], 0)); goto start; } } } else { SASSERT(fr.second == 1); if (definitions.get(vidx, 0) != 0) { visiting.reset_mark(t); order.push_back(vidx); } else { // var was removed from the list of candidate vars to elim cycle // do nothing } } if (t->get_ref_count() > 1) done.mark(t); todo.pop_back(); break; case AST_QUANTIFIER: UNREACHABLE(); todo.pop_back(); break; case AST_APP: num = to_app(t)->get_num_args(); while (fr.second < num) { expr * arg = to_app(t)->get_arg(fr.second); fr.second++; if (arg->get_ref_count() > 1 && done.is_marked(arg)) continue; todo.push_back(frame(arg, 0)); goto start; } if (t->get_ref_count() > 1) done.mark(t); todo.pop_back(); break; default: UNREACHABLE(); todo.pop_back(); break; } } } } bool is_variable(expr * e) const { return (*m_is_variable)(e); } bool is_neg_var(ast_manager & m, expr * e, var*& v) { expr* e1; if (m.is_not(e, e1) && is_variable(e1)) { v = to_var(e1); return true; } else { return false; } } /** \brief Return true if e can be viewed as a variable disequality. Store the variable id in v and the definition in t. For example: if e is (not (= (VAR 1) T)), then v assigned to 1, and t to T. if e is (iff (VAR 2) T), then v is assigned to 2, and t to (not T). (not T) is used because this formula is equivalent to (not (iff (VAR 2) (not T))), and can be viewed as a disequality. */ bool is_var_diseq(expr * e, ptr_vector& vs, expr_ref_vector& ts) { expr* e1; if (m.is_not(e, e1)) { return is_var_eq(e, vs, ts); } else if (is_var_eq(e, vs, ts) && vs.size() == 1 && m.is_bool(vs[0])) { expr_ref tmp(m); bool_rewriter(m).mk_not(ts[0].get(), tmp); ts[0] = tmp; return true; } else { return false; } } bool is_invertible_const(bool is_int, expr* x, rational& a_val) { expr* y; if (a.is_uminus(x, y) && is_invertible_const(is_int, y, a_val)) { a_val.neg(); return true; } else if (a.is_numeral(x, a_val) && !a_val.is_zero()) { if (!is_int || a_val.is_one() || a_val.is_minus_one()) { return true; } } return false; } bool is_invertible_mul(bool is_int, expr*& arg, rational& a_val) { if (is_variable(arg)) { a_val = rational(1); return true; } expr* x, *y; if (a.is_mul(arg, x, y)) { if (is_variable(x) && is_invertible_const(is_int, y, a_val)) { arg = x; return true; } if (is_variable(y) && is_invertible_const(is_int, x, a_val)) { arg = y; return true; } } return false; } typedef std::pair signed_expr; expr_ref solve_arith(bool is_int, rational const& r, bool sign, svector const& exprs) { expr_ref_vector result(m); for (unsigned i = 0; i < exprs.size(); ++i) { bool sign2 = exprs[i].first; expr* e = exprs[i].second; rational r2(r); if (sign == sign2) { r2.neg(); } if (!r2.is_one()) { result.push_back(a.mk_mul(a.mk_numeral(r2, is_int), e)); } else { result.push_back(e); } } return expr_ref(a.mk_add(result.size(), result.c_ptr()), m); } bool solve_arith(expr* lhs, expr* rhs, ptr_vector& vs, expr_ref_vector& ts) { if (!a.is_int(lhs) && !a.is_real(rhs)) { return false; } rational a_val; bool is_int = a.is_int(lhs); svector todo, done; todo.push_back(std::make_pair(true, lhs)); todo.push_back(std::make_pair(false, rhs)); while (!todo.empty()) { expr* e = todo.back().second; bool sign = todo.back().first; todo.pop_back(); if (a.is_add(e)) { for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { todo.push_back(std::make_pair(sign, to_app(e)->get_arg(i))); } } else if (is_invertible_mul(is_int, e, a_val)) { done.append(todo); vs.push_back(to_var(e)); a_val = rational(1)/a_val; ts.push_back(solve_arith(is_int, a_val, sign, done)); TRACE("qe_lite", tout << mk_pp(lhs, m) << " " << mk_pp(rhs, m) << " " << mk_pp(e, m) << " := " << mk_pp(ts.back(), m) << "\n";); return true; } else { done.push_back(std::make_pair(sign, e)); } } return false; } bool arith_solve(expr * lhs, expr * rhs, expr * eq, ptr_vector& vs, expr_ref_vector& ts) { return solve_arith(lhs, rhs, vs, ts); } bool trivial_solve(expr* lhs, expr* rhs, expr* eq, ptr_vector& vs, expr_ref_vector& ts) { if (!is_variable(lhs)) { std::swap(lhs, rhs); } if (!is_variable(lhs)) { return false; } vs.push_back(to_var(lhs)); ts.push_back(rhs); TRACE("qe_lite", tout << mk_pp(eq, m) << "\n";); return true; } bool same_vars(ptr_vector const& vs1, ptr_vector const& vs2) const { if (vs1.size() != vs2.size()) { return false; } for (unsigned i = 0; i < vs1.size(); ++i) { if (vs1[i] != vs2[i]) { return false; } } return true; } /** \brief Return true if e can be viewed as a variable equality. */ bool is_var_eq(expr * e, ptr_vector& vs, expr_ref_vector & ts) { expr* lhs, *rhs; var* v; // (= VAR t), (iff VAR t), (iff (not VAR) t), (iff t (not VAR)) cases if (m.is_eq(e, lhs, rhs) || m.is_iff(e, lhs, rhs)) { // (iff (not VAR) t) (iff t (not VAR)) cases if (!is_variable(lhs) && !is_variable(rhs) && m.is_bool(lhs)) { if (!is_neg_var(m, lhs, v)) { std::swap(lhs, rhs); } if (!is_neg_var(m, lhs, v)) { return false; } vs.push_back(v); ts.push_back(m.mk_not(rhs)); TRACE("qe_lite", tout << mk_pp(e, m) << "\n";); return true; } if (trivial_solve(lhs, rhs, e, vs, ts)) { return true; } if (arith_solve(lhs, rhs, e, vs, ts)) { return true; } return false; } // (ite cond (= VAR t) (= VAR t2)) case expr* cond, *e2, *e3; if (m.is_ite(e, cond, e2, e3)) { if (is_var_eq(e2, vs, ts)) { expr_ref_vector ts2(m); ptr_vector vs2; if (is_var_eq(e3, vs2, ts2) && same_vars(vs, vs2)) { for (unsigned i = 0; i < vs.size(); ++i) { ts[i] = m.mk_ite(cond, ts[i].get(), ts2[i].get()); } return true; } } return false; } // VAR = true case if (is_variable(e)) { ts.push_back(m.mk_true()); vs.push_back(to_var(e)); TRACE("qe_lite", tout << mk_pp(e, m) << "\n";); return true; } // VAR = false case if (is_neg_var(m, e, v)) { ts.push_back(m.mk_false()); vs.push_back(v); TRACE("qe_lite", tout << mk_pp(e, m) << "\n";); return true; } return false; } bool is_var_def(bool check_eq, expr* e, ptr_vector& vs, expr_ref_vector& ts) { if (check_eq) { return is_var_eq(e, vs, ts); } else { return is_var_diseq(e, vs, ts); } } void get_elimination_order() { m_order.reset(); TRACE("top_sort", tout << "DEFINITIONS: " << std::endl; for(unsigned i = 0; i < m_map.size(); i++) if(m_map[i]) tout << "VAR " << i << " = " << mk_pp(m_map[i], m) << std::endl; ); der_sort_vars(m_inx2var, m_map, m_order); TRACE("qe_lite", tout << "Elimination m_order:" << std::endl; for(unsigned i=0; iget_expr(); if ((q->is_forall() && m.is_or(e)) || (q->is_exists() && m.is_and(e))) { num_args = to_app(e)->get_num_args(); args = to_app(e)->get_args(); } } void apply_substitution(quantifier * q, expr_ref & r) { expr * e = q->get_expr(); unsigned num_args = 1; expr* const* args = &e; flatten_args(q, num_args, args); bool_rewriter rw(m); // get a new expression m_new_args.reset(); for(unsigned i = 0; i < num_args; i++) { int x = m_pos2var[i]; if (x == -1 || m_map[x] == 0) { m_new_args.push_back(args[i]); } } expr_ref t(m); if (q->is_forall()) { rw.mk_or(m_new_args.size(), m_new_args.c_ptr(), t); } else { rw.mk_and(m_new_args.size(), m_new_args.c_ptr(), t); } expr_ref new_e(m); m_subst(t, m_subst_map.size(), m_subst_map.c_ptr(), new_e); // don't forget to update the quantifier patterns expr_ref_buffer new_patterns(m); expr_ref_buffer new_no_patterns(m); for (unsigned j = 0; j < q->get_num_patterns(); j++) { expr_ref new_pat(m); m_subst(q->get_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_pat); new_patterns.push_back(new_pat); } for (unsigned j = 0; j < q->get_num_no_patterns(); j++) { expr_ref new_nopat(m); m_subst(q->get_no_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_nopat); new_no_patterns.push_back(new_nopat); } r = m.update_quantifier(q, new_patterns.size(), new_patterns.c_ptr(), new_no_patterns.size(), new_no_patterns.c_ptr(), new_e); } void reduce_quantifier1(quantifier * q, expr_ref & r, proof_ref & pr) { expr * e = q->get_expr(); is_variable_test is_v(q->get_num_decls()); set_is_variable_proc(is_v); unsigned num_args = 1; expr* const* args = &e; flatten_args(q, num_args, args); unsigned def_count = 0; unsigned largest_vinx = 0; find_definitions(num_args, args, q->is_exists(), def_count, largest_vinx); if (def_count > 0) { get_elimination_order(); SASSERT(m_order.size() <= def_count); // some might be missing because of cycles if (!m_order.empty()) { create_substitution(largest_vinx + 1); apply_substitution(q, r); } else { r = q; } } else { TRACE("der_bug", tout << "Did not find any diseq\n" << mk_pp(q, m) << "\n";); r = q; } if (m.proofs_enabled()) { pr = r == q ? 0 : m.mk_der(q, r); } } void elim_unused_vars(expr_ref& r, proof_ref &pr) { if (is_quantifier(r)) { quantifier * q = to_quantifier(r); ::elim_unused_vars(m, q, r); if (m.proofs_enabled()) { proof * p1 = m.mk_elim_unused_vars(q, r); pr = m.mk_transitivity(pr, p1); } } } void find_definitions(unsigned num_args, expr* const* args, bool is_exists, unsigned& def_count, unsigned& largest_vinx) { def_count = 0; largest_vinx = 0; m_map.reset(); m_pos2var.reset(); m_inx2var.reset(); m_pos2var.reserve(num_args, -1); // Find all definitions for (unsigned i = 0; i < num_args; i++) { checkpoint(); ptr_vector vs; expr_ref_vector ts(m); if (is_var_def(is_exists, args[i], vs, ts)) { for (unsigned j = 0; j < vs.size(); ++j) { var* v = vs[j]; expr* t = ts[j].get(); unsigned idx = v->get_idx(); if (m_map.get(idx, 0) == 0) { m_map.reserve(idx + 1, 0); m_inx2var.reserve(idx + 1, 0); m_map[idx] = t; m_inx2var[idx] = v; m_pos2var[i] = idx; def_count++; largest_vinx = std::max(idx, largest_vinx); m_new_exprs.push_back(t); } } } } } void flatten_definitions(expr_ref_vector& conjs) { TRACE("qe_lite", expr_ref tmp(m); tmp = m.mk_and(conjs.size(), conjs.c_ptr()); tout << mk_pp(tmp, m) << "\n";); for (unsigned i = 0; i < conjs.size(); ++i) { expr* c = conjs[i].get(); expr* l, *r; if (m.is_false(c)) { conjs[0] = c; conjs.resize(1); break; } if (is_ground(c)) { continue; } if (!m.is_eq(c, l, r)) { continue; } if (!is_app(l) || !is_app(r)) { continue; } if (dt.is_constructor(to_app(l)->get_decl())) { flatten_constructor(to_app(l), to_app(r), conjs); conjs[i] = conjs.back(); conjs.pop_back(); --i; continue; } if (dt.is_constructor(to_app(r)->get_decl())) { flatten_constructor(to_app(r), to_app(l), conjs); conjs[i] = conjs.back(); conjs.pop_back(); --i; continue; } } TRACE("qe_lite", expr_ref tmp(m); tmp = m.mk_and(conjs.size(), conjs.c_ptr()); tout << "after flatten\n" << mk_pp(tmp, m) << "\n";); } void flatten_constructor(app* c, app* r, expr_ref_vector& conjs) { SASSERT(dt.is_constructor(c)); func_decl* d = c->get_decl(); if (dt.is_constructor(r->get_decl())) { app* b = to_app(r); if (d == b->get_decl()) { for (unsigned j = 0; j < c->get_num_args(); ++j) { conjs.push_back(m.mk_eq(c->get_arg(j), b->get_arg(j))); } } else { conjs.push_back(m.mk_false()); } } else { func_decl* rec = dt.get_constructor_recognizer(d); conjs.push_back(m.mk_app(rec, r)); ptr_vector const& acc = *dt.get_constructor_accessors(d); for (unsigned i = 0; i < acc.size(); ++i) { conjs.push_back(m.mk_eq(c->get_arg(i), m.mk_app(acc[i], r))); } } } bool is_unconstrained(var* x, expr* t, unsigned i, expr_ref_vector const& conjs) { bool occ = occurs(x, t); for (unsigned j = 0; !occ && j < conjs.size(); ++j) { occ = (i != j) && occurs(x, conjs[j]); } return !occ; } bool remove_unconstrained(expr_ref_vector& conjs) { bool reduced = false, change = true; expr* r, *l, *ne; while (change) { change = false; for (unsigned i = 0; i < conjs.size(); ++i) { if (m.is_not(conjs[i].get(), ne) && m.is_eq(ne, l, r)) { TRACE("qe_lite", tout << mk_pp(conjs[i].get(), m) << " " << is_variable(l) << " " << is_variable(r) << "\n";); if (is_variable(l) && ::is_var(l) && is_unconstrained(::to_var(l), r, i, conjs)) { conjs[i] = m.mk_true(); reduced = true; change = true; } else if (is_variable(r) && ::is_var(r) && is_unconstrained(::to_var(r), l, i, conjs)) { conjs[i] = m.mk_true(); reduced = true; change = true; } } } } return reduced; } bool reduce_var_set(expr_ref_vector& conjs) { unsigned def_count = 0; unsigned largest_vinx = 0; bool reduced = false; flatten_definitions(conjs); find_definitions(conjs.size(), conjs.c_ptr(), true, def_count, largest_vinx); if (def_count > 0) { get_elimination_order(); SASSERT(m_order.size() <= def_count); // some might be missing because of cycles if (!m_order.empty()) { expr_ref r(m), new_r(m); r = m.mk_and(conjs.size(), conjs.c_ptr()); create_substitution(largest_vinx + 1); m_subst(r, m_subst_map.size(), m_subst_map.c_ptr(), new_r); m_rewriter(new_r); conjs.reset(); qe::flatten_and(new_r, conjs); reduced = true; } } if (remove_unconstrained(conjs)) { reduced = true; } return reduced; } void checkpoint() { cooperate("der"); if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); } public: der(ast_manager & m): m(m), a(m), dt(m), m_is_variable(0), m_subst(m), m_new_exprs(m), m_subst_map(m), m_new_args(m), m_rewriter(m), m_cancel(false) {} void set_is_variable_proc(is_variable_proc& proc) { m_is_variable = &proc;} void operator()(quantifier * q, expr_ref & r, proof_ref & pr) { TRACE("qe_lite", tout << mk_pp(q, m) << "\n";); pr = 0; r = q; reduce_quantifier(q, r, pr); if (r != q) { elim_unused_vars(r, pr); } } void reduce_quantifier(quantifier * q, expr_ref & r, proof_ref & pr) { r = q; // Keep applying reduce_quantifier1 until r doesn't change anymore do { checkpoint(); proof_ref curr_pr(m); q = to_quantifier(r); reduce_quantifier1(q, r, curr_pr); if (m.proofs_enabled()) { pr = m.mk_transitivity(pr, curr_pr); } } while (q != r && is_quantifier(r)); m_new_exprs.reset(); } void operator()(expr_ref_vector& r) { while (reduce_var_set(r)) ; m_new_exprs.reset(); } ast_manager& get_manager() const { return m; } void set_cancel(bool f) { m_rewriter.set_cancel(f); m_cancel = f; } }; }; // namespace eq // ------------------------------------------------------------ // basic destructive equality (and disequality) resolution for arrays. namespace ar { class der { ast_manager& m; array_util a; is_variable_proc* m_is_variable; ptr_vector m_todo; expr_mark m_visited; volatile bool m_cancel; bool is_variable(expr * e) const { return (*m_is_variable)(e); } void mark_all(expr* e) { for_each_expr(*this, m_visited, e); } void mark_all(expr_ref_vector const& fmls, unsigned j) { for (unsigned i = 0; i < fmls.size(); ++i) { if (i != j) { mark_all(fmls[i]); } } } /** Ex A. A[x] = t & Phi where x \not\in A, t. A \not\in t, x => Ex A. Phi[store(A,x,t)] Perhaps also: Ex A. store(A,y,z)[x] = t & Phi where x \not\in A, t, y, z, A \not\in y z, t => Ex A, v . (x = y => z = t) & Phi[store(store(A,x,t),y,v)] */ bool solve_select(expr_ref_vector& conjs, unsigned i, expr* e1, expr* e2) { if (a.is_select(e1)) { app* a1 = to_app(e1); expr* A = a1->get_arg(0); if (!is_variable(A)) { return false; } m_visited.reset(); for (unsigned j = 1; j < a1->get_num_args(); ++j) { mark_all(a1->get_arg(j)); } mark_all(e2); if (m_visited.is_marked(A)) { return false; } ptr_vector args; args.push_back(A); args.append(a1->get_num_args()-1, a1->get_args()+1); args.push_back(e2); expr* B = a.mk_store(args.size(), args.c_ptr()); expr_safe_replace rep(m); rep.insert(A, B); expr_ref tmp(m); std::cout << mk_pp(e1, m) << " = " << mk_pp(e2, m) << "\n"; for (unsigned j = 0; j < conjs.size(); ++j) { if (i == j) { conjs[j] = m.mk_true(); } else { rep(conjs[j].get(), tmp); conjs[j] = tmp; } } return true; } return false; } bool solve_select(expr_ref_vector& conjs, unsigned i, expr* e) { expr* e1, *e2; return m.is_eq(e, e1, e2) && (solve_select(conjs, i, e1, e2) || solve_select(conjs, i, e2, e1)); } /** Ex x. A[x] != B[x] & Phi where x \not\in A, B, Phi => A != B & Phi */ bool solve_neq_select(expr_ref_vector& conjs, unsigned i, expr* e) { expr* e1, *a1, *a2; if (m.is_not(e, e1) && m.is_eq(e1, a1, a2)) { if (a.is_select(a1) && a.is_select(a2) && to_app(a1)->get_num_args() == to_app(a2)->get_num_args()) { expr* e1 = to_app(a1)->get_arg(0); expr* e2 = to_app(a2)->get_arg(0); m_visited.reset(); mark_all(conjs, i); mark_all(e1); mark_all(e2); for (unsigned j = 1; j < to_app(a1)->get_num_args(); ++j) { expr* x = to_app(a1)->get_arg(j); expr* y = to_app(a2)->get_arg(j); if (!is_variable(x)) { return false; } if (x != y) { return false; } if (m_visited.is_marked(x)) { return false; } } conjs[i] = m.mk_not(m.mk_eq(e1, e2)); return true; } } return false; } void checkpoint() { cooperate("der"); if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); } public: der(ast_manager& m): m(m), a(m), m_is_variable(0), m_cancel(false) {} void operator()(expr_ref_vector& fmls) { for (unsigned i = 0; i < fmls.size(); ++i) { checkpoint(); solve_select(fmls, i, fmls[i].get()); solve_neq_select(fmls, i, fmls[i].get()); } } void operator()(expr* e) {} void set_is_variable_proc(is_variable_proc& proc) { m_is_variable = &proc;} void set_cancel(bool f) { m_cancel = f; } }; }; // namespace ar // ------------------------------------------------------------ // fm_tactic adapted to eliminate designated de-Brujin indices. namespace fm { typedef ptr_vector clauses; typedef unsigned var; typedef int bvar; typedef int literal; typedef svector var_vector; // Encode the constraint // lits \/ ( as[0]*xs[0] + ... + as[num_vars-1]*xs[num_vars-1] <= c // if strict is true, then <= is <. struct constraint { static unsigned get_obj_size(unsigned num_lits, unsigned num_vars) { return sizeof(constraint) + num_lits*sizeof(literal) + num_vars*(sizeof(var) + sizeof(rational)); } unsigned m_id; unsigned m_num_lits:29; unsigned m_strict:1; unsigned m_dead:1; unsigned m_mark:1; unsigned m_num_vars; literal * m_lits; var * m_xs; rational * m_as; rational m_c; expr_dependency * m_dep; ~constraint() { rational * it = m_as; rational * end = it + m_num_vars; for (; it != end; ++it) it->~rational(); } unsigned hash() const { return hash_u(m_id); } }; typedef ptr_vector constraints; class constraint_set { unsigned_vector m_id2pos; constraints m_set; public: typedef constraints::const_iterator iterator; bool contains(constraint const & c) const { if (c.m_id >= m_id2pos.size()) return false; return m_id2pos[c.m_id] != UINT_MAX; } bool empty() const { return m_set.empty(); } unsigned size() const { return m_set.size(); } void insert(constraint & c) { unsigned id = c.m_id; m_id2pos.reserve(id+1, UINT_MAX); if (m_id2pos[id] != UINT_MAX) return; // already in the set unsigned pos = m_set.size(); m_id2pos[id] = pos; m_set.push_back(&c); } void erase(constraint & c) { unsigned id = c.m_id; if (id >= m_id2pos.size()) return; unsigned pos = m_id2pos[id]; if (pos == UINT_MAX) return; m_id2pos[id] = UINT_MAX; unsigned last_pos = m_set.size() - 1; if (pos != last_pos) { constraint * last_c = m_set[last_pos]; m_set[pos] = last_c; m_id2pos[last_c->m_id] = pos; } m_set.pop_back(); } constraint & erase() { SASSERT(!empty()); constraint & c = *m_set.back(); m_id2pos[c.m_id] = UINT_MAX; m_set.pop_back(); return c; } void reset() { m_id2pos.reset(); m_set.reset(); } void finalize() { m_id2pos.finalize(); m_set.finalize(); } iterator begin() const { return m_set.begin(); } iterator end() const { return m_set.end(); } }; class fm { ast_manager & m; is_variable_proc* m_is_variable; small_object_allocator m_allocator; arith_util m_util; constraints m_constraints; expr_ref_vector m_bvar2expr; char_vector m_bvar2sign; obj_map m_expr2bvar; char_vector m_is_int; char_vector m_forbidden; expr_ref_vector m_var2expr; obj_map m_expr2var; unsigned_vector m_var2pos; vector m_lowers; vector m_uppers; uint_set m_forbidden_set; // variables that cannot be eliminated because occur in non OCC ineq part expr_ref_vector m_new_fmls; volatile bool m_cancel; id_gen m_id_gen; bool m_fm_real_only; unsigned m_fm_limit; unsigned m_fm_cutoff1; unsigned m_fm_cutoff2; unsigned m_fm_extra; bool m_fm_occ; unsigned m_counter; bool m_inconsistent; expr_dependency_ref m_inconsistent_core; constraint_set m_sub_todo; // --------------------------- // // OCC clause recognizer // // --------------------------- bool is_literal(expr * t) const { expr * atom; return is_uninterp_const(t) || (m.is_not(t, atom) && is_uninterp_const(atom)); } bool is_constraint(expr * t) const { return !is_literal(t); } bool is_var(expr * t, expr * & x) const { if ((*m_is_variable)(t)) { x = t; return true; } else if (m_util.is_to_real(t) && (*m_is_variable)(to_app(t)->get_arg(0))) { x = to_app(t)->get_arg(0); return true; } return false; } bool is_var(expr * t) const { expr * x; return is_var(t, x); } bool is_linear_mon_core(expr * t, expr * & x) const { expr * c; if (m_util.is_mul(t, c, x) && m_util.is_numeral(c) && is_var(x, x)) return true; return is_var(t, x); } bool is_linear_mon(expr * t) const { expr * x; return is_linear_mon_core(t, x); } bool is_linear_pol(expr * t) const { unsigned num_mons; expr * const * mons; if (m_util.is_add(t)) { num_mons = to_app(t)->get_num_args(); mons = to_app(t)->get_args(); } else { num_mons = 1; mons = &t; } expr_fast_mark2 visited; bool all_forbidden = true; for (unsigned i = 0; i < num_mons; i++) { expr * x; if (!is_linear_mon_core(mons[i], x)) return false; if (visited.is_marked(x)) return false; // duplicates are not supported... must simplify first visited.mark(x); SASSERT(::is_var(x)); if (!m_forbidden_set.contains(::to_var(x)->get_idx()) && (!m_fm_real_only || !m_util.is_int(x))) all_forbidden = false; } return !all_forbidden; } bool is_linear_ineq(expr * t) const { bool result = false; m.is_not(t, t); expr * lhs, * rhs; if (m_util.is_le(t, lhs, rhs) || m_util.is_ge(t, lhs, rhs)) { result = m_util.is_numeral(rhs) && is_linear_pol(lhs); } TRACE("qe_lite", tout << mk_pp(t, m) << " " << (result?"true":"false") << "\n";); return result; } bool is_occ(expr * t) { if (m_fm_occ && m.is_or(t)) { unsigned num = to_app(t)->get_num_args(); bool found = false; for (unsigned i = 0; i < num; i++) { expr * l = to_app(t)->get_arg(i); if (is_literal(l)) { continue; } else if (is_linear_ineq(l)) { if (found) return false; found = true; } else { return false; } } return found; } return is_linear_ineq(t); } // --------------------------- // // Memory mng // // --------------------------- void del_constraint(constraint * c) { m.dec_ref(c->m_dep); m_sub_todo.erase(*c); m_id_gen.recycle(c->m_id); c->~constraint(); unsigned sz = constraint::get_obj_size(c->m_num_lits, c->m_num_vars); m_allocator.deallocate(sz, c); } void del_constraints(unsigned sz, constraint * const * cs) { for (unsigned i = 0; i < sz; i++) del_constraint(cs[i]); } void reset_constraints() { del_constraints(m_constraints.size(), m_constraints.c_ptr()); m_constraints.reset(); } constraint * mk_constraint(unsigned num_lits, literal * lits, unsigned num_vars, var * xs, rational * as, rational & c, bool strict, expr_dependency * dep) { unsigned sz = constraint::get_obj_size(num_lits, num_vars); char * mem = static_cast(m_allocator.allocate(sz)); char * mem_as = mem + sizeof(constraint); char * mem_lits = mem_as + sizeof(rational)*num_vars; char * mem_xs = mem_lits + sizeof(literal)*num_lits; constraint * cnstr = new (mem) constraint(); cnstr->m_id = m_id_gen.mk(); cnstr->m_num_lits = num_lits; cnstr->m_dead = false; cnstr->m_mark = false; cnstr->m_strict = strict; cnstr->m_num_vars = num_vars; cnstr->m_lits = reinterpret_cast(mem_lits); for (unsigned i = 0; i < num_lits; i++) cnstr->m_lits[i] = lits[i]; cnstr->m_xs = reinterpret_cast(mem_xs); cnstr->m_as = reinterpret_cast(mem_as); for (unsigned i = 0; i < num_vars; i++) { TRACE("qe_lite", tout << "xs[" << i << "]: " << xs[i] << "\n";); cnstr->m_xs[i] = xs[i]; new (cnstr->m_as + i) rational(as[i]); } cnstr->m_c = c; DEBUG_CODE({ for (unsigned i = 0; i < num_vars; i++) { SASSERT(cnstr->m_xs[i] == xs[i]); SASSERT(cnstr->m_as[i] == as[i]); } }); cnstr->m_dep = dep; m.inc_ref(dep); return cnstr; } // --------------------------- // // Util // // --------------------------- unsigned num_vars() const { return m_is_int.size(); } // multiply as and c, by the lcm of their denominators void mk_int(unsigned num, rational * as, rational & c) { rational l = denominator(c); for (unsigned i = 0; i < num; i++) l = lcm(l, denominator(as[i])); if (l.is_one()) return; c *= l; SASSERT(c.is_int()); for (unsigned i = 0; i < num; i++) { as[i] *= l; SASSERT(as[i].is_int()); } } void normalize_coeffs(constraint & c) { if (c.m_num_vars == 0) return; // compute gcd of all coefficients rational g = c.m_c; if (g.is_neg()) g.neg(); for (unsigned i = 0; i < c.m_num_vars; i++) { if (g.is_one()) break; if (c.m_as[i].is_pos()) g = gcd(c.m_as[i], g); else g = gcd(-c.m_as[i], g); } if (g.is_one()) return; c.m_c /= g; for (unsigned i = 0; i < c.m_num_vars; i++) c.m_as[i] /= g; } void display(std::ostream & out, constraint const & c) const { for (unsigned i = 0; i < c.m_num_lits; i++) { literal l = c.m_lits[i]; if (sign(l)) out << "~"; bvar p = lit2bvar(l); out << mk_ismt2_pp(m_bvar2expr[p], m); out << " "; } out << "("; if (c.m_num_vars == 0) out << "0"; for (unsigned i = 0; i < c.m_num_vars; i++) { if (i > 0) out << " + "; if (!c.m_as[i].is_one()) out << c.m_as[i] << "*"; out << mk_ismt2_pp(m_var2expr.get(c.m_xs[i]), m); } if (c.m_strict) out << " < "; else out << " <= "; out << c.m_c; out << ")"; } /** \brief Return true if c1 subsumes c2 c1 subsumes c2 If 1) All literals of c1 are literals of c2 2) polynomial of c1 == polynomial of c2 3) c1.m_c <= c2.m_c */ bool subsumes(constraint const & c1, constraint const & c2) { if (&c1 == &c2) return false; // quick checks first if (c1.m_num_lits > c2.m_num_lits) return false; if (c1.m_num_vars != c2.m_num_vars) return false; if (c1.m_c > c2.m_c) return false; if (!c1.m_strict && c2.m_strict && c1.m_c == c2.m_c) return false; m_counter += c1.m_num_lits + c2.m_num_lits; for (unsigned i = 0; i < c1.m_num_vars; i++) { m_var2pos[c1.m_xs[i]] = i; } bool failed = false; for (unsigned i = 0; i < c2.m_num_vars; i++) { unsigned pos1 = m_var2pos[c2.m_xs[i]]; if (pos1 == UINT_MAX || c1.m_as[pos1] != c2.m_as[i]) { failed = true; break; } } for (unsigned i = 0; i < c1.m_num_vars; i++) { m_var2pos[c1.m_xs[i]] = UINT_MAX; } if (failed) return false; for (unsigned i = 0; i < c2.m_num_lits; i++) { literal l = c2.m_lits[i]; bvar b = lit2bvar(l); SASSERT(m_bvar2sign[b] == 0); m_bvar2sign[b] = sign(l) ? -1 : 1; } for (unsigned i = 0; i < c1.m_num_lits; i++) { literal l = c1.m_lits[i]; bvar b = lit2bvar(l); char s = sign(l) ? -1 : 1; if (m_bvar2sign[b] != s) { failed = true; break; } } for (unsigned i = 0; i < c2.m_num_lits; i++) { literal l = c2.m_lits[i]; bvar b = lit2bvar(l); m_bvar2sign[b] = 0; } if (failed) return false; return true; } void backward_subsumption(constraint const & c) { if (c.m_num_vars == 0) return; var best = UINT_MAX; unsigned best_sz = UINT_MAX; bool best_lower = false; for (unsigned i = 0; i < c.m_num_vars; i++) { var xi = c.m_xs[i]; if (is_forbidden(xi)) continue; // variable is not in the index bool neg_a = c.m_as[i].is_neg(); constraints & cs = neg_a ? m_lowers[xi] : m_uppers[xi]; if (cs.size() < best_sz) { best = xi; best_sz = cs.size(); best_lower = neg_a; } } if (best_sz == 0) return; if (best == UINT_MAX) return; // none of the c variables are in the index. constraints & cs = best_lower ? m_lowers[best] : m_uppers[best]; m_counter += cs.size(); constraints::iterator it = cs.begin(); constraints::iterator it2 = it; constraints::iterator end = cs.end(); for (; it != end; ++it) { constraint * c2 = *it; if (c2->m_dead) continue; if (subsumes(c, *c2)) { TRACE("qe_lite", display(tout, c); tout << "\nsubsumed:\n"; display(tout, *c2); tout << "\n";); c2->m_dead = true; continue; } *it2 = *it; ++it2; } cs.set_end(it2); } void subsume() { while (!m_sub_todo.empty()) { constraint & c = m_sub_todo.erase(); if (c.m_dead) continue; backward_subsumption(c); } } public: // --------------------------- // // Initialization // // --------------------------- fm(ast_manager & _m): m(_m), m_is_variable(0), m_allocator("fm-elim"), m_util(m), m_bvar2expr(m), m_var2expr(m), m_new_fmls(m), m_inconsistent_core(m) { m_cancel = false; updt_params(); m_counter = 0; m_inconsistent = false; } ~fm() { reset_constraints(); } void updt_params() { m_fm_real_only = false; m_fm_limit = 5000000; m_fm_cutoff1 = 8; m_fm_cutoff2 = 256; m_fm_extra = 0; m_fm_occ = true; } void set_cancel(bool f) { m_cancel = f; } private: struct forbidden_proc { fm & m_owner; forbidden_proc(fm & o):m_owner(o) {} void operator()(::var * n) { if (m_owner.is_var(n) && m_owner.m.get_sort(n)->get_family_id() == m_owner.m_util.get_family_id()) { m_owner.m_forbidden_set.insert(n->get_idx()); } } void operator()(app * n) { } void operator()(quantifier * n) {} }; void init_forbidden_set(expr_ref_vector const & g) { m_forbidden_set.reset(); expr_fast_mark1 visited; forbidden_proc proc(*this); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * f = g[i]; if (is_occ(f)) continue; TRACE("qe_lite", tout << "not OCC:\n" << mk_ismt2_pp(f, m) << "\n";); quick_for_each_expr(proc, visited, f); } } void init(expr_ref_vector const & g) { m_sub_todo.reset(); m_id_gen.reset(); reset_constraints(); m_bvar2expr.reset(); m_bvar2sign.reset(); m_bvar2expr.push_back(0); // bvar 0 is not used m_bvar2sign.push_back(0); m_expr2var.reset(); m_is_int.reset(); m_var2pos.reset(); m_forbidden.reset(); m_var2expr.reset(); m_expr2var.reset(); m_lowers.reset(); m_uppers.reset(); m_new_fmls.reset(); m_counter = 0; m_inconsistent = false; m_inconsistent_core = 0; init_forbidden_set(g); } // --------------------------- // // Internal data-structures // // --------------------------- static bool sign(literal l) { return l < 0; } static bvar lit2bvar(literal l) { return l < 0 ? -l : l; } bool is_int(var x) const { return m_is_int[x] != 0; } bool is_forbidden(var x) const { return m_forbidden[x] != 0; } bool all_int(constraint const & c) const { for (unsigned i = 0; i < c.m_num_vars; i++) { if (!is_int(c.m_xs[i])) return false; } return true; } app * to_expr(constraint const & c) { expr * ineq; if (c.m_num_vars == 0) { // 0 < k (for k > 0) --> true // 0 <= 0 -- > true if (c.m_c.is_pos() || (!c.m_strict && c.m_c.is_zero())) return m.mk_true(); ineq = 0; } else { bool int_cnstr = all_int(c); ptr_buffer ms; for (unsigned i = 0; i < c.m_num_vars; i++) { expr * x = m_var2expr.get(c.m_xs[i]); if (!int_cnstr && is_int(c.m_xs[i])) x = m_util.mk_to_real(x); if (c.m_as[i].is_one()) ms.push_back(x); else ms.push_back(m_util.mk_mul(m_util.mk_numeral(c.m_as[i], int_cnstr), x)); } expr * lhs; if (c.m_num_vars == 1) lhs = ms[0]; else lhs = m_util.mk_add(ms.size(), ms.c_ptr()); expr * rhs = m_util.mk_numeral(c.m_c, int_cnstr); if (c.m_strict) { ineq = m.mk_not(m_util.mk_ge(lhs, rhs)); } else { ineq = m_util.mk_le(lhs, rhs); } } if (c.m_num_lits == 0) { if (ineq) return to_app(ineq); else return m.mk_false(); } ptr_buffer lits; for (unsigned i = 0; i < c.m_num_lits; i++) { literal l = c.m_lits[i]; if (sign(l)) lits.push_back(m.mk_not(m_bvar2expr.get(lit2bvar(l)))); else lits.push_back(m_bvar2expr.get(lit2bvar(l))); } if (ineq) lits.push_back(ineq); if (lits.size() == 1) return to_app(lits[0]); else return m.mk_or(lits.size(), lits.c_ptr()); } var mk_var(expr * t) { SASSERT(::is_var(t)); SASSERT(m_util.is_int(t) || m_util.is_real(t)); var x = m_var2expr.size(); m_var2expr.push_back(t); bool is_int = m_util.is_int(t); m_is_int.push_back(is_int); m_var2pos.push_back(UINT_MAX); m_expr2var.insert(t, x); m_lowers.push_back(constraints()); m_uppers.push_back(constraints()); bool forbidden = m_forbidden_set.contains(::to_var(t)->get_idx()) || (m_fm_real_only && is_int); m_forbidden.push_back(forbidden); SASSERT(m_var2expr.size() == m_is_int.size()); SASSERT(m_lowers.size() == m_is_int.size()); SASSERT(m_uppers.size() == m_is_int.size()); SASSERT(m_forbidden.size() == m_is_int.size()); SASSERT(m_var2pos.size() == m_is_int.size()); TRACE("qe_lite", tout << mk_pp(t,m) << " |-> " << x << " forbidden: " << forbidden << "\n";); return x; } bvar mk_bvar(expr * t) { SASSERT(is_uninterp_const(t)); SASSERT(m.is_bool(t)); bvar p = m_bvar2expr.size(); m_bvar2expr.push_back(t); m_bvar2sign.push_back(0); SASSERT(m_bvar2expr.size() == m_bvar2sign.size()); m_expr2bvar.insert(t, p); SASSERT(p > 0); return p; } var to_var(expr * t) { var x; if (!m_expr2var.find(t, x)) x = mk_var(t); SASSERT(m_expr2var.contains(t)); SASSERT(m_var2expr.get(x) == t); TRACE("qe_lite", tout << mk_ismt2_pp(t, m) << " --> " << x << "\n";); return x; } bvar to_bvar(expr * t) { bvar p; if (m_expr2bvar.find(t, p)) return p; return mk_bvar(t); } literal to_literal(expr * t) { if (m.is_not(t, t)) return -to_bvar(t); else return to_bvar(t); } void add_constraint(expr * f, expr_dependency * dep) { TRACE("qe_lite", tout << mk_pp(f, m) << "\n";); SASSERT(!m.is_or(f) || m_fm_occ); sbuffer lits; sbuffer xs; buffer as; rational c; bool strict; unsigned num; expr * const * args; if (m.is_or(f)) { num = to_app(f)->get_num_args(); args = to_app(f)->get_args(); } else { num = 1; args = &f; } #if Z3DEBUG bool found_ineq = false; #endif for (unsigned i = 0; i < num; i++) { expr * l = args[i]; if (is_literal(l)) { lits.push_back(to_literal(l)); } else { // found inequality SASSERT(!found_ineq); DEBUG_CODE(found_ineq = true;); bool neg = m.is_not(l, l); SASSERT(m_util.is_le(l) || m_util.is_ge(l)); strict = neg; if (m_util.is_ge(l)) neg = !neg; expr * lhs = to_app(l)->get_arg(0); expr * rhs = to_app(l)->get_arg(1); VERIFY (m_util.is_numeral(rhs, c)); if (neg) c.neg(); unsigned num_mons; expr * const * mons; if (m_util.is_add(lhs)) { num_mons = to_app(lhs)->get_num_args(); mons = to_app(lhs)->get_args(); } else { num_mons = 1; mons = &lhs; } bool all_int = true; for (unsigned j = 0; j < num_mons; j++) { expr * monomial = mons[j]; expr * a; rational a_val; expr * x; if (m_util.is_mul(monomial, a, x)) { VERIFY(m_util.is_numeral(a, a_val)); } else { x = monomial; a_val = rational(1); } if (neg) a_val.neg(); VERIFY(is_var(x, x)); xs.push_back(to_var(x)); as.push_back(a_val); if (!is_int(xs.back())) all_int = false; } mk_int(as.size(), as.c_ptr(), c); if (all_int && strict) { strict = false; c--; } } } TRACE("qe_lite", tout << "before mk_constraint: "; for (unsigned i = 0; i < xs.size(); i++) tout << " " << xs[i]; tout << "\n";); constraint * new_c = mk_constraint(lits.size(), lits.c_ptr(), xs.size(), xs.c_ptr(), as.c_ptr(), c, strict, dep); TRACE("qe_lite", tout << "add_constraint: "; display(tout, *new_c); tout << "\n";); VERIFY(register_constraint(new_c)); } bool is_false(constraint const & c) const { return c.m_num_lits == 0 && c.m_num_vars == 0 && (c.m_c.is_neg() || (c.m_strict && c.m_c.is_zero())); } bool register_constraint(constraint * c) { normalize_coeffs(*c); if (is_false(*c)) { del_constraint(c); m_inconsistent = true; TRACE("qe_lite", tout << "is false "; display(tout, *c); tout << "\n";); return false; } bool r = false; for (unsigned i = 0; i < c->m_num_vars; i++) { var x = c->m_xs[i]; if (!is_forbidden(x)) { r = true; if (c->m_as[i].is_neg()) m_lowers[x].push_back(c); else m_uppers[x].push_back(c); } } if (r) { m_sub_todo.insert(*c); m_constraints.push_back(c); return true; } else { TRACE("qe_lite", tout << "all variables are forbidden "; display(tout, *c); tout << "\n";); m_new_fmls.push_back(to_expr(*c)); del_constraint(c); return false; } } void init_use_list(expr_ref_vector const & g) { unsigned sz = g.size(); for (unsigned i = 0; !m_inconsistent && i < sz; i++) { expr * f = g[i]; if (is_occ(f)) add_constraint(f, 0); else m_new_fmls.push_back(f); } } unsigned get_cost(var x) const { unsigned long long r = static_cast(m_lowers[x].size()) * static_cast(m_uppers[x].size()); if (r > UINT_MAX) return UINT_MAX; return static_cast(r); } typedef std::pair x_cost; struct x_cost_lt { char_vector const m_is_int; x_cost_lt(char_vector & is_int):m_is_int(is_int) {} bool operator()(x_cost const & p1, x_cost const & p2) const { // Integer variables with cost 0 can be eliminated even if they depend on real variables. // Cost 0 == no lower or no upper bound. if (p1.second == 0) { if (p2.second > 0) return true; return p1.first < p2.first; } if (p2.second == 0) return false; bool int1 = m_is_int[p1.first] != 0; bool int2 = m_is_int[p2.first] != 0; return (!int1 && int2) || (int1 == int2 && p1.second < p2.second); } }; void sort_candidates(var_vector & xs) { svector x_cost_vector; unsigned num = num_vars(); for (var x = 0; x < num; x++) { if (!is_forbidden(x)) { x_cost_vector.push_back(x_cost(x, get_cost(x))); } } // x_cost_lt is not a total order on variables std::stable_sort(x_cost_vector.begin(), x_cost_vector.end(), x_cost_lt(m_is_int)); TRACE("qe_lite", svector::iterator it2 = x_cost_vector.begin(); svector::iterator end2 = x_cost_vector.end(); for (; it2 != end2; ++it2) { tout << "(" << mk_ismt2_pp(m_var2expr.get(it2->first), m) << " " << it2->second << ") "; } tout << "\n";); svector::iterator it2 = x_cost_vector.begin(); svector::iterator end2 = x_cost_vector.end(); for (; it2 != end2; ++it2) { xs.push_back(it2->first); } } void cleanup_constraints(constraints & cs) { unsigned j = 0; unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { constraint * c = cs[i]; if (c->m_dead) continue; cs[j] = c; j++; } cs.shrink(j); } // Set all_int = true if all variables in c are int. // Set unit_coeff = true if the coefficient of x in c is 1 or -1. // If all_int = false, then unit_coeff may not be set. void analyze(constraint const & c, var x, bool & all_int, bool & unit_coeff) const { all_int = true; unit_coeff = true; for (unsigned i = 0; i < c.m_num_vars; i++) { if (!is_int(c.m_xs[i])) { all_int = false; return; } if (c.m_xs[i] == x) { unit_coeff = (c.m_as[i].is_one() || c.m_as[i].is_minus_one()); } } } void analyze(constraints const & cs, var x, bool & all_int, bool & unit_coeff) const { all_int = true; unit_coeff = true; constraints::const_iterator it = cs.begin(); constraints::const_iterator end = cs.end(); for (; it != end; ++it) { bool curr_unit_coeff; analyze(*(*it), x, all_int, curr_unit_coeff); if (!all_int) return; if (!curr_unit_coeff) unit_coeff = false; } } // An integer variable x may be eliminated, if // 1- All variables in the contraints it occur are integer. // 2- The coefficient of x in all lower bounds (or all upper bounds) is unit. bool can_eliminate(var x) const { if (!is_int(x)) return true; bool all_int; bool l_unit, u_unit; analyze(m_lowers[x], x, all_int, l_unit); if (!all_int) return false; analyze(m_uppers[x], x, all_int, u_unit); return all_int && (l_unit || u_unit); } void copy_constraints(constraints const & s, clauses & t) { constraints::const_iterator it = s.begin(); constraints::const_iterator end = s.end(); for (; it != end; ++it) { app * c = to_expr(*(*it)); t.push_back(c); } } clauses tmp_clauses; void save_constraints(var x) { } void mark_constraints_dead(constraints const & cs) { constraints::const_iterator it = cs.begin(); constraints::const_iterator end = cs.end(); for (; it != end; ++it) (*it)->m_dead = true; } void mark_constraints_dead(var x) { save_constraints(x); mark_constraints_dead(m_lowers[x]); mark_constraints_dead(m_uppers[x]); } void get_coeff(constraint const & c, var x, rational & a) { for (unsigned i = 0; i < c.m_num_vars; i++) { if (c.m_xs[i] == x) { a = c.m_as[i]; return; } } UNREACHABLE(); } var_vector new_xs; vector new_as; svector new_lits; constraint * resolve(constraint const & l, constraint const & u, var x) { m_counter += l.m_num_vars + u.m_num_vars + l.m_num_lits + u.m_num_lits; rational a, b; get_coeff(l, x, a); get_coeff(u, x, b); SASSERT(a.is_neg()); SASSERT(b.is_pos()); a.neg(); SASSERT(!is_int(x) || a.is_one() || b.is_one()); new_xs.reset(); new_as.reset(); rational new_c = l.m_c*b + u.m_c*a; bool new_strict = l.m_strict || u.m_strict; for (unsigned i = 0; i < l.m_num_vars; i++) { var xi = l.m_xs[i]; if (xi == x) continue; unsigned pos = new_xs.size(); new_xs.push_back(xi); SASSERT(m_var2pos[xi] == UINT_MAX); m_var2pos[xi] = pos; new_as.push_back(l.m_as[i] * b); SASSERT(new_xs[m_var2pos[xi]] == xi); SASSERT(new_xs.size() == new_as.size()); } for (unsigned i = 0; i < u.m_num_vars; i++) { var xi = u.m_xs[i]; if (xi == x) continue; unsigned pos = m_var2pos[xi]; if (pos == UINT_MAX) { new_xs.push_back(xi); new_as.push_back(u.m_as[i] * a); } else { new_as[pos] += u.m_as[i] * a; } } // remove zeros and check whether all variables are int bool all_int = true; unsigned sz = new_xs.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { if (new_as[i].is_zero()) continue; if (!is_int(new_xs[i])) all_int = false; if (i != j) { new_xs[j] = new_xs[i]; new_as[j] = new_as[i]; } j++; } new_xs.shrink(j); new_as.shrink(j); if (all_int && new_strict) { new_strict = false; new_c --; } // reset m_var2pos for (unsigned i = 0; i < l.m_num_vars; i++) { m_var2pos[l.m_xs[i]] = UINT_MAX; } if (new_xs.empty() && (new_c.is_pos() || (!new_strict && new_c.is_zero()))) { // literal is true TRACE("qe_lite", tout << "resolution " << x << " consequent literal is always true: \n"; display(tout, l); tout << "\n"; display(tout, u); tout << "\n";); return 0; // no constraint needs to be created. } new_lits.reset(); for (unsigned i = 0; i < l.m_num_lits; i++) { literal lit = l.m_lits[i]; bvar p = lit2bvar(lit); m_bvar2sign[p] = sign(lit) ? -1 : 1; new_lits.push_back(lit); } bool tautology = false; for (unsigned i = 0; i < u.m_num_lits && !tautology; i++) { literal lit = u.m_lits[i]; bvar p = lit2bvar(lit); switch (m_bvar2sign[p]) { case 0: new_lits.push_back(lit); break; case -1: if (!sign(lit)) tautology = true; break; case 1: if (sign(lit)) tautology = true; break; default: UNREACHABLE(); } } // reset m_bvar2sign for (unsigned i = 0; i < l.m_num_lits; i++) { literal lit = l.m_lits[i]; bvar p = lit2bvar(lit); m_bvar2sign[p] = 0; } if (tautology) { TRACE("qe_lite", tout << "resolution " << x << " tautology: \n"; display(tout, l); tout << "\n"; display(tout, u); tout << "\n";); return 0; } expr_dependency * new_dep = m.mk_join(l.m_dep, u.m_dep); if (new_lits.empty() && new_xs.empty() && (new_c.is_neg() || (new_strict && new_c.is_zero()))) { TRACE("qe_lite", tout << "resolution " << x << " inconsistent: \n"; display(tout, l); tout << "\n"; display(tout, u); tout << "\n";); m_inconsistent = true; m_inconsistent_core = new_dep; return 0; } constraint * new_cnstr = mk_constraint(new_lits.size(), new_lits.c_ptr(), new_xs.size(), new_xs.c_ptr(), new_as.c_ptr(), new_c, new_strict, new_dep); TRACE("qe_lite", tout << "resolution " << x << "\n"; display(tout, l); tout << "\n"; display(tout, u); tout << "\n---->\n"; display(tout, *new_cnstr); tout << "\n"; tout << "new_dep: " << new_dep << "\n";); return new_cnstr; } ptr_vector new_constraints; bool try_eliminate(var x) { constraints & l = m_lowers[x]; constraints & u = m_uppers[x]; cleanup_constraints(l); cleanup_constraints(u); if (l.empty() || u.empty()) { // easy case mark_constraints_dead(x); TRACE("qe_lite", tout << "variable was eliminated (trivial case)\n";); return true; } unsigned num_lowers = l.size(); unsigned num_uppers = u.size(); if (num_lowers > m_fm_cutoff1 && num_uppers > m_fm_cutoff1) return false; if (num_lowers * num_uppers > m_fm_cutoff2) return false; if (!can_eliminate(x)) return false; m_counter += num_lowers * num_uppers; TRACE("qe_lite", tout << "eliminating " << mk_ismt2_pp(m_var2expr.get(x), m) << "\nlowers:\n"; display_constraints(tout, l); tout << "uppers:\n"; display_constraints(tout, u);); unsigned num_old_cnstrs = num_uppers + num_lowers; unsigned limit = num_old_cnstrs + m_fm_extra; unsigned num_new_cnstrs = 0; new_constraints.reset(); for (unsigned i = 0; i < num_lowers; i++) { for (unsigned j = 0; j < num_uppers; j++) { if (m_inconsistent || num_new_cnstrs > limit) { TRACE("qe_lite", tout << "too many new constraints: " << num_new_cnstrs << "\n";); del_constraints(new_constraints.size(), new_constraints.c_ptr()); return false; } constraint const & l_c = *(l[i]); constraint const & u_c = *(u[j]); constraint * new_c = resolve(l_c, u_c, x); if (new_c != 0) { num_new_cnstrs++; new_constraints.push_back(new_c); } } } mark_constraints_dead(x); unsigned sz = new_constraints.size(); m_counter += sz; for (unsigned i = 0; i < sz; i++) { constraint * c = new_constraints[i]; backward_subsumption(*c); register_constraint(c); } TRACE("qe_lite", tout << "variables was eliminated old: " << num_old_cnstrs << " new_constraints: " << sz << "\n";); return true; } void copy_remaining(vector & v2cs) { vector::iterator it = v2cs.begin(); vector::iterator end = v2cs.end(); for (; it != end; ++it) { constraints & cs = *it; constraints::iterator it2 = cs.begin(); constraints::iterator end2 = cs.end(); for (; it2 != end2; ++it2) { constraint * c = *it2; if (!c->m_dead) { c->m_dead = true; expr * new_f = to_expr(*c); TRACE("qe_lite", tout << "asserting...\n" << mk_ismt2_pp(new_f, m) << "\nnew_dep: " << c->m_dep << "\n";); m_new_fmls.push_back(new_f); } } } v2cs.finalize(); } // Copy remaining clauses to m_new_fmls void copy_remaining() { copy_remaining(m_uppers); copy_remaining(m_lowers); } void checkpoint() { cooperate("fm"); if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); } public: void set_is_variable_proc(is_variable_proc& proc) { m_is_variable = &proc;} void operator()(expr_ref_vector& fmls) { init(fmls); init_use_list(fmls); if (m_inconsistent) { m_new_fmls.reset(); m_new_fmls.push_back(m.mk_false()); } else { TRACE("qe_lite", display(tout);); subsume(); var_vector candidates; sort_candidates(candidates); unsigned eliminated = 0; unsigned num = candidates.size(); for (unsigned i = 0; i < num; i++) { checkpoint(); if (m_counter > m_fm_limit) break; m_counter++; if (try_eliminate(candidates[i])) eliminated++; if (m_inconsistent) { m_new_fmls.reset(); m_new_fmls.push_back(m.mk_false()); break; } } if (!m_inconsistent) { copy_remaining(); } } reset_constraints(); fmls.reset(); fmls.append(m_new_fmls); } void display_constraints(std::ostream & out, constraints const & cs) const { constraints::const_iterator it = cs.begin(); constraints::const_iterator end = cs.end(); for (; it != end; ++it) { out << " "; display(out, *(*it)); out << "\n"; } } void display(std::ostream & out) const { unsigned num = num_vars(); for (var x = 0; x < num; x++) { if (is_forbidden(x)) continue; out << mk_ismt2_pp(m_var2expr.get(x), m) << "\n"; display_constraints(out, m_lowers[x]); display_constraints(out, m_uppers[x]); } } }; } // namespace fm class qe_lite::impl { public: struct elim_cfg : public default_rewriter_cfg { impl& m_imp; ast_manager& m; public: elim_cfg(impl& i): m_imp(i), m(i.m) {} bool reduce_quantifier(quantifier * q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { result = new_body; if (is_forall(q)) { result = m.mk_not(result); } uint_set indices; for (unsigned i = 0; i < q->get_num_decls(); ++i) { indices.insert(i); } m_imp(indices, true, result); if (is_forall(q)) { result = m.mk_not(result); } result = m.update_quantifier( q, q->get_num_patterns(), new_patterns, q->get_num_no_patterns(), new_no_patterns, result); m_imp.m_rewriter(result); return true; } }; class elim_star : public rewriter_tpl { elim_cfg m_cfg; public: elim_star(impl& i): rewriter_tpl(i.m, false, m_cfg), m_cfg(i) {} }; private: ast_manager& m; eq::der m_der; fm::fm m_fm; ar::der m_array_der; elim_star m_elim_star; th_rewriter m_rewriter; bool has_unique_non_ground(expr_ref_vector const& fmls, unsigned& index) { index = fmls.size(); if (index <= 1) { return false; } for (unsigned i = 0; i < fmls.size(); ++i) { if (!is_ground(fmls[i])) { if (index != fmls.size()) { return false; } index = i; } } return index < fmls.size(); } public: impl(ast_manager& m): m(m), m_der(m), m_fm(m), m_array_der(m), m_elim_star(*this), m_rewriter(m) {} void operator()(app_ref_vector& vars, expr_ref& fml) { if (vars.empty()) { return; } expr_ref tmp(fml); quantifier_ref q(m); proof_ref pr(m); symbol qe_lite("QE"); expr_abstract(m, 0, vars.size(), (expr*const*)vars.c_ptr(), fml, tmp); ptr_vector sorts; svector names; for (unsigned i = 0; i < vars.size(); ++i) { sorts.push_back(m.get_sort(vars[i].get())); names.push_back(vars[i]->get_decl()->get_name()); } q = m.mk_exists(vars.size(), sorts.c_ptr(), names.c_ptr(), tmp, 1, qe_lite); m_der.reduce_quantifier(q, tmp, pr); // assumes m_der just updates the quantifier and does not change things more. if (is_exists(tmp) && to_quantifier(tmp)->get_qid() == qe_lite) { used_vars used; tmp = to_quantifier(tmp)->get_expr(); used.process(tmp); var_subst vs(m, true); vs(tmp, vars.size(), (expr*const*)vars.c_ptr(), fml); // collect set of variables that were used. unsigned j = 0; for (unsigned i = 0; i < vars.size(); ++i) { if (used.contains(vars.size()-i-1)) { vars.set(j, vars.get(i)); ++j; } } vars.resize(j); } else { fml = tmp; } } void operator()(expr_ref& fml, proof_ref& pr) { expr_ref tmp(m); m_elim_star(fml, tmp, pr); fml = tmp; } void operator()(uint_set const& index_set, bool index_of_bound, expr_ref& fml) { expr_ref_vector disjs(m); qe::flatten_or(fml, disjs); for (unsigned i = 0; i < disjs.size(); ++i) { expr_ref_vector conjs(m); conjs.push_back(disjs[i].get()); (*this)(index_set, index_of_bound, conjs); bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), fml); disjs[i] = fml; } bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), fml); } void operator()(uint_set const& index_set, bool index_of_bound, expr_ref_vector& fmls) { qe::flatten_and(fmls); unsigned index; if (has_unique_non_ground(fmls, index)) { expr_ref fml(m); fml = fmls[index].get(); (*this)(index_set, index_of_bound, fml); fmls[index] = fml; return; } TRACE("qe_lite", for (unsigned i = 0; i < fmls.size(); ++i) { tout << mk_pp(fmls[i].get(), m) << "\n"; }); IF_VERBOSE(3, for (unsigned i = 0; i < fmls.size(); ++i) { verbose_stream() << mk_pp(fmls[i].get(), m) << "\n"; }); is_variable_test is_var(index_set, index_of_bound); m_der.set_is_variable_proc(is_var); m_fm.set_is_variable_proc(is_var); m_array_der.set_is_variable_proc(is_var); m_der(fmls); m_fm(fmls); m_array_der(fmls); TRACE("qe_lite", for (unsigned i = 0; i < fmls.size(); ++i) tout << mk_pp(fmls[i].get(), m) << "\n";); } void set_cancel(bool f) { m_der.set_cancel(f); m_array_der.set_cancel(f); m_fm.set_cancel(f); m_elim_star.set_cancel(f); m_rewriter.set_cancel(f); } }; qe_lite::qe_lite(ast_manager& m) { m_impl = alloc(impl, m); } qe_lite::~qe_lite() { dealloc(m_impl); } void qe_lite::operator()(app_ref_vector& vars, expr_ref& fml) { (*m_impl)(vars, fml); } void qe_lite::set_cancel(bool f) { m_impl->set_cancel(f); } void qe_lite::operator()(expr_ref& fml, proof_ref& pr) { (*m_impl)(fml, pr); } void qe_lite::operator()(uint_set const& index_set, bool index_of_bound, expr_ref& fml) { (*m_impl)(index_set, index_of_bound, fml); } void qe_lite::operator()(uint_set const& index_set, bool index_of_bound, expr_ref_vector& fmls) { (*m_impl)(index_set, index_of_bound, fmls); } class qe_lite_tactic : public tactic { struct imp { ast_manager& m; qe_lite m_qe; volatile bool m_cancel; imp(ast_manager& m, params_ref const& p): m(m), m_qe(m), m_cancel(false) {} void set_cancel(bool f) { m_cancel = f; m_qe.set_cancel(f); } void checkpoint() { if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); cooperate("qe-lite"); } void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("qe-lite", *g); proof_ref new_pr(m); expr_ref new_f(m); bool produce_proofs = g->proofs_enabled(); unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { checkpoint(); if (g->inconsistent()) break; expr * f = g->form(i); if (!has_quantifiers(f)) continue; new_f = f; m_qe(new_f, new_pr); if (produce_proofs) { expr* fact = m.get_fact(new_pr); if (to_app(fact)->get_arg(0) != to_app(fact)->get_arg(1)) { new_pr = m.mk_modus_ponens(g->pr(i), new_pr); } else { new_pr = g->pr(i); } } g->update(i, new_f, new_pr, g->dep(i)); } g->inc_depth(); result.push_back(g.get()); TRACE("qe", g->display(tout);); SASSERT(g->is_well_sorted()); } }; params_ref m_params; imp * m_imp; public: qe_lite_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } virtual ~qe_lite_tactic() { dealloc(m_imp); } virtual tactic * translate(ast_manager & m) { return alloc(qe_lite_tactic, m, m_params); } virtual void updt_params(params_ref const & p) { m_params = p; // m_imp->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { // m_imp->collect_param_descrs(r); } virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { (*m_imp)(in, result, mc, pc, core); } virtual void collect_statistics(statistics & st) const { // m_imp->collect_statistics(st); } virtual void reset_statistics() { // m_imp->reset_statistics(); } virtual void cleanup() { ast_manager & m = m_imp->m; imp * d = m_imp; #pragma omp critical (tactic_cancel) { m_imp = 0; } dealloc(d); d = alloc(imp, m, m_params); #pragma omp critical (tactic_cancel) { m_imp = d; } } }; tactic * mk_qe_lite_tactic(ast_manager & m, params_ref const & p) { return alloc(qe_lite_tactic, m, p); } template class rewriter_tpl; z3-4.4.0/src/qe/qe_datatype_plugin.cpp0000644000175000017500000007421712540347414017436 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ // --------------------- // datatypes // Quantifier elimination routine for recursive data-types. // // // reduce implementation is modeled after Hodges: // subst implementation is using QE "for dummies". // for dummies: // ----------- // // Step 1. ensure x is recognized. // // exists x . phi[x] -> \/_i exists x. R_C(x) & phi[x] // // Step 2. non-recursive data-types // // exists x . R_C(x) & phi[x] -> exists y . phi[C(y)] // // Step 2. recursive data-types, eliminate selectors. // // exists x . R_C(x) & phi[x] -> exists y . phi[C(y)], x occurs with sel^C_i(x) // // Step 3. (recursive data-types) // // Solve equations // . C[t] = C[s] -> t = s // . C[x,t] = y -> x = s1(y) /\ t = s2(y) /\ R_C(y) // . C[x,t] != y -> can remain // // The remaining formula is in NNF where occurrences of 'x' are of the form // x = t_i or t[x] != s_j // // The last normalization step is: // // exists x . R_C(x) & phi[x = t_i, t[x] != s_j] // // -> \/_i R_C(t_i) & phi[t_i/x] \/ phi[false, true] // // Justification: // - We will asume that each of t_i, s_j are constructor terms. // - We can assume that x \notin t_i, x \notin s_j, or otherwise use simplification. // - We can assume that x occurs only in equalities or disequalities, or the recognizer, since // otherwise, we could simplify equalities, or QE does not apply. // - either x = t_i for some positive t_i, or we create // diag = C(t_1, ..., C(t_n, .. C(s_1, .. , C(s_m)))) // and x is different from each t_i, s_j. // // // reduce: // -------- // reduce set of literals containing x. The elimination procedure follows an adaptation of // the proof (with corrections) in Hodges (shorter model theory). // // . x = t - (x \notin t) x is eliminated immediately. // // . x != t1, ..., x != t_n - (x \notin t_i) are collected into distrinct_terms. // // . recognizer(x) - is stored as pos_recognizer. // // . !recognizer(x) - is stored into neg_recognizers. // // . L[constructor(..,x,..)] - // We could assume pre-processing introduces auxiliary // variable y, Is_constructor(y), accessor_j(y) = arg_j. // But we apply the following hack: re-introduce x' into vars, // but also the variable y and the reduced formula. // // . L[accessor_i(x)] - if pos_recognizer(x) matches accessor, // or if complement of neg_recognizers match accessor, then // introduce x_1, .., x_n corresponding to accessor_i(x). // // The only way x is not in the scope of a data-type method is if it is in // an equality or dis-equality of the form: // // . x (!)= acc_1(acc_2(..(acc_i(x)..) - would be false (true) if recognizer(..) // is declared for each sub-term. // // // - otherwise, each x should be in the scope of an accessor. // // Normalized literal elimination: // // . exists x . Lits[accessor_i(x)] & Is_constructor(x) // -> // exists x_1, .., x_n . Lits[x_1, .., x_n] for each accessor_i(x). // // // maintain set of equations and disequations with x. // #include "qe.h" #include "datatype_decl_plugin.h" #include "expr_safe_replace.h" #include "obj_pair_hashtable.h" #include "for_each_expr.h" #include "ast_pp.h" #include "ast_ll_pp.h" namespace qe { class datatype_atoms { ast_manager& m; app_ref_vector m_recognizers; expr_ref_vector m_eqs; expr_ref_vector m_neqs; app_ref_vector m_eq_atoms; app_ref_vector m_neq_atoms; app_ref_vector m_unsat_atoms; expr_ref_vector m_eq_conds; ast_mark m_mark; datatype_util m_util; public: datatype_atoms(ast_manager& m) : m(m), m_recognizers(m), m_eqs(m), m_neqs(m), m_eq_atoms(m), m_neq_atoms(m), m_unsat_atoms(m), m_eq_conds(m), m_util(m) {} bool add_atom(contains_app& contains_x, bool is_pos, app* a) { app* x = contains_x.x(); SASSERT(contains_x(a)); if (m_mark.is_marked(a)) { return true; } m_mark.mark(a, true); func_decl* f = a->get_decl(); if (m_util.is_recognizer(f) && a->get_arg(0) == x) { m_recognizers.push_back(a); TRACE("qe", tout << "add recognizer:" << mk_pp(a, m) << "\n";); return true; } if (!m.is_eq(a)) { return false; } if (add_eq(contains_x, is_pos, a->get_arg(0), a->get_arg(1))) { add_atom(a, is_pos); return true; } if (add_eq(contains_x, is_pos, a->get_arg(1), a->get_arg(0))) { add_atom(a, is_pos); return true; } if (add_unsat_eq(contains_x, a, a->get_arg(0), a->get_arg(1))) { return true; } return false; } unsigned num_eqs() { return m_eqs.size(); } expr* eq(unsigned i) { return m_eqs[i].get(); } expr* eq_cond(unsigned i) { return m_eq_conds[i].get(); } app* eq_atom(unsigned i) { return m_eq_atoms[i].get(); } unsigned num_neqs() { return m_neq_atoms.size(); } app* neq_atom(unsigned i) { return m_neq_atoms[i].get(); } unsigned num_neq_terms() const { return m_neqs.size(); } expr* neq_term(unsigned i) const { return m_neqs[i]; } expr* const* neq_terms() const { return m_neqs.c_ptr(); } unsigned num_recognizers() { return m_recognizers.size(); } app* recognizer(unsigned i) { return m_recognizers[i].get(); } unsigned num_unsat() { return m_unsat_atoms.size(); } app* unsat_atom(unsigned i) { return m_unsat_atoms[i].get(); } private: // // perform occurs check on equality. // bool add_unsat_eq(contains_app& contains_x, app* atom, expr* a, expr* b) { app* x = contains_x.x(); if (x != a) { std::swap(a, b); } if (x != a) { return false; } if (!contains_x(b)) { return false; } ptr_vector todo; ast_mark mark; todo.push_back(b); while (!todo.empty()) { b = todo.back(); todo.pop_back(); if (mark.is_marked(b)) { continue; } mark.mark(b, true); if (!is_app(b)) { continue; } if (b == x) { m_unsat_atoms.push_back(atom); return true; } app* b_app = to_app(b); if (!m_util.is_constructor(b_app)) { continue; } for (unsigned i = 0; i < b_app->get_num_args(); ++i) { todo.push_back(b_app->get_arg(i)); } } return false; } void add_atom(app* a, bool is_pos) { TRACE("qe", tout << "add atom:" << mk_pp(a, m) << " " << (is_pos?"pos":"neg") << "\n";); if (is_pos) { m_eq_atoms.push_back(a); } else { m_neq_atoms.push_back(a); } } bool add_eq(contains_app& contains_x, bool is_pos, expr* a, expr* b) { if (contains_x(b)) { return false; } if (is_pos) { return solve_eq(contains_x, a, b, m.mk_true()); } else { return solve_diseq(contains_x, a, b); } return false; } bool solve_eq(contains_app& contains_x, expr* _a, expr* b, expr* cond0) { SASSERT(contains_x(_a)); SASSERT(!contains_x(b)); app* x = contains_x.x(); if (!is_app(_a)) { return false; } app* a = to_app(_a); if (x == a) { m_eqs.push_back(b); m_eq_conds.push_back(cond0); return true; } if (!m_util.is_constructor(a)) { return false; } func_decl* c = a->get_decl(); func_decl* r = m_util.get_constructor_recognizer(c); ptr_vector const & acc = *m_util.get_constructor_accessors(c); SASSERT(acc.size() == a->get_num_args()); // // It suffices to solve just the first available equality. // The others are determined by the first. // expr_ref cond(m.mk_and(m.mk_app(r, b), cond0), m); for (unsigned i = 0; i < a->get_num_args(); ++i) { expr* l = a->get_arg(i); if (contains_x(l)) { expr_ref r(m.mk_app(acc[i], b), m); if (solve_eq(contains_x, l, r, cond)) { return true; } } } return false; } // // check that some occurrence of 'x' is in a constructor sequence. // bool solve_diseq(contains_app& contains_x, expr* a0, expr* b) { SASSERT(!contains_x(b)); SASSERT(contains_x(a0)); app* x = contains_x.x(); ptr_vector todo; ast_mark mark; todo.push_back(a0); while (!todo.empty()) { a0 = todo.back(); todo.pop_back(); if (mark.is_marked(a0)) { continue; } mark.mark(a0, true); if (!is_app(a0)) { continue; } app* a = to_app(a0); if (a == x) { m_neqs.push_back(b); return true; } if (!m_util.is_constructor(a)) { continue; } for (unsigned i = 0; i < a->get_num_args(); ++i) { todo.push_back(a->get_arg(i)); } } return false; } }; // // eliminate foreign variable under datatype functions (constructors). // (= C(x,y) t) -> (R_C(t) && x = s1(t) && x = s2(t)) // class lift_foreign_vars : public map_proc { ast_manager& m; bool m_change; datatype_util& m_util; i_solver_context& m_ctx; public: lift_foreign_vars(ast_manager& m, datatype_util& util, i_solver_context& ctx): map_proc(m), m(m), m_change(false), m_util(util), m_ctx(ctx) {} bool lift(expr_ref& fml) { m_change = false; for_each_expr(*this, fml.get()); if (m_change) { fml = get_expr(fml.get()); TRACE("qe", tout << "lift:\n" << mk_pp(fml.get(), m) << "\n";); } return m_change; } void operator()(var* v) { visit(v); } void operator()(quantifier* q) { visit(q); } void operator()(app* a) { expr* l, *r; if (m.is_eq(a, l, r)) { if (reduce_eq(a, l, r)) { m_change = true; return; } if (reduce_eq(a, r, l)) { m_change = true; return; } } reconstruct(a); } private: bool reduce_eq(app* a, expr* _l, expr* r) { if (!is_app(_l)) { return false; } app* l = to_app(_l); if (!m_util.is_constructor(l)) { return false; } if (!contains_foreign(l)) { return false; } func_decl* c = l->get_decl(); ptr_vector const& acc = *m_util.get_constructor_accessors(c); func_decl* rec = m_util.get_constructor_recognizer(c); expr_ref_vector conj(m); conj.push_back(m.mk_app(rec, r)); for (unsigned i = 0; i < acc.size(); ++i) { expr* r_i = m.mk_app(acc[i], r); expr* l_i = l->get_arg(i); conj.push_back(m.mk_eq(l_i, r_i)); } expr* e = m.mk_and(conj.size(), conj.c_ptr()); m_map.insert(a, e, 0); TRACE("qe", tout << "replace: " << mk_pp(a, m) << " ==> \n" << mk_pp(e, m) << "\n";); return true; } bool contains_foreign(app* a) { unsigned num_vars = m_ctx.get_num_vars(); for (unsigned i = 0; i < num_vars; ++i) { contains_app& v = m_ctx.contains(i); sort* s = v.x()->get_decl()->get_range(); // // data-type contains arithmetical term or bit-vector. // if (!m_util.is_datatype(s) && !m.is_bool(s) && v(a)) { return true; } } return false; } }; class datatype_plugin : public qe_solver_plugin { typedef std::pair > subst_clos; typedef obj_pair_map eqs_cache; typedef obj_pair_map subst_map; datatype_util m_datatype_util; expr_safe_replace m_replace; eqs_cache m_eqs_cache; subst_map m_subst_cache; ast_ref_vector m_trail; public: datatype_plugin(i_solver_context& ctx, ast_manager& m) : qe_solver_plugin(m, m.mk_family_id("datatype"), ctx), m_datatype_util(m), m_replace(m), m_trail(m) { } virtual ~datatype_plugin() { { eqs_cache::iterator it = m_eqs_cache.begin(), end = m_eqs_cache.end(); for (; it != end; ++it) { dealloc(it->get_value()); } } { subst_map::iterator it = m_subst_cache.begin(), end = m_subst_cache.end(); for (; it != end; ++it) { dealloc(it->get_value()); } } } virtual bool get_num_branches( contains_app& x, expr* fml, rational& num_branches) { sort* s = x.x()->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); if (m_datatype_util.is_recursive(s)) { return get_num_branches_rec(x, fml, num_branches); } else { return get_num_branches_nonrec(x, fml, num_branches); } } virtual void assign(contains_app& x, expr* fml, rational const& vl) { sort* s = x.x()->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); TRACE("qe", tout << mk_pp(x.x(), m) << " " << vl << "\n";); if (m_datatype_util.is_recursive(s)) { assign_rec(x, fml, vl); } else { assign_nonrec(x, fml, vl); } } virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) { sort* s = x.x()->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); TRACE("qe", tout << mk_pp(x.x(), m) << " " << vl << "\n";); if (m_datatype_util.is_recursive(s)) { subst_rec(x, vl, fml, def); } else { subst_nonrec(x, vl, fml, def); } } virtual unsigned get_weight( contains_app& x, expr* fml) { return UINT_MAX; } virtual bool solve( conj_enum& conj, expr* fml) { return false; } virtual bool simplify( expr_ref& fml) { lift_foreign_vars lift(m, m_datatype_util, m_ctx); return lift.lift(fml); } virtual bool mk_atom(expr* e, bool p, expr_ref& result) { return false; } virtual rational get_cost(contains_app&, expr* fml) { return rational(0); } private: void add_def(expr* term, expr_ref* def) { if (def) { *def = term; } } // // replace x by C(y1,..,yn) where y1,..,yn are fresh variables. // void subst_constructor(contains_app& x, func_decl* c, expr_ref& fml, expr_ref* def) { subst_clos* sub = 0; if (m_subst_cache.find(x.x(), c, sub)) { m_replace.apply_substitution(x.x(), sub->first, fml); add_def(sub->first, def); for (unsigned i = 0; i < sub->second.size(); ++i) { m_ctx.add_var(sub->second[i]); } return; } sub = alloc(subst_clos); unsigned arity = c->get_arity(); expr_ref_vector vars(m); for (unsigned i = 0; i < arity; ++i) { sort* sort_x = c->get_domain()[i]; app_ref fresh_x(m.mk_fresh_const("x", sort_x), m); m_ctx.add_var(fresh_x.get()); vars.push_back(fresh_x.get()); sub->second.push_back(fresh_x.get()); } app_ref t(m.mk_app(c, vars.size(), vars.c_ptr()), m); m_trail.push_back(x.x()); m_trail.push_back(c); m_trail.push_back(t); add_def(t, def); m_replace.apply_substitution(x.x(), t, fml); sub->first = t; m_subst_cache.insert(x.x(), c, sub); } void get_recognizers(expr* fml, ptr_vector& recognizers) { conj_enum conjs(m, fml); conj_enum::iterator it = conjs.begin(), end = conjs.end(); for (; it != end; ++it) { expr* e = *it; if (is_app(e)) { app* a = to_app(e); func_decl* f = a->get_decl(); if (m_datatype_util.is_recognizer(f)) { recognizers.push_back(a); } } } } bool has_recognizer(app* x, expr* fml, func_decl*& r, func_decl*& c) { ptr_vector recognizers; get_recognizers(fml, recognizers); for (unsigned i = 0; i < recognizers.size(); ++i) { app* a = recognizers[i]; if (a->get_arg(0) == x) { r = a->get_decl(); c = m_datatype_util.get_recognizer_constructor(a->get_decl()); return true; } } return false; } bool get_num_branches_rec( contains_app& x, expr* fml, rational& num_branches) { sort* s = x.x()->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); SASSERT(m_datatype_util.is_recursive(s)); unsigned sz = m_datatype_util.get_datatype_num_constructors(s); num_branches = rational(sz); func_decl* c = 0, *r = 0; // // If 'x' does not yet have a recognizer, then branch according to recognizers. // if (!has_recognizer(x.x(), fml, r, c)) { return true; } // // eliminate 'x' by applying constructor to fresh variables. // if (has_selector(x, fml, c)) { num_branches = rational(1); return true; } // // 'x' has a recognizer. Count number of equalities and disequalities. // if (update_eqs(x, fml)) { datatype_atoms& eqs = get_eqs(x.x(), fml); num_branches = rational(eqs.num_eqs() + 1); return true; } TRACE("qe", tout << "could not get number of branches " << mk_pp(x.x(), m) << "\n";); return false; } void assign_rec(contains_app& contains_x, expr* fml, rational const& vl) { app* x = contains_x.x(); sort* s = x->get_decl()->get_range(); func_decl* c = 0, *r = 0; // // If 'x' does not yet have a recognizer, then branch according to recognizers. // if (!has_recognizer(x, fml, r, c)) { c = (*m_datatype_util.get_datatype_constructors(s))[vl.get_unsigned()]; r = m_datatype_util.get_constructor_recognizer(c); app* is_c = m.mk_app(r, x); // assert v => r(x) m_ctx.add_constraint(true, is_c); return; } // // eliminate 'x' by applying constructor to fresh variables. // if (has_selector(contains_x, fml, c)) { return; } // // 'x' has a recognizer. The branch ID id provided by the index of the equality. // datatype_atoms& eqs = get_eqs(x, fml); SASSERT(vl.is_unsigned()); unsigned idx = vl.get_unsigned(); SASSERT(idx <= eqs.num_eqs()); if (idx < eqs.num_eqs()) { expr* t = eqs.eq(idx); m_ctx.add_constraint(true, m.mk_eq(x, t)); } else { for (unsigned i = 0; i < eqs.num_eqs(); ++i) { expr* t = eqs.eq(i); m_ctx.add_constraint(true, m.mk_not(m.mk_eq(x, t))); } } } void subst_rec(contains_app& contains_x, rational const& vl, expr_ref& fml, expr_ref* def) { app* x = contains_x.x(); sort* s = x->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); func_decl* c = 0, *r = 0; TRACE("qe", tout << mk_pp(x, m) << " " << vl << " " << mk_pp(fml, m) << " " << (def != 0) << "\n";); // // Add recognizer to formula. // Introduce auxiliary variable to eliminate. // if (!has_recognizer(x, fml, r, c)) { c = (*m_datatype_util.get_datatype_constructors(s))[vl.get_unsigned()]; r = m_datatype_util.get_constructor_recognizer(c); app* is_c = m.mk_app(r, x); fml = m.mk_and(is_c, fml); app_ref fresh_x(m.mk_fresh_const("x", s), m); m_ctx.add_var(fresh_x); m_replace.apply_substitution(x, fresh_x, fml); add_def(fresh_x, def); TRACE("qe", tout << "Add recognizer " << mk_pp(is_c, m) << "\n";); return; } if (has_selector(contains_x, fml, c)) { TRACE("qe", tout << "Eliminate selector " << mk_ll_pp(c, m) << "\n";); subst_constructor(contains_x, c, fml, def); return; } // // 'x' has a recognizer. The branch ID id provided by the index of the equality. // datatype_atoms& eqs = get_eqs(x, fml); SASSERT(vl.is_unsigned()); unsigned idx = vl.get_unsigned(); SASSERT(idx <= eqs.num_eqs()); for (unsigned i = 0; i < eqs.num_recognizers(); ++i) { app* rec = eqs.recognizer(i); if (rec->get_decl() == r) { m_replace.apply_substitution(rec, m.mk_true(), fml); } else { m_replace.apply_substitution(rec, m.mk_false(), fml); } } for (unsigned i = 0; i < eqs.num_unsat(); ++i) { m_replace.apply_substitution(eqs.unsat_atom(i), m.mk_false(), fml); } if (idx < eqs.num_eqs()) { expr* t = eqs.eq(idx); expr* c = eqs.eq_cond(idx); add_def(t, def); m_replace.apply_substitution(x, t, fml); if (!m.is_true(c)) { fml = m.mk_and(c, fml); } } else { for (unsigned i = 0; i < eqs.num_eqs(); ++i) { m_replace.apply_substitution(eqs.eq_atom(i), m.mk_false(), fml); } for (unsigned i = 0; i < eqs.num_neqs(); ++i) { m_replace.apply_substitution(eqs.neq_atom(i), m.mk_false(), fml); } if (def) { sort* s = m.get_sort(x); ptr_vector sorts; sorts.resize(eqs.num_neq_terms(), s); func_decl* diag = m.mk_func_decl(symbol("diag"), sorts.size(), sorts.c_ptr(), s); expr_ref t(m); t = m.mk_app(diag, eqs.num_neq_terms(), eqs.neq_terms()); add_def(t, def); } } TRACE("qe", tout << "reduced " << mk_pp(fml.get(), m) << "\n";); } bool get_num_branches_nonrec( contains_app& x, expr* fml, rational& num_branches) { sort* s = x.x()->get_decl()->get_range(); unsigned sz = m_datatype_util.get_datatype_num_constructors(s); num_branches = rational(sz); func_decl* c = 0, *r = 0; if (sz != 1 && has_recognizer(x.x(), fml, r, c)) { TRACE("qe", tout << mk_pp(x.x(), m) << " has a recognizer\n";); num_branches = rational(1); } TRACE("qe", tout << mk_pp(x.x(), m) << " branches: " << sz << "\n";); return true; } void assign_nonrec(contains_app& contains_x, expr* fml, rational const& vl) { app* x = contains_x.x(); sort* s = x->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); unsigned sz = m_datatype_util.get_datatype_num_constructors(s); SASSERT(vl.is_unsigned()); SASSERT(vl.get_unsigned() < sz); if (sz == 1) { return; } func_decl* c = 0, *r = 0; if (has_recognizer(x, fml, r, c)) { TRACE("qe", tout << mk_pp(x, m) << " has a recognizer\n";); return; } c = (*m_datatype_util.get_datatype_constructors(s))[vl.get_unsigned()]; r = m_datatype_util.get_constructor_recognizer(c); app* is_c = m.mk_app(r, x); // assert v => r(x) m_ctx.add_constraint(true, is_c); } virtual void subst_nonrec(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) { sort* s = x.x()->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); SASSERT(!m_datatype_util.is_recursive(s)); func_decl* c = 0, *r = 0; if (has_recognizer(x.x(), fml, r, c)) { TRACE("qe", tout << mk_pp(x.x(), m) << " has a recognizer\n";); } else { unsigned sz = m_datatype_util.get_datatype_num_constructors(s); SASSERT(vl.is_unsigned()); SASSERT(vl.get_unsigned() < sz); c = (*m_datatype_util.get_datatype_constructors(s))[vl.get_unsigned()]; } subst_constructor(x, c, fml, def); } class has_select : public i_expr_pred { app* m_x; func_decl* m_c; datatype_util& m_util; public: has_select(app* x, func_decl* c, datatype_util& u): m_x(x), m_c(c), m_util(u) {} virtual bool operator()(expr* e) { if (!is_app(e)) return false; app* a = to_app(e); if (!m_util.is_accessor(a)) return false; if (a->get_arg(0) != m_x) return false; func_decl* f = a->get_decl(); return m_c == m_util.get_accessor_constructor(f); } }; bool has_selector(contains_app& x, expr* fml, func_decl* c) { has_select hs(x.x(), c, m_datatype_util); check_pred ch(hs, m); return ch(fml); } datatype_atoms& get_eqs(app* x, expr* fml) { datatype_atoms* eqs = 0; VERIFY (m_eqs_cache.find(x, fml, eqs)); return *eqs; } bool update_eqs(contains_app& contains_x, expr* fml) { datatype_atoms* eqs = 0; if (m_eqs_cache.find(contains_x.x(), fml, eqs)) { return true; } eqs = alloc(datatype_atoms, m); if (!update_eqs(*eqs, contains_x, fml, m_ctx.pos_atoms(), true)) { dealloc(eqs); return false; } if (!update_eqs(*eqs, contains_x, fml, m_ctx.neg_atoms(), false)) { dealloc(eqs); return false; } m_trail.push_back(contains_x.x()); m_trail.push_back(fml); m_eqs_cache.insert(contains_x.x(), fml, eqs); return true; } bool update_eqs(datatype_atoms& eqs, contains_app& contains_x, expr* fml, atom_set const& tbl, bool is_pos) { atom_set::iterator it = tbl.begin(), end = tbl.end(); for (; it != end; ++it) { app* e = *it; if (!contains_x(e)) { continue; } if (!eqs.add_atom(contains_x, is_pos, e)) { return false; } } return true; } }; qe_solver_plugin* mk_datatype_plugin(i_solver_context& ctx) { return alloc(datatype_plugin, ctx, ctx.get_manager()); } } z3-4.4.0/src/api/0000755000175000017500000000000012540347414013205 5ustar michaelmichaelz3-4.4.0/src/api/api_rcf.cpp0000644000175000017500000002107612540347414015322 0ustar michaelmichael/*++ 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_TRY; LOG_Z3_rcf_mk_infinitesimal(c); RESET_ERROR_CODE(); reset_rcf_cancel(c); rcnumeral r; rcfm(c).mk_infinitesimal(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).neq(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_bool compact, Z3_bool html) { Z3_TRY; LOG_Z3_rcf_num_to_string(c, a, compact, html); RESET_ERROR_CODE(); reset_rcf_cancel(c); std::ostringstream buffer; rcfm(c).display(buffer, to_rcnumeral(a), compact != 0, html != 0); 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(""); } void Z3_API Z3_rcf_get_numerator_denominator(Z3_context c, Z3_rcf_num a, Z3_rcf_num * n, Z3_rcf_num * d) { Z3_TRY; LOG_Z3_rcf_get_numerator_denominator(c, a, n, d); RESET_ERROR_CODE(); reset_rcf_cancel(c); rcnumeral _n, _d; rcfm(c).clean_denominators(to_rcnumeral(a), _n, _d); *n = from_rcnumeral(_n); *d = from_rcnumeral(_d); RETURN_Z3_rcf_get_numerator_denominator; Z3_CATCH; } }; z3-4.4.0/src/api/z3_polynomial.h0000644000175000017500000000220612540347414016155 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: z3_polynomial.h Abstract: Additional APIs for polynomials. Author: Leonardo de Moura (leonardo) 2012-12-09 Notes: --*/ #ifndef _Z3_POLYNOMIAL_H_ #define _Z3_POLYNOMIAL_H_ #ifdef __cplusplus extern "C" { #endif // __cplusplus /** \defgroup capi C API */ /*@{*/ /** @name Polynomials API */ /*@{*/ /** \brief Return the nonzero subresultants of \c p and \c q with respect to the "variable" \c x. \pre \c p, \c q and \c x are Z3 expressions where \c p and \c q are arithmetic terms. Note that, any subterm that cannot be viewed as a polynomial is assumed to be a variable. Example: f(a) is a considered to be a variable in the polynomial f(a)*f(a) + 2*f(a) + 1 def_API('Z3_polynomial_subresultants', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(AST), _in(AST))) */ Z3_ast_vector Z3_API Z3_polynomial_subresultants(__in Z3_context c, __in Z3_ast p, __in Z3_ast q, __in Z3_ast x); /*@}*/ /*@}*/ #ifdef __cplusplus }; #endif // __cplusplus #endif z3-4.4.0/src/api/api_util.h0000644000175000017500000001463312540347414015173 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_util.h Abstract: Goodies used to build the Z3 external API. Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #ifndef _API_UTIL_H_ #define _API_UTIL_H_ #include"params.h" #include"lbool.h" #include"ast.h" #define Z3_TRY try { #define Z3_CATCH_CORE(CODE) } catch (z3_exception & ex) { mk_c(c)->handle_exception(ex); CODE } #define Z3_CATCH Z3_CATCH_CORE(return;) #define Z3_CATCH_RETURN(VAL) Z3_CATCH_CORE(return VAL;) #define Z3_CATCH_RETURN_NO_HANDLE(VAL) } catch (z3_exception &) { return VAL; } #define CHECK_REF_COUNT(a) (reinterpret_cast(a)->get_ref_count() > 0) namespace api { // Generic wrapper for ref-count objects exposed by the API class object { unsigned m_ref_count; 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); } }; }; inline ast * to_ast(Z3_ast a) { return reinterpret_cast(a); } inline Z3_ast of_ast(ast* a) { return reinterpret_cast(a); } inline expr * to_expr(Z3_ast a) { return reinterpret_cast(a); } inline Z3_ast of_expr(expr* e) { return reinterpret_cast(e); } inline expr * const * to_exprs(Z3_ast const* a) { return reinterpret_cast(a); } inline Z3_ast * const * of_exprs(expr* const* e) { return reinterpret_cast(e); } inline app * to_app(Z3_app a) { return reinterpret_cast(a); } inline app * to_app(Z3_ast a) { return reinterpret_cast(a); } inline Z3_app of_app(app* a) { return reinterpret_cast(a); } inline app * const* to_apps(Z3_ast const* a) { return reinterpret_cast(a); } inline ast * const * to_asts(Z3_ast const* a) { return reinterpret_cast(a); } inline sort * to_sort(Z3_sort a) { return reinterpret_cast(a); } inline Z3_sort of_sort(sort* s) { return reinterpret_cast(s); } inline sort * const * to_sorts(Z3_sort const* a) { return reinterpret_cast(a); } inline Z3_sort const * of_sorts(sort* const* s) { return reinterpret_cast(s); } inline func_decl * to_func_decl(Z3_func_decl a) { return reinterpret_cast(a); } inline Z3_func_decl of_func_decl(func_decl* f) { return reinterpret_cast(f); } inline func_decl * const * to_func_decls(Z3_func_decl const* f) { return reinterpret_cast(f); } inline symbol to_symbol(Z3_symbol s) { return symbol::mk_symbol_from_c_ptr(reinterpret_cast(s)); } inline Z3_symbol of_symbol(symbol s) { return reinterpret_cast(const_cast(s.c_ptr())); } inline Z3_pattern of_pattern(ast* a) { return reinterpret_cast(a); } inline app* to_pattern(Z3_pattern p) { return reinterpret_cast(p); } inline Z3_lbool of_lbool(lbool b) { return static_cast(b); } inline lbool to_lbool(Z3_lbool b) { return static_cast(b); } struct Z3_params_ref : public api::object { params_ref m_params; virtual ~Z3_params_ref() {} }; inline Z3_params_ref * to_params(Z3_params p) { return reinterpret_cast(p); } inline Z3_params of_params(Z3_params_ref * p) { return reinterpret_cast(p); } inline params_ref to_param_ref(Z3_params p) { return p == 0 ? params_ref() : to_params(p)->m_params; } struct Z3_param_descrs_ref : public api::object { param_descrs m_descrs; virtual ~Z3_param_descrs_ref() {} }; inline Z3_param_descrs_ref * to_param_descrs(Z3_param_descrs p) { return reinterpret_cast(p); } inline Z3_param_descrs of_param_descrs(Z3_param_descrs_ref * p) { return reinterpret_cast(p); } inline param_descrs * to_param_descrs_ptr(Z3_param_descrs p) { return p == 0 ? 0 : &(to_param_descrs(p)->m_descrs); } #define SKIP ((void) 0) #define MK_UNARY_BODY(NAME, FID, OP, EXTRA_CODE) \ Z3_TRY; \ RESET_ERROR_CODE(); \ EXTRA_CODE; \ expr * _n = to_expr(n); \ ast* a = mk_c(c)->m().mk_app(FID, OP, 0, 0, 1, &_n); \ mk_c(c)->save_ast_trail(a); \ check_sorts(c, a); \ RETURN_Z3(of_ast(a)); \ Z3_CATCH_RETURN(0); #define MK_UNARY(NAME, FID, OP, EXTRA_CODE) \ Z3_ast Z3_API NAME(Z3_context c, Z3_ast n) { \ LOG_ ## NAME(c, n); \ MK_UNARY_BODY(NAME, FID, OP, EXTRA_CODE); \ } #define MK_BINARY_BODY(NAME, FID, OP, EXTRA_CODE) \ Z3_TRY; \ RESET_ERROR_CODE(); \ EXTRA_CODE; \ expr * args[2] = { to_expr(n1), to_expr(n2) }; \ ast* a = mk_c(c)->m().mk_app(FID, OP, 0, 0, 2, args); \ mk_c(c)->save_ast_trail(a); \ check_sorts(c, a); \ RETURN_Z3(of_ast(a)); \ Z3_CATCH_RETURN(0); #define MK_BINARY(NAME, FID, OP, EXTRA_CODE) \ Z3_ast Z3_API NAME(Z3_context c, Z3_ast n1, Z3_ast n2) { \ LOG_ ## NAME(c, n1, n2); \ MK_BINARY_BODY(NAME, FID, OP, EXTRA_CODE); \ } #define MK_NARY(NAME, FID, OP, EXTRA_CODE) \ Z3_ast Z3_API NAME(Z3_context c, unsigned num_args, Z3_ast const* args) { \ Z3_TRY; \ LOG_ ## NAME(c, num_args, args); \ RESET_ERROR_CODE(); \ EXTRA_CODE; \ ast* a = mk_c(c)->m().mk_app(FID, OP, 0, 0, num_args, to_exprs(args)); \ mk_c(c)->save_ast_trail(a); \ check_sorts(c, a); \ RETURN_Z3(of_ast(a)); \ Z3_CATCH_RETURN(0); \ } #endif z3-4.4.0/src/api/api_datatype.cpp0000644000175000017500000006005112540347414016357 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_datatype.cpp Abstract: API for datatype theory Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" #include"api_util.h" #include"datatype_decl_plugin.h" extern "C" { Z3_sort Z3_API Z3_mk_tuple_sort(Z3_context c, Z3_symbol name, unsigned num_fields, Z3_symbol const field_names[], Z3_sort const field_sorts[], Z3_func_decl * mk_tuple_decl, Z3_func_decl proj_decls[]) { Z3_TRY; LOG_Z3_mk_tuple_sort(c, name, num_fields, field_names, field_sorts, mk_tuple_decl, proj_decls); RESET_ERROR_CODE(); mk_c(c)->reset_last_result(); ast_manager& m = mk_c(c)->m(); datatype_util& dt_util = mk_c(c)->dtutil(); sort_ref_vector tuples(m); sort* tuple; std::string recognizer_s("is_"); recognizer_s += to_symbol(name).str(); symbol recognizer(recognizer_s.c_str()); ptr_vector acc; for (unsigned i = 0; i < num_fields; ++i) { acc.push_back(mk_accessor_decl(to_symbol(field_names[i]), type_ref(to_sort(field_sorts[i])))); } constructor_decl* constrs[1] = { mk_constructor_decl(to_symbol(name), recognizer, acc.size(), acc.c_ptr()) }; { datatype_decl * dt = mk_datatype_decl(to_symbol(name), 1, constrs); bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &dt, tuples); del_datatype_decl(dt); if (!is_ok) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } } // create tuple type SASSERT(tuples.size() == 1); tuple = tuples[0].get(); mk_c(c)->save_multiple_ast_trail(tuple); // create constructor SASSERT(dt_util.is_datatype(tuple)); SASSERT(!dt_util.is_recursive(tuple)); ptr_vector const * decls = dt_util.get_datatype_constructors(tuple); func_decl* decl = (*decls)[0]; mk_c(c)->save_multiple_ast_trail(decl); *mk_tuple_decl = of_func_decl(decl); // Create projections ptr_vector const * accs = dt_util.get_constructor_accessors(decl); if (!accs) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } ptr_vector const & _accs = *accs; SASSERT(_accs.size() == num_fields); for (unsigned i = 0; i < _accs.size(); i++) { mk_c(c)->save_multiple_ast_trail(_accs[i]); proj_decls[i] = of_func_decl(_accs[i]); } RETURN_Z3_mk_tuple_sort(of_sort(tuple)); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_mk_enumeration_sort(Z3_context c, Z3_symbol name, unsigned n, Z3_symbol const enum_names[], Z3_func_decl enum_consts[], Z3_func_decl enum_testers[]) { Z3_TRY; LOG_Z3_mk_enumeration_sort(c, name, n, enum_names, enum_consts, enum_testers); RESET_ERROR_CODE(); mk_c(c)->reset_last_result(); ast_manager& m = mk_c(c)->m(); datatype_util& dt_util = mk_c(c)->dtutil(); sort_ref_vector sorts(m); sort* e; ptr_vector constrs; for (unsigned i = 0; i < n; ++i) { symbol e_name(to_symbol(enum_names[i])); std::string recognizer_s("is_"); recognizer_s += e_name.str(); symbol recognizer(recognizer_s.c_str()); constrs.push_back(mk_constructor_decl(e_name, recognizer, 0, 0)); } { datatype_decl * dt = mk_datatype_decl(to_symbol(name), n, constrs.c_ptr()); bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &dt, sorts); del_datatype_decl(dt); if (!is_ok) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } } // create enum type. SASSERT(sorts.size() == 1); e = sorts[0].get(); mk_c(c)->save_multiple_ast_trail(e); // create constructor SASSERT(dt_util.is_datatype(e)); SASSERT(!dt_util.is_recursive(e)); ptr_vector const * decls = dt_util.get_datatype_constructors(e); SASSERT(decls && decls->size() == n); for (unsigned i = 0; i < n; ++i) { func_decl* decl = (*decls)[i]; mk_c(c)->save_multiple_ast_trail(decl); enum_consts[i] = of_func_decl(decl); decl = dt_util.get_constructor_recognizer(decl); mk_c(c)->save_multiple_ast_trail(decl); enum_testers[i] = of_func_decl(decl); } RETURN_Z3_mk_enumeration_sort(of_sort(e)); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_mk_list_sort(Z3_context c, Z3_symbol name, Z3_sort elem_sort, Z3_func_decl* nil_decl, Z3_func_decl* is_nil_decl, Z3_func_decl* cons_decl, Z3_func_decl* is_cons_decl, Z3_func_decl* head_decl, Z3_func_decl* tail_decl ) { Z3_TRY; LOG_Z3_mk_list_sort(c, name, elem_sort, nil_decl, is_nil_decl, cons_decl, is_cons_decl, head_decl, tail_decl); RESET_ERROR_CODE(); ast_manager& m = mk_c(c)->m(); mk_c(c)->reset_last_result(); datatype_util data_util(m); accessor_decl* head_tail[2] = { mk_accessor_decl(symbol("head"), type_ref(to_sort(elem_sort))), mk_accessor_decl(symbol("tail"), type_ref(0)) }; constructor_decl* constrs[2] = { mk_constructor_decl(symbol("nil"), symbol("is_nil"), 0, 0), // Leo: SMT 2.0 document uses 'insert' instead of cons mk_constructor_decl(symbol("cons"), symbol("is_cons"), 2, head_tail) }; sort_ref_vector sorts(m); { datatype_decl * decl = mk_datatype_decl(to_symbol(name), 2, constrs); bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &decl, sorts); del_datatype_decl(decl); if (!is_ok) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } } sort * s = sorts.get(0); mk_c(c)->save_multiple_ast_trail(s); ptr_vector const& cnstrs = *data_util.get_datatype_constructors(s); SASSERT(cnstrs.size() == 2); func_decl* f; if (nil_decl) { f = cnstrs[0]; mk_c(c)->save_multiple_ast_trail(f); *nil_decl = of_func_decl(f); } if (is_nil_decl) { f = data_util.get_constructor_recognizer(cnstrs[0]); mk_c(c)->save_multiple_ast_trail(f); *is_nil_decl = of_func_decl(f); } if (cons_decl) { f = cnstrs[1]; mk_c(c)->save_multiple_ast_trail(f); *cons_decl = of_func_decl(f); } if (is_cons_decl) { f = data_util.get_constructor_recognizer(cnstrs[1]); mk_c(c)->save_multiple_ast_trail(f); *is_cons_decl = of_func_decl(f); } if (head_decl) { ptr_vector const* acc = data_util.get_constructor_accessors(cnstrs[1]); SASSERT(acc); SASSERT(acc->size() == 2); f = (*acc)[0]; mk_c(c)->save_multiple_ast_trail(f); *head_decl = of_func_decl(f); } if (tail_decl) { ptr_vector const* acc = data_util.get_constructor_accessors(cnstrs[1]); SASSERT(acc); SASSERT(acc->size() == 2); f = (*acc)[1]; mk_c(c)->save_multiple_ast_trail(f); *tail_decl = of_func_decl(f); } RETURN_Z3_mk_list_sort(of_sort(s)); Z3_CATCH_RETURN(0); } struct constructor { symbol m_name; symbol m_tester; svector m_field_names; sort_ref_vector m_sorts; unsigned_vector m_sort_refs; func_decl_ref m_constructor; constructor(ast_manager& m) : m_sorts(m), m_constructor(m) {} }; Z3_constructor Z3_API Z3_mk_constructor(Z3_context c, Z3_symbol name, Z3_symbol tester, unsigned num_fields, Z3_symbol const field_names[], Z3_sort const sorts[], unsigned sort_refs[] ) { Z3_TRY; LOG_Z3_mk_constructor(c, name, tester, num_fields, field_names, sorts, sort_refs); RESET_ERROR_CODE(); ast_manager& m = mk_c(c)->m(); constructor* cnstr = alloc(constructor, m); cnstr->m_name = to_symbol(name); cnstr->m_tester = to_symbol(tester); for (unsigned i = 0; i < num_fields; ++i) { cnstr->m_field_names.push_back(to_symbol(field_names[i])); cnstr->m_sorts.push_back(to_sort(sorts[i])); cnstr->m_sort_refs.push_back(sort_refs[i]); } RETURN_Z3(reinterpret_cast(cnstr)); Z3_CATCH_RETURN(0); } void Z3_API Z3_query_constructor(Z3_context c, Z3_constructor constr, unsigned num_fields, Z3_func_decl* constructor_decl, Z3_func_decl* tester, Z3_func_decl accessors[]) { Z3_TRY; LOG_Z3_query_constructor(c, constr, num_fields, constructor_decl, tester, accessors); RESET_ERROR_CODE(); mk_c(c)->reset_last_result(); if (!constr) { SET_ERROR_CODE(Z3_INVALID_ARG); return; } ast_manager& m = mk_c(c)->m(); datatype_util data_util(m); func_decl* f = reinterpret_cast(constr)->m_constructor.get(); if (!f) { SET_ERROR_CODE(Z3_INVALID_ARG); return; } if (constructor_decl) { mk_c(c)->save_multiple_ast_trail(f); *constructor_decl = of_func_decl(f); } if (tester) { func_decl* f2 = data_util.get_constructor_recognizer(f); mk_c(c)->save_multiple_ast_trail(f2); *tester = of_func_decl(f2); } ptr_vector const* accs = data_util.get_constructor_accessors(f); if (!accs && num_fields > 0) { SET_ERROR_CODE(Z3_INVALID_ARG); return; } for (unsigned i = 0; i < num_fields; ++i) { func_decl* f2 = (*accs)[i]; mk_c(c)->save_multiple_ast_trail(f2); accessors[i] = of_func_decl(f2); } RETURN_Z3_query_constructor; Z3_CATCH; } void Z3_API Z3_del_constructor(Z3_context c, Z3_constructor constr) { Z3_TRY; LOG_Z3_del_constructor(c, constr); RESET_ERROR_CODE(); dealloc(reinterpret_cast(constr)); Z3_CATCH; } static datatype_decl* mk_datatype_decl(Z3_context c, Z3_symbol name, unsigned num_constructors, Z3_constructor constructors[]) { ptr_vector constrs; for (unsigned i = 0; i < num_constructors; ++i) { constructor* cn = reinterpret_cast(constructors[i]); ptr_vector acc; for (unsigned j = 0; j < cn->m_sorts.size(); ++j) { if (cn->m_sorts[j].get()) { acc.push_back(mk_accessor_decl(cn->m_field_names[j], type_ref(cn->m_sorts[j].get()))); } else { acc.push_back(mk_accessor_decl(cn->m_field_names[j], type_ref(cn->m_sort_refs[j]))); } } constrs.push_back(mk_constructor_decl(cn->m_name, cn->m_tester, acc.size(), acc.c_ptr())); } return mk_datatype_decl(to_symbol(name), num_constructors, constrs.c_ptr()); } Z3_sort Z3_API Z3_mk_datatype(Z3_context c, Z3_symbol name, unsigned num_constructors, Z3_constructor constructors[]) { Z3_TRY; LOG_Z3_mk_datatype(c, name, num_constructors, constructors); RESET_ERROR_CODE(); ast_manager& m = mk_c(c)->m(); datatype_util data_util(m); sort_ref_vector sorts(m); { datatype_decl * data = mk_datatype_decl(c, name, num_constructors, constructors); bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &data, sorts); del_datatype_decl(data); if (!is_ok) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } } sort * s = sorts.get(0); mk_c(c)->save_ast_trail(s); ptr_vector const* cnstrs = data_util.get_datatype_constructors(s); for (unsigned i = 0; i < num_constructors; ++i) { constructor* cn = reinterpret_cast(constructors[i]); cn->m_constructor = (*cnstrs)[i]; } RETURN_Z3_mk_datatype(of_sort(s)); Z3_CATCH_RETURN(0); } typedef ptr_vector constructor_list; Z3_constructor_list Z3_API Z3_mk_constructor_list(Z3_context c, unsigned num_constructors, Z3_constructor const constructors[]) { Z3_TRY; LOG_Z3_mk_constructor_list(c, num_constructors, constructors); RESET_ERROR_CODE(); constructor_list* result = alloc(ptr_vector); for (unsigned i = 0; i < num_constructors; ++i) { result->push_back(reinterpret_cast(constructors[i])); } RETURN_Z3(reinterpret_cast(result)); Z3_CATCH_RETURN(0); } void Z3_API Z3_del_constructor_list(Z3_context c, Z3_constructor_list clist) { Z3_TRY; LOG_Z3_del_constructor_list(c, clist); RESET_ERROR_CODE(); dealloc(reinterpret_cast(clist)); Z3_CATCH; } void Z3_API Z3_mk_datatypes(Z3_context c, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort sorts[], Z3_constructor_list constructor_lists[]) { Z3_TRY; LOG_Z3_mk_datatypes(c, num_sorts, sort_names, sorts, constructor_lists); RESET_ERROR_CODE(); ast_manager& m = mk_c(c)->m(); mk_c(c)->reset_last_result(); datatype_util data_util(m); ptr_vector datas; for (unsigned i = 0; i < num_sorts; ++i) { constructor_list* cl = reinterpret_cast(constructor_lists[i]); datas.push_back(mk_datatype_decl(c,sort_names[i], cl->size(), reinterpret_cast(cl->c_ptr()))); } sort_ref_vector _sorts(m); bool ok = mk_c(c)->get_dt_plugin()->mk_datatypes(datas.size(), datas.c_ptr(), _sorts); del_datatype_decls(datas.size(), datas.c_ptr()); if (!ok) { SET_ERROR_CODE(Z3_INVALID_ARG); return; } SASSERT(_sorts.size() == num_sorts); for (unsigned i = 0; i < _sorts.size(); ++i) { sort* s = _sorts[i].get(); mk_c(c)->save_multiple_ast_trail(s); sorts[i] = of_sort(s); constructor_list* cl = reinterpret_cast(constructor_lists[i]); ptr_vector const* cnstrs = data_util.get_datatype_constructors(s); for (unsigned j = 0; j < cl->size(); ++j) { constructor* cn = (*cl)[j]; cn->m_constructor = (*cnstrs)[j]; } } RETURN_Z3_mk_datatypes; Z3_CATCH; } unsigned Z3_API Z3_get_datatype_sort_num_constructors(Z3_context c, Z3_sort t) { Z3_TRY; LOG_Z3_get_datatype_sort_num_constructors(c, t); RESET_ERROR_CODE(); CHECK_VALID_AST(t, 0); sort * _t = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(_t)) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } ptr_vector const * decls = dt_util.get_datatype_constructors(_t); if (!decls) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } return decls->size(); Z3_CATCH_RETURN(0); } Z3_func_decl get_datatype_sort_constructor_core(Z3_context c, Z3_sort t, unsigned idx) { RESET_ERROR_CODE(); CHECK_VALID_AST(t, 0); sort * _t = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(_t)) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } ptr_vector const * decls = dt_util.get_datatype_constructors(_t); if (!decls || idx >= decls->size()) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } func_decl* decl = (*decls)[idx]; mk_c(c)->save_ast_trail(decl); return of_func_decl(decl); } Z3_func_decl Z3_API Z3_get_datatype_sort_constructor(Z3_context c, Z3_sort t, unsigned idx) { Z3_TRY; LOG_Z3_get_datatype_sort_constructor(c, t, idx); RESET_ERROR_CODE(); Z3_func_decl r = get_datatype_sort_constructor_core(c, t, idx); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_func_decl Z3_API Z3_get_datatype_sort_recognizer(Z3_context c, Z3_sort t, unsigned idx) { Z3_TRY; LOG_Z3_get_datatype_sort_recognizer(c, t, idx); RESET_ERROR_CODE(); sort * _t = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(_t)) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } ptr_vector const * decls = dt_util.get_datatype_constructors(_t); if (!decls || idx >= decls->size()) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } func_decl* decl = (*decls)[idx]; decl = dt_util.get_constructor_recognizer(decl); mk_c(c)->save_ast_trail(decl); RETURN_Z3(of_func_decl(decl)); Z3_CATCH_RETURN(0); } Z3_func_decl Z3_API Z3_get_datatype_sort_constructor_accessor(Z3_context c, Z3_sort t, unsigned idx_c, unsigned idx_a) { Z3_TRY; LOG_Z3_get_datatype_sort_constructor_accessor(c, t, idx_c, idx_a); RESET_ERROR_CODE(); sort * _t = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(_t)) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } ptr_vector const * decls = dt_util.get_datatype_constructors(_t); if (!decls || idx_c >= decls->size()) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } func_decl* decl = (*decls)[idx_c]; if (decl->get_arity() <= idx_a) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } ptr_vector const * accs = dt_util.get_constructor_accessors(decl); SASSERT(accs && accs->size() == decl->get_arity()); if (!accs || accs->size() <= idx_a) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } decl = (*accs)[idx_a]; mk_c(c)->save_ast_trail(decl); RETURN_Z3(of_func_decl(decl)); Z3_CATCH_RETURN(0); } Z3_func_decl Z3_API Z3_get_tuple_sort_mk_decl(Z3_context c, Z3_sort t) { Z3_TRY; LOG_Z3_get_tuple_sort_mk_decl(c, t); RESET_ERROR_CODE(); sort * tuple = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(tuple) || dt_util.is_recursive(tuple) || dt_util.get_datatype_num_constructors(tuple) != 1) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } Z3_func_decl r = get_datatype_sort_constructor_core(c, t, 0); RETURN_Z3(r); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_get_tuple_sort_num_fields(Z3_context c, Z3_sort t) { Z3_TRY; LOG_Z3_get_tuple_sort_num_fields(c, t); RESET_ERROR_CODE(); sort * tuple = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(tuple) || dt_util.is_recursive(tuple) || dt_util.get_datatype_num_constructors(tuple) != 1) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } ptr_vector const * decls = dt_util.get_datatype_constructors(tuple); if (!decls || decls->size() != 1) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } ptr_vector const * accs = dt_util.get_constructor_accessors((*decls)[0]); if (!accs) { return 0; } return accs->size(); Z3_CATCH_RETURN(0); } Z3_func_decl Z3_API Z3_get_tuple_sort_field_decl(Z3_context c, Z3_sort t, unsigned i) { Z3_TRY; LOG_Z3_get_tuple_sort_field_decl(c, t, i); RESET_ERROR_CODE(); sort * tuple = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(tuple) || dt_util.is_recursive(tuple) || dt_util.get_datatype_num_constructors(tuple) != 1) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } ptr_vector const * decls = dt_util.get_datatype_constructors(tuple); if (!decls || decls->size() != 1) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } ptr_vector const * accs = dt_util.get_constructor_accessors((*decls)[0]); if (!accs) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } if (accs->size() <= i) { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } func_decl* acc = (*accs)[i]; mk_c(c)->save_ast_trail(acc); RETURN_Z3(of_func_decl(acc)); Z3_CATCH_RETURN(0); } Z3_ast Z3_datatype_update_field( __in Z3_context c, __in Z3_func_decl f, __in Z3_ast t, __in Z3_ast v) { Z3_TRY; LOG_Z3_datatype_update_field(c, f, t, v); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); func_decl* _f = to_func_decl(f); expr* _t = to_expr(t); expr* _v = to_expr(v); expr* args[2] = { _t, _v }; sort* domain[2] = { m.get_sort(_t), m.get_sort(_v) }; parameter param(_f); func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_DT_UPDATE_FIELD, 1, ¶m, 2, domain); app* r = m.mk_app(d, 2, args); mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(0); } }; z3-4.4.0/src/api/api_parsers.cpp0000644000175000017500000002577112540347414016235 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_parsers.cpp Abstract: API for parsing different formats Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" #include"api_util.h" #include"cmd_context.h" #include"smt2parser.h" #include"smtparser.h" #include"solver_na2as.h" extern "C" { void init_smtlib_parser(Z3_context c, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort const types[], unsigned num_decls, Z3_symbol const decl_names[], Z3_func_decl const decls[]) { mk_c(c)->reset_parser(); mk_c(c)->m_smtlib_parser = smtlib::parser::create(mk_c(c)->m()); mk_c(c)->m_smtlib_parser->initialize_smtlib(); smtlib::symtable * table = mk_c(c)->m_smtlib_parser->get_benchmark()->get_symtable(); for (unsigned i = 0; i < num_sorts; i++) { table->insert(to_symbol(sort_names[i]), to_sort(types[i])); } for (unsigned i = 0; i < num_decls; i++) { table->insert(to_symbol(decl_names[i]), to_func_decl(decls[i])); } } void Z3_API Z3_parse_smtlib_string(Z3_context c, const char * str, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort const sorts[], unsigned num_decls, Z3_symbol const decl_names[], Z3_func_decl const decls[]) { Z3_TRY; LOG_Z3_parse_smtlib_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls); std::ostringstream outs; bool ok = false; RESET_ERROR_CODE(); init_smtlib_parser(c, num_sorts, sort_names, sorts, num_decls, decl_names, decls); mk_c(c)->m_smtlib_parser->set_error_stream(outs); try { ok = mk_c(c)->m_smtlib_parser->parse_string(str); } catch (...) { ok = false; } mk_c(c)->m_smtlib_error_buffer = outs.str(); if (!ok) { mk_c(c)->reset_parser(); SET_ERROR_CODE(Z3_PARSER_ERROR); } Z3_CATCH; } void Z3_API Z3_parse_smtlib_file(Z3_context c, const char * file_name, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort const types[], unsigned num_decls, Z3_symbol const decl_names[], Z3_func_decl const decls[]) { Z3_TRY; LOG_Z3_parse_smtlib_file(c, file_name, num_sorts, sort_names, types, num_decls, decl_names, decls); bool ok = false; RESET_ERROR_CODE(); std::ostringstream outs; init_smtlib_parser(c, num_sorts, sort_names, types, num_decls, decl_names, decls); mk_c(c)->m_smtlib_parser->set_error_stream(outs); try { ok = mk_c(c)->m_smtlib_parser->parse_file(file_name); } catch(...) { ok = false; } mk_c(c)->m_smtlib_error_buffer = outs.str(); if (!ok) { mk_c(c)->reset_parser(); SET_ERROR_CODE(Z3_PARSER_ERROR); } Z3_CATCH; } unsigned Z3_API Z3_get_smtlib_num_formulas(Z3_context c) { Z3_TRY; LOG_Z3_get_smtlib_num_formulas(c); RESET_ERROR_CODE(); if (mk_c(c)->m_smtlib_parser) { return mk_c(c)->m_smtlib_parser->get_benchmark()->get_num_formulas(); } SET_ERROR_CODE(Z3_NO_PARSER); return 0; Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_get_smtlib_formula(Z3_context c, unsigned i) { Z3_TRY; LOG_Z3_get_smtlib_formula(c, i); RESET_ERROR_CODE(); if (mk_c(c)->m_smtlib_parser) { if (i < mk_c(c)->m_smtlib_parser->get_benchmark()->get_num_formulas()) { ast * f = mk_c(c)->m_smtlib_parser->get_benchmark()->begin_formulas()[i]; mk_c(c)->save_ast_trail(f); RETURN_Z3(of_ast(f)); } else { SET_ERROR_CODE(Z3_IOB); } } else { SET_ERROR_CODE(Z3_NO_PARSER); } RETURN_Z3(0); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_get_smtlib_num_assumptions(Z3_context c) { Z3_TRY; LOG_Z3_get_smtlib_num_assumptions(c); RESET_ERROR_CODE(); if (mk_c(c)->m_smtlib_parser) { return mk_c(c)->m_smtlib_parser->get_benchmark()->get_num_axioms(); } SET_ERROR_CODE(Z3_NO_PARSER); return 0; Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_get_smtlib_assumption(Z3_context c, unsigned i) { Z3_TRY; LOG_Z3_get_smtlib_assumption(c, i); RESET_ERROR_CODE(); if (mk_c(c)->m_smtlib_parser) { if (i < mk_c(c)->m_smtlib_parser->get_benchmark()->get_num_axioms()) { ast * a = mk_c(c)->m_smtlib_parser->get_benchmark()->begin_axioms()[i]; mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); } else { SET_ERROR_CODE(Z3_IOB); } } else { SET_ERROR_CODE(Z3_NO_PARSER); } RETURN_Z3(0); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_get_smtlib_num_decls(Z3_context c) { Z3_TRY; LOG_Z3_get_smtlib_num_decls(c); RESET_ERROR_CODE(); if (mk_c(c)->m_smtlib_parser) { mk_c(c)->extract_smtlib_parser_decls(); return mk_c(c)->m_smtlib_parser_decls.size(); } SET_ERROR_CODE(Z3_NO_PARSER); return 0; Z3_CATCH_RETURN(0); } Z3_func_decl Z3_API Z3_get_smtlib_decl(Z3_context c, unsigned i) { Z3_TRY; LOG_Z3_get_smtlib_decl(c, i); RESET_ERROR_CODE(); mk_c(c)->extract_smtlib_parser_decls(); if (mk_c(c)->m_smtlib_parser) { if (i < mk_c(c)->m_smtlib_parser_decls.size()) { func_decl * d = mk_c(c)->m_smtlib_parser_decls[i]; mk_c(c)->save_ast_trail(d); RETURN_Z3(of_func_decl(d)); } else { SET_ERROR_CODE(Z3_IOB); } } else { SET_ERROR_CODE(Z3_NO_PARSER); } RETURN_Z3(0); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_get_smtlib_num_sorts(Z3_context c) { Z3_TRY; LOG_Z3_get_smtlib_num_sorts(c); RESET_ERROR_CODE(); if (mk_c(c)->m_smtlib_parser) { mk_c(c)->extract_smtlib_parser_decls(); return mk_c(c)->m_smtlib_parser_sorts.size(); } SET_ERROR_CODE(Z3_NO_PARSER); return 0; Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_get_smtlib_sort(Z3_context c, unsigned i) { Z3_TRY; LOG_Z3_get_smtlib_sort(c, i); RESET_ERROR_CODE(); if (mk_c(c)->m_smtlib_parser) { mk_c(c)->extract_smtlib_parser_decls(); if (i < mk_c(c)->m_smtlib_parser_sorts.size()) { sort* s = mk_c(c)->m_smtlib_parser_sorts[i]; mk_c(c)->save_ast_trail(s); RETURN_Z3(of_sort(s)); } else { SET_ERROR_CODE(Z3_IOB); } } else { SET_ERROR_CODE(Z3_NO_PARSER); } RETURN_Z3(0); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_get_smtlib_error(Z3_context c) { Z3_TRY; LOG_Z3_get_smtlib_error(c); RESET_ERROR_CODE(); return mk_c(c)->m_smtlib_error_buffer.c_str(); Z3_CATCH_RETURN(""); } // --------------- // Support for SMTLIB2 Z3_ast parse_smtlib2_stream(bool exec, Z3_context c, std::istream& is, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort const sorts[], unsigned num_decls, Z3_symbol const decl_names[], Z3_func_decl const decls[]) { Z3_TRY; cmd_context ctx(false, &(mk_c(c)->m())); ctx.set_ignore_check(true); for (unsigned i = 0; i < num_decls; ++i) { ctx.insert(to_symbol(decl_names[i]), to_func_decl(decls[i])); } for (unsigned i = 0; i < num_sorts; ++i) { psort* ps = ctx.pm().mk_psort_cnst(to_sort(sorts[i])); ctx.insert(ctx.pm().mk_psort_user_decl(0, to_symbol(sort_names[i]), ps)); } if (!parse_smt2_commands(ctx, is)) { SET_ERROR_CODE(Z3_PARSER_ERROR); return of_ast(mk_c(c)->m().mk_true()); } ptr_vector::const_iterator it = ctx.begin_assertions(); ptr_vector::const_iterator end = ctx.end_assertions(); unsigned size = static_cast(end - it); return of_ast(mk_c(c)->mk_and(size, it)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_parse_smtlib2_string(Z3_context c, Z3_string str, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort const sorts[], unsigned num_decls, Z3_symbol const decl_names[], Z3_func_decl const decls[]) { Z3_TRY; LOG_Z3_parse_smtlib2_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls); std::string s(str); std::istringstream is(s); Z3_ast r = parse_smtlib2_stream(false, c, is, num_sorts, sort_names, sorts, num_decls, decl_names, decls); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_parse_smtlib2_file(Z3_context c, Z3_string file_name, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort const sorts[], unsigned num_decls, Z3_symbol const decl_names[], Z3_func_decl const decls[]) { Z3_TRY; LOG_Z3_parse_smtlib2_string(c, file_name, num_sorts, sort_names, sorts, num_decls, decl_names, decls); std::ifstream is(file_name); if (!is) { SET_ERROR_CODE(Z3_PARSER_ERROR); return 0; } Z3_ast r = parse_smtlib2_stream(false, c, is, num_sorts, sort_names, sorts, num_decls, decl_names, decls); RETURN_Z3(r); Z3_CATCH_RETURN(0); } }; z3-4.4.0/src/api/api_context.h0000644000175000017500000002241412540347414015676 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_context.h Abstract: Interface of Z3 with "external world". It was called _Z3_context Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #ifndef _API_CONTEXT_H_ #define _API_CONTEXT_H_ #include"z3.h" #include"ast.h" #include"api_util.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"datatype_decl_plugin.h" #include"dl_decl_plugin.h" #include"fpa_decl_plugin.h" #include"smt_kernel.h" #include"smt_params.h" #include"event_handler.h" #include"tactic_manager.h" #include"context_params.h" #include"api_polynomial.h" namespace smtlib { class parser; }; namespace realclosure { class manager; }; namespace api { Z3_search_failure mk_Z3_search_failure(smt::failure f); class context : public tactic_manager { struct add_plugins { add_plugins(ast_manager & m); }; context_params m_params; bool m_user_ref_count; //!< if true, the user is responsible for managing referenc counters. scoped_ptr m_manager; add_plugins m_plugins; arith_util m_arith_util; bv_util m_bv_util; datalog::dl_decl_util m_datalog_util; fpa_util m_fpa_util; datatype_util m_dtutil; // Support for old solver API smt_params m_fparams; smt::kernel * m_solver; // General purpose solver for backward compatibility // ------------------------------- ast_ref_vector m_last_result; //!< used when m_user_ref_count == true ast_ref_vector m_ast_trail; //!< used when m_user_ref_count == false unsigned_vector m_ast_lim; ptr_vector m_replay_stack; ref m_last_obj; //!< reference to the last API object returned by the APIs family_id m_basic_fid; family_id m_array_fid; family_id m_arith_fid; family_id m_bv_fid; family_id m_dt_fid; family_id m_datalog_fid; family_id m_pb_fid; family_id m_fpa_fid; datatype_decl_plugin * m_dt_plugin; std::string m_string_buffer; // temporary buffer used to cache strings sent to the "external" world. Z3_error_code m_error_code; Z3_error_handler * m_error_handler; std::string m_exception_msg; // catch the message associated with a Z3 exception bool m_searching; Z3_ast_print_mode m_print_mode; event_handler * m_interruptable; // Reference to an object that can be interrupted by Z3_interrupt public: // Scoped obj for setting m_interruptable class set_interruptable { context & m_ctx; public: set_interruptable(context & ctx, event_handler & i); ~set_interruptable(); }; // ------------------------ // // Core // // ------------------------ context(context_params * p, bool user_ref_count = false); ~context(); ast_manager & m() const { return *(m_manager.get()); } context_params & params() { return m_params; } bool produce_proofs() const { return m().proofs_enabled(); } bool produce_models() const { return m_params.m_model; } bool produce_unsat_cores() const { return m_params.m_unsat_core; } bool use_auto_config() const { return m_params.m_auto_config; } unsigned get_timeout() const { return m_params.m_timeout; } arith_util & autil() { return m_arith_util; } bv_util & bvutil() { return m_bv_util; } datalog::dl_decl_util & datalog_util() { return m_datalog_util; } fpa_util & fpautil() { return m_fpa_util; } datatype_util& dtutil() { return m_dtutil; } family_id get_basic_fid() const { return m_basic_fid; } family_id get_array_fid() const { return m_array_fid; } family_id get_arith_fid() const { return m_arith_fid; } family_id get_bv_fid() const { return m_bv_fid; } family_id get_dt_fid() const { return m_dt_fid; } family_id get_datalog_fid() const { return m_datalog_fid; } family_id get_pb_fid() const { return m_pb_fid; } family_id get_fpa_fid() const { return m_fpa_fid; } datatype_decl_plugin * get_dt_plugin() const { return m_dt_plugin; } Z3_error_code get_error_code() const { return m_error_code; } void reset_error_code() { m_error_code = Z3_OK; } void set_error_code(Z3_error_code err); void set_error_handler(Z3_error_handler h) { m_error_handler = h; } // Sign an error if solver is searching void check_searching(); Z3_ast_print_mode get_print_mode() const { return m_print_mode; } void set_print_mode(Z3_ast_print_mode m) { m_print_mode = m; } // Store a copy of str in m_string_buffer, and return a reference to it. // This method is used to communicate local/internal strings with the "external world" char * mk_external_string(char const * str); char * mk_external_string(std::string const & str); // Create a numeral of the given sort expr * mk_numeral_core(rational const & n, sort * s); // Return a conjuction that will be exposed to the "external" world. expr * mk_and(unsigned num_exprs, expr * const * exprs); // Hack for preventing an AST for being GC when ref-count is not used void persist_ast(ast * n, unsigned num_scopes); // "Save" an AST that will exposed to the "external" world. void save_ast_trail(ast * n); // Similar to previous method, but it "adds" n to the result. void save_multiple_ast_trail(ast * n); // Reset the cache that stores the ASTs exposed in the previous call. // This is a NOOP if ref-count is disabled. void reset_last_result(); // "Save" a reference to an object that is exposed by the API void save_object(object * r); // Process exception: save message and set error code. void handle_exception(z3_exception & ex); char const * get_exception_msg() const { return m_exception_msg.c_str(); } // Interrupt the current interruptable object void interrupt(); void invoke_error_handler(Z3_error_code c); static void out_of_memory_handler(void * _ctx); void check_sorts(ast * n); // ------------------------ // // 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 // // ------------------------ smt_params & fparams() { return m_fparams; } bool has_solver() const { return m_solver != 0; } smt::kernel & get_smt_kernel(); void assert_cnstr(expr * a); lbool check(model_ref & m); void push(); void pop(unsigned num_scopes); unsigned get_num_scopes() const { return m_ast_lim.size(); } // ------------------------ // // Parser interface for backward compatibility // // ------------------------ // TODO: move to a "parser" object visible to the external world. std::string m_smtlib_error_buffer; smtlib::parser * m_smtlib_parser; bool m_smtlib_parser_has_decls; ptr_vector m_smtlib_parser_decls; ptr_vector m_smtlib_parser_sorts; void reset_parser(); void extract_smtlib_parser_decls(); }; }; inline api::context * mk_c(Z3_context c) { return reinterpret_cast(c); } #define RESET_ERROR_CODE() { mk_c(c)->reset_error_code(); } #define SET_ERROR_CODE(ERR) { mk_c(c)->set_error_code(ERR); } #define CHECK_NON_NULL(_p_,_ret_) { if (_p_ == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); return _ret_; } } #define CHECK_VALID_AST(_a_, _ret_) { if (_a_ == 0 || !CHECK_REF_COUNT(_a_)) { SET_ERROR_CODE(Z3_INVALID_ARG); return _ret_; } } #define CHECK_SEARCHING(c) mk_c(c)->check_searching(); inline bool is_expr(Z3_ast a) { return is_expr(to_ast(a)); } inline bool is_bool_expr(Z3_context c, Z3_ast a) { return is_expr(a) && mk_c(c)->m().is_bool(to_expr(a)); } #define CHECK_FORMULA(_a_, _ret_) { if (_a_ == 0 || !CHECK_REF_COUNT(_a_) || !is_bool_expr(c, _a_)) { SET_ERROR_CODE(Z3_INVALID_ARG); return _ret_; } } inline void check_sorts(Z3_context c, ast * n) { mk_c(c)->check_sorts(n); } #endif z3-4.4.0/src/api/ml/0000755000175000017500000000000012540347414013615 5ustar michaelmichaelz3-4.4.0/src/api/ml/z3_stubs.c0000644000175000017500000171000612540347414015542 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ /* File generated from z3.idl */ #include #include #include #include #include #include #include #ifdef Custom_tag #include #include #endif #include #include "z3.h" #define xstr(s) str(s) #define str(s) #s #pragma warning(disable:4090) void check_error_code (Z3_context c); Z3_context last_ctx; value caml_final_register (value f, value v); void register_finalizer(value** closure, char* name, Z3_context ctx, value v) { if (*closure == NULL) { *closure = caml_named_value(name); if (*closure == NULL) { Z3_set_error(ctx, Z3_INTERNAL_FATAL); return; } } caml_final_register(**closure, v); } value c2ml_Z3_context (Z3_context* c) { static value* finalize_Z3_context_closure = NULL; value v; v = caml_alloc_small(1, Abstract_tag); Field(v, 0) = (value) *c; register_finalizer(&finalize_Z3_context_closure, "finalize_Z3_context", (Z3_context) *c, v); return v; } void ml2c_Z3_context (value v, Z3_context* c) { *c = (Z3_context) Field(v, 0); last_ctx = *c; } value finalize_Z3_context (value v) { Z3_context c; c = (Z3_context) Field(v, 0); Z3_del_context(c); return Val_unit; } #define camlidl_ml2c_z3_Z3_context(v,c,ctx) ml2c_Z3_context(v,c) #define camlidl_c2ml_z3_Z3_context(c,ctx) c2ml_Z3_context(c) void camlidl_ml2c_z3_Z3_symbol(value _v1, Z3_symbol * _c2, camlidl_ctx _ctx) { *_c2 = *((Z3_symbol *) Bp_val(_v1)); } value camlidl_c2ml_z3_Z3_symbol(Z3_symbol * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_alloc((sizeof(Z3_symbol) + sizeof(value) - 1) / sizeof(value), Abstract_tag); *((Z3_symbol *) Bp_val(_v1)) = *_c2; return _v1; } typedef struct _Z3_ast_context { Z3_ast ast; Z3_context ctx; } Z3_ast_context; void ml2c_Z3_ast (value v, Z3_ast* c) { *c = ((Z3_ast_context*) Data_custom_val(v))->ast; } static int compare_Z3_ast (value v1, value v2) { Z3_ast_context* ac1; Z3_ast_context* ac2; unsigned int id1, id2; ac1 = Data_custom_val(v1); ac2 = Data_custom_val(v2); id1 = Z3_get_ast_id(ac1->ctx, ac1->ast); check_error_code(ac1->ctx); id2 = Z3_get_ast_id(ac2->ctx, ac2->ast); check_error_code(ac2->ctx); return id2 - id1; } static intnat hash_Z3_ast (value v) { Z3_ast_context* ac; unsigned int hash; ac = Data_custom_val(v); hash = Z3_get_ast_hash(ac->ctx, ac->ast); check_error_code(ac->ctx); return hash; } value finalize_Z3_ast (value v) { Z3_ast_context* ac; ac = Data_custom_val(v); Z3_dec_ref(ac->ctx, ac->ast); check_error_code(ac->ctx); return Val_unit; } static struct custom_operations cops_Z3_ast = { NULL, custom_finalize_default, compare_Z3_ast, hash_Z3_ast, custom_serialize_default, custom_deserialize_default }; value c2ml_Z3_ast (Z3_ast* c) { static value* finalize_Z3_ast_closure = NULL; value v; Z3_ast_context* ac; check_error_code(last_ctx); v = caml_alloc_custom(&cops_Z3_ast, sizeof(Z3_ast_context), 0, 1); ac = Data_custom_val(v); ac->ast = *c; ac->ctx = last_ctx; register_finalizer(&finalize_Z3_ast_closure, "finalize_Z3_ast", (Z3_context) *c, v); Z3_inc_ref(last_ctx, *c); return v; } #define camlidl_ml2c_z3_Z3_ast(v,c,ctx) ml2c_Z3_ast(v,c) #define camlidl_c2ml_z3_Z3_ast(c,ctx) c2ml_Z3_ast(c) #define DEFINE_SUBAST_OPS(T) void ml2c_ ## T (value v, T * a) { ml2c_Z3_ast(v, (Z3_ast*) a); } value c2ml_ ## T (T * a) { return c2ml_Z3_ast((Z3_ast*) a); } DEFINE_SUBAST_OPS(Z3_sort) #define camlidl_ml2c_z3_Z3_sort(v,c,ctx) ml2c_Z3_sort(v,c) #define camlidl_c2ml_z3_Z3_sort(c,ctx) c2ml_Z3_sort(c) DEFINE_SUBAST_OPS(Z3_func_decl) #define camlidl_ml2c_z3_Z3_func_decl(v,c,ctx) ml2c_Z3_func_decl(v,c) #define camlidl_c2ml_z3_Z3_func_decl(c,ctx) c2ml_Z3_func_decl(c) DEFINE_SUBAST_OPS(Z3_app) #define camlidl_ml2c_z3_Z3_app(v,c,ctx) ml2c_Z3_app(v,c) #define camlidl_c2ml_z3_Z3_app(c,ctx) c2ml_Z3_app(c) DEFINE_SUBAST_OPS(Z3_pattern) #define camlidl_ml2c_z3_Z3_pattern(v,c,ctx) ml2c_Z3_pattern(v,c) #define camlidl_c2ml_z3_Z3_pattern(c,ctx) c2ml_Z3_pattern(c) #define DEFINE_RC_OPS(T) value c2ml_ ## T (T * c) { static value* finalize_ ## T ## _closure = NULL; value v; check_error_code(last_ctx); v = caml_alloc_small(2, Abstract_tag); Field(v, 0) = (value) *c; Field(v, 1) = (value) last_ctx; register_finalizer(&finalize_ ## T ## _closure, xstr(finalize_ ## T), (Z3_context) *c, v); T ## _inc_ref(last_ctx, *c); return v; } void ml2c_ ## T (value v, T * c) { *c = (T) Field(v, 0); } value finalize_ ## T (value v) { Z3_context c; c = (Z3_context) Field(v, 1); T ## _dec_ref(c, (T) Field(v, 0)); check_error_code(c); return Val_unit; } DEFINE_RC_OPS(Z3_params) #define camlidl_ml2c_z3_Z3_params(v,c,ctx) ml2c_Z3_params(v,c) #define camlidl_c2ml_z3_Z3_params(c,ctx) c2ml_Z3_params(c) DEFINE_RC_OPS(Z3_param_descrs) #define camlidl_ml2c_z3_Z3_param_descrs(v,c,ctx) ml2c_Z3_param_descrs(v,c) #define camlidl_c2ml_z3_Z3_param_descrs(c,ctx) c2ml_Z3_param_descrs(c) DEFINE_RC_OPS(Z3_model) #define camlidl_ml2c_z3_Z3_model(v,c,ctx) ml2c_Z3_model(v,c) #define camlidl_c2ml_z3_Z3_model(c,ctx) c2ml_Z3_model(c) DEFINE_RC_OPS(Z3_func_interp) #define camlidl_ml2c_z3_Z3_func_interp(v,c,ctx) ml2c_Z3_func_interp(v,c) #define camlidl_c2ml_z3_Z3_func_interp(c,ctx) c2ml_Z3_func_interp(c) DEFINE_RC_OPS(Z3_func_entry) #define camlidl_ml2c_z3_Z3_func_entry(v,c,ctx) ml2c_Z3_func_entry(v,c) #define camlidl_c2ml_z3_Z3_func_entry(c,ctx) c2ml_Z3_func_entry(c) DEFINE_RC_OPS(Z3_fixedpoint) #define camlidl_ml2c_z3_Z3_fixedpoint(v,c,ctx) ml2c_Z3_fixedpoint(v,c) #define camlidl_c2ml_z3_Z3_fixedpoint(c,ctx) c2ml_Z3_fixedpoint(c) DEFINE_RC_OPS(Z3_ast_vector) #define camlidl_ml2c_z3_Z3_ast_vector(v,c,ctx) ml2c_Z3_ast_vector(v,c) #define camlidl_c2ml_z3_Z3_ast_vector(c,ctx) c2ml_Z3_ast_vector(c) DEFINE_RC_OPS(Z3_ast_map) #define camlidl_ml2c_z3_Z3_ast_map(v,c,ctx) ml2c_Z3_ast_map(v,c) #define camlidl_c2ml_z3_Z3_ast_map(c,ctx) c2ml_Z3_ast_map(c) DEFINE_RC_OPS(Z3_goal) #define camlidl_ml2c_z3_Z3_goal(v,c,ctx) ml2c_Z3_goal(v,c) #define camlidl_c2ml_z3_Z3_goal(c,ctx) c2ml_Z3_goal(c) DEFINE_RC_OPS(Z3_tactic) #define camlidl_ml2c_z3_Z3_tactic(v,c,ctx) ml2c_Z3_tactic(v,c) #define camlidl_c2ml_z3_Z3_tactic(c,ctx) c2ml_Z3_tactic(c) DEFINE_RC_OPS(Z3_probe) #define camlidl_ml2c_z3_Z3_probe(v,c,ctx) ml2c_Z3_probe(v,c) #define camlidl_c2ml_z3_Z3_probe(c,ctx) c2ml_Z3_probe(c) DEFINE_RC_OPS(Z3_apply_result) #define camlidl_ml2c_z3_Z3_apply_result(v,c,ctx) ml2c_Z3_apply_result(v,c) #define camlidl_c2ml_z3_Z3_apply_result(c,ctx) c2ml_Z3_apply_result(c) DEFINE_RC_OPS(Z3_solver) #define camlidl_ml2c_z3_Z3_solver(v,c,ctx) ml2c_Z3_solver(v,c) #define camlidl_c2ml_z3_Z3_solver(c,ctx) c2ml_Z3_solver(c) DEFINE_RC_OPS(Z3_stats) #define camlidl_ml2c_z3_Z3_stats(v,c,ctx) ml2c_Z3_stats(v,c) #define camlidl_c2ml_z3_Z3_stats(c,ctx) c2ml_Z3_stats(c) #define DEFINE_OPT_OPS(T) void ml2c_ ## T ## _opt (value v, T* c) { struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; if (v != Val_int(0)) { camlidl_ml2c_z3_ ## T(Field(v, 0), c, _ctx); } else { *c = NULL; } } value c2ml_ ## T ## _opt (T* c) { struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; value v; value a; if (*c) { a = camlidl_c2ml_z3_ ## T(c, _ctx); Begin_root(a) v = caml_alloc_small(1, 0); Field(v, 0) = a; End_roots(); } else { v = Val_int(0); } return v; } DEFINE_OPT_OPS(Z3_ast) #define camlidl_ml2c_z3_Z3_ast_opt(v,c,ctx) ml2c_Z3_ast_opt(v,c) #define camlidl_c2ml_z3_Z3_ast_opt(c,ctx) c2ml_Z3_ast_opt(c) DEFINE_OPT_OPS(Z3_sort) #define camlidl_ml2c_z3_Z3_sort_opt(v,c,ctx) ml2c_Z3_sort_opt(v,c) #define camlidl_c2ml_z3_Z3_sort_opt(c,ctx) c2ml_Z3_sort_opt(c) DEFINE_OPT_OPS(Z3_func_interp) #define camlidl_ml2c_z3_Z3_func_interp_opt(v,c,ctx) ml2c_Z3_func_interp_opt(v,c) #define camlidl_c2ml_z3_Z3_func_interp_opt(c,ctx) c2ml_Z3_func_interp_opt(c) void camlidl_ml2c_z3_Z3_constructor(value _v1, Z3_constructor * _c2, camlidl_ctx _ctx) { *_c2 = *((Z3_constructor *) Bp_val(_v1)); } value camlidl_c2ml_z3_Z3_constructor(Z3_constructor * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_alloc((sizeof(Z3_constructor) + sizeof(value) - 1) / sizeof(value), Abstract_tag); *((Z3_constructor *) Bp_val(_v1)) = *_c2; return _v1; } void camlidl_ml2c_z3_Z3_constructor_list(value _v1, Z3_constructor_list * _c2, camlidl_ctx _ctx) { *_c2 = *((Z3_constructor_list *) Bp_val(_v1)); } value camlidl_c2ml_z3_Z3_constructor_list(Z3_constructor_list * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_alloc((sizeof(Z3_constructor_list) + sizeof(value) - 1) / sizeof(value), Abstract_tag); *((Z3_constructor_list *) Bp_val(_v1)) = *_c2; return _v1; } void camlidl_ml2c_z3_Z3_string(value _v1, Z3_string * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_malloc_string(_v1, _ctx); } value camlidl_c2ml_z3_Z3_string(Z3_string * _c2, camlidl_ctx _ctx) { value _v1; _v1 = copy_string((*_c2)); return _v1; } int camlidl_transl_table_z3_enum_1[3] = { Z3_L_FALSE, Z3_L_UNDEF, Z3_L_TRUE, }; void camlidl_ml2c_z3_Z3_lbool(value _v1, Z3_lbool * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3_enum_1[Int_val(_v1)]; } value camlidl_c2ml_z3_Z3_lbool(Z3_lbool * _c2, camlidl_ctx _ctx) { value _v1; switch((*_c2)) { case Z3_L_FALSE: _v1 = Val_int(0); break; case Z3_L_UNDEF: _v1 = Val_int(1); break; case Z3_L_TRUE: _v1 = Val_int(2); break; default: invalid_argument("typedef Z3_lbool: bad enum value"); } return _v1; } int camlidl_transl_table_z3_enum_2[2] = { Z3_INT_SYMBOL, Z3_STRING_SYMBOL, }; void camlidl_ml2c_z3_Z3_symbol_kind(value _v1, Z3_symbol_kind * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3_enum_2[Int_val(_v1)]; } value camlidl_c2ml_z3_Z3_symbol_kind(Z3_symbol_kind * _c2, camlidl_ctx _ctx) { value _v1; switch((*_c2)) { case Z3_INT_SYMBOL: _v1 = Val_int(0); break; case Z3_STRING_SYMBOL: _v1 = Val_int(1); break; default: invalid_argument("typedef Z3_symbol_kind: bad enum value"); } return _v1; } int camlidl_transl_table_z3_enum_3[7] = { Z3_PARAMETER_INT, Z3_PARAMETER_DOUBLE, Z3_PARAMETER_RATIONAL, Z3_PARAMETER_SYMBOL, Z3_PARAMETER_SORT, Z3_PARAMETER_AST, Z3_PARAMETER_FUNC_DECL, }; void camlidl_ml2c_z3_Z3_parameter_kind(value _v1, Z3_parameter_kind * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3_enum_3[Int_val(_v1)]; } value camlidl_c2ml_z3_Z3_parameter_kind(Z3_parameter_kind * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3_enum_3, 7, "typedef Z3_parameter_kind: bad enum value"); return _v1; } int camlidl_transl_table_z3_enum_4[10] = { Z3_UNINTERPRETED_SORT, Z3_BOOL_SORT, Z3_INT_SORT, Z3_REAL_SORT, Z3_BV_SORT, Z3_ARRAY_SORT, Z3_DATATYPE_SORT, Z3_RELATION_SORT, Z3_FINITE_DOMAIN_SORT, Z3_UNKNOWN_SORT, }; void camlidl_ml2c_z3_Z3_sort_kind(value _v1, Z3_sort_kind * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3_enum_4[Int_val(_v1)]; } value camlidl_c2ml_z3_Z3_sort_kind(Z3_sort_kind * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3_enum_4, 10, "typedef Z3_sort_kind: bad enum value"); return _v1; } int camlidl_transl_table_z3_enum_5[7] = { Z3_NUMERAL_AST, Z3_APP_AST, Z3_VAR_AST, Z3_QUANTIFIER_AST, Z3_SORT_AST, Z3_FUNC_DECL_AST, Z3_UNKNOWN_AST, }; void camlidl_ml2c_z3_Z3_ast_kind(value _v1, Z3_ast_kind * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3_enum_5[Int_val(_v1)]; } value camlidl_c2ml_z3_Z3_ast_kind(Z3_ast_kind * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3_enum_5, 7, "typedef Z3_ast_kind: bad enum value"); return _v1; } int camlidl_transl_table_z3_enum_6[152] = { Z3_OP_TRUE, Z3_OP_FALSE, Z3_OP_EQ, Z3_OP_DISTINCT, Z3_OP_ITE, Z3_OP_AND, Z3_OP_OR, Z3_OP_IFF, Z3_OP_XOR, Z3_OP_NOT, Z3_OP_IMPLIES, Z3_OP_OEQ, Z3_OP_ANUM, Z3_OP_AGNUM, Z3_OP_LE, Z3_OP_GE, Z3_OP_LT, Z3_OP_GT, Z3_OP_ADD, Z3_OP_SUB, Z3_OP_UMINUS, Z3_OP_MUL, Z3_OP_DIV, Z3_OP_IDIV, Z3_OP_REM, Z3_OP_MOD, Z3_OP_TO_REAL, Z3_OP_TO_INT, Z3_OP_IS_INT, Z3_OP_POWER, Z3_OP_STORE, Z3_OP_SELECT, Z3_OP_CONST_ARRAY, Z3_OP_ARRAY_MAP, Z3_OP_ARRAY_DEFAULT, Z3_OP_SET_UNION, Z3_OP_SET_INTERSECT, Z3_OP_SET_DIFFERENCE, Z3_OP_SET_COMPLEMENT, Z3_OP_SET_SUBSET, Z3_OP_AS_ARRAY, Z3_OP_BNUM, Z3_OP_BIT1, Z3_OP_BIT0, Z3_OP_BNEG, Z3_OP_BADD, Z3_OP_BSUB, Z3_OP_BMUL, Z3_OP_BSDIV, Z3_OP_BUDIV, Z3_OP_BSREM, Z3_OP_BUREM, Z3_OP_BSMOD, Z3_OP_BSDIV0, Z3_OP_BUDIV0, Z3_OP_BSREM0, Z3_OP_BUREM0, Z3_OP_BSMOD0, Z3_OP_ULEQ, Z3_OP_SLEQ, Z3_OP_UGEQ, Z3_OP_SGEQ, Z3_OP_ULT, Z3_OP_SLT, Z3_OP_UGT, Z3_OP_SGT, Z3_OP_BAND, Z3_OP_BOR, Z3_OP_BNOT, Z3_OP_BXOR, Z3_OP_BNAND, Z3_OP_BNOR, Z3_OP_BXNOR, Z3_OP_CONCAT, Z3_OP_SIGN_EXT, Z3_OP_ZERO_EXT, Z3_OP_EXTRACT, Z3_OP_REPEAT, Z3_OP_BREDOR, Z3_OP_BREDAND, Z3_OP_BCOMP, Z3_OP_BSHL, Z3_OP_BLSHR, Z3_OP_BASHR, Z3_OP_ROTATE_LEFT, Z3_OP_ROTATE_RIGHT, Z3_OP_EXT_ROTATE_LEFT, Z3_OP_EXT_ROTATE_RIGHT, Z3_OP_INT2BV, Z3_OP_BV2INT, Z3_OP_CARRY, Z3_OP_XOR3, Z3_OP_PR_UNDEF, Z3_OP_PR_TRUE, Z3_OP_PR_ASSERTED, Z3_OP_PR_GOAL, Z3_OP_PR_MODUS_PONENS, Z3_OP_PR_REFLEXIVITY, Z3_OP_PR_SYMMETRY, Z3_OP_PR_TRANSITIVITY, Z3_OP_PR_TRANSITIVITY_STAR, Z3_OP_PR_MONOTONICITY, Z3_OP_PR_QUANT_INTRO, Z3_OP_PR_DISTRIBUTIVITY, Z3_OP_PR_AND_ELIM, Z3_OP_PR_NOT_OR_ELIM, Z3_OP_PR_REWRITE, Z3_OP_PR_REWRITE_STAR, Z3_OP_PR_PULL_QUANT, Z3_OP_PR_PULL_QUANT_STAR, Z3_OP_PR_PUSH_QUANT, Z3_OP_PR_ELIM_UNUSED_VARS, Z3_OP_PR_DER, Z3_OP_PR_QUANT_INST, Z3_OP_PR_HYPOTHESIS, Z3_OP_PR_LEMMA, Z3_OP_PR_UNIT_RESOLUTION, Z3_OP_PR_IFF_TRUE, Z3_OP_PR_IFF_FALSE, Z3_OP_PR_COMMUTATIVITY, Z3_OP_PR_DEF_AXIOM, Z3_OP_PR_DEF_INTRO, Z3_OP_PR_APPLY_DEF, Z3_OP_PR_IFF_OEQ, Z3_OP_PR_NNF_POS, Z3_OP_PR_NNF_NEG, Z3_OP_PR_NNF_STAR, Z3_OP_PR_CNF_STAR, Z3_OP_PR_SKOLEMIZE, Z3_OP_PR_MODUS_PONENS_OEQ, Z3_OP_PR_TH_LEMMA, Z3_OP_PR_HYPER_RESOLVE, Z3_OP_RA_STORE, Z3_OP_RA_EMPTY, Z3_OP_RA_IS_EMPTY, Z3_OP_RA_JOIN, Z3_OP_RA_UNION, Z3_OP_RA_WIDEN, Z3_OP_RA_PROJECT, Z3_OP_RA_FILTER, Z3_OP_RA_NEGATION_FILTER, Z3_OP_RA_RENAME, Z3_OP_RA_COMPLEMENT, Z3_OP_RA_SELECT, Z3_OP_RA_CLONE, Z3_OP_FD_LT, Z3_OP_LABEL, Z3_OP_LABEL_LIT, Z3_OP_DT_CONSTRUCTOR, Z3_OP_DT_RECOGNISER, Z3_OP_DT_ACCESSOR, Z3_OP_UNINTERPRETED, }; void camlidl_ml2c_z3_Z3_decl_kind(value _v1, Z3_decl_kind * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3_enum_6[Int_val(_v1)]; } value camlidl_c2ml_z3_Z3_decl_kind(Z3_decl_kind * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3_enum_6, 152, "typedef Z3_decl_kind: bad enum value"); return _v1; } int camlidl_transl_table_z3_enum_7[7] = { Z3_PK_UINT, Z3_PK_BOOL, Z3_PK_DOUBLE, Z3_PK_SYMBOL, Z3_PK_STRING, Z3_PK_OTHER, Z3_PK_INVALID, }; void camlidl_ml2c_z3_Z3_param_kind(value _v1, Z3_param_kind * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3_enum_7[Int_val(_v1)]; } value camlidl_c2ml_z3_Z3_param_kind(Z3_param_kind * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3_enum_7, 7, "typedef Z3_param_kind: bad enum value"); return _v1; } int camlidl_transl_table_z3_enum_8[4] = { Z3_PRINT_SMTLIB_FULL, Z3_PRINT_LOW_LEVEL, Z3_PRINT_SMTLIB_COMPLIANT, Z3_PRINT_SMTLIB2_COMPLIANT, }; void camlidl_ml2c_z3_Z3_ast_print_mode(value _v1, Z3_ast_print_mode * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3_enum_8[Int_val(_v1)]; } value camlidl_c2ml_z3_Z3_ast_print_mode(Z3_ast_print_mode * _c2, camlidl_ctx _ctx) { value _v1; switch((*_c2)) { case Z3_PRINT_SMTLIB_FULL: _v1 = Val_int(0); break; case Z3_PRINT_LOW_LEVEL: _v1 = Val_int(1); break; case Z3_PRINT_SMTLIB_COMPLIANT: _v1 = Val_int(2); break; case Z3_PRINT_SMTLIB2_COMPLIANT: _v1 = Val_int(3); break; default: invalid_argument("typedef Z3_ast_print_mode: bad enum value"); } return _v1; } int camlidl_transl_table_z3_enum_9[13] = { Z3_OK, Z3_SORT_ERROR, Z3_IOB, Z3_INVALID_ARG, Z3_PARSER_ERROR, Z3_NO_PARSER, Z3_INVALID_PATTERN, Z3_MEMOUT_FAIL, Z3_FILE_ACCESS_ERROR, Z3_INTERNAL_FATAL, Z3_INVALID_USAGE, Z3_DEC_REF_ERROR, Z3_EXCEPTION, }; void camlidl_ml2c_z3_Z3_error_code(value _v1, Z3_error_code * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3_enum_9[Int_val(_v1)]; } value camlidl_c2ml_z3_Z3_error_code(Z3_error_code * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3_enum_9, 13, "typedef Z3_error_code: bad enum value"); return _v1; } value camlidl_c2ml_z3_Z3_error_code(Z3_error_code * _c2, camlidl_ctx _ctx); void check_error_code (Z3_context c) { static struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; value* exn_tag = NULL; value ctx_err[2]; Z3_error_code e; e = Z3_get_error_code(c); if (e != Z3_OK) { ctx_err[0] = c2ml_Z3_context(&c); ctx_err[1] = camlidl_c2ml_z3_Z3_error_code(&e, &_ctxs); exn_tag = caml_named_value("Z3.Error"); if (*exn_tag == 0) { fprintf(stderr, "Z3.Error not found"); exit(1); } caml_raise_with_args(*exn_tag, 2, ctx_err); } } void* error_handler_static = NULL; int camlidl_transl_table_z3_enum_10[4] = { Z3_GOAL_PRECISE, Z3_GOAL_UNDER, Z3_GOAL_OVER, Z3_GOAL_UNDER_OVER, }; void camlidl_ml2c_z3_Z3_goal_prec(value _v1, Z3_goal_prec * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3_enum_10[Int_val(_v1)]; } value camlidl_c2ml_z3_Z3_goal_prec(Z3_goal_prec * _c2, camlidl_ctx _ctx) { value _v1; switch((*_c2)) { case Z3_GOAL_PRECISE: _v1 = Val_int(0); break; case Z3_GOAL_UNDER: _v1 = Val_int(1); break; case Z3_GOAL_OVER: _v1 = Val_int(2); break; case Z3_GOAL_UNDER_OVER: _v1 = Val_int(3); break; default: invalid_argument("typedef Z3_goal_prec: bad enum value"); } return _v1; } value caml_z3_mk_context(value key_val_list) { CAMLparam1( key_val_list ); CAMLlocal4( item, vkey, vval, _vres ); char * ckey; char * cval; Z3_config cfg; Z3_context _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; cfg = Z3_mk_config(); while (key_val_list != Val_emptylist) { item = Field(key_val_list, 0); vkey = Field(item, 0); vval = Field(item, 1); ckey = camlidl_malloc_string(vkey, _ctx); cval = camlidl_malloc_string(vval, _ctx); Z3_set_param_value(cfg, ckey, cval); key_val_list = Field(key_val_list, 1); } _res = Z3_mk_context_rc(cfg); Z3_del_config(cfg); _vres = camlidl_c2ml_z3_Z3_context(&_res, _ctx); camlidl_free(_ctx); Z3_set_error_handler(_res, error_handler_static); CAMLreturn(_vres); } value camlidl_z3_Z3_update_param_value( value _v_c, value _v_param_id, value _v_param_value) { Z3_context c; /*in*/ Z3_string param_id; /*in*/ Z3_string param_value; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_param_id, ¶m_id, _ctx); camlidl_ml2c_z3_Z3_string(_v_param_value, ¶m_value, _ctx); Z3_update_param_value(c, param_id, param_value); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_get_param_value( value _v_c, value _v_param_id) { Z3_context c; /*in*/ Z3_string param_id; /*in*/ Z3_string *param_value; /*out*/ Z3_string _c1; value _v2; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_param_id, ¶m_id, _ctx); param_value = &_c1; Z3_get_param_value(c, param_id, param_value); if (param_value == NULL) { _vres = Val_int(0); } else { _v2 = camlidl_c2ml_z3_Z3_string(&*param_value, _ctx); Begin_root(_v2) _vres = camlidl_alloc_small(1, 0); Field(_vres, 0) = _v2; End_roots(); } camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_interrupt( value _v_c) { Z3_context c; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); Z3_interrupt(c); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_mk_params( value _v_c) { Z3_context c; /*in*/ Z3_params _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_mk_params(c); _vres = camlidl_c2ml_z3_Z3_params(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_params_set_bool( value _v_c, value _v_p, value _v_k, value _v_v) { Z3_context c; /*in*/ Z3_params p; /*in*/ Z3_symbol k; /*in*/ int v; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_k, &k, _ctx); v = Int_val(_v_v); Z3_params_set_bool(c, p, k, v); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_params_set_uint( value _v_c, value _v_p, value _v_k, value _v_v) { Z3_context c; /*in*/ Z3_params p; /*in*/ Z3_symbol k; /*in*/ unsigned int v; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_k, &k, _ctx); v = Int_val(_v_v); Z3_params_set_uint(c, p, k, v); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_params_set_double( value _v_c, value _v_p, value _v_k, value _v_v) { Z3_context c; /*in*/ Z3_params p; /*in*/ Z3_symbol k; /*in*/ double v; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_k, &k, _ctx); v = Double_val(_v_v); Z3_params_set_double(c, p, k, v); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_params_set_symbol( value _v_c, value _v_p, value _v_k, value _v_v) { Z3_context c; /*in*/ Z3_params p; /*in*/ Z3_symbol k; /*in*/ Z3_symbol v; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_k, &k, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_v, &v, _ctx); Z3_params_set_symbol(c, p, k, v); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_params_to_string( value _v_c, value _v_p) { Z3_context c; /*in*/ Z3_params p; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); _res = Z3_params_to_string(c, p); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_params_validate( value _v_c, value _v_p, value _v_d) { Z3_context c; /*in*/ Z3_params p; /*in*/ Z3_param_descrs d; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); camlidl_ml2c_z3_Z3_param_descrs(_v_d, &d, _ctx); Z3_params_validate(c, p, d); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_param_descrs_get_kind( value _v_c, value _v_p, value _v_n) { Z3_context c; /*in*/ Z3_param_descrs p; /*in*/ Z3_symbol n; /*in*/ Z3_param_kind _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_param_descrs(_v_p, &p, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_n, &n, _ctx); _res = Z3_param_descrs_get_kind(c, p, n); _vres = camlidl_c2ml_z3_Z3_param_kind(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_param_descrs_size( value _v_c, value _v_p) { Z3_context c; /*in*/ Z3_param_descrs p; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_param_descrs(_v_p, &p, _ctx); _res = Z3_param_descrs_size(c, p); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_param_descrs_get_name( value _v_c, value _v_p, value _v_i) { Z3_context c; /*in*/ Z3_param_descrs p; /*in*/ unsigned int i; /*in*/ Z3_symbol _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_param_descrs(_v_p, &p, _ctx); i = Int_val(_v_i); _res = Z3_param_descrs_get_name(c, p, i); _vres = camlidl_c2ml_z3_Z3_symbol(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_param_descrs_to_string( value _v_c, value _v_p) { Z3_context c; /*in*/ Z3_param_descrs p; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_param_descrs(_v_p, &p, _ctx); _res = Z3_param_descrs_to_string(c, p); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_int_symbol( value _v_c, value _v_i) { Z3_context c; /*in*/ int i; /*in*/ Z3_symbol _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); _res = Z3_mk_int_symbol(c, i); _vres = camlidl_c2ml_z3_Z3_symbol(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_string_symbol( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_string s; /*in*/ Z3_symbol _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_s, &s, _ctx); _res = Z3_mk_string_symbol(c, s); _vres = camlidl_c2ml_z3_Z3_symbol(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_uninterpreted_sort( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_symbol s; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_s, &s, _ctx); _res = Z3_mk_uninterpreted_sort(c, s); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bool_sort( value _v_c) { Z3_context c; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_mk_bool_sort(c); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_int_sort( value _v_c) { Z3_context c; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_mk_int_sort(c); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_real_sort( value _v_c) { Z3_context c; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_mk_real_sort(c); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bv_sort( value _v_c, value _v_sz) { Z3_context c; /*in*/ unsigned int sz; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); sz = Int_val(_v_sz); _res = Z3_mk_bv_sort(c, sz); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_finite_domain_sort( value _v_c, value _v_name, value _v_size) { Z3_context c; /*in*/ Z3_symbol name; /*in*/ __int64 size; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_name, &name, _ctx); size = Int64_val(_v_size); _res = Z3_mk_finite_domain_sort(c, name, size); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_array_sort( value _v_c, value _v_domain, value _v_range) { Z3_context c; /*in*/ Z3_sort domain; /*in*/ Z3_sort range; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_domain, &domain, _ctx); camlidl_ml2c_z3_Z3_sort(_v_range, &range, _ctx); _res = Z3_mk_array_sort(c, domain, range); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_tuple_sort( value _v_c, value _v_mk_tuple_name, value _v_field_names, value _v_field_sorts) { Z3_context c; /*in*/ Z3_symbol mk_tuple_name; /*in*/ unsigned int num_fields; /*in*/ Z3_symbol const *field_names; /*in*/ Z3_sort const *field_sorts; /*in*/ Z3_func_decl *mk_tuple_decl; /*out*/ Z3_func_decl *proj_decl; /*out*/ Z3_sort _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; Z3_func_decl _c7; mlsize_t _c8; value _v9; value _vresult; value _vres[3] = { 0, 0, 0, }; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_mk_tuple_name, &mk_tuple_name, _ctx); _c1 = Wosize_val(_v_field_names); field_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_field_names, _c2); camlidl_ml2c_z3_Z3_symbol(_v3, &field_names[_c2], _ctx); } num_fields = _c1; _c4 = Wosize_val(_v_field_sorts); field_sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_field_sorts, _c5); camlidl_ml2c_z3_Z3_sort(_v6, &field_sorts[_c5], _ctx); } num_fields = _c4; mk_tuple_decl = &_c7; proj_decl = camlidl_malloc(num_fields * sizeof(Z3_func_decl ), _ctx); _res = Z3_mk_tuple_sort(c, mk_tuple_name, num_fields, field_names, field_sorts, mk_tuple_decl, proj_decl); Begin_roots_block(_vres, 3) _vres[0] = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); _vres[1] = camlidl_c2ml_z3_Z3_func_decl(&*mk_tuple_decl, _ctx); _vres[2] = camlidl_alloc(num_fields, 0); Begin_root(_vres[2]) for (_c8 = 0; _c8 < num_fields; _c8++) { _v9 = camlidl_c2ml_z3_Z3_func_decl(&proj_decl[_c8], _ctx); modify(&Field(_vres[2], _c8), _v9); } End_roots() _vresult = camlidl_alloc_small(3, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; Field(_vresult, 2) = _vres[2]; End_roots() camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vresult; } value camlidl_z3_Z3_mk_enumeration_sort( value _v_c, value _v_name, value _v_enum_names) { Z3_context c; /*in*/ Z3_symbol name; /*in*/ unsigned int n; /*in*/ Z3_symbol const *enum_names; /*in*/ Z3_func_decl *enum_consts; /*out*/ Z3_func_decl *enum_testers; /*out*/ Z3_sort _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; value _v5; mlsize_t _c6; value _v7; value _vresult; value _vres[3] = { 0, 0, 0, }; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_name, &name, _ctx); _c1 = Wosize_val(_v_enum_names); enum_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_enum_names, _c2); camlidl_ml2c_z3_Z3_symbol(_v3, &enum_names[_c2], _ctx); } n = _c1; enum_consts = camlidl_malloc(n * sizeof(Z3_func_decl ), _ctx); enum_testers = camlidl_malloc(n * sizeof(Z3_func_decl ), _ctx); _res = Z3_mk_enumeration_sort(c, name, n, enum_names, enum_consts, enum_testers); Begin_roots_block(_vres, 3) _vres[0] = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); _vres[1] = camlidl_alloc(n, 0); Begin_root(_vres[1]) for (_c4 = 0; _c4 < n; _c4++) { _v5 = camlidl_c2ml_z3_Z3_func_decl(&enum_consts[_c4], _ctx); modify(&Field(_vres[1], _c4), _v5); } End_roots() _vres[2] = camlidl_alloc(n, 0); Begin_root(_vres[2]) for (_c6 = 0; _c6 < n; _c6++) { _v7 = camlidl_c2ml_z3_Z3_func_decl(&enum_testers[_c6], _ctx); modify(&Field(_vres[2], _c6), _v7); } End_roots() _vresult = camlidl_alloc_small(3, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; Field(_vresult, 2) = _vres[2]; End_roots() camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vresult; } value camlidl_z3_Z3_mk_list_sort( value _v_c, value _v_name, value _v_elem_sort) { Z3_context c; /*in*/ Z3_symbol name; /*in*/ Z3_sort elem_sort; /*in*/ Z3_func_decl *nil_decl; /*out*/ Z3_func_decl *is_nil_decl; /*out*/ Z3_func_decl *cons_decl; /*out*/ Z3_func_decl *is_cons_decl; /*out*/ Z3_func_decl *head_decl; /*out*/ Z3_func_decl *tail_decl; /*out*/ Z3_sort _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; Z3_func_decl _c1; Z3_func_decl _c2; Z3_func_decl _c3; Z3_func_decl _c4; Z3_func_decl _c5; Z3_func_decl _c6; value _vresult; value _vres[7] = { 0, 0, 0, 0, 0, 0, 0, }; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_name, &name, _ctx); camlidl_ml2c_z3_Z3_sort(_v_elem_sort, &elem_sort, _ctx); nil_decl = &_c1; is_nil_decl = &_c2; cons_decl = &_c3; is_cons_decl = &_c4; head_decl = &_c5; tail_decl = &_c6; _res = Z3_mk_list_sort(c, name, elem_sort, nil_decl, is_nil_decl, cons_decl, is_cons_decl, head_decl, tail_decl); Begin_roots_block(_vres, 7) _vres[0] = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); _vres[1] = camlidl_c2ml_z3_Z3_func_decl(&*nil_decl, _ctx); _vres[2] = camlidl_c2ml_z3_Z3_func_decl(&*is_nil_decl, _ctx); _vres[3] = camlidl_c2ml_z3_Z3_func_decl(&*cons_decl, _ctx); _vres[4] = camlidl_c2ml_z3_Z3_func_decl(&*is_cons_decl, _ctx); _vres[5] = camlidl_c2ml_z3_Z3_func_decl(&*head_decl, _ctx); _vres[6] = camlidl_c2ml_z3_Z3_func_decl(&*tail_decl, _ctx); _vresult = camlidl_alloc_small(7, 0); { mlsize_t _c7; for (_c7 = 0; _c7 < 7; _c7++) Field(_vresult, _c7) = _vres[_c7]; } End_roots() camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vresult; } value camlidl_z3_Z3_mk_constructor( value _v_c, value _v_name, value _v_recognizer, value _v_field_names, value _v_sorts, value _v_sort_refs) { Z3_context c; /*in*/ Z3_symbol name; /*in*/ Z3_symbol recognizer; /*in*/ unsigned int num_fields; /*in*/ Z3_symbol const *field_names; /*in*/ Z3_sort_opt const *sorts; /*in*/ unsigned int *sort_refs; /*in*/ Z3_constructor _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_name, &name, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_recognizer, &recognizer, _ctx); _c1 = Wosize_val(_v_field_names); field_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_field_names, _c2); camlidl_ml2c_z3_Z3_symbol(_v3, &field_names[_c2], _ctx); } num_fields = _c1; _c4 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c4 * sizeof(Z3_sort_opt const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_sorts, _c5); camlidl_ml2c_z3_Z3_sort_opt(_v6, &sorts[_c5], _ctx); } num_fields = _c4; _c7 = Wosize_val(_v_sort_refs); sort_refs = camlidl_malloc(_c7 * sizeof(unsigned int ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_sort_refs, _c8); sort_refs[_c8] = Int_val(_v9); } num_fields = _c7; _res = Z3_mk_constructor(c, name, recognizer, num_fields, field_names, sorts, sort_refs); _vres = camlidl_c2ml_z3_Z3_constructor(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_constructor_bytecode(value * argv, int argn) { return camlidl_z3_Z3_mk_constructor(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } value camlidl_z3_Z3_del_constructor( value _v_c, value _v_constr) { Z3_context c; /*in*/ Z3_constructor constr; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_constructor(_v_constr, &constr, _ctx); Z3_del_constructor(c, constr); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_mk_datatype( value _v_c, value _v_name, value _v_constructors) { Z3_context c; /*in*/ Z3_symbol name; /*in*/ unsigned int num_constructors; /*in*/ Z3_constructor *constructors; /*in,out*/ Z3_sort _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; value _v5; value _vresult; value _vres[2] = { 0, 0, }; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_name, &name, _ctx); _c1 = Wosize_val(_v_constructors); constructors = camlidl_malloc(_c1 * sizeof(Z3_constructor ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_constructors, _c2); camlidl_ml2c_z3_Z3_constructor(_v3, &constructors[_c2], _ctx); } num_constructors = _c1; _res = Z3_mk_datatype(c, name, num_constructors, constructors); Begin_roots_block(_vres, 2) _vres[0] = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); _vres[1] = camlidl_alloc(num_constructors, 0); Begin_root(_vres[1]) for (_c4 = 0; _c4 < num_constructors; _c4++) { _v5 = camlidl_c2ml_z3_Z3_constructor(&constructors[_c4], _ctx); modify(&Field(_vres[1], _c4), _v5); } End_roots() _vresult = camlidl_alloc_small(2, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; End_roots() camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vresult; } value camlidl_z3_Z3_mk_constructor_list( value _v_c, value _v_constructors) { Z3_context c; /*in*/ unsigned int num_constructors; /*in*/ Z3_constructor const *constructors; /*in*/ Z3_constructor_list _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_constructors); constructors = camlidl_malloc(_c1 * sizeof(Z3_constructor const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_constructors, _c2); camlidl_ml2c_z3_Z3_constructor(_v3, &constructors[_c2], _ctx); } num_constructors = _c1; _res = Z3_mk_constructor_list(c, num_constructors, constructors); _vres = camlidl_c2ml_z3_Z3_constructor_list(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_del_constructor_list( value _v_c, value _v_clist) { Z3_context c; /*in*/ Z3_constructor_list clist; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_constructor_list(_v_clist, &clist, _ctx); Z3_del_constructor_list(c, clist); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_mk_datatypes( value _v_c, value _v_sort_names, value _v_constructor_lists) { Z3_context c; /*in*/ unsigned int num_sorts; /*in*/ Z3_symbol const *sort_names; /*in*/ Z3_sort *sorts; /*out*/ Z3_constructor_list *constructor_lists; /*in,out*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; value _v8; mlsize_t _c9; value _v10; value _vresult; value _vres[2] = { 0, 0, }; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_sort_names); sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_sort_names, _c2); camlidl_ml2c_z3_Z3_symbol(_v3, &sort_names[_c2], _ctx); } num_sorts = _c1; _c4 = Wosize_val(_v_constructor_lists); constructor_lists = camlidl_malloc(_c4 * sizeof(Z3_constructor_list ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_constructor_lists, _c5); camlidl_ml2c_z3_Z3_constructor_list(_v6, &constructor_lists[_c5], _ctx); } num_sorts = _c4; sorts = camlidl_malloc(num_sorts * sizeof(Z3_sort ), _ctx); Z3_mk_datatypes(c, num_sorts, sort_names, sorts, constructor_lists); Begin_roots_block(_vres, 2) _vres[0] = camlidl_alloc(num_sorts, 0); Begin_root(_vres[0]) for (_c7 = 0; _c7 < num_sorts; _c7++) { _v8 = camlidl_c2ml_z3_Z3_sort(&sorts[_c7], _ctx); modify(&Field(_vres[0], _c7), _v8); } End_roots() _vres[1] = camlidl_alloc(num_sorts, 0); Begin_root(_vres[1]) for (_c9 = 0; _c9 < num_sorts; _c9++) { _v10 = camlidl_c2ml_z3_Z3_constructor_list(&constructor_lists[_c9], _ctx); modify(&Field(_vres[1], _c9), _v10); } End_roots() _vresult = camlidl_alloc_small(2, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; End_roots() camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vresult; } value camlidl_z3_Z3_query_constructor( value _v_c, value _v_constr, value _v_num_fields) { Z3_context c; /*in*/ Z3_constructor constr; /*in*/ unsigned int num_fields; /*in*/ Z3_func_decl *constructor; /*out*/ Z3_func_decl *tester; /*out*/ Z3_func_decl *accessors; /*out*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; Z3_func_decl _c1; Z3_func_decl _c2; mlsize_t _c3; value _v4; value _vresult; value _vres[3] = { 0, 0, 0, }; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_constructor(_v_constr, &constr, _ctx); num_fields = Int_val(_v_num_fields); constructor = &_c1; tester = &_c2; accessors = camlidl_malloc(num_fields * sizeof(Z3_func_decl ), _ctx); Z3_query_constructor(c, constr, num_fields, constructor, tester, accessors); Begin_roots_block(_vres, 3) _vres[0] = camlidl_c2ml_z3_Z3_func_decl(&*constructor, _ctx); _vres[1] = camlidl_c2ml_z3_Z3_func_decl(&*tester, _ctx); _vres[2] = camlidl_alloc(num_fields, 0); Begin_root(_vres[2]) for (_c3 = 0; _c3 < num_fields; _c3++) { _v4 = camlidl_c2ml_z3_Z3_func_decl(&accessors[_c3], _ctx); modify(&Field(_vres[2], _c3), _v4); } End_roots() _vresult = camlidl_alloc_small(3, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; Field(_vresult, 2) = _vres[2]; End_roots() camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vresult; } value camlidl_z3_Z3_mk_func_decl( value _v_c, value _v_s, value _v_domain, value _v_range) { Z3_context c; /*in*/ Z3_symbol s; /*in*/ unsigned int domain_size; /*in*/ Z3_sort const *domain; /*in*/ Z3_sort range; /*in*/ Z3_func_decl _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_s, &s, _ctx); _c1 = Wosize_val(_v_domain); domain = camlidl_malloc(_c1 * sizeof(Z3_sort const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_domain, _c2); camlidl_ml2c_z3_Z3_sort(_v3, &domain[_c2], _ctx); } domain_size = _c1; camlidl_ml2c_z3_Z3_sort(_v_range, &range, _ctx); _res = Z3_mk_func_decl(c, s, domain_size, domain, range); _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_app( value _v_c, value _v_d, value _v_args) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_app(c, d, num_args, args); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_const( value _v_c, value _v_s, value _v_ty) { Z3_context c; /*in*/ Z3_symbol s; /*in*/ Z3_sort ty; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_s, &s, _ctx); camlidl_ml2c_z3_Z3_sort(_v_ty, &ty, _ctx); _res = Z3_mk_const(c, s, ty); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_fresh_func_decl( value _v_c, value _v_prefix, value _v_domain, value _v_range) { Z3_context c; /*in*/ Z3_string prefix; /*in*/ unsigned int domain_size; /*in*/ Z3_sort const *domain; /*in*/ Z3_sort range; /*in*/ Z3_func_decl _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_prefix, &prefix, _ctx); _c1 = Wosize_val(_v_domain); domain = camlidl_malloc(_c1 * sizeof(Z3_sort const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_domain, _c2); camlidl_ml2c_z3_Z3_sort(_v3, &domain[_c2], _ctx); } domain_size = _c1; camlidl_ml2c_z3_Z3_sort(_v_range, &range, _ctx); _res = Z3_mk_fresh_func_decl(c, prefix, domain_size, domain, range); _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_fresh_const( value _v_c, value _v_prefix, value _v_ty) { Z3_context c; /*in*/ Z3_string prefix; /*in*/ Z3_sort ty; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_prefix, &prefix, _ctx); camlidl_ml2c_z3_Z3_sort(_v_ty, &ty, _ctx); _res = Z3_mk_fresh_const(c, prefix, ty); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_true( value _v_c) { Z3_context c; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_mk_true(c); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_false( value _v_c) { Z3_context c; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_mk_false(c); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_eq( value _v_c, value _v_l, value _v_r) { Z3_context c; /*in*/ Z3_ast l; /*in*/ Z3_ast r; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_l, &l, _ctx); camlidl_ml2c_z3_Z3_ast(_v_r, &r, _ctx); _res = Z3_mk_eq(c, l, r); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_distinct( value _v_c, value _v_args) { Z3_context c; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_distinct(c, num_args, args); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_not( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_mk_not(c, a); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_ite( value _v_c, value _v_t1, value _v_t2, value _v_t3) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast t3; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t3, &t3, _ctx); _res = Z3_mk_ite(c, t1, t2, t3); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_iff( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_iff(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_implies( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_implies(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_xor( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_xor(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_and( value _v_c, value _v_args) { Z3_context c; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_and(c, num_args, args); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_or( value _v_c, value _v_args) { Z3_context c; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_or(c, num_args, args); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_add( value _v_c, value _v_args) { Z3_context c; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_add(c, num_args, args); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_mul( value _v_c, value _v_args) { Z3_context c; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_mul(c, num_args, args); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_sub( value _v_c, value _v_args) { Z3_context c; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_sub(c, num_args, args); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_unary_minus( value _v_c, value _v_arg) { Z3_context c; /*in*/ Z3_ast arg; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_arg, &arg, _ctx); _res = Z3_mk_unary_minus(c, arg); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_div( value _v_c, value _v_arg1, value _v_arg2) { Z3_context c; /*in*/ Z3_ast arg1; /*in*/ Z3_ast arg2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_arg1, &arg1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_arg2, &arg2, _ctx); _res = Z3_mk_div(c, arg1, arg2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_mod( value _v_c, value _v_arg1, value _v_arg2) { Z3_context c; /*in*/ Z3_ast arg1; /*in*/ Z3_ast arg2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_arg1, &arg1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_arg2, &arg2, _ctx); _res = Z3_mk_mod(c, arg1, arg2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_rem( value _v_c, value _v_arg1, value _v_arg2) { Z3_context c; /*in*/ Z3_ast arg1; /*in*/ Z3_ast arg2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_arg1, &arg1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_arg2, &arg2, _ctx); _res = Z3_mk_rem(c, arg1, arg2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_power( value _v_c, value _v_arg1, value _v_arg2) { Z3_context c; /*in*/ Z3_ast arg1; /*in*/ Z3_ast arg2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_arg1, &arg1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_arg2, &arg2, _ctx); _res = Z3_mk_power(c, arg1, arg2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_lt( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_lt(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_le( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_le(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_gt( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_gt(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_ge( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_ge(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_int2real( value _v_c, value _v_t1) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_int2real(c, t1); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_real2int( value _v_c, value _v_t1) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_real2int(c, t1); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_is_int( value _v_c, value _v_t1) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_is_int(c, t1); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvnot( value _v_c, value _v_t1) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_bvnot(c, t1); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvredand( value _v_c, value _v_t1) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_bvredand(c, t1); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvredor( value _v_c, value _v_t1) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_bvredor(c, t1); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvand( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvand(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvor( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvor(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvxor( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvxor(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvnand( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvnand(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvnor( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvnor(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvxnor( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvxnor(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvneg( value _v_c, value _v_t1) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_bvneg(c, t1); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvadd( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvadd(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvsub( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsub(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvmul( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvmul(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvudiv( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvudiv(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvsdiv( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsdiv(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvurem( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvurem(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvsrem( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsrem(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvsmod( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsmod(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvult( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvult(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvslt( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvslt(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvule( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvule(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvsle( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsle(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvuge( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvuge(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvsge( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsge(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvugt( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvugt(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvsgt( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsgt(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_concat( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_concat(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_extract( value _v_c, value _v_high, value _v_low, value _v_t1) { Z3_context c; /*in*/ unsigned int high; /*in*/ unsigned int low; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); high = Int_val(_v_high); low = Int_val(_v_low); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_extract(c, high, low, t1); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_sign_ext( value _v_c, value _v_i, value _v_t1) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_sign_ext(c, i, t1); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_zero_ext( value _v_c, value _v_i, value _v_t1) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_zero_ext(c, i, t1); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_repeat( value _v_c, value _v_i, value _v_t1) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_repeat(c, i, t1); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvshl( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvshl(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvlshr( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvlshr(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvashr( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvashr(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_rotate_left( value _v_c, value _v_i, value _v_t1) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_rotate_left(c, i, t1); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_rotate_right( value _v_c, value _v_i, value _v_t1) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_rotate_right(c, i, t1); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_ext_rotate_left( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_ext_rotate_left(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_ext_rotate_right( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_ext_rotate_right(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_int2bv( value _v_c, value _v_n, value _v_t1) { Z3_context c; /*in*/ unsigned int n; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); n = Int_val(_v_n); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_int2bv(c, n, t1); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bv2int( value _v_c, value _v_t1, value _v_is_signed) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ int is_signed; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); is_signed = Int_val(_v_is_signed); _res = Z3_mk_bv2int(c, t1, is_signed); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvadd_no_overflow( value _v_c, value _v_t1, value _v_t2, value _v_is_signed) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ int is_signed; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); is_signed = Int_val(_v_is_signed); _res = Z3_mk_bvadd_no_overflow(c, t1, t2, is_signed); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvadd_no_underflow( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvadd_no_underflow(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvsub_no_overflow( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsub_no_overflow(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvsub_no_underflow( value _v_c, value _v_t1, value _v_t2, value _v_is_signed) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ int is_signed; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); is_signed = Int_val(_v_is_signed); _res = Z3_mk_bvsub_no_underflow(c, t1, t2, is_signed); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvsdiv_no_overflow( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsdiv_no_overflow(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvneg_no_overflow( value _v_c, value _v_t1) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_bvneg_no_overflow(c, t1); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvmul_no_overflow( value _v_c, value _v_t1, value _v_t2, value _v_is_signed) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ int is_signed; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); is_signed = Int_val(_v_is_signed); _res = Z3_mk_bvmul_no_overflow(c, t1, t2, is_signed); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bvmul_no_underflow( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvmul_no_underflow(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_select( value _v_c, value _v_a, value _v_i) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_ast i; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); camlidl_ml2c_z3_Z3_ast(_v_i, &i, _ctx); _res = Z3_mk_select(c, a, i); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_store( value _v_c, value _v_a, value _v_i, value _v_v) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_ast i; /*in*/ Z3_ast v; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); camlidl_ml2c_z3_Z3_ast(_v_i, &i, _ctx); camlidl_ml2c_z3_Z3_ast(_v_v, &v, _ctx); _res = Z3_mk_store(c, a, i, v); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_const_array( value _v_c, value _v_domain, value _v_v) { Z3_context c; /*in*/ Z3_sort domain; /*in*/ Z3_ast v; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_domain, &domain, _ctx); camlidl_ml2c_z3_Z3_ast(_v_v, &v, _ctx); _res = Z3_mk_const_array(c, domain, v); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_map( value _v_c, value _v_f, value _v_n, value _v_args) { Z3_context c; /*in*/ Z3_func_decl f; /*in*/ unsigned int n; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; Z3_ast _c1; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_f, &f, _ctx); n = Int_val(_v_n); args = &_c1; camlidl_ml2c_z3_Z3_ast(_v_args, &_c1, _ctx); _res = Z3_mk_map(c, f, n, args); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_array_default( value _v_c, value _v_array) { Z3_context c; /*in*/ Z3_ast array; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_array, &array, _ctx); _res = Z3_mk_array_default(c, array); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_set_sort( value _v_c, value _v_ty) { Z3_context c; /*in*/ Z3_sort ty; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_ty, &ty, _ctx); _res = Z3_mk_set_sort(c, ty); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_empty_set( value _v_c, value _v_domain) { Z3_context c; /*in*/ Z3_sort domain; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_domain, &domain, _ctx); _res = Z3_mk_empty_set(c, domain); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_full_set( value _v_c, value _v_domain) { Z3_context c; /*in*/ Z3_sort domain; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_domain, &domain, _ctx); _res = Z3_mk_full_set(c, domain); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_set_add( value _v_c, value _v_set, value _v_elem) { Z3_context c; /*in*/ Z3_ast set; /*in*/ Z3_ast elem; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_set, &set, _ctx); camlidl_ml2c_z3_Z3_ast(_v_elem, &elem, _ctx); _res = Z3_mk_set_add(c, set, elem); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_set_del( value _v_c, value _v_set, value _v_elem) { Z3_context c; /*in*/ Z3_ast set; /*in*/ Z3_ast elem; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_set, &set, _ctx); camlidl_ml2c_z3_Z3_ast(_v_elem, &elem, _ctx); _res = Z3_mk_set_del(c, set, elem); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_set_union( value _v_c, value _v_args) { Z3_context c; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_set_union(c, num_args, args); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_set_intersect( value _v_c, value _v_args) { Z3_context c; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_set_intersect(c, num_args, args); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_set_difference( value _v_c, value _v_arg1, value _v_arg2) { Z3_context c; /*in*/ Z3_ast arg1; /*in*/ Z3_ast arg2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_arg1, &arg1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_arg2, &arg2, _ctx); _res = Z3_mk_set_difference(c, arg1, arg2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_set_complement( value _v_c, value _v_arg) { Z3_context c; /*in*/ Z3_ast arg; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_arg, &arg, _ctx); _res = Z3_mk_set_complement(c, arg); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_set_member( value _v_c, value _v_elem, value _v_set) { Z3_context c; /*in*/ Z3_ast elem; /*in*/ Z3_ast set; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_elem, &elem, _ctx); camlidl_ml2c_z3_Z3_ast(_v_set, &set, _ctx); _res = Z3_mk_set_member(c, elem, set); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_set_subset( value _v_c, value _v_arg1, value _v_arg2) { Z3_context c; /*in*/ Z3_ast arg1; /*in*/ Z3_ast arg2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_arg1, &arg1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_arg2, &arg2, _ctx); _res = Z3_mk_set_subset(c, arg1, arg2); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_numeral( value _v_c, value _v_numeral, value _v_ty) { Z3_context c; /*in*/ Z3_string numeral; /*in*/ Z3_sort ty; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_numeral, &numeral, _ctx); camlidl_ml2c_z3_Z3_sort(_v_ty, &ty, _ctx); _res = Z3_mk_numeral(c, numeral, ty); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_real( value _v_c, value _v_num, value _v_den) { Z3_context c; /*in*/ int num; /*in*/ int den; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); num = Int_val(_v_num); den = Int_val(_v_den); _res = Z3_mk_real(c, num, den); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_int( value _v_c, value _v_v, value _v_ty) { Z3_context c; /*in*/ int v; /*in*/ Z3_sort ty; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); v = Int_val(_v_v); camlidl_ml2c_z3_Z3_sort(_v_ty, &ty, _ctx); _res = Z3_mk_int(c, v, ty); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_int64( value _v_c, value _v_v, value _v_ty) { Z3_context c; /*in*/ __int64 v; /*in*/ Z3_sort ty; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); v = Int64_val(_v_v); camlidl_ml2c_z3_Z3_sort(_v_ty, &ty, _ctx); _res = Z3_mk_int64(c, v, ty); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_pattern( value _v_c, value _v_terms) { Z3_context c; /*in*/ unsigned int num_patterns; /*in*/ Z3_ast const *terms; /*in*/ Z3_pattern _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_terms); terms = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_terms, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &terms[_c2], _ctx); } num_patterns = _c1; _res = Z3_mk_pattern(c, num_patterns, terms); _vres = camlidl_c2ml_z3_Z3_pattern(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_bound( value _v_c, value _v_index, value _v_ty) { Z3_context c; /*in*/ unsigned int index; /*in*/ Z3_sort ty; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); index = Int_val(_v_index); camlidl_ml2c_z3_Z3_sort(_v_ty, &ty, _ctx); _res = Z3_mk_bound(c, index, ty); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_forall( value _v_c, value _v_weight, value _v_patterns, value _v_sorts, value _v_decl_names, value _v_body) { Z3_context c; /*in*/ unsigned int weight; /*in*/ unsigned int num_patterns; /*in*/ Z3_pattern const *patterns; /*in*/ unsigned int num_decls; /*in*/ Z3_sort const *sorts; /*in*/ Z3_symbol const *decl_names; /*in*/ Z3_ast body; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); weight = Int_val(_v_weight); _c1 = Wosize_val(_v_patterns); patterns = camlidl_malloc(_c1 * sizeof(Z3_pattern const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_patterns, _c2); camlidl_ml2c_z3_Z3_pattern(_v3, &patterns[_c2], _ctx); } num_patterns = _c1; _c4 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_sorts, _c5); camlidl_ml2c_z3_Z3_sort(_v6, &sorts[_c5], _ctx); } num_decls = _c4; _c7 = Wosize_val(_v_decl_names); decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_decl_names, _c8); camlidl_ml2c_z3_Z3_symbol(_v9, &decl_names[_c8], _ctx); } num_decls = _c7; camlidl_ml2c_z3_Z3_ast(_v_body, &body, _ctx); _res = Z3_mk_forall(c, weight, num_patterns, patterns, num_decls, sorts, decl_names, body); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_forall_bytecode(value * argv, int argn) { return camlidl_z3_Z3_mk_forall(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } value camlidl_z3_Z3_mk_exists( value _v_c, value _v_weight, value _v_patterns, value _v_sorts, value _v_decl_names, value _v_body) { Z3_context c; /*in*/ unsigned int weight; /*in*/ unsigned int num_patterns; /*in*/ Z3_pattern const *patterns; /*in*/ unsigned int num_decls; /*in*/ Z3_sort const *sorts; /*in*/ Z3_symbol const *decl_names; /*in*/ Z3_ast body; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); weight = Int_val(_v_weight); _c1 = Wosize_val(_v_patterns); patterns = camlidl_malloc(_c1 * sizeof(Z3_pattern const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_patterns, _c2); camlidl_ml2c_z3_Z3_pattern(_v3, &patterns[_c2], _ctx); } num_patterns = _c1; _c4 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_sorts, _c5); camlidl_ml2c_z3_Z3_sort(_v6, &sorts[_c5], _ctx); } num_decls = _c4; _c7 = Wosize_val(_v_decl_names); decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_decl_names, _c8); camlidl_ml2c_z3_Z3_symbol(_v9, &decl_names[_c8], _ctx); } num_decls = _c7; camlidl_ml2c_z3_Z3_ast(_v_body, &body, _ctx); _res = Z3_mk_exists(c, weight, num_patterns, patterns, num_decls, sorts, decl_names, body); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_exists_bytecode(value * argv, int argn) { return camlidl_z3_Z3_mk_exists(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } value camlidl_z3_Z3_mk_quantifier( value _v_c, value _v_is_forall, value _v_weight, value _v_patterns, value _v_sorts, value _v_decl_names, value _v_body) { Z3_context c; /*in*/ int is_forall; /*in*/ unsigned int weight; /*in*/ unsigned int num_patterns; /*in*/ Z3_pattern const *patterns; /*in*/ unsigned int num_decls; /*in*/ Z3_sort const *sorts; /*in*/ Z3_symbol const *decl_names; /*in*/ Z3_ast body; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); is_forall = Int_val(_v_is_forall); weight = Int_val(_v_weight); _c1 = Wosize_val(_v_patterns); patterns = camlidl_malloc(_c1 * sizeof(Z3_pattern const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_patterns, _c2); camlidl_ml2c_z3_Z3_pattern(_v3, &patterns[_c2], _ctx); } num_patterns = _c1; _c4 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_sorts, _c5); camlidl_ml2c_z3_Z3_sort(_v6, &sorts[_c5], _ctx); } num_decls = _c4; _c7 = Wosize_val(_v_decl_names); decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_decl_names, _c8); camlidl_ml2c_z3_Z3_symbol(_v9, &decl_names[_c8], _ctx); } num_decls = _c7; camlidl_ml2c_z3_Z3_ast(_v_body, &body, _ctx); _res = Z3_mk_quantifier(c, is_forall, weight, num_patterns, patterns, num_decls, sorts, decl_names, body); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_quantifier_bytecode(value * argv, int argn) { return camlidl_z3_Z3_mk_quantifier(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); } value camlidl_z3_Z3_mk_quantifier_ex( value _v_c, value _v_is_forall, value _v_weight, value _v_quantifier_id, value _v_skolem_id, value _v_patterns, value _v_no_patterns, value _v_sorts, value _v_decl_names, value _v_body) { Z3_context c; /*in*/ int is_forall; /*in*/ unsigned int weight; /*in*/ Z3_symbol quantifier_id; /*in*/ Z3_symbol skolem_id; /*in*/ unsigned int num_patterns; /*in*/ Z3_pattern const *patterns; /*in*/ unsigned int num_no_patterns; /*in*/ Z3_ast const *no_patterns; /*in*/ unsigned int num_decls; /*in*/ Z3_sort const *sorts; /*in*/ Z3_symbol const *decl_names; /*in*/ Z3_ast body; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; mlsize_t _c10; mlsize_t _c11; value _v12; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); is_forall = Int_val(_v_is_forall); weight = Int_val(_v_weight); camlidl_ml2c_z3_Z3_symbol(_v_quantifier_id, &quantifier_id, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_skolem_id, &skolem_id, _ctx); _c1 = Wosize_val(_v_patterns); patterns = camlidl_malloc(_c1 * sizeof(Z3_pattern const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_patterns, _c2); camlidl_ml2c_z3_Z3_pattern(_v3, &patterns[_c2], _ctx); } num_patterns = _c1; _c4 = Wosize_val(_v_no_patterns); no_patterns = camlidl_malloc(_c4 * sizeof(Z3_ast const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_no_patterns, _c5); camlidl_ml2c_z3_Z3_ast(_v6, &no_patterns[_c5], _ctx); } num_no_patterns = _c4; _c7 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c7 * sizeof(Z3_sort const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_sorts, _c8); camlidl_ml2c_z3_Z3_sort(_v9, &sorts[_c8], _ctx); } num_decls = _c7; _c10 = Wosize_val(_v_decl_names); decl_names = camlidl_malloc(_c10 * sizeof(Z3_symbol const ), _ctx); for (_c11 = 0; _c11 < _c10; _c11++) { _v12 = Field(_v_decl_names, _c11); camlidl_ml2c_z3_Z3_symbol(_v12, &decl_names[_c11], _ctx); } num_decls = _c10; camlidl_ml2c_z3_Z3_ast(_v_body, &body, _ctx); _res = Z3_mk_quantifier_ex(c, is_forall, weight, quantifier_id, skolem_id, num_patterns, patterns, num_no_patterns, no_patterns, num_decls, sorts, decl_names, body); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_quantifier_ex_bytecode(value * argv, int argn) { return camlidl_z3_Z3_mk_quantifier_ex(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9]); } value camlidl_z3_Z3_mk_forall_const( value _v_c, value _v_weight, value _v_bound, value _v_patterns, value _v_body) { Z3_context c; /*in*/ unsigned int weight; /*in*/ unsigned int num_bound; /*in*/ Z3_app const *bound; /*in*/ unsigned int num_patterns; /*in*/ Z3_pattern const *patterns; /*in*/ Z3_ast body; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); weight = Int_val(_v_weight); _c1 = Wosize_val(_v_bound); bound = camlidl_malloc(_c1 * sizeof(Z3_app const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_bound, _c2); camlidl_ml2c_z3_Z3_app(_v3, &bound[_c2], _ctx); } num_bound = _c1; _c4 = Wosize_val(_v_patterns); patterns = camlidl_malloc(_c4 * sizeof(Z3_pattern const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_patterns, _c5); camlidl_ml2c_z3_Z3_pattern(_v6, &patterns[_c5], _ctx); } num_patterns = _c4; camlidl_ml2c_z3_Z3_ast(_v_body, &body, _ctx); _res = Z3_mk_forall_const(c, weight, num_bound, bound, num_patterns, patterns, body); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_exists_const( value _v_c, value _v_weight, value _v_bound, value _v_patterns, value _v_body) { Z3_context c; /*in*/ unsigned int weight; /*in*/ unsigned int num_bound; /*in*/ Z3_app const *bound; /*in*/ unsigned int num_patterns; /*in*/ Z3_pattern const *patterns; /*in*/ Z3_ast body; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); weight = Int_val(_v_weight); _c1 = Wosize_val(_v_bound); bound = camlidl_malloc(_c1 * sizeof(Z3_app const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_bound, _c2); camlidl_ml2c_z3_Z3_app(_v3, &bound[_c2], _ctx); } num_bound = _c1; _c4 = Wosize_val(_v_patterns); patterns = camlidl_malloc(_c4 * sizeof(Z3_pattern const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_patterns, _c5); camlidl_ml2c_z3_Z3_pattern(_v6, &patterns[_c5], _ctx); } num_patterns = _c4; camlidl_ml2c_z3_Z3_ast(_v_body, &body, _ctx); _res = Z3_mk_exists_const(c, weight, num_bound, bound, num_patterns, patterns, body); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_quantifier_const( value _v_c, value _v_is_forall, value _v_weight, value _v_bound, value _v_patterns, value _v_body) { Z3_context c; /*in*/ int is_forall; /*in*/ unsigned int weight; /*in*/ unsigned int num_bound; /*in*/ Z3_app const *bound; /*in*/ unsigned int num_patterns; /*in*/ Z3_pattern const *patterns; /*in*/ Z3_ast body; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); is_forall = Int_val(_v_is_forall); weight = Int_val(_v_weight); _c1 = Wosize_val(_v_bound); bound = camlidl_malloc(_c1 * sizeof(Z3_app const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_bound, _c2); camlidl_ml2c_z3_Z3_app(_v3, &bound[_c2], _ctx); } num_bound = _c1; _c4 = Wosize_val(_v_patterns); patterns = camlidl_malloc(_c4 * sizeof(Z3_pattern const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_patterns, _c5); camlidl_ml2c_z3_Z3_pattern(_v6, &patterns[_c5], _ctx); } num_patterns = _c4; camlidl_ml2c_z3_Z3_ast(_v_body, &body, _ctx); _res = Z3_mk_quantifier_const(c, is_forall, weight, num_bound, bound, num_patterns, patterns, body); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_quantifier_const_bytecode(value * argv, int argn) { return camlidl_z3_Z3_mk_quantifier_const(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } value camlidl_z3_Z3_mk_quantifier_const_ex( value _v_c, value _v_is_forall, value _v_weight, value _v_quantifier_id, value _v_skolem_id, value _v_bound, value _v_patterns, value _v_no_patterns, value _v_body) { Z3_context c; /*in*/ int is_forall; /*in*/ unsigned int weight; /*in*/ Z3_symbol quantifier_id; /*in*/ Z3_symbol skolem_id; /*in*/ unsigned int num_bound; /*in*/ Z3_app const *bound; /*in*/ unsigned int num_patterns; /*in*/ Z3_pattern const *patterns; /*in*/ unsigned int num_no_patterns; /*in*/ Z3_ast const *no_patterns; /*in*/ Z3_ast body; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); is_forall = Int_val(_v_is_forall); weight = Int_val(_v_weight); camlidl_ml2c_z3_Z3_symbol(_v_quantifier_id, &quantifier_id, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_skolem_id, &skolem_id, _ctx); _c1 = Wosize_val(_v_bound); bound = camlidl_malloc(_c1 * sizeof(Z3_app const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_bound, _c2); camlidl_ml2c_z3_Z3_app(_v3, &bound[_c2], _ctx); } num_bound = _c1; _c4 = Wosize_val(_v_patterns); patterns = camlidl_malloc(_c4 * sizeof(Z3_pattern const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_patterns, _c5); camlidl_ml2c_z3_Z3_pattern(_v6, &patterns[_c5], _ctx); } num_patterns = _c4; _c7 = Wosize_val(_v_no_patterns); no_patterns = camlidl_malloc(_c7 * sizeof(Z3_ast const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_no_patterns, _c8); camlidl_ml2c_z3_Z3_ast(_v9, &no_patterns[_c8], _ctx); } num_no_patterns = _c7; camlidl_ml2c_z3_Z3_ast(_v_body, &body, _ctx); _res = Z3_mk_quantifier_const_ex(c, is_forall, weight, quantifier_id, skolem_id, num_bound, bound, num_patterns, patterns, num_no_patterns, no_patterns, body); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_quantifier_const_ex_bytecode(value * argv, int argn) { return camlidl_z3_Z3_mk_quantifier_const_ex(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]); } value camlidl_z3_Z3_get_symbol_kind( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_symbol s; /*in*/ Z3_symbol_kind _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_s, &s, _ctx); _res = Z3_get_symbol_kind(c, s); _vres = camlidl_c2ml_z3_Z3_symbol_kind(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_symbol_int( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_symbol s; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_s, &s, _ctx); _res = Z3_get_symbol_int(c, s); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_symbol_string( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_symbol s; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_s, &s, _ctx); _res = Z3_get_symbol_string(c, s); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_sort_name( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_sort d; /*in*/ Z3_symbol _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_d, &d, _ctx); _res = Z3_get_sort_name(c, d); _vres = camlidl_c2ml_z3_Z3_symbol(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_sort_id( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_sort s; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_s, &s, _ctx); _res = Z3_get_sort_id(c, s); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_sort_to_ast( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_sort s; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_s, &s, _ctx); _res = Z3_sort_to_ast(c, s); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_is_eq_sort( value _v_c, value _v_s1, value _v_s2) { Z3_context c; /*in*/ Z3_sort s1; /*in*/ Z3_sort s2; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_s1, &s1, _ctx); camlidl_ml2c_z3_Z3_sort(_v_s2, &s2, _ctx); _res = Z3_is_eq_sort(c, s1, s2); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_sort_kind( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_sort t; /*in*/ Z3_sort_kind _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); _res = Z3_get_sort_kind(c, t); _vres = camlidl_c2ml_z3_Z3_sort_kind(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_bv_sort_size( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_sort t; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); _res = Z3_get_bv_sort_size(c, t); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_finite_domain_sort_size( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_sort s; /*in*/ __int64 *r; /*out*/ __int64 _c1; value _v2; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_s, &s, _ctx); r = &_c1; Z3_get_finite_domain_sort_size(c, s, r); if (r == NULL) { _vres = Val_int(0); } else { _v2 = copy_int64(*r); Begin_root(_v2) _vres = camlidl_alloc_small(1, 0); Field(_vres, 0) = _v2; End_roots(); } camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_array_sort_domain( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_sort t; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); _res = Z3_get_array_sort_domain(c, t); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_array_sort_range( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_sort t; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); _res = Z3_get_array_sort_range(c, t); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_tuple_sort_mk_decl( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_sort t; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); _res = Z3_get_tuple_sort_mk_decl(c, t); _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_tuple_sort_num_fields( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_sort t; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); _res = Z3_get_tuple_sort_num_fields(c, t); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_tuple_sort_field_decl( value _v_c, value _v_t, value _v_i) { Z3_context c; /*in*/ Z3_sort t; /*in*/ unsigned int i; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); i = Int_val(_v_i); _res = Z3_get_tuple_sort_field_decl(c, t, i); _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_datatype_sort_num_constructors( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_sort t; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); _res = Z3_get_datatype_sort_num_constructors(c, t); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_datatype_sort_constructor( value _v_c, value _v_t, value _v_idx) { Z3_context c; /*in*/ Z3_sort t; /*in*/ unsigned int idx; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); idx = Int_val(_v_idx); _res = Z3_get_datatype_sort_constructor(c, t, idx); _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_datatype_sort_recognizer( value _v_c, value _v_t, value _v_idx) { Z3_context c; /*in*/ Z3_sort t; /*in*/ unsigned int idx; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); idx = Int_val(_v_idx); _res = Z3_get_datatype_sort_recognizer(c, t, idx); _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_datatype_sort_constructor_accessor( value _v_c, value _v_t, value _v_idx_c, value _v_idx_a) { Z3_context c; /*in*/ Z3_sort t; /*in*/ unsigned int idx_c; /*in*/ unsigned int idx_a; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); idx_c = Int_val(_v_idx_c); idx_a = Int_val(_v_idx_a); _res = Z3_get_datatype_sort_constructor_accessor(c, t, idx_c, idx_a); _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_relation_arity( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_sort s; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_s, &s, _ctx); _res = Z3_get_relation_arity(c, s); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_relation_column( value _v_c, value _v_s, value _v_col) { Z3_context c; /*in*/ Z3_sort s; /*in*/ unsigned int col; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_s, &s, _ctx); col = Int_val(_v_col); _res = Z3_get_relation_column(c, s, col); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_func_decl_to_ast( value _v_c, value _v_f) { Z3_context c; /*in*/ Z3_func_decl f; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_f, &f, _ctx); _res = Z3_func_decl_to_ast(c, f); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_is_eq_func_decl( value _v_c, value _v_f1, value _v_f2) { Z3_context c; /*in*/ Z3_func_decl f1; /*in*/ Z3_func_decl f2; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_f1, &f1, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_f2, &f2, _ctx); _res = Z3_is_eq_func_decl(c, f1, f2); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_func_decl_id( value _v_c, value _v_f) { Z3_context c; /*in*/ Z3_func_decl f; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_f, &f, _ctx); _res = Z3_get_func_decl_id(c, f); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_decl_name( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ Z3_symbol _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); _res = Z3_get_decl_name(c, d); _vres = camlidl_c2ml_z3_Z3_symbol(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_decl_kind( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ Z3_decl_kind _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); _res = Z3_get_decl_kind(c, d); _vres = camlidl_c2ml_z3_Z3_decl_kind(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_domain_size( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); _res = Z3_get_domain_size(c, d); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_arity( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); _res = Z3_get_arity(c, d); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_domain( value _v_c, value _v_d, value _v_i) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int i; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); i = Int_val(_v_i); _res = Z3_get_domain(c, d, i); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_range( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); _res = Z3_get_range(c, d); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_decl_num_parameters( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); _res = Z3_get_decl_num_parameters(c, d); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_decl_parameter_kind( value _v_c, value _v_d, value _v_idx) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int idx; /*in*/ Z3_parameter_kind _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); idx = Int_val(_v_idx); _res = Z3_get_decl_parameter_kind(c, d, idx); _vres = camlidl_c2ml_z3_Z3_parameter_kind(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_decl_int_parameter( value _v_c, value _v_d, value _v_idx) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int idx; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); idx = Int_val(_v_idx); _res = Z3_get_decl_int_parameter(c, d, idx); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_decl_double_parameter( value _v_c, value _v_d, value _v_idx) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int idx; /*in*/ double _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); idx = Int_val(_v_idx); _res = Z3_get_decl_double_parameter(c, d, idx); _vres = copy_double(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_decl_symbol_parameter( value _v_c, value _v_d, value _v_idx) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int idx; /*in*/ Z3_symbol _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); idx = Int_val(_v_idx); _res = Z3_get_decl_symbol_parameter(c, d, idx); _vres = camlidl_c2ml_z3_Z3_symbol(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_decl_sort_parameter( value _v_c, value _v_d, value _v_idx) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int idx; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); idx = Int_val(_v_idx); _res = Z3_get_decl_sort_parameter(c, d, idx); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_decl_ast_parameter( value _v_c, value _v_d, value _v_idx) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int idx; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); idx = Int_val(_v_idx); _res = Z3_get_decl_ast_parameter(c, d, idx); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_decl_func_decl_parameter( value _v_c, value _v_d, value _v_idx) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int idx; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); idx = Int_val(_v_idx); _res = Z3_get_decl_func_decl_parameter(c, d, idx); _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_decl_rational_parameter( value _v_c, value _v_d, value _v_idx) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int idx; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); idx = Int_val(_v_idx); _res = Z3_get_decl_rational_parameter(c, d, idx); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_app_to_ast( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_app a; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_app(_v_a, &a, _ctx); _res = Z3_app_to_ast(c, a); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_app_decl( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_app a; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_app(_v_a, &a, _ctx); _res = Z3_get_app_decl(c, a); _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_app_num_args( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_app a; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_app(_v_a, &a, _ctx); _res = Z3_get_app_num_args(c, a); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_app_arg( value _v_c, value _v_a, value _v_i) { Z3_context c; /*in*/ Z3_app a; /*in*/ unsigned int i; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_app(_v_a, &a, _ctx); i = Int_val(_v_i); _res = Z3_get_app_arg(c, a, i); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_is_eq_ast( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_is_eq_ast(c, t1, t2); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_ast_id( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_ast t; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t, &t, _ctx); _res = Z3_get_ast_id(c, t); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_ast_hash( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_ast_hash(c, a); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_sort( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_sort(c, a); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_is_well_sorted( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_ast t; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t, &t, _ctx); _res = Z3_is_well_sorted(c, t); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_bool_value( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_lbool _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_bool_value(c, a); _vres = camlidl_c2ml_z3_Z3_lbool(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_ast_kind( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_ast_kind _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_ast_kind(c, a); _vres = camlidl_c2ml_z3_Z3_ast_kind(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_is_app( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_is_app(c, a); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_is_numeral_ast( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_is_numeral_ast(c, a); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_is_algebraic_number( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_is_algebraic_number(c, a); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_to_app( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_app _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_to_app(c, a); _vres = camlidl_c2ml_z3_Z3_app(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_to_func_decl( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_to_func_decl(c, a); _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_numeral_string( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_numeral_string(c, a); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_numeral_decimal_string( value _v_c, value _v_a, value _v_precision) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int precision; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); precision = Int_val(_v_precision); _res = Z3_get_numeral_decimal_string(c, a, precision); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_numerator( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_numerator(c, a); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_denominator( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_denominator(c, a); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_numeral_small( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ __int64 *num; /*out*/ __int64 *den; /*out*/ int _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; __int64 _c1; __int64 _c2; value _vresult; value _vres[3] = { 0, 0, 0, }; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); num = &_c1; den = &_c2; _res = Z3_get_numeral_small(c, a, num, den); Begin_roots_block(_vres, 3) _vres[0] = Val_int(_res); _vres[1] = copy_int64(*num); _vres[2] = copy_int64(*den); _vresult = camlidl_alloc_small(3, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; Field(_vresult, 2) = _vres[2]; End_roots() camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vresult; } value camlidl_z3_Z3_get_numeral_int( value _v_c, value _v_v) { Z3_context c; /*in*/ Z3_ast v; /*in*/ int *i; /*out*/ int _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; int _c1; value _vresult; value _vres[2] = { 0, 0, }; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_v, &v, _ctx); i = &_c1; _res = Z3_get_numeral_int(c, v, i); Begin_roots_block(_vres, 2) _vres[0] = Val_int(_res); _vres[1] = Val_int(*i); _vresult = camlidl_alloc_small(2, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; End_roots() camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vresult; } value camlidl_z3_Z3_get_numeral_int64( value _v_c, value _v_v) { Z3_context c; /*in*/ Z3_ast v; /*in*/ __int64 *i; /*out*/ int _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; __int64 _c1; value _vresult; value _vres[2] = { 0, 0, }; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_v, &v, _ctx); i = &_c1; _res = Z3_get_numeral_int64(c, v, i); Begin_roots_block(_vres, 2) _vres[0] = Val_int(_res); _vres[1] = copy_int64(*i); _vresult = camlidl_alloc_small(2, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; End_roots() camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vresult; } value camlidl_z3_Z3_get_numeral_rational_int64( value _v_c, value _v_v) { Z3_context c; /*in*/ Z3_ast v; /*in*/ __int64 *num; /*out*/ __int64 *den; /*out*/ int _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; __int64 _c1; __int64 _c2; value _vresult; value _vres[3] = { 0, 0, 0, }; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_v, &v, _ctx); num = &_c1; den = &_c2; _res = Z3_get_numeral_rational_int64(c, v, num, den); Begin_roots_block(_vres, 3) _vres[0] = Val_int(_res); _vres[1] = copy_int64(*num); _vres[2] = copy_int64(*den); _vresult = camlidl_alloc_small(3, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; Field(_vresult, 2) = _vres[2]; End_roots() camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vresult; } value camlidl_z3_Z3_get_algebraic_number_lower( value _v_c, value _v_a, value _v_precision) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int precision; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); precision = Int_val(_v_precision); _res = Z3_get_algebraic_number_lower(c, a, precision); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_algebraic_number_upper( value _v_c, value _v_a, value _v_precision) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int precision; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); precision = Int_val(_v_precision); _res = Z3_get_algebraic_number_upper(c, a, precision); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_pattern_to_ast( value _v_c, value _v_p) { Z3_context c; /*in*/ Z3_pattern p; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_pattern(_v_p, &p, _ctx); _res = Z3_pattern_to_ast(c, p); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_pattern_num_terms( value _v_c, value _v_p) { Z3_context c; /*in*/ Z3_pattern p; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_pattern(_v_p, &p, _ctx); _res = Z3_get_pattern_num_terms(c, p); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_pattern( value _v_c, value _v_p, value _v_idx) { Z3_context c; /*in*/ Z3_pattern p; /*in*/ unsigned int idx; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_pattern(_v_p, &p, _ctx); idx = Int_val(_v_idx); _res = Z3_get_pattern(c, p, idx); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_index_value( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_index_value(c, a); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_is_quantifier_forall( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_is_quantifier_forall(c, a); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_quantifier_weight( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_quantifier_weight(c, a); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_quantifier_num_patterns( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_quantifier_num_patterns(c, a); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_quantifier_pattern_ast( value _v_c, value _v_a, value _v_i) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int i; /*in*/ Z3_pattern _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); i = Int_val(_v_i); _res = Z3_get_quantifier_pattern_ast(c, a, i); _vres = camlidl_c2ml_z3_Z3_pattern(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_quantifier_num_no_patterns( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_quantifier_num_no_patterns(c, a); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_quantifier_no_pattern_ast( value _v_c, value _v_a, value _v_i) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int i; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); i = Int_val(_v_i); _res = Z3_get_quantifier_no_pattern_ast(c, a, i); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_quantifier_num_bound( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_quantifier_num_bound(c, a); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_quantifier_bound_name( value _v_c, value _v_a, value _v_i) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int i; /*in*/ Z3_symbol _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); i = Int_val(_v_i); _res = Z3_get_quantifier_bound_name(c, a, i); _vres = camlidl_c2ml_z3_Z3_symbol(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_quantifier_bound_sort( value _v_c, value _v_a, value _v_i) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int i; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); i = Int_val(_v_i); _res = Z3_get_quantifier_bound_sort(c, a, i); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_quantifier_body( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_quantifier_body(c, a); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_simplify( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_simplify(c, a); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_simplify_ex( value _v_c, value _v_a, value _v_p) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_params p; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); _res = Z3_simplify_ex(c, a, p); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_simplify_get_help( value _v_c) { Z3_context c; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_simplify_get_help(c); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_simplify_get_param_descrs( value _v_c) { Z3_context c; /*in*/ Z3_param_descrs _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_simplify_get_param_descrs(c); _vres = camlidl_c2ml_z3_Z3_param_descrs(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_update_term( value _v_c, value _v_a, value _v_args) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_update_term(c, a, num_args, args); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_substitute( value _v_c, value _v_a, value _v_from, value _v_to) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int num_exprs; /*in*/ Z3_ast const *from; /*in*/ Z3_ast const *to; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _c1 = Wosize_val(_v_from); from = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_from, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &from[_c2], _ctx); } num_exprs = _c1; _c4 = Wosize_val(_v_to); to = camlidl_malloc(_c4 * sizeof(Z3_ast const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_to, _c5); camlidl_ml2c_z3_Z3_ast(_v6, &to[_c5], _ctx); } num_exprs = _c4; _res = Z3_substitute(c, a, num_exprs, from, to); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_substitute_vars( value _v_c, value _v_a, value _v_to) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int num_exprs; /*in*/ Z3_ast const *to; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _c1 = Wosize_val(_v_to); to = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_to, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &to[_c2], _ctx); } num_exprs = _c1; _res = Z3_substitute_vars(c, a, num_exprs, to); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_translate( value _v_source, value _v_a, value _v_target) { Z3_context source; /*in*/ Z3_ast a; /*in*/ Z3_context target; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_source, &source, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); camlidl_ml2c_z3_Z3_context(_v_target, &target, _ctx); _res = Z3_translate(source, a, target); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(source); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_model_eval( value _v_c, value _v_m, value _v_t, value _v_model_completion) { Z3_context c; /*in*/ Z3_model m; /*in*/ Z3_ast t; /*in*/ int model_completion; /*in*/ Z3_ast *v; /*out*/ Z3_ast _c1; value _v2; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); camlidl_ml2c_z3_Z3_ast(_v_t, &t, _ctx); model_completion = Int_val(_v_model_completion); v = &_c1; Z3_model_eval(c, m, t, model_completion, v); if (v == NULL) { _vres = Val_int(0); } else { _v2 = camlidl_c2ml_z3_Z3_ast(&*v, _ctx); Begin_root(_v2) _vres = camlidl_alloc_small(1, 0); Field(_vres, 0) = _v2; End_roots(); } camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_model_get_const_interp( value _v_c, value _v_m, value _v_a) { Z3_context c; /*in*/ Z3_model m; /*in*/ Z3_func_decl a; /*in*/ Z3_ast_opt _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_a, &a, _ctx); _res = Z3_model_get_const_interp(c, m, a); _vres = camlidl_c2ml_z3_Z3_ast_opt(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_model_get_func_interp( value _v_c, value _v_m, value _v_f) { Z3_context c; /*in*/ Z3_model m; /*in*/ Z3_func_decl f; /*in*/ Z3_func_interp_opt _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_f, &f, _ctx); _res = Z3_model_get_func_interp(c, m, f); _vres = camlidl_c2ml_z3_Z3_func_interp_opt(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_model_get_num_consts( value _v_c, value _v_m) { Z3_context c; /*in*/ Z3_model m; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); _res = Z3_model_get_num_consts(c, m); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_model_get_const_decl( value _v_c, value _v_m, value _v_i) { Z3_context c; /*in*/ Z3_model m; /*in*/ unsigned int i; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); i = Int_val(_v_i); _res = Z3_model_get_const_decl(c, m, i); _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_model_get_num_funcs( value _v_c, value _v_m) { Z3_context c; /*in*/ Z3_model m; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); _res = Z3_model_get_num_funcs(c, m); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_model_get_func_decl( value _v_c, value _v_m, value _v_i) { Z3_context c; /*in*/ Z3_model m; /*in*/ unsigned int i; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); i = Int_val(_v_i); _res = Z3_model_get_func_decl(c, m, i); _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_model_get_num_sorts( value _v_c, value _v_m) { Z3_context c; /*in*/ Z3_model m; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); _res = Z3_model_get_num_sorts(c, m); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_model_get_sort( value _v_c, value _v_m, value _v_i) { Z3_context c; /*in*/ Z3_model m; /*in*/ unsigned int i; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); i = Int_val(_v_i); _res = Z3_model_get_sort(c, m, i); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_model_get_sort_universe( value _v_c, value _v_m, value _v_s) { Z3_context c; /*in*/ Z3_model m; /*in*/ Z3_sort s; /*in*/ Z3_ast_vector _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); camlidl_ml2c_z3_Z3_sort(_v_s, &s, _ctx); _res = Z3_model_get_sort_universe(c, m, s); _vres = camlidl_c2ml_z3_Z3_ast_vector(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_is_as_array( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_is_as_array(c, a); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_as_array_func_decl( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_as_array_func_decl(c, a); _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_func_interp_get_num_entries( value _v_c, value _v_f) { Z3_context c; /*in*/ Z3_func_interp f; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_interp(_v_f, &f, _ctx); _res = Z3_func_interp_get_num_entries(c, f); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_func_interp_get_entry( value _v_c, value _v_f, value _v_i) { Z3_context c; /*in*/ Z3_func_interp f; /*in*/ unsigned int i; /*in*/ Z3_func_entry _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_interp(_v_f, &f, _ctx); i = Int_val(_v_i); _res = Z3_func_interp_get_entry(c, f, i); _vres = camlidl_c2ml_z3_Z3_func_entry(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_func_interp_get_else( value _v_c, value _v_f) { Z3_context c; /*in*/ Z3_func_interp f; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_interp(_v_f, &f, _ctx); _res = Z3_func_interp_get_else(c, f); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_func_interp_get_arity( value _v_c, value _v_f) { Z3_context c; /*in*/ Z3_func_interp f; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_interp(_v_f, &f, _ctx); _res = Z3_func_interp_get_arity(c, f); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_func_entry_get_value( value _v_c, value _v_e) { Z3_context c; /*in*/ Z3_func_entry e; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_entry(_v_e, &e, _ctx); _res = Z3_func_entry_get_value(c, e); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_func_entry_get_num_args( value _v_c, value _v_e) { Z3_context c; /*in*/ Z3_func_entry e; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_entry(_v_e, &e, _ctx); _res = Z3_func_entry_get_num_args(c, e); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_func_entry_get_arg( value _v_c, value _v_e, value _v_i) { Z3_context c; /*in*/ Z3_func_entry e; /*in*/ unsigned int i; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_entry(_v_e, &e, _ctx); i = Int_val(_v_i); _res = Z3_func_entry_get_arg(c, e, i); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_open_log( value _v_filename) { Z3_string filename; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_string(_v_filename, &filename, _ctx); _res = Z3_open_log(filename); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3_Z3_append_log( value _v_string) { Z3_string string; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_string(_v_string, &string, _ctx); Z3_append_log(string); camlidl_free(_ctx); return Val_unit; } value camlidl_z3_Z3_close_log(value _unit) { Z3_close_log(); return Val_unit; } value camlidl_z3_Z3_toggle_warning_messages( value _v_enabled) { int enabled; /*in*/ enabled = Int_val(_v_enabled); Z3_toggle_warning_messages(enabled); return Val_unit; } value camlidl_z3_Z3_set_ast_print_mode( value _v_c, value _v_mode) { Z3_context c; /*in*/ Z3_ast_print_mode mode; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast_print_mode(_v_mode, &mode, _ctx); Z3_set_ast_print_mode(c, mode); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_ast_to_string( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); _res = Z3_ast_to_string(c, a); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_pattern_to_string( value _v_c, value _v_p) { Z3_context c; /*in*/ Z3_pattern p; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_pattern(_v_p, &p, _ctx); _res = Z3_pattern_to_string(c, p); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_sort_to_string( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_sort s; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_sort(_v_s, &s, _ctx); _res = Z3_sort_to_string(c, s); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_func_decl_to_string( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); _res = Z3_func_decl_to_string(c, d); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_model_to_string( value _v_c, value _v_m) { Z3_context c; /*in*/ Z3_model m; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); _res = Z3_model_to_string(c, m); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_benchmark_to_smtlib_string( value _v_c, value _v_name, value _v_logic, value _v_status, value _v_attributes, value _v_assumptions, value _v_formula) { Z3_context c; /*in*/ Z3_string name; /*in*/ Z3_string logic; /*in*/ Z3_string status; /*in*/ Z3_string attributes; /*in*/ unsigned int num_assumptions; /*in*/ Z3_ast const *assumptions; /*in*/ Z3_ast formula; /*in*/ Z3_string _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_name, &name, _ctx); camlidl_ml2c_z3_Z3_string(_v_logic, &logic, _ctx); camlidl_ml2c_z3_Z3_string(_v_status, &status, _ctx); camlidl_ml2c_z3_Z3_string(_v_attributes, &attributes, _ctx); _c1 = Wosize_val(_v_assumptions); assumptions = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_assumptions, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &assumptions[_c2], _ctx); } num_assumptions = _c1; camlidl_ml2c_z3_Z3_ast(_v_formula, &formula, _ctx); _res = Z3_benchmark_to_smtlib_string(c, name, logic, status, attributes, num_assumptions, assumptions, formula); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_benchmark_to_smtlib_string_bytecode(value * argv, int argn) { return camlidl_z3_Z3_benchmark_to_smtlib_string(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); } value camlidl_z3_Z3_parse_smtlib2_string( value _v_c, value _v_str, value _v_sort_names, value _v_sorts, value _v_decl_names, value _v_decls) { Z3_context c; /*in*/ Z3_string str; /*in*/ unsigned int num_sorts; /*in*/ Z3_symbol const *sort_names; /*in*/ Z3_sort const *sorts; /*in*/ unsigned int num_decls; /*in*/ Z3_symbol const *decl_names; /*in*/ Z3_func_decl const *decls; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; mlsize_t _c10; mlsize_t _c11; value _v12; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_str, &str, _ctx); _c1 = Wosize_val(_v_sort_names); sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_sort_names, _c2); camlidl_ml2c_z3_Z3_symbol(_v3, &sort_names[_c2], _ctx); } num_sorts = _c1; _c4 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_sorts, _c5); camlidl_ml2c_z3_Z3_sort(_v6, &sorts[_c5], _ctx); } num_sorts = _c4; _c7 = Wosize_val(_v_decl_names); decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_decl_names, _c8); camlidl_ml2c_z3_Z3_symbol(_v9, &decl_names[_c8], _ctx); } num_decls = _c7; _c10 = Wosize_val(_v_decls); decls = camlidl_malloc(_c10 * sizeof(Z3_func_decl const ), _ctx); for (_c11 = 0; _c11 < _c10; _c11++) { _v12 = Field(_v_decls, _c11); camlidl_ml2c_z3_Z3_func_decl(_v12, &decls[_c11], _ctx); } num_decls = _c10; _res = Z3_parse_smtlib2_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_parse_smtlib2_string_bytecode(value * argv, int argn) { return camlidl_z3_Z3_parse_smtlib2_string(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } value camlidl_z3_Z3_parse_smtlib2_file( value _v_c, value _v_file_name, value _v_sort_names, value _v_sorts, value _v_decl_names, value _v_decls) { Z3_context c; /*in*/ Z3_string file_name; /*in*/ unsigned int num_sorts; /*in*/ Z3_symbol const *sort_names; /*in*/ Z3_sort const *sorts; /*in*/ unsigned int num_decls; /*in*/ Z3_symbol const *decl_names; /*in*/ Z3_func_decl const *decls; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; mlsize_t _c10; mlsize_t _c11; value _v12; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_file_name, &file_name, _ctx); _c1 = Wosize_val(_v_sort_names); sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_sort_names, _c2); camlidl_ml2c_z3_Z3_symbol(_v3, &sort_names[_c2], _ctx); } num_sorts = _c1; _c4 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_sorts, _c5); camlidl_ml2c_z3_Z3_sort(_v6, &sorts[_c5], _ctx); } num_sorts = _c4; _c7 = Wosize_val(_v_decl_names); decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_decl_names, _c8); camlidl_ml2c_z3_Z3_symbol(_v9, &decl_names[_c8], _ctx); } num_decls = _c7; _c10 = Wosize_val(_v_decls); decls = camlidl_malloc(_c10 * sizeof(Z3_func_decl const ), _ctx); for (_c11 = 0; _c11 < _c10; _c11++) { _v12 = Field(_v_decls, _c11); camlidl_ml2c_z3_Z3_func_decl(_v12, &decls[_c11], _ctx); } num_decls = _c10; _res = Z3_parse_smtlib2_file(c, file_name, num_sorts, sort_names, sorts, num_decls, decl_names, decls); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_parse_smtlib2_file_bytecode(value * argv, int argn) { return camlidl_z3_Z3_parse_smtlib2_file(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } value camlidl_z3_Z3_parse_smtlib_string( value _v_c, value _v_str, value _v_sort_names, value _v_sorts, value _v_decl_names, value _v_decls) { Z3_context c; /*in*/ Z3_string str; /*in*/ unsigned int num_sorts; /*in*/ Z3_symbol const *sort_names; /*in*/ Z3_sort const *sorts; /*in*/ unsigned int num_decls; /*in*/ Z3_symbol const *decl_names; /*in*/ Z3_func_decl const *decls; /*in*/ mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; mlsize_t _c10; mlsize_t _c11; value _v12; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_str, &str, _ctx); _c1 = Wosize_val(_v_sort_names); sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_sort_names, _c2); camlidl_ml2c_z3_Z3_symbol(_v3, &sort_names[_c2], _ctx); } num_sorts = _c1; _c4 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_sorts, _c5); camlidl_ml2c_z3_Z3_sort(_v6, &sorts[_c5], _ctx); } num_sorts = _c4; _c7 = Wosize_val(_v_decl_names); decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_decl_names, _c8); camlidl_ml2c_z3_Z3_symbol(_v9, &decl_names[_c8], _ctx); } num_decls = _c7; _c10 = Wosize_val(_v_decls); decls = camlidl_malloc(_c10 * sizeof(Z3_func_decl const ), _ctx); for (_c11 = 0; _c11 < _c10; _c11++) { _v12 = Field(_v_decls, _c11); camlidl_ml2c_z3_Z3_func_decl(_v12, &decls[_c11], _ctx); } num_decls = _c10; Z3_parse_smtlib_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_parse_smtlib_string_bytecode(value * argv, int argn) { return camlidl_z3_Z3_parse_smtlib_string(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } value camlidl_z3_Z3_parse_smtlib_file( value _v_c, value _v_file_name, value _v_sort_names, value _v_sorts, value _v_decl_names, value _v_decls) { Z3_context c; /*in*/ Z3_string file_name; /*in*/ unsigned int num_sorts; /*in*/ Z3_symbol const *sort_names; /*in*/ Z3_sort const *sorts; /*in*/ unsigned int num_decls; /*in*/ Z3_symbol const *decl_names; /*in*/ Z3_func_decl const *decls; /*in*/ mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; mlsize_t _c10; mlsize_t _c11; value _v12; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_file_name, &file_name, _ctx); _c1 = Wosize_val(_v_sort_names); sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_sort_names, _c2); camlidl_ml2c_z3_Z3_symbol(_v3, &sort_names[_c2], _ctx); } num_sorts = _c1; _c4 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_sorts, _c5); camlidl_ml2c_z3_Z3_sort(_v6, &sorts[_c5], _ctx); } num_sorts = _c4; _c7 = Wosize_val(_v_decl_names); decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_decl_names, _c8); camlidl_ml2c_z3_Z3_symbol(_v9, &decl_names[_c8], _ctx); } num_decls = _c7; _c10 = Wosize_val(_v_decls); decls = camlidl_malloc(_c10 * sizeof(Z3_func_decl const ), _ctx); for (_c11 = 0; _c11 < _c10; _c11++) { _v12 = Field(_v_decls, _c11); camlidl_ml2c_z3_Z3_func_decl(_v12, &decls[_c11], _ctx); } num_decls = _c10; Z3_parse_smtlib_file(c, file_name, num_sorts, sort_names, sorts, num_decls, decl_names, decls); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_parse_smtlib_file_bytecode(value * argv, int argn) { return camlidl_z3_Z3_parse_smtlib_file(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } value camlidl_z3_Z3_get_smtlib_num_formulas( value _v_c) { Z3_context c; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_smtlib_num_formulas(c); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_smtlib_formula( value _v_c, value _v_i) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); _res = Z3_get_smtlib_formula(c, i); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_smtlib_num_assumptions( value _v_c) { Z3_context c; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_smtlib_num_assumptions(c); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_smtlib_assumption( value _v_c, value _v_i) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); _res = Z3_get_smtlib_assumption(c, i); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_smtlib_num_decls( value _v_c) { Z3_context c; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_smtlib_num_decls(c); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_smtlib_decl( value _v_c, value _v_i) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); _res = Z3_get_smtlib_decl(c, i); _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_smtlib_num_sorts( value _v_c) { Z3_context c; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_smtlib_num_sorts(c); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_smtlib_sort( value _v_c, value _v_i) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); _res = Z3_get_smtlib_sort(c, i); _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_smtlib_error( value _v_c) { Z3_context c; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_smtlib_error(c); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } /* value camlidl_z3_Z3_parse_z3_string( value _v_c, value _v_str) { Z3_context c; /*in Z3_string str; /*in Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_str, &str, _ctx); _res = Z3_parse_z3_string(c, str); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence check_error_code(c); /* end user-supplied deallocation sequence return _vres; } value camlidl_z3_Z3_parse_z3_file( value _v_c, value _v_file_name) { Z3_context c; /*in Z3_string file_name; /*in Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_file_name, &file_name, _ctx); _res = Z3_parse_z3_file(c, file_name); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence check_error_code(c); /* end user-supplied deallocation sequence return _vres; } */ value camlidl_z3_Z3_set_error( value _v_c, value _v_e) { Z3_context c; /*in*/ Z3_error_code e; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_error_code(_v_e, &e, _ctx); Z3_set_error(c, e); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_get_error_msg_ex( value _v_c, value _v_err) { Z3_context c; /*in*/ Z3_error_code err; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_error_code(_v_err, &err, _ctx); _res = Z3_get_error_msg_ex(c, err); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_version(value _unit) { unsigned int *major; /*out*/ unsigned int *minor; /*out*/ unsigned int *build_number; /*out*/ unsigned int *revision_number; /*out*/ unsigned int _c1; unsigned int _c2; unsigned int _c3; unsigned int _c4; value _vresult; value _vres[4] = { 0, 0, 0, 0, }; major = &_c1; minor = &_c2; build_number = &_c3; revision_number = &_c4; Z3_get_version(major, minor, build_number, revision_number); Begin_roots_block(_vres, 4) _vres[0] = Val_int(*major); _vres[1] = Val_int(*minor); _vres[2] = Val_int(*build_number); _vres[3] = Val_int(*revision_number); _vresult = camlidl_alloc_small(4, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; Field(_vresult, 2) = _vres[2]; Field(_vresult, 3) = _vres[3]; End_roots() return _vresult; } value camlidl_z3_Z3_mk_fixedpoint( value _v_c) { Z3_context c; /*in*/ Z3_fixedpoint _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_mk_fixedpoint(c); _vres = camlidl_c2ml_z3_Z3_fixedpoint(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_fixedpoint_add_rule( value _v_c, value _v_d, value _v_rule, value _v_name) { Z3_context c; /*in*/ Z3_fixedpoint d; /*in*/ Z3_ast rule; /*in*/ Z3_symbol name; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); camlidl_ml2c_z3_Z3_ast(_v_rule, &rule, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_name, &name, _ctx); Z3_fixedpoint_add_rule(c, d, rule, name); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_fixedpoint_add_fact( value _v_c, value _v_d, value _v_r, value _v_args) { Z3_context c; /*in*/ Z3_fixedpoint d; /*in*/ Z3_func_decl r; /*in*/ unsigned int num_args; /*in*/ unsigned int *args; /*in*/ mlsize_t _c1; mlsize_t _c2; value _v3; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_r, &r, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(unsigned int ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); args[_c2] = Int_val(_v3); } num_args = _c1; Z3_fixedpoint_add_fact(c, d, r, num_args, args); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_fixedpoint_assert( value _v_c, value _v_d, value _v_axiom) { Z3_context c; /*in*/ Z3_fixedpoint d; /*in*/ Z3_ast axiom; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); camlidl_ml2c_z3_Z3_ast(_v_axiom, &axiom, _ctx); Z3_fixedpoint_assert(c, d, axiom); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_fixedpoint_query( value _v_c, value _v_d, value _v_query) { Z3_context c; /*in*/ Z3_fixedpoint d; /*in*/ Z3_ast query; /*in*/ Z3_lbool _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); camlidl_ml2c_z3_Z3_ast(_v_query, &query, _ctx); _res = Z3_fixedpoint_query(c, d, query); _vres = camlidl_c2ml_z3_Z3_lbool(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_fixedpoint_query_relations( value _v_c, value _v_d, value _v_relations) { Z3_context c; /*in*/ Z3_fixedpoint d; /*in*/ unsigned int num_relations; /*in*/ Z3_func_decl const *relations; /*in*/ Z3_lbool _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); _c1 = Wosize_val(_v_relations); relations = camlidl_malloc(_c1 * sizeof(Z3_func_decl const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_relations, _c2); camlidl_ml2c_z3_Z3_func_decl(_v3, &relations[_c2], _ctx); } num_relations = _c1; _res = Z3_fixedpoint_query_relations(c, d, num_relations, relations); _vres = camlidl_c2ml_z3_Z3_lbool(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_fixedpoint_get_answer( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_fixedpoint d; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); _res = Z3_fixedpoint_get_answer(c, d); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_fixedpoint_get_reason_unknown( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_fixedpoint d; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); _res = Z3_fixedpoint_get_reason_unknown(c, d); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_fixedpoint_update_rule( value _v_c, value _v_d, value _v_a, value _v_name) { Z3_context c; /*in*/ Z3_fixedpoint d; /*in*/ Z3_ast a; /*in*/ Z3_symbol name; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_name, &name, _ctx); Z3_fixedpoint_update_rule(c, d, a, name); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_fixedpoint_get_num_levels( value _v_c, value _v_d, value _v_pred) { Z3_context c; /*in*/ Z3_fixedpoint d; /*in*/ Z3_func_decl pred; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_pred, &pred, _ctx); _res = Z3_fixedpoint_get_num_levels(c, d, pred); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_fixedpoint_get_cover_delta( value _v_c, value _v_d, value _v_level, value _v_pred) { Z3_context c; /*in*/ Z3_fixedpoint d; /*in*/ int level; /*in*/ Z3_func_decl pred; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); level = Int_val(_v_level); camlidl_ml2c_z3_Z3_func_decl(_v_pred, &pred, _ctx); _res = Z3_fixedpoint_get_cover_delta(c, d, level, pred); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_fixedpoint_add_cover( value _v_c, value _v_d, value _v_level, value _v_pred, value _v_property) { Z3_context c; /*in*/ Z3_fixedpoint d; /*in*/ int level; /*in*/ Z3_func_decl pred; /*in*/ Z3_ast property; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); level = Int_val(_v_level); camlidl_ml2c_z3_Z3_func_decl(_v_pred, &pred, _ctx); camlidl_ml2c_z3_Z3_ast(_v_property, &property, _ctx); Z3_fixedpoint_add_cover(c, d, level, pred, property); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_fixedpoint_get_statistics( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_fixedpoint d; /*in*/ Z3_stats _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); _res = Z3_fixedpoint_get_statistics(c, d); _vres = camlidl_c2ml_z3_Z3_stats(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_fixedpoint_register_relation( value _v_c, value _v_d, value _v_f) { Z3_context c; /*in*/ Z3_fixedpoint d; /*in*/ Z3_func_decl f; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_f, &f, _ctx); Z3_fixedpoint_register_relation(c, d, f); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_fixedpoint_set_predicate_representation( value _v_c, value _v_d, value _v_f, value _v_relation_kinds) { Z3_context c; /*in*/ Z3_fixedpoint d; /*in*/ Z3_func_decl f; /*in*/ unsigned int num_relations; /*in*/ Z3_symbol const *relation_kinds; /*in*/ mlsize_t _c1; mlsize_t _c2; value _v3; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); camlidl_ml2c_z3_Z3_func_decl(_v_f, &f, _ctx); _c1 = Wosize_val(_v_relation_kinds); relation_kinds = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_relation_kinds, _c2); camlidl_ml2c_z3_Z3_symbol(_v3, &relation_kinds[_c2], _ctx); } num_relations = _c1; Z3_fixedpoint_set_predicate_representation(c, d, f, num_relations, relation_kinds); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_fixedpoint_simplify_rules( value _v_c, value _v_f, value _v_rules, value _v_outputs) { Z3_context c; /*in*/ Z3_fixedpoint f; /*in*/ unsigned int num_rules; /*in*/ Z3_ast *rules; /*in*/ unsigned int num_outputs; /*in*/ Z3_func_decl *outputs; /*in*/ Z3_ast_vector _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_f, &f, _ctx); _c1 = Wosize_val(_v_rules); rules = camlidl_malloc(_c1 * sizeof(Z3_ast ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_rules, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &rules[_c2], _ctx); } num_rules = _c1; _c4 = Wosize_val(_v_outputs); outputs = camlidl_malloc(_c4 * sizeof(Z3_func_decl ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_outputs, _c5); camlidl_ml2c_z3_Z3_func_decl(_v6, &outputs[_c5], _ctx); } num_outputs = _c4; // _res = Z3_fixedpoint_simplify_rules(c, f, num_rules, rules, num_outputs, outputs); _vres = camlidl_c2ml_z3_Z3_ast_vector(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_fixedpoint_set_params( value _v_c, value _v_f, value _v_p) { Z3_context c; /*in*/ Z3_fixedpoint f; /*in*/ Z3_params p; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_f, &f, _ctx); camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); Z3_fixedpoint_set_params(c, f, p); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_fixedpoint_get_help( value _v_c, value _v_f) { Z3_context c; /*in*/ Z3_fixedpoint f; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_f, &f, _ctx); _res = Z3_fixedpoint_get_help(c, f); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_fixedpoint_get_param_descrs( value _v_c, value _v_f) { Z3_context c; /*in*/ Z3_fixedpoint f; /*in*/ Z3_param_descrs _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_f, &f, _ctx); _res = Z3_fixedpoint_get_param_descrs(c, f); _vres = camlidl_c2ml_z3_Z3_param_descrs(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_fixedpoint_to_string( value _v_c, value _v_f, value _v_queries) { Z3_context c; /*in*/ Z3_fixedpoint f; /*in*/ unsigned int num_queries; /*in*/ Z3_ast *queries; /*in*/ Z3_string _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_f, &f, _ctx); _c1 = Wosize_val(_v_queries); queries = camlidl_malloc(_c1 * sizeof(Z3_ast ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_queries, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &queries[_c2], _ctx); } num_queries = _c1; _res = Z3_fixedpoint_to_string(c, f, num_queries, queries); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_fixedpoint_push( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_fixedpoint d; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); Z3_fixedpoint_push(c, d); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_fixedpoint_pop( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_fixedpoint d; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); Z3_fixedpoint_pop(c, d); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_mk_ast_vector( value _v_c) { Z3_context c; /*in*/ Z3_ast_vector _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_mk_ast_vector(c); _vres = camlidl_c2ml_z3_Z3_ast_vector(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_ast_vector_size( value _v_c, value _v_v) { Z3_context c; /*in*/ Z3_ast_vector v; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast_vector(_v_v, &v, _ctx); _res = Z3_ast_vector_size(c, v); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_ast_vector_get( value _v_c, value _v_v, value _v_i) { Z3_context c; /*in*/ Z3_ast_vector v; /*in*/ unsigned int i; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast_vector(_v_v, &v, _ctx); i = Int_val(_v_i); _res = Z3_ast_vector_get(c, v, i); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_ast_vector_set( value _v_c, value _v_v, value _v_i, value _v_a) { Z3_context c; /*in*/ Z3_ast_vector v; /*in*/ unsigned int i; /*in*/ Z3_ast a; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast_vector(_v_v, &v, _ctx); i = Int_val(_v_i); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); Z3_ast_vector_set(c, v, i, a); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_ast_vector_resize( value _v_c, value _v_v, value _v_n) { Z3_context c; /*in*/ Z3_ast_vector v; /*in*/ unsigned int n; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast_vector(_v_v, &v, _ctx); n = Int_val(_v_n); Z3_ast_vector_resize(c, v, n); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_ast_vector_push( value _v_c, value _v_v, value _v_a) { Z3_context c; /*in*/ Z3_ast_vector v; /*in*/ Z3_ast a; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast_vector(_v_v, &v, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); Z3_ast_vector_push(c, v, a); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_ast_vector_translate( value _v_s, value _v_v, value _v_t) { Z3_context s; /*in*/ Z3_ast_vector v; /*in*/ Z3_context t; /*in*/ Z3_ast_vector _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_s, &s, _ctx); camlidl_ml2c_z3_Z3_ast_vector(_v_v, &v, _ctx); camlidl_ml2c_z3_Z3_context(_v_t, &t, _ctx); _res = Z3_ast_vector_translate(s, v, t); _vres = camlidl_c2ml_z3_Z3_ast_vector(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(s); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_ast_vector_to_string( value _v_c, value _v_v) { Z3_context c; /*in*/ Z3_ast_vector v; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast_vector(_v_v, &v, _ctx); _res = Z3_ast_vector_to_string(c, v); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_ast_map( value _v_c) { Z3_context c; /*in*/ Z3_ast_map _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_mk_ast_map(c); _vres = camlidl_c2ml_z3_Z3_ast_map(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_ast_map_contains( value _v_c, value _v_m, value _v_k) { Z3_context c; /*in*/ Z3_ast_map m; /*in*/ Z3_ast k; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast_map(_v_m, &m, _ctx); camlidl_ml2c_z3_Z3_ast(_v_k, &k, _ctx); _res = Z3_ast_map_contains(c, m, k); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_ast_map_find( value _v_c, value _v_m, value _v_k) { Z3_context c; /*in*/ Z3_ast_map m; /*in*/ Z3_ast k; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast_map(_v_m, &m, _ctx); camlidl_ml2c_z3_Z3_ast(_v_k, &k, _ctx); _res = Z3_ast_map_find(c, m, k); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_ast_map_insert( value _v_c, value _v_m, value _v_k, value _v_v) { Z3_context c; /*in*/ Z3_ast_map m; /*in*/ Z3_ast k; /*in*/ Z3_ast v; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast_map(_v_m, &m, _ctx); camlidl_ml2c_z3_Z3_ast(_v_k, &k, _ctx); camlidl_ml2c_z3_Z3_ast(_v_v, &v, _ctx); Z3_ast_map_insert(c, m, k, v); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_ast_map_erase( value _v_c, value _v_m, value _v_k) { Z3_context c; /*in*/ Z3_ast_map m; /*in*/ Z3_ast k; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast_map(_v_m, &m, _ctx); camlidl_ml2c_z3_Z3_ast(_v_k, &k, _ctx); Z3_ast_map_erase(c, m, k); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_ast_map_reset( value _v_c, value _v_m) { Z3_context c; /*in*/ Z3_ast_map m; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast_map(_v_m, &m, _ctx); Z3_ast_map_reset(c, m); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_ast_map_size( value _v_c, value _v_m) { Z3_context c; /*in*/ Z3_ast_map m; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast_map(_v_m, &m, _ctx); _res = Z3_ast_map_size(c, m); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_ast_map_keys( value _v_c, value _v_m) { Z3_context c; /*in*/ Z3_ast_map m; /*in*/ Z3_ast_vector _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast_map(_v_m, &m, _ctx); _res = Z3_ast_map_keys(c, m); _vres = camlidl_c2ml_z3_Z3_ast_vector(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_ast_map_to_string( value _v_c, value _v_m) { Z3_context c; /*in*/ Z3_ast_map m; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_ast_map(_v_m, &m, _ctx); _res = Z3_ast_map_to_string(c, m); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_goal( value _v_c, value _v_models, value _v_unsat_cores, value _v_proofs) { Z3_context c; /*in*/ int models; /*in*/ int unsat_cores; /*in*/ int proofs; /*in*/ Z3_goal _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); models = Int_val(_v_models); unsat_cores = Int_val(_v_unsat_cores); proofs = Int_val(_v_proofs); _res = Z3_mk_goal(c, models, unsat_cores, proofs); _vres = camlidl_c2ml_z3_Z3_goal(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_goal_precision( value _v_c, value _v_g) { Z3_context c; /*in*/ Z3_goal g; /*in*/ Z3_goal_prec _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); _res = Z3_goal_precision(c, g); _vres = camlidl_c2ml_z3_Z3_goal_prec(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_goal_assert( value _v_c, value _v_g, value _v_a) { Z3_context c; /*in*/ Z3_goal g; /*in*/ Z3_ast a; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); Z3_goal_assert(c, g, a); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_goal_inconsistent( value _v_c, value _v_g) { Z3_context c; /*in*/ Z3_goal g; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); _res = Z3_goal_inconsistent(c, g); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_goal_depth( value _v_c, value _v_g) { Z3_context c; /*in*/ Z3_goal g; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); _res = Z3_goal_depth(c, g); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_goal_reset( value _v_c, value _v_g) { Z3_context c; /*in*/ Z3_goal g; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); Z3_goal_reset(c, g); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_goal_size( value _v_c, value _v_g) { Z3_context c; /*in*/ Z3_goal g; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); _res = Z3_goal_size(c, g); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_goal_formula( value _v_c, value _v_g, value _v_idx) { Z3_context c; /*in*/ Z3_goal g; /*in*/ unsigned int idx; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); idx = Int_val(_v_idx); _res = Z3_goal_formula(c, g, idx); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_goal_num_exprs( value _v_c, value _v_g) { Z3_context c; /*in*/ Z3_goal g; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); _res = Z3_goal_num_exprs(c, g); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_goal_is_decided_sat( value _v_c, value _v_g) { Z3_context c; /*in*/ Z3_goal g; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); _res = Z3_goal_is_decided_sat(c, g); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_goal_is_decided_unsat( value _v_c, value _v_g) { Z3_context c; /*in*/ Z3_goal g; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); _res = Z3_goal_is_decided_unsat(c, g); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_goal_translate( value _v_source, value _v_g, value _v_target) { Z3_context source; /*in*/ Z3_goal g; /*in*/ Z3_context target; /*in*/ Z3_goal _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_source, &source, _ctx); camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); camlidl_ml2c_z3_Z3_context(_v_target, &target, _ctx); _res = Z3_goal_translate(source, g, target); _vres = camlidl_c2ml_z3_Z3_goal(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(source); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_goal_to_string( value _v_c, value _v_g) { Z3_context c; /*in*/ Z3_goal g; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); _res = Z3_goal_to_string(c, g); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_tactic( value _v_c, value _v_name) { Z3_context c; /*in*/ Z3_string name; /*in*/ Z3_tactic _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_name, &name, _ctx); _res = Z3_mk_tactic(c, name); _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_probe( value _v_c, value _v_name) { Z3_context c; /*in*/ Z3_string name; /*in*/ Z3_probe _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_name, &name, _ctx); _res = Z3_mk_probe(c, name); _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_and_then( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_tactic t1; /*in*/ Z3_tactic t2; /*in*/ Z3_tactic _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t2, &t2, _ctx); _res = Z3_tactic_and_then(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_or_else( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_tactic t1; /*in*/ Z3_tactic t2; /*in*/ Z3_tactic _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t2, &t2, _ctx); _res = Z3_tactic_or_else(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_par_or( value _v_c, value _v_ts) { Z3_context c; /*in*/ unsigned int num; /*in*/ Z3_tactic const *ts; /*in*/ Z3_tactic _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_ts); ts = camlidl_malloc(_c1 * sizeof(Z3_tactic const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_ts, _c2); camlidl_ml2c_z3_Z3_tactic(_v3, &ts[_c2], _ctx); } num = _c1; _res = Z3_tactic_par_or(c, num, ts); _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_par_and_then( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_tactic t1; /*in*/ Z3_tactic t2; /*in*/ Z3_tactic _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t2, &t2, _ctx); _res = Z3_tactic_par_and_then(c, t1, t2); _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_try_for( value _v_c, value _v_t, value _v_ms) { Z3_context c; /*in*/ Z3_tactic t; /*in*/ unsigned int ms; /*in*/ Z3_tactic _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); ms = Int_val(_v_ms); _res = Z3_tactic_try_for(c, t, ms); _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_when( value _v_c, value _v_p, value _v_t) { Z3_context c; /*in*/ Z3_probe p; /*in*/ Z3_tactic t; /*in*/ Z3_tactic _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p, &p, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); _res = Z3_tactic_when(c, p, t); _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_cond( value _v_c, value _v_p, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_probe p; /*in*/ Z3_tactic t1; /*in*/ Z3_tactic t2; /*in*/ Z3_tactic _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p, &p, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t1, &t1, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t2, &t2, _ctx); _res = Z3_tactic_cond(c, p, t1, t2); _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_repeat( value _v_c, value _v_t, value _v_max) { Z3_context c; /*in*/ Z3_tactic t; /*in*/ unsigned int max; /*in*/ Z3_tactic _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); max = Int_val(_v_max); _res = Z3_tactic_repeat(c, t, max); _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_skip( value _v_c) { Z3_context c; /*in*/ Z3_tactic _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_tactic_skip(c); _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_fail( value _v_c) { Z3_context c; /*in*/ Z3_tactic _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_tactic_fail(c); _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_fail_if( value _v_c, value _v_p) { Z3_context c; /*in*/ Z3_probe p; /*in*/ Z3_tactic _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p, &p, _ctx); _res = Z3_tactic_fail_if(c, p); _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_fail_if_not_decided( value _v_c) { Z3_context c; /*in*/ Z3_tactic _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_tactic_fail_if_not_decided(c); _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_using_params( value _v_c, value _v_t, value _v_p) { Z3_context c; /*in*/ Z3_tactic t; /*in*/ Z3_params p; /*in*/ Z3_tactic _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); _res = Z3_tactic_using_params(c, t, p); _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_probe_const( value _v_x, value _v_val) { Z3_context x; /*in*/ double val; /*in*/ Z3_probe _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); val = Double_val(_v_val); _res = Z3_probe_const(x, val); _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(x); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_probe_lt( value _v_x, value _v_p1, value _v_p2) { Z3_context x; /*in*/ Z3_probe p1; /*in*/ Z3_probe p2; /*in*/ Z3_probe _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p1, &p1, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p2, &p2, _ctx); _res = Z3_probe_lt(x, p1, p2); _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(x); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_probe_gt( value _v_x, value _v_p1, value _v_p2) { Z3_context x; /*in*/ Z3_probe p1; /*in*/ Z3_probe p2; /*in*/ Z3_probe _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p1, &p1, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p2, &p2, _ctx); _res = Z3_probe_gt(x, p1, p2); _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(x); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_probe_le( value _v_x, value _v_p1, value _v_p2) { Z3_context x; /*in*/ Z3_probe p1; /*in*/ Z3_probe p2; /*in*/ Z3_probe _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p1, &p1, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p2, &p2, _ctx); _res = Z3_probe_le(x, p1, p2); _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(x); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_probe_ge( value _v_x, value _v_p1, value _v_p2) { Z3_context x; /*in*/ Z3_probe p1; /*in*/ Z3_probe p2; /*in*/ Z3_probe _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p1, &p1, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p2, &p2, _ctx); _res = Z3_probe_ge(x, p1, p2); _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(x); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_probe_eq( value _v_x, value _v_p1, value _v_p2) { Z3_context x; /*in*/ Z3_probe p1; /*in*/ Z3_probe p2; /*in*/ Z3_probe _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p1, &p1, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p2, &p2, _ctx); _res = Z3_probe_eq(x, p1, p2); _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(x); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_probe_and( value _v_x, value _v_p1, value _v_p2) { Z3_context x; /*in*/ Z3_probe p1; /*in*/ Z3_probe p2; /*in*/ Z3_probe _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p1, &p1, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p2, &p2, _ctx); _res = Z3_probe_and(x, p1, p2); _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(x); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_probe_or( value _v_x, value _v_p1, value _v_p2) { Z3_context x; /*in*/ Z3_probe p1; /*in*/ Z3_probe p2; /*in*/ Z3_probe _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p1, &p1, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p2, &p2, _ctx); _res = Z3_probe_or(x, p1, p2); _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(x); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_probe_not( value _v_x, value _v_p) { Z3_context x; /*in*/ Z3_probe p; /*in*/ Z3_probe _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p, &p, _ctx); _res = Z3_probe_not(x, p); _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(x); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_num_tactics( value _v_c) { Z3_context c; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_num_tactics(c); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_tactic_name( value _v_c, value _v_i) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); _res = Z3_get_tactic_name(c, i); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_num_probes( value _v_c) { Z3_context c; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_num_probes(c); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_probe_name( value _v_c, value _v_i) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); _res = Z3_get_probe_name(c, i); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_get_help( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_tactic t; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); _res = Z3_tactic_get_help(c, t); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_get_param_descrs( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_tactic t; /*in*/ Z3_param_descrs _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); _res = Z3_tactic_get_param_descrs(c, t); _vres = camlidl_c2ml_z3_Z3_param_descrs(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_get_descr( value _v_c, value _v_name) { Z3_context c; /*in*/ Z3_string name; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_name, &name, _ctx); _res = Z3_tactic_get_descr(c, name); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_probe_get_descr( value _v_c, value _v_name) { Z3_context c; /*in*/ Z3_string name; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_string(_v_name, &name, _ctx); _res = Z3_probe_get_descr(c, name); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_probe_apply( value _v_c, value _v_p, value _v_g) { Z3_context c; /*in*/ Z3_probe p; /*in*/ Z3_goal g; /*in*/ double _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_probe(_v_p, &p, _ctx); camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); _res = Z3_probe_apply(c, p, g); _vres = copy_double(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_apply( value _v_c, value _v_t, value _v_g) { Z3_context c; /*in*/ Z3_tactic t; /*in*/ Z3_goal g; /*in*/ Z3_apply_result _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); _res = Z3_tactic_apply(c, t, g); _vres = camlidl_c2ml_z3_Z3_apply_result(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_tactic_apply_ex( value _v_c, value _v_t, value _v_g, value _v_p) { Z3_context c; /*in*/ Z3_tactic t; /*in*/ Z3_goal g; /*in*/ Z3_params p; /*in*/ Z3_apply_result _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); _res = Z3_tactic_apply_ex(c, t, g, p); _vres = camlidl_c2ml_z3_Z3_apply_result(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_apply_result_to_string( value _v_c, value _v_r) { Z3_context c; /*in*/ Z3_apply_result r; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_apply_result(_v_r, &r, _ctx); _res = Z3_apply_result_to_string(c, r); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_apply_result_get_num_subgoals( value _v_c, value _v_r) { Z3_context c; /*in*/ Z3_apply_result r; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_apply_result(_v_r, &r, _ctx); _res = Z3_apply_result_get_num_subgoals(c, r); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_apply_result_get_subgoal( value _v_c, value _v_r, value _v_i) { Z3_context c; /*in*/ Z3_apply_result r; /*in*/ unsigned int i; /*in*/ Z3_goal _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_apply_result(_v_r, &r, _ctx); i = Int_val(_v_i); _res = Z3_apply_result_get_subgoal(c, r, i); _vres = camlidl_c2ml_z3_Z3_goal(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_apply_result_convert_model( value _v_c, value _v_r, value _v_i, value _v_m) { Z3_context c; /*in*/ Z3_apply_result r; /*in*/ unsigned int i; /*in*/ Z3_model m; /*in*/ Z3_model _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_apply_result(_v_r, &r, _ctx); i = Int_val(_v_i); camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); _res = Z3_apply_result_convert_model(c, r, i, m); _vres = camlidl_c2ml_z3_Z3_model(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_solver( value _v_c) { Z3_context c; /*in*/ Z3_solver _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_mk_solver(c); _vres = camlidl_c2ml_z3_Z3_solver(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_simple_solver( value _v_c) { Z3_context c; /*in*/ Z3_solver _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); _res = Z3_mk_simple_solver(c); _vres = camlidl_c2ml_z3_Z3_solver(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_solver_for_logic( value _v_c, value _v_logic) { Z3_context c; /*in*/ Z3_symbol logic; /*in*/ Z3_solver _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_symbol(_v_logic, &logic, _ctx); _res = Z3_mk_solver_for_logic(c, logic); _vres = camlidl_c2ml_z3_Z3_solver(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_mk_solver_from_tactic( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_tactic t; /*in*/ Z3_solver _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); _res = Z3_mk_solver_from_tactic(c, t); _vres = camlidl_c2ml_z3_Z3_solver(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_solver_get_help( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_solver s; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); _res = Z3_solver_get_help(c, s); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_solver_get_param_descrs( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_solver s; /*in*/ Z3_param_descrs _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); _res = Z3_solver_get_param_descrs(c, s); _vres = camlidl_c2ml_z3_Z3_param_descrs(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_solver_set_params( value _v_c, value _v_s, value _v_p) { Z3_context c; /*in*/ Z3_solver s; /*in*/ Z3_params p; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); Z3_solver_set_params(c, s, p); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_solver_push( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_solver s; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); Z3_solver_push(c, s); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_solver_pop( value _v_c, value _v_s, value _v_n) { Z3_context c; /*in*/ Z3_solver s; /*in*/ unsigned int n; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); n = Int_val(_v_n); Z3_solver_pop(c, s, n); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_solver_reset( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_solver s; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); Z3_solver_reset(c, s); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_solver_get_num_scopes( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_solver s; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); _res = Z3_solver_get_num_scopes(c, s); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_solver_assert( value _v_c, value _v_s, value _v_a) { Z3_context c; /*in*/ Z3_solver s; /*in*/ Z3_ast a; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); Z3_solver_assert(c, s, a); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return Val_unit; } value camlidl_z3_Z3_solver_get_assertions( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_solver s; /*in*/ Z3_ast_vector _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); _res = Z3_solver_get_assertions(c, s); _vres = camlidl_c2ml_z3_Z3_ast_vector(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_solver_check( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_solver s; /*in*/ Z3_lbool _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); _res = Z3_solver_check(c, s); _vres = camlidl_c2ml_z3_Z3_lbool(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_solver_check_assumptions( value _v_c, value _v_s, value _v_assumptions) { Z3_context c; /*in*/ Z3_solver s; /*in*/ unsigned int num_assumptions; /*in*/ Z3_ast const *assumptions; /*in*/ Z3_lbool _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); _c1 = Wosize_val(_v_assumptions); assumptions = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_assumptions, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &assumptions[_c2], _ctx); } num_assumptions = _c1; _res = Z3_solver_check_assumptions(c, s, num_assumptions, assumptions); _vres = camlidl_c2ml_z3_Z3_lbool(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_solver_get_model( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_solver s; /*in*/ Z3_model _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); _res = Z3_solver_get_model(c, s); _vres = camlidl_c2ml_z3_Z3_model(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_solver_get_proof( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_solver s; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); _res = Z3_solver_get_proof(c, s); _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_solver_get_unsat_core( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_solver s; /*in*/ Z3_ast_vector _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); _res = Z3_solver_get_unsat_core(c, s); _vres = camlidl_c2ml_z3_Z3_ast_vector(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_solver_get_reason_unknown( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_solver s; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); _res = Z3_solver_get_reason_unknown(c, s); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_solver_get_statistics( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_solver s; /*in*/ Z3_stats _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); _res = Z3_solver_get_statistics(c, s); _vres = camlidl_c2ml_z3_Z3_stats(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_solver_to_string( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_solver s; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); _res = Z3_solver_to_string(c, s); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_stats_to_string( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_stats s; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_stats(_v_s, &s, _ctx); _res = Z3_stats_to_string(c, s); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_stats_size( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_stats s; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_stats(_v_s, &s, _ctx); _res = Z3_stats_size(c, s); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_stats_get_key( value _v_c, value _v_s, value _v_idx) { Z3_context c; /*in*/ Z3_stats s; /*in*/ unsigned int idx; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_stats(_v_s, &s, _ctx); idx = Int_val(_v_idx); _res = Z3_stats_get_key(c, s, idx); _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_stats_is_uint( value _v_c, value _v_s, value _v_idx) { Z3_context c; /*in*/ Z3_stats s; /*in*/ unsigned int idx; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_stats(_v_s, &s, _ctx); idx = Int_val(_v_idx); _res = Z3_stats_is_uint(c, s, idx); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_stats_is_double( value _v_c, value _v_s, value _v_idx) { Z3_context c; /*in*/ Z3_stats s; /*in*/ unsigned int idx; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_stats(_v_s, &s, _ctx); idx = Int_val(_v_idx); _res = Z3_stats_is_double(c, s, idx); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_stats_get_uint_value( value _v_c, value _v_s, value _v_idx) { Z3_context c; /*in*/ Z3_stats s; /*in*/ unsigned int idx; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_stats(_v_s, &s, _ctx); idx = Int_val(_v_idx); _res = Z3_stats_get_uint_value(c, s, idx); _vres = Val_int(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_stats_get_double_value( value _v_c, value _v_s, value _v_idx) { Z3_context c; /*in*/ Z3_stats s; /*in*/ unsigned int idx; /*in*/ double _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_stats(_v_s, &s, _ctx); idx = Int_val(_v_idx); _res = Z3_stats_get_double_value(c, s, idx); _vres = copy_double(_res); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3_Z3_get_implied_equalities( value _v_c, value _v_s, value _v_terms) { Z3_context c; /*in*/ Z3_solver s; /*in*/ unsigned int num_terms; /*in*/ Z3_ast const *terms; /*in*/ unsigned int *class_ids; /*out*/ Z3_lbool _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; value _v5; value _vresult; value _vres[2] = { 0, 0, }; camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); _c1 = Wosize_val(_v_terms); terms = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_terms, _c2); camlidl_ml2c_z3_Z3_ast(_v3, &terms[_c2], _ctx); } num_terms = _c1; class_ids = camlidl_malloc(num_terms * sizeof(unsigned int ), _ctx); _res = Z3_get_implied_equalities(c, s, num_terms, terms, class_ids); Begin_roots_block(_vres, 2) _vres[0] = camlidl_c2ml_z3_Z3_lbool(&_res, _ctx); _vres[1] = camlidl_alloc(num_terms, 0); for (_c4 = 0; _c4 < num_terms; _c4++) { _v5 = Val_int(class_ids[_c4]); modify(&Field(_vres[1], _c4), _v5); } _vresult = camlidl_alloc_small(2, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; End_roots() camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ check_error_code(c); /* end user-supplied deallocation sequence */ return _vresult; } void camlidl_ml2c_z3V3_Z3_symbol(value _v1, Z3_symbol * _c2, camlidl_ctx _ctx) { *_c2 = *((Z3_symbol *) Bp_val(_v1)); } value camlidl_c2ml_z3V3_Z3_symbol(Z3_symbol * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_alloc((sizeof(Z3_symbol) + sizeof(value) - 1) / sizeof(value), Abstract_tag); *((Z3_symbol *) Bp_val(_v1)) = *_c2; return _v1; } void camlidl_ml2c_z3V3_Z3_literals(value _v1, Z3_literals * _c2, camlidl_ctx _ctx) { *_c2 = *((Z3_literals *) Bp_val(_v1)); } value camlidl_c2ml_z3V3_Z3_literals(Z3_literals * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_alloc((sizeof(Z3_literals) + sizeof(value) - 1) / sizeof(value), Abstract_tag); *((Z3_literals *) Bp_val(_v1)) = *_c2; return _v1; } void camlidl_ml2c_z3V3_Z3_theory(value _v1, Z3_theory * _c2, camlidl_ctx _ctx) { *_c2 = *((Z3_theory *) Bp_val(_v1)); } value camlidl_c2ml_z3V3_Z3_theory(Z3_theory * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_alloc((sizeof(Z3_theory) + sizeof(value) - 1) / sizeof(value), Abstract_tag); *((Z3_theory *) Bp_val(_v1)) = *_c2; return _v1; } void camlidl_ml2c_z3V3_Z3_config(value _v1, Z3_config * _c2, camlidl_ctx _ctx) { *_c2 = *((Z3_config *) Bp_val(_v1)); } value camlidl_c2ml_z3V3_Z3_config(Z3_config * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_alloc((sizeof(Z3_config) + sizeof(value) - 1) / sizeof(value), Abstract_tag); *((Z3_config *) Bp_val(_v1)) = *_c2; return _v1; } void camlidl_ml2c_z3V3_Z3_context(value _v1, Z3_context * _c2, camlidl_ctx _ctx) { *_c2 = *((Z3_context *) Bp_val(_v1)); } value camlidl_c2ml_z3V3_Z3_context(Z3_context * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_alloc((sizeof(Z3_context) + sizeof(value) - 1) / sizeof(value), Abstract_tag); *((Z3_context *) Bp_val(_v1)) = *_c2; return _v1; } void camlidl_ml2c_z3V3_Z3_sort(value _v1, Z3_sort * _c2, camlidl_ctx _ctx) { *_c2 = *((Z3_sort *) Bp_val(_v1)); } value camlidl_c2ml_z3V3_Z3_sort(Z3_sort * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_alloc((sizeof(Z3_sort) + sizeof(value) - 1) / sizeof(value), Abstract_tag); *((Z3_sort *) Bp_val(_v1)) = *_c2; return _v1; } void camlidl_ml2c_z3V3_Z3_func_decl(value _v1, Z3_func_decl * _c2, camlidl_ctx _ctx) { *_c2 = *((Z3_func_decl *) Bp_val(_v1)); } value camlidl_c2ml_z3V3_Z3_func_decl(Z3_func_decl * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_alloc((sizeof(Z3_func_decl) + sizeof(value) - 1) / sizeof(value), Abstract_tag); *((Z3_func_decl *) Bp_val(_v1)) = *_c2; return _v1; } void camlidl_ml2c_z3V3_Z3_ast(value _v1, Z3_ast * _c2, camlidl_ctx _ctx) { *_c2 = *((Z3_ast *) Bp_val(_v1)); } value camlidl_c2ml_z3V3_Z3_ast(Z3_ast * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_alloc((sizeof(Z3_ast) + sizeof(value) - 1) / sizeof(value), Abstract_tag); *((Z3_ast *) Bp_val(_v1)) = *_c2; return _v1; } void camlidl_ml2c_z3V3_Z3_app(value _v1, Z3_app * _c2, camlidl_ctx _ctx) { *_c2 = *((Z3_app *) Bp_val(_v1)); } value camlidl_c2ml_z3V3_Z3_app(Z3_app * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_alloc((sizeof(Z3_app) + sizeof(value) - 1) / sizeof(value), Abstract_tag); *((Z3_app *) Bp_val(_v1)) = *_c2; return _v1; } void camlidl_ml2c_z3V3_Z3_pattern(value _v1, Z3_pattern * _c2, camlidl_ctx _ctx) { *_c2 = *((Z3_pattern *) Bp_val(_v1)); } value camlidl_c2ml_z3V3_Z3_pattern(Z3_pattern * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_alloc((sizeof(Z3_pattern) + sizeof(value) - 1) / sizeof(value), Abstract_tag); *((Z3_pattern *) Bp_val(_v1)) = *_c2; return _v1; } void camlidl_ml2c_z3V3_Z3_model(value _v1, Z3_model * _c2, camlidl_ctx _ctx) { *_c2 = *((Z3_model *) Bp_val(_v1)); } value camlidl_c2ml_z3V3_Z3_model(Z3_model * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_alloc((sizeof(Z3_model) + sizeof(value) - 1) / sizeof(value), Abstract_tag); *((Z3_model *) Bp_val(_v1)) = *_c2; return _v1; } void camlidl_ml2c_z3V3_Z3_constructor(value _v1, Z3_constructor * _c2, camlidl_ctx _ctx) { *_c2 = *((Z3_constructor *) Bp_val(_v1)); } value camlidl_c2ml_z3V3_Z3_constructor(Z3_constructor * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_alloc((sizeof(Z3_constructor) + sizeof(value) - 1) / sizeof(value), Abstract_tag); *((Z3_constructor *) Bp_val(_v1)) = *_c2; return _v1; } void camlidl_ml2c_z3V3_Z3_constructor_list(value _v1, Z3_constructor_list * _c2, camlidl_ctx _ctx) { *_c2 = *((Z3_constructor_list *) Bp_val(_v1)); } value camlidl_c2ml_z3V3_Z3_constructor_list(Z3_constructor_list * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_alloc((sizeof(Z3_constructor_list) + sizeof(value) - 1) / sizeof(value), Abstract_tag); *((Z3_constructor_list *) Bp_val(_v1)) = *_c2; return _v1; } void camlidl_ml2c_z3V3_Z3_string(value _v1, Z3_string * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_malloc_string(_v1, _ctx); } value camlidl_c2ml_z3V3_Z3_string(Z3_string * _c2, camlidl_ctx _ctx) { value _v1; _v1 = copy_string((*_c2)); return _v1; } int camlidl_transl_table_z3V3_enum_1[3] = { Z3_L_FALSE, Z3_L_UNDEF, Z3_L_TRUE, }; void camlidl_ml2c_z3V3_Z3_lbool(value _v1, Z3_lbool * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3V3_enum_1[Int_val(_v1)]; } value camlidl_c2ml_z3V3_Z3_lbool(Z3_lbool * _c2, camlidl_ctx _ctx) { value _v1; switch((*_c2)) { case Z3_L_FALSE: _v1 = Val_int(0); break; case Z3_L_UNDEF: _v1 = Val_int(1); break; case Z3_L_TRUE: _v1 = Val_int(2); break; default: invalid_argument("typedef Z3_lbool: bad enum value"); } return _v1; } int camlidl_transl_table_z3V3_enum_2[2] = { Z3_INT_SYMBOL, Z3_STRING_SYMBOL, }; void camlidl_ml2c_z3V3_Z3_symbol_kind(value _v1, Z3_symbol_kind * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3V3_enum_2[Int_val(_v1)]; } value camlidl_c2ml_z3V3_Z3_symbol_kind(Z3_symbol_kind * _c2, camlidl_ctx _ctx) { value _v1; switch((*_c2)) { case Z3_INT_SYMBOL: _v1 = Val_int(0); break; case Z3_STRING_SYMBOL: _v1 = Val_int(1); break; default: invalid_argument("typedef Z3_symbol_kind: bad enum value"); } return _v1; } int camlidl_transl_table_z3V3_enum_3[7] = { Z3_PARAMETER_INT, Z3_PARAMETER_DOUBLE, Z3_PARAMETER_RATIONAL, Z3_PARAMETER_SYMBOL, Z3_PARAMETER_SORT, Z3_PARAMETER_AST, Z3_PARAMETER_FUNC_DECL, }; void camlidl_ml2c_z3V3_Z3_parameter_kind(value _v1, Z3_parameter_kind * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3V3_enum_3[Int_val(_v1)]; } value camlidl_c2ml_z3V3_Z3_parameter_kind(Z3_parameter_kind * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3V3_enum_3, 7, "typedef Z3_parameter_kind: bad enum value"); return _v1; } int camlidl_transl_table_z3V3_enum_4[10] = { Z3_UNINTERPRETED_SORT, Z3_BOOL_SORT, Z3_INT_SORT, Z3_REAL_SORT, Z3_BV_SORT, Z3_ARRAY_SORT, Z3_DATATYPE_SORT, Z3_RELATION_SORT, Z3_FINITE_DOMAIN_SORT, Z3_UNKNOWN_SORT, }; void camlidl_ml2c_z3V3_Z3_sort_kind(value _v1, Z3_sort_kind * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3V3_enum_4[Int_val(_v1)]; } value camlidl_c2ml_z3V3_Z3_sort_kind(Z3_sort_kind * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3V3_enum_4, 10, "typedef Z3_sort_kind: bad enum value"); return _v1; } int camlidl_transl_table_z3V3_enum_5[7] = { Z3_NUMERAL_AST, Z3_APP_AST, Z3_VAR_AST, Z3_QUANTIFIER_AST, Z3_SORT_AST, Z3_FUNC_DECL_AST, Z3_UNKNOWN_AST, }; void camlidl_ml2c_z3V3_Z3_ast_kind(value _v1, Z3_ast_kind * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3V3_enum_5[Int_val(_v1)]; } value camlidl_c2ml_z3V3_Z3_ast_kind(Z3_ast_kind * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3V3_enum_5, 7, "typedef Z3_ast_kind: bad enum value"); return _v1; } int camlidl_transl_table_z3V3_enum_6[152] = { Z3_OP_TRUE, Z3_OP_FALSE, Z3_OP_EQ, Z3_OP_DISTINCT, Z3_OP_ITE, Z3_OP_AND, Z3_OP_OR, Z3_OP_IFF, Z3_OP_XOR, Z3_OP_NOT, Z3_OP_IMPLIES, Z3_OP_OEQ, Z3_OP_ANUM, Z3_OP_AGNUM, Z3_OP_LE, Z3_OP_GE, Z3_OP_LT, Z3_OP_GT, Z3_OP_ADD, Z3_OP_SUB, Z3_OP_UMINUS, Z3_OP_MUL, Z3_OP_DIV, Z3_OP_IDIV, Z3_OP_REM, Z3_OP_MOD, Z3_OP_TO_REAL, Z3_OP_TO_INT, Z3_OP_IS_INT, Z3_OP_POWER, Z3_OP_STORE, Z3_OP_SELECT, Z3_OP_CONST_ARRAY, Z3_OP_ARRAY_MAP, Z3_OP_ARRAY_DEFAULT, Z3_OP_SET_UNION, Z3_OP_SET_INTERSECT, Z3_OP_SET_DIFFERENCE, Z3_OP_SET_COMPLEMENT, Z3_OP_SET_SUBSET, Z3_OP_AS_ARRAY, Z3_OP_BNUM, Z3_OP_BIT1, Z3_OP_BIT0, Z3_OP_BNEG, Z3_OP_BADD, Z3_OP_BSUB, Z3_OP_BMUL, Z3_OP_BSDIV, Z3_OP_BUDIV, Z3_OP_BSREM, Z3_OP_BUREM, Z3_OP_BSMOD, Z3_OP_BSDIV0, Z3_OP_BUDIV0, Z3_OP_BSREM0, Z3_OP_BUREM0, Z3_OP_BSMOD0, Z3_OP_ULEQ, Z3_OP_SLEQ, Z3_OP_UGEQ, Z3_OP_SGEQ, Z3_OP_ULT, Z3_OP_SLT, Z3_OP_UGT, Z3_OP_SGT, Z3_OP_BAND, Z3_OP_BOR, Z3_OP_BNOT, Z3_OP_BXOR, Z3_OP_BNAND, Z3_OP_BNOR, Z3_OP_BXNOR, Z3_OP_CONCAT, Z3_OP_SIGN_EXT, Z3_OP_ZERO_EXT, Z3_OP_EXTRACT, Z3_OP_REPEAT, Z3_OP_BREDOR, Z3_OP_BREDAND, Z3_OP_BCOMP, Z3_OP_BSHL, Z3_OP_BLSHR, Z3_OP_BASHR, Z3_OP_ROTATE_LEFT, Z3_OP_ROTATE_RIGHT, Z3_OP_EXT_ROTATE_LEFT, Z3_OP_EXT_ROTATE_RIGHT, Z3_OP_INT2BV, Z3_OP_BV2INT, Z3_OP_CARRY, Z3_OP_XOR3, Z3_OP_PR_UNDEF, Z3_OP_PR_TRUE, Z3_OP_PR_ASSERTED, Z3_OP_PR_GOAL, Z3_OP_PR_MODUS_PONENS, Z3_OP_PR_REFLEXIVITY, Z3_OP_PR_SYMMETRY, Z3_OP_PR_TRANSITIVITY, Z3_OP_PR_TRANSITIVITY_STAR, Z3_OP_PR_MONOTONICITY, Z3_OP_PR_QUANT_INTRO, Z3_OP_PR_DISTRIBUTIVITY, Z3_OP_PR_AND_ELIM, Z3_OP_PR_NOT_OR_ELIM, Z3_OP_PR_REWRITE, Z3_OP_PR_REWRITE_STAR, Z3_OP_PR_PULL_QUANT, Z3_OP_PR_PULL_QUANT_STAR, Z3_OP_PR_PUSH_QUANT, Z3_OP_PR_ELIM_UNUSED_VARS, Z3_OP_PR_DER, Z3_OP_PR_QUANT_INST, Z3_OP_PR_HYPOTHESIS, Z3_OP_PR_LEMMA, Z3_OP_PR_UNIT_RESOLUTION, Z3_OP_PR_IFF_TRUE, Z3_OP_PR_IFF_FALSE, Z3_OP_PR_COMMUTATIVITY, Z3_OP_PR_DEF_AXIOM, Z3_OP_PR_DEF_INTRO, Z3_OP_PR_APPLY_DEF, Z3_OP_PR_IFF_OEQ, Z3_OP_PR_NNF_POS, Z3_OP_PR_NNF_NEG, Z3_OP_PR_NNF_STAR, Z3_OP_PR_CNF_STAR, Z3_OP_PR_SKOLEMIZE, Z3_OP_PR_MODUS_PONENS_OEQ, Z3_OP_PR_TH_LEMMA, Z3_OP_PR_HYPER_RESOLVE, Z3_OP_RA_STORE, Z3_OP_RA_EMPTY, Z3_OP_RA_IS_EMPTY, Z3_OP_RA_JOIN, Z3_OP_RA_UNION, Z3_OP_RA_WIDEN, Z3_OP_RA_PROJECT, Z3_OP_RA_FILTER, Z3_OP_RA_NEGATION_FILTER, Z3_OP_RA_RENAME, Z3_OP_RA_COMPLEMENT, Z3_OP_RA_SELECT, Z3_OP_RA_CLONE, Z3_OP_FD_LT, Z3_OP_LABEL, Z3_OP_LABEL_LIT, Z3_OP_DT_CONSTRUCTOR, Z3_OP_DT_RECOGNISER, Z3_OP_DT_ACCESSOR, Z3_OP_UNINTERPRETED, }; void camlidl_ml2c_z3V3_Z3_decl_kind(value _v1, Z3_decl_kind * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3V3_enum_6[Int_val(_v1)]; } value camlidl_c2ml_z3V3_Z3_decl_kind(Z3_decl_kind * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3V3_enum_6, 152, "typedef Z3_decl_kind: bad enum value"); return _v1; } int camlidl_transl_table_z3V3_enum_7[7] = { Z3_PK_UINT, Z3_PK_BOOL, Z3_PK_DOUBLE, Z3_PK_SYMBOL, Z3_PK_STRING, Z3_PK_OTHER, Z3_PK_INVALID, }; void camlidl_ml2c_z3V3_Z3_param_kind(value _v1, Z3_param_kind * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3V3_enum_7[Int_val(_v1)]; } value camlidl_c2ml_z3V3_Z3_param_kind(Z3_param_kind * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3V3_enum_7, 7, "typedef Z3_param_kind: bad enum value"); return _v1; } int camlidl_transl_table_z3V3_enum_8[8] = { Z3_NO_FAILURE, Z3_UNKNOWN, Z3_TIMEOUT, Z3_MEMOUT_WATERMARK, Z3_CANCELED, Z3_NUM_CONFLICTS, Z3_THEORY, Z3_QUANTIFIERS, }; void camlidl_ml2c_z3V3_Z3_search_failure(value _v1, Z3_search_failure * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3V3_enum_8[Int_val(_v1)]; } value camlidl_c2ml_z3V3_Z3_search_failure(Z3_search_failure * _c2, camlidl_ctx _ctx) { value _v1; _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3V3_enum_8, 8, "typedef Z3_search_failure: bad enum value"); return _v1; } int camlidl_transl_table_z3V3_enum_9[4] = { Z3_PRINT_SMTLIB_FULL, Z3_PRINT_LOW_LEVEL, Z3_PRINT_SMTLIB_COMPLIANT, Z3_PRINT_SMTLIB2_COMPLIANT, }; void camlidl_ml2c_z3V3_Z3_ast_print_mode(value _v1, Z3_ast_print_mode * _c2, camlidl_ctx _ctx) { (*_c2) = camlidl_transl_table_z3V3_enum_9[Int_val(_v1)]; } value camlidl_c2ml_z3V3_Z3_ast_print_mode(Z3_ast_print_mode * _c2, camlidl_ctx _ctx) { value _v1; switch((*_c2)) { case Z3_PRINT_SMTLIB_FULL: _v1 = Val_int(0); break; case Z3_PRINT_LOW_LEVEL: _v1 = Val_int(1); break; case Z3_PRINT_SMTLIB_COMPLIANT: _v1 = Val_int(2); break; case Z3_PRINT_SMTLIB2_COMPLIANT: _v1 = Val_int(3); break; default: invalid_argument("typedef Z3_ast_print_mode: bad enum value"); } return _v1; } value camlidl_z3V3_Z3_mk_config(value _unit) { Z3_config _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; _res = Z3_mk_config(); _vres = camlidl_c2ml_z3V3_Z3_config(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_del_config( value _v_c) { Z3_config c; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_config(_v_c, &c, _ctx); Z3_del_config(c); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_set_param_value( value _v_c, value _v_param_id, value _v_param_value) { Z3_config c; /*in*/ Z3_string param_id; /*in*/ Z3_string param_value; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_config(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_param_id, ¶m_id, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_param_value, ¶m_value, _ctx); Z3_set_param_value(c, param_id, param_value); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_mk_context( value _v_c) { Z3_config c; /*in*/ Z3_context _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_config(_v_c, &c, _ctx); _res = Z3_mk_context(c); _vres = camlidl_c2ml_z3V3_Z3_context(&_res, _ctx); camlidl_free(_ctx); /* begin user-supplied deallocation sequence */ Z3_set_error_handler(_res, caml_z3_error_handler); /* end user-supplied deallocation sequence */ return _vres; } value camlidl_z3V3_Z3_del_context( value _v_c) { Z3_context c; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); Z3_del_context(c); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_update_param_value( value _v_c, value _v_param_id, value _v_param_value) { Z3_context c; /*in*/ Z3_string param_id; /*in*/ Z3_string param_value; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_param_id, ¶m_id, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_param_value, ¶m_value, _ctx); Z3_update_param_value(c, param_id, param_value); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_get_param_value( value _v_c, value _v_param_id) { Z3_context c; /*in*/ Z3_string param_id; /*in*/ Z3_string *param_value; /*out*/ Z3_string _c1; value _v2; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_param_id, ¶m_id, _ctx); param_value = &_c1; Z3_get_param_value(c, param_id, param_value); if (param_value == NULL) { _vres = Val_int(0); } else { _v2 = camlidl_c2ml_z3V3_Z3_string(&*param_value, _ctx); Begin_root(_v2) _vres = camlidl_alloc_small(1, 0); Field(_vres, 0) = _v2; End_roots(); } camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_int_symbol( value _v_c, value _v_i) { Z3_context c; /*in*/ int i; /*in*/ Z3_symbol _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); _res = Z3_mk_int_symbol(c, i); _vres = camlidl_c2ml_z3V3_Z3_symbol(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_string_symbol( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_string s; /*in*/ Z3_symbol _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_s, &s, _ctx); _res = Z3_mk_string_symbol(c, s); _vres = camlidl_c2ml_z3V3_Z3_symbol(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_uninterpreted_sort( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_symbol s; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); _res = Z3_mk_uninterpreted_sort(c, s); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bool_sort( value _v_c) { Z3_context c; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_mk_bool_sort(c); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_int_sort( value _v_c) { Z3_context c; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_mk_int_sort(c); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_real_sort( value _v_c) { Z3_context c; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_mk_real_sort(c); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bv_sort( value _v_c, value _v_sz) { Z3_context c; /*in*/ unsigned int sz; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); sz = Int_val(_v_sz); _res = Z3_mk_bv_sort(c, sz); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_finite_domain_sort( value _v_c, value _v_name, value _v_size) { Z3_context c; /*in*/ Z3_symbol name; /*in*/ __int64 size; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_name, &name, _ctx); size = Int64_val(_v_size); _res = Z3_mk_finite_domain_sort(c, name, size); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_array_sort( value _v_c, value _v_domain, value _v_range) { Z3_context c; /*in*/ Z3_sort domain; /*in*/ Z3_sort range; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_domain, &domain, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_range, &range, _ctx); _res = Z3_mk_array_sort(c, domain, range); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_tuple_sort( value _v_c, value _v_mk_tuple_name, value _v_field_names, value _v_field_sorts) { Z3_context c; /*in*/ Z3_symbol mk_tuple_name; /*in*/ unsigned int num_fields; /*in*/ Z3_symbol const *field_names; /*in*/ Z3_sort const *field_sorts; /*in*/ Z3_func_decl *mk_tuple_decl; /*out*/ Z3_func_decl *proj_decl; /*out*/ Z3_sort _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; Z3_func_decl _c7; mlsize_t _c8; value _v9; value _vresult; value _vres[3] = { 0, 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_mk_tuple_name, &mk_tuple_name, _ctx); _c1 = Wosize_val(_v_field_names); field_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_field_names, _c2); camlidl_ml2c_z3V3_Z3_symbol(_v3, &field_names[_c2], _ctx); } num_fields = _c1; _c4 = Wosize_val(_v_field_sorts); field_sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_field_sorts, _c5); camlidl_ml2c_z3V3_Z3_sort(_v6, &field_sorts[_c5], _ctx); } num_fields = _c4; mk_tuple_decl = &_c7; proj_decl = camlidl_malloc(num_fields * sizeof(Z3_func_decl ), _ctx); _res = Z3_mk_tuple_sort(c, mk_tuple_name, num_fields, field_names, field_sorts, mk_tuple_decl, proj_decl); Begin_roots_block(_vres, 3) _vres[0] = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); _vres[1] = camlidl_c2ml_z3V3_Z3_func_decl(&*mk_tuple_decl, _ctx); _vres[2] = camlidl_alloc(num_fields, 0); Begin_root(_vres[2]) for (_c8 = 0; _c8 < num_fields; _c8++) { _v9 = camlidl_c2ml_z3V3_Z3_func_decl(&proj_decl[_c8], _ctx); modify(&Field(_vres[2], _c8), _v9); } End_roots() _vresult = camlidl_alloc_small(3, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; Field(_vresult, 2) = _vres[2]; End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_mk_enumeration_sort( value _v_c, value _v_name, value _v_enum_names) { Z3_context c; /*in*/ Z3_symbol name; /*in*/ unsigned int n; /*in*/ Z3_symbol const *enum_names; /*in*/ Z3_func_decl *enum_consts; /*out*/ Z3_func_decl *enum_testers; /*out*/ Z3_sort _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; value _v5; mlsize_t _c6; value _v7; value _vresult; value _vres[3] = { 0, 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_name, &name, _ctx); _c1 = Wosize_val(_v_enum_names); enum_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_enum_names, _c2); camlidl_ml2c_z3V3_Z3_symbol(_v3, &enum_names[_c2], _ctx); } n = _c1; enum_consts = camlidl_malloc(n * sizeof(Z3_func_decl ), _ctx); enum_testers = camlidl_malloc(n * sizeof(Z3_func_decl ), _ctx); _res = Z3_mk_enumeration_sort(c, name, n, enum_names, enum_consts, enum_testers); Begin_roots_block(_vres, 3) _vres[0] = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); _vres[1] = camlidl_alloc(n, 0); Begin_root(_vres[1]) for (_c4 = 0; _c4 < n; _c4++) { _v5 = camlidl_c2ml_z3V3_Z3_func_decl(&enum_consts[_c4], _ctx); modify(&Field(_vres[1], _c4), _v5); } End_roots() _vres[2] = camlidl_alloc(n, 0); Begin_root(_vres[2]) for (_c6 = 0; _c6 < n; _c6++) { _v7 = camlidl_c2ml_z3V3_Z3_func_decl(&enum_testers[_c6], _ctx); modify(&Field(_vres[2], _c6), _v7); } End_roots() _vresult = camlidl_alloc_small(3, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; Field(_vresult, 2) = _vres[2]; End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_mk_list_sort( value _v_c, value _v_name, value _v_elem_sort) { Z3_context c; /*in*/ Z3_symbol name; /*in*/ Z3_sort elem_sort; /*in*/ Z3_func_decl *nil_decl; /*out*/ Z3_func_decl *is_nil_decl; /*out*/ Z3_func_decl *cons_decl; /*out*/ Z3_func_decl *is_cons_decl; /*out*/ Z3_func_decl *head_decl; /*out*/ Z3_func_decl *tail_decl; /*out*/ Z3_sort _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; Z3_func_decl _c1; Z3_func_decl _c2; Z3_func_decl _c3; Z3_func_decl _c4; Z3_func_decl _c5; Z3_func_decl _c6; value _vresult; value _vres[7] = { 0, 0, 0, 0, 0, 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_name, &name, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_elem_sort, &elem_sort, _ctx); nil_decl = &_c1; is_nil_decl = &_c2; cons_decl = &_c3; is_cons_decl = &_c4; head_decl = &_c5; tail_decl = &_c6; _res = Z3_mk_list_sort(c, name, elem_sort, nil_decl, is_nil_decl, cons_decl, is_cons_decl, head_decl, tail_decl); Begin_roots_block(_vres, 7) _vres[0] = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); _vres[1] = camlidl_c2ml_z3V3_Z3_func_decl(&*nil_decl, _ctx); _vres[2] = camlidl_c2ml_z3V3_Z3_func_decl(&*is_nil_decl, _ctx); _vres[3] = camlidl_c2ml_z3V3_Z3_func_decl(&*cons_decl, _ctx); _vres[4] = camlidl_c2ml_z3V3_Z3_func_decl(&*is_cons_decl, _ctx); _vres[5] = camlidl_c2ml_z3V3_Z3_func_decl(&*head_decl, _ctx); _vres[6] = camlidl_c2ml_z3V3_Z3_func_decl(&*tail_decl, _ctx); _vresult = camlidl_alloc_small(7, 0); { mlsize_t _c7; for (_c7 = 0; _c7 < 7; _c7++) Field(_vresult, _c7) = _vres[_c7]; } End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_mk_constructor( value _v_c, value _v_name, value _v_recognizer, value _v_field_names, value _v_sorts, value _v_sort_refs) { Z3_context c; /*in*/ Z3_symbol name; /*in*/ Z3_symbol recognizer; /*in*/ unsigned int num_fields; /*in*/ Z3_symbol const *field_names; /*in*/ Z3_sort const *sorts; /*in*/ unsigned int *sort_refs; /*in*/ Z3_constructor _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_name, &name, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_recognizer, &recognizer, _ctx); _c1 = Wosize_val(_v_field_names); field_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_field_names, _c2); camlidl_ml2c_z3V3_Z3_symbol(_v3, &field_names[_c2], _ctx); } num_fields = _c1; _c4 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_sorts, _c5); camlidl_ml2c_z3V3_Z3_sort(_v6, &sorts[_c5], _ctx); } num_fields = _c4; _c7 = Wosize_val(_v_sort_refs); sort_refs = camlidl_malloc(_c7 * sizeof(unsigned int ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_sort_refs, _c8); sort_refs[_c8] = Int_val(_v9); } num_fields = _c7; _res = Z3_mk_constructor(c, name, recognizer, num_fields, field_names, sorts, sort_refs); _vres = camlidl_c2ml_z3V3_Z3_constructor(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_constructor_bytecode(value * argv, int argn) { return camlidl_z3V3_Z3_mk_constructor(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } value camlidl_z3V3_Z3_del_constructor( value _v_c, value _v_constr) { Z3_context c; /*in*/ Z3_constructor constr; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_constructor(_v_constr, &constr, _ctx); Z3_del_constructor(c, constr); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_mk_datatype( value _v_c, value _v_name, value _v_constructors) { Z3_context c; /*in*/ Z3_symbol name; /*in*/ unsigned int num_constructors; /*in*/ Z3_constructor *constructors; /*in,out*/ Z3_sort _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; value _v5; value _vresult; value _vres[2] = { 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_name, &name, _ctx); _c1 = Wosize_val(_v_constructors); constructors = camlidl_malloc(_c1 * sizeof(Z3_constructor ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_constructors, _c2); camlidl_ml2c_z3V3_Z3_constructor(_v3, &constructors[_c2], _ctx); } num_constructors = _c1; _res = Z3_mk_datatype(c, name, num_constructors, constructors); Begin_roots_block(_vres, 2) _vres[0] = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); _vres[1] = camlidl_alloc(num_constructors, 0); Begin_root(_vres[1]) for (_c4 = 0; _c4 < num_constructors; _c4++) { _v5 = camlidl_c2ml_z3V3_Z3_constructor(&constructors[_c4], _ctx); modify(&Field(_vres[1], _c4), _v5); } End_roots() _vresult = camlidl_alloc_small(2, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_mk_constructor_list( value _v_c, value _v_constructors) { Z3_context c; /*in*/ unsigned int num_constructors; /*in*/ Z3_constructor const *constructors; /*in*/ Z3_constructor_list _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_constructors); constructors = camlidl_malloc(_c1 * sizeof(Z3_constructor const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_constructors, _c2); camlidl_ml2c_z3V3_Z3_constructor(_v3, &constructors[_c2], _ctx); } num_constructors = _c1; _res = Z3_mk_constructor_list(c, num_constructors, constructors); _vres = camlidl_c2ml_z3V3_Z3_constructor_list(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_del_constructor_list( value _v_c, value _v_clist) { Z3_context c; /*in*/ Z3_constructor_list clist; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_constructor_list(_v_clist, &clist, _ctx); Z3_del_constructor_list(c, clist); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_mk_datatypes( value _v_c, value _v_sort_names, value _v_constructor_lists) { Z3_context c; /*in*/ unsigned int num_sorts; /*in*/ Z3_symbol const *sort_names; /*in*/ Z3_sort *sorts; /*out*/ Z3_constructor_list *constructor_lists; /*in,out*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; value _v8; mlsize_t _c9; value _v10; value _vresult; value _vres[2] = { 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_sort_names); sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_sort_names, _c2); camlidl_ml2c_z3V3_Z3_symbol(_v3, &sort_names[_c2], _ctx); } num_sorts = _c1; _c4 = Wosize_val(_v_constructor_lists); constructor_lists = camlidl_malloc(_c4 * sizeof(Z3_constructor_list ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_constructor_lists, _c5); camlidl_ml2c_z3V3_Z3_constructor_list(_v6, &constructor_lists[_c5], _ctx); } num_sorts = _c4; sorts = camlidl_malloc(num_sorts * sizeof(Z3_sort ), _ctx); Z3_mk_datatypes(c, num_sorts, sort_names, sorts, constructor_lists); Begin_roots_block(_vres, 2) _vres[0] = camlidl_alloc(num_sorts, 0); Begin_root(_vres[0]) for (_c7 = 0; _c7 < num_sorts; _c7++) { _v8 = camlidl_c2ml_z3V3_Z3_sort(&sorts[_c7], _ctx); modify(&Field(_vres[0], _c7), _v8); } End_roots() _vres[1] = camlidl_alloc(num_sorts, 0); Begin_root(_vres[1]) for (_c9 = 0; _c9 < num_sorts; _c9++) { _v10 = camlidl_c2ml_z3V3_Z3_constructor_list(&constructor_lists[_c9], _ctx); modify(&Field(_vres[1], _c9), _v10); } End_roots() _vresult = camlidl_alloc_small(2, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_query_constructor( value _v_c, value _v_constr, value _v_num_fields) { Z3_context c; /*in*/ Z3_constructor constr; /*in*/ unsigned int num_fields; /*in*/ Z3_func_decl *constructor; /*out*/ Z3_func_decl *tester; /*out*/ Z3_func_decl *accessors; /*out*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; Z3_func_decl _c1; Z3_func_decl _c2; mlsize_t _c3; value _v4; value _vresult; value _vres[3] = { 0, 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_constructor(_v_constr, &constr, _ctx); num_fields = Int_val(_v_num_fields); constructor = &_c1; tester = &_c2; accessors = camlidl_malloc(num_fields * sizeof(Z3_func_decl ), _ctx); Z3_query_constructor(c, constr, num_fields, constructor, tester, accessors); Begin_roots_block(_vres, 3) _vres[0] = camlidl_c2ml_z3V3_Z3_func_decl(&*constructor, _ctx); _vres[1] = camlidl_c2ml_z3V3_Z3_func_decl(&*tester, _ctx); _vres[2] = camlidl_alloc(num_fields, 0); Begin_root(_vres[2]) for (_c3 = 0; _c3 < num_fields; _c3++) { _v4 = camlidl_c2ml_z3V3_Z3_func_decl(&accessors[_c3], _ctx); modify(&Field(_vres[2], _c3), _v4); } End_roots() _vresult = camlidl_alloc_small(3, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; Field(_vresult, 2) = _vres[2]; End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_mk_func_decl( value _v_c, value _v_s, value _v_domain, value _v_range) { Z3_context c; /*in*/ Z3_symbol s; /*in*/ unsigned int domain_size; /*in*/ Z3_sort const *domain; /*in*/ Z3_sort range; /*in*/ Z3_func_decl _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); _c1 = Wosize_val(_v_domain); domain = camlidl_malloc(_c1 * sizeof(Z3_sort const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_domain, _c2); camlidl_ml2c_z3V3_Z3_sort(_v3, &domain[_c2], _ctx); } domain_size = _c1; camlidl_ml2c_z3V3_Z3_sort(_v_range, &range, _ctx); _res = Z3_mk_func_decl(c, s, domain_size, domain, range); _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_app( value _v_c, value _v_d, value _v_args) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_app(c, d, num_args, args); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_const( value _v_c, value _v_s, value _v_ty) { Z3_context c; /*in*/ Z3_symbol s; /*in*/ Z3_sort ty; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_ty, &ty, _ctx); _res = Z3_mk_const(c, s, ty); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_fresh_func_decl( value _v_c, value _v_prefix, value _v_domain, value _v_range) { Z3_context c; /*in*/ Z3_string prefix; /*in*/ unsigned int domain_size; /*in*/ Z3_sort const *domain; /*in*/ Z3_sort range; /*in*/ Z3_func_decl _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_prefix, &prefix, _ctx); _c1 = Wosize_val(_v_domain); domain = camlidl_malloc(_c1 * sizeof(Z3_sort const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_domain, _c2); camlidl_ml2c_z3V3_Z3_sort(_v3, &domain[_c2], _ctx); } domain_size = _c1; camlidl_ml2c_z3V3_Z3_sort(_v_range, &range, _ctx); _res = Z3_mk_fresh_func_decl(c, prefix, domain_size, domain, range); _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_fresh_const( value _v_c, value _v_prefix, value _v_ty) { Z3_context c; /*in*/ Z3_string prefix; /*in*/ Z3_sort ty; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_prefix, &prefix, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_ty, &ty, _ctx); _res = Z3_mk_fresh_const(c, prefix, ty); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_true( value _v_c) { Z3_context c; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_mk_true(c); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_false( value _v_c) { Z3_context c; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_mk_false(c); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_eq( value _v_c, value _v_l, value _v_r) { Z3_context c; /*in*/ Z3_ast l; /*in*/ Z3_ast r; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_l, &l, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_r, &r, _ctx); _res = Z3_mk_eq(c, l, r); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_distinct( value _v_c, value _v_args) { Z3_context c; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_distinct(c, num_args, args); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_not( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_mk_not(c, a); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_ite( value _v_c, value _v_t1, value _v_t2, value _v_t3) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast t3; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t3, &t3, _ctx); _res = Z3_mk_ite(c, t1, t2, t3); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_iff( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_iff(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_implies( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_implies(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_xor( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_xor(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_and( value _v_c, value _v_args) { Z3_context c; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_and(c, num_args, args); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_or( value _v_c, value _v_args) { Z3_context c; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_or(c, num_args, args); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_add( value _v_c, value _v_args) { Z3_context c; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_add(c, num_args, args); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_mul( value _v_c, value _v_args) { Z3_context c; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_mul(c, num_args, args); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_sub( value _v_c, value _v_args) { Z3_context c; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_sub(c, num_args, args); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_unary_minus( value _v_c, value _v_arg) { Z3_context c; /*in*/ Z3_ast arg; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_arg, &arg, _ctx); _res = Z3_mk_unary_minus(c, arg); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_div( value _v_c, value _v_arg1, value _v_arg2) { Z3_context c; /*in*/ Z3_ast arg1; /*in*/ Z3_ast arg2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_arg1, &arg1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_arg2, &arg2, _ctx); _res = Z3_mk_div(c, arg1, arg2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_mod( value _v_c, value _v_arg1, value _v_arg2) { Z3_context c; /*in*/ Z3_ast arg1; /*in*/ Z3_ast arg2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_arg1, &arg1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_arg2, &arg2, _ctx); _res = Z3_mk_mod(c, arg1, arg2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_rem( value _v_c, value _v_arg1, value _v_arg2) { Z3_context c; /*in*/ Z3_ast arg1; /*in*/ Z3_ast arg2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_arg1, &arg1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_arg2, &arg2, _ctx); _res = Z3_mk_rem(c, arg1, arg2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_power( value _v_c, value _v_arg1, value _v_arg2) { Z3_context c; /*in*/ Z3_ast arg1; /*in*/ Z3_ast arg2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_arg1, &arg1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_arg2, &arg2, _ctx); _res = Z3_mk_power(c, arg1, arg2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_lt( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_lt(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_le( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_le(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_gt( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_gt(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_ge( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_ge(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_int2real( value _v_c, value _v_t1) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_int2real(c, t1); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_real2int( value _v_c, value _v_t1) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_real2int(c, t1); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_is_int( value _v_c, value _v_t1) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_is_int(c, t1); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvnot( value _v_c, value _v_t1) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_bvnot(c, t1); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvredand( value _v_c, value _v_t1) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_bvredand(c, t1); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvredor( value _v_c, value _v_t1) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_bvredor(c, t1); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvand( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvand(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvor( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvor(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvxor( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvxor(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvnand( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvnand(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvnor( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvnor(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvxnor( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvxnor(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvneg( value _v_c, value _v_t1) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_bvneg(c, t1); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvadd( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvadd(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvsub( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsub(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvmul( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvmul(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvudiv( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvudiv(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvsdiv( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsdiv(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvurem( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvurem(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvsrem( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsrem(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvsmod( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsmod(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvult( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvult(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvslt( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvslt(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvule( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvule(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvsle( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsle(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvuge( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvuge(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvsge( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsge(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvugt( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvugt(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvsgt( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsgt(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_concat( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_concat(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_extract( value _v_c, value _v_high, value _v_low, value _v_t1) { Z3_context c; /*in*/ unsigned int high; /*in*/ unsigned int low; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); high = Int_val(_v_high); low = Int_val(_v_low); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_extract(c, high, low, t1); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_sign_ext( value _v_c, value _v_i, value _v_t1) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_sign_ext(c, i, t1); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_zero_ext( value _v_c, value _v_i, value _v_t1) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_zero_ext(c, i, t1); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_repeat( value _v_c, value _v_i, value _v_t1) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_repeat(c, i, t1); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvshl( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvshl(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvlshr( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvlshr(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvashr( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvashr(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_rotate_left( value _v_c, value _v_i, value _v_t1) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_rotate_left(c, i, t1); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_rotate_right( value _v_c, value _v_i, value _v_t1) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_rotate_right(c, i, t1); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_ext_rotate_left( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_ext_rotate_left(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_ext_rotate_right( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_ext_rotate_right(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_int2bv( value _v_c, value _v_n, value _v_t1) { Z3_context c; /*in*/ unsigned int n; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); n = Int_val(_v_n); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_int2bv(c, n, t1); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bv2int( value _v_c, value _v_t1, value _v_is_signed) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ int is_signed; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); is_signed = Int_val(_v_is_signed); _res = Z3_mk_bv2int(c, t1, is_signed); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvadd_no_overflow( value _v_c, value _v_t1, value _v_t2, value _v_is_signed) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ int is_signed; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); is_signed = Int_val(_v_is_signed); _res = Z3_mk_bvadd_no_overflow(c, t1, t2, is_signed); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvadd_no_underflow( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvadd_no_underflow(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvsub_no_overflow( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsub_no_overflow(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvsub_no_underflow( value _v_c, value _v_t1, value _v_t2, value _v_is_signed) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ int is_signed; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); is_signed = Int_val(_v_is_signed); _res = Z3_mk_bvsub_no_underflow(c, t1, t2, is_signed); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvsdiv_no_overflow( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvsdiv_no_overflow(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvneg_no_overflow( value _v_c, value _v_t1) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); _res = Z3_mk_bvneg_no_overflow(c, t1); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvmul_no_overflow( value _v_c, value _v_t1, value _v_t2, value _v_is_signed) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ int is_signed; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); is_signed = Int_val(_v_is_signed); _res = Z3_mk_bvmul_no_overflow(c, t1, t2, is_signed); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bvmul_no_underflow( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_mk_bvmul_no_underflow(c, t1, t2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_select( value _v_c, value _v_a, value _v_i) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_ast i; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_i, &i, _ctx); _res = Z3_mk_select(c, a, i); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_store( value _v_c, value _v_a, value _v_i, value _v_v) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_ast i; /*in*/ Z3_ast v; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_i, &i, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_v, &v, _ctx); _res = Z3_mk_store(c, a, i, v); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_const_array( value _v_c, value _v_domain, value _v_v) { Z3_context c; /*in*/ Z3_sort domain; /*in*/ Z3_ast v; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_domain, &domain, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_v, &v, _ctx); _res = Z3_mk_const_array(c, domain, v); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_map( value _v_c, value _v_f, value _v_n, value _v_args) { Z3_context c; /*in*/ Z3_func_decl f; /*in*/ unsigned int n; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; Z3_ast _c1; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_f, &f, _ctx); n = Int_val(_v_n); args = &_c1; camlidl_ml2c_z3V3_Z3_ast(_v_args, &_c1, _ctx); _res = Z3_mk_map(c, f, n, args); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_array_default( value _v_c, value _v_array) { Z3_context c; /*in*/ Z3_ast array; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_array, &array, _ctx); _res = Z3_mk_array_default(c, array); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_set_sort( value _v_c, value _v_ty) { Z3_context c; /*in*/ Z3_sort ty; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_ty, &ty, _ctx); _res = Z3_mk_set_sort(c, ty); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_empty_set( value _v_c, value _v_domain) { Z3_context c; /*in*/ Z3_sort domain; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_domain, &domain, _ctx); _res = Z3_mk_empty_set(c, domain); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_full_set( value _v_c, value _v_domain) { Z3_context c; /*in*/ Z3_sort domain; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_domain, &domain, _ctx); _res = Z3_mk_full_set(c, domain); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_set_add( value _v_c, value _v_set, value _v_elem) { Z3_context c; /*in*/ Z3_ast set; /*in*/ Z3_ast elem; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_set, &set, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_elem, &elem, _ctx); _res = Z3_mk_set_add(c, set, elem); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_set_del( value _v_c, value _v_set, value _v_elem) { Z3_context c; /*in*/ Z3_ast set; /*in*/ Z3_ast elem; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_set, &set, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_elem, &elem, _ctx); _res = Z3_mk_set_del(c, set, elem); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_set_union( value _v_c, value _v_args) { Z3_context c; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_set_union(c, num_args, args); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_set_intersect( value _v_c, value _v_args) { Z3_context c; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_mk_set_intersect(c, num_args, args); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_set_difference( value _v_c, value _v_arg1, value _v_arg2) { Z3_context c; /*in*/ Z3_ast arg1; /*in*/ Z3_ast arg2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_arg1, &arg1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_arg2, &arg2, _ctx); _res = Z3_mk_set_difference(c, arg1, arg2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_set_complement( value _v_c, value _v_arg) { Z3_context c; /*in*/ Z3_ast arg; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_arg, &arg, _ctx); _res = Z3_mk_set_complement(c, arg); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_set_member( value _v_c, value _v_elem, value _v_set) { Z3_context c; /*in*/ Z3_ast elem; /*in*/ Z3_ast set; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_elem, &elem, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_set, &set, _ctx); _res = Z3_mk_set_member(c, elem, set); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_set_subset( value _v_c, value _v_arg1, value _v_arg2) { Z3_context c; /*in*/ Z3_ast arg1; /*in*/ Z3_ast arg2; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_arg1, &arg1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_arg2, &arg2, _ctx); _res = Z3_mk_set_subset(c, arg1, arg2); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_numeral( value _v_c, value _v_numeral, value _v_ty) { Z3_context c; /*in*/ Z3_string numeral; /*in*/ Z3_sort ty; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_numeral, &numeral, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_ty, &ty, _ctx); _res = Z3_mk_numeral(c, numeral, ty); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_real( value _v_c, value _v_num, value _v_den) { Z3_context c; /*in*/ int num; /*in*/ int den; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); num = Int_val(_v_num); den = Int_val(_v_den); _res = Z3_mk_real(c, num, den); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_int( value _v_c, value _v_v, value _v_ty) { Z3_context c; /*in*/ int v; /*in*/ Z3_sort ty; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); v = Int_val(_v_v); camlidl_ml2c_z3V3_Z3_sort(_v_ty, &ty, _ctx); _res = Z3_mk_int(c, v, ty); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_int64( value _v_c, value _v_v, value _v_ty) { Z3_context c; /*in*/ __int64 v; /*in*/ Z3_sort ty; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); v = Int64_val(_v_v); camlidl_ml2c_z3V3_Z3_sort(_v_ty, &ty, _ctx); _res = Z3_mk_int64(c, v, ty); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_pattern( value _v_c, value _v_terms) { Z3_context c; /*in*/ unsigned int num_patterns; /*in*/ Z3_ast const *terms; /*in*/ Z3_pattern _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_terms); terms = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_terms, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &terms[_c2], _ctx); } num_patterns = _c1; _res = Z3_mk_pattern(c, num_patterns, terms); _vres = camlidl_c2ml_z3V3_Z3_pattern(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_bound( value _v_c, value _v_index, value _v_ty) { Z3_context c; /*in*/ unsigned int index; /*in*/ Z3_sort ty; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); index = Int_val(_v_index); camlidl_ml2c_z3V3_Z3_sort(_v_ty, &ty, _ctx); _res = Z3_mk_bound(c, index, ty); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_forall( value _v_c, value _v_weight, value _v_patterns, value _v_sorts, value _v_decl_names, value _v_body) { Z3_context c; /*in*/ unsigned int weight; /*in*/ unsigned int num_patterns; /*in*/ Z3_pattern const *patterns; /*in*/ unsigned int num_decls; /*in*/ Z3_sort const *sorts; /*in*/ Z3_symbol const *decl_names; /*in*/ Z3_ast body; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); weight = Int_val(_v_weight); _c1 = Wosize_val(_v_patterns); patterns = camlidl_malloc(_c1 * sizeof(Z3_pattern const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_patterns, _c2); camlidl_ml2c_z3V3_Z3_pattern(_v3, &patterns[_c2], _ctx); } num_patterns = _c1; _c4 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_sorts, _c5); camlidl_ml2c_z3V3_Z3_sort(_v6, &sorts[_c5], _ctx); } num_decls = _c4; _c7 = Wosize_val(_v_decl_names); decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_decl_names, _c8); camlidl_ml2c_z3V3_Z3_symbol(_v9, &decl_names[_c8], _ctx); } num_decls = _c7; camlidl_ml2c_z3V3_Z3_ast(_v_body, &body, _ctx); _res = Z3_mk_forall(c, weight, num_patterns, patterns, num_decls, sorts, decl_names, body); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_forall_bytecode(value * argv, int argn) { return camlidl_z3V3_Z3_mk_forall(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } value camlidl_z3V3_Z3_mk_exists( value _v_c, value _v_weight, value _v_patterns, value _v_sorts, value _v_decl_names, value _v_body) { Z3_context c; /*in*/ unsigned int weight; /*in*/ unsigned int num_patterns; /*in*/ Z3_pattern const *patterns; /*in*/ unsigned int num_decls; /*in*/ Z3_sort const *sorts; /*in*/ Z3_symbol const *decl_names; /*in*/ Z3_ast body; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); weight = Int_val(_v_weight); _c1 = Wosize_val(_v_patterns); patterns = camlidl_malloc(_c1 * sizeof(Z3_pattern const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_patterns, _c2); camlidl_ml2c_z3V3_Z3_pattern(_v3, &patterns[_c2], _ctx); } num_patterns = _c1; _c4 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_sorts, _c5); camlidl_ml2c_z3V3_Z3_sort(_v6, &sorts[_c5], _ctx); } num_decls = _c4; _c7 = Wosize_val(_v_decl_names); decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_decl_names, _c8); camlidl_ml2c_z3V3_Z3_symbol(_v9, &decl_names[_c8], _ctx); } num_decls = _c7; camlidl_ml2c_z3V3_Z3_ast(_v_body, &body, _ctx); _res = Z3_mk_exists(c, weight, num_patterns, patterns, num_decls, sorts, decl_names, body); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_exists_bytecode(value * argv, int argn) { return camlidl_z3V3_Z3_mk_exists(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } value camlidl_z3V3_Z3_mk_quantifier( value _v_c, value _v_is_forall, value _v_weight, value _v_patterns, value _v_sorts, value _v_decl_names, value _v_body) { Z3_context c; /*in*/ int is_forall; /*in*/ unsigned int weight; /*in*/ unsigned int num_patterns; /*in*/ Z3_pattern const *patterns; /*in*/ unsigned int num_decls; /*in*/ Z3_sort const *sorts; /*in*/ Z3_symbol const *decl_names; /*in*/ Z3_ast body; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); is_forall = Int_val(_v_is_forall); weight = Int_val(_v_weight); _c1 = Wosize_val(_v_patterns); patterns = camlidl_malloc(_c1 * sizeof(Z3_pattern const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_patterns, _c2); camlidl_ml2c_z3V3_Z3_pattern(_v3, &patterns[_c2], _ctx); } num_patterns = _c1; _c4 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_sorts, _c5); camlidl_ml2c_z3V3_Z3_sort(_v6, &sorts[_c5], _ctx); } num_decls = _c4; _c7 = Wosize_val(_v_decl_names); decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_decl_names, _c8); camlidl_ml2c_z3V3_Z3_symbol(_v9, &decl_names[_c8], _ctx); } num_decls = _c7; camlidl_ml2c_z3V3_Z3_ast(_v_body, &body, _ctx); _res = Z3_mk_quantifier(c, is_forall, weight, num_patterns, patterns, num_decls, sorts, decl_names, body); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_quantifier_bytecode(value * argv, int argn) { return camlidl_z3V3_Z3_mk_quantifier(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); } value camlidl_z3V3_Z3_mk_quantifier_ex( value _v_c, value _v_is_forall, value _v_weight, value _v_quantifier_id, value _v_skolem_id, value _v_patterns, value _v_no_patterns, value _v_sorts, value _v_decl_names, value _v_body) { Z3_context c; /*in*/ int is_forall; /*in*/ unsigned int weight; /*in*/ Z3_symbol quantifier_id; /*in*/ Z3_symbol skolem_id; /*in*/ unsigned int num_patterns; /*in*/ Z3_pattern const *patterns; /*in*/ unsigned int num_no_patterns; /*in*/ Z3_ast const *no_patterns; /*in*/ unsigned int num_decls; /*in*/ Z3_sort const *sorts; /*in*/ Z3_symbol const *decl_names; /*in*/ Z3_ast body; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; mlsize_t _c10; mlsize_t _c11; value _v12; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); is_forall = Int_val(_v_is_forall); weight = Int_val(_v_weight); camlidl_ml2c_z3V3_Z3_symbol(_v_quantifier_id, &quantifier_id, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_skolem_id, &skolem_id, _ctx); _c1 = Wosize_val(_v_patterns); patterns = camlidl_malloc(_c1 * sizeof(Z3_pattern const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_patterns, _c2); camlidl_ml2c_z3V3_Z3_pattern(_v3, &patterns[_c2], _ctx); } num_patterns = _c1; _c4 = Wosize_val(_v_no_patterns); no_patterns = camlidl_malloc(_c4 * sizeof(Z3_ast const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_no_patterns, _c5); camlidl_ml2c_z3V3_Z3_ast(_v6, &no_patterns[_c5], _ctx); } num_no_patterns = _c4; _c7 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c7 * sizeof(Z3_sort const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_sorts, _c8); camlidl_ml2c_z3V3_Z3_sort(_v9, &sorts[_c8], _ctx); } num_decls = _c7; _c10 = Wosize_val(_v_decl_names); decl_names = camlidl_malloc(_c10 * sizeof(Z3_symbol const ), _ctx); for (_c11 = 0; _c11 < _c10; _c11++) { _v12 = Field(_v_decl_names, _c11); camlidl_ml2c_z3V3_Z3_symbol(_v12, &decl_names[_c11], _ctx); } num_decls = _c10; camlidl_ml2c_z3V3_Z3_ast(_v_body, &body, _ctx); _res = Z3_mk_quantifier_ex(c, is_forall, weight, quantifier_id, skolem_id, num_patterns, patterns, num_no_patterns, no_patterns, num_decls, sorts, decl_names, body); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_quantifier_ex_bytecode(value * argv, int argn) { return camlidl_z3V3_Z3_mk_quantifier_ex(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9]); } value camlidl_z3V3_Z3_mk_forall_const( value _v_c, value _v_weight, value _v_bound, value _v_patterns, value _v_body) { Z3_context c; /*in*/ unsigned int weight; /*in*/ unsigned int num_bound; /*in*/ Z3_app const *bound; /*in*/ unsigned int num_patterns; /*in*/ Z3_pattern const *patterns; /*in*/ Z3_ast body; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); weight = Int_val(_v_weight); _c1 = Wosize_val(_v_bound); bound = camlidl_malloc(_c1 * sizeof(Z3_app const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_bound, _c2); camlidl_ml2c_z3V3_Z3_app(_v3, &bound[_c2], _ctx); } num_bound = _c1; _c4 = Wosize_val(_v_patterns); patterns = camlidl_malloc(_c4 * sizeof(Z3_pattern const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_patterns, _c5); camlidl_ml2c_z3V3_Z3_pattern(_v6, &patterns[_c5], _ctx); } num_patterns = _c4; camlidl_ml2c_z3V3_Z3_ast(_v_body, &body, _ctx); _res = Z3_mk_forall_const(c, weight, num_bound, bound, num_patterns, patterns, body); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_exists_const( value _v_c, value _v_weight, value _v_bound, value _v_patterns, value _v_body) { Z3_context c; /*in*/ unsigned int weight; /*in*/ unsigned int num_bound; /*in*/ Z3_app const *bound; /*in*/ unsigned int num_patterns; /*in*/ Z3_pattern const *patterns; /*in*/ Z3_ast body; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); weight = Int_val(_v_weight); _c1 = Wosize_val(_v_bound); bound = camlidl_malloc(_c1 * sizeof(Z3_app const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_bound, _c2); camlidl_ml2c_z3V3_Z3_app(_v3, &bound[_c2], _ctx); } num_bound = _c1; _c4 = Wosize_val(_v_patterns); patterns = camlidl_malloc(_c4 * sizeof(Z3_pattern const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_patterns, _c5); camlidl_ml2c_z3V3_Z3_pattern(_v6, &patterns[_c5], _ctx); } num_patterns = _c4; camlidl_ml2c_z3V3_Z3_ast(_v_body, &body, _ctx); _res = Z3_mk_exists_const(c, weight, num_bound, bound, num_patterns, patterns, body); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_quantifier_const( value _v_c, value _v_is_forall, value _v_weight, value _v_bound, value _v_patterns, value _v_body) { Z3_context c; /*in*/ int is_forall; /*in*/ unsigned int weight; /*in*/ unsigned int num_bound; /*in*/ Z3_app const *bound; /*in*/ unsigned int num_patterns; /*in*/ Z3_pattern const *patterns; /*in*/ Z3_ast body; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); is_forall = Int_val(_v_is_forall); weight = Int_val(_v_weight); _c1 = Wosize_val(_v_bound); bound = camlidl_malloc(_c1 * sizeof(Z3_app const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_bound, _c2); camlidl_ml2c_z3V3_Z3_app(_v3, &bound[_c2], _ctx); } num_bound = _c1; _c4 = Wosize_val(_v_patterns); patterns = camlidl_malloc(_c4 * sizeof(Z3_pattern const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_patterns, _c5); camlidl_ml2c_z3V3_Z3_pattern(_v6, &patterns[_c5], _ctx); } num_patterns = _c4; camlidl_ml2c_z3V3_Z3_ast(_v_body, &body, _ctx); _res = Z3_mk_quantifier_const(c, is_forall, weight, num_bound, bound, num_patterns, patterns, body); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_quantifier_const_bytecode(value * argv, int argn) { return camlidl_z3V3_Z3_mk_quantifier_const(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } value camlidl_z3V3_Z3_mk_quantifier_const_ex( value _v_c, value _v_is_forall, value _v_weight, value _v_quantifier_id, value _v_skolem_id, value _v_bound, value _v_patterns, value _v_no_patterns, value _v_body) { Z3_context c; /*in*/ int is_forall; /*in*/ unsigned int weight; /*in*/ Z3_symbol quantifier_id; /*in*/ Z3_symbol skolem_id; /*in*/ unsigned int num_bound; /*in*/ Z3_app const *bound; /*in*/ unsigned int num_patterns; /*in*/ Z3_pattern const *patterns; /*in*/ unsigned int num_no_patterns; /*in*/ Z3_ast const *no_patterns; /*in*/ Z3_ast body; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); is_forall = Int_val(_v_is_forall); weight = Int_val(_v_weight); camlidl_ml2c_z3V3_Z3_symbol(_v_quantifier_id, &quantifier_id, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_skolem_id, &skolem_id, _ctx); _c1 = Wosize_val(_v_bound); bound = camlidl_malloc(_c1 * sizeof(Z3_app const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_bound, _c2); camlidl_ml2c_z3V3_Z3_app(_v3, &bound[_c2], _ctx); } num_bound = _c1; _c4 = Wosize_val(_v_patterns); patterns = camlidl_malloc(_c4 * sizeof(Z3_pattern const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_patterns, _c5); camlidl_ml2c_z3V3_Z3_pattern(_v6, &patterns[_c5], _ctx); } num_patterns = _c4; _c7 = Wosize_val(_v_no_patterns); no_patterns = camlidl_malloc(_c7 * sizeof(Z3_ast const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_no_patterns, _c8); camlidl_ml2c_z3V3_Z3_ast(_v9, &no_patterns[_c8], _ctx); } num_no_patterns = _c7; camlidl_ml2c_z3V3_Z3_ast(_v_body, &body, _ctx); _res = Z3_mk_quantifier_const_ex(c, is_forall, weight, quantifier_id, skolem_id, num_bound, bound, num_patterns, patterns, num_no_patterns, no_patterns, body); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_quantifier_const_ex_bytecode(value * argv, int argn) { return camlidl_z3V3_Z3_mk_quantifier_const_ex(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]); } value camlidl_z3V3_Z3_get_symbol_kind( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_symbol s; /*in*/ Z3_symbol_kind _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); _res = Z3_get_symbol_kind(c, s); _vres = camlidl_c2ml_z3V3_Z3_symbol_kind(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_symbol_int( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_symbol s; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); _res = Z3_get_symbol_int(c, s); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_symbol_string( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_symbol s; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); _res = Z3_get_symbol_string(c, s); _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_sort_name( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_sort d; /*in*/ Z3_symbol _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_d, &d, _ctx); _res = Z3_get_sort_name(c, d); _vres = camlidl_c2ml_z3V3_Z3_symbol(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_sort_id( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_sort s; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_s, &s, _ctx); _res = Z3_get_sort_id(c, s); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_sort_to_ast( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_sort s; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_s, &s, _ctx); _res = Z3_sort_to_ast(c, s); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_is_eq_sort( value _v_c, value _v_s1, value _v_s2) { Z3_context c; /*in*/ Z3_sort s1; /*in*/ Z3_sort s2; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_s1, &s1, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_s2, &s2, _ctx); _res = Z3_is_eq_sort(c, s1, s2); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_sort_kind( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_sort t; /*in*/ Z3_sort_kind _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); _res = Z3_get_sort_kind(c, t); _vres = camlidl_c2ml_z3V3_Z3_sort_kind(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_bv_sort_size( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_sort t; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); _res = Z3_get_bv_sort_size(c, t); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_finite_domain_sort_size( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_sort s; /*in*/ __int64 *r; /*out*/ __int64 _c1; value _v2; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_s, &s, _ctx); r = &_c1; Z3_get_finite_domain_sort_size(c, s, r); if (r == NULL) { _vres = Val_int(0); } else { _v2 = copy_int64(*r); Begin_root(_v2) _vres = camlidl_alloc_small(1, 0); Field(_vres, 0) = _v2; End_roots(); } camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_array_sort_domain( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_sort t; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); _res = Z3_get_array_sort_domain(c, t); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_array_sort_range( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_sort t; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); _res = Z3_get_array_sort_range(c, t); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_tuple_sort_mk_decl( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_sort t; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); _res = Z3_get_tuple_sort_mk_decl(c, t); _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_tuple_sort_num_fields( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_sort t; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); _res = Z3_get_tuple_sort_num_fields(c, t); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_tuple_sort_field_decl( value _v_c, value _v_t, value _v_i) { Z3_context c; /*in*/ Z3_sort t; /*in*/ unsigned int i; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); i = Int_val(_v_i); _res = Z3_get_tuple_sort_field_decl(c, t, i); _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_datatype_sort_num_constructors( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_sort t; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); _res = Z3_get_datatype_sort_num_constructors(c, t); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_datatype_sort_constructor( value _v_c, value _v_t, value _v_idx) { Z3_context c; /*in*/ Z3_sort t; /*in*/ unsigned int idx; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); idx = Int_val(_v_idx); _res = Z3_get_datatype_sort_constructor(c, t, idx); _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_datatype_sort_recognizer( value _v_c, value _v_t, value _v_idx) { Z3_context c; /*in*/ Z3_sort t; /*in*/ unsigned int idx; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); idx = Int_val(_v_idx); _res = Z3_get_datatype_sort_recognizer(c, t, idx); _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_datatype_sort_constructor_accessor( value _v_c, value _v_t, value _v_idx_c, value _v_idx_a) { Z3_context c; /*in*/ Z3_sort t; /*in*/ unsigned int idx_c; /*in*/ unsigned int idx_a; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); idx_c = Int_val(_v_idx_c); idx_a = Int_val(_v_idx_a); _res = Z3_get_datatype_sort_constructor_accessor(c, t, idx_c, idx_a); _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_relation_arity( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_sort s; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_s, &s, _ctx); _res = Z3_get_relation_arity(c, s); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_relation_column( value _v_c, value _v_s, value _v_col) { Z3_context c; /*in*/ Z3_sort s; /*in*/ unsigned int col; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_s, &s, _ctx); col = Int_val(_v_col); _res = Z3_get_relation_column(c, s, col); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_func_decl_to_ast( value _v_c, value _v_f) { Z3_context c; /*in*/ Z3_func_decl f; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_f, &f, _ctx); _res = Z3_func_decl_to_ast(c, f); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_is_eq_func_decl( value _v_c, value _v_f1, value _v_f2) { Z3_context c; /*in*/ Z3_func_decl f1; /*in*/ Z3_func_decl f2; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_f1, &f1, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_f2, &f2, _ctx); _res = Z3_is_eq_func_decl(c, f1, f2); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_func_decl_id( value _v_c, value _v_f) { Z3_context c; /*in*/ Z3_func_decl f; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_f, &f, _ctx); _res = Z3_get_func_decl_id(c, f); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_decl_name( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ Z3_symbol _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); _res = Z3_get_decl_name(c, d); _vres = camlidl_c2ml_z3V3_Z3_symbol(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_decl_kind( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ Z3_decl_kind _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); _res = Z3_get_decl_kind(c, d); _vres = camlidl_c2ml_z3V3_Z3_decl_kind(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_domain_size( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); _res = Z3_get_domain_size(c, d); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_arity( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); _res = Z3_get_arity(c, d); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_domain( value _v_c, value _v_d, value _v_i) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int i; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); i = Int_val(_v_i); _res = Z3_get_domain(c, d, i); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_range( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); _res = Z3_get_range(c, d); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_decl_num_parameters( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); _res = Z3_get_decl_num_parameters(c, d); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_decl_parameter_kind( value _v_c, value _v_d, value _v_idx) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int idx; /*in*/ Z3_parameter_kind _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); idx = Int_val(_v_idx); _res = Z3_get_decl_parameter_kind(c, d, idx); _vres = camlidl_c2ml_z3V3_Z3_parameter_kind(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_decl_int_parameter( value _v_c, value _v_d, value _v_idx) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int idx; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); idx = Int_val(_v_idx); _res = Z3_get_decl_int_parameter(c, d, idx); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_decl_double_parameter( value _v_c, value _v_d, value _v_idx) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int idx; /*in*/ double _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); idx = Int_val(_v_idx); _res = Z3_get_decl_double_parameter(c, d, idx); _vres = copy_double(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_decl_symbol_parameter( value _v_c, value _v_d, value _v_idx) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int idx; /*in*/ Z3_symbol _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); idx = Int_val(_v_idx); _res = Z3_get_decl_symbol_parameter(c, d, idx); _vres = camlidl_c2ml_z3V3_Z3_symbol(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_decl_sort_parameter( value _v_c, value _v_d, value _v_idx) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int idx; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); idx = Int_val(_v_idx); _res = Z3_get_decl_sort_parameter(c, d, idx); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_decl_ast_parameter( value _v_c, value _v_d, value _v_idx) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int idx; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); idx = Int_val(_v_idx); _res = Z3_get_decl_ast_parameter(c, d, idx); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_decl_func_decl_parameter( value _v_c, value _v_d, value _v_idx) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int idx; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); idx = Int_val(_v_idx); _res = Z3_get_decl_func_decl_parameter(c, d, idx); _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_decl_rational_parameter( value _v_c, value _v_d, value _v_idx) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ unsigned int idx; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); idx = Int_val(_v_idx); _res = Z3_get_decl_rational_parameter(c, d, idx); _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_app_to_ast( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_app a; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_app(_v_a, &a, _ctx); _res = Z3_app_to_ast(c, a); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_app_decl( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_app a; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_app(_v_a, &a, _ctx); _res = Z3_get_app_decl(c, a); _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_app_num_args( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_app a; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_app(_v_a, &a, _ctx); _res = Z3_get_app_num_args(c, a); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_app_arg( value _v_c, value _v_a, value _v_i) { Z3_context c; /*in*/ Z3_app a; /*in*/ unsigned int i; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_app(_v_a, &a, _ctx); i = Int_val(_v_i); _res = Z3_get_app_arg(c, a, i); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_is_eq_ast( value _v_c, value _v_t1, value _v_t2) { Z3_context c; /*in*/ Z3_ast t1; /*in*/ Z3_ast t2; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); _res = Z3_is_eq_ast(c, t1, t2); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_ast_id( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_ast t; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t, &t, _ctx); _res = Z3_get_ast_id(c, t); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_ast_hash( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_ast_hash(c, a); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_sort( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_sort(c, a); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_is_well_sorted( value _v_c, value _v_t) { Z3_context c; /*in*/ Z3_ast t; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t, &t, _ctx); _res = Z3_is_well_sorted(c, t); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_bool_value( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_lbool _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_bool_value(c, a); _vres = camlidl_c2ml_z3V3_Z3_lbool(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_ast_kind( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_ast_kind _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_ast_kind(c, a); _vres = camlidl_c2ml_z3V3_Z3_ast_kind(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_is_app( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_is_app(c, a); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_is_numeral_ast( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_is_numeral_ast(c, a); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_is_algebraic_number( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_is_algebraic_number(c, a); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_to_app( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_app _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_to_app(c, a); _vres = camlidl_c2ml_z3V3_Z3_app(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_to_func_decl( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_to_func_decl(c, a); _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_numeral_string( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_numeral_string(c, a); _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_numeral_decimal_string( value _v_c, value _v_a, value _v_precision) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int precision; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); precision = Int_val(_v_precision); _res = Z3_get_numeral_decimal_string(c, a, precision); _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_numerator( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_numerator(c, a); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_denominator( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_denominator(c, a); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_numeral_small( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ __int64 *num; /*out*/ __int64 *den; /*out*/ int _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; __int64 _c1; __int64 _c2; value _vresult; value _vres[3] = { 0, 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); num = &_c1; den = &_c2; _res = Z3_get_numeral_small(c, a, num, den); Begin_roots_block(_vres, 3) _vres[0] = Val_int(_res); _vres[1] = copy_int64(*num); _vres[2] = copy_int64(*den); _vresult = camlidl_alloc_small(3, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; Field(_vresult, 2) = _vres[2]; End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_get_numeral_int( value _v_c, value _v_v) { Z3_context c; /*in*/ Z3_ast v; /*in*/ int *i; /*out*/ int _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; int _c1; value _vresult; value _vres[2] = { 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_v, &v, _ctx); i = &_c1; _res = Z3_get_numeral_int(c, v, i); Begin_roots_block(_vres, 2) _vres[0] = Val_int(_res); _vres[1] = Val_int(*i); _vresult = camlidl_alloc_small(2, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_get_numeral_int64( value _v_c, value _v_v) { Z3_context c; /*in*/ Z3_ast v; /*in*/ __int64 *i; /*out*/ int _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; __int64 _c1; value _vresult; value _vres[2] = { 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_v, &v, _ctx); i = &_c1; _res = Z3_get_numeral_int64(c, v, i); Begin_roots_block(_vres, 2) _vres[0] = Val_int(_res); _vres[1] = copy_int64(*i); _vresult = camlidl_alloc_small(2, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_get_numeral_rational_int64( value _v_c, value _v_v) { Z3_context c; /*in*/ Z3_ast v; /*in*/ __int64 *num; /*out*/ __int64 *den; /*out*/ int _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; __int64 _c1; __int64 _c2; value _vresult; value _vres[3] = { 0, 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_v, &v, _ctx); num = &_c1; den = &_c2; _res = Z3_get_numeral_rational_int64(c, v, num, den); Begin_roots_block(_vres, 3) _vres[0] = Val_int(_res); _vres[1] = copy_int64(*num); _vres[2] = copy_int64(*den); _vresult = camlidl_alloc_small(3, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; Field(_vresult, 2) = _vres[2]; End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_get_algebraic_number_lower( value _v_c, value _v_a, value _v_precision) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int precision; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); precision = Int_val(_v_precision); _res = Z3_get_algebraic_number_lower(c, a, precision); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_algebraic_number_upper( value _v_c, value _v_a, value _v_precision) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int precision; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); precision = Int_val(_v_precision); _res = Z3_get_algebraic_number_upper(c, a, precision); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_pattern_to_ast( value _v_c, value _v_p) { Z3_context c; /*in*/ Z3_pattern p; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_pattern(_v_p, &p, _ctx); _res = Z3_pattern_to_ast(c, p); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_pattern_num_terms( value _v_c, value _v_p) { Z3_context c; /*in*/ Z3_pattern p; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_pattern(_v_p, &p, _ctx); _res = Z3_get_pattern_num_terms(c, p); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_pattern( value _v_c, value _v_p, value _v_idx) { Z3_context c; /*in*/ Z3_pattern p; /*in*/ unsigned int idx; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_pattern(_v_p, &p, _ctx); idx = Int_val(_v_idx); _res = Z3_get_pattern(c, p, idx); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_index_value( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_index_value(c, a); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_is_quantifier_forall( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_is_quantifier_forall(c, a); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_quantifier_weight( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_quantifier_weight(c, a); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_quantifier_num_patterns( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_quantifier_num_patterns(c, a); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_quantifier_pattern_ast( value _v_c, value _v_a, value _v_i) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int i; /*in*/ Z3_pattern _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); i = Int_val(_v_i); _res = Z3_get_quantifier_pattern_ast(c, a, i); _vres = camlidl_c2ml_z3V3_Z3_pattern(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_quantifier_num_no_patterns( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_quantifier_num_no_patterns(c, a); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_quantifier_no_pattern_ast( value _v_c, value _v_a, value _v_i) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int i; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); i = Int_val(_v_i); _res = Z3_get_quantifier_no_pattern_ast(c, a, i); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_quantifier_num_bound( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_quantifier_num_bound(c, a); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_quantifier_bound_name( value _v_c, value _v_a, value _v_i) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int i; /*in*/ Z3_symbol _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); i = Int_val(_v_i); _res = Z3_get_quantifier_bound_name(c, a, i); _vres = camlidl_c2ml_z3V3_Z3_symbol(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_quantifier_bound_sort( value _v_c, value _v_a, value _v_i) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int i; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); i = Int_val(_v_i); _res = Z3_get_quantifier_bound_sort(c, a, i); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_quantifier_body( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_get_quantifier_body(c, a); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_simplify( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_simplify(c, a); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_update_term( value _v_c, value _v_a, value _v_args) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; _res = Z3_update_term(c, a, num_args, args); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_substitute( value _v_c, value _v_a, value _v_from, value _v_to) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int num_exprs; /*in*/ Z3_ast const *from; /*in*/ Z3_ast const *to; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _c1 = Wosize_val(_v_from); from = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_from, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &from[_c2], _ctx); } num_exprs = _c1; _c4 = Wosize_val(_v_to); to = camlidl_malloc(_c4 * sizeof(Z3_ast const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_to, _c5); camlidl_ml2c_z3V3_Z3_ast(_v6, &to[_c5], _ctx); } num_exprs = _c4; _res = Z3_substitute(c, a, num_exprs, from, to); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_substitute_vars( value _v_c, value _v_a, value _v_to) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int num_exprs; /*in*/ Z3_ast const *to; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _c1 = Wosize_val(_v_to); to = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_to, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &to[_c2], _ctx); } num_exprs = _c1; _res = Z3_substitute_vars(c, a, num_exprs, to); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_open_log( value _v_filename) { Z3_string filename; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_string(_v_filename, &filename, _ctx); _res = Z3_open_log(filename); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_append_log( value _v_string) { Z3_string string; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_string(_v_string, &string, _ctx); Z3_append_log(string); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_close_log(value _unit) { Z3_close_log(); return Val_unit; } value camlidl_z3V3_Z3_toggle_warning_messages( value _v_enabled) { int enabled; /*in*/ enabled = Int_val(_v_enabled); Z3_toggle_warning_messages(enabled); return Val_unit; } value camlidl_z3V3_Z3_set_ast_print_mode( value _v_c, value _v_mode) { Z3_context c; /*in*/ Z3_ast_print_mode mode; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast_print_mode(_v_mode, &mode, _ctx); Z3_set_ast_print_mode(c, mode); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_ast_to_string( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); _res = Z3_ast_to_string(c, a); _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_pattern_to_string( value _v_c, value _v_p) { Z3_context c; /*in*/ Z3_pattern p; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_pattern(_v_p, &p, _ctx); _res = Z3_pattern_to_string(c, p); _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_sort_to_string( value _v_c, value _v_s) { Z3_context c; /*in*/ Z3_sort s; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_s, &s, _ctx); _res = Z3_sort_to_string(c, s); _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_func_decl_to_string( value _v_c, value _v_d) { Z3_context c; /*in*/ Z3_func_decl d; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); _res = Z3_func_decl_to_string(c, d); _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_model_to_string( value _v_c, value _v_m) { Z3_context c; /*in*/ Z3_model m; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); _res = Z3_model_to_string(c, m); _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_benchmark_to_smtlib_string( value _v_c, value _v_name, value _v_logic, value _v_status, value _v_attributes, value _v_assumptions, value _v_formula) { Z3_context c; /*in*/ Z3_string name; /*in*/ Z3_string logic; /*in*/ Z3_string status; /*in*/ Z3_string attributes; /*in*/ unsigned int num_assumptions; /*in*/ Z3_ast const *assumptions; /*in*/ Z3_ast formula; /*in*/ Z3_string _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_name, &name, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_logic, &logic, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_status, &status, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_attributes, &attributes, _ctx); _c1 = Wosize_val(_v_assumptions); assumptions = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_assumptions, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &assumptions[_c2], _ctx); } num_assumptions = _c1; camlidl_ml2c_z3V3_Z3_ast(_v_formula, &formula, _ctx); _res = Z3_benchmark_to_smtlib_string(c, name, logic, status, attributes, num_assumptions, assumptions, formula); _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_benchmark_to_smtlib_string_bytecode(value * argv, int argn) { return camlidl_z3V3_Z3_benchmark_to_smtlib_string(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); } value camlidl_z3V3_Z3_parse_smtlib2_string( value _v_c, value _v_str, value _v_sort_names, value _v_sorts, value _v_decl_names, value _v_decls) { Z3_context c; /*in*/ Z3_string str; /*in*/ unsigned int num_sorts; /*in*/ Z3_symbol const *sort_names; /*in*/ Z3_sort const *sorts; /*in*/ unsigned int num_decls; /*in*/ Z3_symbol const *decl_names; /*in*/ Z3_func_decl const *decls; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; mlsize_t _c10; mlsize_t _c11; value _v12; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_str, &str, _ctx); _c1 = Wosize_val(_v_sort_names); sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_sort_names, _c2); camlidl_ml2c_z3V3_Z3_symbol(_v3, &sort_names[_c2], _ctx); } num_sorts = _c1; _c4 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_sorts, _c5); camlidl_ml2c_z3V3_Z3_sort(_v6, &sorts[_c5], _ctx); } num_sorts = _c4; _c7 = Wosize_val(_v_decl_names); decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_decl_names, _c8); camlidl_ml2c_z3V3_Z3_symbol(_v9, &decl_names[_c8], _ctx); } num_decls = _c7; _c10 = Wosize_val(_v_decls); decls = camlidl_malloc(_c10 * sizeof(Z3_func_decl const ), _ctx); for (_c11 = 0; _c11 < _c10; _c11++) { _v12 = Field(_v_decls, _c11); camlidl_ml2c_z3V3_Z3_func_decl(_v12, &decls[_c11], _ctx); } num_decls = _c10; _res = Z3_parse_smtlib2_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_parse_smtlib2_string_bytecode(value * argv, int argn) { return camlidl_z3V3_Z3_parse_smtlib2_string(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } value camlidl_z3V3_Z3_parse_smtlib2_file( value _v_c, value _v_file_name, value _v_sort_names, value _v_sorts, value _v_decl_names, value _v_decls) { Z3_context c; /*in*/ Z3_string file_name; /*in*/ unsigned int num_sorts; /*in*/ Z3_symbol const *sort_names; /*in*/ Z3_sort const *sorts; /*in*/ unsigned int num_decls; /*in*/ Z3_symbol const *decl_names; /*in*/ Z3_func_decl const *decls; /*in*/ Z3_ast _res; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; mlsize_t _c10; mlsize_t _c11; value _v12; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_file_name, &file_name, _ctx); _c1 = Wosize_val(_v_sort_names); sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_sort_names, _c2); camlidl_ml2c_z3V3_Z3_symbol(_v3, &sort_names[_c2], _ctx); } num_sorts = _c1; _c4 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_sorts, _c5); camlidl_ml2c_z3V3_Z3_sort(_v6, &sorts[_c5], _ctx); } num_sorts = _c4; _c7 = Wosize_val(_v_decl_names); decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_decl_names, _c8); camlidl_ml2c_z3V3_Z3_symbol(_v9, &decl_names[_c8], _ctx); } num_decls = _c7; _c10 = Wosize_val(_v_decls); decls = camlidl_malloc(_c10 * sizeof(Z3_func_decl const ), _ctx); for (_c11 = 0; _c11 < _c10; _c11++) { _v12 = Field(_v_decls, _c11); camlidl_ml2c_z3V3_Z3_func_decl(_v12, &decls[_c11], _ctx); } num_decls = _c10; _res = Z3_parse_smtlib2_file(c, file_name, num_sorts, sort_names, sorts, num_decls, decl_names, decls); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_parse_smtlib2_file_bytecode(value * argv, int argn) { return camlidl_z3V3_Z3_parse_smtlib2_file(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } value camlidl_z3V3_Z3_parse_smtlib_string( value _v_c, value _v_str, value _v_sort_names, value _v_sorts, value _v_decl_names, value _v_decls) { Z3_context c; /*in*/ Z3_string str; /*in*/ unsigned int num_sorts; /*in*/ Z3_symbol const *sort_names; /*in*/ Z3_sort const *sorts; /*in*/ unsigned int num_decls; /*in*/ Z3_symbol const *decl_names; /*in*/ Z3_func_decl const *decls; /*in*/ mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; mlsize_t _c10; mlsize_t _c11; value _v12; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_str, &str, _ctx); _c1 = Wosize_val(_v_sort_names); sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_sort_names, _c2); camlidl_ml2c_z3V3_Z3_symbol(_v3, &sort_names[_c2], _ctx); } num_sorts = _c1; _c4 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_sorts, _c5); camlidl_ml2c_z3V3_Z3_sort(_v6, &sorts[_c5], _ctx); } num_sorts = _c4; _c7 = Wosize_val(_v_decl_names); decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_decl_names, _c8); camlidl_ml2c_z3V3_Z3_symbol(_v9, &decl_names[_c8], _ctx); } num_decls = _c7; _c10 = Wosize_val(_v_decls); decls = camlidl_malloc(_c10 * sizeof(Z3_func_decl const ), _ctx); for (_c11 = 0; _c11 < _c10; _c11++) { _v12 = Field(_v_decls, _c11); camlidl_ml2c_z3V3_Z3_func_decl(_v12, &decls[_c11], _ctx); } num_decls = _c10; Z3_parse_smtlib_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_parse_smtlib_string_bytecode(value * argv, int argn) { return camlidl_z3V3_Z3_parse_smtlib_string(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } value camlidl_z3V3_Z3_parse_smtlib_file( value _v_c, value _v_file_name, value _v_sort_names, value _v_sorts, value _v_decl_names, value _v_decls) { Z3_context c; /*in*/ Z3_string file_name; /*in*/ unsigned int num_sorts; /*in*/ Z3_symbol const *sort_names; /*in*/ Z3_sort const *sorts; /*in*/ unsigned int num_decls; /*in*/ Z3_symbol const *decl_names; /*in*/ Z3_func_decl const *decls; /*in*/ mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; mlsize_t _c7; mlsize_t _c8; value _v9; mlsize_t _c10; mlsize_t _c11; value _v12; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_file_name, &file_name, _ctx); _c1 = Wosize_val(_v_sort_names); sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_sort_names, _c2); camlidl_ml2c_z3V3_Z3_symbol(_v3, &sort_names[_c2], _ctx); } num_sorts = _c1; _c4 = Wosize_val(_v_sorts); sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_sorts, _c5); camlidl_ml2c_z3V3_Z3_sort(_v6, &sorts[_c5], _ctx); } num_sorts = _c4; _c7 = Wosize_val(_v_decl_names); decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); for (_c8 = 0; _c8 < _c7; _c8++) { _v9 = Field(_v_decl_names, _c8); camlidl_ml2c_z3V3_Z3_symbol(_v9, &decl_names[_c8], _ctx); } num_decls = _c7; _c10 = Wosize_val(_v_decls); decls = camlidl_malloc(_c10 * sizeof(Z3_func_decl const ), _ctx); for (_c11 = 0; _c11 < _c10; _c11++) { _v12 = Field(_v_decls, _c11); camlidl_ml2c_z3V3_Z3_func_decl(_v12, &decls[_c11], _ctx); } num_decls = _c10; Z3_parse_smtlib_file(c, file_name, num_sorts, sort_names, sorts, num_decls, decl_names, decls); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_parse_smtlib_file_bytecode(value * argv, int argn) { return camlidl_z3V3_Z3_parse_smtlib_file(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } value camlidl_z3V3_Z3_get_smtlib_num_formulas( value _v_c) { Z3_context c; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_smtlib_num_formulas(c); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_smtlib_formula( value _v_c, value _v_i) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); _res = Z3_get_smtlib_formula(c, i); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_smtlib_num_assumptions( value _v_c) { Z3_context c; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_smtlib_num_assumptions(c); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_smtlib_assumption( value _v_c, value _v_i) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); _res = Z3_get_smtlib_assumption(c, i); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_smtlib_num_decls( value _v_c) { Z3_context c; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_smtlib_num_decls(c); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_smtlib_decl( value _v_c, value _v_i) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); _res = Z3_get_smtlib_decl(c, i); _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_smtlib_num_sorts( value _v_c) { Z3_context c; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_smtlib_num_sorts(c); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_smtlib_sort( value _v_c, value _v_i) { Z3_context c; /*in*/ unsigned int i; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); i = Int_val(_v_i); _res = Z3_get_smtlib_sort(c, i); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_smtlib_error( value _v_c) { Z3_context c; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_smtlib_error(c); _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); camlidl_free(_ctx); return _vres; } /* value camlidl_z3_Z3_parse_z3V3_string( value _v_c, value _v_str) { Z3_context c; /*in Z3_string str; /*in Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_str, &str, _ctx); _res = Z3_parse_z3_string(c, str); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3_Z3_parse_z3V3_file( value _v_c, value _v_file_name) { Z3_context c; /*in Z3_string file_name; /*in Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_file_name, &file_name, _ctx); _res = Z3_parse_z3_file(c, file_name); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } */ value camlidl_z3V3_Z3_get_version(value _unit) { unsigned int *major; /*out*/ unsigned int *minor; /*out*/ unsigned int *build_number; /*out*/ unsigned int *revision_number; /*out*/ unsigned int _c1; unsigned int _c2; unsigned int _c3; unsigned int _c4; value _vresult; value _vres[4] = { 0, 0, 0, 0, }; major = &_c1; minor = &_c2; build_number = &_c3; revision_number = &_c4; Z3_get_version(major, minor, build_number, revision_number); Begin_roots_block(_vres, 4) _vres[0] = Val_int(*major); _vres[1] = Val_int(*minor); _vres[2] = Val_int(*build_number); _vres[3] = Val_int(*revision_number); _vresult = camlidl_alloc_small(4, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; Field(_vresult, 2) = _vres[2]; Field(_vresult, 3) = _vres[3]; End_roots() return _vresult; } value camlidl_z3V3_Z3_reset_memory(value _unit) { Z3_reset_memory(); return Val_unit; } value camlidl_z3V3_Z3_theory_mk_sort( value _v_c, value _v_t, value _v_s) { Z3_context c; /*in*/ Z3_theory t; /*in*/ Z3_symbol s; /*in*/ Z3_sort _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); _res = Z3_theory_mk_sort(c, t, s); _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_theory_mk_value( value _v_c, value _v_t, value _v_n, value _v_s) { Z3_context c; /*in*/ Z3_theory t; /*in*/ Z3_symbol n; /*in*/ Z3_sort s; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_n, &n, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_s, &s, _ctx); _res = Z3_theory_mk_value(c, t, n, s); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_theory_mk_constant( value _v_c, value _v_t, value _v_n, value _v_s) { Z3_context c; /*in*/ Z3_theory t; /*in*/ Z3_symbol n; /*in*/ Z3_sort s; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_n, &n, _ctx); camlidl_ml2c_z3V3_Z3_sort(_v_s, &s, _ctx); _res = Z3_theory_mk_constant(c, t, n, s); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_theory_mk_func_decl( value _v_c, value _v_t, value _v_n, value _v_domain, value _v_range) { Z3_context c; /*in*/ Z3_theory t; /*in*/ Z3_symbol n; /*in*/ unsigned int domain_size; /*in*/ Z3_sort const *domain; /*in*/ Z3_sort range; /*in*/ Z3_func_decl _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_n, &n, _ctx); _c1 = Wosize_val(_v_domain); domain = camlidl_malloc(_c1 * sizeof(Z3_sort const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_domain, _c2); camlidl_ml2c_z3V3_Z3_sort(_v3, &domain[_c2], _ctx); } domain_size = _c1; camlidl_ml2c_z3V3_Z3_sort(_v_range, &range, _ctx); _res = Z3_theory_mk_func_decl(c, t, n, domain_size, domain, range); _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_theory_get_context( value _v_t) { Z3_theory t; /*in*/ Z3_context _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); _res = Z3_theory_get_context(t); _vres = camlidl_c2ml_z3V3_Z3_context(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_theory_assert_axiom( value _v_t, value _v_ax) { Z3_theory t; /*in*/ Z3_ast ax; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_ax, &ax, _ctx); Z3_theory_assert_axiom(t, ax); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_theory_assume_eq( value _v_t, value _v_lhs, value _v_rhs) { Z3_theory t; /*in*/ Z3_ast lhs; /*in*/ Z3_ast rhs; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_lhs, &lhs, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_rhs, &rhs, _ctx); Z3_theory_assume_eq(t, lhs, rhs); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_theory_enable_axiom_simplification( value _v_t, value _v_flag) { Z3_theory t; /*in*/ int flag; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); flag = Int_val(_v_flag); Z3_theory_enable_axiom_simplification(t, flag); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_theory_get_eqc_root( value _v_t, value _v_n) { Z3_theory t; /*in*/ Z3_ast n; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_n, &n, _ctx); _res = Z3_theory_get_eqc_root(t, n); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_theory_get_eqc_next( value _v_t, value _v_n) { Z3_theory t; /*in*/ Z3_ast n; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_n, &n, _ctx); _res = Z3_theory_get_eqc_next(t, n); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_theory_get_num_parents( value _v_t, value _v_n) { Z3_theory t; /*in*/ Z3_ast n; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_n, &n, _ctx); _res = Z3_theory_get_num_parents(t, n); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_theory_get_parent( value _v_t, value _v_n, value _v_i) { Z3_theory t; /*in*/ Z3_ast n; /*in*/ unsigned int i; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_n, &n, _ctx); i = Int_val(_v_i); _res = Z3_theory_get_parent(t, n, i); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_theory_is_value( value _v_t, value _v_n) { Z3_theory t; /*in*/ Z3_ast n; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_n, &n, _ctx); _res = Z3_theory_is_value(t, n); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_theory_is_decl( value _v_t, value _v_d) { Z3_theory t; /*in*/ Z3_func_decl d; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); _res = Z3_theory_is_decl(t, d); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_theory_get_num_elems( value _v_t) { Z3_theory t; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); _res = Z3_theory_get_num_elems(t); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_theory_get_elem( value _v_t, value _v_i) { Z3_theory t; /*in*/ unsigned int i; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); i = Int_val(_v_i); _res = Z3_theory_get_elem(t, i); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_theory_get_num_apps( value _v_t) { Z3_theory t; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); _res = Z3_theory_get_num_apps(t); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_theory_get_app( value _v_t, value _v_i) { Z3_theory t; /*in*/ unsigned int i; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); i = Int_val(_v_i); _res = Z3_theory_get_app(t, i); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_injective_function( value _v_c, value _v_s, value _v_domain, value _v_range) { Z3_context c; /*in*/ Z3_symbol s; /*in*/ unsigned int domain_size; /*in*/ Z3_sort const *domain; /*in*/ Z3_sort range; /*in*/ Z3_func_decl _res; mlsize_t _c1; mlsize_t _c2; value _v3; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); _c1 = Wosize_val(_v_domain); domain = camlidl_malloc(_c1 * sizeof(Z3_sort const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_domain, _c2); camlidl_ml2c_z3V3_Z3_sort(_v3, &domain[_c2], _ctx); } domain_size = _c1; camlidl_ml2c_z3V3_Z3_sort(_v_range, &range, _ctx); _res = Z3_mk_injective_function(c, s, domain_size, domain, range); _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_set_logic( value _v_c, value _v_logic) { Z3_context c; /*in*/ Z3_string logic; /*in*/ int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_string(_v_logic, &logic, _ctx); _res = Z3_set_logic(c, logic); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_push( value _v_c) { Z3_context c; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); Z3_push(c); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_pop( value _v_c, value _v_num_scopes) { Z3_context c; /*in*/ unsigned int num_scopes; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); num_scopes = Int_val(_v_num_scopes); Z3_pop(c, num_scopes); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_get_num_scopes( value _v_c) { Z3_context c; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_num_scopes(c); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_persist_ast( value _v_c, value _v_a, value _v_num_scopes) { Z3_context c; /*in*/ Z3_ast a; /*in*/ unsigned int num_scopes; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); num_scopes = Int_val(_v_num_scopes); Z3_persist_ast(c, a, num_scopes); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_assert_cnstr( value _v_c, value _v_a) { Z3_context c; /*in*/ Z3_ast a; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); Z3_assert_cnstr(c, a); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_check_and_get_model( value _v_c) { Z3_context c; /*in*/ Z3_model *m; /*out*/ Z3_lbool _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; Z3_model _c1; value _vresult; value _vres[2] = { 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); m = &_c1; _res = Z3_check_and_get_model(c, m); Begin_roots_block(_vres, 2) _vres[0] = camlidl_c2ml_z3V3_Z3_lbool(&_res, _ctx); _vres[1] = camlidl_c2ml_z3V3_Z3_model(&*m, _ctx); _vresult = camlidl_alloc_small(2, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_check( value _v_c) { Z3_context c; /*in*/ Z3_lbool _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_check(c); _vres = camlidl_c2ml_z3V3_Z3_lbool(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_check_assumptions( value _v_c, value _v_assumptions, value _v_core_size, value _v_core) { Z3_context c; /*in*/ unsigned int num_assumptions; /*in*/ Z3_ast const *assumptions; /*in*/ Z3_model *m; /*out*/ Z3_ast *proof; /*out*/ unsigned int *core_size; /*in,out*/ Z3_ast *core; /*in,out*/ Z3_lbool _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; mlsize_t _c1; mlsize_t _c2; value _v3; unsigned int _c4; mlsize_t _c5; mlsize_t _c6; value _v7; Z3_model _c8; Z3_ast _c9; mlsize_t _c10; value _v11; value _vresult; value _vres[5] = { 0, 0, 0, 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _c1 = Wosize_val(_v_assumptions); assumptions = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_assumptions, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &assumptions[_c2], _ctx); } num_assumptions = _c1; core_size = &_c4; _c4 = Int_val(_v_core_size); _c5 = Wosize_val(_v_core); core = camlidl_malloc(_c5 * sizeof(Z3_ast ), _ctx); for (_c6 = 0; _c6 < _c5; _c6++) { _v7 = Field(_v_core, _c6); camlidl_ml2c_z3V3_Z3_ast(_v7, &core[_c6], _ctx); } num_assumptions = _c5; m = &_c8; proof = &_c9; _res = Z3_check_assumptions(c, num_assumptions, assumptions, m, proof, core_size, core); Begin_roots_block(_vres, 5) _vres[0] = camlidl_c2ml_z3V3_Z3_lbool(&_res, _ctx); _vres[1] = camlidl_c2ml_z3V3_Z3_model(&*m, _ctx); _vres[2] = camlidl_c2ml_z3V3_Z3_ast(&*proof, _ctx); _vres[3] = Val_int(*core_size); _vres[4] = camlidl_alloc(num_assumptions, 0); Begin_root(_vres[4]) for (_c10 = 0; _c10 < num_assumptions; _c10++) { _v11 = camlidl_c2ml_z3V3_Z3_ast(&core[_c10], _ctx); modify(&Field(_vres[4], _c10), _v11); } End_roots() _vresult = camlidl_alloc_small(5, 0); { mlsize_t _c12; for (_c12 = 0; _c12 < 5; _c12++) Field(_vresult, _c12) = _vres[_c12]; } End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_del_model( value _v_c, value _v_m) { Z3_context c; /*in*/ Z3_model m; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); Z3_del_model(c, m); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_soft_check_cancel( value _v_c) { Z3_context c; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); Z3_soft_check_cancel(c); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_get_search_failure( value _v_c) { Z3_context c; /*in*/ Z3_search_failure _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_search_failure(c); _vres = camlidl_c2ml_z3V3_Z3_search_failure(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_mk_label( value _v_c, value _v_s, value _v_is_pos, value _v_f) { Z3_context c; /*in*/ Z3_symbol s; /*in*/ int is_pos; /*in*/ Z3_ast f; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); is_pos = Int_val(_v_is_pos); camlidl_ml2c_z3V3_Z3_ast(_v_f, &f, _ctx); _res = Z3_mk_label(c, s, is_pos, f); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_relevant_labels( value _v_c) { Z3_context c; /*in*/ Z3_literals _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_relevant_labels(c); _vres = camlidl_c2ml_z3V3_Z3_literals(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_relevant_literals( value _v_c) { Z3_context c; /*in*/ Z3_literals _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_relevant_literals(c); _vres = camlidl_c2ml_z3V3_Z3_literals(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_guessed_literals( value _v_c) { Z3_context c; /*in*/ Z3_literals _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_guessed_literals(c); _vres = camlidl_c2ml_z3V3_Z3_literals(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_del_literals( value _v_c, value _v_lbls) { Z3_context c; /*in*/ Z3_literals lbls; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_literals(_v_lbls, &lbls, _ctx); Z3_del_literals(c, lbls); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_get_num_literals( value _v_c, value _v_lbls) { Z3_context c; /*in*/ Z3_literals lbls; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_literals(_v_lbls, &lbls, _ctx); _res = Z3_get_num_literals(c, lbls); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_label_symbol( value _v_c, value _v_lbls, value _v_idx) { Z3_context c; /*in*/ Z3_literals lbls; /*in*/ unsigned int idx; /*in*/ Z3_symbol _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_literals(_v_lbls, &lbls, _ctx); idx = Int_val(_v_idx); _res = Z3_get_label_symbol(c, lbls, idx); _vres = camlidl_c2ml_z3V3_Z3_symbol(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_literal( value _v_c, value _v_lbls, value _v_idx) { Z3_context c; /*in*/ Z3_literals lbls; /*in*/ unsigned int idx; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_literals(_v_lbls, &lbls, _ctx); idx = Int_val(_v_idx); _res = Z3_get_literal(c, lbls, idx); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_disable_literal( value _v_c, value _v_lbls, value _v_idx) { Z3_context c; /*in*/ Z3_literals lbls; /*in*/ unsigned int idx; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_literals(_v_lbls, &lbls, _ctx); idx = Int_val(_v_idx); Z3_disable_literal(c, lbls, idx); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_block_literals( value _v_c, value _v_lbls) { Z3_context c; /*in*/ Z3_literals lbls; /*in*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_literals(_v_lbls, &lbls, _ctx); Z3_block_literals(c, lbls); camlidl_free(_ctx); return Val_unit; } value camlidl_z3V3_Z3_get_model_num_constants( value _v_c, value _v_m) { Z3_context c; /*in*/ Z3_model m; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); _res = Z3_get_model_num_constants(c, m); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_model_constant( value _v_c, value _v_m, value _v_i) { Z3_context c; /*in*/ Z3_model m; /*in*/ unsigned int i; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); i = Int_val(_v_i); _res = Z3_get_model_constant(c, m, i); _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_model_num_funcs( value _v_c, value _v_m) { Z3_context c; /*in*/ Z3_model m; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); _res = Z3_get_model_num_funcs(c, m); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_model_func_decl( value _v_c, value _v_m, value _v_i) { Z3_context c; /*in*/ Z3_model m; /*in*/ unsigned int i; /*in*/ Z3_func_decl _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); i = Int_val(_v_i); _res = Z3_get_model_func_decl(c, m, i); _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_eval_func_decl( value _v_c, value _v_m, value _v_decl) { Z3_context c; /*in*/ Z3_model m; /*in*/ Z3_func_decl decl; /*in*/ Z3_ast *v; /*out*/ int _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; Z3_ast _c1; value _vresult; value _vres[2] = { 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_decl, &decl, _ctx); v = &_c1; _res = Z3_eval_func_decl(c, m, decl, v); Begin_roots_block(_vres, 2) _vres[0] = Val_int(_res); _vres[1] = camlidl_c2ml_z3V3_Z3_ast(&*v, _ctx); _vresult = camlidl_alloc_small(2, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_is_array_value( value _v_c, value _v_m, value _v_v) { Z3_context c; /*in*/ Z3_model m; /*in*/ Z3_ast v; /*in*/ unsigned int *num_entries; /*out*/ int _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; unsigned int _c1; value _vresult; value _vres[2] = { 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_v, &v, _ctx); num_entries = &_c1; _res = Z3_is_array_value(c, m, v, num_entries); Begin_roots_block(_vres, 2) _vres[0] = Val_int(_res); _vres[1] = Val_int(*num_entries); _vresult = camlidl_alloc_small(2, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_get_array_value( value _v_c, value _v_m, value _v_v, value _v_indices, value _v_values) { Z3_context c; /*in*/ Z3_model m; /*in*/ Z3_ast v; /*in*/ unsigned int num_entries; /*in*/ Z3_ast *indices; /*in,out*/ Z3_ast *values; /*in,out*/ Z3_ast *else_value; /*out*/ struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; mlsize_t _c1; mlsize_t _c2; value _v3; mlsize_t _c4; mlsize_t _c5; value _v6; Z3_ast _c7; mlsize_t _c8; value _v9; mlsize_t _c10; value _v11; value _vresult; value _vres[3] = { 0, 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_v, &v, _ctx); _c1 = Wosize_val(_v_indices); indices = camlidl_malloc(_c1 * sizeof(Z3_ast ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_indices, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &indices[_c2], _ctx); } num_entries = _c1; _c4 = Wosize_val(_v_values); values = camlidl_malloc(_c4 * sizeof(Z3_ast ), _ctx); for (_c5 = 0; _c5 < _c4; _c5++) { _v6 = Field(_v_values, _c5); camlidl_ml2c_z3V3_Z3_ast(_v6, &values[_c5], _ctx); } num_entries = _c4; else_value = &_c7; Z3_get_array_value(c, m, v, num_entries, indices, values, else_value); Begin_roots_block(_vres, 3) _vres[0] = camlidl_alloc(num_entries, 0); Begin_root(_vres[0]) for (_c8 = 0; _c8 < num_entries; _c8++) { _v9 = camlidl_c2ml_z3V3_Z3_ast(&indices[_c8], _ctx); modify(&Field(_vres[0], _c8), _v9); } End_roots() _vres[1] = camlidl_alloc(num_entries, 0); Begin_root(_vres[1]) for (_c10 = 0; _c10 < num_entries; _c10++) { _v11 = camlidl_c2ml_z3V3_Z3_ast(&values[_c10], _ctx); modify(&Field(_vres[1], _c10), _v11); } End_roots() _vres[2] = camlidl_c2ml_z3V3_Z3_ast(&*else_value, _ctx); _vresult = camlidl_alloc_small(3, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; Field(_vresult, 2) = _vres[2]; End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_get_model_func_else( value _v_c, value _v_m, value _v_i) { Z3_context c; /*in*/ Z3_model m; /*in*/ unsigned int i; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); i = Int_val(_v_i); _res = Z3_get_model_func_else(c, m, i); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_model_func_num_entries( value _v_c, value _v_m, value _v_i) { Z3_context c; /*in*/ Z3_model m; /*in*/ unsigned int i; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); i = Int_val(_v_i); _res = Z3_get_model_func_num_entries(c, m, i); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_model_func_entry_num_args( value _v_c, value _v_m, value _v_i, value _v_j) { Z3_context c; /*in*/ Z3_model m; /*in*/ unsigned int i; /*in*/ unsigned int j; /*in*/ unsigned int _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); i = Int_val(_v_i); j = Int_val(_v_j); _res = Z3_get_model_func_entry_num_args(c, m, i, j); _vres = Val_int(_res); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_model_func_entry_arg( value _v_c, value _v_m, value _v_i, value _v_j, value _v_k) { Z3_context c; /*in*/ Z3_model m; /*in*/ unsigned int i; /*in*/ unsigned int j; /*in*/ unsigned int k; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); i = Int_val(_v_i); j = Int_val(_v_j); k = Int_val(_v_k); _res = Z3_get_model_func_entry_arg(c, m, i, j, k); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_model_func_entry_value( value _v_c, value _v_m, value _v_i, value _v_j) { Z3_context c; /*in*/ Z3_model m; /*in*/ unsigned int i; /*in*/ unsigned int j; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); i = Int_val(_v_i); j = Int_val(_v_j); _res = Z3_get_model_func_entry_value(c, m, i, j); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_eval( value _v_c, value _v_m, value _v_t) { Z3_context c; /*in*/ Z3_model m; /*in*/ Z3_ast t; /*in*/ Z3_ast *v; /*out*/ int _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; Z3_ast _c1; value _vresult; value _vres[2] = { 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); camlidl_ml2c_z3V3_Z3_ast(_v_t, &t, _ctx); v = &_c1; _res = Z3_eval(c, m, t, v); Begin_roots_block(_vres, 2) _vres[0] = Val_int(_res); _vres[1] = camlidl_c2ml_z3V3_Z3_ast(&*v, _ctx); _vresult = camlidl_alloc_small(2, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_eval_decl( value _v_c, value _v_m, value _v_d, value _v_args) { Z3_context c; /*in*/ Z3_model m; /*in*/ Z3_func_decl d; /*in*/ unsigned int num_args; /*in*/ Z3_ast const *args; /*in*/ Z3_ast *v; /*out*/ int _res; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; mlsize_t _c1; mlsize_t _c2; value _v3; Z3_ast _c4; value _vresult; value _vres[2] = { 0, 0, }; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); _c1 = Wosize_val(_v_args); args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); for (_c2 = 0; _c2 < _c1; _c2++) { _v3 = Field(_v_args, _c2); camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); } num_args = _c1; v = &_c4; _res = Z3_eval_decl(c, m, d, num_args, args, v); Begin_roots_block(_vres, 2) _vres[0] = Val_int(_res); _vres[1] = camlidl_c2ml_z3V3_Z3_ast(&*v, _ctx); _vresult = camlidl_alloc_small(2, 0); Field(_vresult, 0) = _vres[0]; Field(_vresult, 1) = _vres[1]; End_roots() camlidl_free(_ctx); return _vresult; } value camlidl_z3V3_Z3_context_to_string( value _v_c) { Z3_context c; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_context_to_string(c); _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_statistics_to_string( value _v_c) { Z3_context c; /*in*/ Z3_string _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_statistics_to_string(c); _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); camlidl_free(_ctx); return _vres; } value camlidl_z3V3_Z3_get_context_assignment( value _v_c) { Z3_context c; /*in*/ Z3_ast _res; value _vres; struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); _res = Z3_get_context_assignment(c); _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); camlidl_free(_ctx); return _vres; } z3-4.4.0/src/api/ml/META0000644000175000017500000000052712540347414014272 0ustar michaelmichael# META file for the "z3" package: version = "VERSION" description = "Z3 Theorem Prover (OCaml API)" requires = "num" archive(byte) = "z3ml.cma" archive(native) = "z3ml.cmxa" archive(byte,plugin) = "z3ml.cma" archive(native,plugin) = "z3ml.cmxa" archive(byte,toploop) = "z3ml.cma" archive(native,toploop) = "z3ml.cmxa" linkopts = "-cclib -lz3" z3-4.4.0/src/api/ml/Makefile0000644000175000017500000000042012540347414015251 0ustar michaelmichael# This is a temporary support file for emacs annotations. # It does not compile the Z3 ML API; this will be built # in the top-level build directory. all: ocamlbuild -cflag -annot z3.cmxa doc: *.ml mkdir -p doc ocamldoc -html -d doc -I _build -sort *.mli -hide Z3 z3-4.4.0/src/api/ml/z3.ml0000644000175000017500000043554212540347414014520 0ustar michaelmichael(** The Z3 ML/OCaml Interface. Copyright (C) 2012 Microsoft Corporation @author CM Wintersteiger (cwinter) 2012-12-17 *) open Z3enums exception Error = Z3native.Exception (* Some helpers. *) let null = Z3native.mk_null() let is_null o = (Z3native.is_null o) (* Internal types *) type z3_native_context = { m_n_ctx : Z3native.z3_context; m_n_obj_cnt: int; } type context = z3_native_context type z3_native_object = { m_ctx : context ; mutable m_n_obj : Z3native.ptr ; inc_ref : Z3native.z3_context -> Z3native.ptr -> unit; dec_ref : Z3native.z3_context -> Z3native.ptr -> unit } (** Internal stuff *) module Internal = struct let dispose_context ctx = if ctx.m_n_obj_cnt == 0 then ( (Z3native.del_context ctx.m_n_ctx) ) else ( Printf.printf "ERROR: NOT DISPOSING CONTEXT (because it still has %d objects alive)\n" ctx.m_n_obj_cnt; ) let create_context settings = let cfg = Z3native.mk_config () in let f e = (Z3native.set_param_value cfg (fst e) (snd e)) in (List.iter f settings) ; let v = Z3native.mk_context_rc cfg in Z3native.del_config(cfg) ; Z3native.set_ast_print_mode v (int_of_ast_print_mode PRINT_SMTLIB2_COMPLIANT) ; Z3native.set_internal_error_handler v ; let res = { m_n_ctx = v; m_n_obj_cnt = 0 } in let f = fun o -> dispose_context o in Gc.finalise f res; res let context_add1 ctx = ignore (ctx.m_n_obj_cnt = ctx.m_n_obj_cnt + 1) let context_sub1 ctx = ignore (ctx.m_n_obj_cnt = ctx.m_n_obj_cnt - 1) let context_gno ctx = ctx.m_n_ctx let z3obj_gc o = o.m_ctx let z3obj_gnc o = (context_gno o.m_ctx) let z3obj_gno o = o.m_n_obj let z3obj_sno o ctx no = (context_add1 ctx) ; o.inc_ref (context_gno ctx) no ; ( if not (is_null o.m_n_obj) then o.dec_ref (context_gno ctx) o.m_n_obj ; (context_sub1 ctx) ) ; o.m_n_obj <- no let z3obj_dispose o = if not (is_null o.m_n_obj) then ( o.dec_ref (z3obj_gnc o) o.m_n_obj ; (context_sub1 (z3obj_gc o)) ) ; o.m_n_obj <- null let z3obj_create o = let f = fun o -> (z3obj_dispose o) in Gc.finalise f o let z3obj_nil_ref x y = () let z3_native_object_of_ast_ptr : context -> Z3native.ptr -> z3_native_object = fun ctx no -> let res : z3_native_object = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.inc_ref ; dec_ref = Z3native.dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res end open Internal module Log = struct let open_ filename = ((lbool_of_int (Z3native.open_log filename)) == L_TRUE) let close = Z3native.close_log let append s = Z3native.append_log s end module Version = struct let major = let (x, _, _, _) = Z3native.get_version () in x let minor = let (_, x, _, _) = Z3native.get_version () in x let build = let (_, _, x, _) = Z3native.get_version () in x let revision = let (_, _, _, x) = Z3native.get_version () in x let to_string = let (mj, mn, bld, rev) = Z3native.get_version () in string_of_int mj ^ "." ^ string_of_int mn ^ "." ^ string_of_int bld ^ "." ^ string_of_int rev end let mk_list ( f : int -> 'a ) ( n : int ) = let rec mk_list' ( f : int -> 'a ) ( i : int ) ( n : int ) ( tail : 'a list ) : 'a list = if (i >= n) then tail else (f i) :: (mk_list' f (i+1) n tail) in mk_list' f 0 n [] let list_of_array ( x : _ array ) = let f i = (Array.get x i) in mk_list f (Array.length x) let mk_context ( cfg : ( string * string ) list ) = create_context cfg module Symbol = struct type symbol = z3_native_object let create_i ( ctx : context ) ( no : Z3native.ptr ) = let res : symbol = { m_ctx = ctx ; m_n_obj = null ; inc_ref = z3obj_nil_ref ; dec_ref = z3obj_nil_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res let create_s ( ctx : context ) ( no : Z3native.ptr ) = let res : symbol = { m_ctx = ctx ; m_n_obj = null ; inc_ref = z3obj_nil_ref ; dec_ref = z3obj_nil_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res let create ( ctx : context ) ( no : Z3native.ptr ) = match (symbol_kind_of_int (Z3native.get_symbol_kind (context_gno ctx) no)) with | INT_SYMBOL -> (create_i ctx no) | STRING_SYMBOL -> (create_s ctx no) let gc ( x : symbol ) = (z3obj_gc x) let gnc ( x : symbol ) = (z3obj_gnc x) let gno ( x : symbol ) = (z3obj_gno x) let symbol_lton ( a : symbol list ) = let f ( e : symbol ) = (gno e) in Array.of_list (List.map f a) let kind ( o : symbol ) = (symbol_kind_of_int (Z3native.get_symbol_kind (gnc o) (gno o))) let is_int_symbol ( o : symbol ) = (kind o) == INT_SYMBOL let is_string_symbol ( o : symbol ) = (kind o) == STRING_SYMBOL let get_int (o : symbol) = Z3native.get_symbol_int (z3obj_gnc o) (z3obj_gno o) let get_string (o : symbol ) = Z3native.get_symbol_string (z3obj_gnc o) (z3obj_gno o) let to_string ( o : symbol ) = match (kind o) with | INT_SYMBOL -> (string_of_int (Z3native.get_symbol_int (gnc o) (gno o))) | STRING_SYMBOL -> (Z3native.get_symbol_string (gnc o) (gno o)) let mk_int ( ctx : context ) ( i : int ) = (create_i ctx (Z3native.mk_int_symbol (context_gno ctx) i)) let mk_string ( ctx : context ) ( s : string ) = (create_s ctx (Z3native.mk_string_symbol (context_gno ctx) s)) let mk_ints ( ctx : context ) ( names : int list ) = let f elem = mk_int ( ctx : context ) elem in (List.map f names) let mk_strings ( ctx : context ) ( names : string list ) = let f elem = mk_string ( ctx : context ) elem in (List.map f names) end module rec AST : sig type ast = z3_native_object val context_of_ast : ast -> context val nc_of_ast : ast -> Z3native.z3_context val ptr_of_ast : ast -> Z3native.ptr val ast_of_ptr : context -> Z3native.ptr -> ast module ASTVector : sig type ast_vector = z3_native_object val create : context -> Z3native.ptr -> ast_vector val mk_ast_vector : context -> ast_vector val get_size : ast_vector -> int val get : ast_vector -> int -> ast val set : ast_vector -> int -> ast -> unit val resize : ast_vector -> int -> unit val push : ast_vector -> ast -> unit val translate : ast_vector -> context -> ast_vector val to_list : ast_vector -> ast list val to_expr_list : ast_vector -> Expr.expr list val to_string : ast_vector -> string end module ASTMap : sig type ast_map = z3_native_object val create : context -> Z3native.ptr -> ast_map val mk_ast_map : context -> ast_map val contains : ast_map -> ast -> bool val find : ast_map -> ast -> ast val insert : ast_map -> ast -> ast -> unit val erase : ast_map -> ast -> unit val reset : ast_map -> unit val get_size : ast_map -> int val get_keys : ast_map -> Expr.expr list val to_string : ast_map -> string end val hash : ast -> int val get_id : ast -> int val get_ast_kind : ast -> Z3enums.ast_kind val is_expr : ast -> bool val is_app : ast -> bool val is_var : ast -> bool val is_quantifier : ast -> bool val is_sort : ast -> bool val is_func_decl : ast -> bool val to_string : ast -> string val to_sexpr : ast -> string val equal : ast -> ast -> bool val compare : ast -> ast -> int val translate : ast -> context -> ast val unwrap_ast : ast -> Z3native.ptr val wrap_ast : context -> Z3native.z3_ast -> ast end = struct type ast = z3_native_object let context_of_ast ( x : ast ) = (z3obj_gc x) let nc_of_ast ( x : ast ) = (z3obj_gnc x) let ptr_of_ast ( x : ast ) = (z3obj_gno x) let rec ast_of_ptr : context -> Z3native.ptr -> ast = fun ctx no -> match (ast_kind_of_int (Z3native.get_ast_kind (context_gno ctx) no)) with | FUNC_DECL_AST | SORT_AST | QUANTIFIER_AST | APP_AST | NUMERAL_AST | VAR_AST -> z3_native_object_of_ast_ptr ctx no | UNKNOWN_AST -> raise (Z3native.Exception "Cannot create asts of type unknown") module ASTVector = struct type ast_vector = z3_native_object let create ( ctx : context ) ( no : Z3native.ptr ) = let res : ast_vector = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.ast_vector_inc_ref ; dec_ref = Z3native.ast_vector_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res let mk_ast_vector ( ctx : context ) = (create ctx (Z3native.mk_ast_vector (context_gno ctx))) let get_size ( x : ast_vector ) = Z3native.ast_vector_size (z3obj_gnc x) (z3obj_gno x) let get ( x : ast_vector ) ( i : int ) = ast_of_ptr (z3obj_gc x) (Z3native.ast_vector_get (z3obj_gnc x) (z3obj_gno x) i) let set ( x : ast_vector ) ( i : int ) ( value : ast ) = Z3native.ast_vector_set (z3obj_gnc x) (z3obj_gno x) i (z3obj_gno value) let resize ( x : ast_vector ) ( new_size : int ) = Z3native.ast_vector_resize (z3obj_gnc x) (z3obj_gno x) new_size let push ( x : ast_vector ) ( a : ast ) = Z3native.ast_vector_push (z3obj_gnc x) (z3obj_gno x) (z3obj_gno a) let translate ( x : ast_vector ) ( to_ctx : context ) = create to_ctx (Z3native.ast_vector_translate (z3obj_gnc x) (z3obj_gno x) (context_gno to_ctx)) let to_list ( x : ast_vector ) = let xs = (get_size x) in let f i = (get x i) in mk_list f xs let to_expr_list ( x : ast_vector ) = let xs = (get_size x) in let f i = (Expr.expr_of_ptr (z3obj_gc x) (z3obj_gno (get x i))) in mk_list f xs let to_string ( x : ast_vector ) = Z3native.ast_vector_to_string (z3obj_gnc x) (z3obj_gno x) end module ASTMap = struct type ast_map = z3_native_object let create ( ctx : context ) ( no : Z3native.ptr ) = let res : ast_map = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.ast_map_inc_ref ; dec_ref = Z3native.ast_map_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res let mk_ast_map ( ctx : context ) = (create ctx (Z3native.mk_ast_map (context_gno ctx))) let astmap_of_ptr ( ctx : context ) ( no : Z3native.ptr ) = let res : ast_map = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.ast_map_inc_ref ; dec_ref = Z3native.ast_map_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res let contains ( x : ast_map ) ( key : ast ) = Z3native.ast_map_contains (z3obj_gnc x) (z3obj_gno x) (z3obj_gno key) let find ( x : ast_map ) ( key : ast ) = ast_of_ptr (z3obj_gc x) (Z3native.ast_map_find (z3obj_gnc x) (z3obj_gno x) (z3obj_gno key)) let insert ( x : ast_map ) ( key : ast ) ( value : ast ) = Z3native.ast_map_insert (z3obj_gnc x) (z3obj_gno x) (z3obj_gno key) (z3obj_gno value) let erase ( x : ast_map ) ( key : ast ) = Z3native.ast_map_erase (z3obj_gnc x) (z3obj_gno x) (z3obj_gno key) let reset ( x : ast_map ) = Z3native.ast_map_reset (z3obj_gnc x) (z3obj_gno x) let get_size ( x : ast_map ) = Z3native.ast_map_size (z3obj_gnc x) (z3obj_gno x) let get_keys ( x : ast_map ) = let av = ASTVector.create (z3obj_gc x) (Z3native.ast_map_keys (z3obj_gnc x) (z3obj_gno x)) in (ASTVector.to_expr_list av) let to_string ( x : ast_map ) = Z3native.ast_map_to_string (z3obj_gnc x) (z3obj_gno x) end let hash ( x : ast ) = Z3native.get_ast_hash (z3obj_gnc x) (z3obj_gno x) let get_id ( x : ast ) = Z3native.get_ast_id (z3obj_gnc x) (z3obj_gno x) let get_ast_kind ( x : ast ) = (ast_kind_of_int (Z3native.get_ast_kind (z3obj_gnc x) (z3obj_gno x))) let is_expr ( x : ast ) = match get_ast_kind ( x : ast ) with | APP_AST | NUMERAL_AST | QUANTIFIER_AST | VAR_AST -> true | _ -> false let is_app ( x : ast ) = (get_ast_kind x) == APP_AST let is_var ( x : ast ) = (get_ast_kind x) == VAR_AST let is_quantifier ( x : ast ) = (get_ast_kind x) == QUANTIFIER_AST let is_sort ( x : ast ) = (get_ast_kind x) == SORT_AST let is_func_decl ( x : ast ) = (get_ast_kind x) == FUNC_DECL_AST let to_string ( x : ast ) = Z3native.ast_to_string (z3obj_gnc x) (z3obj_gno x) let to_sexpr ( x : ast ) = Z3native.ast_to_string (z3obj_gnc x) (z3obj_gno x) let equal ( a : ast ) ( b : ast ) = (a == b) || if (z3obj_gnc a) != (z3obj_gnc b) then false else Z3native.is_eq_ast (z3obj_gnc a) (z3obj_gno a) (z3obj_gno b) let compare a b = if (get_id a) < (get_id b) then -1 else if (get_id a) > (get_id b) then 1 else 0 let translate ( x : ast ) ( to_ctx : context ) = if (z3obj_gnc x) == (context_gno to_ctx) then x else ast_of_ptr to_ctx (Z3native.translate (z3obj_gnc x) (z3obj_gno x) (context_gno to_ctx)) let unwrap_ast ( x : ast ) = (z3obj_gno x) let wrap_ast ( ctx : context ) ( ptr : Z3native.ptr ) = ast_of_ptr ctx ptr end and Sort : sig type sort = Sort of AST.ast val ast_of_sort : Sort.sort -> AST.ast val sort_of_ptr : context -> Z3native.ptr -> sort val gc : sort -> context val gnc : sort -> Z3native.ptr val gno : sort -> Z3native.ptr val sort_lton : sort list -> Z3native.ptr array val sort_option_lton : sort option list -> Z3native.ptr array val equal : sort -> sort -> bool val get_id : sort -> int val get_sort_kind : sort -> Z3enums.sort_kind val get_name : sort -> Symbol.symbol val to_string : sort -> string val mk_uninterpreted : context -> Symbol.symbol -> sort val mk_uninterpreted_s : context -> string -> sort end = struct type sort = Sort of AST.ast let sort_of_ptr : context -> Z3native.ptr -> sort = fun ctx no -> if ((Z3enums.ast_kind_of_int (Z3native.get_ast_kind (context_gno ctx) no)) != Z3enums.SORT_AST) then raise (Z3native.Exception "Invalid coercion") else match (sort_kind_of_int (Z3native.get_sort_kind (context_gno ctx) no)) with | ARRAY_SORT | BOOL_SORT | BV_SORT | DATATYPE_SORT | INT_SORT | REAL_SORT | UNINTERPRETED_SORT | FINITE_DOMAIN_SORT | RELATION_SORT | FLOATING_POINT_SORT | ROUNDING_MODE_SORT -> Sort(z3_native_object_of_ast_ptr ctx no) | UNKNOWN_SORT -> raise (Z3native.Exception "Unknown sort kind encountered") let ast_of_sort s = match s with Sort(x) -> x let gc ( x : sort ) = (match x with Sort(a) -> (z3obj_gc a)) let gnc ( x : sort ) = (match x with Sort(a) -> (z3obj_gnc a)) let gno ( x : sort ) = (match x with Sort(a) -> (z3obj_gno a)) let sort_lton ( a : sort list ) = let f ( e : sort ) = match e with Sort(a) -> (AST.ptr_of_ast a) in Array.of_list (List.map f a) let sort_option_lton ( a : sort option list ) = let f ( e : sort option ) = match e with None -> null | Some(Sort(a)) -> (AST.ptr_of_ast a) in Array.of_list (List.map f a) let equal : sort -> sort -> bool = fun a b -> (a == b) || if (gnc a) != (gnc b) then false else (Z3native.is_eq_sort (gnc a) (gno a) (gno b)) let get_id ( x : sort ) = Z3native.get_sort_id (gnc x) (gno x) let get_sort_kind ( x : sort ) = (sort_kind_of_int (Z3native.get_sort_kind (gnc x) (gno x))) let get_name ( x : sort ) = (Symbol.create (gc x) (Z3native.get_sort_name (gnc x) (gno x))) let to_string ( x : sort ) = Z3native.sort_to_string (gnc x) (gno x) let mk_uninterpreted ( ctx : context ) ( s : Symbol.symbol ) = let res = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.inc_ref ; dec_ref = Z3native.dec_ref } in (z3obj_sno res ctx (Z3native.mk_uninterpreted_sort (context_gno ctx) (Symbol.gno s))) ; (z3obj_create res) ; Sort(res) let mk_uninterpreted_s ( ctx : context ) ( s : string ) = mk_uninterpreted ctx (Symbol.mk_string ( ctx : context ) s) end and FuncDecl : sig type func_decl = FuncDecl of AST.ast val ast_of_func_decl : FuncDecl.func_decl -> AST.ast val func_decl_of_ptr : context -> Z3native.ptr -> func_decl val gc : func_decl -> context val gnc : func_decl -> Z3native.ptr val gno : func_decl -> Z3native.ptr module Parameter : sig type parameter = P_Int of int | P_Dbl of float | P_Sym of Symbol.symbol | P_Srt of Sort.sort | P_Ast of AST.ast | P_Fdl of func_decl | P_Rat of string val get_kind : parameter -> Z3enums.parameter_kind val get_int : parameter -> int val get_float : parameter -> float val get_symbol : parameter -> Symbol.symbol val get_sort : parameter -> Sort.sort val get_ast : parameter -> AST.ast val get_func_decl : parameter -> func_decl val get_rational : parameter -> string end val mk_func_decl : context -> Symbol.symbol -> Sort.sort list -> Sort.sort -> func_decl val mk_func_decl_s : context -> string -> Sort.sort list -> Sort.sort -> func_decl val mk_fresh_func_decl : context -> string -> Sort.sort list -> Sort.sort -> func_decl val mk_const_decl : context -> Symbol.symbol -> Sort.sort -> func_decl val mk_const_decl_s : context -> string -> Sort.sort -> func_decl val mk_fresh_const_decl : context -> string -> Sort.sort -> func_decl val equal : func_decl -> func_decl -> bool val to_string : func_decl -> string val get_id : func_decl -> int val get_arity : func_decl -> int val get_domain_size : func_decl -> int val get_domain : func_decl -> Sort.sort list val get_range : func_decl -> Sort.sort val get_decl_kind : func_decl -> Z3enums.decl_kind val get_name : func_decl -> Symbol.symbol val get_num_parameters : func_decl -> int val get_parameters : func_decl -> Parameter.parameter list val apply : func_decl -> Expr.expr list -> Expr.expr end = struct type func_decl = FuncDecl of AST.ast let func_decl_of_ptr : context -> Z3native.ptr -> func_decl = fun ctx no -> if ((Z3enums.ast_kind_of_int (Z3native.get_ast_kind (context_gno ctx) no)) != Z3enums.FUNC_DECL_AST) then raise (Z3native.Exception "Invalid coercion") else FuncDecl(z3_native_object_of_ast_ptr ctx no) let ast_of_func_decl f = match f with FuncDecl(x) -> x let create_ndr ( ctx : context ) ( name : Symbol.symbol ) ( domain : Sort.sort list ) ( range : Sort.sort ) = let res = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.inc_ref ; dec_ref = Z3native.dec_ref } in (z3obj_sno res ctx (Z3native.mk_func_decl (context_gno ctx) (Symbol.gno name) (List.length domain) (Sort.sort_lton domain) (Sort.gno range))) ; (z3obj_create res) ; FuncDecl(res) let create_pdr ( ctx : context) ( prefix : string ) ( domain : Sort.sort list ) ( range : Sort.sort ) = let res = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.inc_ref ; dec_ref = Z3native.dec_ref } in (z3obj_sno res ctx (Z3native.mk_fresh_func_decl (context_gno ctx) prefix (List.length domain) (Sort.sort_lton domain) (Sort.gno range))) ; (z3obj_create res) ; FuncDecl(res) let gc ( x : func_decl ) = match x with FuncDecl(a) -> (z3obj_gc a) let gnc ( x : func_decl ) = match x with FuncDecl(a) -> (z3obj_gnc a) let gno ( x : func_decl ) = match x with FuncDecl(a) -> (z3obj_gno a) module Parameter = struct type parameter = | P_Int of int | P_Dbl of float | P_Sym of Symbol.symbol | P_Srt of Sort.sort | P_Ast of AST.ast | P_Fdl of func_decl | P_Rat of string let get_kind ( x : parameter ) = (match x with | P_Int(_) -> PARAMETER_INT | P_Dbl(_) -> PARAMETER_DOUBLE | P_Sym(_) -> PARAMETER_SYMBOL | P_Srt(_) -> PARAMETER_SORT | P_Ast(_) -> PARAMETER_AST | P_Fdl(_) -> PARAMETER_FUNC_DECL | P_Rat(_) -> PARAMETER_RATIONAL) let get_int ( x : parameter ) = match x with | P_Int(x) -> x | _ -> raise (Z3native.Exception "parameter is not an int") let get_float ( x : parameter ) = match x with | P_Dbl(x) -> x | _ -> raise (Z3native.Exception "parameter is not a float") let get_symbol ( x : parameter ) = match x with | P_Sym(x) -> x | _ -> raise (Z3native.Exception "parameter is not a symbol") let get_sort ( x : parameter ) = match x with | P_Srt(x) -> x | _ -> raise (Z3native.Exception "parameter is not a sort") let get_ast ( x : parameter ) = match x with | P_Ast(x) -> x | _ -> raise (Z3native.Exception "parameter is not an ast") let get_func_decl ( x : parameter ) = match x with | P_Fdl(x) -> x | _ -> raise (Z3native.Exception "parameter is not a func_decl") let get_rational ( x : parameter ) = match x with | P_Rat(x) -> x | _ -> raise (Z3native.Exception "parameter is not a rational string") end let mk_func_decl ( ctx : context ) ( name : Symbol.symbol ) ( domain : Sort.sort list ) ( range : Sort.sort ) = create_ndr ctx name domain range let mk_func_decl_s ( ctx : context ) ( name : string ) ( domain : Sort.sort list ) ( range : Sort.sort ) = mk_func_decl ctx (Symbol.mk_string ctx name) domain range let mk_fresh_func_decl ( ctx : context ) ( prefix : string ) ( domain : Sort.sort list ) ( range : Sort.sort ) = create_pdr ctx prefix domain range let mk_const_decl ( ctx : context ) ( name : Symbol.symbol ) ( range : Sort.sort ) = create_ndr ctx name [] range let mk_const_decl_s ( ctx : context ) ( name : string ) ( range : Sort.sort ) = create_ndr ctx (Symbol.mk_string ctx name) [] range let mk_fresh_const_decl ( ctx : context ) ( prefix : string ) ( range : Sort.sort ) = create_pdr ctx prefix [] range let equal ( a : func_decl ) ( b : func_decl ) = (a == b) || if (gnc a) != (gnc b) then false else (Z3native.is_eq_func_decl (gnc a) (gno a) (gno b)) let to_string ( x : func_decl ) = Z3native.func_decl_to_string (gnc x) (gno x) let get_id ( x : func_decl ) = Z3native.get_func_decl_id (gnc x) (gno x) let get_arity ( x : func_decl ) = Z3native.get_arity (gnc x) (gno x) let get_domain_size ( x : func_decl ) = Z3native.get_domain_size (gnc x) (gno x) let get_domain ( x : func_decl ) = let n = (get_domain_size x) in let f i = Sort.sort_of_ptr (gc x) (Z3native.get_domain (gnc x) (gno x) i) in mk_list f n let get_range ( x : func_decl ) = Sort.sort_of_ptr (gc x) (Z3native.get_range (gnc x) (gno x)) let get_decl_kind ( x : func_decl ) = (decl_kind_of_int (Z3native.get_decl_kind (gnc x) (gno x))) let get_name ( x : func_decl ) = (Symbol.create (gc x) (Z3native.get_decl_name (gnc x) (gno x))) let get_num_parameters ( x : func_decl ) = (Z3native.get_decl_num_parameters (gnc x) (gno x)) let get_parameters ( x : func_decl ) = let n = (get_num_parameters x) in let f i = (match (parameter_kind_of_int (Z3native.get_decl_parameter_kind (gnc x) (gno x) i)) with | PARAMETER_INT -> Parameter.P_Int (Z3native.get_decl_int_parameter (gnc x) (gno x) i) | PARAMETER_DOUBLE -> Parameter.P_Dbl (Z3native.get_decl_double_parameter (gnc x) (gno x) i) | PARAMETER_SYMBOL-> Parameter.P_Sym (Symbol.create (gc x) (Z3native.get_decl_symbol_parameter (gnc x) (gno x) i)) | PARAMETER_SORT -> Parameter.P_Srt (Sort.sort_of_ptr (gc x) (Z3native.get_decl_sort_parameter (gnc x) (gno x) i)) | PARAMETER_AST -> Parameter.P_Ast (AST.ast_of_ptr (gc x) (Z3native.get_decl_ast_parameter (gnc x) (gno x) i)) | PARAMETER_FUNC_DECL -> Parameter.P_Fdl (func_decl_of_ptr (gc x) (Z3native.get_decl_func_decl_parameter (gnc x) (gno x) i)) | PARAMETER_RATIONAL -> Parameter.P_Rat (Z3native.get_decl_rational_parameter (gnc x) (gno x) i) ) in mk_list f n let apply ( x : func_decl ) ( args : Expr.expr list ) = Expr.expr_of_func_app (gc x) x args end and Params : sig type params = z3_native_object module ParamDescrs : sig type param_descrs val param_descrs_of_ptr : context -> Z3native.ptr -> param_descrs val validate : param_descrs -> params -> unit val get_kind : param_descrs -> Symbol.symbol -> Z3enums.param_kind val get_names : param_descrs -> Symbol.symbol list val get_size : param_descrs -> int val to_string : param_descrs -> string end val add_bool : params -> Symbol.symbol -> bool -> unit val add_int : params -> Symbol.symbol -> int -> unit val add_float : params -> Symbol.symbol -> float -> unit val add_symbol : params -> Symbol.symbol -> Symbol.symbol -> unit val mk_params : context -> params val to_string : params -> string val update_param_value : context -> string -> string -> unit val set_print_mode : context -> Z3enums.ast_print_mode -> unit end = struct type params = z3_native_object module ParamDescrs = struct type param_descrs = z3_native_object let param_descrs_of_ptr ( ctx : context ) ( no : Z3native.ptr ) = let res : param_descrs = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.param_descrs_inc_ref ; dec_ref = Z3native.param_descrs_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res let validate ( x : param_descrs ) ( p : params ) = Z3native.params_validate (z3obj_gnc x) (z3obj_gno p) (z3obj_gno x) let get_kind ( x : param_descrs ) ( name : Symbol.symbol ) = (param_kind_of_int (Z3native.param_descrs_get_kind (z3obj_gnc x) (z3obj_gno x) (Symbol.gno name))) let get_names ( x : param_descrs ) = let n = Z3native.param_descrs_size (z3obj_gnc x) (z3obj_gno x) in let f i = Symbol.create (z3obj_gc x) (Z3native.param_descrs_get_name (z3obj_gnc x) (z3obj_gno x) i) in mk_list f n let get_size ( x : param_descrs ) = Z3native.param_descrs_size (z3obj_gnc x) (z3obj_gno x) let to_string ( x : param_descrs ) = Z3native.param_descrs_to_string (z3obj_gnc x) (z3obj_gno x) end let add_bool ( x : params ) ( name : Symbol.symbol ) ( value : bool ) = Z3native.params_set_bool (z3obj_gnc x) (z3obj_gno x) (Symbol.gno name) value let add_int ( x : params ) (name : Symbol.symbol ) ( value : int ) = Z3native.params_set_uint (z3obj_gnc x) (z3obj_gno x) (Symbol.gno name) value let add_float ( x : params ) ( name : Symbol.symbol ) ( value : float ) = Z3native.params_set_double (z3obj_gnc x) (z3obj_gno x) (Symbol.gno name) value let add_symbol ( x : params ) ( name : Symbol.symbol ) ( value : Symbol.symbol ) = Z3native.params_set_symbol (z3obj_gnc x) (z3obj_gno x) (Symbol.gno name) (Symbol.gno value) let mk_params ( ctx : context ) = let res : params = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.params_inc_ref ; dec_ref = Z3native.params_dec_ref } in (z3obj_sno res ctx (Z3native.mk_params (context_gno ctx))) ; (z3obj_create res) ; res let to_string ( x : params ) = Z3native.params_to_string (z3obj_gnc x) (z3obj_gno x) let update_param_value ( ctx : context ) ( id : string) ( value : string )= Z3native.update_param_value (context_gno ctx) id value let set_print_mode ( ctx : context ) ( value : ast_print_mode ) = Z3native.set_ast_print_mode (context_gno ctx) (int_of_ast_print_mode value) end (** General expressions (terms) *) and Expr : sig type expr = Expr of AST.ast val expr_of_ptr : context -> Z3native.ptr -> expr val gc : expr -> context val gnc : expr -> Z3native.ptr val gno : expr -> Z3native.ptr val expr_lton : expr list -> Z3native.ptr array val ast_of_expr : expr -> AST.ast val expr_of_ast : AST.ast -> expr val expr_of_func_app : context -> FuncDecl.func_decl -> expr list -> expr val simplify : expr -> Params.params option -> expr val get_simplify_help : context -> string val get_simplify_parameter_descrs : context -> Params.ParamDescrs.param_descrs val get_func_decl : expr -> FuncDecl.func_decl val get_num_args : expr -> int val get_args : expr -> expr list val update : expr -> expr list -> expr val substitute : expr -> expr list -> expr list -> expr val substitute_one : expr -> expr -> expr -> expr val substitute_vars : expr -> expr list -> expr val translate : expr -> context -> expr val to_string : expr -> string val is_numeral : expr -> bool val is_well_sorted : expr -> bool val get_sort : expr -> Sort.sort val is_const : expr -> bool val mk_const : context -> Symbol.symbol -> Sort.sort -> expr val mk_const_s : context -> string -> Sort.sort -> expr val mk_const_f : context -> FuncDecl.func_decl -> expr val mk_fresh_const : context -> string -> Sort.sort -> expr val mk_app : context -> FuncDecl.func_decl -> expr list -> expr val mk_numeral_string : context -> string -> Sort.sort -> expr val mk_numeral_int : context -> int -> Sort.sort -> expr val equal : expr -> expr -> bool val compare : expr -> expr -> int end = struct type expr = Expr of AST.ast let gc e = match e with Expr(a) -> (z3obj_gc a) let gnc e = match e with Expr(a) -> (z3obj_gnc a) let gno e = match e with Expr(a) -> (z3obj_gno a) let expr_of_ptr : context -> Z3native.ptr -> expr = fun ctx no -> if ast_kind_of_int (Z3native.get_ast_kind (context_gno ctx) no) == QUANTIFIER_AST then Expr(z3_native_object_of_ast_ptr ctx no) else let s = Z3native.get_sort (context_gno ctx) no in let sk = (sort_kind_of_int (Z3native.get_sort_kind (context_gno ctx) s)) in if (Z3native.is_algebraic_number (context_gno ctx) no) then Expr(z3_native_object_of_ast_ptr ctx no) else if (Z3native.is_numeral_ast (context_gno ctx) no) then if (sk == INT_SORT || sk == REAL_SORT || sk == BV_SORT || sk == FLOATING_POINT_SORT || sk == ROUNDING_MODE_SORT) then Expr(z3_native_object_of_ast_ptr ctx no) else raise (Z3native.Exception "Unsupported numeral object") else Expr(z3_native_object_of_ast_ptr ctx no) let expr_of_ast a = let q = (Z3enums.ast_kind_of_int (Z3native.get_ast_kind (z3obj_gnc a) (z3obj_gno a))) in if (q != Z3enums.APP_AST && q != VAR_AST && q != QUANTIFIER_AST && q != NUMERAL_AST) then raise (Z3native.Exception "Invalid coercion") else Expr(a) let ast_of_expr e = match e with Expr(a) -> a let expr_lton ( a : expr list ) = let f ( e : expr ) = match e with Expr(a) -> (AST.ptr_of_ast a) in Array.of_list (List.map f a) let expr_of_func_app : context -> FuncDecl.func_decl -> expr list -> expr = fun ctx f args -> match f with FuncDecl.FuncDecl(fa) -> let o = Z3native.mk_app (context_gno ctx) (AST.ptr_of_ast fa) (List.length args) (expr_lton args) in expr_of_ptr ctx o let simplify ( x : expr ) ( p : Params.params option ) = match p with | None -> expr_of_ptr (Expr.gc x) (Z3native.simplify (gnc x) (gno x)) | Some pp -> expr_of_ptr (Expr.gc x) (Z3native.simplify_ex (gnc x) (gno x) (z3obj_gno pp)) let get_simplify_help ( ctx : context ) = Z3native.simplify_get_help (context_gno ctx) let get_simplify_parameter_descrs ( ctx : context ) = Params.ParamDescrs.param_descrs_of_ptr ctx (Z3native.simplify_get_param_descrs (context_gno ctx)) let get_func_decl ( x : expr ) = FuncDecl.func_decl_of_ptr (Expr.gc x) (Z3native.get_app_decl (gnc x) (gno x)) let get_num_args ( x : expr ) = Z3native.get_app_num_args (gnc x) (gno x) let get_args ( x : expr ) = let n = (get_num_args x) in let f i = expr_of_ptr (Expr.gc x) (Z3native.get_app_arg (gnc x) (gno x) i) in mk_list f n let update ( x : expr ) ( args : expr list ) = if ((AST.is_app (ast_of_expr x)) && (List.length args <> (get_num_args x))) then raise (Z3native.Exception "Number of arguments does not match") else expr_of_ptr (Expr.gc x) (Z3native.update_term (gnc x) (gno x) (List.length args) (expr_lton args)) let substitute ( x : expr ) from to_ = if (List.length from) <> (List.length to_) then raise (Z3native.Exception "Argument sizes do not match") else expr_of_ptr (Expr.gc x) (Z3native.substitute (gnc x) (gno x) (List.length from) (expr_lton from) (expr_lton to_)) let substitute_one ( x : expr ) from to_ = substitute ( x : expr ) [ from ] [ to_ ] let substitute_vars ( x : expr ) to_ = expr_of_ptr (Expr.gc x) (Z3native.substitute_vars (gnc x) (gno x) (List.length to_) (expr_lton to_)) let translate ( x : expr ) to_ctx = if (Expr.gc x) == to_ctx then x else expr_of_ptr to_ctx (Z3native.translate (gnc x) (gno x) (context_gno to_ctx)) let to_string ( x : expr ) = Z3native.ast_to_string (gnc x) (gno x) let is_numeral ( x : expr ) = (Z3native.is_numeral_ast (gnc x) (gno x)) let is_well_sorted ( x : expr ) = Z3native.is_well_sorted (gnc x) (gno x) let get_sort ( x : expr ) = Sort.sort_of_ptr (Expr.gc x) (Z3native.get_sort (gnc x) (gno x)) let is_const ( x : expr ) = (match x with Expr(a) -> (AST.is_app a)) && (get_num_args x) == 0 && (FuncDecl.get_domain_size (get_func_decl x)) == 0 let mk_const ( ctx : context ) ( name : Symbol.symbol ) ( range : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_const (context_gno ctx) (Symbol.gno name) (Sort.gno range)) let mk_const_s ( ctx : context ) ( name : string ) ( range : Sort.sort ) = mk_const ctx (Symbol.mk_string ctx name) range let mk_const_f ( ctx : context ) ( f : FuncDecl.func_decl ) = Expr.expr_of_func_app ctx f [] let mk_fresh_const ( ctx : context ) ( prefix : string ) ( range : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_fresh_const (context_gno ctx) prefix (Sort.gno range)) let mk_app ( ctx : context ) ( f : FuncDecl.func_decl ) ( args : expr list ) = expr_of_func_app ctx f args let mk_numeral_string ( ctx : context ) ( v : string ) ( ty : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_numeral (context_gno ctx) v (Sort.gno ty)) let mk_numeral_int ( ctx : context ) ( v : int ) ( ty : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_int (context_gno ctx) v (Sort.gno ty)) let equal ( a : expr ) ( b : expr ) = AST.equal (ast_of_expr a) (ast_of_expr b) let compare a b = AST.compare (ast_of_expr a) (ast_of_expr b) end open FuncDecl open Expr module Boolean = struct let mk_sort ( ctx : context ) = (Sort.sort_of_ptr ctx (Z3native.mk_bool_sort (context_gno ctx))) let mk_const ( ctx : context ) ( name : Symbol.symbol ) = (Expr.mk_const ctx name (mk_sort ctx)) let mk_const_s ( ctx : context ) ( name : string ) = mk_const ctx (Symbol.mk_string ctx name) let mk_true ( ctx : context ) = expr_of_ptr ctx (Z3native.mk_true (context_gno ctx)) let mk_false ( ctx : context ) = expr_of_ptr ctx (Z3native.mk_false (context_gno ctx)) let mk_val ( ctx : context ) ( value : bool ) = if value then mk_true ctx else mk_false ctx let mk_not ( ctx : context ) ( a : expr ) = expr_of_ptr ctx (Z3native.mk_not (context_gno ctx) (gno a)) let mk_ite ( ctx : context ) ( t1 : expr ) ( t2 : expr ) ( t3 : expr ) = expr_of_ptr ctx (Z3native.mk_ite (context_gno ctx) (gno t1) (gno t2) (gno t3)) let mk_iff ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_iff (context_gno ctx) (gno t1) (gno t2)) let mk_implies ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_implies (context_gno ctx) (gno t1) (gno t2)) let mk_xor ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_xor (context_gno ctx) (gno t1) (gno t2)) let mk_and ( ctx : context ) ( args : expr list ) = let f x = (Expr.gno (x)) in expr_of_ptr ctx (Z3native.mk_and (context_gno ctx) (List.length args) (Array.of_list (List.map f args))) let mk_or ( ctx : context ) ( args : expr list ) = let f x = (Expr.gno (x)) in expr_of_ptr ctx (Z3native.mk_or (context_gno ctx) (List.length args) (Array.of_list(List.map f args))) let mk_eq ( ctx : context ) ( x : expr ) ( y : expr ) = expr_of_ptr ctx (Z3native.mk_eq (context_gno ctx) (Expr.gno x) (Expr.gno y)) let mk_distinct ( ctx : context ) ( args : expr list ) = expr_of_ptr ctx (Z3native.mk_distinct (context_gno ctx) (List.length args) (expr_lton args)) let get_bool_value ( x : expr ) = lbool_of_int (Z3native.get_bool_value (gnc x) (gno x)) let is_bool ( x : expr ) = (match x with Expr(a) -> (AST.is_expr a)) && (Z3native.is_eq_sort (gnc x) (Z3native.mk_bool_sort (gnc x)) (Z3native.get_sort (gnc x) (gno x))) let is_true ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (get_func_decl x) == OP_TRUE) let is_false ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (get_func_decl x) == OP_FALSE) let is_eq ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (get_func_decl x) == OP_EQ) let is_distinct ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (get_func_decl x) == OP_DISTINCT) let is_ite ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (get_func_decl x) == OP_ITE) let is_and ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (get_func_decl x) == OP_AND) let is_or ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (get_func_decl x) == OP_OR) let is_iff ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (get_func_decl x) == OP_IFF) let is_xor ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (get_func_decl x) == OP_XOR) let is_not ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (get_func_decl x) == OP_NOT) let is_implies ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (get_func_decl x) == OP_IMPLIES) end module Quantifier = struct type quantifier = Quantifier of expr let expr_of_quantifier e = match e with Quantifier(x) -> x let quantifier_of_expr e = match e with Expr.Expr(a) -> let q = (Z3enums.ast_kind_of_int (Z3native.get_ast_kind (z3obj_gnc a) (z3obj_gno a))) in if (q != Z3enums.QUANTIFIER_AST) then raise (Z3native.Exception "Invalid coercion") else Quantifier(e) let gc ( x : quantifier ) = match (x) with Quantifier(e) -> (Expr.gc e) let gnc ( x : quantifier ) = match (x) with Quantifier(e) -> (Expr.gnc e) let gno ( x : quantifier ) = match (x) with Quantifier(e) -> (Expr.gno e) module Pattern = struct type pattern = Pattern of AST.ast let ast_of_pattern e = match e with Pattern(x) -> x let pattern_of_ast a = (* CMW: Unchecked ok? *) Pattern(a) let gc ( x : pattern ) = match (x) with Pattern(a) -> (z3obj_gc a) let gnc ( x : pattern ) = match (x) with Pattern(a) -> (z3obj_gnc a) let gno ( x : pattern ) = match (x) with Pattern(a) -> (z3obj_gno a) let get_num_terms ( x : pattern ) = Z3native.get_pattern_num_terms (gnc x) (gno x) let get_terms ( x : pattern ) = let n = (get_num_terms x) in let f i = (expr_of_ptr (gc x) (Z3native.get_pattern (gnc x) (gno x) i)) in mk_list f n let to_string ( x : pattern ) = Z3native.pattern_to_string (gnc x) (gno x) end let get_index ( x : expr ) = if not (AST.is_var (match x with Expr.Expr(a) -> a)) then raise (Z3native.Exception "Term is not a bound variable.") else Z3native.get_index_value (Expr.gnc x) (Expr.gno x) let is_universal ( x : quantifier ) = Z3native.is_quantifier_forall (gnc x) (gno x) let is_existential ( x : quantifier ) = not (is_universal x) let get_weight ( x : quantifier ) = Z3native.get_quantifier_weight (gnc x) (gno x) let get_num_patterns ( x : quantifier ) = Z3native.get_quantifier_num_patterns (gnc x) (gno x) let get_patterns ( x : quantifier ) = let n = (get_num_patterns x) in let f i = Pattern.Pattern (z3_native_object_of_ast_ptr (gc x) (Z3native.get_quantifier_pattern_ast (gnc x) (gno x) i)) in mk_list f n let get_num_no_patterns ( x : quantifier ) = Z3native.get_quantifier_num_no_patterns (gnc x) (gno x) let get_no_patterns ( x : quantifier ) = let n = (get_num_patterns x) in let f i = Pattern.Pattern (z3_native_object_of_ast_ptr (gc x) (Z3native.get_quantifier_no_pattern_ast (gnc x) (gno x) i)) in mk_list f n let get_num_bound ( x : quantifier ) = Z3native.get_quantifier_num_bound (gnc x) (gno x) let get_bound_variable_names ( x : quantifier ) = let n = (get_num_bound x) in let f i = (Symbol.create (gc x) (Z3native.get_quantifier_bound_name (gnc x) (gno x) i)) in mk_list f n let get_bound_variable_sorts ( x : quantifier ) = let n = (get_num_bound x) in let f i = (Sort.sort_of_ptr (gc x) (Z3native.get_quantifier_bound_sort (gnc x) (gno x) i)) in mk_list f n let get_body ( x : quantifier ) = expr_of_ptr (gc x) (Z3native.get_quantifier_body (gnc x) (gno x)) let mk_bound ( ctx : context ) ( index : int ) ( ty : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_bound (context_gno ctx) index (Sort.gno ty)) let mk_pattern ( ctx : context ) ( terms : expr list ) = if (List.length terms) == 0 then raise (Z3native.Exception "Cannot create a pattern from zero terms") else Pattern.Pattern(z3_native_object_of_ast_ptr ctx (Z3native.mk_pattern (context_gno ctx) (List.length terms) (expr_lton terms))) let mk_forall ( ctx : context ) ( sorts : Sort.sort list ) ( names : Symbol.symbol list ) ( body : expr ) ( weight : int option ) ( patterns : Pattern.pattern list ) ( nopatterns : expr list ) ( quantifier_id : Symbol.symbol option ) ( skolem_id : Symbol.symbol option ) = if (List.length sorts) != (List.length names) then raise (Z3native.Exception "Number of sorts does not match number of names") else if ((List.length nopatterns) == 0 && quantifier_id == None && skolem_id == None) then Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier (context_gno ctx) true (match weight with | None -> 1 | Some(x) -> x) (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) (List.length sorts) (Sort.sort_lton sorts) (Symbol.symbol_lton names) (Expr.gno body))) else Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier_ex (context_gno ctx) true (match weight with | None -> 1 | Some(x) -> x) (match quantifier_id with | None -> null | Some(x) -> (Symbol.gno x)) (match skolem_id with | None -> null | Some(x) -> (Symbol.gno x)) (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) (List.length nopatterns) (expr_lton nopatterns) (List.length sorts) (Sort.sort_lton sorts) (Symbol.symbol_lton names) (Expr.gno body))) let mk_forall_const ( ctx : context ) ( bound_constants : expr list ) ( body : expr ) ( weight : int option ) ( patterns : Pattern.pattern list ) ( nopatterns : expr list ) ( quantifier_id : Symbol.symbol option ) ( skolem_id : Symbol.symbol option ) = if ((List.length nopatterns) == 0 && quantifier_id == None && skolem_id == None) then Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier_const (context_gno ctx) true (match weight with | None -> 1 | Some(x) -> x) (List.length bound_constants) (expr_lton bound_constants) (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) (Expr.gno body))) else Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier_const_ex (context_gno ctx) true (match weight with | None -> 1 | Some(x) -> x) (match quantifier_id with | None -> null | Some(x) -> (Symbol.gno x)) (match skolem_id with | None -> null | Some(x) -> (Symbol.gno x)) (List.length bound_constants) (expr_lton bound_constants) (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) (List.length nopatterns) (expr_lton nopatterns) (Expr.gno body))) let mk_exists ( ctx : context ) ( sorts : Sort.sort list ) ( names : Symbol.symbol list ) ( body : expr ) ( weight : int option ) ( patterns : Pattern.pattern list ) ( nopatterns : expr list ) ( quantifier_id : Symbol.symbol option ) ( skolem_id : Symbol.symbol option ) = if (List.length sorts) != (List.length names) then raise (Z3native.Exception "Number of sorts does not match number of names") else if ((List.length nopatterns) == 0 && quantifier_id == None && skolem_id == None) then Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier (context_gno ctx) false (match weight with | None -> 1 | Some(x) -> x) (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) (List.length sorts) (Sort.sort_lton sorts) (Symbol.symbol_lton names) (Expr.gno body))) else Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier_ex (context_gno ctx) false (match weight with | None -> 1 | Some(x) -> x) (match quantifier_id with | None -> null | Some(x) -> (Symbol.gno x)) (match skolem_id with | None -> null | Some(x) -> (Symbol.gno x)) (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) (List.length nopatterns) (expr_lton nopatterns) (List.length sorts) (Sort.sort_lton sorts) (Symbol.symbol_lton names) (Expr.gno body))) let mk_exists_const ( ctx : context ) ( bound_constants : expr list ) ( body : expr ) ( weight : int option ) ( patterns : Pattern.pattern list ) ( nopatterns : expr list ) ( quantifier_id : Symbol.symbol option ) ( skolem_id : Symbol.symbol option ) = if ((List.length nopatterns) == 0 && quantifier_id == None && skolem_id == None) then Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier_const (context_gno ctx) false (match weight with | None -> 1 | Some(x) -> x) (List.length bound_constants) (expr_lton bound_constants) (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) (Expr.gno body))) else Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier_const_ex (context_gno ctx) false (match weight with | None -> 1 | Some(x) -> x) (match quantifier_id with | None -> null | Some(x) -> (Symbol.gno x)) (match skolem_id with | None -> null | Some(x) -> (Symbol.gno x)) (List.length bound_constants) (expr_lton bound_constants) (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) (List.length nopatterns) (expr_lton nopatterns) (Expr.gno body))) let mk_quantifier ( ctx : context ) ( universal : bool ) ( sorts : Sort.sort list ) ( names : Symbol.symbol list ) ( body : expr ) ( weight : int option ) ( patterns : Pattern.pattern list ) ( nopatterns : expr list ) ( quantifier_id : Symbol.symbol option ) ( skolem_id : Symbol.symbol option ) = if (universal) then (mk_forall ctx sorts names body weight patterns nopatterns quantifier_id skolem_id) else (mk_exists ctx sorts names body weight patterns nopatterns quantifier_id skolem_id) let mk_quantifier ( ctx : context ) ( universal : bool ) ( bound_constants : expr list ) ( body : expr ) ( weight : int option ) ( patterns : Pattern.pattern list ) ( nopatterns : expr list ) ( quantifier_id : Symbol.symbol option ) ( skolem_id : Symbol.symbol option ) = if (universal) then mk_forall_const ctx bound_constants body weight patterns nopatterns quantifier_id skolem_id else mk_exists_const ctx bound_constants body weight patterns nopatterns quantifier_id skolem_id let to_string ( x : quantifier ) = (Expr.to_string (expr_of_quantifier x)) end module Z3Array = struct let mk_sort ( ctx : context ) ( domain : Sort.sort ) ( range : Sort.sort ) = Sort.sort_of_ptr ctx (Z3native.mk_array_sort (context_gno ctx) (Sort.gno domain) (Sort.gno range)) let is_store ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_STORE) let is_select ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_SELECT) let is_constant_array ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_CONST_ARRAY) let is_default_array ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_ARRAY_DEFAULT) let is_array_map ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_ARRAY_MAP) let is_as_array ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_AS_ARRAY) let is_array ( x : expr ) = (Z3native.is_app (Expr.gnc x) (Expr.gno x)) && ((sort_kind_of_int (Z3native.get_sort_kind (Expr.gnc x) (Z3native.get_sort (Expr.gnc x) (Expr.gno x)))) == ARRAY_SORT) let get_domain ( x : Sort.sort ) = Sort.sort_of_ptr (Sort.gc x) (Z3native.get_array_sort_domain (Sort.gnc x) (Sort.gno x)) let get_range ( x : Sort.sort ) = Sort.sort_of_ptr (Sort.gc x) (Z3native.get_array_sort_range (Sort.gnc x) (Sort.gno x)) let mk_const ( ctx : context ) ( name : Symbol.symbol ) ( domain : Sort.sort ) ( range : Sort.sort ) = (Expr.mk_const ctx name (mk_sort ctx domain range)) let mk_const_s ( ctx : context ) ( name : string ) ( domain : Sort.sort ) ( range : Sort.sort ) = mk_const ctx (Symbol.mk_string ctx name) domain range let mk_select ( ctx : context ) ( a : expr ) ( i : expr ) = expr_of_ptr ctx (Z3native.mk_select (context_gno ctx) (Expr.gno a) (Expr.gno i)) let mk_store ( ctx : context ) ( a : expr ) ( i : expr ) ( v : expr ) = expr_of_ptr ctx (Z3native.mk_store (context_gno ctx) (Expr.gno a) (Expr.gno i) (Expr.gno v)) let mk_const_array ( ctx : context ) ( domain : Sort.sort ) ( v : expr ) = expr_of_ptr ctx (Z3native.mk_const_array (context_gno ctx) (Sort.gno domain) (Expr.gno v)) let mk_map ( ctx : context ) ( f : func_decl ) ( args : expr list ) = let m x = (Expr.gno x) in expr_of_ptr ctx (Z3native.mk_map (context_gno ctx) (FuncDecl.gno f) (List.length args) (Array.of_list (List.map m args))) let mk_term_array ( ctx : context ) ( arg : expr ) = expr_of_ptr ctx (Z3native.mk_array_default (context_gno ctx) (Expr.gno arg)) end module Set = struct let mk_sort ( ctx : context ) ( ty : Sort.sort ) = Sort.sort_of_ptr ctx (Z3native.mk_set_sort (context_gno ctx) (Sort.gno ty)) let is_union ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_SET_UNION) let is_intersect ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_SET_INTERSECT) let is_difference ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_SET_DIFFERENCE) let is_complement ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_SET_COMPLEMENT) let is_subset ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_SET_SUBSET) let mk_empty ( ctx : context ) ( domain : Sort.sort ) = (expr_of_ptr ctx (Z3native.mk_empty_set (context_gno ctx) (Sort.gno domain))) let mk_full ( ctx : context ) ( domain : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_full_set (context_gno ctx) (Sort.gno domain)) let mk_set_add ( ctx : context ) ( set : expr ) ( element : expr ) = expr_of_ptr ctx (Z3native.mk_set_add (context_gno ctx) (Expr.gno set) (Expr.gno element)) let mk_del ( ctx : context ) ( set : expr ) ( element : expr ) = expr_of_ptr ctx (Z3native.mk_set_del (context_gno ctx) (Expr.gno set) (Expr.gno element)) let mk_union ( ctx : context ) ( args : expr list ) = expr_of_ptr ctx (Z3native.mk_set_union (context_gno ctx) (List.length args) (expr_lton args)) let mk_intersection ( ctx : context ) ( args : expr list ) = expr_of_ptr ctx (Z3native.mk_set_intersect (context_gno ctx) (List.length args) (expr_lton args)) let mk_difference ( ctx : context ) ( arg1 : expr ) ( arg2 : expr ) = expr_of_ptr ctx (Z3native.mk_set_difference (context_gno ctx) (Expr.gno arg1) (Expr.gno arg2)) let mk_complement ( ctx : context ) ( arg : expr ) = expr_of_ptr ctx (Z3native.mk_set_complement (context_gno ctx) (Expr.gno arg)) let mk_membership ( ctx : context ) ( elem : expr ) ( set : expr ) = expr_of_ptr ctx (Z3native.mk_set_member (context_gno ctx) (Expr.gno elem) (Expr.gno set)) let mk_subset ( ctx : context ) ( arg1 : expr ) ( arg2 : expr ) = expr_of_ptr ctx (Z3native.mk_set_subset (context_gno ctx) (Expr.gno arg1) (Expr.gno arg2)) end module FiniteDomain = struct let mk_sort ( ctx : context ) ( name : Symbol.symbol ) ( size : int ) = Sort.sort_of_ptr ctx (Z3native.mk_finite_domain_sort (context_gno ctx) (Symbol.gno name) size) let mk_sort_s ( ctx : context ) ( name : string ) ( size : int ) = mk_sort ctx (Symbol.mk_string ctx name) size let is_finite_domain ( x : expr ) = let nc = (Expr.gnc x) in (Z3native.is_app (Expr.gnc x) (Expr.gno x)) && (sort_kind_of_int (Z3native.get_sort_kind nc (Z3native.get_sort nc (Expr.gno x))) == FINITE_DOMAIN_SORT) let is_lt ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FD_LT) let get_size ( x : Sort.sort ) = let (r, v) = (Z3native.get_finite_domain_sort_size (Sort.gnc x) (Sort.gno x)) in if r then v else raise (Z3native.Exception "Conversion failed.") end module Relation = struct let is_relation ( x : expr ) = let nc = (Expr.gnc x) in ((Z3native.is_app (Expr.gnc x) (Expr.gno x)) && (sort_kind_of_int (Z3native.get_sort_kind nc (Z3native.get_sort nc (Expr.gno x))) == RELATION_SORT)) let is_store ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_STORE) let is_empty ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_EMPTY) let is_is_empty ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_IS_EMPTY) let is_join ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_JOIN) let is_union ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_UNION) let is_widen ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_WIDEN) let is_project ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_PROJECT) let is_filter ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_FILTER) let is_negation_filter ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_NEGATION_FILTER) let is_rename ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_RENAME) let is_complement ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_COMPLEMENT) let is_select ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_SELECT) let is_clone ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_CLONE) let get_arity ( x : Sort.sort ) = Z3native.get_relation_arity (Sort.gnc x) (Sort.gno x) let get_column_sorts ( x : Sort.sort ) = let n = get_arity x in let f i = (Sort.sort_of_ptr (Sort.gc x) (Z3native.get_relation_column (Sort.gnc x) (Sort.gno x) i)) in mk_list f n end module Datatype = struct module Constructor = struct type constructor = z3_native_object module FieldNumTable = Hashtbl.Make(struct type t = AST.ast let equal x y = AST.compare x y = 0 let hash = AST.hash end) let _field_nums = FieldNumTable.create 0 let create ( ctx : context ) ( name : Symbol.symbol ) ( recognizer : Symbol.symbol ) ( field_names : Symbol.symbol list ) ( sorts : Sort.sort option list ) ( sort_refs : int list ) = let n = (List.length field_names) in if n != (List.length sorts) then raise (Z3native.Exception "Number of field names does not match number of sorts") else if n != (List.length sort_refs) then raise (Z3native.Exception "Number of field names does not match number of sort refs") else let ptr = (Z3native.mk_constructor (context_gno ctx) (Symbol.gno name) (Symbol.gno recognizer) n (Symbol.symbol_lton field_names) (Sort.sort_option_lton sorts) (Array.of_list sort_refs)) in let no : constructor = { m_ctx = ctx ; m_n_obj = null ; inc_ref = z3obj_nil_ref ; dec_ref = z3obj_nil_ref} in (z3obj_sno no ctx ptr) ; (z3obj_create no) ; let f = fun o -> Z3native.del_constructor (z3obj_gnc o) (z3obj_gno o) in Gc.finalise f no ; FieldNumTable.add _field_nums no n ; no let get_num_fields ( x : constructor ) = FieldNumTable.find _field_nums x let get_constructor_decl ( x : constructor ) = let (a, _, _) = (Z3native.query_constructor (z3obj_gnc x) (z3obj_gno x) (get_num_fields x)) in func_decl_of_ptr (z3obj_gc x) a let get_tester_decl ( x : constructor ) = let (_, b, _) = (Z3native.query_constructor (z3obj_gnc x) (z3obj_gno x) (get_num_fields x)) in func_decl_of_ptr (z3obj_gc x) b let get_accessor_decls ( x : constructor ) = let (_, _, c) = (Z3native.query_constructor (z3obj_gnc x) (z3obj_gno x) (get_num_fields x)) in let f i = func_decl_of_ptr (z3obj_gc x) (Array.get c i) in mk_list f (Array.length c) end module ConstructorList = struct type constructor_list = z3_native_object let create ( ctx : context ) ( c : Constructor.constructor list ) = let res : constructor_list = { m_ctx = ctx ; m_n_obj = null ; inc_ref = z3obj_nil_ref ; dec_ref = z3obj_nil_ref} in let f x =(z3obj_gno x) in (z3obj_sno res ctx (Z3native.mk_constructor_list (context_gno ctx) (List.length c) (Array.of_list (List.map f c)))) ; (z3obj_create res) ; let f = fun o -> Z3native.del_constructor_list (z3obj_gnc o) (z3obj_gno o) in Gc.finalise f res; res end let mk_constructor ( ctx : context ) ( name : Symbol.symbol ) ( recognizer : Symbol.symbol ) ( field_names : Symbol.symbol list ) ( sorts : Sort.sort option list ) ( sort_refs : int list ) = Constructor.create ctx name recognizer field_names sorts sort_refs let mk_constructor_s ( ctx : context ) ( name : string ) ( recognizer : Symbol.symbol ) ( field_names : Symbol.symbol list ) ( sorts : Sort.sort option list ) ( sort_refs : int list ) = mk_constructor ctx (Symbol.mk_string ctx name) recognizer field_names sorts sort_refs let mk_sort ( ctx : context ) ( name : Symbol.symbol ) ( constructors : Constructor.constructor list ) = let f x = (z3obj_gno x) in let (x,_) = (Z3native.mk_datatype (context_gno ctx) (Symbol.gno name) (List.length constructors) (Array.of_list (List.map f constructors))) in Sort.sort_of_ptr ctx x let mk_sort_s ( ctx : context ) ( name : string ) ( constructors : Constructor.constructor list ) = mk_sort ctx (Symbol.mk_string ctx name) constructors let mk_sorts ( ctx : context ) ( names : Symbol.symbol list ) ( c : Constructor.constructor list list ) = let n = (List.length names) in let f e = (AST.ptr_of_ast (ConstructorList.create ctx e)) in let cla = (Array.of_list (List.map f c)) in let (r, a) = (Z3native.mk_datatypes (context_gno ctx) n (Symbol.symbol_lton names) cla) in let g i = (Sort.sort_of_ptr ctx (Array.get r i)) in mk_list g (Array.length r) let mk_sorts_s ( ctx : context ) ( names : string list ) ( c : Constructor.constructor list list ) = mk_sorts ctx ( let f e = (Symbol.mk_string ctx e) in List.map f names ) c let get_num_constructors ( x : Sort.sort ) = Z3native.get_datatype_sort_num_constructors (Sort.gnc x) (Sort.gno x) let get_constructors ( x : Sort.sort ) = let n = (get_num_constructors x) in let f i = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor (Sort.gnc x) (Sort.gno x) i) in mk_list f n let get_recognizers ( x : Sort.sort ) = let n = (get_num_constructors x) in let f i = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_recognizer (Sort.gnc x) (Sort.gno x) i) in mk_list f n let get_accessors ( x : Sort.sort ) = let n = (get_num_constructors x) in let f i = ( let fd = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor (Sort.gnc x) (Sort.gno x) i) in let ds = Z3native.get_domain_size (FuncDecl.gnc fd) (FuncDecl.gno fd) in let g j = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor_accessor (Sort.gnc x) (Sort.gno x) i j) in mk_list g ds ) in mk_list f n end module Enumeration = struct let mk_sort ( ctx : context ) ( name : Symbol.symbol ) ( enum_names : Symbol.symbol list ) = let (a, _, _) = (Z3native.mk_enumeration_sort (context_gno ctx) (Symbol.gno name) (List.length enum_names) (Symbol.symbol_lton enum_names)) in Sort.sort_of_ptr ctx a let mk_sort_s ( ctx : context ) ( name : string ) ( enum_names : string list ) = mk_sort ctx (Symbol.mk_string ctx name) (Symbol.mk_strings ctx enum_names) let get_const_decls ( x : Sort.sort ) = let n = Z3native.get_datatype_sort_num_constructors (Sort.gnc x) (Sort.gno x) in let f i = (func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor (Sort.gnc x) (Sort.gno x) i)) in mk_list f n let get_const_decl ( x : Sort.sort ) ( inx : int ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor (Sort.gnc x) (Sort.gno x) inx) let get_consts ( x : Sort.sort ) = let n = Z3native.get_datatype_sort_num_constructors (Sort.gnc x) (Sort.gno x) in let f i = (Expr.mk_const_f (Sort.gc x) (get_const_decl x i)) in mk_list f n let get_const ( x : Sort.sort ) ( inx : int ) = Expr.mk_const_f (Sort.gc x) (get_const_decl x inx) let get_tester_decls ( x : Sort.sort ) = let n = Z3native.get_datatype_sort_num_constructors (Sort.gnc x) (Sort.gno x) in let f i = (func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_recognizer (Sort.gnc x) (Sort.gno x) i)) in mk_list f n let get_tester_decl ( x : Sort.sort ) ( inx : int ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_recognizer (Sort.gnc x) (Sort.gno x) inx) end module Z3List = struct let mk_sort ( ctx : context ) ( name : Symbol.symbol ) ( elem_sort : Sort.sort ) = let (r, _, _, _, _, _, _) = (Z3native.mk_list_sort (context_gno ctx) (Symbol.gno name) (Sort.gno elem_sort)) in Sort.sort_of_ptr ctx r let mk_list_s ( ctx : context ) ( name : string ) elem_sort = mk_sort ctx (Symbol.mk_string ctx name) elem_sort let get_nil_decl ( x : Sort.sort ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor (Sort.gnc x) (Sort.gno x) 0) let get_is_nil_decl ( x : Sort.sort ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_recognizer (Sort.gnc x) (Sort.gno x) 0) let get_cons_decl ( x : Sort.sort ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor (Sort.gnc x) (Sort.gno x) 1) let get_is_cons_decl ( x : Sort.sort ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_recognizer (Sort.gnc x) (Sort.gno x) 1) let get_head_decl ( x : Sort.sort ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor_accessor (Sort.gnc x) (Sort.gno x) 1 0) let get_tail_decl ( x : Sort.sort ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor_accessor (Sort.gnc x) (Sort.gno x) 1 1) let nil ( x : Sort.sort ) = expr_of_func_app (Sort.gc x) (get_nil_decl x) [] end module Tuple = struct let mk_sort ( ctx : context ) ( name : Symbol.symbol ) ( field_names : Symbol.symbol list ) ( field_sorts : Sort.sort list ) = let (r, _, _) = (Z3native.mk_tuple_sort (context_gno ctx) (Symbol.gno name) (List.length field_names) (Symbol.symbol_lton field_names) (Sort.sort_lton field_sorts)) in Sort.sort_of_ptr ctx r let get_mk_decl ( x : Sort.sort ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_tuple_sort_mk_decl (Sort.gnc x) (Sort.gno x)) let get_num_fields ( x : Sort.sort ) = Z3native.get_tuple_sort_num_fields (Sort.gnc x) (Sort.gno x) let get_field_decls ( x : Sort.sort ) = let n = get_num_fields x in let f i = func_decl_of_ptr (Sort.gc x) (Z3native.get_tuple_sort_field_decl (Sort.gnc x) (Sort.gno x) i) in mk_list f n end module Arithmetic = struct let is_int ( x : expr ) = (Z3native.is_numeral_ast (Expr.gnc x) (Expr.gno x)) && ((sort_kind_of_int (Z3native.get_sort_kind (Expr.gnc x) (Z3native.get_sort (Expr.gnc x) (Expr.gno x)))) == INT_SORT) let is_arithmetic_numeral ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_ANUM) let is_le ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_LE) let is_ge ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_GE) let is_lt ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_LT) let is_gt ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_GT) let is_add ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_ADD) let is_sub ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_SUB) let is_uminus ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_UMINUS) let is_mul ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_MUL) let is_div ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_DIV) let is_idiv ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_IDIV) let is_remainder ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_REM) let is_modulus ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_MOD) let is_int2real ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_TO_REAL) let is_real2int ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_TO_INT) let is_real_is_int ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_IS_INT) let is_real ( x : expr ) = ((sort_kind_of_int (Z3native.get_sort_kind (Expr.gnc x) (Z3native.get_sort (Expr.gnc x) (Expr.gno x)))) == REAL_SORT) let is_int_numeral ( x : expr ) = (Expr.is_numeral x) && (is_int x) let is_rat_numeral ( x : expr ) = (Expr.is_numeral x) && (is_real x) let is_algebraic_number ( x : expr ) = Z3native.is_algebraic_number (Expr.gnc x) (Expr.gno x) module Integer = struct let mk_sort ( ctx : context ) = Sort.sort_of_ptr ctx (Z3native.mk_int_sort (context_gno ctx)) let get_int ( x : expr ) = let (r, v) = Z3native.get_numeral_int (Expr.gnc x) (Expr.gno x) in if r then v else raise (Z3native.Exception "Conversion failed.") let get_big_int ( x : expr ) = if (is_int_numeral x) then let s = (Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x)) in (Big_int.big_int_of_string s) else raise (Z3native.Exception "Conversion failed.") let numeral_to_string ( x : expr ) = Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x) let mk_const ( ctx : context ) ( name : Symbol.symbol ) = Expr.mk_const ctx name (mk_sort ctx) let mk_const_s ( ctx : context ) ( name : string ) = mk_const ctx (Symbol.mk_string ctx name) let mk_mod ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_mod (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_rem ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_rem (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_numeral_s ( ctx : context ) ( v : string ) = expr_of_ptr ctx (Z3native.mk_numeral (context_gno ctx) v (Sort.gno (mk_sort ctx))) let mk_numeral_i ( ctx : context ) ( v : int ) = expr_of_ptr ctx (Z3native.mk_int (context_gno ctx) v (Sort.gno (mk_sort ctx))) let mk_int2real ( ctx : context ) ( t : expr ) = (Expr.expr_of_ptr ctx (Z3native.mk_int2real (context_gno ctx) (Expr.gno t))) let mk_int2bv ( ctx : context ) ( n : int ) ( t : expr ) = (Expr.expr_of_ptr ctx (Z3native.mk_int2bv (context_gno ctx) n (Expr.gno t))) end module Real = struct let mk_sort ( ctx : context ) = Sort.sort_of_ptr ctx (Z3native.mk_real_sort (context_gno ctx)) let get_numerator ( x : expr ) = expr_of_ptr (Expr.gc x) (Z3native.get_numerator (Expr.gnc x) (Expr.gno x)) let get_denominator ( x : expr ) = expr_of_ptr (Expr.gc x) (Z3native.get_denominator (Expr.gnc x) (Expr.gno x)) let get_ratio ( x : expr ) = if (is_rat_numeral x) then let s = (Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x)) in (Ratio.ratio_of_string s) else raise (Z3native.Exception "Conversion failed.") let to_decimal_string ( x : expr ) ( precision : int ) = Z3native.get_numeral_decimal_string (Expr.gnc x) (Expr.gno x) precision let numeral_to_string ( x : expr ) = Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x) let mk_const ( ctx : context ) ( name : Symbol.symbol ) = Expr.mk_const ctx name (mk_sort ctx) let mk_const_s ( ctx : context ) ( name : string ) = mk_const ctx (Symbol.mk_string ctx name) let mk_numeral_nd ( ctx : context ) ( num : int ) ( den : int ) = if (den == 0) then raise (Z3native.Exception "Denominator is zero") else expr_of_ptr ctx (Z3native.mk_real (context_gno ctx) num den) let mk_numeral_s ( ctx : context ) ( v : string ) = expr_of_ptr ctx (Z3native.mk_numeral (context_gno ctx) v (Sort.gno (mk_sort ctx))) let mk_numeral_i ( ctx : context ) ( v : int ) = expr_of_ptr ctx (Z3native.mk_int (context_gno ctx) v (Sort.gno (mk_sort ctx))) let mk_is_integer ( ctx : context ) ( t : expr ) = (expr_of_ptr ctx (Z3native.mk_is_int (context_gno ctx) (Expr.gno t))) let mk_real2int ( ctx : context ) ( t : expr ) = (expr_of_ptr ctx (Z3native.mk_real2int (context_gno ctx) (Expr.gno t))) module AlgebraicNumber = struct let to_upper ( x : expr ) ( precision : int ) = expr_of_ptr (Expr.gc x) (Z3native.get_algebraic_number_upper (Expr.gnc x) (Expr.gno x) precision) let to_lower ( x : expr ) precision = expr_of_ptr (Expr.gc x) (Z3native.get_algebraic_number_lower (Expr.gnc x) (Expr.gno x) precision) let to_decimal_string ( x : expr ) ( precision : int ) = Z3native.get_numeral_decimal_string (Expr.gnc x) (Expr.gno x) precision let numeral_to_string ( x : expr ) = Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x) end end let mk_add ( ctx : context ) ( t : expr list ) = let f x = (Expr.gno x) in (expr_of_ptr ctx (Z3native.mk_add (context_gno ctx) (List.length t) (Array.of_list (List.map f t)))) let mk_mul ( ctx : context ) ( t : expr list ) = let f x = (Expr.gno x) in (expr_of_ptr ctx (Z3native.mk_mul (context_gno ctx) (List.length t) (Array.of_list (List.map f t)))) let mk_sub ( ctx : context ) ( t : expr list ) = let f x = (Expr.gno x) in (expr_of_ptr ctx (Z3native.mk_sub (context_gno ctx) (List.length t) (Array.of_list (List.map f t)))) let mk_unary_minus ( ctx : context ) ( t : expr ) = (expr_of_ptr ctx (Z3native.mk_unary_minus (context_gno ctx) (Expr.gno t))) let mk_div ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_div (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_power ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_power (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_lt ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_lt (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_le ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_le (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_gt ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_gt (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_ge ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_ge (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) end module BitVector = struct let mk_sort ( ctx : context ) size = Sort.sort_of_ptr ctx (Z3native.mk_bv_sort (context_gno ctx) size) let is_bv ( x : expr ) = ((sort_kind_of_int (Z3native.get_sort_kind (Expr.gnc x) (Z3native.get_sort (Expr.gnc x) (Expr.gno x)))) == BV_SORT) let is_bv_numeral ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BNUM) let is_bv_bit1 ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BIT1) let is_bv_bit0 ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BIT0) let is_bv_uminus ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BNEG) let is_bv_add ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BADD) let is_bv_sub ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BSUB) let is_bv_mul ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BMUL) let is_bv_sdiv ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BSDIV) let is_bv_udiv ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BUDIV) let is_bv_SRem ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BSREM) let is_bv_urem ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BUREM) let is_bv_smod ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BSMOD) let is_bv_sdiv0 ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BSDIV0) let is_bv_udiv0 ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BUDIV0) let is_bv_srem0 ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BSREM0) let is_bv_urem0 ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BUREM0) let is_bv_smod0 ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BSMOD0) let is_bv_ule ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_ULEQ) let is_bv_sle ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_SLEQ) let is_bv_uge ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_UGEQ) let is_bv_sge ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_SGEQ) let is_bv_ult ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_ULT) let is_bv_slt ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_SLT) let is_bv_ugt ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_UGT) let is_bv_sgt ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_SGT) let is_bv_and ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BAND) let is_bv_or ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BOR) let is_bv_not ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BNOT) let is_bv_xor ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BXOR) let is_bv_nand ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BNAND) let is_bv_nor ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BNOR) let is_bv_xnor ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BXNOR) let is_bv_concat ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_CONCAT) let is_bv_signextension ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_SIGN_EXT) let is_bv_zeroextension ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_ZERO_EXT) let is_bv_extract ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_EXTRACT) let is_bv_repeat ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_REPEAT) let is_bv_reduceor ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BREDOR) let is_bv_reduceand ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BREDAND) let is_bv_comp ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BCOMP) let is_bv_shiftleft ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BSHL) let is_bv_shiftrightlogical ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BLSHR) let is_bv_shiftrightarithmetic ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BASHR) let is_bv_rotateleft ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_ROTATE_LEFT) let is_bv_rotateright ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_ROTATE_RIGHT) let is_bv_rotateleftextended ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_EXT_ROTATE_LEFT) let is_bv_rotaterightextended ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_EXT_ROTATE_RIGHT) let is_int2bv ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_INT2BV) let is_bv2int ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BV2INT) let is_bv_carry ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_CARRY) let is_bv_xor3 ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_XOR3) let get_size (x : Sort.sort ) = Z3native.get_bv_sort_size (Sort.gnc x) (Sort.gno x) let get_int ( x : expr ) = let (r, v) = Z3native.get_numeral_int (Expr.gnc x) (Expr.gno x) in if r then v else raise (Z3native.Exception "Conversion failed.") let numeral_to_string ( x : expr ) = Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x) let mk_const ( ctx : context ) ( name : Symbol.symbol ) ( size : int ) = Expr.mk_const ctx name (mk_sort ctx size) let mk_const_s ( ctx : context ) ( name : string ) ( size : int ) = mk_const ctx (Symbol.mk_string ctx name) size let mk_not ( ctx : context ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_bvnot (context_gno ctx) (Expr.gno t)) let mk_redand ( ctx : context ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_bvredand (context_gno ctx) (Expr.gno t)) let mk_redor ( ctx : context ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_bvredor (context_gno ctx) (Expr.gno t)) let mk_and ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvand (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_or ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvor (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_xor ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvxor (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_nand ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvnand (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_nor ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvnor (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_xnor ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvxnor (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_neg ( ctx : context ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_bvneg (context_gno ctx) (Expr.gno t)) let mk_add ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvadd (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_sub ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvsub (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_mul ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvmul (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_udiv ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvudiv (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_sdiv ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvsdiv (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_urem ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvurem (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_srem ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvsrem (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_smod ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvsmod (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_ult ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_bvult (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_slt ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_bvslt (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_ule ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_bvule (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_sle ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_bvsle (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_uge ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_bvuge (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_sge ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_bvsge (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_ugt ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_bvugt (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_sgt ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_bvsgt (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_concat ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_concat (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_extract ( ctx : context ) ( high : int ) ( low : int ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_extract (context_gno ctx) high low (Expr.gno t)) let mk_sign_ext ( ctx : context ) ( i : int ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_sign_ext (context_gno ctx) i (Expr.gno t)) let mk_zero_ext ( ctx : context ) ( i : int ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_zero_ext (context_gno ctx) i (Expr.gno t)) let mk_repeat ( ctx : context ) ( i : int ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_repeat (context_gno ctx) i (Expr.gno t)) let mk_shl ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvshl (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_lshr ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvlshr (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_ashr ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvashr (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_rotate_left ( ctx : context ) ( i : int ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_rotate_left (context_gno ctx) i (Expr.gno t)) let mk_rotate_right ( ctx : context ) ( i : int ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_rotate_right (context_gno ctx) i (Expr.gno t)) let mk_ext_rotate_left ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_ext_rotate_left (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_ext_rotate_right ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_ext_rotate_right (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_bv2int ( ctx : context ) ( t : expr ) ( signed : bool ) = expr_of_ptr ctx (Z3native.mk_bv2int (context_gno ctx) (Expr.gno t) signed) let mk_add_no_overflow ( ctx : context ) ( t1 : expr ) ( t2 : expr ) ( signed : bool) = (expr_of_ptr ctx (Z3native.mk_bvadd_no_overflow (context_gno ctx) (Expr.gno t1) (Expr.gno t2) signed)) let mk_add_no_underflow ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_bvadd_no_underflow (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_sub_no_overflow ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_bvsub_no_overflow (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_sub_no_underflow ( ctx : context ) ( t1 : expr ) ( t2 : expr ) ( signed : bool) = (expr_of_ptr ctx (Z3native.mk_bvsub_no_underflow (context_gno ctx) (Expr.gno t1) (Expr.gno t2) signed)) let mk_sdiv_no_overflow ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_bvsdiv_no_overflow (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_neg_no_overflow ( ctx : context ) ( t : expr ) = (expr_of_ptr ctx (Z3native.mk_bvneg_no_overflow (context_gno ctx) (Expr.gno t))) let mk_mul_no_overflow ( ctx : context ) ( t1 : expr ) ( t2 : expr ) ( signed : bool) = (expr_of_ptr ctx (Z3native.mk_bvmul_no_overflow (context_gno ctx) (Expr.gno t1) (Expr.gno t2) signed)) let mk_mul_no_underflow ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_bvmul_no_underflow (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_numeral ( ctx : context ) ( v : string ) ( size : int ) = expr_of_ptr ctx (Z3native.mk_numeral (context_gno ctx) v (Sort.gno (mk_sort ctx size))) end module FloatingPoint = struct module RoundingMode = struct let mk_sort ( ctx : context ) = (Sort.sort_of_ptr ctx (Z3native.mk_fpa_rounding_mode_sort (context_gno ctx))) let is_fprm ( x : expr ) = (Sort.get_sort_kind (Expr.get_sort(x))) == ROUNDING_MODE_SORT let mk_round_nearest_ties_to_even ( ctx : context ) = (expr_of_ptr ctx (Z3native.mk_fpa_round_nearest_ties_to_even (context_gno ctx))) let mk_rne ( ctx : context ) = (expr_of_ptr ctx (Z3native.mk_fpa_rne (context_gno ctx))) let mk_round_nearest_ties_to_away ( ctx : context ) = (expr_of_ptr ctx (Z3native.mk_fpa_round_nearest_ties_to_away (context_gno ctx))) let mk_rna ( ctx : context ) = (expr_of_ptr ctx (Z3native.mk_fpa_rna (context_gno ctx))) let mk_round_toward_positive ( ctx : context ) = (expr_of_ptr ctx (Z3native.mk_fpa_round_toward_positive (context_gno ctx))) let mk_rtp ( ctx : context ) = (expr_of_ptr ctx (Z3native.mk_fpa_rtp (context_gno ctx))) let mk_round_toward_negative ( ctx : context ) = (expr_of_ptr ctx (Z3native.mk_fpa_round_toward_negative (context_gno ctx))) let mk_rtn ( ctx : context ) = (expr_of_ptr ctx (Z3native.mk_fpa_rtn (context_gno ctx))) let mk_round_toward_zero ( ctx : context ) = (expr_of_ptr ctx (Z3native.mk_fpa_round_toward_zero (context_gno ctx))) let mk_rtz ( ctx : context ) = (expr_of_ptr ctx (Z3native.mk_fpa_rtz (context_gno ctx))) end let mk_sort ( ctx : context ) ( ebits : int ) ( sbits : int ) = (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort (context_gno ctx) ebits sbits)) let mk_sort_half ( ctx : context ) = (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_half (context_gno ctx))) let mk_sort_16 ( ctx : context ) = (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_16 (context_gno ctx))) let mk_sort_single ( ctx : context ) = (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_single (context_gno ctx))) let mk_sort_32 ( ctx : context ) = (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_32 (context_gno ctx))) let mk_sort_double ( ctx : context ) = (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_double (context_gno ctx))) let mk_sort_64 ( ctx : context ) = (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_64 (context_gno ctx))) let mk_sort_quadruple ( ctx : context ) = (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_quadruple (context_gno ctx))) let mk_sort_128 ( ctx : context ) = (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_128 (context_gno ctx))) let mk_nan ( ctx : context ) ( s : Sort.sort ) = (expr_of_ptr ctx (Z3native.mk_fpa_nan (context_gno ctx) (Sort.gno s))) let mk_inf ( ctx : context ) ( s : Sort.sort ) ( negative : bool ) = (expr_of_ptr ctx (Z3native.mk_fpa_inf (context_gno ctx) (Sort.gno s) negative)) let mk_zero ( ctx : context ) ( s : Sort.sort ) ( negative : bool ) = (expr_of_ptr ctx (Z3native.mk_fpa_zero (context_gno ctx) (Sort.gno s) negative)) let mk_fp ( ctx : context ) ( sign : expr ) ( exponent : expr ) ( significand : expr ) = (expr_of_ptr ctx (Z3native.mk_fpa_fp (context_gno ctx) (Expr.gno sign) (Expr.gno exponent) (Expr.gno significand))) let mk_numeral_f ( ctx : context ) ( value : float ) ( s : Sort.sort ) = (expr_of_ptr ctx (Z3native.mk_fpa_numeral_double (context_gno ctx) value (Sort.gno s))) let mk_numeral_i ( ctx : context ) ( value : int ) ( s : Sort.sort ) = (expr_of_ptr ctx (Z3native.mk_fpa_numeral_int (context_gno ctx) value (Sort.gno s))) let mk_numeral_i_u ( ctx : context ) ( sign : bool ) ( exponent : int ) ( significand : int ) ( s : Sort.sort ) = (expr_of_ptr ctx (Z3native.mk_fpa_numeral_int64_uint64 (context_gno ctx) sign exponent significand (Sort.gno s))) let mk_numeral_s ( ctx : context ) ( v : string ) ( s : Sort.sort ) = (expr_of_ptr ctx (Z3native.mk_numeral (context_gno ctx) v (Sort.gno s))) let is_fp ( x : expr ) = (Sort.get_sort_kind (Expr.get_sort x)) == FLOATING_POINT_SORT let is_abs ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_ABS) let is_neg ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_NEG) let is_add ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_ADD) let is_sub ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_SUB) let is_mul ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_MUL) let is_div ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_DIV) let is_fma ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_FMA) let is_sqrt ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_SQRT) let is_rem ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_REM) let is_round_to_integral ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_ROUND_TO_INTEGRAL) let is_min ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_MIN) let is_max ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_MAX) let is_leq ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_LE) let is_lt ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_LT) let is_geq ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_GE) let is_gt ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_GT) let is_eq ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_EQ) let is_is_normal ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_IS_NORMAL) let is_is_subnormal ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_IS_SUBNORMAL) let is_is_zero ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_IS_ZERO) let is_is_infinite ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_IS_INF) let is_is_nan ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_IS_NAN) let is_is_negative ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_IS_NEGATIVE) let is_is_positive ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_IS_POSITIVE) let is_to_fp ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_TO_FP) let is_to_fp_unsigned ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_TO_FP_UNSIGNED) let is_to_ubv ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_TO_UBV) let is_to_sbv ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_TO_SBV) let is_to_real ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_TO_REAL) let is_to_ieee_bv ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_TO_IEEE_BV) let numeral_to_string ( x : expr ) = Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x) let mk_const ( ctx : context ) ( name : Symbol.symbol ) ( s : Sort.sort ) = Expr.mk_const ctx name s let mk_const_s ( ctx : context ) ( name : string ) ( s : Sort.sort ) = mk_const ctx (Symbol.mk_string ctx name) s let mk_abs ( ctx : context ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_abs (context_gno ctx) (Expr.gno t)) let mk_neg ( ctx : context ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_neg (context_gno ctx) (Expr.gno t)) let mk_add ( ctx : context ) ( rm : expr ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_add (context_gno ctx) (Expr.gno rm) (Expr.gno t1) (Expr.gno t2)) let mk_sub ( ctx : context ) ( rm : expr ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_sub (context_gno ctx) (Expr.gno rm) (Expr.gno t1) (Expr.gno t2)) let mk_mul ( ctx : context ) ( rm : expr ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_mul (context_gno ctx) (Expr.gno rm) (Expr.gno t1) (Expr.gno t2)) let mk_div ( ctx : context ) ( rm : expr ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_div (context_gno ctx) (Expr.gno rm) (Expr.gno t1) (Expr.gno t2)) let mk_fma ( ctx : context ) ( rm : expr ) ( t1 : expr ) ( t2 : expr ) ( t3 : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_fma (context_gno ctx) (Expr.gno rm) (Expr.gno t1) (Expr.gno t2) (Expr.gno t3)) let mk_sqrt ( ctx : context ) ( rm : expr ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_sqrt (context_gno ctx) (Expr.gno rm) (Expr.gno t)) let mk_rem ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_rem (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_round_to_integral ( ctx : context ) ( rm : expr ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_round_to_integral (context_gno ctx) (Expr.gno rm) (Expr.gno t)) let mk_min ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_min (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_max ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_max (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_leq ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_leq (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_lt ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_lt (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_geq ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_geq (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_gt ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_gt (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_eq ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_eq (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_is_normal ( ctx : context ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_is_normal (context_gno ctx) (Expr.gno t)) let mk_is_subnormal ( ctx : context ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_is_subnormal (context_gno ctx) (Expr.gno t)) let mk_is_zero ( ctx : context ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_is_zero (context_gno ctx) (Expr.gno t)) let mk_is_infinite ( ctx : context ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_is_infinite (context_gno ctx) (Expr.gno t)) let mk_is_nan ( ctx : context ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_is_nan (context_gno ctx) (Expr.gno t)) let mk_is_negative ( ctx : context ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_is_negative (context_gno ctx) (Expr.gno t)) let mk_is_positive ( ctx : context ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_is_positive (context_gno ctx) (Expr.gno t)) let mk_to_fp_bv ( ctx : context ) ( t : expr ) ( s : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_fpa_to_fp_bv (context_gno ctx) (Expr.gno t) (Sort.gno s)) let mk_to_fp_float ( ctx : context ) ( rm : expr) ( t : expr ) ( s : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_fpa_to_fp_float (context_gno ctx) (Expr.gno rm) (Expr.gno t) (Sort.gno s)) let mk_to_fp_real ( ctx : context ) ( rm : expr ) ( t : expr ) ( s : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_fpa_to_fp_real (context_gno ctx) (Expr.gno rm) (Expr.gno t) (Sort.gno s)) let mk_to_fp_signed ( ctx : context ) ( rm : expr) ( t : expr ) ( s : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_fpa_to_fp_signed (context_gno ctx) (Expr.gno rm) (Expr.gno t) (Sort.gno s)) let mk_to_fp_unsigned ( ctx : context ) ( rm : expr) ( t : expr ) ( s : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_fpa_to_fp_unsigned (context_gno ctx) (Expr.gno rm) (Expr.gno t) (Sort.gno s)) let mk_to_ubv ( ctx : context ) ( rm : expr) ( t : expr ) ( size : int ) = expr_of_ptr ctx (Z3native.mk_fpa_to_ubv (context_gno ctx) (Expr.gno rm) (Expr.gno t) size) let mk_to_sbv ( ctx : context ) ( rm : expr) ( t : expr ) ( size : int ) = expr_of_ptr ctx (Z3native.mk_fpa_to_sbv (context_gno ctx) (Expr.gno rm) (Expr.gno t) size) let mk_to_real ( ctx : context ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_to_real (context_gno ctx) (Expr.gno t)) let get_ebits ( ctx : context ) ( s : Sort.sort ) = (Z3native.fpa_get_ebits (context_gno ctx) (Sort.gno s)) let get_sbits ( ctx : context ) ( s : Sort.sort ) = (Z3native.fpa_get_sbits (context_gno ctx) (Sort.gno s)) let get_numeral_sign ( ctx : context ) ( t : expr ) = (Z3native.fpa_get_numeral_sign (context_gno ctx) (Expr.gno t)) let get_numeral_significand_string ( ctx : context ) ( t : expr ) = (Z3native.fpa_get_numeral_significand_string (context_gno ctx) (Expr.gno t)) let get_numeral_significand_uint ( ctx : context ) ( t : expr ) = (Z3native.fpa_get_numeral_significand_uint64 (context_gno ctx) (Expr.gno t)) let get_numeral_exponent_string ( ctx : context ) ( t : expr ) = (Z3native.fpa_get_numeral_exponent_string (context_gno ctx) (Expr.gno t)) let get_numeral_exponent_int ( ctx : context ) ( t : expr ) = (Z3native.fpa_get_numeral_exponent_int64 (context_gno ctx) (Expr.gno t)) let mk_to_ieee_bv ( ctx : context ) ( t : expr ) = (expr_of_ptr ctx (Z3native.mk_fpa_to_ieee_bv (context_gno ctx) (Expr.gno t))) let mk_to_fp_int_real ( ctx : context ) ( rm : expr ) ( exponent : expr ) ( significand : expr ) ( s : Sort.sort ) = (expr_of_ptr ctx (Z3native.mk_fpa_to_fp_int_real (context_gno ctx) (Expr.gno rm) (Expr.gno exponent) (Expr.gno significand) (Sort.gno s))) let numeral_to_string ( x : expr ) = Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x) end module Proof = struct let is_true ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_TRUE) let is_asserted ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_ASSERTED) let is_goal ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_GOAL) let is_oeq ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_OEQ) let is_modus_ponens ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_MODUS_PONENS) let is_reflexivity ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_REFLEXIVITY) let is_symmetry ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_SYMMETRY) let is_transitivity ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_TRANSITIVITY) let is_Transitivity_star ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_TRANSITIVITY_STAR) let is_monotonicity ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_MONOTONICITY) let is_quant_intro ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_QUANT_INTRO) let is_distributivity ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_DISTRIBUTIVITY) let is_and_elimination ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_AND_ELIM) let is_or_elimination ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_NOT_OR_ELIM) let is_rewrite ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_REWRITE) let is_rewrite_star ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_REWRITE_STAR) let is_pull_quant ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_PULL_QUANT) let is_pull_quant_star ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_PULL_QUANT_STAR) let is_push_quant ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_PUSH_QUANT) let is_elim_unused_vars ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_ELIM_UNUSED_VARS) let is_der ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_DER) let is_quant_inst ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_QUANT_INST) let is_hypothesis ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_HYPOTHESIS) let is_lemma ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_LEMMA) let is_unit_resolution ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_UNIT_RESOLUTION) let is_iff_true ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_IFF_TRUE) let is_iff_false ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_IFF_FALSE) let is_commutativity ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_COMMUTATIVITY) (* *) let is_def_axiom ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_DEF_AXIOM) let is_def_intro ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_DEF_INTRO) let is_apply_def ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_APPLY_DEF) let is_iff_oeq ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_IFF_OEQ) let is_nnf_pos ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_NNF_POS) let is_nnf_neg ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_NNF_NEG) let is_nnf_star ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_NNF_STAR) let is_cnf_star ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_CNF_STAR) let is_skolemize ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_SKOLEMIZE) let is_modus_ponens_oeq ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_MODUS_PONENS_OEQ) let is_theory_lemma ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_PR_TH_LEMMA) end module Goal = struct type goal = z3_native_object let create ( ctx : context ) ( no : Z3native.ptr ) = let res : goal = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.goal_inc_ref ; dec_ref = Z3native.goal_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res let get_precision ( x : goal ) = goal_prec_of_int (Z3native.goal_precision (z3obj_gnc x) (z3obj_gno x)) let is_precise ( x : goal ) = (get_precision x) == GOAL_PRECISE let is_underapproximation ( x : goal ) = (get_precision x) == GOAL_UNDER let is_overapproximation ( x : goal ) = (get_precision x) == GOAL_OVER let is_garbage ( x : goal ) = (get_precision x) == GOAL_UNDER_OVER let add ( x : goal ) ( constraints : expr list ) = let f e = Z3native.goal_assert (z3obj_gnc x) (z3obj_gno x) (Expr.gno e) in ignore (List.map f constraints) ; () let is_inconsistent ( x : goal ) = Z3native.goal_inconsistent (z3obj_gnc x) (z3obj_gno x) let get_depth ( x : goal ) = Z3native.goal_depth (z3obj_gnc x) (z3obj_gno x) let reset ( x : goal ) = Z3native.goal_reset (z3obj_gnc x) (z3obj_gno x) let get_size ( x : goal ) = Z3native.goal_size (z3obj_gnc x) (z3obj_gno x) let get_formulas ( x : goal ) = let n = get_size x in let f i = ((expr_of_ptr (z3obj_gc x) (Z3native.goal_formula (z3obj_gnc x) (z3obj_gno x) i))) in mk_list f n let get_num_exprs ( x : goal ) = Z3native.goal_num_exprs (z3obj_gnc x) (z3obj_gno x) let is_decided_sat ( x : goal ) = Z3native.goal_is_decided_sat (z3obj_gnc x) (z3obj_gno x) let is_decided_unsat ( x : goal ) = Z3native.goal_is_decided_unsat (z3obj_gnc x) (z3obj_gno x) let translate ( x : goal ) ( to_ctx : context ) = create to_ctx (Z3native.goal_translate (z3obj_gnc x) (z3obj_gno x) (context_gno to_ctx)) let simplify ( x : goal ) ( p : Params.params option ) = let tn = Z3native.mk_tactic (z3obj_gnc x) "simplify" in Z3native.tactic_inc_ref (z3obj_gnc x) tn ; let arn = match p with | None -> Z3native.tactic_apply (z3obj_gnc x) tn (z3obj_gno x) | Some(pn) -> Z3native.tactic_apply_ex (z3obj_gnc x) tn (z3obj_gno x) (z3obj_gno pn) in Z3native.apply_result_inc_ref (z3obj_gnc x) arn ; let sg = Z3native.apply_result_get_num_subgoals (z3obj_gnc x) arn in let res = if sg == 0 then raise (Z3native.Exception "No subgoals") else Z3native.apply_result_get_subgoal (z3obj_gnc x) arn 0 in Z3native.apply_result_dec_ref (z3obj_gnc x) arn ; Z3native.tactic_dec_ref (z3obj_gnc x) tn ; create (z3obj_gc x) res let mk_goal ( ctx : context ) ( models : bool ) ( unsat_cores : bool ) ( proofs : bool ) = create ctx (Z3native.mk_goal (context_gno ctx) models unsat_cores proofs) let to_string ( x : goal ) = Z3native.goal_to_string (z3obj_gnc x) (z3obj_gno x) let as_expr ( x : goal ) = let n = get_size x in if n = 0 then (Boolean.mk_true (z3obj_gc x)) else if n = 1 then (List.hd (get_formulas x)) else (Boolean.mk_and (z3obj_gc x) (get_formulas x)) end module Model = struct type model = z3_native_object let create ( ctx : context ) ( no : Z3native.ptr ) = let res : model = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.model_inc_ref ; dec_ref = Z3native.model_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res module FuncInterp = struct type func_interp = z3_native_object let create ( ctx : context ) ( no : Z3native.ptr ) = let res : func_interp = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.func_interp_inc_ref ; dec_ref = Z3native.func_interp_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res module FuncEntry = struct type func_entry = z3_native_object let create ( ctx : context ) ( no : Z3native.ptr ) = let res : func_entry = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.func_entry_inc_ref ; dec_ref = Z3native.func_entry_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res let get_value ( x : func_entry ) = expr_of_ptr (z3obj_gc x) (Z3native.func_entry_get_value (z3obj_gnc x) (z3obj_gno x)) let get_num_args ( x : func_entry ) = Z3native.func_entry_get_num_args (z3obj_gnc x) (z3obj_gno x) let get_args ( x : func_entry ) = let n = (get_num_args x) in let f i = (expr_of_ptr (z3obj_gc x) (Z3native.func_entry_get_arg (z3obj_gnc x) (z3obj_gno x) i)) in mk_list f n let to_string ( x : func_entry ) = let a = (get_args x) in let f c p = (p ^ (Expr.to_string c) ^ ", ") in "[" ^ List.fold_right f a ((Expr.to_string (get_value x)) ^ "]") end let get_num_entries ( x: func_interp ) = Z3native.func_interp_get_num_entries (z3obj_gnc x) (z3obj_gno x) let get_entries ( x : func_interp ) = let n = (get_num_entries x) in let f i = (FuncEntry.create (z3obj_gc x) (Z3native.func_interp_get_entry (z3obj_gnc x) (z3obj_gno x) i)) in mk_list f n let get_else ( x : func_interp ) = expr_of_ptr (z3obj_gc x) (Z3native.func_interp_get_else (z3obj_gnc x) (z3obj_gno x)) let get_arity ( x : func_interp ) = Z3native.func_interp_get_arity (z3obj_gnc x) (z3obj_gno x) let to_string ( x : func_interp ) = let f c p = ( let n = (FuncEntry.get_num_args c) in p ^ let g c p = (p ^ (Expr.to_string c) ^ ", ") in (if n > 1 then "[" else "") ^ (List.fold_right g (FuncEntry.get_args c) ((if n > 1 then "]" else "") ^ " -> " ^ (Expr.to_string (FuncEntry.get_value c)) ^ ", ")) ) in List.fold_right f (get_entries x) ("else -> " ^ (Expr.to_string (get_else x)) ^ "]") end let get_const_interp ( x : model ) ( f : func_decl ) = if (FuncDecl.get_arity f) != 0 || (sort_kind_of_int (Z3native.get_sort_kind (FuncDecl.gnc f) (Z3native.get_range (FuncDecl.gnc f) (FuncDecl.gno f)))) == ARRAY_SORT then raise (Z3native.Exception "Non-zero arity functions and arrays have FunctionInterpretations as a model. Use FuncInterp.") else let np = Z3native.model_get_const_interp (z3obj_gnc x) (z3obj_gno x) (FuncDecl.gno f) in if (Z3native.is_null np) then None else Some (expr_of_ptr (z3obj_gc x) np) let get_const_interp_e ( x : model ) ( a : expr ) = get_const_interp x (Expr.get_func_decl a) let rec get_func_interp ( x : model ) ( f : func_decl ) = let sk = (sort_kind_of_int (Z3native.get_sort_kind (z3obj_gnc x) (Z3native.get_range (FuncDecl.gnc f) (FuncDecl.gno f)))) in if (FuncDecl.get_arity f) == 0 then let n = Z3native.model_get_const_interp (z3obj_gnc x) (z3obj_gno x) (FuncDecl.gno f) in if (Z3native.is_null n) then None else match sk with | ARRAY_SORT -> if not (Z3native.is_as_array (z3obj_gnc x) n) then raise (Z3native.Exception "Argument was not an array constant") else let fd = Z3native.get_as_array_func_decl (z3obj_gnc x) n in get_func_interp x (func_decl_of_ptr (z3obj_gc x) fd) | _ -> raise (Z3native.Exception "Constant functions do not have a function interpretation; use ConstInterp"); else let n = (Z3native.model_get_func_interp (z3obj_gnc x) (z3obj_gno x) (FuncDecl.gno f)) in if (Z3native.is_null n) then None else Some (FuncInterp.create (z3obj_gc x) n) (** The number of constants that have an interpretation in the model. *) let get_num_consts ( x : model ) = Z3native.model_get_num_consts (z3obj_gnc x) (z3obj_gno x) let get_const_decls ( x : model ) = let n = (get_num_consts x) in let f i = func_decl_of_ptr (z3obj_gc x) (Z3native.model_get_const_decl (z3obj_gnc x) (z3obj_gno x) i) in mk_list f n let get_num_funcs ( x : model ) = Z3native.model_get_num_funcs (z3obj_gnc x) (z3obj_gno x) let get_func_decls ( x : model ) = let n = (get_num_funcs x) in let f i = func_decl_of_ptr (z3obj_gc x) (Z3native.model_get_func_decl (z3obj_gnc x) (z3obj_gno x) i) in mk_list f n let get_decls ( x : model ) = let n_funcs = (get_num_funcs x) in let n_consts = (get_num_consts x ) in let f i = func_decl_of_ptr (z3obj_gc x) (Z3native.model_get_func_decl (z3obj_gnc x) (z3obj_gno x) i) in let g i = func_decl_of_ptr (z3obj_gc x) (Z3native.model_get_const_decl (z3obj_gnc x) (z3obj_gno x) i) in (mk_list f n_funcs) @ (mk_list g n_consts) let eval ( x : model ) ( t : expr ) ( completion : bool ) = let (r, v) = (Z3native.model_eval (z3obj_gnc x) (z3obj_gno x) (Expr.gno t) completion) in if not r then None else Some(expr_of_ptr (z3obj_gc x) v) let evaluate ( x : model ) ( t : expr ) ( completion : bool ) = eval x t completion let get_num_sorts ( x : model ) = Z3native.model_get_num_sorts (z3obj_gnc x) (z3obj_gno x) let get_sorts ( x : model ) = let n = (get_num_sorts x) in let f i = (Sort.sort_of_ptr (z3obj_gc x) (Z3native.model_get_sort (z3obj_gnc x) (z3obj_gno x) i)) in mk_list f n let sort_universe ( x : model ) ( s : Sort.sort ) = let av = AST.ASTVector.create (z3obj_gc x) (Z3native.model_get_sort_universe (z3obj_gnc x) (z3obj_gno x) (Sort.gno s)) in (AST.ASTVector.to_expr_list av) let to_string ( x : model ) = Z3native.model_to_string (z3obj_gnc x) (z3obj_gno x) end module Probe = struct type probe = z3_native_object let create ( ctx : context ) ( no : Z3native.ptr ) = let res : probe = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.probe_inc_ref ; dec_ref = Z3native.probe_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res let apply ( x : probe ) ( g : Goal.goal ) = Z3native.probe_apply (z3obj_gnc x) (z3obj_gno x) (z3obj_gno g) let get_num_probes ( ctx : context ) = Z3native.get_num_probes (context_gno ctx) let get_probe_names ( ctx : context ) = let n = (get_num_probes ctx) in let f i = (Z3native.get_probe_name (context_gno ctx) i) in mk_list f n let get_probe_description ( ctx : context ) ( name : string ) = Z3native.probe_get_descr (context_gno ctx) name let mk_probe ( ctx : context ) ( name : string ) = (create ctx (Z3native.mk_probe (context_gno ctx) name)) let const ( ctx : context ) ( v : float ) = (create ctx (Z3native.probe_const (context_gno ctx) v)) let lt ( ctx : context ) ( p1 : probe ) ( p2 : probe ) = (create ctx (Z3native.probe_lt (context_gno ctx) (z3obj_gno p1) (z3obj_gno p2))) let gt ( ctx : context ) ( p1 : probe ) ( p2 : probe ) = (create ctx (Z3native.probe_gt (context_gno ctx) (z3obj_gno p1) (z3obj_gno p2))) let le ( ctx : context ) ( p1 : probe ) ( p2 : probe ) = (create ctx (Z3native.probe_le (context_gno ctx) (z3obj_gno p1) (z3obj_gno p2))) let ge ( ctx : context ) ( p1 : probe ) ( p2 : probe ) = (create ctx (Z3native.probe_ge (context_gno ctx) (z3obj_gno p1) (z3obj_gno p2))) let eq ( ctx : context ) ( p1 : probe ) ( p2 : probe ) = (create ctx (Z3native.probe_eq (context_gno ctx) (z3obj_gno p1) (z3obj_gno p2))) let and_ ( ctx : context ) ( p1 : probe ) ( p2 : probe ) = (create ctx (Z3native.probe_and (context_gno ctx) (z3obj_gno p1) (z3obj_gno p2))) let or_ ( ctx : context ) ( p1 : probe ) ( p2 : probe ) = (create ctx (Z3native.probe_or (context_gno ctx) (z3obj_gno p1) (z3obj_gno p2))) let not_ ( ctx : context ) ( p : probe ) = (create ctx (Z3native.probe_not (context_gno ctx) (z3obj_gno p))) end module Tactic = struct type tactic = z3_native_object let create ( ctx : context ) ( no : Z3native.ptr ) = let res : tactic = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.tactic_inc_ref ; dec_ref = Z3native.tactic_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res module ApplyResult = struct type apply_result = z3_native_object let create ( ctx : context ) ( no : Z3native.ptr ) = let res : apply_result = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.apply_result_inc_ref ; dec_ref = Z3native.apply_result_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res let get_num_subgoals ( x : apply_result ) = Z3native.apply_result_get_num_subgoals (z3obj_gnc x) (z3obj_gno x) let get_subgoals ( x : apply_result ) = let n = (get_num_subgoals x) in let f i = Goal.create (z3obj_gc x) (Z3native.apply_result_get_subgoal (z3obj_gnc x) (z3obj_gno x) i) in mk_list f n let get_subgoal ( x : apply_result ) ( i : int ) = Goal.create (z3obj_gc x) (Z3native.apply_result_get_subgoal (z3obj_gnc x) (z3obj_gno x) i) let convert_model ( x : apply_result ) ( i : int ) ( m : Model.model ) = Model.create (z3obj_gc x) (Z3native.apply_result_convert_model (z3obj_gnc x) (z3obj_gno x) i (z3obj_gno m)) let to_string ( x : apply_result ) = Z3native.apply_result_to_string (z3obj_gnc x) (z3obj_gno x) end let get_help ( x : tactic ) = Z3native.tactic_get_help (z3obj_gnc x) (z3obj_gno x) let get_param_descrs ( x : tactic ) = Params.ParamDescrs.param_descrs_of_ptr (z3obj_gc x) (Z3native.tactic_get_param_descrs (z3obj_gnc x) (z3obj_gno x)) let apply ( x : tactic ) ( g : Goal.goal ) ( p : Params.params option ) = match p with | None -> (ApplyResult.create (z3obj_gc x) (Z3native.tactic_apply (z3obj_gnc x) (z3obj_gno x) (z3obj_gno g))) | Some (pn) -> (ApplyResult.create (z3obj_gc x) (Z3native.tactic_apply_ex (z3obj_gnc x) (z3obj_gno x) (z3obj_gno g) (z3obj_gno pn))) let get_num_tactics ( ctx : context ) = Z3native.get_num_tactics (context_gno ctx) let get_tactic_names ( ctx : context ) = let n = (get_num_tactics ctx ) in let f i = (Z3native.get_tactic_name (context_gno ctx) i) in mk_list f n let get_tactic_description ( ctx : context ) ( name : string ) = Z3native.tactic_get_descr (context_gno ctx) name let mk_tactic ( ctx : context ) ( name : string ) = create ctx (Z3native.mk_tactic (context_gno ctx) name) let and_then ( ctx : context ) ( t1 : tactic ) ( t2 : tactic ) ( ts : tactic list ) = let f p c = (match p with | None -> (Some (z3obj_gno c)) | Some(x) -> (Some (Z3native.tactic_and_then (context_gno ctx) (z3obj_gno c) x))) in match (List.fold_left f None ts) with | None -> create ctx (Z3native.tactic_and_then (context_gno ctx) (z3obj_gno t1) (z3obj_gno t2)) | Some(x) -> let o = (Z3native.tactic_and_then (context_gno ctx) (z3obj_gno t2) x) in create ctx (Z3native.tactic_and_then (context_gno ctx) (z3obj_gno t1) o) let or_else ( ctx : context ) ( t1 : tactic ) ( t2 : tactic ) = create ctx (Z3native.tactic_or_else (context_gno ctx) (z3obj_gno t1) (z3obj_gno t2)) let try_for ( ctx : context ) ( t : tactic ) ( ms : int ) = create ctx (Z3native.tactic_try_for (context_gno ctx) (z3obj_gno t) ms) let when_ ( ctx : context ) ( p : Probe.probe ) ( t : tactic ) = create ctx (Z3native.tactic_when (context_gno ctx) (z3obj_gno p) (z3obj_gno t)) let cond ( ctx : context ) ( p : Probe.probe ) ( t1 : tactic ) ( t2 : tactic ) = create ctx (Z3native.tactic_cond (context_gno ctx) (z3obj_gno p) (z3obj_gno t1) (z3obj_gno t2)) let repeat ( ctx : context ) ( t : tactic ) ( max : int ) = create ctx (Z3native.tactic_repeat (context_gno ctx) (z3obj_gno t) max) let skip ( ctx : context ) = create ctx (Z3native.tactic_skip (context_gno ctx)) let fail ( ctx : context ) = create ctx (Z3native.tactic_fail (context_gno ctx)) let fail_if ( ctx : context ) ( p : Probe.probe ) = create ctx (Z3native.tactic_fail_if (context_gno ctx) (z3obj_gno p)) let fail_if_not_decided ( ctx : context ) = create ctx (Z3native.tactic_fail_if_not_decided (context_gno ctx)) let using_params ( ctx : context ) ( t : tactic ) ( p : Params.params ) = create ctx (Z3native.tactic_using_params (context_gno ctx) (z3obj_gno t) (z3obj_gno p)) let with_ ( ctx : context ) ( t : tactic ) ( p : Params.params ) = using_params ctx t p let par_or ( ctx : context ) ( t : tactic list ) = let f e = (z3obj_gno e) in create ctx (Z3native.tactic_par_or (context_gno ctx) (List.length t) (Array.of_list (List.map f t))) let par_and_then ( ctx : context ) ( t1 : tactic ) ( t2 : tactic ) = create ctx (Z3native.tactic_par_and_then (context_gno ctx) (z3obj_gno t1) (z3obj_gno t2)) let interrupt ( ctx : context ) = Z3native.interrupt (context_gno ctx) end module Statistics = struct type statistics = z3_native_object let create ( ctx : context ) ( no : Z3native.ptr ) = let res : statistics = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.stats_inc_ref ; dec_ref = Z3native.stats_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res module Entry = struct type statistics_entry = { mutable m_key : string; mutable m_is_int : bool ; mutable m_is_float : bool ; mutable m_int : int ; mutable m_float : float } let create_si k v = let res : statistics_entry = { m_key = k ; m_is_int = true ; m_is_float = false ; m_int = v ; m_float = 0.0 } in res let create_sd k v = let res : statistics_entry = { m_key = k ; m_is_int = false ; m_is_float = true ; m_int = 0 ; m_float = v } in res let get_key (x : statistics_entry) = x.m_key let get_int (x : statistics_entry) = x.m_int let get_float (x : statistics_entry) = x.m_float let is_int (x : statistics_entry) = x.m_is_int let is_float (x : statistics_entry) = x.m_is_float let to_string_value (x : statistics_entry) = if (is_int x) then string_of_int (get_int x) else if (is_float x) then string_of_float (get_float x) else raise (Z3native.Exception "Unknown statistical entry type") let to_string ( x : statistics_entry ) = (get_key x) ^ ": " ^ (to_string_value x) end let to_string ( x : statistics ) = Z3native.stats_to_string (z3obj_gnc x) (z3obj_gno x) let get_size ( x : statistics ) = Z3native.stats_size (z3obj_gnc x) (z3obj_gno x) let get_entries ( x : statistics ) = let n = (get_size x ) in let f i = ( let k = Z3native.stats_get_key (z3obj_gnc x) (z3obj_gno x) i in if (Z3native.stats_is_uint (z3obj_gnc x) (z3obj_gno x) i) then (Entry.create_si k (Z3native.stats_get_uint_value (z3obj_gnc x) (z3obj_gno x) i)) else (Entry.create_sd k (Z3native.stats_get_double_value (z3obj_gnc x) (z3obj_gno x) i)) ) in mk_list f n let get_keys ( x : statistics ) = let n = (get_size x) in let f i = (Z3native.stats_get_key (z3obj_gnc x) (z3obj_gno x) i) in mk_list f n let get ( x : statistics ) ( key : string ) = let f p c = (if ((Entry.get_key c) == key) then (Some c) else p) in List.fold_left f None (get_entries x) end module Solver = struct type solver = z3_native_object type status = UNSATISFIABLE | UNKNOWN | SATISFIABLE let create ( ctx : context ) ( no : Z3native.ptr ) = let res : solver = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.solver_inc_ref ; dec_ref = Z3native.solver_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res let string_of_status ( s : status) = match s with | UNSATISFIABLE -> "unsatisfiable" | SATISFIABLE -> "satisfiable" | _ -> "unknown" let get_help ( x : solver ) = Z3native.solver_get_help (z3obj_gnc x) (z3obj_gno x) let set_parameters ( x : solver ) ( p : Params.params )= Z3native.solver_set_params (z3obj_gnc x) (z3obj_gno x) (z3obj_gno p) let get_param_descrs ( x : solver ) = Params.ParamDescrs.param_descrs_of_ptr (z3obj_gc x) (Z3native.solver_get_param_descrs (z3obj_gnc x) (z3obj_gno x)) let get_num_scopes ( x : solver ) = Z3native.solver_get_num_scopes (z3obj_gnc x) (z3obj_gno x) let push ( x : solver ) = Z3native.solver_push (z3obj_gnc x) (z3obj_gno x) let pop ( x : solver ) ( n : int ) = Z3native.solver_pop (z3obj_gnc x) (z3obj_gno x) n let reset ( x : solver ) = Z3native.solver_reset (z3obj_gnc x) (z3obj_gno x) let add ( x : solver ) ( constraints : expr list ) = let f e = (Z3native.solver_assert (z3obj_gnc x) (z3obj_gno x) (Expr.gno e)) in ignore (List.map f constraints) let assert_and_track_l ( x : solver ) ( cs : expr list ) ( ps : expr list ) = if ((List.length cs) != (List.length ps)) then raise (Z3native.Exception "Argument size mismatch") else let f a b = (Z3native.solver_assert_and_track (z3obj_gnc x) (z3obj_gno x) (Expr.gno a) (Expr.gno b)) in ignore (List.iter2 f cs ps) let assert_and_track ( x : solver ) ( c : expr ) ( p : expr ) = Z3native.solver_assert_and_track (z3obj_gnc x) (z3obj_gno x) (Expr.gno c) (Expr.gno p) let get_num_assertions ( x : solver ) = let a = AST.ASTVector.create (z3obj_gc x) (Z3native.solver_get_assertions (z3obj_gnc x) (z3obj_gno x)) in (AST.ASTVector.get_size a) let get_assertions ( x : solver ) = let av = AST.ASTVector.create (z3obj_gc x) (Z3native.solver_get_assertions (z3obj_gnc x) (z3obj_gno x)) in (AST.ASTVector.to_expr_list av) let check ( x : solver ) ( assumptions : expr list ) = let r = if ((List.length assumptions) == 0) then lbool_of_int (Z3native.solver_check (z3obj_gnc x) (z3obj_gno x)) else let f x = (Expr.gno x) in lbool_of_int (Z3native.solver_check_assumptions (z3obj_gnc x) (z3obj_gno x) (List.length assumptions) (Array.of_list (List.map f assumptions))) in match r with | L_TRUE -> SATISFIABLE | L_FALSE -> UNSATISFIABLE | _ -> UNKNOWN let get_model ( x : solver ) = let q = Z3native.solver_get_model (z3obj_gnc x) (z3obj_gno x) in if (Z3native.is_null q) then None else Some (Model.create (z3obj_gc x) q) let get_proof ( x : solver ) = let q = Z3native.solver_get_proof (z3obj_gnc x) (z3obj_gno x) in if (Z3native.is_null q) then None else Some (expr_of_ptr (z3obj_gc x) q) let get_unsat_core ( x : solver ) = let av = AST.ASTVector.create (z3obj_gc x) (Z3native.solver_get_unsat_core (z3obj_gnc x) (z3obj_gno x)) in (AST.ASTVector.to_expr_list av) let get_reason_unknown ( x : solver ) = Z3native.solver_get_reason_unknown (z3obj_gnc x) (z3obj_gno x) let get_statistics ( x : solver ) = (Statistics.create (z3obj_gc x) (Z3native.solver_get_statistics (z3obj_gnc x) (z3obj_gno x))) let mk_solver ( ctx : context ) ( logic : Symbol.symbol option ) = match logic with | None -> (create ctx (Z3native.mk_solver (context_gno ctx))) | Some (x) -> (create ctx (Z3native.mk_solver_for_logic (context_gno ctx) (Symbol.gno x))) let mk_solver_s ( ctx : context ) ( logic : string ) = mk_solver ctx (Some (Symbol.mk_string ctx logic)) let mk_simple_solver ( ctx : context ) = (create ctx (Z3native.mk_simple_solver (context_gno ctx))) let mk_solver_t ( ctx : context ) ( t : Tactic.tactic ) = (create ctx (Z3native.mk_solver_from_tactic (context_gno ctx) (z3obj_gno t))) let to_string ( x : solver ) = Z3native.solver_to_string (z3obj_gnc x) (z3obj_gno x) end module Fixedpoint = struct type fixedpoint = z3_native_object let create ( ctx : context ) = let res : fixedpoint = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.fixedpoint_inc_ref ; dec_ref = Z3native.fixedpoint_dec_ref } in (z3obj_sno res ctx (Z3native.mk_fixedpoint (context_gno ctx))) ; (z3obj_create res) ; res let get_help ( x : fixedpoint ) = Z3native.fixedpoint_get_help (z3obj_gnc x) (z3obj_gno x) let set_params ( x : fixedpoint ) ( p : Params.params )= Z3native.fixedpoint_set_params (z3obj_gnc x) (z3obj_gno x) (z3obj_gno p) let get_param_descrs ( x : fixedpoint ) = Params.ParamDescrs.param_descrs_of_ptr (z3obj_gc x) (Z3native.fixedpoint_get_param_descrs (z3obj_gnc x) (z3obj_gno x)) let add ( x : fixedpoint ) ( constraints : expr list ) = let f e = (Z3native.fixedpoint_assert (z3obj_gnc x) (z3obj_gno x) (Expr.gno e)) in ignore (List.map f constraints) ; () let register_relation ( x : fixedpoint ) ( f : func_decl ) = Z3native.fixedpoint_register_relation (z3obj_gnc x) (z3obj_gno x) (FuncDecl.gno f) let add_rule ( x : fixedpoint ) ( rule : expr ) ( name : Symbol.symbol option ) = match name with | None -> Z3native.fixedpoint_add_rule (z3obj_gnc x) (z3obj_gno x) (Expr.gno rule) null | Some(y) -> Z3native.fixedpoint_add_rule (z3obj_gnc x) (z3obj_gno x) (Expr.gno rule) (Symbol.gno y) let add_fact ( x : fixedpoint ) ( pred : func_decl ) ( args : int list ) = Z3native.fixedpoint_add_fact (z3obj_gnc x) (z3obj_gno x) (FuncDecl.gno pred) (List.length args) (Array.of_list args) let query ( x : fixedpoint ) ( query : expr ) = match (lbool_of_int (Z3native.fixedpoint_query (z3obj_gnc x) (z3obj_gno x) (Expr.gno query))) with | L_TRUE -> Solver.SATISFIABLE | L_FALSE -> Solver.UNSATISFIABLE | _ -> Solver.UNKNOWN let query_r ( x : fixedpoint ) ( relations : func_decl list ) = let f x = AST.ptr_of_ast (ast_of_func_decl x) in match (lbool_of_int (Z3native.fixedpoint_query_relations (z3obj_gnc x) (z3obj_gno x) (List.length relations) (Array.of_list (List.map f relations)))) with | L_TRUE -> Solver.SATISFIABLE | L_FALSE -> Solver.UNSATISFIABLE | _ -> Solver.UNKNOWN let push ( x : fixedpoint ) = Z3native.fixedpoint_push (z3obj_gnc x) (z3obj_gno x) let pop ( x : fixedpoint ) = Z3native.fixedpoint_pop (z3obj_gnc x) (z3obj_gno x) let update_rule ( x : fixedpoint ) ( rule : expr ) ( name : Symbol.symbol ) = Z3native.fixedpoint_update_rule (z3obj_gnc x) (z3obj_gno x) (Expr.gno rule) (Symbol.gno name) let get_answer ( x : fixedpoint ) = let q = (Z3native.fixedpoint_get_answer (z3obj_gnc x) (z3obj_gno x)) in if (Z3native.is_null q) then None else Some (expr_of_ptr (z3obj_gc x) q) let get_reason_unknown ( x : fixedpoint ) = Z3native.fixedpoint_get_reason_unknown (z3obj_gnc x) (z3obj_gno x) let get_num_levels ( x : fixedpoint ) ( predicate : func_decl ) = Z3native.fixedpoint_get_num_levels (z3obj_gnc x) (z3obj_gno x) (FuncDecl.gno predicate) let get_cover_delta ( x : fixedpoint ) ( level : int ) ( predicate : func_decl ) = let q = (Z3native.fixedpoint_get_cover_delta (z3obj_gnc x) (z3obj_gno x) level (FuncDecl.gno predicate)) in if (Z3native.is_null q) then None else Some (expr_of_ptr (z3obj_gc x) q) let add_cover ( x : fixedpoint ) ( level : int ) ( predicate : func_decl ) ( property : expr ) = Z3native.fixedpoint_add_cover (z3obj_gnc x) (z3obj_gno x) level (FuncDecl.gno predicate) (Expr.gno property) let to_string ( x : fixedpoint ) = Z3native.fixedpoint_to_string (z3obj_gnc x) (z3obj_gno x) 0 [||] let set_predicate_representation ( x : fixedpoint ) ( f : func_decl ) ( kinds : Symbol.symbol list ) = Z3native.fixedpoint_set_predicate_representation (z3obj_gnc x) (z3obj_gno x) (FuncDecl.gno f) (List.length kinds) (Symbol.symbol_lton kinds) let to_string_q ( x : fixedpoint ) ( queries : expr list ) = let f x = Expr.gno x in Z3native.fixedpoint_to_string (z3obj_gnc x) (z3obj_gno x) (List.length queries) (Array.of_list (List.map f queries)) let get_rules ( x : fixedpoint ) = let av = (AST.ASTVector.create (z3obj_gc x) (Z3native.fixedpoint_get_rules (z3obj_gnc x) (z3obj_gno x))) in (AST.ASTVector.to_expr_list av) let get_assertions ( x : fixedpoint ) = let av = (AST.ASTVector.create (z3obj_gc x) (Z3native.fixedpoint_get_assertions (z3obj_gnc x) (z3obj_gno x))) in (AST.ASTVector.to_expr_list av) let mk_fixedpoint ( ctx : context ) = create ctx let get_statistics ( x : fixedpoint ) = let s = Z3native.fixedpoint_get_statistics (z3obj_gnc x) (z3obj_gno x) in (Statistics.create (z3obj_gc x) s) let parse_string ( x : fixedpoint ) ( s : string ) = let av = (AST.ASTVector.create (z3obj_gc x) (Z3native.fixedpoint_from_string (z3obj_gnc x) (z3obj_gno x) s)) in (AST.ASTVector.to_expr_list av) let parse_file ( x : fixedpoint ) ( filename : string ) = let av = (AST.ASTVector.create (z3obj_gc x) (Z3native.fixedpoint_from_file (z3obj_gnc x) (z3obj_gno x) filename)) in (AST.ASTVector.to_expr_list av) end module SMT = struct let benchmark_to_smtstring ( ctx : context ) ( name : string ) ( logic : string ) ( status : string ) ( attributes : string ) ( assumptions : expr list ) ( formula : expr ) = Z3native.benchmark_to_smtlib_string (context_gno ctx) name logic status attributes (List.length assumptions) (let f x = Expr.gno (x) in (Array.of_list (List.map f assumptions))) (Expr.gno formula) let parse_smtlib_string ( ctx : context ) ( str : string ) ( sort_names : Symbol.symbol list ) ( sorts : Sort.sort list ) ( decl_names : Symbol.symbol list ) ( decls : func_decl list ) = let csn = (List.length sort_names) in let cs = (List.length sorts) in let cdn = (List.length decl_names) in let cd = (List.length decls) in if (csn != cs || cdn != cd) then raise (Z3native.Exception "Argument size mismatch") else Z3native.parse_smtlib_string (context_gno ctx) str cs (Symbol.symbol_lton sort_names) (Sort.sort_lton sorts) cd (Symbol.symbol_lton decl_names) (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))) let parse_smtlib_file ( ctx : context ) ( file_name : string ) ( sort_names : Symbol.symbol list ) ( sorts : Sort.sort list ) ( decl_names : Symbol.symbol list ) ( decls : func_decl list ) = let csn = (List.length sort_names) in let cs = (List.length sorts) in let cdn = (List.length decl_names) in let cd = (List.length decls) in if (csn != cs || cdn != cd) then raise (Z3native.Exception "Argument size mismatch") else Z3native.parse_smtlib_file (context_gno ctx) file_name cs (Symbol.symbol_lton sort_names) (Sort.sort_lton sorts) cd (Symbol.symbol_lton decl_names) (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))) let get_num_smtlib_formulas ( ctx : context ) = Z3native.get_smtlib_num_formulas (context_gno ctx) let get_smtlib_formulas ( ctx : context ) = let n = (get_num_smtlib_formulas ctx ) in let f i =(expr_of_ptr ctx (Z3native.get_smtlib_formula (context_gno ctx) i)) in mk_list f n let get_num_smtlib_assumptions ( ctx : context ) = Z3native.get_smtlib_num_assumptions (context_gno ctx) let get_smtlib_assumptions ( ctx : context ) = let n = (get_num_smtlib_assumptions ctx ) in let f i = (expr_of_ptr ctx (Z3native.get_smtlib_assumption (context_gno ctx) i)) in mk_list f n let get_num_smtlib_decls ( ctx : context ) = Z3native.get_smtlib_num_decls (context_gno ctx) let get_smtlib_decls ( ctx : context ) = let n = (get_num_smtlib_decls ctx) in let f i = func_decl_of_ptr ctx (Z3native.get_smtlib_decl (context_gno ctx) i) in mk_list f n let get_num_smtlib_sorts ( ctx : context ) = Z3native.get_smtlib_num_sorts (context_gno ctx) let get_smtlib_sorts ( ctx : context ) = let n = (get_num_smtlib_sorts ctx) in let f i = (Sort.sort_of_ptr ctx (Z3native.get_smtlib_sort (context_gno ctx) i)) in mk_list f n let parse_smtlib2_string ( ctx : context ) ( str : string ) ( sort_names : Symbol.symbol list ) ( sorts : Sort.sort list ) ( decl_names : Symbol.symbol list ) ( decls : func_decl list ) = let csn = (List.length sort_names) in let cs = (List.length sorts) in let cdn = (List.length decl_names) in let cd = (List.length decls) in if (csn != cs || cdn != cd) then raise (Z3native.Exception "Argument size mismatch") else (expr_of_ptr ctx (Z3native.parse_smtlib2_string (context_gno ctx) str cs (Symbol.symbol_lton sort_names) (Sort.sort_lton sorts) cd (Symbol.symbol_lton decl_names) (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))))) let parse_smtlib2_file ( ctx : context ) ( file_name : string ) ( sort_names : Symbol.symbol list ) ( sorts : Sort.sort list ) ( decl_names : Symbol.symbol list ) ( decls : func_decl list ) = let csn = (List.length sort_names) in let cs = (List.length sorts) in let cdn = (List.length decl_names) in let cd = (List.length decls) in if (csn != cs || cdn != cd) then raise (Z3native.Exception "Argument size mismatch") else (expr_of_ptr ctx (Z3native.parse_smtlib2_string (context_gno ctx) file_name cs (Symbol.symbol_lton sort_names) (Sort.sort_lton sorts) cd (Symbol.symbol_lton decl_names) (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))))) end module Interpolation = struct let mk_interpolant ( ctx : context ) ( a : expr ) = (expr_of_ptr ctx (Z3native.mk_interpolant (context_gno ctx) (Expr.gno a))) let mk_interpolation_context ( settings : ( string * string ) list ) = let cfg = Z3native.mk_config () in let f e = (Z3native.set_param_value cfg (fst e) (snd e)) in (List.iter f settings) ; let v = Z3native.mk_interpolation_context cfg in Z3native.del_config(cfg) ; Z3native.set_ast_print_mode v (int_of_ast_print_mode PRINT_SMTLIB2_COMPLIANT) ; Z3native.set_internal_error_handler v ; let res = { m_n_ctx = v; m_n_obj_cnt = 0 } in let f = fun o -> dispose_context o in Gc.finalise f res; res let get_interpolant ( ctx : context ) ( pf : expr ) ( pat: expr ) ( p : Params.params ) = let av = (AST.ASTVector.create ctx (Z3native.get_interpolant (context_gno ctx) (Expr.gno pf) (Expr.gno pat) (z3obj_gno p))) in AST.ASTVector.to_expr_list av let compute_interpolant ( ctx : context ) ( pat : expr ) ( p : Params.params ) = let (r, interp, model) = (Z3native.compute_interpolant (context_gno ctx) (Expr.gno pat) (z3obj_gno p)) in let res = (lbool_of_int r) in match res with | L_TRUE -> (res, None, Some(Model.create ctx model)) | L_FALSE -> (res, Some((AST.ASTVector.to_expr_list (AST.ASTVector.create ctx interp))), None) | _ -> (res, None, None) let get_interpolation_profile ( ctx : context ) = (Z3native.interpolation_profile (context_gno ctx)) let read_interpolation_problem ( ctx : context ) ( filename : string ) = let (r, num, cnsts, parents, error, num_theory, theory) = (Z3native.read_interpolation_problem (context_gno ctx) filename) in match r with | 0 -> raise (Z3native.Exception "Interpolation problem could not be read.") | _ -> let f1 i = (expr_of_ptr ctx (Array.get cnsts i)) in let f2 i = (Array.get parents i) in let f3 i = (expr_of_ptr ctx (Array.get theory i)) in ((mk_list f1 num), (mk_list f2 num), (mk_list f3 num_theory)) let check_interpolant ( ctx : context ) ( num : int ) ( cnsts : Expr.expr list ) ( parents : int list ) ( interps : Expr.expr list ) ( num_theory : int ) ( theory : Expr.expr list ) = let (r, str) = (Z3native.check_interpolant (context_gno ctx) num (let f x = Expr.gno x in (Array.of_list (List.map f cnsts))) (Array.of_list parents) (let f x = Expr.gno x in (Array.of_list (List.map f interps))) num_theory (let f x = Expr.gno x in (Array.of_list (List.map f theory)))) in match (lbool_of_int r) with | L_UNDEF -> raise (Z3native.Exception "Interpolant could not be verified.") | L_FALSE -> raise (Z3native.Exception "Interpolant could not be verified.") | _ -> () let write_interpolation_problem ( ctx : context ) ( num : int ) ( cnsts : Expr.expr list ) ( parents : int list ) ( filename : string ) ( num_theory : int ) ( theory : Expr.expr list ) = (Z3native.write_interpolation_problem (context_gno ctx) num (expr_lton cnsts) (Array.of_list parents) filename num_theory (expr_lton theory)) ; () end let set_global_param ( id : string ) ( value : string ) = (Z3native.global_param_set id value) let get_global_param ( id : string ) = let (r, v) = (Z3native.global_param_get id) in if not r then None else Some v let global_param_reset_all = Z3native.global_param_reset_all let toggle_warning_messages ( enabled : bool ) = Z3native.toggle_warning_messages enabled let enable_trace ( tag : string ) = (Z3native.enable_trace tag) let disable_trace ( tag : string ) = (Z3native.enable_trace tag) z3-4.4.0/src/api/ml/README0000644000175000017500000000103212540347414014471 0ustar michaelmichaelThis is the new ML API introduced with Z3 4.4. For the legacy bindings, please refer to previous releases of Z3. On Windows, there are no less than four different ports of OCaml. The Z3 build system assumes that either the win32 or the win64 port is installed. This means that OCaml will use `cl' as the underlying C compiler and not the cygwin or mingw compilers. By default, a make target called `ocamlfind_install' is added. This installs Z3 into the ocamlfind package repository such that programs can be compiled via ocamlfind.z3-4.4.0/src/api/ml/z3.mli0000644000175000017500000036720212540347414014666 0ustar michaelmichael(** The Z3 ML/OCaml Interface. Copyright (C) 2012 Microsoft Corporation @author CM Wintersteiger (cwinter) 2012-12-17 *) (** General Z3 exceptions Many functions in this API may throw an exception; if they do, it is this one.*) exception Error of string (** Context objects. Most interactions with Z3 are interpreted in some context; many users will only require one such object, but power users may require more than one. To start using Z3, do let ctx = (mk_context []) in (...) where a list of pairs of strings may be passed to set options on the context, e.g., like so: let cfg = [("model", "true"); ("...", "...")] in let ctx = (mk_context cfg) in (...) *) type context (** Create a context object The following parameters can be set: - proof (Boolean) Enable proof generation - debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting - trace (Boolean) Tracing support for VCC - trace_file_name (String) Trace out file for VCC traces - timeout (unsigned) default timeout (in milliseconds) used for solvers - well_sorted_check type checker - auto_config use heuristics to automatically select solver and configure it - model model generation for solvers, this parameter can be overwritten when creating a solver - model_validate validate models produced by solvers - unsat_core unsat-core generation for solvers, this parameter can be overwritten when creating a solver *) val mk_context : (string * string) list -> context (** Interaction logging for Z3 Note that this is a global, static log and if multiple Context objects are created, it logs the interaction with all of them. *) module Log : sig (** Open an interaction log file. @return True if opening the log file succeeds, false otherwise. *) (* CMW: "open" is a reserved keyword. *) val open_ : string -> bool (** Closes the interaction log. *) val close : unit -> unit (** Appends a user-provided string to the interaction log. *) val append : string -> unit end (** Version information *) module Version : sig (** The major version. *) val major : int (** The minor version. *) val minor : int (** The build version. *) val build : int (** The revision. *) val revision : int (** A string representation of the version information. *) val to_string : string end (** Symbols are used to name several term and type constructors *) module Symbol : sig type symbol (** The kind of the symbol (int or string) *) val kind : symbol -> Z3enums.symbol_kind (** Indicates whether the symbol is of Int kind *) val is_int_symbol : symbol -> bool (** Indicates whether the symbol is of string kind. *) val is_string_symbol : symbol -> bool (** The int value of the symbol. *) val get_int : symbol -> int (** The string value of the symbol. *) val get_string : symbol -> string (** A string representation of the symbol. *) val to_string : symbol -> string (** Creates a new symbol using an integer. Not all integers can be passed to this function. The legal range of unsigned integers is 0 to 2^30-1. *) val mk_int : context -> int -> symbol (** Creates a new symbol using a string. *) val mk_string : context -> string -> symbol (** Create a list of symbols. *) val mk_ints : context -> int list -> symbol list (** Create a list of symbols. *) val mk_strings : context -> string list -> symbol list end (** The abstract syntax tree (AST) module *) module rec AST : sig type ast (** Vectors of ASTs *) module ASTVector : sig type ast_vector (** Create an empty AST vector *) val mk_ast_vector : context -> ast_vector (** The size of the vector *) val get_size : ast_vector -> int (** Retrieves the i-th object in the vector. @return An AST *) val get : ast_vector -> int -> ast (** Sets the i-th object in the vector. *) val set : ast_vector -> int -> ast -> unit (** Resize the vector to a new size. *) val resize : ast_vector -> int -> unit (** Add an ast to the back of the vector. The size is increased by 1. *) val push : ast_vector -> ast -> unit (** Translates all ASTs in the vector to another context. @return A new ASTVector *) val translate : ast_vector -> context -> ast_vector (** Translates the ASTVector into an (Ast.ast list) *) val to_list : ast_vector -> ast list (** Translates the ASTVector into an (Expr.expr list) *) val to_expr_list : ast_vector -> Expr.expr list (** Retrieves a string representation of the vector. *) val to_string : ast_vector -> string end (** Map from AST to AST *) module ASTMap : sig type ast_map (** Create an empty mapping from AST to AST *) val mk_ast_map : context -> ast_map (** Checks whether the map contains a key. @return True if the key in the map, false otherwise. *) val contains : ast_map -> ast -> bool (** Finds the value associated with the key. This function signs an error when the key is not a key in the map. *) val find : ast_map -> ast -> ast (** Stores or replaces a new key/value pair in the map. *) val insert : ast_map -> ast -> ast -> unit (** Erases the key from the map.*) val erase : ast_map -> ast -> unit (** Removes all keys from the map. *) val reset : ast_map -> unit (** The size of the map *) val get_size : ast_map -> int (** The keys stored in the map. *) val get_keys : ast_map -> Expr.expr list (** Retrieves a string representation of the map.*) val to_string : ast_map -> string end (** The AST's hash code. @return A hash code *) val hash : ast -> int (** A unique identifier for the AST (unique among all ASTs). *) val get_id : ast -> int (** The kind of the AST. *) val get_ast_kind : ast -> Z3enums.ast_kind (** Indicates whether the AST is an Expr *) val is_expr : ast -> bool (** Indicates whether the AST is a bound variable*) val is_var : ast -> bool (** Indicates whether the AST is a Quantifier *) val is_quantifier : ast -> bool (** Indicates whether the AST is a Sort *) val is_sort : ast -> bool (** Indicates whether the AST is a func_decl *) val is_func_decl : ast -> bool (** A string representation of the AST. *) val to_string : ast -> string (** A string representation of the AST in s-expression notation. *) val to_sexpr : ast -> string (** Comparison operator. @return True if the two ast's are from the same context and represent the same sort; false otherwise. *) val equal : ast -> ast -> bool (** Object Comparison. @return Negative if the first ast should be sorted before the second, positive if after else zero. *) val compare : ast -> ast -> int (** Translates (copies) the AST to another context. @return A copy of the AST which is associated with the other context. *) val translate : ast -> context -> ast (** Unwraps an AST. This function is used for transitions between native and managed objects. It returns the native pointer to the AST. Note that AST objects are reference counted and unwrapping an AST disables automatic reference counting, i.e., all references to the IntPtr that is returned must be handled externally and through native calls (see e.g., [Z3native.inc_ref]). {!wrap_ast} *) val unwrap_ast : ast -> Z3native.ptr (** Wraps an AST. This function is used for transitions between native and managed objects. Note that the native ast that is passed must be a native object obtained from Z3 (e.g., through {!unwrap_ast}) and that it must have a correct reference count (see e.g., [Z3native.inc_ref]). *) val wrap_ast : context -> Z3native.z3_ast -> ast end (** The Sort module implements type information for ASTs *) and Sort : sig type sort = Sort of AST.ast val ast_of_sort : sort -> AST.ast (** Comparison operator. @return True if the two sorts are from the same context and represent the same sort; false otherwise. *) val equal : sort -> sort -> bool (** Returns a unique identifier for the sort. *) val get_id : sort -> int (** The kind of the sort. *) val get_sort_kind : sort -> Z3enums.sort_kind (** The name of the sort *) val get_name : sort -> Symbol.symbol (** A string representation of the sort. *) val to_string : sort -> string (** Create a new uninterpreted sort. *) val mk_uninterpreted : context -> Symbol.symbol -> sort (** Create a new uninterpreted sort. *) val mk_uninterpreted_s : context -> string -> sort end (** Function declarations *) and FuncDecl : sig type func_decl = FuncDecl of AST.ast val ast_of_func_decl : FuncDecl.func_decl -> AST.ast (** Parameters of Func_Decls *) module Parameter : sig (** Parameters of func_decls *) type parameter = P_Int of int | P_Dbl of float | P_Sym of Symbol.symbol | P_Srt of Sort.sort | P_Ast of AST.ast | P_Fdl of func_decl | P_Rat of string (** The kind of the parameter. *) val get_kind : parameter -> Z3enums.parameter_kind (** The int value of the parameter.*) val get_int : parameter -> int (** The float value of the parameter.*) val get_float : parameter -> float (** The Symbol.Symbol value of the parameter.*) val get_symbol : parameter -> Symbol.symbol (** The Sort value of the parameter.*) val get_sort : parameter -> Sort.sort (** The AST value of the parameter.*) val get_ast : parameter -> AST.ast (** The FunctionDeclaration value of the parameter.*) val get_func_decl : parameter -> func_decl (** The rational string value of the parameter.*) val get_rational : parameter -> string end (** Creates a new function declaration. *) val mk_func_decl : context -> Symbol.symbol -> Sort.sort list -> Sort.sort -> func_decl (** Creates a new function declaration. *) val mk_func_decl_s : context -> string -> Sort.sort list -> Sort.sort -> func_decl (** Creates a fresh function declaration with a name prefixed with a prefix string. *) val mk_fresh_func_decl : context -> string -> Sort.sort list -> Sort.sort -> func_decl (** Creates a new constant function declaration. *) val mk_const_decl : context -> Symbol.symbol -> Sort.sort -> func_decl (** Creates a new constant function declaration. *) val mk_const_decl_s : context -> string -> Sort.sort -> func_decl (** Creates a fresh constant function declaration with a name prefixed with a prefix string. {!mk_func_decl} {!mk_func_decl} *) val mk_fresh_const_decl : context -> string -> Sort.sort -> func_decl (** Comparison operator. @return True if a and b are from the same context and represent the same func_decl; false otherwise. *) val equal : func_decl -> func_decl -> bool (** A string representations of the function declaration. *) val to_string : func_decl -> string (** Returns a unique identifier for the function declaration. *) val get_id : func_decl -> int (** The arity of the function declaration *) val get_arity : func_decl -> int (** The size of the domain of the function declaration {!get_arity} *) val get_domain_size : func_decl -> int (** The domain of the function declaration *) val get_domain : func_decl -> Sort.sort list (** The range of the function declaration *) val get_range : func_decl -> Sort.sort (** The kind of the function declaration. *) val get_decl_kind : func_decl -> Z3enums.decl_kind (** The name of the function declaration*) val get_name : func_decl -> Symbol.symbol (** The number of parameters of the function declaration *) val get_num_parameters : func_decl -> int (** The parameters of the function declaration *) val get_parameters : func_decl -> Parameter.parameter list (** Create expression that applies function to arguments. *) val apply : func_decl -> Expr.expr list -> Expr.expr end (** Parameter sets (of Solvers, Tactics, ...) A Params objects represents a configuration in the form of Symbol.symbol/value pairs. *) and Params : sig type params (** ParamDescrs describe sets of parameters (of Solvers, Tactics, ...) *) module ParamDescrs : sig type param_descrs (** Validate a set of parameters. *) val validate : param_descrs -> params -> unit (** Retrieve kind of parameter. *) val get_kind : param_descrs -> Symbol.symbol -> Z3enums.param_kind (** Retrieve all names of parameters. *) val get_names : param_descrs -> Symbol.symbol list (** The size of the ParamDescrs. *) val get_size : param_descrs -> int (** Retrieves a string representation of the ParamDescrs. *) val to_string : param_descrs -> string end (** Adds a parameter setting. *) val add_bool : params -> Symbol.symbol -> bool -> unit (** Adds a parameter setting. *) val add_int : params -> Symbol.symbol -> int -> unit (** Adds a parameter setting. *) val add_float : params -> Symbol.symbol -> float -> unit (** Adds a parameter setting. *) val add_symbol : params -> Symbol.symbol -> Symbol.symbol -> unit (** Creates a new parameter set *) val mk_params : context -> params (** A string representation of the parameter set. *) val to_string : params -> string (** Update a mutable configuration parameter. The list of all configuration parameters can be obtained using the Z3 executable: [z3.exe -p] Only a few configuration parameters are mutable once the context is created. An exception is thrown when trying to modify an immutable parameter. *) val update_param_value : context -> string -> string -> unit (** Selects the format used for pretty-printing expressions. The default mode for pretty printing expressions is to produce SMT-LIB style output where common subexpressions are printed at each occurrence. The mode is called PRINT_SMTLIB_FULL. To print shared common subexpressions only once, use the PRINT_LOW_LEVEL mode. To print in way that conforms to SMT-LIB standards and uses let expressions to share common sub-expressions use PRINT_SMTLIB_COMPLIANT. {!AST.to_string} {!Quantifier.Pattern.to_string} {!FuncDecl.to_string} {!Sort.to_string} *) val set_print_mode : context -> Z3enums.ast_print_mode -> unit end (** General Expressions (terms) *) and Expr : sig type expr = Expr of AST.ast val ast_of_expr : Expr.expr -> AST.ast val expr_of_ast : AST.ast -> Expr.expr (** Returns a simplified version of the expression. {!get_simplify_help} *) val simplify : Expr.expr -> Params.params option -> expr (** A string describing all available parameters to [Expr.Simplify]. *) val get_simplify_help : context -> string (** Retrieves parameter descriptions for simplifier. *) val get_simplify_parameter_descrs : context -> Params.ParamDescrs.param_descrs (** The function declaration of the function that is applied in this expression. *) val get_func_decl : Expr.expr -> FuncDecl.func_decl (** The number of arguments of the expression. *) val get_num_args : Expr.expr -> int (** The arguments of the expression. *) val get_args : Expr.expr -> Expr.expr list (** Update the arguments of the expression using an array of expressions. The number of new arguments should coincide with the current number of arguments. *) val update : Expr.expr -> Expr.expr list -> expr (** Substitute every occurrence of [from[i]] in the expression with [to[i]], for [i] smaller than [num_exprs]. The result is the new expression. The arrays [from] and [to] must have size [num_exprs]. For every [i] smaller than [num_exprs], we must have that sort of [from[i]] must be equal to sort of [to[i]]. *) val substitute : Expr.expr -> Expr.expr list -> Expr.expr list -> expr (** Substitute every occurrence of [from] in the expression with [to]. {!substitute} *) val substitute_one : Expr.expr -> Expr.expr -> Expr.expr -> expr (** Substitute the free variables in the expression with the expressions in the expr array For every [i] smaller than [num_exprs], the variable with de-Bruijn index [i] is replaced with term [to[i]]. *) val substitute_vars : Expr.expr -> Expr.expr list -> expr (** Translates (copies) the term to another context. @return A copy of the term which is associated with the other context *) val translate : Expr.expr -> context -> expr (** Returns a string representation of the expression. *) val to_string : Expr.expr -> string (** Indicates whether the term is a numeral *) val is_numeral : Expr.expr -> bool (** Indicates whether the term is well-sorted. @return True if the term is well-sorted, false otherwise. *) val is_well_sorted : Expr.expr -> bool (** The Sort of the term. *) val get_sort : Expr.expr -> Sort.sort (** Indicates whether the term represents a constant. *) val is_const : Expr.expr -> bool (** Creates a new constant. *) val mk_const : context -> Symbol.symbol -> Sort.sort -> expr (** Creates a new constant. *) val mk_const_s : context -> string -> Sort.sort -> expr (** Creates a constant from the func_decl. *) val mk_const_f : context -> FuncDecl.func_decl -> expr (** Creates a fresh constant with a name prefixed with a string. *) val mk_fresh_const : context -> string -> Sort.sort -> expr (** Create a new function application. *) val mk_app : context -> FuncDecl.func_decl -> Expr.expr list -> expr (** Create a numeral of a given sort. @return A Term with the given value and sort *) val mk_numeral_string : context -> string -> Sort.sort -> expr (** Create a numeral of a given sort. This function can be use to create numerals that fit in a machine integer. It is slightly faster than [MakeNumeral] since it is not necessary to parse a string. @return A Term with the given value and sort *) val mk_numeral_int : context -> int -> Sort.sort -> expr (** Comparison operator. @return True if the two expr's are equal; false otherwise. *) val equal : expr -> expr -> bool (** Object Comparison. @return Negative if the first expr should be sorted before the second, positive if after, else zero. *) val compare : expr -> expr -> int end (** Boolean expressions; Propositional logic and equality *) module Boolean : sig (** Create a Boolean sort *) val mk_sort : context -> Sort.sort (** Create a Boolean constant. *) val mk_const : context -> Symbol.symbol -> Expr.expr (** Create a Boolean constant. *) val mk_const_s : context -> string -> Expr.expr (** The true Term. *) val mk_true : context -> Expr.expr (** The false Term. *) val mk_false : context -> Expr.expr (** Creates a Boolean value. *) val mk_val : context -> bool -> Expr.expr (** Mk an expression representing [not(a)]. *) val mk_not : context -> Expr.expr -> Expr.expr (** Create an expression representing an if-then-else: [ite(t1, t2, t3)]. *) val mk_ite : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 iff t2]. *) val mk_iff : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 -> t2]. *) val mk_implies : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 xor t2]. *) val mk_xor : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing the AND of args *) val mk_and : context -> Expr.expr list -> Expr.expr (** Create an expression representing the OR of args *) val mk_or : context -> Expr.expr list -> Expr.expr (** Creates the equality between two expr's. *) val mk_eq : context -> Expr.expr -> Expr.expr -> Expr.expr (** Creates a [distinct] term. *) val mk_distinct : context -> Expr.expr list -> Expr.expr (** Indicates whether the expression is the true or false expression or something else (L_UNDEF). *) val get_bool_value : Expr.expr -> Z3enums.lbool (** Indicates whether the term has Boolean sort. *) val is_bool : Expr.expr -> bool (** Indicates whether the term is the constant true. *) val is_true : Expr.expr -> bool (** Indicates whether the term is the constant false. *) val is_false : Expr.expr -> bool (** Indicates whether the term is an equality predicate. *) val is_eq : Expr.expr -> bool (** Indicates whether the term is an n-ary distinct predicate (every argument is mutually distinct). *) val is_distinct : Expr.expr -> bool (** Indicates whether the term is a ternary if-then-else term *) val is_ite : Expr.expr -> bool (** Indicates whether the term is an n-ary conjunction *) val is_and : Expr.expr -> bool (** Indicates whether the term is an n-ary disjunction *) val is_or : Expr.expr -> bool (** Indicates whether the term is an if-and-only-if (Boolean equivalence, binary) *) val is_iff : Expr.expr -> bool (** Indicates whether the term is an exclusive or *) val is_xor : Expr.expr -> bool (** Indicates whether the term is a negation *) val is_not : Expr.expr -> bool (** Indicates whether the term is an implication *) val is_implies : Expr.expr -> bool end (** Quantifier expressions *) module Quantifier : sig type quantifier = Quantifier of Expr.expr val expr_of_quantifier : quantifier -> Expr.expr val quantifier_of_expr : Expr.expr -> quantifier (** Quantifier patterns Patterns comprise a list of terms. The list should be non-empty. If the list comprises of more than one term, it is also called a multi-pattern. *) module Pattern : sig type pattern = Pattern of AST.ast val ast_of_pattern : pattern -> AST.ast val pattern_of_ast : AST.ast -> pattern (** The number of terms in the pattern. *) val get_num_terms : pattern -> int (** The terms in the pattern. *) val get_terms : pattern -> Expr.expr list (** A string representation of the pattern. *) val to_string : pattern -> string end (** The de-Burijn index of a bound variable. Bound variables are indexed by de-Bruijn indices. It is perhaps easiest to explain the meaning of de-Bruijn indices by indicating the compilation process from non-de-Bruijn formulas to de-Bruijn format. abs(forall (x1) phi) = forall (x1) abs1(phi, x1, 0) abs(forall (x1, x2) phi) = abs(forall (x1) abs(forall (x2) phi)) abs1(x, x, n) = b_n abs1(y, x, n) = y abs1(f(t1,...,tn), x, n) = f(abs1(t1,x,n), ..., abs1(tn,x,n)) abs1(forall (x1) phi, x, n) = forall (x1) (abs1(phi, x, n+1)) The last line is significant: the index of a bound variable is different depending on the scope in which it appears. The deeper ( x : expr ) appears, the higher is its index. *) val get_index : Expr.expr -> int (** Indicates whether the quantifier is universal. *) val is_universal : quantifier -> bool (** Indicates whether the quantifier is existential. *) val is_existential : quantifier -> bool (** The weight of the quantifier. *) val get_weight : quantifier -> int (** The number of patterns. *) val get_num_patterns : quantifier -> int (** The patterns. *) val get_patterns : quantifier -> Pattern.pattern list (** The number of no-patterns. *) val get_num_no_patterns : quantifier -> int (** The no-patterns. *) val get_no_patterns : quantifier -> Pattern.pattern list (** The number of bound variables. *) val get_num_bound : quantifier -> int (** The symbols for the bound variables. *) val get_bound_variable_names : quantifier -> Symbol.symbol list (** The sorts of the bound variables. *) val get_bound_variable_sorts : quantifier -> Sort.sort list (** The body of the quantifier. *) val get_body : quantifier -> Expr.expr (** Creates a new bound variable. *) val mk_bound : context -> int -> Sort.sort -> Expr.expr (** Create a quantifier pattern. *) val mk_pattern : context -> Expr.expr list -> Pattern.pattern (** Create a universal Quantifier. *) val mk_forall : context -> Sort.sort list -> Symbol.symbol list -> Expr.expr -> int option -> Pattern.pattern list -> Expr.expr list -> Symbol.symbol option -> Symbol.symbol option -> quantifier (** Create a universal Quantifier. *) val mk_forall_const : context -> Expr.expr list -> Expr.expr -> int option -> Pattern.pattern list -> Expr.expr list -> Symbol.symbol option -> Symbol.symbol option -> quantifier (** Create an existential Quantifier. *) val mk_exists : context -> Sort.sort list -> Symbol.symbol list -> Expr.expr -> int option -> Pattern.pattern list -> Expr.expr list -> Symbol.symbol option -> Symbol.symbol option -> quantifier (** Create an existential Quantifier. *) val mk_exists_const : context -> Expr.expr list -> Expr.expr -> int option -> Pattern.pattern list -> Expr.expr list -> Symbol.symbol option -> Symbol.symbol option -> quantifier (** Create a Quantifier. *) val mk_quantifier : context -> Sort.sort list -> Symbol.symbol list -> Expr.expr -> int option -> Pattern.pattern list -> Expr.expr list -> Symbol.symbol option -> Symbol.symbol option -> quantifier (** Create a Quantifier. *) val mk_quantifier : context -> bool -> Expr.expr list -> Expr.expr -> int option -> Pattern.pattern list -> Expr.expr list -> Symbol.symbol option -> Symbol.symbol option -> quantifier (** A string representation of the quantifier. *) val to_string : quantifier -> string end (** Functions to manipulate Array expressions *) module Z3Array : sig (** Create a new array sort. *) val mk_sort : context -> Sort.sort -> Sort.sort -> Sort.sort (** Indicates whether the term is an array store. It satisfies select(store(a,i,v),j) = if i = j then v else select(a,j). Array store takes at least 3 arguments. *) val is_store : Expr.expr -> bool (** Indicates whether the term is an array select. *) val is_select : Expr.expr -> bool (** Indicates whether the term is a constant array. For example, select(const(v),i) = v holds for every v and i. The function is unary. *) val is_constant_array : Expr.expr -> bool (** Indicates whether the term is a default array. For example default(const(v)) = v. The function is unary. *) val is_default_array : Expr.expr -> bool (** Indicates whether the term is an array map. It satisfies map[f](a1,..,a_n)[i] = f(a1[i],...,a_n[i]) for every i. *) val is_array_map : Expr.expr -> bool (** Indicates whether the term is an as-array term. An as-array term is n array value that behaves as the function graph of the function passed as parameter. *) val is_as_array : Expr.expr -> bool (** Indicates whether the term is of an array sort. *) val is_array : Expr.expr -> bool (** The domain of the array sort. *) val get_domain : Sort.sort -> Sort.sort (** The range of the array sort. *) val get_range : Sort.sort -> Sort.sort (** Create an array constant. *) val mk_const : context -> Symbol.symbol -> Sort.sort -> Sort.sort -> Expr.expr (** Create an array constant. *) val mk_const_s : context -> string -> Sort.sort -> Sort.sort -> Expr.expr (** Array read. The argument [a] is the array and [i] is the index of the array that gets read. The node [a] must have an array sort [[domain -> range]], and [i] must have the sort [domain]. The sort of the result is [range]. {!Z3Array.mk_sort} {!mk_store} *) val mk_select : context -> Expr.expr -> Expr.expr -> Expr.expr (** Array update. The node [a] must have an array sort [[domain -> range]], [i] must have sort [domain], [v] must have sort range. The sort of the result is [[domain -> range]]. The semantics of this function is given by the theory of arrays described in the SMT-LIB standard. See http://smtlib.org for more details. The result of this function is an array that is equal to [a] (with respect to [select]) on all indices except for [i], where it maps to [v] (and the [select] of [a] with respect to [i] may be a different value). {!Z3Array.mk_sort} {!mk_select} *) val mk_store : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** Create a constant array. The resulting term is an array, such that a [select]on an arbitrary index produces the value [v]. {!Z3Array.mk_sort} {!mk_select} *) val mk_const_array : context -> Sort.sort -> Expr.expr -> Expr.expr (** Maps f on the argument arrays. Eeach element of [args] must be of an array sort [[domain_i -> range_i]]. The function declaration [f] must have type [ range_1 .. range_n -> range]. [v] must have sort range. The sort of the result is [[domain_i -> range]]. {!Z3Array.mk_sort} {!mk_select} {!mk_store} *) val mk_map : context -> FuncDecl.func_decl -> Expr.expr list -> Expr.expr (** Access the array default value. Produces the default range value, for arrays that can be represented as finite maps with a default range value. *) val mk_term_array : context -> Expr.expr -> Expr.expr end (** Functions to manipulate Set expressions *) module Set : sig (** Create a set type. *) val mk_sort : context -> Sort.sort -> Sort.sort (** Indicates whether the term is set union *) val is_union : Expr.expr -> bool (** Indicates whether the term is set intersection *) val is_intersect : Expr.expr -> bool (** Indicates whether the term is set difference *) val is_difference : Expr.expr -> bool (** Indicates whether the term is set complement *) val is_complement : Expr.expr -> bool (** Indicates whether the term is set subset *) val is_subset : Expr.expr -> bool (** Create an empty set. *) val mk_empty : context -> Sort.sort -> Expr.expr (** Create the full set. *) val mk_full : context -> Sort.sort -> Expr.expr (** Add an element to the set. *) val mk_set_add : context -> Expr.expr -> Expr.expr -> Expr.expr (** Remove an element from a set. *) val mk_del : context -> Expr.expr -> Expr.expr -> Expr.expr (** Take the union of a list of sets. *) val mk_union : context -> Expr.expr list -> Expr.expr (** Take the intersection of a list of sets. *) val mk_intersection : context -> Expr.expr list -> Expr.expr (** Take the difference between two sets. *) val mk_difference : context -> Expr.expr -> Expr.expr -> Expr.expr (** Take the complement of a set. *) val mk_complement : context -> Expr.expr -> Expr.expr (** Check for set membership. *) val mk_membership : context -> Expr.expr -> Expr.expr -> Expr.expr (** Check for subsetness of sets. *) val mk_subset : context -> Expr.expr -> Expr.expr -> Expr.expr end (** Functions to manipulate Finite Domain expressions *) module FiniteDomain : sig (** Create a new finite domain sort. *) val mk_sort : context -> Symbol.symbol -> int -> Sort.sort (** Create a new finite domain sort. *) val mk_sort_s : context -> string -> int -> Sort.sort (** Indicates whether the term is of an array sort. *) val is_finite_domain : Expr.expr -> bool (** Indicates whether the term is a less than predicate over a finite domain. *) val is_lt : Expr.expr -> bool (** The size of the finite domain sort. *) val get_size : Sort.sort -> int end (** Functions to manipulate Relation expressions *) module Relation : sig (** Indicates whether the term is of a relation sort. *) val is_relation : Expr.expr -> bool (** Indicates whether the term is an relation store Insert a record into a relation. The function takes [n+1] arguments, where the first argument is the relation and the remaining [n] elements correspond to the [n] columns of the relation. *) val is_store : Expr.expr -> bool (** Indicates whether the term is an empty relation *) val is_empty : Expr.expr -> bool (** Indicates whether the term is a test for the emptiness of a relation *) val is_is_empty : Expr.expr -> bool (** Indicates whether the term is a relational join *) val is_join : Expr.expr -> bool (** Indicates whether the term is the union or convex hull of two relations. The function takes two arguments. *) val is_union : Expr.expr -> bool (** Indicates whether the term is the widening of two relations The function takes two arguments. *) val is_widen : Expr.expr -> bool (** Indicates whether the term is a projection of columns (provided as numbers in the parameters). The function takes one argument. *) val is_project : Expr.expr -> bool (** Indicates whether the term is a relation filter Filter (restrict) a relation with respect to a predicate. The first argument is a relation. The second argument is a predicate with free de-Brujin indices corresponding to the columns of the relation. So the first column in the relation has index 0. *) val is_filter : Expr.expr -> bool (** Indicates whether the term is an intersection of a relation with the negation of another. Intersect the first relation with respect to negation of the second relation (the function takes two arguments). Logically, the specification can be described by a function target = filter_by_negation(pos, neg, columns) where columns are pairs c1, d1, .., cN, dN of columns from pos and neg, such that target are elements in ( x : expr ) in pos, such that there is no y in neg that agrees with ( x : expr ) on the columns c1, d1, .., cN, dN. *) val is_negation_filter : Expr.expr -> bool (** Indicates whether the term is the renaming of a column in a relation The function takes one argument. The parameters contain the renaming as a cycle. *) val is_rename : Expr.expr -> bool (** Indicates whether the term is the complement of a relation *) val is_complement : Expr.expr -> bool (** Indicates whether the term is a relational select Check if a record is an element of the relation. The function takes [n+1] arguments, where the first argument is a relation, and the remaining [n] arguments correspond to a record. *) val is_select : Expr.expr -> bool (** Indicates whether the term is a relational clone (copy) Create a fresh copy (clone) of a relation. The function is logically the identity, but in the context of a register machine allows for terms of kind {!is_union} to perform destructive updates to the first argument. *) val is_clone : Expr.expr -> bool (** The arity of the relation sort. *) val get_arity : Sort.sort -> int (** The sorts of the columns of the relation sort. *) val get_column_sorts : Sort.sort -> Sort.sort list end (** Functions to manipulate Datatype expressions *) module Datatype : sig (** Datatype Constructors *) module Constructor : sig type constructor (** The number of fields of the constructor. *) val get_num_fields : constructor -> int (** The function declaration of the constructor. *) val get_constructor_decl : constructor -> FuncDecl.func_decl (** The function declaration of the tester. *) val get_tester_decl : constructor -> FuncDecl.func_decl (** The function declarations of the accessors *) val get_accessor_decls : constructor -> FuncDecl.func_decl list end (** Create a datatype constructor. if the corresponding sort reference is 0, then the value in sort_refs should be an index referring to one of the recursive datatypes that is declared. *) val mk_constructor : context -> Symbol.symbol -> Symbol.symbol -> Symbol.symbol list -> Sort.sort option list -> int list -> Constructor.constructor (** Create a datatype constructor. if the corresponding sort reference is 0, then the value in sort_refs should be an index referring to one of the recursive datatypes that is declared. *) val mk_constructor_s : context -> string -> Symbol.symbol -> Symbol.symbol list -> Sort.sort option list -> int list -> Constructor.constructor (** Create a new datatype sort. *) val mk_sort : context -> Symbol.symbol -> Constructor.constructor list -> Sort.sort (** Create a new datatype sort. *) val mk_sort_s : context -> string -> Constructor.constructor list -> Sort.sort (** Create mutually recursive datatypes. *) val mk_sorts : context -> Symbol.symbol list -> Constructor.constructor list list -> Sort.sort list (** Create mutually recursive data-types. *) val mk_sorts_s : context -> string list -> Constructor.constructor list list -> Sort.sort list (** The number of constructors of the datatype sort. *) val get_num_constructors : Sort.sort -> int (** The constructors. *) val get_constructors : Sort.sort -> FuncDecl.func_decl list (** The recognizers. *) val get_recognizers : Sort.sort -> FuncDecl.func_decl list (** The constructor accessors. *) val get_accessors : Sort.sort -> FuncDecl.func_decl list list end (** Functions to manipulate Enumeration expressions *) module Enumeration : sig (** Create a new enumeration sort. *) val mk_sort : context -> Symbol.symbol -> Symbol.symbol list -> Sort.sort (** Create a new enumeration sort. *) val mk_sort_s : context -> string -> string list -> Sort.sort (** The function declarations of the constants in the enumeration. *) val get_const_decls : Sort.sort -> FuncDecl.func_decl list (** Retrieves the inx'th constant declaration in the enumeration. *) val get_const_decl : Sort.sort -> int -> FuncDecl.func_decl (** The constants in the enumeration. *) val get_consts : Sort.sort -> Expr.expr list (** Retrieves the inx'th constant in the enumeration. *) val get_const : Sort.sort -> int -> Expr.expr (** The test predicates for the constants in the enumeration. *) val get_tester_decls : Sort.sort -> FuncDecl.func_decl list (** Retrieves the inx'th tester/recognizer declaration in the enumeration. *) val get_tester_decl : Sort.sort -> int -> FuncDecl.func_decl end (** Functions to manipulate List expressions *) module Z3List : sig (** Create a new list sort. *) val mk_sort : context -> Symbol.symbol -> Sort.sort -> Sort.sort (** Create a new list sort. *) val mk_list_s : context -> string -> Sort.sort -> Sort.sort (** The declaration of the nil function of this list sort. *) val get_nil_decl : Sort.sort -> FuncDecl.func_decl (** The declaration of the isNil function of this list sort. *) val get_is_nil_decl : Sort.sort -> FuncDecl.func_decl (** The declaration of the cons function of this list sort. *) val get_cons_decl : Sort.sort -> FuncDecl.func_decl (** The declaration of the isCons function of this list sort. *) val get_is_cons_decl : Sort.sort -> FuncDecl.func_decl (** The declaration of the head function of this list sort. *) val get_head_decl : Sort.sort -> FuncDecl.func_decl (** The declaration of the tail function of this list sort. *) val get_tail_decl : Sort.sort -> FuncDecl.func_decl (** The empty list. *) val nil : Sort.sort -> Expr.expr end (** Functions to manipulate Tuple expressions *) module Tuple : sig (** Create a new tuple sort. *) val mk_sort : context -> Symbol.symbol -> Symbol.symbol list -> Sort.sort list -> Sort.sort (** The constructor function of the tuple. *) val get_mk_decl : Sort.sort -> FuncDecl.func_decl (** The number of fields in the tuple. *) val get_num_fields : Sort.sort -> int (** The field declarations. *) val get_field_decls : Sort.sort -> FuncDecl.func_decl list end (** Functions to manipulate arithmetic expressions *) module Arithmetic : sig (** Integer Arithmetic *) module Integer : sig (** Create a new integer sort. *) val mk_sort : context -> Sort.sort (** Retrieve the int value. *) val get_int : Expr.expr -> int (** Get a big_int from an integer numeral *) val get_big_int : Expr.expr -> Big_int.big_int (** Returns a string representation of a numeral. *) val numeral_to_string : Expr.expr -> string (** Creates an integer constant. *) val mk_const : context -> Symbol.symbol -> Expr.expr (** Creates an integer constant. *) val mk_const_s : context -> string -> Expr.expr (** Create an expression representing [t1 mod t2]. The arguments must have int type. *) val mk_mod : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 rem t2]. The arguments must have int type. *) val mk_rem : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an integer numeral. *) val mk_numeral_s : context -> string -> Expr.expr (** Create an integer numeral. @return A Term with the given value and sort Integer *) val mk_numeral_i : context -> int -> Expr.expr (** Coerce an integer to a real. There is also a converse operation exposed. It follows the semantics prescribed by the SMT-LIB standard. You can take the floor of a real by creating an auxiliary integer Term [k] and and asserting [MakeInt2Real(k) <= t1 < MkInt2Real(k)+1]. The argument must be of integer sort. *) val mk_int2real : context -> Expr.expr -> Expr.expr (** Create an n-bit bit-vector from an integer argument. NB. This function is essentially treated as uninterpreted. So you cannot expect Z3 to precisely reflect the semantics of this function when solving constraints with this function. The argument must be of integer sort. *) val mk_int2bv : context -> int -> Expr.expr -> Expr.expr end (** Real Arithmetic *) module Real : sig (** Create a real sort. *) val mk_sort : context -> Sort.sort (** The numerator of a rational numeral. *) val get_numerator : Expr.expr -> Expr.expr (** The denominator of a rational numeral. *) val get_denominator : Expr.expr -> Expr.expr (** Get a ratio from a real numeral *) val get_ratio : Expr.expr -> Ratio.ratio (** Returns a string representation in decimal notation. The result has at most as many decimal places as indicated by the int argument.*) val to_decimal_string : Expr.expr-> int -> string (** Returns a string representation of a numeral. *) val numeral_to_string : Expr.expr-> string (** Creates a real constant. *) val mk_const : context -> Symbol.symbol -> Expr.expr (** Creates a real constant. *) val mk_const_s : context -> string -> Expr.expr (** Create a real numeral from a fraction. @return A Term with rational value and sort Real {!mk_numeral_s} *) val mk_numeral_nd : context -> int -> int -> Expr.expr (** Create a real numeral. @return A Term with the given value and sort Real *) val mk_numeral_s : context -> string -> Expr.expr (** Create a real numeral. @return A Term with the given value and sort Real *) val mk_numeral_i : context -> int -> Expr.expr (** Creates an expression that checks whether a real number is an integer. *) val mk_is_integer : context -> Expr.expr -> Expr.expr (** Coerce a real to an integer. The semantics of this function follows the SMT-LIB standard for the function to_int. The argument must be of real sort. *) val mk_real2int : context -> Expr.expr -> Expr.expr (** Algebraic Numbers *) module AlgebraicNumber : sig (** Return a upper bound for a given real algebraic number. The interval isolating the number is smaller than 1/10^precision. {!is_algebraic_number} @return A numeral Expr of sort Real *) val to_upper : Expr.expr -> int -> Expr.expr (** Return a lower bound for the given real algebraic number. The interval isolating the number is smaller than 1/10^precision. {!is_algebraic_number} @return A numeral Expr of sort Real *) val to_lower : Expr.expr -> int -> Expr.expr (** Returns a string representation in decimal notation. The result has at most as many decimal places as the int argument provided.*) val to_decimal_string : Expr.expr -> int -> string (** Returns a string representation of a numeral. *) val numeral_to_string : Expr.expr -> string end end (** Indicates whether the term is of integer sort. *) val is_int : Expr.expr -> bool (** Indicates whether the term is an arithmetic numeral. *) val is_arithmetic_numeral : Expr.expr -> bool (** Indicates whether the term is a less-than-or-equal *) val is_le : Expr.expr -> bool (** Indicates whether the term is a greater-than-or-equal *) val is_ge : Expr.expr -> bool (** Indicates whether the term is a less-than *) val is_lt : Expr.expr -> bool (** Indicates whether the term is a greater-than *) val is_gt : Expr.expr -> bool (** Indicates whether the term is addition (binary) *) val is_add : Expr.expr -> bool (** Indicates whether the term is subtraction (binary) *) val is_sub : Expr.expr -> bool (** Indicates whether the term is a unary minus *) val is_uminus : Expr.expr -> bool (** Indicates whether the term is multiplication (binary) *) val is_mul : Expr.expr -> bool (** Indicates whether the term is division (binary) *) val is_div : Expr.expr -> bool (** Indicates whether the term is integer division (binary) *) val is_idiv : Expr.expr -> bool (** Indicates whether the term is remainder (binary) *) val is_remainder : Expr.expr -> bool (** Indicates whether the term is modulus (binary) *) val is_modulus : Expr.expr -> bool (** Indicates whether the term is a coercion of integer to real (unary) *) val is_int2real : Expr.expr -> bool (** Indicates whether the term is a coercion of real to integer (unary) *) val is_real2int : Expr.expr -> bool (** Indicates whether the term is a check that tests whether a real is integral (unary) *) val is_real_is_int : Expr.expr -> bool (** Indicates whether the term is of sort real. *) val is_real : Expr.expr -> bool (** Indicates whether the term is an integer numeral. *) val is_int_numeral : Expr.expr -> bool (** Indicates whether the term is a real numeral. *) val is_rat_numeral : Expr.expr -> bool (** Indicates whether the term is an algebraic number *) val is_algebraic_number : Expr.expr -> bool (** Create an expression representing [t[0] + t[1] + ...]. *) val mk_add : context -> Expr.expr list -> Expr.expr (** Create an expression representing [t[0] * t[1] * ...]. *) val mk_mul : context -> Expr.expr list -> Expr.expr (** Create an expression representing [t[0] - t[1] - ...]. *) val mk_sub : context -> Expr.expr list -> Expr.expr (** Create an expression representing [-t]. *) val mk_unary_minus : context -> Expr.expr -> Expr.expr (** Create an expression representing [t1 / t2]. *) val mk_div : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 ^ t2]. *) val mk_power : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 < t2] *) val mk_lt : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 <= t2] *) val mk_le : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 > t2] *) val mk_gt : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 >= t2] *) val mk_ge : context -> Expr.expr -> Expr.expr -> Expr.expr end (** Functions to manipulate bit-vector expressions *) module BitVector : sig (** Create a new bit-vector sort. *) val mk_sort : context -> int -> Sort.sort (** Indicates whether the terms is of bit-vector sort. *) val is_bv : Expr.expr -> bool (** Indicates whether the term is a bit-vector numeral *) val is_bv_numeral : Expr.expr -> bool (** Indicates whether the term is a one-bit bit-vector with value one *) val is_bv_bit1 : Expr.expr -> bool (** Indicates whether the term is a one-bit bit-vector with value zero *) val is_bv_bit0 : Expr.expr -> bool (** Indicates whether the term is a bit-vector unary minus *) val is_bv_uminus : Expr.expr -> bool (** Indicates whether the term is a bit-vector addition (binary) *) val is_bv_add : Expr.expr -> bool (** Indicates whether the term is a bit-vector subtraction (binary) *) val is_bv_sub : Expr.expr -> bool (** Indicates whether the term is a bit-vector multiplication (binary) *) val is_bv_mul : Expr.expr -> bool (** Indicates whether the term is a bit-vector signed division (binary) *) val is_bv_sdiv : Expr.expr -> bool (** Indicates whether the term is a bit-vector unsigned division (binary) *) val is_bv_udiv : Expr.expr -> bool (** Indicates whether the term is a bit-vector signed remainder (binary) *) val is_bv_SRem : Expr.expr -> bool (** Indicates whether the term is a bit-vector unsigned remainder (binary) *) val is_bv_urem : Expr.expr -> bool (** Indicates whether the term is a bit-vector signed modulus *) val is_bv_smod : Expr.expr -> bool (** Indicates whether the term is a bit-vector signed division by zero *) val is_bv_sdiv0 : Expr.expr -> bool (** Indicates whether the term is a bit-vector unsigned division by zero *) val is_bv_udiv0 : Expr.expr -> bool (** Indicates whether the term is a bit-vector signed remainder by zero *) val is_bv_srem0 : Expr.expr -> bool (** Indicates whether the term is a bit-vector unsigned remainder by zero *) val is_bv_urem0 : Expr.expr -> bool (** Indicates whether the term is a bit-vector signed modulus by zero *) val is_bv_smod0 : Expr.expr -> bool (** Indicates whether the term is an unsigned bit-vector less-than-or-equal *) val is_bv_ule : Expr.expr -> bool (** Indicates whether the term is a signed bit-vector less-than-or-equal *) val is_bv_sle : Expr.expr -> bool (** Indicates whether the term is an unsigned bit-vector greater-than-or-equal *) val is_bv_uge : Expr.expr -> bool (** Indicates whether the term is a signed bit-vector greater-than-or-equal *) val is_bv_sge : Expr.expr -> bool (** Indicates whether the term is an unsigned bit-vector less-than *) val is_bv_ult : Expr.expr -> bool (** Indicates whether the term is a signed bit-vector less-than *) val is_bv_slt : Expr.expr -> bool (** Indicates whether the term is an unsigned bit-vector greater-than *) val is_bv_ugt : Expr.expr -> bool (** Indicates whether the term is a signed bit-vector greater-than *) val is_bv_sgt : Expr.expr -> bool (** Indicates whether the term is a bit-wise AND *) val is_bv_and : Expr.expr -> bool (** Indicates whether the term is a bit-wise OR *) val is_bv_or : Expr.expr -> bool (** Indicates whether the term is a bit-wise NOT *) val is_bv_not : Expr.expr -> bool (** Indicates whether the term is a bit-wise XOR *) val is_bv_xor : Expr.expr -> bool (** Indicates whether the term is a bit-wise NAND *) val is_bv_nand : Expr.expr -> bool (** Indicates whether the term is a bit-wise NOR *) val is_bv_nor : Expr.expr -> bool (** Indicates whether the term is a bit-wise XNOR *) val is_bv_xnor : Expr.expr -> bool (** Indicates whether the term is a bit-vector concatenation (binary) *) val is_bv_concat : Expr.expr -> bool (** Indicates whether the term is a bit-vector sign extension *) val is_bv_signextension : Expr.expr -> bool (** Indicates whether the term is a bit-vector zero extension *) val is_bv_zeroextension : Expr.expr -> bool (** Indicates whether the term is a bit-vector extraction *) val is_bv_extract : Expr.expr -> bool (** Indicates whether the term is a bit-vector repetition *) val is_bv_repeat : Expr.expr -> bool (** Indicates whether the term is a bit-vector reduce OR *) val is_bv_reduceor : Expr.expr -> bool (** Indicates whether the term is a bit-vector reduce AND *) val is_bv_reduceand : Expr.expr -> bool (** Indicates whether the term is a bit-vector comparison *) val is_bv_comp : Expr.expr -> bool (** Indicates whether the term is a bit-vector shift left *) val is_bv_shiftleft : Expr.expr -> bool (** Indicates whether the term is a bit-vector logical shift right *) val is_bv_shiftrightlogical : Expr.expr -> bool (** Indicates whether the term is a bit-vector arithmetic shift left *) val is_bv_shiftrightarithmetic : Expr.expr -> bool (** Indicates whether the term is a bit-vector rotate left *) val is_bv_rotateleft : Expr.expr -> bool (** Indicates whether the term is a bit-vector rotate right *) val is_bv_rotateright : Expr.expr -> bool (** Indicates whether the term is a bit-vector rotate left (extended) Similar to Z3_OP_ROTATE_LEFT, but it is a binary operator instead of a parametric one. *) val is_bv_rotateleftextended : Expr.expr -> bool (** Indicates whether the term is a bit-vector rotate right (extended) Similar to Z3_OP_ROTATE_RIGHT, but it is a binary operator instead of a parametric one. *) val is_bv_rotaterightextended : Expr.expr -> bool (** Indicates whether the term is a coercion from bit-vector to integer This function is not supported by the decision procedures. Only the most rudimentary simplification rules are applied to this function. *) val is_int2bv : Expr.expr -> bool (** Indicates whether the term is a coercion from integer to bit-vector This function is not supported by the decision procedures. Only the most rudimentary simplification rules are applied to this function. *) val is_bv2int : Expr.expr -> bool (** Indicates whether the term is a bit-vector carry Compute the carry bit in a full-adder. The meaning is given by the equivalence (carry l1 l2 l3) <=> (or (and l1 l2) (and l1 l3) (and l2 l3))) *) val is_bv_carry : Expr.expr -> bool (** Indicates whether the term is a bit-vector ternary XOR The meaning is given by the equivalence (xor3 l1 l2 l3) <=> (xor (xor l1 l2) l3) *) val is_bv_xor3 : Expr.expr -> bool (** The size of a bit-vector sort. *) val get_size : Sort.sort -> int (** Retrieve the int value. *) val get_int : Expr.expr -> int (** Returns a string representation of a numeral. *) val numeral_to_string : Expr.expr -> string (** Creates a bit-vector constant. *) val mk_const : context -> Symbol.symbol -> int -> Expr.expr (** Creates a bit-vector constant. *) val mk_const_s : context -> string -> int -> Expr.expr (** Bitwise negation. The argument must have a bit-vector sort. *) val mk_not : context -> Expr.expr -> Expr.expr (** Take conjunction of bits in a vector,vector of length 1. The argument must have a bit-vector sort. *) val mk_redand : context -> Expr.expr -> Expr.expr (** Take disjunction of bits in a vector,vector of length 1. The argument must have a bit-vector sort. *) val mk_redor : context -> Expr.expr -> Expr.expr (** Bitwise conjunction. The arguments must have a bit-vector sort. *) val mk_and : context -> Expr.expr -> Expr.expr -> Expr.expr (** Bitwise disjunction. The arguments must have a bit-vector sort. *) val mk_or : context -> Expr.expr -> Expr.expr -> Expr.expr (** Bitwise XOR. The arguments must have a bit-vector sort. *) val mk_xor : context -> Expr.expr -> Expr.expr -> Expr.expr (** Bitwise NAND. The arguments must have a bit-vector sort. *) val mk_nand : context -> Expr.expr -> Expr.expr -> Expr.expr (** Bitwise NOR. The arguments must have a bit-vector sort. *) val mk_nor : context -> Expr.expr -> Expr.expr -> Expr.expr (** Bitwise XNOR. The arguments must have a bit-vector sort. *) val mk_xnor : context -> Expr.expr -> Expr.expr -> Expr.expr (** Standard two's complement unary minus. The arguments must have a bit-vector sort. *) val mk_neg : context -> Expr.expr -> Expr.expr (** Two's complement addition. The arguments must have the same bit-vector sort. *) val mk_add : context -> Expr.expr -> Expr.expr -> Expr.expr (** Two's complement subtraction. The arguments must have the same bit-vector sort. *) val mk_sub : context -> Expr.expr -> Expr.expr -> Expr.expr (** Two's complement multiplication. The arguments must have the same bit-vector sort. *) val mk_mul : context -> Expr.expr -> Expr.expr -> Expr.expr (** Unsigned division. It is defined as the floor of [t1/t2] if \c t2 is different from zero. If [t2] is zero, then the result is undefined. The arguments must have the same bit-vector sort. *) val mk_udiv : context -> Expr.expr -> Expr.expr -> Expr.expr (** Signed division. It is defined in the following way: - The \c floor of [t1/t2] if \c t2 is different from zero, and [t1*t2 >= 0]. - The \c ceiling of [t1/t2] if \c t2 is different from zero, and [t1*t2 < 0]. If [t2] is zero, then the result is undefined. The arguments must have the same bit-vector sort. *) val mk_sdiv : context -> Expr.expr -> Expr.expr -> Expr.expr (** Unsigned remainder. It is defined as [t1 - (t1 /u t2) * t2], where [/u] represents unsigned division. If [t2] is zero, then the result is undefined. The arguments must have the same bit-vector sort. *) val mk_urem : context -> Expr.expr -> Expr.expr -> Expr.expr (** Signed remainder. It is defined as [t1 - (t1 /s t2) * t2], where [/s] represents signed division. The most significant bit (sign) of the result is equal to the most significant bit of \c t1. If [t2] is zero, then the result is undefined. The arguments must have the same bit-vector sort. *) val mk_srem : context -> Expr.expr -> Expr.expr -> Expr.expr (** Two's complement signed remainder (sign follows divisor). If [t2] is zero, then the result is undefined. The arguments must have the same bit-vector sort. *) val mk_smod : context -> Expr.expr -> Expr.expr -> Expr.expr (** Unsigned less-than The arguments must have the same bit-vector sort. *) val mk_ult : context -> Expr.expr -> Expr.expr -> Expr.expr (** Two's complement signed less-than The arguments must have the same bit-vector sort. *) val mk_slt : context -> Expr.expr -> Expr.expr -> Expr.expr (** Unsigned less-than or equal to. The arguments must have the same bit-vector sort. *) val mk_ule : context -> Expr.expr -> Expr.expr -> Expr.expr (** Two's complement signed less-than or equal to. The arguments must have the same bit-vector sort. *) val mk_sle : context -> Expr.expr -> Expr.expr -> Expr.expr (** Unsigned greater than or equal to. The arguments must have the same bit-vector sort. *) val mk_uge : context -> Expr.expr -> Expr.expr -> Expr.expr (** Two's complement signed greater than or equal to. The arguments must have the same bit-vector sort. *) val mk_sge : context -> Expr.expr -> Expr.expr -> Expr.expr (** Unsigned greater-than. The arguments must have the same bit-vector sort. *) val mk_ugt : context -> Expr.expr -> Expr.expr -> Expr.expr (** Two's complement signed greater-than. The arguments must have the same bit-vector sort. *) val mk_sgt : context -> Expr.expr -> Expr.expr -> Expr.expr (** Bit-vector concatenation. The arguments must have a bit-vector sort. @return The result is a bit-vector of size [n1+n2], where [n1] ([n2]) is the size of [t1] ([t2]). *) val mk_concat : context -> Expr.expr -> Expr.expr -> Expr.expr (** Bit-vector extraction. Extract the bits between two limits from a bitvector of size [m] to yield a new bitvector of size [n], where [n = high - low + 1]. *) val mk_extract : context -> int -> int -> Expr.expr -> Expr.expr (** Bit-vector sign extension. Sign-extends the given bit-vector to the (signed) equivalent bitvector of size [m+i], where \c m is the size of the given bit-vector. *) val mk_sign_ext : context -> int -> Expr.expr -> Expr.expr (** Bit-vector zero extension. Extend the given bit-vector with zeros to the (unsigned) equivalent bitvector of size [m+i], where \c m is the size of the given bit-vector. *) val mk_zero_ext : context -> int -> Expr.expr -> Expr.expr (** Bit-vector repetition. *) val mk_repeat : context -> int -> Expr.expr -> Expr.expr (** Shift left. It is equivalent to multiplication by [2^x] where \c x is the value of third argument. NB. The semantics of shift operations varies between environments. This definition does not necessarily capture directly the semantics of the programming language or assembly architecture you are modeling.*) val mk_shl : context -> Expr.expr -> Expr.expr -> Expr.expr (** Logical shift right It is equivalent to unsigned division by [2^x] where \c x is the value of the third argument. NB. The semantics of shift operations varies between environments. This definition does not necessarily capture directly the semantics of the programming language or assembly architecture you are modeling. The arguments must have a bit-vector sort. *) val mk_lshr : context -> Expr.expr -> Expr.expr -> Expr.expr (** Arithmetic shift right It is like logical shift right except that the most significant bits of the result always copy the most significant bit of the second argument. NB. The semantics of shift operations varies between environments. This definition does not necessarily capture directly the semantics of the programming language or assembly architecture you are modeling. The arguments must have a bit-vector sort. *) val mk_ashr : context -> Expr.expr -> Expr.expr -> Expr.expr (** Rotate Left. Rotate bits of \c t to the left \c i times. *) val mk_rotate_left : context -> int -> Expr.expr -> Expr.expr (** Rotate Right. Rotate bits of \c t to the right \c i times.*) val mk_rotate_right : context -> int -> Expr.expr -> Expr.expr (** Rotate Left. Rotate bits of the second argument to the left.*) val mk_ext_rotate_left : context -> Expr.expr -> Expr.expr -> Expr.expr (** Rotate Right. Rotate bits of the second argument to the right. *) val mk_ext_rotate_right : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an integer from the bit-vector argument If \c is_signed is false, then the bit-vector \c t1 is treated as unsigned. So the result is non-negative and in the range [[0..2^N-1]], where N are the number of bits in the argument. If \c is_signed is true, \c t1 is treated as a signed bit-vector. NB. This function is essentially treated as uninterpreted. So you cannot expect Z3 to precisely reflect the semantics of this function when solving constraints with this function.*) val mk_bv2int : context -> Expr.expr -> bool -> Expr.expr (** Create a predicate that checks that the bit-wise addition does not overflow. The arguments must be of bit-vector sort. *) val mk_add_no_overflow : context -> Expr.expr -> Expr.expr -> bool -> Expr.expr (** Create a predicate that checks that the bit-wise addition does not underflow. The arguments must be of bit-vector sort. *) val mk_add_no_underflow : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create a predicate that checks that the bit-wise subtraction does not overflow. The arguments must be of bit-vector sort. *) val mk_sub_no_overflow : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create a predicate that checks that the bit-wise subtraction does not underflow. The arguments must be of bit-vector sort. *) val mk_sub_no_underflow : context -> Expr.expr -> Expr.expr -> bool -> Expr.expr (** Create a predicate that checks that the bit-wise signed division does not overflow. The arguments must be of bit-vector sort. *) val mk_sdiv_no_overflow : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create a predicate that checks that the bit-wise negation does not overflow. The arguments must be of bit-vector sort. *) val mk_neg_no_overflow : context -> Expr.expr -> Expr.expr (** Create a predicate that checks that the bit-wise multiplication does not overflow. The arguments must be of bit-vector sort. *) val mk_mul_no_overflow : context -> Expr.expr -> Expr.expr -> bool -> Expr.expr (** Create a predicate that checks that the bit-wise multiplication does not underflow. The arguments must be of bit-vector sort. *) val mk_mul_no_underflow : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create a bit-vector numeral. *) val mk_numeral : context -> string -> int -> Expr.expr end (** Floating-Point Arithmetic *) module FloatingPoint : sig (** Rounding Modes *) module RoundingMode : sig (** Create the RoundingMode sort. *) val mk_sort : context -> Sort.sort (** Indicates whether the terms is of floating-point rounding mode sort. *) val is_fprm : Expr.expr -> bool (** Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. *) val mk_round_nearest_ties_to_even : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. *) val mk_rne : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. *) val mk_round_nearest_ties_to_away : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. *) val mk_rna : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the TowardPositive rounding mode. *) val mk_round_toward_positive : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the TowardPositive rounding mode. *) val mk_rtp : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the TowardNegative rounding mode. *) val mk_round_toward_negative : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the TowardNegative rounding mode. *) val mk_rtn : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the TowardZero rounding mode. *) val mk_round_toward_zero : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the TowardZero rounding mode. *) val mk_rtz : context -> Expr.expr end (** Create a FloatingPoint sort. *) val mk_sort : context -> int -> int -> Sort.sort (** Create the half-precision (16-bit) FloatingPoint sort.*) val mk_sort_half : context -> Sort.sort (** Create the half-precision (16-bit) FloatingPoint sort. *) val mk_sort_16 : context -> Sort.sort (** Create the single-precision (32-bit) FloatingPoint sort.*) val mk_sort_single : context -> Sort.sort (** Create the single-precision (32-bit) FloatingPoint sort. *) val mk_sort_32 : context -> Sort.sort (** Create the double-precision (64-bit) FloatingPoint sort. *) val mk_sort_double : context -> Sort.sort (** Create the double-precision (64-bit) FloatingPoint sort. *) val mk_sort_64 : context -> Sort.sort (** Create the quadruple-precision (128-bit) FloatingPoint sort. *) val mk_sort_quadruple : context -> Sort.sort (** Create the quadruple-precision (128-bit) FloatingPoint sort. *) val mk_sort_128 : context -> Sort.sort (** Create a floating-point NaN of a given FloatingPoint sort. *) val mk_nan : context -> Sort.sort -> Expr.expr (** Create a floating-point infinity of a given FloatingPoint sort. *) val mk_inf : context -> Sort.sort -> bool -> Expr.expr (** Create a floating-point zero of a given FloatingPoint sort. *) val mk_zero : context -> Sort.sort -> bool -> Expr.expr (** Create an expression of FloatingPoint sort from three bit-vector expressions. This is the operator named `fp' in the SMT FP theory definition. Note that \c sign is required to be a bit-vector of size 1. Significand and exponent are required to be greater than 1 and 2 respectively. The FloatingPoint sort of the resulting expression is automatically determined from the bit-vector sizes of the arguments. *) val mk_fp : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** Create a numeral of FloatingPoint sort from a float. This function is used to create numerals that fit in a float value. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. *) val mk_numeral_f : context -> float -> Sort.sort -> Expr.expr (** Create a numeral of FloatingPoint sort from a signed integer. *) val mk_numeral_i : context -> int -> Sort.sort -> Expr.expr (** Create a numeral of FloatingPoint sort from a sign bit and two integers. *) val mk_numeral_i_u : context -> bool -> int -> int -> Sort.sort -> Expr.expr (** Create a numeral of FloatingPoint sort from a string *) val mk_numeral_s : context -> string -> Sort.sort -> Expr.expr (** Indicates whether the terms is of floating-point sort. *) val is_fp : Expr.expr -> bool (** Indicates whether an expression is a floating-point abs expression *) val is_abs : Expr.expr -> bool (** Indicates whether an expression is a floating-point neg expression *) val is_neg : Expr.expr -> bool (** Indicates whether an expression is a floating-point add expression *) val is_add : Expr.expr -> bool (** Indicates whether an expression is a floating-point sub expression *) val is_sub : Expr.expr -> bool (** Indicates whether an expression is a floating-point mul expression *) val is_mul : Expr.expr -> bool (** Indicates whether an expression is a floating-point div expression *) val is_div : Expr.expr -> bool (** Indicates whether an expression is a floating-point fma expression *) val is_fma : Expr.expr -> bool (** Indicates whether an expression is a floating-point sqrt expression *) val is_sqrt : Expr.expr -> bool (** Indicates whether an expression is a floating-point rem expression *) val is_rem : Expr.expr -> bool (** Indicates whether an expression is a floating-point round_to_integral expression *) val is_round_to_integral : Expr.expr -> bool (** Indicates whether an expression is a floating-point min expression *) val is_min : Expr.expr -> bool (** Indicates whether an expression is a floating-point max expression *) val is_max : Expr.expr -> bool (** Indicates whether an expression is a floating-point leq expression *) val is_leq : Expr.expr -> bool (** Indicates whether an expression is a floating-point lt expression *) val is_lt : Expr.expr -> bool (** Indicates whether an expression is a floating-point geqexpression *) val is_geq : Expr.expr -> bool (** Indicates whether an expression is a floating-point gt expression *) val is_gt : Expr.expr -> bool (** Indicates whether an expression is a floating-point eq expression *) val is_eq : Expr.expr -> bool (** Indicates whether an expression is a floating-point is_normal expression *) val is_is_normal : Expr.expr -> bool (** Indicates whether an expression is a floating-point is_subnormal expression *) val is_is_subnormal : Expr.expr -> bool (** Indicates whether an expression is a floating-point is_zero expression *) val is_is_zero : Expr.expr -> bool (** Indicates whether an expression is a floating-point is_infinite expression *) val is_is_infinite : Expr.expr -> bool (** Indicates whether an expression is a floating-point is_nan expression *) val is_is_nan : Expr.expr -> bool (** Indicates whether an expression is a floating-point is_negative expression *) val is_is_negative : Expr.expr -> bool (** Indicates whether an expression is a floating-point is_positive expression *) val is_is_positive : Expr.expr -> bool (** Indicates whether an expression is a floating-point to_fp expression *) val is_to_fp : Expr.expr -> bool (** Indicates whether an expression is a floating-point to_fp_unsigned expression *) val is_to_fp_unsigned : Expr.expr -> bool (** Indicates whether an expression is a floating-point to_ubv expression *) val is_to_ubv : Expr.expr -> bool (** Indicates whether an expression is a floating-point to_sbv expression *) val is_to_sbv : Expr.expr -> bool (** Indicates whether an expression is a floating-point to_real expression *) val is_to_real : Expr.expr -> bool (** Indicates whether an expression is a floating-point to_ieee_bv expression *) val is_to_ieee_bv : Expr.expr -> bool (** Returns a string representation of a numeral. *) val numeral_to_string : Expr.expr -> string (** Creates a floating-point constant. *) val mk_const : context -> Symbol.symbol -> Sort.sort -> Expr.expr (** Creates a floating-point constant. *) val mk_const_s : context -> string -> Sort.sort -> Expr.expr (** Floating-point absolute value *) val mk_abs : context -> Expr.expr -> Expr.expr (** Floating-point negation *) val mk_neg : context -> Expr.expr -> Expr.expr (** Floating-point addition *) val mk_add : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point subtraction *) val mk_sub : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point multiplication *) val mk_mul : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point division *) val mk_div : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point fused multiply-add. *) val mk_fma : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point square root *) val mk_sqrt : context -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point remainder *) val mk_rem : context -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point roundToIntegral. Rounds a floating-point number to the closest integer, again represented as a floating-point number. *) val mk_round_to_integral : context -> Expr.expr -> Expr.expr -> Expr.expr (** Minimum of floating-point numbers. *) val mk_min : context -> Expr.expr -> Expr.expr -> Expr.expr (** Maximum of floating-point numbers. *) val mk_max : context -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point less than or equal. *) val mk_leq : context -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point less than. *) val mk_lt : context -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point greater than or equal. *) val mk_geq : context -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point greater than. *) val mk_gt : context -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point equality. *) val mk_eq : context -> Expr.expr -> Expr.expr -> Expr.expr (** Predicate indicating whether t is a normal floating-point number. *) val mk_is_normal : context -> Expr.expr -> Expr.expr (** Predicate indicating whether t is a subnormal floating-point number. *) val mk_is_subnormal : context -> Expr.expr -> Expr.expr (** Predicate indicating whether t is a floating-point number with zero value, i.e., +zero or -zero. *) val mk_is_zero : context -> Expr.expr -> Expr.expr (** Predicate indicating whether t is a floating-point number representing +oo or -oo. *) val mk_is_infinite : context -> Expr.expr -> Expr.expr (** Predicate indicating whether t is a NaN. *) val mk_is_nan : context -> Expr.expr -> Expr.expr (** Predicate indicating whether t is a negative floating-point number. *) val mk_is_negative : context -> Expr.expr -> Expr.expr (** Predicate indicating whether t is a positive floating-point number. *) val mk_is_positive : context -> Expr.expr -> Expr.expr (** Conversion of a single IEEE 754-2008 bit-vector into a floating-point number. *) val mk_to_fp_bv : context -> Expr.expr -> Sort.sort -> Expr.expr (** Conversion of a FloatingPoint term into another term of different FloatingPoint sort. *) val mk_to_fp_float : context -> Expr.expr -> Expr.expr -> Sort.sort -> Expr.expr (** Conversion of a term of real sort into a term of FloatingPoint sort. *) val mk_to_fp_real : context -> Expr.expr -> Expr.expr -> Sort.sort -> Expr.expr (** Conversion of a 2's complement signed bit-vector term into a term of FloatingPoint sort. *) val mk_to_fp_signed : context -> Expr.expr -> Expr.expr -> Sort.sort -> Expr.expr (** Conversion of a 2's complement unsigned bit-vector term into a term of FloatingPoint sort. *) val mk_to_fp_unsigned : context -> Expr.expr -> Expr.expr -> Sort.sort -> Expr.expr (** C1onversion of a floating-point term into an unsigned bit-vector. *) val mk_to_ubv : context -> Expr.expr -> Expr.expr -> int -> Expr.expr (** Conversion of a floating-point term into a signed bit-vector. *) val mk_to_sbv : context -> Expr.expr -> Expr.expr -> int -> Expr.expr (** Conversion of a floating-point term into a real-numbered term. *) val mk_to_real : context -> Expr.expr -> Expr.expr (** Retrieves the number of bits reserved for the exponent in a FloatingPoint sort. *) val get_ebits : context -> Sort.sort -> int (** Retrieves the number of bits reserved for the significand in a FloatingPoint sort. *) val get_sbits : context -> Sort.sort -> int (** Retrieves the sign of a floating-point literal. *) val get_numeral_sign : context -> Expr.expr -> bool * int (** Return the significand value of a floating-point numeral as a string. *) val get_numeral_significand_string : context -> Expr.expr -> string (** Return the significand value of a floating-point numeral as a uint64. Remark: This function extracts the significand bits, without the hidden bit or normalization. Throws an exception if the significand does not fit into a uint64. *) val get_numeral_significand_uint : context -> Expr.expr -> bool * int (** Return the exponent value of a floating-point numeral as a string *) val get_numeral_exponent_string : context -> Expr.expr -> string (** Return the exponent value of a floating-point numeral as a signed integer *) val get_numeral_exponent_int : context -> Expr.expr -> bool * int (** Conversion of a floating-point term into a bit-vector term in IEEE 754-2008 format. *) val mk_to_ieee_bv : context -> Expr.expr -> Expr.expr (** Conversion of a real-sorted significand and an integer-sorted exponent into a term of FloatingPoint sort. *) val mk_to_fp_int_real : context -> Expr.expr -> Expr.expr -> Expr.expr -> Sort.sort -> Expr.expr (** The string representation of a numeral *) val numeral_to_string : Expr.expr -> string end (** Functions to manipulate proof expressions *) module Proof : sig (** Indicates whether the term is a Proof for the expression 'true'. *) val is_true : Expr.expr -> bool (** Indicates whether the term is a proof for a fact asserted by the user. *) val is_asserted : Expr.expr -> bool (** Indicates whether the term is a proof for a fact (tagged as goal) asserted by the user. *) val is_goal : Expr.expr -> bool (** Indicates whether the term is a binary equivalence modulo namings. This binary predicate is used in proof terms. It captures equisatisfiability and equivalence modulo renamings. *) val is_oeq : Expr.expr -> bool (** Indicates whether the term is proof via modus ponens Given a proof for p and a proof for (implies p q), produces a proof for q. T1: p T2: (implies p q) [mp T1 T2]: q The second antecedents may also be a proof for (iff p q). *) val is_modus_ponens : Expr.expr -> bool (** Indicates whether the term is a proof for (R t t), where R is a reflexive relation. This proof object has no antecedents. The only reflexive relations that are used are equivalence modulo namings, equality and equivalence. That is, R is either '~', '=' or 'iff'. *) val is_reflexivity : Expr.expr -> bool (** Indicates whether the term is proof by symmetricity of a relation Given an symmetric relation R and a proof for (R t s), produces a proof for (R s t). T1: (R t s) [symmetry T1]: (R s t) T1 is the antecedent of this proof object. *) val is_symmetry : Expr.expr -> bool (** Indicates whether the term is a proof by transitivity of a relation Given a transitive relation R, and proofs for (R t s) and (R s u), produces a proof for (R t u). T1: (R t s) T2: (R s u) [trans T1 T2]: (R t u) *) val is_transitivity : Expr.expr -> bool (** Indicates whether the term is a proof by condensed transitivity of a relation Condensed transitivity proof. This proof object is only used if the parameter PROOF_MODE is 1. It combines several symmetry and transitivity proofs. Example: T1: (R a b) T2: (R c b) T3: (R c d) [trans* T1 T2 T3]: (R a d) R must be a symmetric and transitive relation. Assuming that this proof object is a proof for (R s t), then a proof checker must check if it is possible to prove (R s t) using the antecedents, symmetry and transitivity. That is, if there is a path from s to t, if we view every antecedent (R a b) as an edge between a and b. *) val is_Transitivity_star : Expr.expr -> bool (** Indicates whether the term is a monotonicity proof object. T1: (R t_1 s_1) ... Tn: (R t_n s_n) [monotonicity T1 ... Tn]: (R (f t_1 ... t_n) (f s_1 ... s_n)) Remark: if t_i == s_i, then the antecedent Ti is suppressed. That is, reflexivity proofs are supressed to save space. *) val is_monotonicity : Expr.expr -> bool (** Indicates whether the term is a quant-intro proof Given a proof for (~ p q), produces a proof for (~ (forall (x) p) (forall (x) q)). T1: (~ p q) [quant-intro T1]: (~ (forall (x) p) (forall (x) q)) *) val is_quant_intro : Expr.expr -> bool (** Indicates whether the term is a distributivity proof object. Given that f (= or) distributes over g (= and), produces a proof for (= (f a (g c d)) (g (f a c) (f a d))) If f and g are associative, this proof also justifies the following equality: (= (f (g a b) (g c d)) (g (f a c) (f a d) (f b c) (f b d))) where each f and g can have arbitrary number of arguments. This proof object has no antecedents. Remark. This rule is used by the CNF conversion pass and instantiated by f = or, and g = and. *) val is_distributivity : Expr.expr -> bool (** Indicates whether the term is a proof by elimination of AND Given a proof for (and l_1 ... l_n), produces a proof for l_i T1: (and l_1 ... l_n) [and-elim T1]: l_i *) val is_and_elimination : Expr.expr -> bool (** Indicates whether the term is a proof by eliminiation of not-or Given a proof for (not (or l_1 ... l_n)), produces a proof for (not l_i). T1: (not (or l_1 ... l_n)) [not-or-elim T1]: (not l_i) *) val is_or_elimination : Expr.expr -> bool (** Indicates whether the term is a proof by rewriting A proof for a local rewriting step (= t s). The head function symbol of t is interpreted. This proof object has no antecedents. The conclusion of a rewrite rule is either an equality (= t s), an equivalence (iff t s), or equi-satisfiability (~ t s). Remark: if f is bool, then = is iff. Examples: (= (+ ( x : expr ) 0) x) (= (+ ( x : expr ) 1 2) (+ 3 x)) (iff (or ( x : expr ) false) x) *) val is_rewrite : Expr.expr -> bool (** Indicates whether the term is a proof by rewriting A proof for rewriting an expression t into an expression s. This proof object is used if the parameter PROOF_MODE is 1. This proof object can have n antecedents. The antecedents are proofs for equalities used as substitution rules. The object is also used in a few cases if the parameter PROOF_MODE is 2. The cases are: - When applying contextual simplification (CONTEXT_SIMPLIFIER=true) - When converting bit-vectors to Booleans (BIT2BOOL=true) - When pulling ite expression up (PULL_CHEAP_ITE_TREES=true) *) val is_rewrite_star : Expr.expr -> bool (** Indicates whether the term is a proof for pulling quantifiers out. A proof for (iff (f (forall (x) q(x)) r) (forall (x) (f (q x) r))). This proof object has no antecedents. *) val is_pull_quant : Expr.expr -> bool (** Indicates whether the term is a proof for pulling quantifiers out. A proof for (iff P Q) where Q is in prenex normal form. This proof object is only used if the parameter PROOF_MODE is 1. This proof object has no antecedents *) val is_pull_quant_star : Expr.expr -> bool (** Indicates whether the term is a proof for pushing quantifiers in. A proof for: (iff (forall (x_1 ... x_m) (and p_1[x_1 ... x_m] ... p_n[x_1 ... x_m])) (and (forall (x_1 ... x_m) p_1[x_1 ... x_m]) ... (forall (x_1 ... x_m) p_n[x_1 ... x_m]))) This proof object has no antecedents *) val is_push_quant : Expr.expr -> bool (** Indicates whether the term is a proof for elimination of unused variables. A proof for (iff (forall (x_1 ... x_n y_1 ... y_m) p[x_1 ... x_n]) (forall (x_1 ... x_n) p[x_1 ... x_n])) It is used to justify the elimination of unused variables. This proof object has no antecedents. *) val is_elim_unused_vars : Expr.expr -> bool (** Indicates whether the term is a proof for destructive equality resolution A proof for destructive equality resolution: (iff (forall (x) (or (not (= ( x : expr ) t)) P[x])) P[t]) if ( x : expr ) does not occur in t. This proof object has no antecedents. Several variables can be eliminated simultaneously. *) val is_der : Expr.expr -> bool (** Indicates whether the term is a proof for quantifier instantiation A proof of (or (not (forall (x) (P x))) (P a)) *) val is_quant_inst : Expr.expr -> bool (** Indicates whether the term is a hypthesis marker. Mark a hypothesis in a natural deduction style proof. *) val is_hypothesis : Expr.expr -> bool (** Indicates whether the term is a proof by lemma T1: false [lemma T1]: (or (not l_1) ... (not l_n)) This proof object has one antecedent: a hypothetical proof for false. It converts the proof in a proof for (or (not l_1) ... (not l_n)), when T1 contains the hypotheses: l_1, ..., l_n. *) val is_lemma : Expr.expr -> bool (** Indicates whether the term is a proof by unit resolution T1: (or l_1 ... l_n l_1' ... l_m') T2: (not l_1) ... T(n+1): (not l_n) [unit-resolution T1 ... T(n+1)]: (or l_1' ... l_m') *) val is_unit_resolution : Expr.expr -> bool (** Indicates whether the term is a proof by iff-true T1: p [iff-true T1]: (iff p true) *) val is_iff_true : Expr.expr -> bool (** Indicates whether the term is a proof by iff-false T1: (not p) [iff-false T1]: (iff p false) *) val is_iff_false : Expr.expr -> bool (** Indicates whether the term is a proof by commutativity [comm]: (= (f a b) (f b a)) f is a commutative operator. This proof object has no antecedents. Remark: if f is bool, then = is iff. *) val is_commutativity : Expr.expr -> bool (** Indicates whether the term is a proof for Tseitin-like axioms Proof object used to justify Tseitin's like axioms: (or (not (and p q)) p) (or (not (and p q)) q) (or (not (and p q r)) p) (or (not (and p q r)) q) (or (not (and p q r)) r) ... (or (and p q) (not p) (not q)) (or (not (or p q)) p q) (or (or p q) (not p)) (or (or p q) (not q)) (or (not (iff p q)) (not p) q) (or (not (iff p q)) p (not q)) (or (iff p q) (not p) (not q)) (or (iff p q) p q) (or (not (ite a b c)) (not a) b) (or (not (ite a b c)) a c) (or (ite a b c) (not a) (not b)) (or (ite a b c) a (not c)) (or (not (not a)) (not a)) (or (not a) a) This proof object has no antecedents. Note: all axioms are propositional tautologies. Note also that 'and' and 'or' can take multiple arguments. You can recover the propositional tautologies by unfolding the Boolean connectives in the axioms a small bounded number of steps (=3). *) val is_def_axiom : Expr.expr -> bool (** Indicates whether the term is a proof for introduction of a name Introduces a name for a formula/term. Suppose e is an expression with free variables x, and def-intro introduces the name n(x). The possible cases are: When e is of Boolean type: [def-intro]: (and (or n (not e)) (or (not n) e)) or: [def-intro]: (or (not n) e) when e only occurs positively. When e is of the form (ite cond th el): [def-intro]: (and (or (not cond) (= n th)) (or cond (= n el))) Otherwise: [def-intro]: (= n e) *) val is_def_intro : Expr.expr -> bool (** Indicates whether the term is a proof for application of a definition [apply-def T1]: F ~ n F is 'equivalent' to n, given that T1 is a proof that n is a name for F. *) val is_apply_def : Expr.expr -> bool (** Indicates whether the term is a proof iff-oeq T1: (iff p q) [iff~ T1]: (~ p q) *) val is_iff_oeq : Expr.expr -> bool (** Indicates whether the term is a proof for a positive NNF step Proof for a (positive) NNF step. Example: T1: (not s_1) ~ r_1 T2: (not s_2) ~ r_2 T3: s_1 ~ r_1' T4: s_2 ~ r_2' [nnf-pos T1 T2 T3 T4]: (~ (iff s_1 s_2) (and (or r_1 r_2') (or r_1' r_2))) The negation normal form steps NNF_POS and NNF_NEG are used in the following cases: (a) When creating the NNF of a positive force quantifier. The quantifier is retained (unless the bound variables are eliminated). Example T1: q ~ q_new [nnf-pos T1]: (~ (forall (x T) q) (forall (x T) q_new)) (b) When recursively creating NNF over Boolean formulas, where the top-level connective is changed during NNF conversion. The relevant Boolean connectives for NNF_POS are 'implies', 'iff', 'xor', 'ite'. NNF_NEG furthermore handles the case where negation is pushed over Boolean connectives 'and' and 'or'. *) val is_nnf_pos : Expr.expr -> bool (** Indicates whether the term is a proof for a negative NNF step Proof for a (negative) NNF step. Examples: T1: (not s_1) ~ r_1 ... Tn: (not s_n) ~ r_n [nnf-neg T1 ... Tn]: (not (and s_1 ... s_n)) ~ (or r_1 ... r_n) and T1: (not s_1) ~ r_1 ... Tn: (not s_n) ~ r_n [nnf-neg T1 ... Tn]: (not (or s_1 ... s_n)) ~ (and r_1 ... r_n) and T1: (not s_1) ~ r_1 T2: (not s_2) ~ r_2 T3: s_1 ~ r_1' T4: s_2 ~ r_2' [nnf-neg T1 T2 T3 T4]: (~ (not (iff s_1 s_2)) (and (or r_1 r_2) (or r_1' r_2'))) *) val is_nnf_neg : Expr.expr -> bool (** Indicates whether the term is a proof for (~ P Q) here Q is in negation normal form. A proof for (~ P Q) where Q is in negation normal form. This proof object is only used if the parameter PROOF_MODE is 1. This proof object may have n antecedents. Each antecedent is a PR_DEF_INTRO. *) val is_nnf_star : Expr.expr -> bool (** Indicates whether the term is a proof for (~ P Q) where Q is in conjunctive normal form. A proof for (~ P Q) where Q is in conjunctive normal form. This proof object is only used if the parameter PROOF_MODE is 1. This proof object may have n antecedents. Each antecedent is a PR_DEF_INTRO. *) val is_cnf_star : Expr.expr -> bool (** Indicates whether the term is a proof for a Skolemization step Proof for: [sk]: (~ (not (forall ( x : expr ) (p ( x : expr ) y))) (not (p (sk y) y))) [sk]: (~ (exists ( x : expr ) (p ( x : expr ) y)) (p (sk y) y)) This proof object has no antecedents. *) val is_skolemize : Expr.expr -> bool (** Indicates whether the term is a proof by modus ponens for equi-satisfiability. Modus ponens style rule for equi-satisfiability. T1: p T2: (~ p q) [mp~ T1 T2]: q *) val is_modus_ponens_oeq : Expr.expr -> bool (** Indicates whether the term is a proof for theory lemma Generic proof for theory lemmas. The theory lemma function comes with one or more parameters. The first parameter indicates the name of the theory. For the theory of arithmetic, additional parameters provide hints for checking the theory lemma. The hints for arithmetic are: - farkas - followed by rational coefficients. Multiply the coefficients to the inequalities in the lemma, add the (negated) inequalities and obtain a contradiction. - triangle-eq - Indicates a lemma related to the equivalence: (iff (= t1 t2) (and (<= t1 t2) (<= t2 t1))) - gcd-test - Indicates an integer linear arithmetic lemma that uses a gcd test. *) val is_theory_lemma : Expr.expr -> bool end (** Goals A goal (aka problem). A goal is essentially a of formulas, that can be solved and/or transformed using tactics and solvers. *) module Goal : sig type goal (** The precision of the goal. Goals can be transformed using over and under approximations. An under approximation is applied when the objective is to find a model for a given goal. An over approximation is applied when the objective is to find a proof for a given goal. *) val get_precision : goal -> Z3enums.goal_prec (** Indicates whether the goal is precise. *) val is_precise : goal -> bool (** Indicates whether the goal is an under-approximation. *) val is_underapproximation : goal -> bool (** Indicates whether the goal is an over-approximation. *) val is_overapproximation : goal -> bool (** Indicates whether the goal is garbage (i.e., the product of over- and under-approximations). *) val is_garbage : goal -> bool (** Adds the constraints to the given goal. *) val add : goal -> Expr.expr list -> unit (** Indicates whether the goal contains `false'. *) val is_inconsistent : goal -> bool (** The depth of the goal. This tracks how many transformations were applied to it. *) val get_depth : goal -> int (** Erases all formulas from the given goal. *) val reset : goal -> unit (** The number of formulas in the goal. *) val get_size : goal -> int (** The formulas in the goal. *) val get_formulas : goal -> Expr.expr list (** The number of formulas, subformulas and terms in the goal. *) val get_num_exprs : goal -> int (** Indicates whether the goal is empty, and it is precise or the product of an under approximation. *) val is_decided_sat : goal -> bool (** Indicates whether the goal contains `false', and it is precise or the product of an over approximation. *) val is_decided_unsat : goal -> bool (** Translates (copies) the Goal to another context.. *) val translate : goal -> context -> goal (** Simplifies the goal. Essentially invokes the `simplify' tactic on the goal. *) val simplify : goal -> Params.params option -> goal (** Creates a new Goal. Note that the Context must have been created with proof generation support if the fourth argument is set to true here. *) val mk_goal : context -> bool -> bool -> bool -> goal (** A string representation of the Goal. *) val to_string : goal -> string (** Goal to BoolExpr conversion. *) val as_expr : goal -> Expr.expr end (** Models A Model contains interpretations (assignments) of constants and functions. *) module Model : sig type model (** Function interpretations A function interpretation is represented as a finite map and an 'else'. Each entry in the finite map represents the value of a function given a set of arguments. *) module FuncInterp : sig type func_interp (** Function interpretations entries An Entry object represents an element in the finite map used to a function interpretation. *) module FuncEntry : sig type func_entry (** Return the (symbolic) value of this entry. *) val get_value : func_entry -> Expr.expr (** The number of arguments of the entry. *) val get_num_args : func_entry -> int (** The arguments of the function entry. *) val get_args : func_entry -> Expr.expr list (** A string representation of the function entry. *) val to_string : func_entry -> string end (** The number of entries in the function interpretation. *) val get_num_entries : func_interp -> int (** The entries in the function interpretation *) val get_entries : func_interp -> FuncEntry.func_entry list (** The (symbolic) `else' value of the function interpretation. *) val get_else : func_interp -> Expr.expr (** The arity of the function interpretation *) val get_arity : func_interp -> int (** A string representation of the function interpretation. *) val to_string : func_interp -> string end (** Retrieves the interpretation (the assignment) of a func_decl in the model. @return An expression if the function has an interpretation in the model, null otherwise. *) val get_const_interp : model -> FuncDecl.func_decl -> Expr.expr option (** Retrieves the interpretation (the assignment) of an expression in the model. @return An expression if the constant has an interpretation in the model, null otherwise. *) val get_const_interp_e : model -> Expr.expr -> Expr.expr option (** Retrieves the interpretation (the assignment) of a non-constant func_decl in the model. @return A FunctionInterpretation if the function has an interpretation in the model, null otherwise. *) val get_func_interp : model -> FuncDecl.func_decl -> FuncInterp.func_interp option (** The number of constant interpretations in the model. *) val get_num_consts : model -> int (** The function declarations of the constants in the model. *) val get_const_decls : model -> FuncDecl.func_decl list (** The number of function interpretations in the model. *) val get_num_funcs : model -> int (** The function declarations of the function interpretations in the model. *) val get_func_decls : model -> FuncDecl.func_decl list (** All symbols that have an interpretation in the model. *) val get_decls : model -> FuncDecl.func_decl list (** Evaluates an expression in the current model. This function may fail if the argument contains quantifiers, is partial (MODEL_PARTIAL enabled), or if it is not well-sorted. In this case a [ModelEvaluationFailedException] is thrown. *) val eval : model -> Expr.expr -> bool -> Expr.expr option (** Alias for [eval]. *) val evaluate : model -> Expr.expr -> bool -> Expr.expr option (** The number of uninterpreted sorts that the model has an interpretation for. *) val get_num_sorts : model -> int (** The uninterpreted sorts that the model has an interpretation for. Z3 also provides an intepretation for uninterpreted sorts used in a formula. The interpretation for a sort is a finite set of distinct values. We say this finite set is the "universe" of the sort. {!get_num_sorts} {!sort_universe} *) val get_sorts : model -> Sort.sort list (** The finite set of distinct values that represent the interpretation of a sort. {!get_sorts} @return A list of expressions, where each is an element of the universe of the sort *) val sort_universe : model -> Sort.sort -> Expr.expr list (** Conversion of models to strings. @return A string representation of the model. *) val to_string : model -> string end (** Probes Probes are used to inspect a goal (aka problem) and collect information that may be used to decide which solver and/or preprocessing step will be used. The complete list of probes may be obtained using the procedures [Context.NumProbes] and [Context.ProbeNames]. It may also be obtained using the command [(help-tactics)] in the SMT 2.0 front-end. *) module Probe : sig type probe (** Execute the probe over the goal. @return A probe always produce a float value. "Boolean" probes return 0.0 for false, and a value different from 0.0 for true. *) val apply : probe -> Goal.goal -> float (** The number of supported Probes. *) val get_num_probes : context -> int (** The names of all supported Probes. *) val get_probe_names : context -> string list (** Returns a string containing a description of the probe with the given name. *) val get_probe_description : context -> string -> string (** Creates a new Probe. *) val mk_probe : context -> string -> probe (** Create a probe that always evaluates to a float value. *) val const : context -> float -> probe (** Create a probe that evaluates to "true" when the value returned by the first argument is less than the value returned by second argument *) val lt : context -> probe -> probe -> probe (** Create a probe that evaluates to "true" when the value returned by the first argument is greater than the value returned by second argument *) val gt : context -> probe -> probe -> probe (** Create a probe that evaluates to "true" when the value returned by the first argument is less than or equal the value returned by second argument *) val le : context -> probe -> probe -> probe (** Create a probe that evaluates to "true" when the value returned by the first argument is greater than or equal the value returned by second argument *) val ge : context -> probe -> probe -> probe (** Create a probe that evaluates to "true" when the value returned by the first argument is equal the value returned by second argument *) val eq : context -> probe -> probe -> probe (** Create a probe that evaluates to "true" when both of two probes evaluate to "true". *) val and_ : context -> probe -> probe -> probe (** Create a probe that evaluates to "true" when either of two probes evaluates to "true". *) val or_ : context -> probe -> probe -> probe (** Create a probe that evaluates to "true" when another probe does not evaluate to "true". *) val not_ : context -> probe -> probe end (** Tactics Tactics are the basic building block for creating custom solvers for specific problem domains. The complete list of tactics may be obtained using [Context.get_num_tactics] and [Context.get_tactic_names]. It may also be obtained using the command [(help-tactics)] in the SMT 2.0 front-end. *) module Tactic : sig type tactic (** Tactic application results ApplyResult objects represent the result of an application of a tactic to a goal. It contains the subgoals that were produced. *) module ApplyResult : sig type apply_result (** The number of Subgoals. *) val get_num_subgoals : apply_result -> int (** Retrieves the subgoals from the apply_result. *) val get_subgoals : apply_result -> Goal.goal list (** Retrieves a subgoal from the apply_result. *) val get_subgoal : apply_result -> int -> Goal.goal (** Convert a model for a subgoal into a model for the original goal [g], that the ApplyResult was obtained from. #return A model for [g] *) val convert_model : apply_result -> int -> Model.model -> Model.model (** A string representation of the ApplyResult. *) val to_string : apply_result -> string end (** A string containing a description of parameters accepted by the tactic. *) val get_help : tactic -> string (** Retrieves parameter descriptions for Tactics. *) val get_param_descrs : tactic -> Params.ParamDescrs.param_descrs (** Apply the tactic to the goal. *) val apply : tactic -> Goal.goal -> Params.params option -> ApplyResult.apply_result (** The number of supported tactics. *) val get_num_tactics : context -> int (** The names of all supported tactics. *) val get_tactic_names : context -> string list (** Returns a string containing a description of the tactic with the given name. *) val get_tactic_description : context -> string -> string (** Creates a new Tactic. *) val mk_tactic : context -> string -> tactic (** Create a tactic that applies one tactic to a Goal and then another one to every subgoal produced by the first one. *) val and_then : context -> tactic -> tactic -> tactic list -> tactic (** Create a tactic that first applies one tactic to a Goal and if it fails then returns the result of another tactic applied to the Goal. *) val or_else : context -> tactic -> tactic -> tactic (** Create a tactic that applies one tactic to a goal for some time (in milliseconds). If the tactic does not terminate within the timeout, then it fails. *) val try_for : context -> tactic -> int -> tactic (** Create a tactic that applies one tactic to a given goal if the probe evaluates to true. If the probe evaluates to false, then the new tactic behaves like the [skip] tactic. *) val when_ : context -> Probe.probe -> tactic -> tactic (** Create a tactic that applies a tactic to a given goal if the probe evaluates to true and another tactic otherwise. *) val cond : context -> Probe.probe -> tactic -> tactic -> tactic (** Create a tactic that keeps applying one tactic until the goal is not modified anymore or the maximum number of iterations is reached. *) val repeat : context -> tactic -> int -> tactic (** Create a tactic that just returns the given goal. *) val skip : context -> tactic (** Create a tactic always fails. *) val fail : context -> tactic (** Create a tactic that fails if the probe evaluates to false. *) val fail_if : context -> Probe.probe -> tactic (** Create a tactic that fails if the goal is not triviall satisfiable (i.e., empty) or trivially unsatisfiable (i.e., contains `false'). *) val fail_if_not_decided : context -> tactic (** Create a tactic that applies a tactic using the given set of parameters. *) val using_params : context -> tactic -> Params.params -> tactic (** Create a tactic that applies a tactic using the given set of parameters. Alias for [UsingParams]*) val with_ : context -> tactic -> Params.params -> tactic (** Create a tactic that applies the given tactics in parallel. *) val par_or : context -> tactic list -> tactic (** Create a tactic that applies a tactic to a given goal and then another tactic to every subgoal produced by the first one. The subgoals are processed in parallel. *) val par_and_then : context -> tactic -> tactic -> tactic (** Interrupt the execution of a Z3 procedure. This procedure can be used to interrupt: solvers, simplifiers and tactics. *) val interrupt : context -> unit end (** Objects that track statistical information. *) module Statistics : sig type statistics (** Statistical data is organized into pairs of \[Key, Entry\], where every Entry is either a floating point or integer value. *) module Entry : sig type statistics_entry (** The key of the entry. *) val get_key : statistics_entry -> string (** The int-value of the entry. *) val get_int : statistics_entry -> int (** The float-value of the entry. *) val get_float : statistics_entry -> float (** True if the entry is uint-valued. *) val is_int : statistics_entry -> bool (** True if the entry is float-valued. *) val is_float : statistics_entry -> bool (** The string representation of the the entry's value. *) val to_string_value : statistics_entry -> string (** The string representation of the entry (key and value) *) val to_string : statistics_entry -> string end (** A string representation of the statistical data. *) val to_string : statistics -> string (** The number of statistical data. *) val get_size : statistics -> int (** The data entries. *) val get_entries : statistics -> Entry.statistics_entry list (** The statistical counters. *) val get_keys : statistics -> string list (** The value of a particular statistical counter. *) val get : statistics -> string -> Entry.statistics_entry option end (** Solvers *) module Solver : sig type solver type status = UNSATISFIABLE | UNKNOWN | SATISFIABLE val string_of_status : status -> string (** A string that describes all available solver parameters. *) val get_help : solver -> string (** Sets the solver parameters. *) val set_parameters : solver -> Params.params -> unit (** Retrieves parameter descriptions for solver. *) val get_param_descrs : solver -> Params.ParamDescrs.param_descrs (** The current number of backtracking points (scopes). {!pop} {!push} *) val get_num_scopes : solver -> int (** Creates a backtracking point. {!pop} *) val push : solver -> unit (** Backtracks a number of backtracking points. Note that an exception is thrown if the integer is not smaller than {!get_num_scopes} {!push} *) val pop : solver -> int -> unit (** Resets the Solver. This removes all assertions from the solver. *) val reset : solver -> unit (** Assert a constraint (or multiple) into the solver. *) val add : solver -> Expr.expr list -> unit (** * Assert multiple constraints (cs) into the solver, and track them (in the * unsat) core * using the Boolean constants in ps. * * This API is an alternative to {!check} with assumptions for * extracting unsat cores. * Both APIs can be used in the same solver. The unsat core will contain a * combination * of the Boolean variables provided using {!assert_and_track} * and the Boolean literals * provided using {!check} with assumptions. *) val assert_and_track_l : solver -> Expr.expr list -> Expr.expr list -> unit (** * Assert a constraint (c) into the solver, and track it (in the unsat) core * using the Boolean constant p. * * This API is an alternative to {!check} with assumptions for * extracting unsat cores. * Both APIs can be used in the same solver. The unsat core will contain a * combination * of the Boolean variables provided using {!assert_and_track} * and the Boolean literals * provided using {!check} with assumptions. *) val assert_and_track : solver -> Expr.expr -> Expr.expr -> unit (** The number of assertions in the solver. *) val get_num_assertions : solver -> int (** The set of asserted formulas. *) val get_assertions : solver -> Expr.expr list (** Checks whether the assertions in the solver are consistent or not. {!Model} {!get_unsat_core} {!Proof} *) val check : solver -> Expr.expr list -> status (** The model of the last [Check]. The result is [None] if [Check] was not invoked before, if its results was not [SATISFIABLE], or if model production is not enabled. *) val get_model : solver -> Model.model option (** The proof of the last [Check]. The result is [null] if [Check] was not invoked before, if its results was not [UNSATISFIABLE], or if proof production is disabled. *) val get_proof : solver -> Expr.expr option (** The unsat core of the last [Check]. The unsat core is a subset of [Assertions] The result is empty if [Check] was not invoked before, if its results was not [UNSATISFIABLE], or if core production is disabled. *) val get_unsat_core : solver -> Expr.expr list (** A brief justification of why the last call to [Check] returned [UNKNOWN]. *) val get_reason_unknown : solver -> string (** Solver statistics. *) val get_statistics : solver -> Statistics.statistics (** Creates a new (incremental) solver. This solver also uses a set of builtin tactics for handling the first check-sat command, and check-sat commands that take more than a given number of milliseconds to be solved. *) val mk_solver : context -> Symbol.symbol option -> solver (** Creates a new (incremental) solver. {!mk_solver} *) val mk_solver_s : context -> string -> solver (** Creates a new (incremental) solver. *) val mk_simple_solver : context -> solver (** Creates a solver that is implemented using the given tactic. The solver supports the commands [Push] and [Pop], but it will always solve each check from scratch. *) val mk_solver_t : context -> Tactic.tactic -> solver (** A string representation of the solver. *) val to_string : solver -> string end (** Fixedpoint solving *) module Fixedpoint : sig type fixedpoint (** A string that describes all available fixedpoint solver parameters. *) val get_help : fixedpoint -> string (** Sets the fixedpoint solver parameters. *) val set_params : fixedpoint -> Params.params -> unit (** Retrieves parameter descriptions for Fixedpoint solver. *) val get_param_descrs : fixedpoint -> Params.ParamDescrs.param_descrs (** Assert a constraints into the fixedpoint solver. *) val add : fixedpoint -> Expr.expr list -> unit (** Register predicate as recursive relation. *) val register_relation : fixedpoint -> FuncDecl.func_decl -> unit (** Add rule into the fixedpoint solver. *) val add_rule : fixedpoint -> Expr.expr -> Symbol.symbol option -> unit (** Add table fact to the fixedpoint solver. *) val add_fact : fixedpoint -> FuncDecl.func_decl -> int list -> unit (** Query the fixedpoint solver. A query is a conjunction of constraints. The constraints may include the recursively defined relations. The query is satisfiable if there is an instance of the query variables and a derivation for it. The query is unsatisfiable if there are no derivations satisfying the query variables. *) val query : fixedpoint -> Expr.expr -> Solver.status (** Query the fixedpoint solver. A query is an array of relations. The query is satisfiable if there is an instance of some relation that is non-empty. The query is unsatisfiable if there are no derivations satisfying any of the relations. *) val query_r : fixedpoint -> FuncDecl.func_decl list -> Solver.status (** Creates a backtracking point. {!pop} *) val push : fixedpoint -> unit (** Backtrack one backtracking point. Note that an exception is thrown if Pop is called without a corresponding [Push] {!push} *) val pop : fixedpoint -> unit (** Update named rule into in the fixedpoint solver. *) val update_rule : fixedpoint -> Expr.expr -> Symbol.symbol -> unit (** Retrieve satisfying instance or instances of solver, or definitions for the recursive predicates that show unsatisfiability. *) val get_answer : fixedpoint -> Expr.expr option (** Retrieve explanation why fixedpoint engine returned status Unknown. *) val get_reason_unknown : fixedpoint -> string (** Retrieve the number of levels explored for a given predicate. *) val get_num_levels : fixedpoint -> FuncDecl.func_decl -> int (** Retrieve the cover of a predicate. *) val get_cover_delta : fixedpoint -> int -> FuncDecl.func_decl -> Expr.expr option (** Add property about the predicate. The property is added at level. *) val add_cover : fixedpoint -> int -> FuncDecl.func_decl -> Expr.expr -> unit (** Retrieve internal string representation of fixedpoint object. *) val to_string : fixedpoint -> string (** Instrument the Datalog engine on which table representation to use for recursive predicate. *) val set_predicate_representation : fixedpoint -> FuncDecl.func_decl -> Symbol.symbol list -> unit (** Convert benchmark given as set of axioms, rules and queries to a string. *) val to_string_q : fixedpoint -> Expr.expr list -> string (** Retrieve set of rules added to fixedpoint context. *) val get_rules : fixedpoint -> Expr.expr list (** Retrieve set of assertions added to fixedpoint context. *) val get_assertions : fixedpoint -> Expr.expr list (** Create a Fixedpoint context. *) val mk_fixedpoint : context -> fixedpoint (** Retrieve statistics information from the last call to #Z3_fixedpoint_query. *) val get_statistics : fixedpoint -> Statistics.statistics (** Parse an SMT-LIB2 string with fixedpoint rules. Add the rules to the current fixedpoint context. Return the set of queries in the string. *) val parse_string : fixedpoint -> string -> Expr.expr list (** Parse an SMT-LIB2 file with fixedpoint rules. Add the rules to the current fixedpoint context. Return the set of queries in the file. *) val parse_file : fixedpoint -> string -> Expr.expr list end (** Functions for handling SMT and SMT2 expressions and files *) module SMT : sig (** Convert a benchmark into an SMT-LIB formatted string. @return A string representation of the benchmark. *) val benchmark_to_smtstring : context -> string -> string -> string -> string -> Expr.expr list -> Expr.expr -> string (** Parse the given string using the SMT-LIB parser. The symbol table of the parser can be initialized using the given sorts and declarations. The symbols in the arrays in the third and fifth argument don't need to match the names of the sorts and declarations in the arrays in the fourth and sixth argument. This is a useful feature since we can use arbitrary names to reference sorts and declarations. *) val parse_smtlib_string : context -> string -> Symbol.symbol list -> Sort.sort list -> Symbol.symbol list -> FuncDecl.func_decl list -> unit (** Parse the given file using the SMT-LIB parser. {!parse_smtlib_string} *) val parse_smtlib_file : context -> string -> Symbol.symbol list -> Sort.sort list -> Symbol.symbol list -> FuncDecl.func_decl list -> unit (** The number of SMTLIB formulas parsed by the last call to [ParseSMTLIBString] or [ParseSMTLIBFile]. *) val get_num_smtlib_formulas : context -> int (** The formulas parsed by the last call to [ParseSMTLIBString] or [ParseSMTLIBFile]. *) val get_smtlib_formulas : context -> Expr.expr list (** The number of SMTLIB assumptions parsed by the last call to [ParseSMTLIBString] or [ParseSMTLIBFile]. *) val get_num_smtlib_assumptions : context -> int (** The assumptions parsed by the last call to [ParseSMTLIBString] or [ParseSMTLIBFile]. *) val get_smtlib_assumptions : context -> Expr.expr list (** The number of SMTLIB declarations parsed by the last call to [ParseSMTLIBString] or [ParseSMTLIBFile]. *) val get_num_smtlib_decls : context -> int (** The declarations parsed by the last call to [ParseSMTLIBString] or [ParseSMTLIBFile]. *) val get_smtlib_decls : context -> FuncDecl.func_decl list (** The number of SMTLIB sorts parsed by the last call to [ParseSMTLIBString] or [ParseSMTLIBFile]. *) val get_num_smtlib_sorts : context -> int (** The sort declarations parsed by the last call to [ParseSMTLIBString] or [ParseSMTLIBFile]. *) val get_smtlib_sorts : context -> Sort.sort list (** Parse the given string using the SMT-LIB2 parser. {!parse_smtlib_string} @return A conjunction of assertions in the scope (up to push/pop) at the end of the string. *) val parse_smtlib2_string : context -> string -> Symbol.symbol list -> Sort.sort list -> Symbol.symbol list -> FuncDecl.func_decl list -> Expr.expr (** Parse the given file using the SMT-LIB2 parser. {!parse_smtlib2_string} *) val parse_smtlib2_file : context -> string -> Symbol.symbol list -> Sort.sort list -> Symbol.symbol list -> FuncDecl.func_decl list -> Expr.expr end (** Interpolation *) module Interpolation : sig (** Create an AST node marking a formula position for interpolation. The expression must have Boolean sort. *) val mk_interpolant : context -> Expr.expr -> Expr.expr (** The interpolation context is suitable for generation of interpolants. For more information on interpolation please refer too the C/C++ API, which is well documented. *) val mk_interpolation_context : (string * string) list -> context (** Gets an interpolant. For more information on interpolation please refer too the C/C++ API, which is well documented. *) val get_interpolant : context -> Expr.expr -> Expr.expr -> Params.params -> Expr.expr list (** Computes an interpolant. For more information on interpolation please refer too the C/C++ API, which is well documented. *) val compute_interpolant : context -> Expr.expr -> Params.params -> (Z3enums.lbool * Expr.expr list option * Model.model option) (** Retrieves an interpolation profile. For more information on interpolation please refer too the C/C++ API, which is well documented. *) val get_interpolation_profile : context -> string (** Read an interpolation problem from file. For more information on interpolation please refer too the C/C++ API, which is well documented. *) val read_interpolation_problem : context -> string -> (Expr.expr list * int list * Expr.expr list) (** Check the correctness of an interpolant. For more information on interpolation please refer too the C/C++ API, which is well documented. *) val check_interpolant : context -> int -> Expr.expr list -> int list -> Expr.expr list -> int -> Expr.expr list -> unit (** Write an interpolation problem to file suitable for reading with Z3_read_interpolation_problem. For more information on interpolation please refer too the C/C++ API, which is well documented. *) val write_interpolation_problem : context -> int -> Expr.expr list -> int list -> string -> int -> Expr.expr list -> unit end (** Set a global (or module) parameter, which is shared by all Z3 contexts. When a Z3 module is initialized it will use the value of these parameters when Z3_params objects are not provided. The name of parameter can be composed of characters [a-z][A-Z], digits [0-9], '-' and '_'. The character '.' is a delimiter (more later). The parameter names are case-insensitive. The character '-' should be viewed as an "alias" for '_'. Thus, the following parameter names are considered equivalent: "pp.decimal-precision" and "PP.DECIMAL_PRECISION". This function can be used to set parameters for a specific Z3 module. This can be done by using .. For example: (set_global_param "pp.decimal" "true") will set the parameter "decimal" in the module "pp" to true. *) val set_global_param : string -> string -> unit (** Get a global (or module) parameter. Returns None if the parameter does not exist. The caller must invoke #Z3_global_param_del_value to delete the value returned at param_value. This function cannot be invoked simultaneously from different threads without synchronization. The result string stored in param_value is stored in a shared location. *) val get_global_param : string -> string option (** Restore the value of all global (and module) parameters. This command will not affect already created objects (such as tactics and solvers) {!set_global_param} *) val global_param_reset_all : unit -> unit (** Enable/disable printing of warning messages to the console. Note that this function is static and effects the behaviour of all contexts globally. *) val toggle_warning_messages : bool -> unit (** Enable tracing messages tagged as `tag' when Z3 is compiled in debug mode. Remarks: It is a NOOP otherwise. *) val enable_trace : string -> unit (** Disable tracing messages tagged as `tag' when Z3 is compiled in debug mode. Remarks: It is a NOOP otherwise. *) val disable_trace : string -> unit z3-4.4.0/src/api/api_quant.cpp0000644000175000017500000004431112540347414015675 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_quant.cpp Abstract: API for quantifiers Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" #include"api_util.h" #include"pattern_validation.h" #include"expr_abstract.h" extern "C" { Z3_ast Z3_API Z3_mk_quantifier( Z3_context c, Z3_bool is_forall, unsigned weight, unsigned num_patterns, Z3_pattern const patterns[], unsigned num_decls, Z3_sort const sorts[], Z3_symbol const decl_names[], Z3_ast body) { return Z3_mk_quantifier_ex( c, is_forall, weight, 0, 0, num_patterns, patterns, 0, 0, num_decls, sorts, decl_names, body ); } Z3_ast mk_quantifier_ex_core( Z3_context c, Z3_bool is_forall, unsigned weight, Z3_symbol quantifier_id, Z3_symbol skolem_id, unsigned num_patterns, Z3_pattern const patterns[], unsigned num_no_patterns, Z3_ast const no_patterns[], unsigned num_decls, Z3_sort const sorts[], Z3_symbol const decl_names[], Z3_ast body) { Z3_TRY; RESET_ERROR_CODE(); if (!mk_c(c)->m().is_bool(to_expr(body))) { SET_ERROR_CODE(Z3_SORT_ERROR); } if (num_patterns > 0 && num_no_patterns > 0) { SET_ERROR_CODE(Z3_INVALID_USAGE); } expr * const* ps = reinterpret_cast(patterns); expr * const* no_ps = reinterpret_cast(no_patterns); pattern_validator v(mk_c(c)->m()); for (unsigned i = 0; i < num_patterns; i++) { if (!v(num_decls, ps[i])) { SET_ERROR_CODE(Z3_INVALID_PATTERN); return 0; } } sort* const* ts = reinterpret_cast(sorts); svector names; for (unsigned i = 0; i < num_decls; ++i) { names.push_back(to_symbol(decl_names[i])); } expr_ref result(mk_c(c)->m()); if (num_decls > 0) { result = mk_c(c)->m().mk_quantifier( (0 != is_forall), names.size(), ts, names.c_ptr(), to_expr(body), weight, to_symbol(quantifier_id), to_symbol(skolem_id), num_patterns, ps, num_no_patterns, no_ps ); } else { result = to_expr(body); } mk_c(c)->save_ast_trail(result.get()); return of_ast(result.get()); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_quantifier_ex( Z3_context c, Z3_bool is_forall, unsigned weight, Z3_symbol quantifier_id, Z3_symbol skolem_id, unsigned num_patterns, Z3_pattern const patterns[], unsigned num_no_patterns, Z3_ast const no_patterns[], unsigned num_decls, Z3_sort const sorts[], Z3_symbol const decl_names[], Z3_ast body) { LOG_Z3_mk_quantifier_ex(c, is_forall, weight, quantifier_id, skolem_id, num_patterns, patterns, num_no_patterns, no_patterns, num_decls, sorts, decl_names, body); Z3_ast r = mk_quantifier_ex_core(c, is_forall, weight, quantifier_id, skolem_id, num_patterns, patterns, num_no_patterns, no_patterns, num_decls, sorts, decl_names, body); RETURN_Z3(r); } Z3_ast Z3_API Z3_mk_forall(Z3_context c, unsigned weight, unsigned num_patterns, Z3_pattern const patterns[], unsigned num_decls, Z3_sort const types[], Z3_symbol const decl_names[], Z3_ast body) { return Z3_mk_quantifier(c, 1, weight, num_patterns, patterns, num_decls, types, decl_names, body); } Z3_ast Z3_API Z3_mk_exists(Z3_context c, unsigned weight, unsigned num_patterns, Z3_pattern const patterns[], unsigned num_decls, Z3_sort const types[], Z3_symbol const decl_names[], Z3_ast body) { return Z3_mk_quantifier(c, 0, weight, num_patterns, patterns, num_decls, types, decl_names, body); } Z3_ast Z3_API Z3_mk_quantifier_const_ex(Z3_context c, Z3_bool is_forall, unsigned weight, Z3_symbol quantifier_id, Z3_symbol skolem_id, unsigned num_bound, Z3_app const bound[], unsigned num_patterns, Z3_pattern const patterns[], unsigned num_no_patterns, Z3_ast const no_patterns[], Z3_ast body) { Z3_TRY; LOG_Z3_mk_quantifier_const_ex(c, is_forall, weight, quantifier_id, skolem_id, num_bound, bound, num_patterns, patterns, num_no_patterns, no_patterns, body); RESET_ERROR_CODE(); svector names; svector types; ptr_vector bound_asts; if (num_patterns > 0 && num_no_patterns > 0) { SET_ERROR_CODE(Z3_INVALID_USAGE); } for (unsigned i = 0; i < num_bound; ++i) { app* a = to_app(bound[i]); if (a->get_kind() != AST_APP) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } symbol s(to_app(a)->get_decl()->get_name()); names.push_back(of_symbol(s)); types.push_back(of_sort(mk_c(c)->m().get_sort(a))); bound_asts.push_back(a); if (a->get_family_id() != null_family_id || a->get_num_args() != 0) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } } // Abstract patterns svector _patterns; expr_ref_vector pinned(mk_c(c)->m()); for (unsigned i = 0; i < num_patterns; ++i) { expr_ref result(mk_c(c)->m()); app* pat = to_pattern(patterns[i]); SASSERT(mk_c(c)->m().is_pattern(pat)); expr_abstract(mk_c(c)->m(), 0, num_bound, bound_asts.c_ptr(), pat, result); SASSERT(result.get()->get_kind() == AST_APP); pinned.push_back(result.get()); SASSERT(mk_c(c)->m().is_pattern(result.get())); _patterns.push_back(of_pattern(result.get())); } svector _no_patterns; for (unsigned i = 0; i < num_no_patterns; ++i) { expr_ref result(mk_c(c)->m()); if (!is_app(to_expr(no_patterns[i]))) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } app* pat = to_app(to_expr(no_patterns[i])); expr_abstract(mk_c(c)->m(), 0, num_bound, bound_asts.c_ptr(), pat, result); SASSERT(result.get()->get_kind() == AST_APP); pinned.push_back(result.get()); _no_patterns.push_back(of_ast(result.get())); } expr_ref abs_body(mk_c(c)->m()); expr_abstract(mk_c(c)->m(), 0, num_bound, bound_asts.c_ptr(), to_expr(body), abs_body); Z3_ast result = mk_quantifier_ex_core(c, is_forall, weight, quantifier_id, skolem_id, num_patterns, _patterns.c_ptr(), num_no_patterns, _no_patterns.c_ptr(), names.size(), types.c_ptr(), names.c_ptr(), of_ast(abs_body.get())); RETURN_Z3(result); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_quantifier_const(Z3_context c, Z3_bool is_forall, unsigned weight, unsigned num_bound, Z3_app const bound[], unsigned num_patterns, Z3_pattern const patterns[], Z3_ast body) { return Z3_mk_quantifier_const_ex(c, is_forall, weight, 0, 0, num_bound, bound, num_patterns, patterns, 0, 0, body); } Z3_ast Z3_API Z3_mk_forall_const(Z3_context c, unsigned weight, unsigned num_bound, Z3_app const bound[], unsigned num_patterns, Z3_pattern const patterns[], Z3_ast body) { return Z3_mk_quantifier_const(c, true, weight, num_bound, bound, num_patterns, patterns, body); } Z3_ast Z3_API Z3_mk_exists_const(Z3_context c, unsigned weight, unsigned num_bound, Z3_app const bound[], unsigned num_patterns, Z3_pattern const patterns[], Z3_ast body) { return Z3_mk_quantifier_const(c, false, weight, num_bound, bound, num_patterns, patterns, body); } Z3_pattern Z3_API Z3_mk_pattern(Z3_context c, unsigned num_patterns, Z3_ast const terms[]) { Z3_TRY; LOG_Z3_mk_pattern(c, num_patterns, terms); RESET_ERROR_CODE(); for (unsigned i = 0; i < num_patterns; ++i) { if (!is_app(to_expr(terms[i]))) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } } app* a = mk_c(c)->m().mk_pattern(num_patterns, reinterpret_cast(to_exprs(terms))); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_pattern(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_bound(Z3_context c, unsigned index, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_bound(c, index, ty); RESET_ERROR_CODE(); ast* a = mk_c(c)->m().mk_var(index, to_sort(ty)); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(0); } Z3_bool Z3_API Z3_is_quantifier_forall(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_is_quantifier_forall(c, a); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { return to_quantifier(_a)->is_forall(); } else { SET_ERROR_CODE(Z3_SORT_ERROR); return Z3_FALSE; } Z3_CATCH_RETURN(Z3_FALSE); } unsigned Z3_API Z3_get_quantifier_weight(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_quantifier_weight(c, a); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { return to_quantifier(_a)->get_weight(); } else { SET_ERROR_CODE(Z3_SORT_ERROR); return 0; } Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_get_quantifier_num_patterns(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_quantifier_num_patterns(c, a); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { return to_quantifier(_a)->get_num_patterns(); } else { SET_ERROR_CODE(Z3_SORT_ERROR); return 0; } Z3_CATCH_RETURN(0); } Z3_pattern Z3_API Z3_get_quantifier_pattern_ast(Z3_context c, Z3_ast a, unsigned i) { Z3_TRY; LOG_Z3_get_quantifier_pattern_ast(c, a, i); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { Z3_pattern r = of_pattern(to_quantifier(_a)->get_patterns()[i]); RETURN_Z3(r); } else { SET_ERROR_CODE(Z3_SORT_ERROR); RETURN_Z3(0); } Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_get_quantifier_num_no_patterns(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_quantifier_num_no_patterns(c, a); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { return to_quantifier(_a)->get_num_no_patterns(); } else { SET_ERROR_CODE(Z3_SORT_ERROR); return 0; } Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_get_quantifier_no_pattern_ast(Z3_context c, Z3_ast a, unsigned i) { Z3_TRY; LOG_Z3_get_quantifier_no_pattern_ast(c, a, i); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { Z3_ast r = of_ast(to_quantifier(_a)->get_no_pattern(i)); RETURN_Z3(r); } else { SET_ERROR_CODE(Z3_SORT_ERROR); RETURN_Z3(0); } Z3_CATCH_RETURN(0); } Z3_symbol Z3_API Z3_get_quantifier_bound_name(Z3_context c, Z3_ast a, unsigned i) { Z3_TRY; LOG_Z3_get_quantifier_bound_name(c, a, i); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { return of_symbol(to_quantifier(_a)->get_decl_names()[i]); } else { SET_ERROR_CODE(Z3_SORT_ERROR); return 0; } Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_get_quantifier_bound_sort(Z3_context c, Z3_ast a, unsigned i) { Z3_TRY; LOG_Z3_get_quantifier_bound_sort(c, a, i); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { Z3_sort r = of_sort(to_quantifier(_a)->get_decl_sort(i)); RETURN_Z3(r); } else { SET_ERROR_CODE(Z3_SORT_ERROR); RETURN_Z3(0); } Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_get_quantifier_body(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_quantifier_body(c, a); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { Z3_ast r = of_ast(to_quantifier(_a)->get_expr()); RETURN_Z3(r); } else { SET_ERROR_CODE(Z3_SORT_ERROR); RETURN_Z3(0); } Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_get_quantifier_num_bound(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_quantifier_num_bound(c, a); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { return to_quantifier(_a)->get_num_decls(); } else { SET_ERROR_CODE(Z3_SORT_ERROR); return 0; } Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_get_pattern_num_terms(Z3_context c, Z3_pattern p) { Z3_TRY; LOG_Z3_get_pattern_num_terms(c, p); RESET_ERROR_CODE(); app* _p = to_pattern(p); if (mk_c(c)->m().is_pattern(_p)) { return _p->get_num_args(); } else { SET_ERROR_CODE(Z3_SORT_ERROR); return 0; } Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_get_pattern(Z3_context c, Z3_pattern p, unsigned idx) { Z3_TRY; LOG_Z3_get_pattern(c, p, idx); RESET_ERROR_CODE(); app* _p = to_pattern(p); if (mk_c(c)->m().is_pattern(_p)) { Z3_ast r = of_ast(_p->get_arg(idx)); RETURN_Z3(r); } else { SET_ERROR_CODE(Z3_SORT_ERROR); RETURN_Z3(0); } Z3_CATCH_RETURN(0); } Z3_func_decl Z3_API Z3_mk_injective_function(Z3_context c, Z3_symbol s, unsigned domain_size, Z3_sort const domain[], Z3_sort range) { Z3_TRY; LOG_Z3_mk_injective_function(c, s, domain_size, domain, range); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); mk_c(c)->reset_last_result(); sort* range_ = to_sort(range); func_decl* d = m.mk_func_decl(to_symbol(s), domain_size, to_sorts(domain), range_); expr_ref_vector args(m); expr_ref fn(m), body(m); vector names; for (unsigned i = 0; i < domain_size; ++i) { unsigned idx = domain_size-i-1; args.push_back(m.mk_var(idx, to_sort(domain[i]))); names.push_back(symbol(idx)); } fn = m.mk_app(d, args.size(), args.c_ptr()); for (unsigned i = 0; i < domain_size; ++i) { expr* arg = args[i].get(); sort* dom = m.get_sort(arg); func_decl* inv = m.mk_fresh_func_decl(symbol("inv"), to_symbol(s), 1, &range_, dom); body = m.mk_eq(m.mk_app(inv, fn.get()), arg); body = m.mk_forall(args.size(), to_sorts(domain), names.c_ptr(), body.get()); mk_c(c)->save_multiple_ast_trail(body.get()); mk_c(c)->assert_cnstr(body.get()); } mk_c(c)->save_multiple_ast_trail(d); RETURN_Z3(of_func_decl(d)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_pattern_to_ast(Z3_context c, Z3_pattern p) { RESET_ERROR_CODE(); return (Z3_ast)(p); } Z3_API char const * Z3_pattern_to_string(Z3_context c, Z3_pattern p) { return Z3_ast_to_string(c, reinterpret_cast(p)); } }; z3-4.4.0/src/api/api_polynomial.h0000644000175000017500000000121412540347414016370 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_polynomial.h Abstract: Polynomial manager and caches for the external API. Author: Leonardo de Moura (leonardo) 2012-12-08 Notes: --*/ #ifndef _API_POLYNOMIAL_H_ #define _API_POLYNOMIAL_H_ #include"polynomial.h" namespace api { class pmanager { unsynch_mpz_manager m_nm; polynomial::manager m_pm; // TODO: add support for caching expressions -> polynomial and back public: pmanager(); virtual ~pmanager(); polynomial::manager & pm() { return m_pm; } void set_cancel(bool f); }; }; #endif z3-4.4.0/src/api/api_datalog.h0000644000175000017500000000224412540347414015624 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_datalog.h Abstract: Datalog API old external_relation_context_impl Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #ifndef _API_DATALOG_H_ #define _API_DATALOG_H_ #include"z3.h" #include"ast.h" #include"smt_params.h" #include"smt_kernel.h" #include"api_util.h" typedef void (*reduce_app_callback_fptr)(void*, func_decl*, unsigned, expr*const*, expr**); typedef void (*reduce_assign_callback_fptr)(void*, func_decl*, unsigned, expr*const*, unsigned, expr*const*); namespace api { class fixedpoint_context; }; struct Z3_fixedpoint_ref : public api::object { api::fixedpoint_context * m_datalog; params_ref m_params; Z3_fixedpoint_ref():m_datalog(0) {} virtual ~Z3_fixedpoint_ref() { dealloc(m_datalog); } }; inline Z3_fixedpoint_ref * to_fixedpoint(Z3_fixedpoint s) { return reinterpret_cast(s); } inline Z3_fixedpoint of_datalog(Z3_fixedpoint_ref * s) { return reinterpret_cast(s); } inline api::fixedpoint_context * to_fixedpoint_ref(Z3_fixedpoint s) { return to_fixedpoint(s)->m_datalog; } #endif z3-4.4.0/src/api/dll/0000755000175000017500000000000012540347414013760 5ustar michaelmichaelz3-4.4.0/src/api/dll/dll.cpp0000644000175000017500000000076612540347414015250 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifdef _WINDOWS #include #ifdef _MANAGED #pragma managed(push, off) #endif BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } #ifdef _MANAGED #pragma managed(pop) #endif #endif z3-4.4.0/src/api/dotnet/0000755000175000017500000000000012540347414014502 5ustar michaelmichaelz3-4.4.0/src/api/dotnet/Context.cs0000644000175000017500000054265712540347414016500 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Context.cs Abstract: Z3 Managed API: Context Author: Christoph Wintersteiger (cwinter) 2012-03-15 Notes: --*/ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// The main interaction with Z3 happens via the Context. /// [ContractVerification(true)] public class Context : IDisposable { #region Constructors /// /// Constructor. /// public Context() : base() { m_ctx = Native.Z3_mk_context_rc(IntPtr.Zero); InitContext(); } /// /// Constructor. /// /// /// The following parameters can be set: /// - proof (Boolean) Enable proof generation /// - debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting /// - trace (Boolean) Tracing support for VCC /// - trace_file_name (String) Trace out file for VCC traces /// - timeout (unsigned) default timeout (in milliseconds) used for solvers /// - well_sorted_check type checker /// - auto_config use heuristics to automatically select solver and configure it /// - model model generation for solvers, this parameter can be overwritten when creating a solver /// - model_validate validate models produced by solvers /// - unsat_core unsat-core generation for solvers, this parameter can be overwritten when creating a solver /// Note that in previous versions of Z3, this constructor was also used to set global and module parameters. /// For this purpose we should now use /// public Context(Dictionary settings) : base() { Contract.Requires(settings != null); IntPtr cfg = Native.Z3_mk_config(); foreach (KeyValuePair kv in settings) Native.Z3_set_param_value(cfg, kv.Key, kv.Value); m_ctx = Native.Z3_mk_context_rc(cfg); Native.Z3_del_config(cfg); InitContext(); } #endregion #region Symbols /// /// Creates a new symbol using an integer. /// /// /// Not all integers can be passed to this function. /// The legal range of unsigned integers is 0 to 2^30-1. /// public IntSymbol MkSymbol(int i) { Contract.Ensures(Contract.Result() != null); return new IntSymbol(this, i); } /// /// Create a symbol using a string. /// public StringSymbol MkSymbol(string name) { Contract.Ensures(Contract.Result() != null); return new StringSymbol(this, name); } /// /// Create an array of symbols. /// internal Symbol[] MkSymbols(string[] names) { Contract.Ensures(names == null || Contract.Result() != null); Contract.Ensures(names != null || Contract.Result() == null); Contract.Ensures(Contract.Result() == null || Contract.Result().Length == names.Length); Contract.Ensures(Contract.Result() == null || Contract.ForAll(Contract.Result(), s => s != null)); if (names == null) return null; Symbol[] result = new Symbol[names.Length]; for (int i = 0; i < names.Length; ++i) result[i] = MkSymbol(names[i]); return result; } #endregion #region Sorts private BoolSort m_boolSort = null; private IntSort m_intSort = null; private RealSort m_realSort = null; /// /// Retrieves the Boolean sort of the context. /// public BoolSort BoolSort { get { Contract.Ensures(Contract.Result() != null); if (m_boolSort == null) m_boolSort = new BoolSort(this); return m_boolSort; } } /// /// Retrieves the Integer sort of the context. /// public IntSort IntSort { get { Contract.Ensures(Contract.Result() != null); if (m_intSort == null) m_intSort = new IntSort(this); return m_intSort; } } /// /// Retrieves the Real sort of the context. /// public RealSort RealSort { get { Contract.Ensures(Contract.Result() != null); if (m_realSort == null) m_realSort = new RealSort(this); return m_realSort; } } /// /// Create a new Boolean sort. /// public BoolSort MkBoolSort() { Contract.Ensures(Contract.Result() != null); return new BoolSort(this); } /// /// Create a new uninterpreted sort. /// public UninterpretedSort MkUninterpretedSort(Symbol s) { Contract.Requires(s != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(s); return new UninterpretedSort(this, s); } /// /// Create a new uninterpreted sort. /// public UninterpretedSort MkUninterpretedSort(string str) { Contract.Ensures(Contract.Result() != null); return MkUninterpretedSort(MkSymbol(str)); } /// /// Create a new integer sort. /// public IntSort MkIntSort() { Contract.Ensures(Contract.Result() != null); return new IntSort(this); } /// /// Create a real sort. /// public RealSort MkRealSort() { Contract.Ensures(Contract.Result() != null); return new RealSort(this); } /// /// Create a new bit-vector sort. /// public BitVecSort MkBitVecSort(uint size) { Contract.Ensures(Contract.Result() != null); return new BitVecSort(this, Native.Z3_mk_bv_sort(nCtx, size)); } /// /// Create a new array sort. /// public ArraySort MkArraySort(Sort domain, Sort range) { Contract.Requires(domain != null); Contract.Requires(range != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(domain); CheckContextMatch(range); return new ArraySort(this, domain, range); } /// /// Create a new tuple sort. /// public TupleSort MkTupleSort(Symbol name, Symbol[] fieldNames, Sort[] fieldSorts) { Contract.Requires(name != null); Contract.Requires(fieldNames != null); Contract.Requires(Contract.ForAll(fieldNames, fn => fn != null)); Contract.Requires(fieldSorts == null || Contract.ForAll(fieldSorts, fs => fs != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(name); CheckContextMatch(fieldNames); CheckContextMatch(fieldSorts); return new TupleSort(this, name, (uint)fieldNames.Length, fieldNames, fieldSorts); } /// /// Create a new enumeration sort. /// public EnumSort MkEnumSort(Symbol name, params Symbol[] enumNames) { Contract.Requires(name != null); Contract.Requires(enumNames != null); Contract.Requires(Contract.ForAll(enumNames, f => f != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(name); CheckContextMatch(enumNames); return new EnumSort(this, name, enumNames); } /// /// Create a new enumeration sort. /// public EnumSort MkEnumSort(string name, params string[] enumNames) { Contract.Requires(enumNames != null); Contract.Ensures(Contract.Result() != null); return new EnumSort(this, MkSymbol(name), MkSymbols(enumNames)); } /// /// Create a new list sort. /// public ListSort MkListSort(Symbol name, Sort elemSort) { Contract.Requires(name != null); Contract.Requires(elemSort != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(name); CheckContextMatch(elemSort); return new ListSort(this, name, elemSort); } /// /// Create a new list sort. /// public ListSort MkListSort(string name, Sort elemSort) { Contract.Requires(elemSort != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(elemSort); return new ListSort(this, MkSymbol(name), elemSort); } /// /// Create a new finite domain sort. /// The result is a sort /// /// The name used to identify the sort /// The size of the sort public FiniteDomainSort MkFiniteDomainSort(Symbol name, ulong size) { Contract.Requires(name != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(name); return new FiniteDomainSort(this, name, size); } /// /// Create a new finite domain sort. /// The result is a sort /// Elements of the sort are created using , /// and the elements range from 0 to size-1. /// /// The name used to identify the sort /// The size of the sort public FiniteDomainSort MkFiniteDomainSort(string name, ulong size) { Contract.Ensures(Contract.Result() != null); return new FiniteDomainSort(this, MkSymbol(name), size); } #region Datatypes /// /// Create a datatype constructor. /// /// constructor name /// name of recognizer function. /// names of the constructor fields. /// field sorts, 0 if the field sort refers to a recursive sort. /// reference to datatype sort that is an argument to the constructor; /// if the corresponding sort reference is 0, then the value in sort_refs should be an index /// referring to one of the recursive datatypes that is declared. public Constructor MkConstructor(Symbol name, Symbol recognizer, Symbol[] fieldNames = null, Sort[] sorts = null, uint[] sortRefs = null) { Contract.Requires(name != null); Contract.Requires(recognizer != null); Contract.Ensures(Contract.Result() != null); return new Constructor(this, name, recognizer, fieldNames, sorts, sortRefs); } /// /// Create a datatype constructor. /// /// /// /// /// /// /// public Constructor MkConstructor(string name, string recognizer, string[] fieldNames = null, Sort[] sorts = null, uint[] sortRefs = null) { Contract.Ensures(Contract.Result() != null); return new Constructor(this, MkSymbol(name), MkSymbol(recognizer), MkSymbols(fieldNames), sorts, sortRefs); } /// /// Create a new datatype sort. /// public DatatypeSort MkDatatypeSort(Symbol name, Constructor[] constructors) { Contract.Requires(name != null); Contract.Requires(constructors != null); Contract.Requires(Contract.ForAll(constructors, c => c != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(name); CheckContextMatch(constructors); return new DatatypeSort(this, name, constructors); } /// /// Create a new datatype sort. /// public DatatypeSort MkDatatypeSort(string name, Constructor[] constructors) { Contract.Requires(constructors != null); Contract.Requires(Contract.ForAll(constructors, c => c != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(constructors); return new DatatypeSort(this, MkSymbol(name), constructors); } /// /// Create mutually recursive datatypes. /// /// names of datatype sorts /// list of constructors, one list per sort. public DatatypeSort[] MkDatatypeSorts(Symbol[] names, Constructor[][] c) { Contract.Requires(names != null); Contract.Requires(c != null); Contract.Requires(names.Length == c.Length); Contract.Requires(Contract.ForAll(0, c.Length, j => c[j] != null)); Contract.Requires(Contract.ForAll(names, name => name != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(names); uint n = (uint)names.Length; ConstructorList[] cla = new ConstructorList[n]; IntPtr[] n_constr = new IntPtr[n]; for (uint i = 0; i < n; i++) { Constructor[] constructor = c[i]; Contract.Assume(Contract.ForAll(constructor, arr => arr != null), "Clousot does not support yet quantified formula on multidimensional arrays"); CheckContextMatch(constructor); cla[i] = new ConstructorList(this, constructor); n_constr[i] = cla[i].NativeObject; } IntPtr[] n_res = new IntPtr[n]; Native.Z3_mk_datatypes(nCtx, n, Symbol.ArrayToNative(names), n_res, n_constr); DatatypeSort[] res = new DatatypeSort[n]; for (uint i = 0; i < n; i++) res[i] = new DatatypeSort(this, n_res[i]); return res; } /// /// Create mutually recursive data-types. /// /// /// /// public DatatypeSort[] MkDatatypeSorts(string[] names, Constructor[][] c) { Contract.Requires(names != null); Contract.Requires(c != null); Contract.Requires(names.Length == c.Length); Contract.Requires(Contract.ForAll(0, c.Length, j => c[j] != null)); Contract.Requires(Contract.ForAll(names, name => name != null)); Contract.Ensures(Contract.Result() != null); return MkDatatypeSorts(MkSymbols(names), c); } /// /// Update a datatype field at expression t with value v. /// The function performs a record update at t. The field /// that is passed in as argument is updated with value v, /// the remainig fields of t are unchanged. /// public Expr MkUpdateField(FuncDecl field, Expr t, Expr v) { return Expr.Create(this, Native.Z3_datatype_update_field( nCtx, field.NativeObject, t.NativeObject, v.NativeObject)); } #endregion #endregion #region Function Declarations /// /// Creates a new function declaration. /// public FuncDecl MkFuncDecl(Symbol name, Sort[] domain, Sort range) { Contract.Requires(name != null); Contract.Requires(range != null); Contract.Requires(Contract.ForAll(domain, d => d != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(name); CheckContextMatch(domain); CheckContextMatch(range); return new FuncDecl(this, name, domain, range); } /// /// Creates a new function declaration. /// public FuncDecl MkFuncDecl(Symbol name, Sort domain, Sort range) { Contract.Requires(name != null); Contract.Requires(domain != null); Contract.Requires(range != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(name); CheckContextMatch(domain); CheckContextMatch(range); Sort[] q = new Sort[] { domain }; return new FuncDecl(this, name, q, range); } /// /// Creates a new function declaration. /// public FuncDecl MkFuncDecl(string name, Sort[] domain, Sort range) { Contract.Requires(range != null); Contract.Requires(Contract.ForAll(domain, d => d != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(domain); CheckContextMatch(range); return new FuncDecl(this, MkSymbol(name), domain, range); } /// /// Creates a new function declaration. /// public FuncDecl MkFuncDecl(string name, Sort domain, Sort range) { Contract.Requires(range != null); Contract.Requires(domain != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(domain); CheckContextMatch(range); Sort[] q = new Sort[] { domain }; return new FuncDecl(this, MkSymbol(name), q, range); } /// /// Creates a fresh function declaration with a name prefixed with . /// /// /// public FuncDecl MkFreshFuncDecl(string prefix, Sort[] domain, Sort range) { Contract.Requires(range != null); Contract.Requires(Contract.ForAll(domain, d => d != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(domain); CheckContextMatch(range); return new FuncDecl(this, prefix, domain, range); } /// /// Creates a new constant function declaration. /// public FuncDecl MkConstDecl(Symbol name, Sort range) { Contract.Requires(name != null); Contract.Requires(range != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(name); CheckContextMatch(range); return new FuncDecl(this, name, null, range); } /// /// Creates a new constant function declaration. /// public FuncDecl MkConstDecl(string name, Sort range) { Contract.Requires(range != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(range); return new FuncDecl(this, MkSymbol(name), null, range); } /// /// Creates a fresh constant function declaration with a name prefixed with . /// /// /// public FuncDecl MkFreshConstDecl(string prefix, Sort range) { Contract.Requires(range != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(range); return new FuncDecl(this, prefix, null, range); } #endregion #region Bound Variables /// /// Creates a new bound variable. /// /// The de-Bruijn index of the variable /// The sort of the variable public Expr MkBound(uint index, Sort ty) { Contract.Requires(ty != null); Contract.Ensures(Contract.Result() != null); return Expr.Create(this, Native.Z3_mk_bound(nCtx, index, ty.NativeObject)); } #endregion #region Quantifier Patterns /// /// Create a quantifier pattern. /// public Pattern MkPattern(params Expr[] terms) { Contract.Requires(terms != null); if (terms.Length == 0) throw new Z3Exception("Cannot create a pattern from zero terms"); Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); IntPtr[] termsNative = AST.ArrayToNative(terms); return new Pattern(this, Native.Z3_mk_pattern(nCtx, (uint)terms.Length, termsNative)); } #endregion #region Constants /// /// Creates a new Constant of sort and named . /// public Expr MkConst(Symbol name, Sort range) { Contract.Requires(name != null); Contract.Requires(range != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(name); CheckContextMatch(range); return Expr.Create(this, Native.Z3_mk_const(nCtx, name.NativeObject, range.NativeObject)); } /// /// Creates a new Constant of sort and named . /// public Expr MkConst(string name, Sort range) { Contract.Requires(range != null); Contract.Ensures(Contract.Result() != null); return MkConst(MkSymbol(name), range); } /// /// Creates a fresh Constant of sort and a /// name prefixed with . /// public Expr MkFreshConst(string prefix, Sort range) { Contract.Requires(range != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(range); return Expr.Create(this, Native.Z3_mk_fresh_const(nCtx, prefix, range.NativeObject)); } /// /// Creates a fresh constant from the FuncDecl . /// /// A decl of a 0-arity function public Expr MkConst(FuncDecl f) { Contract.Requires(f != null); Contract.Ensures(Contract.Result() != null); return MkApp(f); } /// /// Create a Boolean constant. /// public BoolExpr MkBoolConst(Symbol name) { Contract.Requires(name != null); Contract.Ensures(Contract.Result() != null); return (BoolExpr)MkConst(name, BoolSort); } /// /// Create a Boolean constant. /// public BoolExpr MkBoolConst(string name) { Contract.Ensures(Contract.Result() != null); return (BoolExpr)MkConst(MkSymbol(name), BoolSort); } /// /// Creates an integer constant. /// public IntExpr MkIntConst(Symbol name) { Contract.Requires(name != null); Contract.Ensures(Contract.Result() != null); return (IntExpr)MkConst(name, IntSort); } /// /// Creates an integer constant. /// public IntExpr MkIntConst(string name) { Contract.Requires(name != null); Contract.Ensures(Contract.Result() != null); return (IntExpr)MkConst(name, IntSort); } /// /// Creates a real constant. /// public RealExpr MkRealConst(Symbol name) { Contract.Requires(name != null); Contract.Ensures(Contract.Result() != null); return (RealExpr)MkConst(name, RealSort); } /// /// Creates a real constant. /// public RealExpr MkRealConst(string name) { Contract.Ensures(Contract.Result() != null); return (RealExpr)MkConst(name, RealSort); } /// /// Creates a bit-vector constant. /// public BitVecExpr MkBVConst(Symbol name, uint size) { Contract.Requires(name != null); Contract.Ensures(Contract.Result() != null); return (BitVecExpr)MkConst(name, MkBitVecSort(size)); } /// /// Creates a bit-vector constant. /// public BitVecExpr MkBVConst(string name, uint size) { Contract.Ensures(Contract.Result() != null); return (BitVecExpr)MkConst(name, MkBitVecSort(size)); } #endregion #region Terms /// /// Create a new function application. /// public Expr MkApp(FuncDecl f, params Expr[] args) { Contract.Requires(f != null); Contract.Requires(args == null || Contract.ForAll(args, a => a != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(f); CheckContextMatch(args); return Expr.Create(this, f, args); } #region Propositional /// /// The true Term. /// public BoolExpr MkTrue() { Contract.Ensures(Contract.Result() != null); return new BoolExpr(this, Native.Z3_mk_true(nCtx)); } /// /// The false Term. /// public BoolExpr MkFalse() { Contract.Ensures(Contract.Result() != null); return new BoolExpr(this, Native.Z3_mk_false(nCtx)); } /// /// Creates a Boolean value. /// public BoolExpr MkBool(bool value) { Contract.Ensures(Contract.Result() != null); return value ? MkTrue() : MkFalse(); } /// /// Creates the equality = . /// public BoolExpr MkEq(Expr x, Expr y) { Contract.Requires(x != null); Contract.Requires(y != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(x); CheckContextMatch(y); return new BoolExpr(this, Native.Z3_mk_eq(nCtx, x.NativeObject, y.NativeObject)); } /// /// Creates a distinct term. /// public BoolExpr MkDistinct(params Expr[] args) { Contract.Requires(args != null); Contract.Requires(Contract.ForAll(args, a => a != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(args); return new BoolExpr(this, Native.Z3_mk_distinct(nCtx, (uint)args.Length, AST.ArrayToNative(args))); } /// /// Mk an expression representing not(a). /// public BoolExpr MkNot(BoolExpr a) { Contract.Requires(a != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(a); return new BoolExpr(this, Native.Z3_mk_not(nCtx, a.NativeObject)); } /// /// Create an expression representing an if-then-else: ite(t1, t2, t3). /// /// An expression with Boolean sort /// An expression /// An expression with the same sort as public Expr MkITE(BoolExpr t1, Expr t2, Expr t3) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Requires(t3 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); CheckContextMatch(t3); return Expr.Create(this, Native.Z3_mk_ite(nCtx, t1.NativeObject, t2.NativeObject, t3.NativeObject)); } /// /// Create an expression representing t1 iff t2. /// public BoolExpr MkIff(BoolExpr t1, BoolExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_iff(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 -> t2. /// public BoolExpr MkImplies(BoolExpr t1, BoolExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_implies(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 xor t2. /// public BoolExpr MkXor(BoolExpr t1, BoolExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_xor(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t[0] and t[1] and .... /// public BoolExpr MkAnd(params BoolExpr[] t) { Contract.Requires(t != null); Contract.Requires(Contract.ForAll(t, a => a != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new BoolExpr(this, Native.Z3_mk_and(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } /// /// Create an expression representing t[0] or t[1] or .... /// public BoolExpr MkOr(params BoolExpr[] t) { Contract.Requires(t != null); Contract.Requires(Contract.ForAll(t, a => a != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new BoolExpr(this, Native.Z3_mk_or(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } #endregion #region Arithmetic /// /// Create an expression representing t[0] + t[1] + .... /// public ArithExpr MkAdd(params ArithExpr[] t) { Contract.Requires(t != null); Contract.Requires(Contract.ForAll(t, a => a != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return (ArithExpr)Expr.Create(this, Native.Z3_mk_add(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } /// /// Create an expression representing t[0] * t[1] * .... /// public ArithExpr MkMul(params ArithExpr[] t) { Contract.Requires(t != null); Contract.Requires(Contract.ForAll(t, a => a != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return (ArithExpr)Expr.Create(this, Native.Z3_mk_mul(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } /// /// Create an expression representing t[0] - t[1] - .... /// public ArithExpr MkSub(params ArithExpr[] t) { Contract.Requires(t != null); Contract.Requires(Contract.ForAll(t, a => a != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return (ArithExpr)Expr.Create(this, Native.Z3_mk_sub(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } /// /// Create an expression representing -t. /// public ArithExpr MkUnaryMinus(ArithExpr t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return (ArithExpr)Expr.Create(this, Native.Z3_mk_unary_minus(nCtx, t.NativeObject)); } /// /// Create an expression representing t1 / t2. /// public ArithExpr MkDiv(ArithExpr t1, ArithExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return (ArithExpr)Expr.Create(this, Native.Z3_mk_div(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 mod t2. /// /// The arguments must have int type. public IntExpr MkMod(IntExpr t1, IntExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new IntExpr(this, Native.Z3_mk_mod(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 rem t2. /// /// The arguments must have int type. public IntExpr MkRem(IntExpr t1, IntExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new IntExpr(this, Native.Z3_mk_rem(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 ^ t2. /// public ArithExpr MkPower(ArithExpr t1, ArithExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return (ArithExpr)Expr.Create(this, Native.Z3_mk_power(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 < t2 /// public BoolExpr MkLt(ArithExpr t1, ArithExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_lt(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 <= t2 /// public BoolExpr MkLe(ArithExpr t1, ArithExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_le(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 > t2 /// public BoolExpr MkGt(ArithExpr t1, ArithExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_gt(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 >= t2 /// public BoolExpr MkGe(ArithExpr t1, ArithExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_ge(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Coerce an integer to a real. /// /// /// There is also a converse operation exposed. It follows the semantics prescribed by the SMT-LIB standard. /// /// You can take the floor of a real by creating an auxiliary integer Term k and /// and asserting MakeInt2Real(k) <= t1 < MkInt2Real(k)+1. /// The argument must be of integer sort. /// public RealExpr MkInt2Real(IntExpr t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new RealExpr(this, Native.Z3_mk_int2real(nCtx, t.NativeObject)); } /// /// Coerce a real to an integer. /// /// /// The semantics of this function follows the SMT-LIB standard for the function to_int. /// The argument must be of real sort. /// public IntExpr MkReal2Int(RealExpr t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new IntExpr(this, Native.Z3_mk_real2int(nCtx, t.NativeObject)); } /// /// Creates an expression that checks whether a real number is an integer. /// public BoolExpr MkIsInteger(RealExpr t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new BoolExpr(this, Native.Z3_mk_is_int(nCtx, t.NativeObject)); } #endregion #region Bit-vectors /// /// Bitwise negation. /// /// The argument must have a bit-vector sort. public BitVecExpr MkBVNot(BitVecExpr t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_bvnot(nCtx, t.NativeObject)); } /// /// Take conjunction of bits in a vector, return vector of length 1. /// /// The argument must have a bit-vector sort. public BitVecExpr MkBVRedAND(BitVecExpr t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_bvredand(nCtx, t.NativeObject)); } /// /// Take disjunction of bits in a vector, return vector of length 1. /// /// The argument must have a bit-vector sort. public BitVecExpr MkBVRedOR(BitVecExpr t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_bvredor(nCtx, t.NativeObject)); } /// /// Bitwise conjunction. /// /// The arguments must have a bit-vector sort. public BitVecExpr MkBVAND(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvand(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Bitwise disjunction. /// /// The arguments must have a bit-vector sort. public BitVecExpr MkBVOR(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvor(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Bitwise XOR. /// /// The arguments must have a bit-vector sort. public BitVecExpr MkBVXOR(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvxor(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Bitwise NAND. /// /// The arguments must have a bit-vector sort. public BitVecExpr MkBVNAND(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvnand(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Bitwise NOR. /// /// The arguments must have a bit-vector sort. public BitVecExpr MkBVNOR(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvnor(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Bitwise XNOR. /// /// The arguments must have a bit-vector sort. public BitVecExpr MkBVXNOR(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvxnor(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Standard two's complement unary minus. /// /// The arguments must have a bit-vector sort. public BitVecExpr MkBVNeg(BitVecExpr t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_bvneg(nCtx, t.NativeObject)); } /// /// Two's complement addition. /// /// The arguments must have the same bit-vector sort. public BitVecExpr MkBVAdd(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvadd(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Two's complement subtraction. /// /// The arguments must have the same bit-vector sort. public BitVecExpr MkBVSub(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvsub(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Two's complement multiplication. /// /// The arguments must have the same bit-vector sort. public BitVecExpr MkBVMul(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvmul(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Unsigned division. /// /// /// It is defined as the floor of t1/t2 if \c t2 is /// different from zero. If t2 is zero, then the result /// is undefined. /// The arguments must have the same bit-vector sort. /// public BitVecExpr MkBVUDiv(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvudiv(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Signed division. /// /// /// It is defined in the following way: /// /// - The \c floor of t1/t2 if \c t2 is different from zero, and t1*t2 >= 0. /// /// - The \c ceiling of t1/t2 if \c t2 is different from zero, and t1*t2 < 0. /// /// If t2 is zero, then the result is undefined. /// The arguments must have the same bit-vector sort. /// public BitVecExpr MkBVSDiv(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvsdiv(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Unsigned remainder. /// /// /// It is defined as t1 - (t1 /u t2) * t2, where /u represents unsigned division. /// If t2 is zero, then the result is undefined. /// The arguments must have the same bit-vector sort. /// public BitVecExpr MkBVURem(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvurem(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Signed remainder. /// /// /// It is defined as t1 - (t1 /s t2) * t2, where /s represents signed division. /// The most significant bit (sign) of the result is equal to the most significant bit of \c t1. /// /// If t2 is zero, then the result is undefined. /// The arguments must have the same bit-vector sort. /// public BitVecExpr MkBVSRem(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvsrem(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Two's complement signed remainder (sign follows divisor). /// /// /// If t2 is zero, then the result is undefined. /// The arguments must have the same bit-vector sort. /// public BitVecExpr MkBVSMod(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvsmod(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Unsigned less-than /// /// /// The arguments must have the same bit-vector sort. /// public BoolExpr MkBVULT(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvult(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Two's complement signed less-than /// /// /// The arguments must have the same bit-vector sort. /// public BoolExpr MkBVSLT(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvslt(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Unsigned less-than or equal to. /// /// /// The arguments must have the same bit-vector sort. /// public BoolExpr MkBVULE(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvule(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Two's complement signed less-than or equal to. /// /// /// The arguments must have the same bit-vector sort. /// public BoolExpr MkBVSLE(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvsle(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Unsigned greater than or equal to. /// /// /// The arguments must have the same bit-vector sort. /// public BoolExpr MkBVUGE(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvuge(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Two's complement signed greater than or equal to. /// /// /// The arguments must have the same bit-vector sort. /// public BoolExpr MkBVSGE(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvsge(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Unsigned greater-than. /// /// /// The arguments must have the same bit-vector sort. /// public BoolExpr MkBVUGT(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvugt(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Two's complement signed greater-than. /// /// /// The arguments must have the same bit-vector sort. /// public BoolExpr MkBVSGT(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvsgt(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Bit-vector concatenation. /// /// /// The arguments must have a bit-vector sort. /// /// /// The result is a bit-vector of size n1+n2, where n1 (n2) /// is the size of t1 (t2). /// public BitVecExpr MkConcat(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_concat(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Bit-vector extraction. /// /// /// Extract the bits down to from a bitvector of /// size m to yield a new bitvector of size n, where /// n = high - low + 1. /// The argument must have a bit-vector sort. /// public BitVecExpr MkExtract(uint high, uint low, BitVecExpr t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_extract(nCtx, high, low, t.NativeObject)); } /// /// Bit-vector sign extension. /// /// /// Sign-extends the given bit-vector to the (signed) equivalent bitvector of /// size m+i, where \c m is the size of the given bit-vector. /// The argument must have a bit-vector sort. /// public BitVecExpr MkSignExt(uint i, BitVecExpr t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_sign_ext(nCtx, i, t.NativeObject)); } /// /// Bit-vector zero extension. /// /// /// Extend the given bit-vector with zeros to the (unsigned) equivalent /// bitvector of size m+i, where \c m is the size of the /// given bit-vector. /// The argument must have a bit-vector sort. /// public BitVecExpr MkZeroExt(uint i, BitVecExpr t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_zero_ext(nCtx, i, t.NativeObject)); } /// /// Bit-vector repetition. /// /// /// The argument must have a bit-vector sort. /// public BitVecExpr MkRepeat(uint i, BitVecExpr t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_repeat(nCtx, i, t.NativeObject)); } /// /// Shift left. /// /// /// It is equivalent to multiplication by 2^x where \c x is the value of . /// /// NB. The semantics of shift operations varies between environments. This /// definition does not necessarily capture directly the semantics of the /// programming language or assembly architecture you are modeling. /// /// The arguments must have a bit-vector sort. /// public BitVecExpr MkBVSHL(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvshl(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Logical shift right /// /// /// It is equivalent to unsigned division by 2^x where \c x is the value of . /// /// NB. The semantics of shift operations varies between environments. This /// definition does not necessarily capture directly the semantics of the /// programming language or assembly architecture you are modeling. /// /// The arguments must have a bit-vector sort. /// public BitVecExpr MkBVLSHR(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvlshr(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Arithmetic shift right /// /// /// It is like logical shift right except that the most significant /// bits of the result always copy the most significant bit of the /// second argument. /// /// NB. The semantics of shift operations varies between environments. This /// definition does not necessarily capture directly the semantics of the /// programming language or assembly architecture you are modeling. /// /// The arguments must have a bit-vector sort. /// public BitVecExpr MkBVASHR(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvashr(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Rotate Left. /// /// /// Rotate bits of \c t to the left \c i times. /// The argument must have a bit-vector sort. /// public BitVecExpr MkBVRotateLeft(uint i, BitVecExpr t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_rotate_left(nCtx, i, t.NativeObject)); } /// /// Rotate Right. /// /// /// Rotate bits of \c t to the right \c i times. /// The argument must have a bit-vector sort. /// public BitVecExpr MkBVRotateRight(uint i, BitVecExpr t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_rotate_right(nCtx, i, t.NativeObject)); } /// /// Rotate Left. /// /// /// Rotate bits of to the left times. /// The arguments must have the same bit-vector sort. /// public BitVecExpr MkBVRotateLeft(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_ext_rotate_left(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Rotate Right. /// /// /// Rotate bits of to the right times. /// The arguments must have the same bit-vector sort. /// public BitVecExpr MkBVRotateRight(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_ext_rotate_right(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an bit bit-vector from the integer argument . /// /// /// NB. This function is essentially treated as uninterpreted. /// So you cannot expect Z3 to precisely reflect the semantics of this function /// when solving constraints with this function. /// /// The argument must be of integer sort. /// public BitVecExpr MkInt2BV(uint n, IntExpr t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_int2bv(nCtx, n, t.NativeObject)); } /// /// Create an integer from the bit-vector argument . /// /// /// If \c is_signed is false, then the bit-vector \c t1 is treated as unsigned. /// So the result is non-negative and in the range [0..2^N-1], where /// N are the number of bits in . /// If \c is_signed is true, \c t1 is treated as a signed bit-vector. /// /// NB. This function is essentially treated as uninterpreted. /// So you cannot expect Z3 to precisely reflect the semantics of this function /// when solving constraints with this function. /// /// The argument must be of bit-vector sort. /// public IntExpr MkBV2Int(BitVecExpr t, bool signed) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new IntExpr(this, Native.Z3_mk_bv2int(nCtx, t.NativeObject, (signed) ? 1 : 0)); } /// /// Create a predicate that checks that the bit-wise addition does not overflow. /// /// /// The arguments must be of bit-vector sort. /// public BoolExpr MkBVAddNoOverflow(BitVecExpr t1, BitVecExpr t2, bool isSigned) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvadd_no_overflow(nCtx, t1.NativeObject, t2.NativeObject, (isSigned) ? 1 : 0)); } /// /// Create a predicate that checks that the bit-wise addition does not underflow. /// /// /// The arguments must be of bit-vector sort. /// public BoolExpr MkBVAddNoUnderflow(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvadd_no_underflow(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create a predicate that checks that the bit-wise subtraction does not overflow. /// /// /// The arguments must be of bit-vector sort. /// public BoolExpr MkBVSubNoOverflow(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvsub_no_overflow(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create a predicate that checks that the bit-wise subtraction does not underflow. /// /// /// The arguments must be of bit-vector sort. /// public BoolExpr MkBVSubNoUnderflow(BitVecExpr t1, BitVecExpr t2, bool isSigned) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvsub_no_underflow(nCtx, t1.NativeObject, t2.NativeObject, (isSigned) ? 1 : 0)); } /// /// Create a predicate that checks that the bit-wise signed division does not overflow. /// /// /// The arguments must be of bit-vector sort. /// public BoolExpr MkBVSDivNoOverflow(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvsdiv_no_overflow(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create a predicate that checks that the bit-wise negation does not overflow. /// /// /// The arguments must be of bit-vector sort. /// public BoolExpr MkBVNegNoOverflow(BitVecExpr t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new BoolExpr(this, Native.Z3_mk_bvneg_no_overflow(nCtx, t.NativeObject)); } /// /// Create a predicate that checks that the bit-wise multiplication does not overflow. /// /// /// The arguments must be of bit-vector sort. /// public BoolExpr MkBVMulNoOverflow(BitVecExpr t1, BitVecExpr t2, bool isSigned) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvmul_no_overflow(nCtx, t1.NativeObject, t2.NativeObject, (isSigned) ? 1 : 0)); } /// /// Create a predicate that checks that the bit-wise multiplication does not underflow. /// /// /// The arguments must be of bit-vector sort. /// public BoolExpr MkBVMulNoUnderflow(BitVecExpr t1, BitVecExpr t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvmul_no_underflow(nCtx, t1.NativeObject, t2.NativeObject)); } #endregion #region Arrays /// /// Create an array constant. /// public ArrayExpr MkArrayConst(Symbol name, Sort domain, Sort range) { Contract.Requires(name != null); Contract.Requires(domain != null); Contract.Requires(range != null); Contract.Ensures(Contract.Result() != null); return (ArrayExpr)MkConst(name, MkArraySort(domain, range)); } /// /// Create an array constant. /// public ArrayExpr MkArrayConst(string name, Sort domain, Sort range) { Contract.Requires(domain != null); Contract.Requires(range != null); Contract.Ensures(Contract.Result() != null); return (ArrayExpr)MkConst(MkSymbol(name), MkArraySort(domain, range)); } /// /// Array read. /// /// /// The argument a is the array and i is the index /// of the array that gets read. /// /// The node a must have an array sort [domain -> range], /// and i must have the sort domain. /// The sort of the result is range. /// /// /// public Expr MkSelect(ArrayExpr a, Expr i) { Contract.Requires(a != null); Contract.Requires(i != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(a); CheckContextMatch(i); return Expr.Create(this, Native.Z3_mk_select(nCtx, a.NativeObject, i.NativeObject)); } /// /// Array update. /// /// /// The node a must have an array sort [domain -> range], /// i must have sort domain, /// v must have sort range. The sort of the result is [domain -> range]. /// The semantics of this function is given by the theory of arrays described in the SMT-LIB /// standard. See http://smtlib.org for more details. /// The result of this function is an array that is equal to a /// (with respect to select) /// on all indices except for i, where it maps to v /// (and the select of a with /// respect to i may be a different value). /// /// /// public ArrayExpr MkStore(ArrayExpr a, Expr i, Expr v) { Contract.Requires(a != null); Contract.Requires(i != null); Contract.Requires(v != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(a); CheckContextMatch(i); CheckContextMatch(v); return new ArrayExpr(this, Native.Z3_mk_store(nCtx, a.NativeObject, i.NativeObject, v.NativeObject)); } /// /// Create a constant array. /// /// /// The resulting term is an array, such that a selecton an arbitrary index /// produces the value v. /// /// /// public ArrayExpr MkConstArray(Sort domain, Expr v) { Contract.Requires(domain != null); Contract.Requires(v != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(domain); CheckContextMatch(v); return new ArrayExpr(this, Native.Z3_mk_const_array(nCtx, domain.NativeObject, v.NativeObject)); } /// /// Maps f on the argument arrays. /// /// /// Eeach element of args must be of an array sort [domain_i -> range_i]. /// The function declaration f must have type range_1 .. range_n -> range. /// v must have sort range. The sort of the result is [domain_i -> range]. /// /// /// /// public ArrayExpr MkMap(FuncDecl f, params ArrayExpr[] args) { Contract.Requires(f != null); Contract.Requires(args == null || Contract.ForAll(args, a => a != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(f); CheckContextMatch(args); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_map(nCtx, f.NativeObject, AST.ArrayLength(args), AST.ArrayToNative(args))); } /// /// Access the array default value. /// /// /// Produces the default range value, for arrays that can be represented as /// finite maps with a default range value. /// public Expr MkTermArray(ArrayExpr array) { Contract.Requires(array != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(array); return Expr.Create(this, Native.Z3_mk_array_default(nCtx, array.NativeObject)); } #endregion #region Sets /// /// Create a set type. /// public SetSort MkSetSort(Sort ty) { Contract.Requires(ty != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(ty); return new SetSort(this, ty); } /// /// Create an empty set. /// public Expr MkEmptySet(Sort domain) { Contract.Requires(domain != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(domain); return Expr.Create(this, Native.Z3_mk_empty_set(nCtx, domain.NativeObject)); } /// /// Create the full set. /// public Expr MkFullSet(Sort domain) { Contract.Requires(domain != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(domain); return Expr.Create(this, Native.Z3_mk_full_set(nCtx, domain.NativeObject)); } /// /// Add an element to the set. /// public Expr MkSetAdd(Expr set, Expr element) { Contract.Requires(set != null); Contract.Requires(element != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(set); CheckContextMatch(element); return Expr.Create(this, Native.Z3_mk_set_add(nCtx, set.NativeObject, element.NativeObject)); } /// /// Remove an element from a set. /// public Expr MkSetDel(Expr set, Expr element) { Contract.Requires(set != null); Contract.Requires(element != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(set); CheckContextMatch(element); return Expr.Create(this, Native.Z3_mk_set_del(nCtx, set.NativeObject, element.NativeObject)); } /// /// Take the union of a list of sets. /// public Expr MkSetUnion(params Expr[] args) { Contract.Requires(args != null); Contract.Requires(Contract.ForAll(args, a => a != null)); CheckContextMatch(args); return Expr.Create(this, Native.Z3_mk_set_union(nCtx, (uint)args.Length, AST.ArrayToNative(args))); } /// /// Take the intersection of a list of sets. /// public Expr MkSetIntersection(params Expr[] args) { Contract.Requires(args != null); Contract.Requires(Contract.ForAll(args, a => a != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(args); return Expr.Create(this, Native.Z3_mk_set_intersect(nCtx, (uint)args.Length, AST.ArrayToNative(args))); } /// /// Take the difference between two sets. /// public Expr MkSetDifference(Expr arg1, Expr arg2) { Contract.Requires(arg1 != null); Contract.Requires(arg2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(arg1); CheckContextMatch(arg2); return Expr.Create(this, Native.Z3_mk_set_difference(nCtx, arg1.NativeObject, arg2.NativeObject)); } /// /// Take the complement of a set. /// public Expr MkSetComplement(Expr arg) { Contract.Requires(arg != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(arg); return Expr.Create(this, Native.Z3_mk_set_complement(nCtx, arg.NativeObject)); } /// /// Check for set membership. /// public Expr MkSetMembership(Expr elem, Expr set) { Contract.Requires(elem != null); Contract.Requires(set != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(elem); CheckContextMatch(set); return Expr.Create(this, Native.Z3_mk_set_member(nCtx, elem.NativeObject, set.NativeObject)); } /// /// Check for subsetness of sets. /// public Expr MkSetSubset(Expr arg1, Expr arg2) { Contract.Requires(arg1 != null); Contract.Requires(arg2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(arg1); CheckContextMatch(arg2); return Expr.Create(this, Native.Z3_mk_set_subset(nCtx, arg1.NativeObject, arg2.NativeObject)); } #endregion #region Pseudo-Boolean constraints /// /// Create an at-most-k constraint. /// public BoolExpr MkAtMost(BoolExpr[] args, uint k) { Contract.Requires(args != null); Contract.Requires(Contract.Result() != null); CheckContextMatch(args); return new BoolExpr(this, Native.Z3_mk_atmost(nCtx, (uint) args.Length, AST.ArrayToNative(args), k)); } /// /// Create a pseudo-Boolean less-or-equal constraint. /// public BoolExpr MkPBLe(int[] coeffs, BoolExpr[] args, int k) { Contract.Requires(args != null); Contract.Requires(coeffs != null); Contract.Requires(args.Length == coeffs.Length); Contract.Requires(Contract.Result() != null); CheckContextMatch(args); return new BoolExpr(this, Native.Z3_mk_pble(nCtx, (uint) args.Length, AST.ArrayToNative(args), coeffs, k)); } #endregion #region Numerals #region General Numerals /// /// Create a Term of a given sort. /// /// A string representing the Term value in decimal notation. If the given sort is a real, then the Term can be a rational, that is, a string of the form [num]* / [num]*. /// The sort of the numeral. In the current implementation, the given sort can be an int, real, or bit-vectors of arbitrary size. /// A Term with value and sort public Expr MkNumeral(string v, Sort ty) { Contract.Requires(ty != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(ty); return Expr.Create(this, Native.Z3_mk_numeral(nCtx, v, ty.NativeObject)); } /// /// Create a Term of a given sort. This function can be use to create numerals that fit in a machine integer. /// It is slightly faster than MakeNumeral since it is not necessary to parse a string. /// /// Value of the numeral /// Sort of the numeral /// A Term with value and type public Expr MkNumeral(int v, Sort ty) { Contract.Requires(ty != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(ty); return Expr.Create(this, Native.Z3_mk_int(nCtx, v, ty.NativeObject)); } /// /// Create a Term of a given sort. This function can be use to create numerals that fit in a machine integer. /// It is slightly faster than MakeNumeral since it is not necessary to parse a string. /// /// Value of the numeral /// Sort of the numeral /// A Term with value and type public Expr MkNumeral(uint v, Sort ty) { Contract.Requires(ty != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(ty); return Expr.Create(this, Native.Z3_mk_unsigned_int(nCtx, v, ty.NativeObject)); } /// /// Create a Term of a given sort. This function can be use to create numerals that fit in a machine integer. /// It is slightly faster than MakeNumeral since it is not necessary to parse a string. /// /// Value of the numeral /// Sort of the numeral /// A Term with value and type public Expr MkNumeral(long v, Sort ty) { Contract.Requires(ty != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(ty); return Expr.Create(this, Native.Z3_mk_int64(nCtx, v, ty.NativeObject)); } /// /// Create a Term of a given sort. This function can be use to create numerals that fit in a machine integer. /// It is slightly faster than MakeNumeral since it is not necessary to parse a string. /// /// Value of the numeral /// Sort of the numeral /// A Term with value and type public Expr MkNumeral(ulong v, Sort ty) { Contract.Requires(ty != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(ty); return Expr.Create(this, Native.Z3_mk_unsigned_int64(nCtx, v, ty.NativeObject)); } #endregion #region Reals /// /// Create a real from a fraction. /// /// numerator of rational. /// denominator of rational. /// A Term with value / and sort Real /// public RatNum MkReal(int num, int den) { if (den == 0) throw new Z3Exception("Denominator is zero"); Contract.Ensures(Contract.Result() != null); Contract.EndContractBlock(); return new RatNum(this, Native.Z3_mk_real(nCtx, num, den)); } /// /// Create a real numeral. /// /// A string representing the Term value in decimal notation. /// A Term with value and sort Real public RatNum MkReal(string v) { Contract.Ensures(Contract.Result() != null); return new RatNum(this, Native.Z3_mk_numeral(nCtx, v, RealSort.NativeObject)); } /// /// Create a real numeral. /// /// value of the numeral. /// A Term with value and sort Real public RatNum MkReal(int v) { Contract.Ensures(Contract.Result() != null); return new RatNum(this, Native.Z3_mk_int(nCtx, v, RealSort.NativeObject)); } /// /// Create a real numeral. /// /// value of the numeral. /// A Term with value and sort Real public RatNum MkReal(uint v) { Contract.Ensures(Contract.Result() != null); return new RatNum(this, Native.Z3_mk_unsigned_int(nCtx, v, RealSort.NativeObject)); } /// /// Create a real numeral. /// /// value of the numeral. /// A Term with value and sort Real public RatNum MkReal(long v) { Contract.Ensures(Contract.Result() != null); return new RatNum(this, Native.Z3_mk_int64(nCtx, v, RealSort.NativeObject)); } /// /// Create a real numeral. /// /// value of the numeral. /// A Term with value and sort Real public RatNum MkReal(ulong v) { Contract.Ensures(Contract.Result() != null); return new RatNum(this, Native.Z3_mk_unsigned_int64(nCtx, v, RealSort.NativeObject)); } #endregion #region Integers /// /// Create an integer numeral. /// /// A string representing the Term value in decimal notation. public IntNum MkInt(string v) { Contract.Ensures(Contract.Result() != null); return new IntNum(this, Native.Z3_mk_numeral(nCtx, v, IntSort.NativeObject)); } /// /// Create an integer numeral. /// /// value of the numeral. /// A Term with value and sort Integer public IntNum MkInt(int v) { Contract.Ensures(Contract.Result() != null); return new IntNum(this, Native.Z3_mk_int(nCtx, v, IntSort.NativeObject)); } /// /// Create an integer numeral. /// /// value of the numeral. /// A Term with value and sort Integer public IntNum MkInt(uint v) { Contract.Ensures(Contract.Result() != null); return new IntNum(this, Native.Z3_mk_unsigned_int(nCtx, v, IntSort.NativeObject)); } /// /// Create an integer numeral. /// /// value of the numeral. /// A Term with value and sort Integer public IntNum MkInt(long v) { Contract.Ensures(Contract.Result() != null); return new IntNum(this, Native.Z3_mk_int64(nCtx, v, IntSort.NativeObject)); } /// /// Create an integer numeral. /// /// value of the numeral. /// A Term with value and sort Integer public IntNum MkInt(ulong v) { Contract.Ensures(Contract.Result() != null); return new IntNum(this, Native.Z3_mk_unsigned_int64(nCtx, v, IntSort.NativeObject)); } #endregion #region Bit-vectors /// /// Create a bit-vector numeral. /// /// A string representing the value in decimal notation. /// the size of the bit-vector public BitVecNum MkBV(string v, uint size) { Contract.Ensures(Contract.Result() != null); return (BitVecNum)MkNumeral(v, MkBitVecSort(size)); } /// /// Create a bit-vector numeral. /// /// value of the numeral. /// the size of the bit-vector public BitVecNum MkBV(int v, uint size) { Contract.Ensures(Contract.Result() != null); return (BitVecNum)MkNumeral(v, MkBitVecSort(size)); } /// /// Create a bit-vector numeral. /// /// value of the numeral. /// the size of the bit-vector public BitVecNum MkBV(uint v, uint size) { Contract.Ensures(Contract.Result() != null); return (BitVecNum)MkNumeral(v, MkBitVecSort(size)); } /// /// Create a bit-vector numeral. /// /// value of the numeral. /// the size of the bit-vector public BitVecNum MkBV(long v, uint size) { Contract.Ensures(Contract.Result() != null); return (BitVecNum)MkNumeral(v, MkBitVecSort(size)); } /// /// Create a bit-vector numeral. /// /// value of the numeral. /// the size of the bit-vector public BitVecNum MkBV(ulong v, uint size) { Contract.Ensures(Contract.Result() != null); return (BitVecNum)MkNumeral(v, MkBitVecSort(size)); } #endregion #endregion // Numerals #region Quantifiers /// /// Create a universal Quantifier. /// /// /// Creates a forall formula, where is the weight, /// is an array of patterns, is an array /// with the sorts of the bound variables, is an array with the /// 'names' of the bound variables, and is the body of the /// quantifier. Quantifiers are associated with weights indicating /// the importance of using the quantifier during instantiation. /// /// the sorts of the bound variables. /// names of the bound variables /// the body of the quantifier. /// quantifiers are associated with weights indicating the importance of using the quantifier during instantiation. By default, pass the weight 0. /// array containing the patterns created using MkPattern. /// array containing the anti-patterns created using MkPattern. /// optional symbol to track quantifier. /// optional symbol to track skolem constants. public Quantifier MkForall(Sort[] sorts, Symbol[] names, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) { Contract.Requires(sorts != null); Contract.Requires(names != null); Contract.Requires(body != null); Contract.Requires(sorts.Length == names.Length); Contract.Requires(Contract.ForAll(sorts, s => s != null)); Contract.Requires(Contract.ForAll(names, n => n != null)); Contract.Requires(patterns == null || Contract.ForAll(patterns, p => p != null)); Contract.Requires(noPatterns == null || Contract.ForAll(noPatterns, np => np != null)); Contract.Ensures(Contract.Result() != null); return new Quantifier(this, true, sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); } /// /// Create a universal Quantifier. /// public Quantifier MkForall(Expr[] boundConstants, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) { Contract.Requires(body != null); Contract.Requires(boundConstants == null || Contract.ForAll(boundConstants, b => b != null)); Contract.Requires(patterns == null || Contract.ForAll(patterns, p => p != null)); Contract.Requires(noPatterns == null || Contract.ForAll(noPatterns, np => np != null)); Contract.Ensures(Contract.Result() != null); return new Quantifier(this, true, boundConstants, body, weight, patterns, noPatterns, quantifierID, skolemID); } /// /// Create an existential Quantifier. /// /// public Quantifier MkExists(Sort[] sorts, Symbol[] names, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) { Contract.Requires(sorts != null); Contract.Requires(names != null); Contract.Requires(body != null); Contract.Requires(sorts.Length == names.Length); Contract.Requires(Contract.ForAll(sorts, s => s != null)); Contract.Requires(Contract.ForAll(names, n => n != null)); Contract.Requires(patterns == null || Contract.ForAll(patterns, p => p != null)); Contract.Requires(noPatterns == null || Contract.ForAll(noPatterns, np => np != null)); Contract.Ensures(Contract.Result() != null); return new Quantifier(this, false, sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); } /// /// Create an existential Quantifier. /// public Quantifier MkExists(Expr[] boundConstants, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) { Contract.Requires(body != null); Contract.Requires(boundConstants == null || Contract.ForAll(boundConstants, n => n != null)); Contract.Requires(patterns == null || Contract.ForAll(patterns, p => p != null)); Contract.Requires(noPatterns == null || Contract.ForAll(noPatterns, np => np != null)); Contract.Ensures(Contract.Result() != null); return new Quantifier(this, false, boundConstants, body, weight, patterns, noPatterns, quantifierID, skolemID); } /// /// Create a Quantifier. /// public Quantifier MkQuantifier(bool universal, Sort[] sorts, Symbol[] names, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) { Contract.Requires(body != null); Contract.Requires(names != null); Contract.Requires(sorts != null); Contract.Requires(sorts.Length == names.Length); Contract.Requires(Contract.ForAll(sorts, s => s != null)); Contract.Requires(Contract.ForAll(names, n => n != null)); Contract.Requires(patterns == null || Contract.ForAll(patterns, p => p != null)); Contract.Requires(noPatterns == null || Contract.ForAll(noPatterns, np => np != null)); Contract.Ensures(Contract.Result() != null); if (universal) return MkForall(sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); else return MkExists(sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); } /// /// Create a Quantifier. /// public Quantifier MkQuantifier(bool universal, Expr[] boundConstants, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) { Contract.Requires(body != null); Contract.Requires(boundConstants == null || Contract.ForAll(boundConstants, n => n != null)); Contract.Requires(patterns == null || Contract.ForAll(patterns, p => p != null)); Contract.Requires(noPatterns == null || Contract.ForAll(noPatterns, np => np != null)); Contract.Ensures(Contract.Result() != null); if (universal) return MkForall(boundConstants, body, weight, patterns, noPatterns, quantifierID, skolemID); else return MkExists(boundConstants, body, weight, patterns, noPatterns, quantifierID, skolemID); } #endregion #endregion // Expr #region Options /// /// Selects the format used for pretty-printing expressions. /// /// /// The default mode for pretty printing expressions is to produce /// SMT-LIB style output where common subexpressions are printed /// at each occurrence. The mode is called Z3_PRINT_SMTLIB_FULL. /// To print shared common subexpressions only once, /// use the Z3_PRINT_LOW_LEVEL mode. /// To print in way that conforms to SMT-LIB standards and uses let /// expressions to share common sub-expressions use Z3_PRINT_SMTLIB_COMPLIANT. /// /// /// /// /// public Z3_ast_print_mode PrintMode { set { Native.Z3_set_ast_print_mode(nCtx, (uint)value); } } #endregion #region SMT Files & Strings /// /// Convert a benchmark into an SMT-LIB formatted string. /// /// Name of the benchmark. The argument is optional. /// The benchmark logic. /// The status string (sat, unsat, or unknown) /// Other attributes, such as source, difficulty or category. /// Auxiliary assumptions. /// Formula to be checked for consistency in conjunction with assumptions. /// A string representation of the benchmark. public string BenchmarkToSMTString(string name, string logic, string status, string attributes, BoolExpr[] assumptions, BoolExpr formula) { Contract.Requires(assumptions != null); Contract.Requires(formula != null); Contract.Ensures(Contract.Result() != null); return Native.Z3_benchmark_to_smtlib_string(nCtx, name, logic, status, attributes, (uint)assumptions.Length, AST.ArrayToNative(assumptions), formula.NativeObject); } /// /// Parse the given string using the SMT-LIB parser. /// /// /// The symbol table of the parser can be initialized using the given sorts and declarations. /// The symbols in the arrays and /// don't need to match the names of the sorts and declarations in the arrays /// and . This is a useful feature since we can use arbitrary names to /// reference sorts and declarations. /// public void ParseSMTLIBString(string str, Symbol[] sortNames = null, Sort[] sorts = null, Symbol[] declNames = null, FuncDecl[] decls = null) { uint csn = Symbol.ArrayLength(sortNames); uint cs = Sort.ArrayLength(sorts); uint cdn = Symbol.ArrayLength(declNames); uint cd = AST.ArrayLength(decls); if (csn != cs || cdn != cd) throw new Z3Exception("Argument size mismatch"); Native.Z3_parse_smtlib_string(nCtx, str, AST.ArrayLength(sorts), Symbol.ArrayToNative(sortNames), AST.ArrayToNative(sorts), AST.ArrayLength(decls), Symbol.ArrayToNative(declNames), AST.ArrayToNative(decls)); } /// /// Parse the given file using the SMT-LIB parser. /// /// public void ParseSMTLIBFile(string fileName, Symbol[] sortNames = null, Sort[] sorts = null, Symbol[] declNames = null, FuncDecl[] decls = null) { uint csn = Symbol.ArrayLength(sortNames); uint cs = Sort.ArrayLength(sorts); uint cdn = Symbol.ArrayLength(declNames); uint cd = AST.ArrayLength(decls); if (csn != cs || cdn != cd) throw new Z3Exception("Argument size mismatch"); Native.Z3_parse_smtlib_file(nCtx, fileName, AST.ArrayLength(sorts), Symbol.ArrayToNative(sortNames), AST.ArrayToNative(sorts), AST.ArrayLength(decls), Symbol.ArrayToNative(declNames), AST.ArrayToNative(decls)); } /// /// The number of SMTLIB formulas parsed by the last call to ParseSMTLIBString or ParseSMTLIBFile. /// public uint NumSMTLIBFormulas { get { return Native.Z3_get_smtlib_num_formulas(nCtx); } } /// /// The formulas parsed by the last call to ParseSMTLIBString or ParseSMTLIBFile. /// public BoolExpr[] SMTLIBFormulas { get { Contract.Ensures(Contract.Result() != null); uint n = NumSMTLIBFormulas; BoolExpr[] res = new BoolExpr[n]; for (uint i = 0; i < n; i++) res[i] = (BoolExpr)Expr.Create(this, Native.Z3_get_smtlib_formula(nCtx, i)); return res; } } /// /// The number of SMTLIB assumptions parsed by the last call to ParseSMTLIBString or ParseSMTLIBFile. /// public uint NumSMTLIBAssumptions { get { return Native.Z3_get_smtlib_num_assumptions(nCtx); } } /// /// The assumptions parsed by the last call to ParseSMTLIBString or ParseSMTLIBFile. /// public BoolExpr[] SMTLIBAssumptions { get { Contract.Ensures(Contract.Result() != null); uint n = NumSMTLIBAssumptions; BoolExpr[] res = new BoolExpr[n]; for (uint i = 0; i < n; i++) res[i] = (BoolExpr)Expr.Create(this, Native.Z3_get_smtlib_assumption(nCtx, i)); return res; } } /// /// The number of SMTLIB declarations parsed by the last call to ParseSMTLIBString or ParseSMTLIBFile. /// public uint NumSMTLIBDecls { get { return Native.Z3_get_smtlib_num_decls(nCtx); } } /// /// The declarations parsed by the last call to ParseSMTLIBString or ParseSMTLIBFile. /// public FuncDecl[] SMTLIBDecls { get { Contract.Ensures(Contract.Result() != null); uint n = NumSMTLIBDecls; FuncDecl[] res = new FuncDecl[n]; for (uint i = 0; i < n; i++) res[i] = new FuncDecl(this, Native.Z3_get_smtlib_decl(nCtx, i)); return res; } } /// /// The number of SMTLIB sorts parsed by the last call to ParseSMTLIBString or ParseSMTLIBFile. /// public uint NumSMTLIBSorts { get { return Native.Z3_get_smtlib_num_sorts(nCtx); } } /// /// The declarations parsed by the last call to ParseSMTLIBString or ParseSMTLIBFile. /// public Sort[] SMTLIBSorts { get { Contract.Ensures(Contract.Result() != null); uint n = NumSMTLIBSorts; Sort[] res = new Sort[n]; for (uint i = 0; i < n; i++) res[i] = Sort.Create(this, Native.Z3_get_smtlib_sort(nCtx, i)); return res; } } /// /// Parse the given string using the SMT-LIB2 parser. /// /// /// A conjunction of assertions in the scope (up to push/pop) at the end of the string. public BoolExpr ParseSMTLIB2String(string str, Symbol[] sortNames = null, Sort[] sorts = null, Symbol[] declNames = null, FuncDecl[] decls = null) { Contract.Ensures(Contract.Result() != null); uint csn = Symbol.ArrayLength(sortNames); uint cs = Sort.ArrayLength(sorts); uint cdn = Symbol.ArrayLength(declNames); uint cd = AST.ArrayLength(decls); if (csn != cs || cdn != cd) throw new Z3Exception("Argument size mismatch"); return (BoolExpr)Expr.Create(this, Native.Z3_parse_smtlib2_string(nCtx, str, AST.ArrayLength(sorts), Symbol.ArrayToNative(sortNames), AST.ArrayToNative(sorts), AST.ArrayLength(decls), Symbol.ArrayToNative(declNames), AST.ArrayToNative(decls))); } /// /// Parse the given file using the SMT-LIB2 parser. /// /// public BoolExpr ParseSMTLIB2File(string fileName, Symbol[] sortNames = null, Sort[] sorts = null, Symbol[] declNames = null, FuncDecl[] decls = null) { Contract.Ensures(Contract.Result() != null); uint csn = Symbol.ArrayLength(sortNames); uint cs = Sort.ArrayLength(sorts); uint cdn = Symbol.ArrayLength(declNames); uint cd = AST.ArrayLength(decls); if (csn != cs || cdn != cd) throw new Z3Exception("Argument size mismatch"); return (BoolExpr)Expr.Create(this, Native.Z3_parse_smtlib2_file(nCtx, fileName, AST.ArrayLength(sorts), Symbol.ArrayToNative(sortNames), AST.ArrayToNative(sorts), AST.ArrayLength(decls), Symbol.ArrayToNative(declNames), AST.ArrayToNative(decls))); } #endregion #region Goals /// /// Creates a new Goal. /// /// /// Note that the Context must have been created with proof generation support if /// is set to true here. /// /// Indicates whether model generation should be enabled. /// Indicates whether unsat core generation should be enabled. /// Indicates whether proof generation should be enabled. public Goal MkGoal(bool models = true, bool unsatCores = false, bool proofs = false) { Contract.Ensures(Contract.Result() != null); return new Goal(this, models, unsatCores, proofs); } #endregion #region ParameterSets /// /// Creates a new ParameterSet. /// public Params MkParams() { Contract.Ensures(Contract.Result() != null); return new Params(this); } #endregion #region Tactics /// /// The number of supported tactics. /// public uint NumTactics { get { return Native.Z3_get_num_tactics(nCtx); } } /// /// The names of all supported tactics. /// public string[] TacticNames { get { Contract.Ensures(Contract.Result() != null); uint n = NumTactics; string[] res = new string[n]; for (uint i = 0; i < n; i++) res[i] = Native.Z3_get_tactic_name(nCtx, i); return res; } } /// /// Returns a string containing a description of the tactic with the given name. /// public string TacticDescription(string name) { Contract.Ensures(Contract.Result() != null); return Native.Z3_tactic_get_descr(nCtx, name); } /// /// Creates a new Tactic. /// public Tactic MkTactic(string name) { Contract.Ensures(Contract.Result() != null); return new Tactic(this, name); } /// /// Create a tactic that applies to a Goal and /// then to every subgoal produced by . /// public Tactic AndThen(Tactic t1, Tactic t2, params Tactic[] ts) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Requires(ts == null || Contract.ForAll(0, ts.Length, j => ts[j] != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); CheckContextMatch(ts); IntPtr last = IntPtr.Zero; if (ts != null && ts.Length > 0) { last = ts[ts.Length - 1].NativeObject; for (int i = ts.Length - 2; i >= 0; i--) last = Native.Z3_tactic_and_then(nCtx, ts[i].NativeObject, last); } if (last != IntPtr.Zero) { last = Native.Z3_tactic_and_then(nCtx, t2.NativeObject, last); return new Tactic(this, Native.Z3_tactic_and_then(nCtx, t1.NativeObject, last)); } else return new Tactic(this, Native.Z3_tactic_and_then(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create a tactic that applies to a Goal and /// then to every subgoal produced by . /// /// /// Shorthand for AndThen. /// public Tactic Then(Tactic t1, Tactic t2, params Tactic[] ts) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Requires(ts == null || Contract.ForAll(0, ts.Length, j => ts[j] != null)); Contract.Ensures(Contract.Result() != null); return AndThen(t1, t2, ts); } /// /// Create a tactic that first applies to a Goal and /// if it fails then returns the result of applied to the Goal. /// public Tactic OrElse(Tactic t1, Tactic t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new Tactic(this, Native.Z3_tactic_or_else(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create a tactic that applies to a goal for milliseconds. /// /// /// If does not terminate within milliseconds, then it fails. /// public Tactic TryFor(Tactic t, uint ms) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new Tactic(this, Native.Z3_tactic_try_for(nCtx, t.NativeObject, ms)); } /// /// Create a tactic that applies to a given goal if the probe /// evaluates to true. /// /// /// If evaluates to false, then the new tactic behaves like the skip tactic. /// public Tactic When(Probe p, Tactic t) { Contract.Requires(p != null); Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); CheckContextMatch(p); return new Tactic(this, Native.Z3_tactic_when(nCtx, p.NativeObject, t.NativeObject)); } /// /// Create a tactic that applies to a given goal if the probe /// evaluates to true and otherwise. /// public Tactic Cond(Probe p, Tactic t1, Tactic t2) { Contract.Requires(p != null); Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(p); CheckContextMatch(t1); CheckContextMatch(t2); return new Tactic(this, Native.Z3_tactic_cond(nCtx, p.NativeObject, t1.NativeObject, t2.NativeObject)); } /// /// Create a tactic that keeps applying until the goal is not /// modified anymore or the maximum number of iterations is reached. /// public Tactic Repeat(Tactic t, uint max = uint.MaxValue) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new Tactic(this, Native.Z3_tactic_repeat(nCtx, t.NativeObject, max)); } /// /// Create a tactic that just returns the given goal. /// public Tactic Skip() { Contract.Ensures(Contract.Result() != null); return new Tactic(this, Native.Z3_tactic_skip(nCtx)); } /// /// Create a tactic always fails. /// public Tactic Fail() { Contract.Ensures(Contract.Result() != null); return new Tactic(this, Native.Z3_tactic_fail(nCtx)); } /// /// Create a tactic that fails if the probe evaluates to false. /// public Tactic FailIf(Probe p) { Contract.Requires(p != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(p); return new Tactic(this, Native.Z3_tactic_fail_if(nCtx, p.NativeObject)); } /// /// Create a tactic that fails if the goal is not triviall satisfiable (i.e., empty) /// or trivially unsatisfiable (i.e., contains `false'). /// public Tactic FailIfNotDecided() { Contract.Ensures(Contract.Result() != null); return new Tactic(this, Native.Z3_tactic_fail_if_not_decided(nCtx)); } /// /// Create a tactic that applies using the given set of parameters . /// public Tactic UsingParams(Tactic t, Params p) { Contract.Requires(t != null); Contract.Requires(p != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); CheckContextMatch(p); return new Tactic(this, Native.Z3_tactic_using_params(nCtx, t.NativeObject, p.NativeObject)); } /// /// Create a tactic that applies using the given set of parameters . /// /// Alias for UsingParams public Tactic With(Tactic t, Params p) { Contract.Requires(t != null); Contract.Requires(p != null); Contract.Ensures(Contract.Result() != null); return UsingParams(t, p); } /// /// Create a tactic that applies the given tactics in parallel. /// public Tactic ParOr(params Tactic[] t) { Contract.Requires(t == null || Contract.ForAll(t, tactic => tactic != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); return new Tactic(this, Native.Z3_tactic_par_or(nCtx, Tactic.ArrayLength(t), Tactic.ArrayToNative(t))); } /// /// Create a tactic that applies to a given goal and then /// to every subgoal produced by . The subgoals are processed in parallel. /// public Tactic ParAndThen(Tactic t1, Tactic t2) { Contract.Requires(t1 != null); Contract.Requires(t2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(t1); CheckContextMatch(t2); return new Tactic(this, Native.Z3_tactic_par_and_then(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Interrupt the execution of a Z3 procedure. /// /// This procedure can be used to interrupt: solvers, simplifiers and tactics. public void Interrupt() { Native.Z3_interrupt(nCtx); } #endregion #region Probes /// /// The number of supported Probes. /// public uint NumProbes { get { return Native.Z3_get_num_probes(nCtx); } } /// /// The names of all supported Probes. /// public string[] ProbeNames { get { Contract.Ensures(Contract.Result() != null); uint n = NumProbes; string[] res = new string[n]; for (uint i = 0; i < n; i++) res[i] = Native.Z3_get_probe_name(nCtx, i); return res; } } /// /// Returns a string containing a description of the probe with the given name. /// public string ProbeDescription(string name) { Contract.Ensures(Contract.Result() != null); return Native.Z3_probe_get_descr(nCtx, name); } /// /// Creates a new Probe. /// public Probe MkProbe(string name) { Contract.Ensures(Contract.Result() != null); return new Probe(this, name); } /// /// Create a probe that always evaluates to . /// public Probe ConstProbe(double val) { Contract.Ensures(Contract.Result() != null); return new Probe(this, Native.Z3_probe_const(nCtx, val)); } /// /// Create a probe that evaluates to "true" when the value returned by /// is less than the value returned by /// public Probe Lt(Probe p1, Probe p2) { Contract.Requires(p1 != null); Contract.Requires(p2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(p1); CheckContextMatch(p2); return new Probe(this, Native.Z3_probe_lt(nCtx, p1.NativeObject, p2.NativeObject)); } /// /// Create a probe that evaluates to "true" when the value returned by /// is greater than the value returned by /// public Probe Gt(Probe p1, Probe p2) { Contract.Requires(p1 != null); Contract.Requires(p2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(p1); CheckContextMatch(p2); return new Probe(this, Native.Z3_probe_gt(nCtx, p1.NativeObject, p2.NativeObject)); } /// /// Create a probe that evaluates to "true" when the value returned by /// is less than or equal the value returned by /// public Probe Le(Probe p1, Probe p2) { Contract.Requires(p1 != null); Contract.Requires(p2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(p1); CheckContextMatch(p2); return new Probe(this, Native.Z3_probe_le(nCtx, p1.NativeObject, p2.NativeObject)); } /// /// Create a probe that evaluates to "true" when the value returned by /// is greater than or equal the value returned by /// public Probe Ge(Probe p1, Probe p2) { Contract.Requires(p1 != null); Contract.Requires(p2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(p1); CheckContextMatch(p2); return new Probe(this, Native.Z3_probe_ge(nCtx, p1.NativeObject, p2.NativeObject)); } /// /// Create a probe that evaluates to "true" when the value returned by /// is equal to the value returned by /// public Probe Eq(Probe p1, Probe p2) { Contract.Requires(p1 != null); Contract.Requires(p2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(p1); CheckContextMatch(p2); return new Probe(this, Native.Z3_probe_eq(nCtx, p1.NativeObject, p2.NativeObject)); } /// /// Create a probe that evaluates to "true" when the value /// and evaluate to "true". /// public Probe And(Probe p1, Probe p2) { Contract.Requires(p1 != null); Contract.Requires(p2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(p1); CheckContextMatch(p2); return new Probe(this, Native.Z3_probe_and(nCtx, p1.NativeObject, p2.NativeObject)); } /// /// Create a probe that evaluates to "true" when the value /// or evaluate to "true". /// public Probe Or(Probe p1, Probe p2) { Contract.Requires(p1 != null); Contract.Requires(p2 != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(p1); CheckContextMatch(p2); return new Probe(this, Native.Z3_probe_or(nCtx, p1.NativeObject, p2.NativeObject)); } /// /// Create a probe that evaluates to "true" when the value /// does not evaluate to "true". /// public Probe Not(Probe p) { Contract.Requires(p != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(p); return new Probe(this, Native.Z3_probe_not(nCtx, p.NativeObject)); } #endregion #region Solvers /// /// Creates a new (incremental) solver. /// /// /// This solver also uses a set of builtin tactics for handling the first /// check-sat command, and check-sat commands that take more than a given /// number of milliseconds to be solved. /// public Solver MkSolver(Symbol logic = null) { Contract.Ensures(Contract.Result() != null); if (logic == null) return new Solver(this, Native.Z3_mk_solver(nCtx)); else return new Solver(this, Native.Z3_mk_solver_for_logic(nCtx, logic.NativeObject)); } /// /// Creates a new (incremental) solver. /// /// public Solver MkSolver(string logic) { Contract.Ensures(Contract.Result() != null); return MkSolver(MkSymbol(logic)); } /// /// Creates a new (incremental) solver. /// public Solver MkSimpleSolver() { Contract.Ensures(Contract.Result() != null); return new Solver(this, Native.Z3_mk_simple_solver(nCtx)); } /// /// Creates a solver that is implemented using the given tactic. /// /// /// The solver supports the commands Push and Pop, but it /// will always solve each check from scratch. /// public Solver MkSolver(Tactic t) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); return new Solver(this, Native.Z3_mk_solver_from_tactic(nCtx, t.NativeObject)); } #endregion #region Fixedpoints /// /// Create a Fixedpoint context. /// public Fixedpoint MkFixedpoint() { Contract.Ensures(Contract.Result() != null); return new Fixedpoint(this); } #endregion #region Optimization /// /// Create an Optimization context. /// public Optimize MkOptimize() { Contract.Ensures(Contract.Result() != null); return new Optimize(this); } #endregion #region Floating-Point Arithmetic #region Rounding Modes #region RoundingMode Sort /// /// Create the floating-point RoundingMode sort. /// public FPRMSort MkFPRoundingModeSort() { Contract.Ensures(Contract.Result() != null); return new FPRMSort(this); } #endregion #region Numerals /// /// Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. /// public FPRMExpr MkFPRoundNearestTiesToEven() { Contract.Ensures(Contract.Result() != null); return new FPRMExpr(this, Native.Z3_mk_fpa_round_nearest_ties_to_even(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. /// public FPRMNum MkFPRNE() { Contract.Ensures(Contract.Result() != null); return new FPRMNum(this, Native.Z3_mk_fpa_rne(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. /// public FPRMNum MkFPRoundNearestTiesToAway() { Contract.Ensures(Contract.Result() != null); return new FPRMNum(this, Native.Z3_mk_fpa_round_nearest_ties_to_away(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. /// public FPRMNum MkFPRNA() { Contract.Ensures(Contract.Result() != null); return new FPRMNum(this, Native.Z3_mk_fpa_rna(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the RoundTowardPositive rounding mode. /// public FPRMNum MkFPRoundTowardPositive() { Contract.Ensures(Contract.Result() != null); return new FPRMNum(this, Native.Z3_mk_fpa_round_toward_positive(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the RoundTowardPositive rounding mode. /// public FPRMNum MkFPRTP() { Contract.Ensures(Contract.Result() != null); return new FPRMNum(this, Native.Z3_mk_fpa_rtp(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the RoundTowardNegative rounding mode. /// public FPRMNum MkFPRoundTowardNegative() { Contract.Ensures(Contract.Result() != null); return new FPRMNum(this, Native.Z3_mk_fpa_round_toward_negative(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the RoundTowardNegative rounding mode. /// public FPRMNum MkFPRTN() { Contract.Ensures(Contract.Result() != null); return new FPRMNum(this, Native.Z3_mk_fpa_rtn(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the RoundTowardZero rounding mode. /// public FPRMNum MkFPRoundTowardZero() { Contract.Ensures(Contract.Result() != null); return new FPRMNum(this, Native.Z3_mk_fpa_round_toward_zero(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the RoundTowardZero rounding mode. /// public FPRMNum MkFPRTZ() { Contract.Ensures(Contract.Result() != null); return new FPRMNum(this, Native.Z3_mk_fpa_rtz(nCtx)); } #endregion #endregion #region FloatingPoint Sorts /// /// Create a FloatingPoint sort. /// /// exponent bits in the FloatingPoint sort. /// significand bits in the FloatingPoint sort. public FPSort MkFPSort(uint ebits, uint sbits) { Contract.Ensures(Contract.Result() != null); return new FPSort(this, ebits, sbits); } /// /// Create the half-precision (16-bit) FloatingPoint sort. /// public FPSort MkFPSortHalf() { Contract.Ensures(Contract.Result() != null); return new FPSort(this, Native.Z3_mk_fpa_sort_half(nCtx)); } /// /// Create the half-precision (16-bit) FloatingPoint sort. /// public FPSort MkFPSort16() { Contract.Ensures(Contract.Result() != null); return new FPSort(this, Native.Z3_mk_fpa_sort_16(nCtx)); } /// /// Create the single-precision (32-bit) FloatingPoint sort. /// public FPSort MkFPSortSingle() { Contract.Ensures(Contract.Result() != null); return new FPSort(this, Native.Z3_mk_fpa_sort_single(nCtx)); } /// /// Create the single-precision (32-bit) FloatingPoint sort. /// public FPSort MkFPSort32() { Contract.Ensures(Contract.Result() != null); return new FPSort(this, Native.Z3_mk_fpa_sort_32(nCtx)); } /// /// Create the double-precision (64-bit) FloatingPoint sort. /// public FPSort MkFPSortDouble() { Contract.Ensures(Contract.Result() != null); return new FPSort(this, Native.Z3_mk_fpa_sort_double(nCtx)); } /// /// Create the double-precision (64-bit) FloatingPoint sort. /// public FPSort MkFPSort64() { Contract.Ensures(Contract.Result() != null); return new FPSort(this, Native.Z3_mk_fpa_sort_64(nCtx)); } /// /// Create the quadruple-precision (128-bit) FloatingPoint sort. /// public FPSort MkFPSortQuadruple() { Contract.Ensures(Contract.Result() != null); return new FPSort(this, Native.Z3_mk_fpa_sort_quadruple(nCtx)); } /// /// Create the quadruple-precision (128-bit) FloatingPoint sort. /// public FPSort MkFPSort128() { Contract.Ensures(Contract.Result() != null); return new FPSort(this, Native.Z3_mk_fpa_sort_128(nCtx)); } #endregion #region Numerals /// /// Create a NaN of sort s. /// /// FloatingPoint sort. public FPNum MkFPNaN(FPSort s) { Contract.Ensures(Contract.Result() != null); return new FPNum(this, Native.Z3_mk_fpa_nan(nCtx, s.NativeObject)); } /// /// Create a floating-point infinity of sort s. /// /// FloatingPoint sort. /// indicates whether the result should be negative. public FPNum MkFPInf(FPSort s, bool negative) { Contract.Ensures(Contract.Result() != null); return new FPNum(this, Native.Z3_mk_fpa_inf(nCtx, s.NativeObject, negative ? 1 : 0)); } /// /// Create a floating-point zero of sort s. /// /// FloatingPoint sort. /// indicates whether the result should be negative. public FPNum MkFPZero(FPSort s, bool negative) { Contract.Ensures(Contract.Result() != null); return new FPNum(this, Native.Z3_mk_fpa_zero(nCtx, s.NativeObject, negative ? 1 : 0)); } /// /// Create a numeral of FloatingPoint sort from a float. /// /// numeral value. /// FloatingPoint sort. public FPNum MkFPNumeral(float v, FPSort s) { Contract.Ensures(Contract.Result() != null); return new FPNum(this, Native.Z3_mk_fpa_numeral_float(nCtx, v, s.NativeObject)); } /// /// Create a numeral of FloatingPoint sort from a float. /// /// numeral value. /// FloatingPoint sort. public FPNum MkFPNumeral(double v, FPSort s) { Contract.Ensures(Contract.Result() != null); return new FPNum(this, Native.Z3_mk_fpa_numeral_double(nCtx, v, s.NativeObject)); } /// /// Create a numeral of FloatingPoint sort from an int. /// /// numeral value. /// FloatingPoint sort. public FPNum MkFPNumeral(int v, FPSort s) { Contract.Ensures(Contract.Result() != null); return new FPNum(this, Native.Z3_mk_fpa_numeral_int(nCtx, v, s.NativeObject)); } /// /// Create a numeral of FloatingPoint sort from a sign bit and two integers. /// /// the sign. /// the significand. /// the exponent. /// FloatingPoint sort. public FPNum MkFPNumeral(bool sgn, uint sig, int exp, FPSort s) { Contract.Ensures(Contract.Result() != null); return new FPNum(this, Native.Z3_mk_fpa_numeral_int_uint(nCtx, sgn ? 1 : 0, exp, sig, s.NativeObject)); } /// /// Create a numeral of FloatingPoint sort from a sign bit and two 64-bit integers. /// /// the sign. /// the significand. /// the exponent. /// FloatingPoint sort. public FPNum MkFPNumeral(bool sgn, Int64 exp, UInt64 sig, FPSort s) { Contract.Ensures(Contract.Result() != null); return new FPNum(this, Native.Z3_mk_fpa_numeral_int64_uint64(nCtx, sgn ? 1 : 0, exp, sig, s.NativeObject)); } /// /// Create a numeral of FloatingPoint sort from a float. /// /// numeral value. /// FloatingPoint sort. public FPNum MkFP(float v, FPSort s) { Contract.Ensures(Contract.Result() != null); return MkFPNumeral(v, s); } /// /// Create a numeral of FloatingPoint sort from a float. /// /// numeral value. /// FloatingPoint sort. public FPNum MkFP(double v, FPSort s) { Contract.Ensures(Contract.Result() != null); return MkFPNumeral(v, s); } /// /// Create a numeral of FloatingPoint sort from an int. /// /// numeral value. /// FloatingPoint sort. public FPNum MkFP(int v, FPSort s) { Contract.Ensures(Contract.Result() != null); return MkFPNumeral(v, s); } /// /// Create a numeral of FloatingPoint sort from a sign bit and two integers. /// /// the sign. /// the exponent. /// the significand. /// FloatingPoint sort. public FPNum MkFP(bool sgn, int exp, uint sig, FPSort s) { Contract.Ensures(Contract.Result() != null); return MkFPNumeral(sgn, exp, sig, s); } /// /// Create a numeral of FloatingPoint sort from a sign bit and two 64-bit integers. /// /// the sign. /// the exponent. /// the significand. /// FloatingPoint sort. public FPNum MkFP(bool sgn, Int64 exp, UInt64 sig, FPSort s) { Contract.Ensures(Contract.Result() != null); return MkFPNumeral(sgn, exp, sig, s); } #endregion #region Operators /// /// Floating-point absolute value /// /// floating-point term public FPExpr MkFPAbs(FPExpr t) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_abs(this.nCtx, t.NativeObject)); } /// /// Floating-point negation /// /// floating-point term public FPExpr MkFPNeg(FPExpr t) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_neg(this.nCtx, t.NativeObject)); } /// /// Floating-point addition /// /// rounding mode term /// floating-point term /// floating-point term public FPExpr MkFPAdd(FPRMExpr rm, FPExpr t1, FPExpr t2) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_add(this.nCtx, rm.NativeObject, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point subtraction /// /// rounding mode term /// floating-point term /// floating-point term public FPExpr MkFPSub(FPRMExpr rm, FPExpr t1, FPExpr t2) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_sub(this.nCtx, rm.NativeObject, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point multiplication /// /// rounding mode term /// floating-point term /// floating-point term public FPExpr MkFPMul(FPRMExpr rm, FPExpr t1, FPExpr t2) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_mul(this.nCtx, rm.NativeObject, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point division /// /// rounding mode term /// floating-point term /// floating-point term public FPExpr MkFPDiv(FPRMExpr rm, FPExpr t1, FPExpr t2) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_div(this.nCtx, rm.NativeObject, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point fused multiply-add /// /// /// The result is round((t1 * t2) + t3) /// /// rounding mode term /// floating-point term /// floating-point term /// floating-point term public FPExpr MkFPFMA(FPRMExpr rm, FPExpr t1, FPExpr t2, FPExpr t3) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_fma(this.nCtx, rm.NativeObject, t1.NativeObject, t2.NativeObject, t3.NativeObject)); } /// /// Floating-point square root /// /// rounding mode term /// floating-point term public FPExpr MkFPSqrt(FPRMExpr rm, FPExpr t) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_sqrt(this.nCtx, rm.NativeObject, t.NativeObject)); } /// /// Floating-point remainder /// /// floating-point term /// floating-point term public FPExpr MkFPRem(FPExpr t1, FPExpr t2) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_rem(this.nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point roundToIntegral. Rounds a floating-point number to /// the closest integer, again represented as a floating-point number. /// /// term of RoundingMode sort /// floating-point term public FPExpr MkFPRoundToIntegral(FPRMExpr rm, FPExpr t) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_round_to_integral(this.nCtx, rm.NativeObject, t.NativeObject)); } /// /// Minimum of floating-point numbers. /// /// floating-point term /// floating-point term public FPExpr MkFPMin(FPExpr t1, FPExpr t2) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_min(this.nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Maximum of floating-point numbers. /// /// floating-point term /// floating-point term public FPExpr MkFPMax(FPExpr t1, FPExpr t2) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_max(this.nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point less than or equal. /// /// floating-point term /// floating-point term public BoolExpr MkFPLEq(FPExpr t1, FPExpr t2) { Contract.Ensures(Contract.Result() != null); return new BoolExpr(this, Native.Z3_mk_fpa_leq(this.nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point less than. /// /// floating-point term /// floating-point term public BoolExpr MkFPLt(FPExpr t1, FPExpr t2) { Contract.Ensures(Contract.Result() != null); return new BoolExpr(this, Native.Z3_mk_fpa_lt(this.nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point greater than or equal. /// /// floating-point term /// floating-point term public BoolExpr MkFPGEq(FPExpr t1, FPExpr t2) { Contract.Ensures(Contract.Result() != null); return new BoolExpr(this, Native.Z3_mk_fpa_geq(this.nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point greater than. /// /// floating-point term /// floating-point term public BoolExpr MkFPGt(FPExpr t1, FPExpr t2) { Contract.Ensures(Contract.Result() != null); return new BoolExpr(this, Native.Z3_mk_fpa_gt(this.nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point equality. /// /// /// Note that this is IEEE 754 equality (as opposed to standard =). /// /// floating-point term /// floating-point term public BoolExpr MkFPEq(FPExpr t1, FPExpr t2) { Contract.Ensures(Contract.Result() != null); return new BoolExpr(this, Native.Z3_mk_fpa_eq(this.nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Predicate indicating whether t is a normal floating-point number. /// /// floating-point term public BoolExpr MkFPIsNormal(FPExpr t) { Contract.Ensures(Contract.Result() != null); return new BoolExpr(this, Native.Z3_mk_fpa_is_normal(this.nCtx, t.NativeObject)); } /// /// Predicate indicating whether t is a subnormal floating-point number. /// /// floating-point term public BoolExpr MkFPIsSubnormal(FPExpr t) { Contract.Ensures(Contract.Result() != null); return new BoolExpr(this, Native.Z3_mk_fpa_is_subnormal(this.nCtx, t.NativeObject)); } /// /// Predicate indicating whether t is a floating-point number with zero value, i.e., +0 or -0. /// /// floating-point term public BoolExpr MkFPIsZero(FPExpr t) { Contract.Ensures(Contract.Result() != null); return new BoolExpr(this, Native.Z3_mk_fpa_is_zero(this.nCtx, t.NativeObject)); } /// /// Predicate indicating whether t is a floating-point number representing +oo or -oo. /// /// floating-point term public BoolExpr MkFPIsInfinite(FPExpr t) { Contract.Ensures(Contract.Result() != null); return new BoolExpr(this, Native.Z3_mk_fpa_is_infinite(this.nCtx, t.NativeObject)); } /// /// Predicate indicating whether t is a NaN. /// /// floating-point term public BoolExpr MkFPIsNaN(FPExpr t) { Contract.Ensures(Contract.Result() != null); return new BoolExpr(this, Native.Z3_mk_fpa_is_nan(this.nCtx, t.NativeObject)); } /// /// Predicate indicating whether t is a negative floating-point number. /// /// floating-point term public BoolExpr MkFPIsNegative(FPExpr t) { Contract.Ensures(Contract.Result() != null); return new BoolExpr(this, Native.Z3_mk_fpa_is_negative(this.nCtx, t.NativeObject)); } /// /// Predicate indicating whether t is a positive floating-point number. /// /// floating-point term public BoolExpr MkFPIsPositive(FPExpr t) { Contract.Ensures(Contract.Result() != null); return new BoolExpr(this, Native.Z3_mk_fpa_is_positive(this.nCtx, t.NativeObject)); } #endregion #region Conversions to FloatingPoint terms /// /// Create an expression of FloatingPoint sort from three bit-vector expressions. /// /// /// This is the operator named `fp' in the SMT FP theory definition. /// Note that sgn is required to be a bit-vector of size 1. Significand and exponent /// are required to be greater than 1 and 2 respectively. The FloatingPoint sort /// of the resulting expression is automatically determined from the bit-vector sizes /// of the arguments. /// /// bit-vector term (of size 1) representing the sign. /// bit-vector term representing the significand. /// bit-vector term representing the exponent. public FPExpr MkFP(BitVecExpr sgn, BitVecExpr sig, BitVecExpr exp) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_fp(this.nCtx, sgn.NativeObject, sig.NativeObject, exp.NativeObject)); } /// /// Conversion of a single IEEE 754-2008 bit-vector into a floating-point number. /// /// /// Produces a term that represents the conversion of a bit-vector term bv to a /// floating-point term of sort s. The bit-vector size of bv (m) must be equal /// to ebits+sbits of s. The format of the bit-vector is as defined by the /// IEEE 754-2008 interchange format. /// /// bit-vector value (of size m). /// FloatingPoint sort (ebits+sbits == m) public FPExpr MkFPToFP(BitVecExpr bv, FPSort s) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_to_fp_bv(this.nCtx, bv.NativeObject, s.NativeObject)); } /// /// Conversion of a FloatingPoint term into another term of different FloatingPoint sort. /// /// /// Produces a term that represents the conversion of a floating-point term t to a /// floating-point term of sort s. If necessary, the result will be rounded according /// to rounding mode rm. /// /// RoundingMode term. /// FloatingPoint term. /// FloatingPoint sort. public FPExpr MkFPToFP(FPRMExpr rm, FPExpr t, FPSort s) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_to_fp_float(this.nCtx, rm.NativeObject, t.NativeObject, s.NativeObject)); } /// /// Conversion of a term of real sort into a term of FloatingPoint sort. /// /// /// Produces a term that represents the conversion of term t of real sort into a /// floating-point term of sort s. If necessary, the result will be rounded according /// to rounding mode rm. /// /// RoundingMode term. /// term of Real sort. /// FloatingPoint sort. public FPExpr MkFPToFP(FPRMExpr rm, RealExpr t, FPSort s) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_to_fp_real(this.nCtx, rm.NativeObject, t.NativeObject, s.NativeObject)); } /// /// Conversion of a 2's complement signed bit-vector term into a term of FloatingPoint sort. /// /// /// Produces a term that represents the conversion of the bit-vector term t into a /// floating-point term of sort s. The bit-vector t is taken to be in signed /// 2's complement format (when signed==true, otherwise unsigned). If necessary, the /// result will be rounded according to rounding mode rm. /// /// RoundingMode term. /// term of bit-vector sort. /// FloatingPoint sort. /// flag indicating whether t is interpreted as signed or unsigned bit-vector. public FPExpr MkFPToFP(FPRMExpr rm, BitVecExpr t, FPSort s, bool signed) { Contract.Ensures(Contract.Result() != null); if (signed) return new FPExpr(this, Native.Z3_mk_fpa_to_fp_signed(this.nCtx, rm.NativeObject, t.NativeObject, s.NativeObject)); else return new FPExpr(this, Native.Z3_mk_fpa_to_fp_unsigned(this.nCtx, rm.NativeObject, t.NativeObject, s.NativeObject)); } /// /// Conversion of a floating-point number to another FloatingPoint sort s. /// /// /// Produces a term that represents the conversion of a floating-point term t to a different /// FloatingPoint sort s. If necessary, rounding according to rm is applied. /// /// FloatingPoint sort /// floating-point rounding mode term /// floating-point term public FPExpr MkFPToFP(FPSort s, FPRMExpr rm, FPExpr t) { Contract.Ensures(Contract.Result() != null); return new FPExpr(this, Native.Z3_mk_fpa_to_fp_float(this.nCtx, s.NativeObject, rm.NativeObject, t.NativeObject)); } #endregion #region Conversions from FloatingPoint terms /// /// Conversion of a floating-point term into a bit-vector. /// /// /// Produces a term that represents the conversion of the floating-poiunt term t into a /// bit-vector term of size sz in 2's complement format (signed when signed==true). If necessary, /// the result will be rounded according to rounding mode rm. /// /// RoundingMode term. /// FloatingPoint term /// Size of the resulting bit-vector. /// Indicates whether the result is a signed or unsigned bit-vector. public BitVecExpr MkFPToBV(FPRMExpr rm, FPExpr t, uint sz, bool signed) { Contract.Ensures(Contract.Result() != null); if (signed) return new BitVecExpr(this, Native.Z3_mk_fpa_to_sbv(this.nCtx, rm.NativeObject, t.NativeObject, sz)); else return new BitVecExpr(this, Native.Z3_mk_fpa_to_ubv(this.nCtx, rm.NativeObject, t.NativeObject, sz)); } /// /// Conversion of a floating-point term into a real-numbered term. /// /// /// Produces a term that represents the conversion of the floating-poiunt term t into a /// real number. Note that this type of conversion will often result in non-linear /// constraints over real terms. /// /// FloatingPoint term public RealExpr MkFPToReal(FPExpr t) { Contract.Ensures(Contract.Result() != null); return new RealExpr(this, Native.Z3_mk_fpa_to_real(this.nCtx, t.NativeObject)); } #endregion #region Z3-specific extensions /// /// Conversion of a floating-point term into a bit-vector term in IEEE 754-2008 format. /// /// /// The size of the resulting bit-vector is automatically determined. Note that /// IEEE 754-2008 allows multiple different representations of NaN. This conversion /// knows only one NaN and it will always produce the same bit-vector represenatation of /// that NaN. /// /// FloatingPoint term. public BitVecExpr MkFPToIEEEBV(FPExpr t) { Contract.Ensures(Contract.Result() != null); return new BitVecExpr(this, Native.Z3_mk_fpa_to_ieee_bv(this.nCtx, t.NativeObject)); } /// /// Conversion of a real-sorted significand and an integer-sorted exponent into a term of FloatingPoint sort. /// /// /// Produces a term that represents the conversion of sig * 2^exp into a /// floating-point term of sort s. If necessary, the result will be rounded /// according to rounding mode rm. /// /// RoundingMode term. /// Exponent term of Int sort. /// Significand term of Real sort. /// FloatingPoint sort. public BitVecExpr MkFPToFP(FPRMExpr rm, IntExpr exp, RealExpr sig, FPSort s) { Contract.Ensures(Contract.Result() != null); return new BitVecExpr(this, Native.Z3_mk_fpa_to_fp_int_real(this.nCtx, rm.NativeObject, exp.NativeObject, sig.NativeObject, s.NativeObject)); } #endregion #endregion // Floating-point Arithmetic #region Miscellaneous /// /// Wraps an AST. /// /// This function is used for transitions between native and /// managed objects. Note that must be a /// native object obtained from Z3 (e.g., through ) /// and that it must have a correct reference count (see e.g., /// . /// /// The native pointer to wrap. public AST WrapAST(IntPtr nativeObject) { Contract.Ensures(Contract.Result() != null); return AST.Create(this, nativeObject); } /// /// Unwraps an AST. /// /// This function is used for transitions between native and /// managed objects. It returns the native pointer to the AST. Note that /// AST objects are reference counted and unwrapping an AST disables automatic /// reference counting, i.e., all references to the IntPtr that is returned /// must be handled externally and through native calls (see e.g., /// ). /// /// The AST to unwrap. public IntPtr UnwrapAST(AST a) { return a.NativeObject; } /// /// Return a string describing all available parameters to Expr.Simplify. /// public string SimplifyHelp() { Contract.Ensures(Contract.Result() != null); return Native.Z3_simplify_get_help(nCtx); } /// /// Retrieves parameter descriptions for simplifier. /// public ParamDescrs SimplifyParameterDescriptions { get { return new ParamDescrs(this, Native.Z3_simplify_get_param_descrs(nCtx)); } } #endregion #region Error Handling ///// ///// A delegate which is executed when an error is raised. ///// ///// ///// Note that it is possible for memory leaks to occur if error handlers ///// throw exceptions. ///// //public delegate void ErrorHandler(Context ctx, Z3_error_code errorCode, string errorString); ///// ///// The OnError event. ///// //public event ErrorHandler OnError = null; #endregion #region Parameters /// /// Update a mutable configuration parameter. /// /// /// The list of all configuration parameters can be obtained using the Z3 executable: /// z3.exe -p /// Only a few configuration parameters are mutable once the context is created. /// An exception is thrown when trying to modify an immutable parameter. /// public void UpdateParamValue(string id, string value) { Native.Z3_update_param_value(nCtx, id, value); } #endregion #region Internal internal IntPtr m_ctx = IntPtr.Zero; internal Native.Z3_error_handler m_n_err_handler = null; internal IntPtr nCtx { get { return m_ctx; } } internal void NativeErrorHandler(IntPtr ctx, Z3_error_code errorCode) { // Do-nothing error handler. The wrappers in Z3.Native will throw exceptions upon errors. } internal void InitContext() { PrintMode = Z3_ast_print_mode.Z3_PRINT_SMTLIB2_COMPLIANT; m_n_err_handler = new Native.Z3_error_handler(NativeErrorHandler); // keep reference so it doesn't get collected. Native.Z3_set_error_handler(m_ctx, m_n_err_handler); GC.SuppressFinalize(this); } [Pure] internal void CheckContextMatch(Z3Object other) { Contract.Requires(other != null); if (!ReferenceEquals(this, other.Context)) throw new Z3Exception("Context mismatch"); } [Pure] internal void CheckContextMatch(Z3Object[] arr) { Contract.Requires(arr == null || Contract.ForAll(arr, a => a != null)); if (arr != null) { foreach (Z3Object a in arr) { Contract.Assert(a != null); // It was an assume, now we added the precondition, and we made it into an assert CheckContextMatch(a); } } } [ContractInvariantMethod] private void ObjectInvariant() { Contract.Invariant(m_AST_DRQ != null); Contract.Invariant(m_ASTMap_DRQ != null); Contract.Invariant(m_ASTVector_DRQ != null); Contract.Invariant(m_ApplyResult_DRQ != null); Contract.Invariant(m_FuncEntry_DRQ != null); Contract.Invariant(m_FuncInterp_DRQ != null); Contract.Invariant(m_Goal_DRQ != null); Contract.Invariant(m_Model_DRQ != null); Contract.Invariant(m_Params_DRQ != null); Contract.Invariant(m_ParamDescrs_DRQ != null); Contract.Invariant(m_Probe_DRQ != null); Contract.Invariant(m_Solver_DRQ != null); Contract.Invariant(m_Statistics_DRQ != null); Contract.Invariant(m_Tactic_DRQ != null); Contract.Invariant(m_Fixedpoint_DRQ != null); Contract.Invariant(m_Optimize_DRQ != null); } readonly private AST.DecRefQueue m_AST_DRQ = new AST.DecRefQueue(); readonly private ASTMap.DecRefQueue m_ASTMap_DRQ = new ASTMap.DecRefQueue(10); readonly private ASTVector.DecRefQueue m_ASTVector_DRQ = new ASTVector.DecRefQueue(10); readonly private ApplyResult.DecRefQueue m_ApplyResult_DRQ = new ApplyResult.DecRefQueue(10); readonly private FuncInterp.Entry.DecRefQueue m_FuncEntry_DRQ = new FuncInterp.Entry.DecRefQueue(10); readonly private FuncInterp.DecRefQueue m_FuncInterp_DRQ = new FuncInterp.DecRefQueue(10); readonly private Goal.DecRefQueue m_Goal_DRQ = new Goal.DecRefQueue(10); readonly private Model.DecRefQueue m_Model_DRQ = new Model.DecRefQueue(10); readonly private Params.DecRefQueue m_Params_DRQ = new Params.DecRefQueue(10); readonly private ParamDescrs.DecRefQueue m_ParamDescrs_DRQ = new ParamDescrs.DecRefQueue(10); readonly private Probe.DecRefQueue m_Probe_DRQ = new Probe.DecRefQueue(10); readonly private Solver.DecRefQueue m_Solver_DRQ = new Solver.DecRefQueue(10); readonly private Statistics.DecRefQueue m_Statistics_DRQ = new Statistics.DecRefQueue(10); readonly private Tactic.DecRefQueue m_Tactic_DRQ = new Tactic.DecRefQueue(10); readonly private Fixedpoint.DecRefQueue m_Fixedpoint_DRQ = new Fixedpoint.DecRefQueue(10); readonly private Optimize.DecRefQueue m_Optimize_DRQ = new Optimize.DecRefQueue(10); /// /// AST DRQ /// public IDecRefQueue AST_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_AST_DRQ; } } /// /// ASTMap DRQ /// public IDecRefQueue ASTMap_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_ASTMap_DRQ; } } /// /// ASTVector DRQ /// public IDecRefQueue ASTVector_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_ASTVector_DRQ; } } /// /// ApplyResult DRQ /// public IDecRefQueue ApplyResult_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_ApplyResult_DRQ; } } /// /// FuncEntry DRQ /// public IDecRefQueue FuncEntry_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_FuncEntry_DRQ; } } /// /// FuncInterp DRQ /// public IDecRefQueue FuncInterp_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_FuncInterp_DRQ; } } /// /// Goal DRQ /// public IDecRefQueue Goal_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_Goal_DRQ; } } /// /// Model DRQ /// public IDecRefQueue Model_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_Model_DRQ; } } /// /// Params DRQ /// public IDecRefQueue Params_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_Params_DRQ; } } /// /// ParamDescrs DRQ /// public IDecRefQueue ParamDescrs_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_ParamDescrs_DRQ; } } /// /// Probe DRQ /// public IDecRefQueue Probe_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_Probe_DRQ; } } /// /// Solver DRQ /// public IDecRefQueue Solver_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_Solver_DRQ; } } /// /// Statistics DRQ /// public IDecRefQueue Statistics_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_Statistics_DRQ; } } /// /// Tactic DRQ /// public IDecRefQueue Tactic_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_Tactic_DRQ; } } /// /// FixedPoint DRQ /// public IDecRefQueue Fixedpoint_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_Fixedpoint_DRQ; } } /// /// Optimize DRQ /// public IDecRefQueue Optimize_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_Fixedpoint_DRQ; } } internal long refCount = 0; /// /// Finalizer. /// ~Context() { // Console.WriteLine("Context Finalizer from " + System.Threading.Thread.CurrentThread.ManagedThreadId); Dispose(); if (refCount == 0) { m_n_err_handler = null; Native.Z3_del_context(m_ctx); m_ctx = IntPtr.Zero; } else GC.ReRegisterForFinalize(this); } /// /// Disposes of the context. /// public void Dispose() { // Console.WriteLine("Context Dispose from " + System.Threading.Thread.CurrentThread.ManagedThreadId); AST_DRQ.Clear(this); ASTMap_DRQ.Clear(this); ASTVector_DRQ.Clear(this); ApplyResult_DRQ.Clear(this); FuncEntry_DRQ.Clear(this); FuncInterp_DRQ.Clear(this); Goal_DRQ.Clear(this); Model_DRQ.Clear(this); Params_DRQ.Clear(this); ParamDescrs_DRQ.Clear(this); Probe_DRQ.Clear(this); Solver_DRQ.Clear(this); Statistics_DRQ.Clear(this); Tactic_DRQ.Clear(this); Fixedpoint_DRQ.Clear(this); Optimize_DRQ.Clear(this); m_boolSort = null; m_intSort = null; m_realSort = null; } #endregion } } z3-4.4.0/src/api/dotnet/IntNum.cs0000644000175000017500000000533612540347414016252 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: IntNum.cs Abstract: Z3 Managed API: Int Numerals Author: Christoph Wintersteiger (cwinter) 2012-03-20 Notes: --*/ using System; using System.Diagnostics.Contracts; #if !FRAMEWORK_LT_4 using System.Numerics; #endif namespace Microsoft.Z3 { /// /// Integer Numerals /// [ContractVerification(true)] public class IntNum : IntExpr { #region Internal internal IntNum(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion /// /// Retrieve the 64-bit unsigned integer value. /// public UInt64 UInt64 { get { UInt64 res = 0; if (Native.Z3_get_numeral_uint64(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not a 64 bit unsigned"); return res; } } /// /// Retrieve the int value. /// public int Int { get { int res = 0; if (Native.Z3_get_numeral_int(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not an int"); return res; } } /// /// Retrieve the 64-bit int value. /// public Int64 Int64 { get { Int64 res = 0; if (Native.Z3_get_numeral_int64(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not an int64"); return res; } } /// /// Retrieve the int value. /// public uint UInt { get { uint res = 0; if (Native.Z3_get_numeral_uint(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not a uint"); return res; } } #if !FRAMEWORK_LT_4 /// /// Retrieve the BigInteger value. /// public BigInteger BigInteger { get { return BigInteger.Parse(this.ToString()); } } #endif /// /// Returns a string representation of the numeral. /// public override string ToString() { return Native.Z3_get_numeral_string(Context.nCtx, NativeObject); } } } z3-4.4.0/src/api/dotnet/ASTVector.cs0000644000175000017500000001767212540347414016660 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ASTVector.cs Abstract: Z3 Managed API: AST Vectors Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Vectors of ASTs. /// public class ASTVector : Z3Object { /// /// The size of the vector /// public uint Size { get { return Native.Z3_ast_vector_size(Context.nCtx, NativeObject); } } /// /// Retrieves the i-th object in the vector. /// /// May throw an IndexOutOfBoundsException when is out of range. /// Index /// An AST public AST this[uint i] { get { Contract.Ensures(Contract.Result() != null); return new AST(Context, Native.Z3_ast_vector_get(Context.nCtx, NativeObject, i)); } set { Contract.Requires(value != null); Native.Z3_ast_vector_set(Context.nCtx, NativeObject, i, value.NativeObject); } } /// /// Resize the vector to . /// /// The new size of the vector. public void Resize(uint newSize) { Native.Z3_ast_vector_resize(Context.nCtx, NativeObject, newSize); } /// /// Add the AST to the back of the vector. The size /// is increased by 1. /// /// An AST public void Push(AST a) { Contract.Requires(a != null); Native.Z3_ast_vector_push(Context.nCtx, NativeObject, a.NativeObject); } /// /// Translates all ASTs in the vector to . /// /// A context /// A new ASTVector public ASTVector Translate(Context ctx) { Contract.Requires(ctx != null); Contract.Ensures(Contract.Result() != null); return new ASTVector(Context, Native.Z3_ast_vector_translate(Context.nCtx, NativeObject, ctx.nCtx)); } /// /// Retrieves a string representation of the vector. /// public override string ToString() { return Native.Z3_ast_vector_to_string(Context.nCtx, NativeObject); } /// /// Translates an AST vector into an AST[] /// public AST[] ToArray() { uint n = Size; AST[] res = new AST[n]; for (uint i = 0; i < n; i++) res[i] = AST.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into an Expr[] /// public Expr[] ToExprArray() { uint n = Size; Expr[] res = new Expr[n]; for (uint i = 0; i < n; i++) res[i] = Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a BoolExpr[] /// public BoolExpr[] ToBoolExprArray() { uint n = Size; BoolExpr[] res = new BoolExpr[n]; for (uint i = 0; i < n; i++) res[i] = (BoolExpr) Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a BitVecExpr[] /// public BitVecExpr[] ToBitVecExprArray() { uint n = Size; BitVecExpr[] res = new BitVecExpr[n]; for (uint i = 0; i < n; i++) res[i] = (BitVecExpr)Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a ArithExpr[] /// public ArithExpr[] ToArithExprArray() { uint n = Size; ArithExpr[] res = new ArithExpr[n]; for (uint i = 0; i < n; i++) res[i] = (ArithExpr)Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a ArrayExpr[] /// public ArrayExpr[] ToArrayExprArray() { uint n = Size; ArrayExpr[] res = new ArrayExpr[n]; for (uint i = 0; i < n; i++) res[i] = (ArrayExpr)Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a DatatypeExpr[] /// public DatatypeExpr[] ToDatatypeExprArray() { uint n = Size; DatatypeExpr[] res = new DatatypeExpr[n]; for (uint i = 0; i < n; i++) res[i] = (DatatypeExpr)Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a FPExpr[] /// public FPExpr[] ToFPExprArray() { uint n = Size; FPExpr[] res = new FPExpr[n]; for (uint i = 0; i < n; i++) res[i] = (FPExpr)Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a FPRMExpr[] /// public FPRMExpr[] ToFPRMExprArray() { uint n = Size; FPRMExpr[] res = new FPRMExpr[n]; for (uint i = 0; i < n; i++) res[i] = (FPRMExpr)Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a IntExpr[] /// public IntExpr[] ToIntExprArray() { uint n = Size; IntExpr[] res = new IntExpr[n]; for (uint i = 0; i < n; i++) res[i] = (IntExpr)Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a RealExpr[] /// public RealExpr[] ToRealExprArray() { uint n = Size; RealExpr[] res = new RealExpr[n]; for (uint i = 0; i < n; i++) res[i] = (RealExpr)Expr.Create(this.Context, this[i].NativeObject); return res; } #region Internal internal ASTVector(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal ASTVector(Context ctx) : base(ctx, Native.Z3_mk_ast_vector(ctx.nCtx)) { Contract.Requires(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_ast_vector_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_ast_vector_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.ASTVector_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.ASTVector_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-4.4.0/src/api/dotnet/IDecRefQueue.cs0000644000175000017500000000455012540347414017303 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: DecRefQueue.cs Abstract: Z3 Managed API: DecRef Queues Author: Christoph Wintersteiger (cwinter) 2012-03-16 Notes: --*/ using System; using System.Collections; using System.Collections.Generic; using System.Threading; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// DecRefQueue interface /// [ContractClass(typeof(DecRefQueueContracts))] public abstract class IDecRefQueue { #region Object invariant [ContractInvariantMethod] private void ObjectInvariant() { Contract.Invariant(this.m_queue != null); } #endregion readonly private Object m_lock = new Object(); readonly private List m_queue = new List(); private uint m_move_limit; internal IDecRefQueue(uint move_limit = 1024) { m_move_limit = move_limit; } /// /// Sets the limit on numbers of objects that are kept back at GC collection. /// /// public void SetLimit(uint l) { m_move_limit = l; } internal abstract void IncRef(Context ctx, IntPtr obj); internal abstract void DecRef(Context ctx, IntPtr obj); internal void IncAndClear(Context ctx, IntPtr o) { Contract.Requires(ctx != null); IncRef(ctx, o); if (m_queue.Count >= m_move_limit) Clear(ctx); } internal void Add(IntPtr o) { if (o == IntPtr.Zero) return; lock (m_lock) { m_queue.Add(o); } } internal void Clear(Context ctx) { Contract.Requires(ctx != null); lock (m_lock) { foreach (IntPtr o in m_queue) DecRef(ctx, o); m_queue.Clear(); } } } [ContractClassFor(typeof(IDecRefQueue))] abstract class DecRefQueueContracts : IDecRefQueue { internal override void IncRef(Context ctx, IntPtr obj) { Contract.Requires(ctx != null); } internal override void DecRef(Context ctx, IntPtr obj) { Contract.Requires(ctx != null); } } } z3-4.4.0/src/api/dotnet/FPRMExpr.cs0000644000175000017500000000137212540347414016437 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPRMExpr.cs Abstract: Z3 Managed API: Floating Point Expressions over Rounding Modes Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// FloatingPoint RoundingMode Expressions /// public class FPRMExpr : Expr { #region Internal /// Constructor for FPRMExpr internal FPRMExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/ConstructorList.cs0000644000175000017500000000231612540347414020214 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ConstructorList.cs Abstract: Z3 Managed API: Constructor Lists Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Lists of constructors /// public class ConstructorList : Z3Object { /// /// Destructor. /// ~ConstructorList() { Native.Z3_del_constructor_list(Context.nCtx, NativeObject); } #region Internal internal ConstructorList(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal ConstructorList(Context ctx, Constructor[] constructors) : base(ctx) { Contract.Requires(ctx != null); Contract.Requires(constructors != null); NativeObject = Native.Z3_mk_constructor_list(Context.nCtx, (uint)constructors.Length, Constructor.ArrayToNative(constructors)); } #endregion } } z3-4.4.0/src/api/dotnet/InterpolationContext.cs0000644000175000017500000001514212540347414021230 0ustar michaelmichael /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics.Contracts; using System.Runtime.InteropServices; namespace Microsoft.Z3 { /// /// The InterpolationContext is suitable for generation of interpolants. /// /// For more information on interpolation please refer /// too the C/C++ API, which is well documented. [ContractVerification(true)] public class InterpolationContext : Context { /// /// Constructor. /// public InterpolationContext() : base() { } /// /// Constructor. /// /// public InterpolationContext(Dictionary settings) : base(settings) { } #region Terms /// /// Create an expression that marks a formula position for interpolation. /// public BoolExpr MkInterpolant(BoolExpr a) { Contract.Requires(a != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(a); return new BoolExpr(this, Native.Z3_mk_interpolant(nCtx, a.NativeObject)); } #endregion /// /// Computes an interpolant. /// /// For more information on interpolation please refer /// too the function Z3_get_interpolant in the C/C++ API, which is /// well documented. public BoolExpr[] GetInterpolant(Expr pf, Expr pat, Params p) { Contract.Requires(pf != null); Contract.Requires(pat != null); Contract.Requires(p != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(pf); CheckContextMatch(pat); CheckContextMatch(p); ASTVector seq = new ASTVector(this, Native.Z3_get_interpolant(nCtx, pf.NativeObject, pat.NativeObject, p.NativeObject)); return seq.ToBoolExprArray(); } /// /// Computes an interpolant. /// /// For more information on interpolation please refer /// too the function Z3_compute_interpolant in the C/C++ API, which is /// well documented. public Z3_lbool ComputeInterpolant(Expr pat, Params p, out BoolExpr[] interp, out Model model) { Contract.Requires(pat != null); Contract.Requires(p != null); Contract.Ensures(Contract.ValueAtReturn(out interp) != null); Contract.Ensures(Contract.ValueAtReturn(out model) != null); CheckContextMatch(pat); CheckContextMatch(p); IntPtr i = IntPtr.Zero, m = IntPtr.Zero; int r = Native.Z3_compute_interpolant(nCtx, pat.NativeObject, p.NativeObject, ref i, ref m); interp = new ASTVector(this, i).ToBoolExprArray(); model = new Model(this, m); return (Z3_lbool)r; } /// /// Return a string summarizing cumulative time used for interpolation. /// /// For more information on interpolation please refer /// too the function Z3_interpolation_profile in the C/C++ API, which is /// well documented. public string InterpolationProfile() { return Native.Z3_interpolation_profile(nCtx); } /// /// Checks the correctness of an interpolant. /// /// For more information on interpolation please refer /// too the function Z3_check_interpolant in the C/C++ API, which is /// well documented. public int CheckInterpolant(Expr[] cnsts, uint[] parents, BoolExpr[] interps, out string error, Expr[] theory) { Contract.Requires(cnsts.Length == parents.Length); Contract.Requires(cnsts.Length == interps.Length + 1); IntPtr n_err_str; int r = Native.Z3_check_interpolant(nCtx, (uint)cnsts.Length, Expr.ArrayToNative(cnsts), parents, Expr.ArrayToNative(interps), out n_err_str, (uint)theory.Length, Expr.ArrayToNative(theory)); error = Marshal.PtrToStringAnsi(n_err_str); return r; } /// /// Reads an interpolation problem from a file. /// /// For more information on interpolation please refer /// too the function Z3_read_interpolation_problem in the C/C++ API, which is /// well documented. public int ReadInterpolationProblem(string filename, out Expr[] cnsts, out uint[] parents, out string error, out Expr[] theory) { uint num = 0, num_theory = 0; IntPtr[] n_cnsts; IntPtr[] n_theory; IntPtr n_err_str; int r = Native.Z3_read_interpolation_problem(nCtx, ref num, out n_cnsts, out parents, filename, out n_err_str, ref num_theory, out n_theory); error = Marshal.PtrToStringAnsi(n_err_str); cnsts = new Expr[num]; parents = new uint[num]; theory = new Expr[num_theory]; for (int i = 0; i < num; i++) cnsts[i] = Expr.Create(this, n_cnsts[i]); for (int i = 0; i < num_theory; i++) theory[i] = Expr.Create(this, n_theory[i]); return r; } /// /// Writes an interpolation problem to a file. /// /// For more information on interpolation please refer /// too the function Z3_write_interpolation_problem in the C/C++ API, which is /// well documented. public void WriteInterpolationProblem(string filename, Expr[] cnsts, uint[] parents, Expr[] theory) { Contract.Requires(cnsts.Length == parents.Length); Native.Z3_write_interpolation_problem(nCtx, (uint)cnsts.Length, Expr.ArrayToNative(cnsts), parents, filename, (uint)theory.Length, Expr.ArrayToNative(theory)); } } } z3-4.4.0/src/api/dotnet/FPNum.cs0000644000175000017500000000662312540347414016025 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPNum.cs Abstract: Z3 Managed API: Floating Point Numerals Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// FloatiungPoint Numerals /// [ContractVerification(true)] public class FPNum : FPExpr { /// /// Retrieves the sign of a floating-point literal /// /// /// Remarks: returns true if the numeral is negative /// public bool Sign { get { int res = 0; if (Native.Z3_fpa_get_numeral_sign(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Sign is not a Boolean value"); return res != 0; } } /// /// The significand value of a floating-point numeral as a string /// /// /// The significand s is always 0 < s < 2.0; the resulting string is long /// enough to represent the real significand precisely. /// public string Significand { get { return Native.Z3_fpa_get_numeral_significand_string(Context.nCtx, NativeObject); } } /// /// The significand value of a floating-point numeral as a UInt64 /// /// /// This function extracts the significand bits, without the /// hidden bit or normalization. Throws an exception if the /// significand does not fit into a UInt64. /// public UInt64 SignificandUInt64 { get { UInt64 result = 0; if (Native.Z3_fpa_get_numeral_significand_uint64(Context.nCtx, NativeObject, ref result) == 0) throw new Z3Exception("Significand is not a 64 bit unsigned integer"); return result; } } /// /// Return the exponent value of a floating-point numeral as a string /// public string Exponent { get { return Native.Z3_fpa_get_numeral_exponent_string(Context.nCtx, NativeObject); } } /// /// Return the exponent value of a floating-point numeral as a signed 64-bit integer /// public Int64 ExponentInt64 { get { Int64 result = 0; if (Native.Z3_fpa_get_numeral_exponent_int64(Context.nCtx, NativeObject, ref result) == 0) throw new Z3Exception("Exponent is not a 64 bit integer"); return result; } } #region Internal internal FPNum(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion /// /// Returns a string representation of the numeral. /// public override string ToString() { return Native.Z3_get_numeral_string(Context.nCtx, NativeObject); } } } z3-4.4.0/src/api/dotnet/RatNum.cs0000644000175000017500000000516212540347414016243 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: IntNum.cs Abstract: Z3 Managed API: Int Numerals Author: Christoph Wintersteiger (cwinter) 2012-03-20 Notes: --*/ using System; using System.Diagnostics.Contracts; #if !FRAMEWORK_LT_4 using System.Numerics; #endif namespace Microsoft.Z3 { /// /// Rational Numerals /// [ContractVerification(true)] public class RatNum : RealExpr { /// /// The numerator of a rational numeral. /// public IntNum Numerator { get { Contract.Ensures(Contract.Result() != null); return new IntNum(Context, Native.Z3_get_numerator(Context.nCtx, NativeObject)); } } /// /// The denominator of a rational numeral. /// public IntNum Denominator { get { Contract.Ensures(Contract.Result() != null); return new IntNum(Context, Native.Z3_get_denominator(Context.nCtx, NativeObject)); } } #if !FRAMEWORK_LT_4 /// /// Converts the numerator of the rational to a BigInteger /// public BigInteger BigIntNumerator { get { IntNum n = Numerator; return BigInteger.Parse(n.ToString()); } } /// /// Converts the denominator of the rational to a BigInteger /// public BigInteger BigIntDenominator { get { IntNum n = Denominator; return BigInteger.Parse(n.ToString()); } } #endif /// /// Returns a string representation in decimal notation. /// /// The result has at most decimal places. public string ToDecimalString(uint precision) { return Native.Z3_get_numeral_decimal_string(Context.nCtx, NativeObject, precision); } /// /// Returns a string representation of the numeral. /// public override string ToString() { return Native.Z3_get_numeral_string(Context.nCtx, NativeObject); } #region Internal internal RatNum(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/ASTMap.cs0000644000175000017500000001034412540347414016120 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ASTMap.cs Abstract: Z3 Managed API: AST Maps Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Map from AST to AST /// [ContractVerification(true)] internal class ASTMap : Z3Object { /// /// Checks whether the map contains the key . /// /// An AST /// True if is a key in the map, false otherwise. public bool Contains(AST k) { Contract.Requires(k != null); return Native.Z3_ast_map_contains(Context.nCtx, NativeObject, k.NativeObject) != 0; } /// /// Finds the value associated with the key . /// /// /// This function signs an error when is not a key in the map. /// /// An AST public AST Find(AST k) { Contract.Requires(k != null); Contract.Ensures(Contract.Result() != null); return new AST(Context, Native.Z3_ast_map_find(Context.nCtx, NativeObject, k.NativeObject)); } /// /// Stores or replaces a new key/value pair in the map. /// /// The key AST /// The value AST public void Insert(AST k, AST v) { Contract.Requires(k != null); Contract.Requires(v != null); Native.Z3_ast_map_insert(Context.nCtx, NativeObject, k.NativeObject, v.NativeObject); } /// /// Erases the key from the map. /// /// An AST public void Erase(AST k) { Contract.Requires(k != null); Native.Z3_ast_map_erase(Context.nCtx, NativeObject, k.NativeObject); } /// /// Removes all keys from the map. /// public void Reset() { Native.Z3_ast_map_reset(Context.nCtx, NativeObject); } /// /// The size of the map /// public uint Size { get { return Native.Z3_ast_map_size(Context.nCtx, NativeObject); } } /// /// The keys stored in the map. /// public AST[] Keys { get { ASTVector res = new ASTVector(Context, Native.Z3_ast_map_keys(Context.nCtx, NativeObject)); return res.ToArray(); } } /// /// Retrieves a string representation of the map. /// public override string ToString() { return Native.Z3_ast_map_to_string(Context.nCtx, NativeObject); } #region Internal internal ASTMap(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal ASTMap(Context ctx) : base(ctx, Native.Z3_mk_ast_map(ctx.nCtx)) { Contract.Requires(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_ast_map_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_ast_map_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.ASTMap_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.ASTMap_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-4.4.0/src/api/dotnet/FPRMNum.cs0000644000175000017500000000721412540347414016261 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPRMExpr.cs Abstract: Z3 Managed API: Floating Point Rounding Mode Numerals Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Floating-point rounding mode numerals /// public class FPRMNum : FPRMExpr { /// /// Indicates whether the term is the floating-point rounding numeral roundNearestTiesToEven /// public bool isRoundNearestTiesToEven { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN; } } /// /// Indicates whether the term is the floating-point rounding numeral roundNearestTiesToEven /// public bool isRNE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN; } } /// /// Indicates whether the term is the floating-point rounding numeral roundNearestTiesToAway /// public bool isRoundNearestTiesToAway { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY; } } /// /// Indicates whether the term is the floating-point rounding numeral roundNearestTiesToAway /// public bool isRNA { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardPositive /// public bool isRoundTowardPositive { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_POSITIVE; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardPositive /// public bool isRTP { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_POSITIVE; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardNegative /// public bool isRoundTowardNegative { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_NEGATIVE; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardNegative /// public bool isRTN { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_NEGATIVE; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardZero /// public bool isRoundTowardZero { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_ZERO; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardZero /// public bool isRTZ { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_ZERO; } } /// /// Returns a string representation of the numeral. /// public override string ToString() { return Native.Z3_get_numeral_string(Context.nCtx, NativeObject); } #region Internal /// Constructor for FPRMNum internal FPRMNum(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/Constructor.cs0000644000175000017500000000772412540347414017370 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Constructor.cs Abstract: Z3 Managed API: Constructors Author: Christoph Wintersteiger (cwinter) 2012-03-22 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Constructors are used for datatype sorts. /// [ContractVerification(true)] public class Constructor : Z3Object { /// /// The number of fields of the constructor. /// public uint NumFields { get { return n; } } /// /// The function declaration of the constructor. /// public FuncDecl ConstructorDecl { get { Contract.Ensures(Contract.Result() != null); IntPtr constructor = IntPtr.Zero; IntPtr tester = IntPtr.Zero; IntPtr[] accessors = new IntPtr[n]; Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, accessors); return new FuncDecl(Context, constructor); } } /// /// The function declaration of the tester. /// public FuncDecl TesterDecl { get { Contract.Ensures(Contract.Result() != null); IntPtr constructor = IntPtr.Zero; IntPtr tester = IntPtr.Zero; IntPtr[] accessors = new IntPtr[n]; Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, accessors); return new FuncDecl(Context, tester); } } /// /// The function declarations of the accessors /// public FuncDecl[] AccessorDecls { get { Contract.Ensures(Contract.Result() != null); IntPtr constructor = IntPtr.Zero; IntPtr tester = IntPtr.Zero; IntPtr[] accessors = new IntPtr[n]; Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, accessors); FuncDecl[] t = new FuncDecl[n]; for (uint i = 0; i < n; i++) t[i] = new FuncDecl(Context, accessors[i]); return t; } } /// /// Destructor. /// ~Constructor() { Native.Z3_del_constructor(Context.nCtx, NativeObject); } #region Internal private uint n = 0; internal Constructor(Context ctx, Symbol name, Symbol recognizer, Symbol[] fieldNames, Sort[] sorts, uint[] sortRefs) : base(ctx) { Contract.Requires(ctx != null); Contract.Requires(name != null); Contract.Requires(recognizer != null); n = AST.ArrayLength(fieldNames); if (n != AST.ArrayLength(sorts)) throw new Z3Exception("Number of field names does not match number of sorts"); if (sortRefs != null && sortRefs.Length != n) throw new Z3Exception("Number of field names does not match number of sort refs"); if (sortRefs == null) sortRefs = new uint[n]; NativeObject = Native.Z3_mk_constructor(ctx.nCtx, name.NativeObject, recognizer.NativeObject, n, Symbol.ArrayToNative(fieldNames), Sort.ArrayToNative(sorts), sortRefs); } #endregion } } z3-4.4.0/src/api/dotnet/Goal.cs0000644000175000017500000001753312540347414015724 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Goal.cs Abstract: Z3 Managed API: Goal Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// A goal (aka problem). A goal is essentially a set /// of formulas, that can be solved and/or transformed using /// tactics and solvers. /// [ContractVerification(true)] public class Goal : Z3Object { /// /// The precision of the goal. /// /// /// Goals can be transformed using over and under approximations. /// An under approximation is applied when the objective is to find a model for a given goal. /// An over approximation is applied when the objective is to find a proof for a given goal. /// public Z3_goal_prec Precision { get { return (Z3_goal_prec)Native.Z3_goal_precision(Context.nCtx, NativeObject); } } /// /// Indicates whether the goal is precise. /// public bool IsPrecise { get { return Precision == Z3_goal_prec.Z3_GOAL_PRECISE; } } /// /// Indicates whether the goal is an under-approximation. /// public bool IsUnderApproximation { get { return Precision == Z3_goal_prec.Z3_GOAL_UNDER; } } /// /// Indicates whether the goal is an over-approximation. /// public bool IsOverApproximation { get { return Precision == Z3_goal_prec.Z3_GOAL_OVER; } } /// /// Indicates whether the goal is garbage (i.e., the product of over- and under-approximations). /// public bool IsGarbage { get { return Precision == Z3_goal_prec.Z3_GOAL_UNDER_OVER; } } /// /// Adds the to the given goal. /// public void Assert(params BoolExpr[] constraints) { Contract.Requires(constraints != null); Contract.Requires(Contract.ForAll(constraints, c => c != null)); Context.CheckContextMatch(constraints); foreach (BoolExpr c in constraints) { Contract.Assert(c != null); // It was an assume, now made an assert just to be sure we do not regress Native.Z3_goal_assert(Context.nCtx, NativeObject, c.NativeObject); } } /// /// Alias for Assert. /// public void Add(params BoolExpr[] constraints) { Assert(constraints); } /// /// Indicates whether the goal contains `false'. /// public bool Inconsistent { get { return Native.Z3_goal_inconsistent(Context.nCtx, NativeObject) != 0; } } /// /// The depth of the goal. /// /// /// This tracks how many transformations were applied to it. /// public uint Depth { get { return Native.Z3_goal_depth(Context.nCtx, NativeObject); } } /// /// Erases all formulas from the given goal. /// public void Reset() { Native.Z3_goal_reset(Context.nCtx, NativeObject); } /// /// The number of formulas in the goal. /// public uint Size { get { return Native.Z3_goal_size(Context.nCtx, NativeObject); } } /// /// The formulas in the goal. /// public BoolExpr[] Formulas { get { Contract.Ensures(Contract.Result() != null); uint n = Size; BoolExpr[] res = new BoolExpr[n]; for (uint i = 0; i < n; i++) res[i] = new BoolExpr(Context, Native.Z3_goal_formula(Context.nCtx, NativeObject, i)); return res; } } /// /// The number of formulas, subformulas and terms in the goal. /// public uint NumExprs { get { return Native.Z3_goal_num_exprs(Context.nCtx, NativeObject); } } /// /// Indicates whether the goal is empty, and it is precise or the product of an under approximation. /// public bool IsDecidedSat { get { return Native.Z3_goal_is_decided_sat(Context.nCtx, NativeObject) != 0; } } /// /// Indicates whether the goal contains `false', and it is precise or the product of an over approximation. /// public bool IsDecidedUnsat { get { return Native.Z3_goal_is_decided_unsat(Context.nCtx, NativeObject) != 0; } } /// /// Translates (copies) the Goal to the target Context . /// public Goal Translate(Context ctx) { Contract.Requires(ctx != null); return new Goal(ctx, Native.Z3_goal_translate(Context.nCtx, NativeObject, ctx.nCtx)); } /// /// Simplifies the goal. /// /// Essentially invokes the `simplify' tactic on the goal. public Goal Simplify(Params p = null) { Tactic t = Context.MkTactic("simplify"); ApplyResult res = t.Apply(this, p); if (res.NumSubgoals == 0) throw new Z3Exception("No subgoals"); else return res.Subgoals[0]; } /// /// Goal to string conversion. /// /// A string representation of the Goal. public override string ToString() { return Native.Z3_goal_to_string(Context.nCtx, NativeObject); } /// /// Goal to BoolExpr conversion. /// /// A string representation of the Goal. public BoolExpr AsBoolExpr() { uint n = Size; if (n == 0) return Context.MkTrue(); else if (n == 1) return Formulas[0]; else { return Context.MkAnd(Formulas); } } #region Internal internal Goal(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal Goal(Context ctx, bool models, bool unsatCores, bool proofs) : base(ctx, Native.Z3_mk_goal(ctx.nCtx, (models) ? 1 : 0, (unsatCores) ? 1 : 0, (proofs) ? 1 : 0)) { Contract.Requires(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_goal_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_goal_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Goal_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Goal_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-4.4.0/src/api/dotnet/DatatypeExpr.cs0000644000175000017500000000134012540347414017441 0ustar michaelmichael/*++ Copyright () 2012 Microsoft Corporation Module Name: DatatypeExpr.cs Abstract: Z3 Managed API: Datatype Expressions Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Datatype expressions /// public class DatatypeExpr : Expr { #region Internal /// Constructor for DatatypeExpr internal DatatypeExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/Z3Exception.cs0000644000175000017500000000135212540347414017205 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Exception.cs Abstract: Z3 Managed API: Exceptions Author: Christoph Wintersteiger (cwinter) 2012-03-15 Notes: --*/ using System; namespace Microsoft.Z3 { /// /// The exception base class for error reporting from Z3 /// public class Z3Exception : Exception { /// /// Constructor. /// public Z3Exception() : base() { } /// /// Constructor. /// public Z3Exception(string message) : base(message) { } /// /// Constructor. /// public Z3Exception(string message, System.Exception inner) : base(message, inner) { } } } z3-4.4.0/src/api/dotnet/TupleSort.cs0000644000175000017500000000424712540347414017001 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: TupleSort.cs Abstract: Z3 Managed API: Tuple Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Tuple sorts. /// [ContractVerification(true)] public class TupleSort : Sort { /// /// The constructor function of the tuple. /// public FuncDecl MkDecl { get { Contract.Ensures(Contract.Result() != null); return new FuncDecl(Context, Native.Z3_get_tuple_sort_mk_decl(Context.nCtx, NativeObject)); } } /// /// The number of fields in the tuple. /// public uint NumFields { get { return Native.Z3_get_tuple_sort_num_fields(Context.nCtx, NativeObject); } } /// /// The field declarations. /// public FuncDecl[] FieldDecls { get { Contract.Ensures(Contract.Result() != null); uint n = NumFields; FuncDecl[] res = new FuncDecl[n]; for (uint i = 0; i < n; i++) res[i] = new FuncDecl(Context, Native.Z3_get_tuple_sort_field_decl(Context.nCtx, NativeObject, i)); return res; } } #region Internal internal TupleSort(Context ctx, Symbol name, uint numFields, Symbol[] fieldNames, Sort[] fieldSorts) : base(ctx, IntPtr.Zero) { Contract.Requires(ctx != null); Contract.Requires(name != null); IntPtr t = IntPtr.Zero; IntPtr[] f = new IntPtr[numFields]; NativeObject = Native.Z3_mk_tuple_sort(ctx.nCtx, name.NativeObject, numFields, Symbol.ArrayToNative(fieldNames), AST.ArrayToNative(fieldSorts), ref t, f); } #endregion }; } z3-4.4.0/src/api/dotnet/RealExpr.cs0000644000175000017500000000131512540347414016553 0ustar michaelmichael/*++ Copyright () 2012 Microsoft Corporation Module Name: RealExpr.cs Abstract: Z3 Managed API: Real Expressions Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Real expressions /// public class RealExpr : ArithExpr { #region Internal /// Constructor for RealExpr internal RealExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/Status.cs0000644000175000017500000000117512540347414016320 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Status.cs Abstract: Z3 Managed API: Status Author: Christoph Wintersteiger (cwinter) 2012-03-15 Notes: --*/ using System; namespace Microsoft.Z3 { /// /// Status values. /// public enum Status { /// /// Used to signify an unsatisfiable status. /// UNSATISFIABLE = -1, /// /// Used to signify an unknown status. /// UNKNOWN = 0, /// /// Used to signify a satisfiable status. /// SATISFIABLE = 1 } } z3-4.4.0/src/api/dotnet/UninterpretedSort.cs0000644000175000017500000000153412540347414020534 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: UninterpretedSort.cs Abstract: Z3 Managed API: Uninterpreted Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Uninterpreted Sorts /// public class UninterpretedSort : Sort { #region Internal internal UninterpretedSort(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal UninterpretedSort(Context ctx, Symbol s) : base(ctx, Native.Z3_mk_uninterpreted_sort(ctx.nCtx, s.NativeObject)) { Contract.Requires(ctx != null); Contract.Requires(s != null); } #endregion } } z3-4.4.0/src/api/dotnet/IntSort.cs0000644000175000017500000000133612540347414016436 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: IntSort.cs Abstract: Z3 Managed API: Int Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// An Integer sort /// public class IntSort : ArithSort { #region Internal internal IntSort(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal IntSort(Context ctx) : base(ctx, Native.Z3_mk_int_sort(ctx.nCtx)) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/RealSort.cs0000644000175000017500000000133712540347414016570 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: RealSort.cs Abstract: Z3 Managed API: Real Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// A real sort /// public class RealSort : ArithSort { #region Internal internal RealSort(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal RealSort(Context ctx) : base(ctx, Native.Z3_mk_real_sort(ctx.nCtx)) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/BoolSort.cs0000644000175000017500000000121712540347414016575 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: BoolSort.cs Abstract: Z3 Managed API: Bool Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// A Boolean sort. /// public class BoolSort : Sort { #region Internal internal BoolSort(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal BoolSort(Context ctx) : base(ctx, Native.Z3_mk_bool_sort(ctx.nCtx)) { Contract.Requires(ctx != null); } #endregion }; } z3-4.4.0/src/api/dotnet/StringSymbol.cs0000644000175000017500000000326312540347414017471 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: StringSymbol.cs Abstract: Z3 Managed API: String Symbols Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Runtime.InteropServices; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Named symbols /// [ContractVerification(true)] public class StringSymbol : Symbol { /// /// The string value of the symbol. /// /// Throws an exception if the symbol is not of string kind. public string String { get { Contract.Ensures(Contract.Result() != null); if (!IsStringSymbol()) throw new Z3Exception("String requested from non-String symbol"); return Native.Z3_get_symbol_string(Context.nCtx, NativeObject); } } #region Internal internal StringSymbol(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal StringSymbol(Context ctx, string s) : base(ctx, Native.Z3_mk_string_symbol(ctx.nCtx, s)) { Contract.Requires(ctx != null); } #if DEBUG internal override void CheckNativeObject(IntPtr obj) { if ((Z3_symbol_kind)Native.Z3_get_symbol_kind(Context.nCtx, obj) != Z3_symbol_kind.Z3_STRING_SYMBOL) throw new Z3Exception("Symbol is not of string kind"); base.CheckNativeObject(obj); } #endif #endregion } } z3-4.4.0/src/api/dotnet/ListSort.cs0000644000175000017500000000746112540347414016624 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ListSort.cs Abstract: Z3 Managed API: List Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// List sorts. /// [ContractVerification(true)] public class ListSort : Sort { /// /// The declaration of the nil function of this list sort. /// public FuncDecl NilDecl { get { Contract.Ensures(Contract.Result() != null); return new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, 0)); } } /// /// The empty list. /// public Expr Nil { get { Contract.Ensures(Contract.Result() != null); return Context.MkApp(NilDecl); } } /// /// The declaration of the isNil function of this list sort. /// public FuncDecl IsNilDecl { get { Contract.Ensures(Contract.Result() != null); return new FuncDecl(Context, Native.Z3_get_datatype_sort_recognizer(Context.nCtx, NativeObject, 0)); } } /// /// The declaration of the cons function of this list sort. /// public FuncDecl ConsDecl { get { Contract.Ensures(Contract.Result() != null); return new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, 1)); } } /// /// The declaration of the isCons function of this list sort. /// /// public FuncDecl IsConsDecl { get { Contract.Ensures(Contract.Result() != null); return new FuncDecl(Context, Native.Z3_get_datatype_sort_recognizer(Context.nCtx, NativeObject, 1)); } } /// /// The declaration of the head function of this list sort. /// public FuncDecl HeadDecl { get { Contract.Ensures(Contract.Result() != null); return new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor_accessor(Context.nCtx, NativeObject, 1, 0)); } } /// /// The declaration of the tail function of this list sort. /// public FuncDecl TailDecl { get { Contract.Ensures(Contract.Result() != null); return new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor_accessor(Context.nCtx, NativeObject, 1, 1)); } } #region Internal internal ListSort(Context ctx, Symbol name, Sort elemSort) : base(ctx, IntPtr.Zero) { Contract.Requires(ctx != null); Contract.Requires(name != null); Contract.Requires(elemSort != null); IntPtr inil = IntPtr.Zero, iisnil = IntPtr.Zero, icons = IntPtr.Zero, iiscons = IntPtr.Zero, ihead = IntPtr.Zero, itail = IntPtr.Zero; NativeObject = Native.Z3_mk_list_sort(ctx.nCtx, name.NativeObject, elemSort.NativeObject, ref inil, ref iisnil, ref icons, ref iiscons, ref ihead, ref itail); } #endregion }; } z3-4.4.0/src/api/dotnet/Readme.NET350000644000175000017500000000074412540347414016424 0ustar michaelmichaelThe default Z3 bindings for .NET are built for the .NET framework version 4. Should the need arise, it is also possible to build them for .NET 3.5; the instructions are as follows: In the project properties of Microsoft.Z3.csproj: - Under 'Application': Change Target framework to .NET Framework 3.5 - Under 'Build': Add FRAMEWORK_LT_4 to the condidional compilation symbols - Remove the reference to System.Numerics - Install the NuGet Package "Microsoft Code Contracts for Net3.5" z3-4.4.0/src/api/dotnet/ArraySort.cs0000644000175000017500000000307212540347414016761 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ArraySort.cs Abstract: Z3 Managed API: Array Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Array sorts. /// [ContractVerification(true)] public class ArraySort : Sort { /// /// The domain of the array sort. /// public Sort Domain { get { Contract.Ensures(Contract.Result() != null); return Sort.Create(Context, Native.Z3_get_array_sort_domain(Context.nCtx, NativeObject)); } } /// /// The range of the array sort. /// public Sort Range { get { Contract.Ensures(Contract.Result() != null); return Sort.Create(Context, Native.Z3_get_array_sort_range(Context.nCtx, NativeObject)); } } #region Internal internal ArraySort(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal ArraySort(Context ctx, Sort domain, Sort range) : base(ctx, Native.Z3_mk_array_sort(ctx.nCtx, domain.NativeObject, range.NativeObject)) { Contract.Requires(ctx != null); Contract.Requires(domain != null); Contract.Requires(range != null); } #endregion }; } z3-4.4.0/src/api/dotnet/FPSort.cs0000644000175000017500000000220012540347414016200 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPSort.cs Abstract: Z3 Managed API: Floating Point Sorts Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// FloatingPoint sort /// public class FPSort : Sort { /// /// The number of exponent bits. /// public uint EBits { get { return Native.Z3_fpa_get_ebits(Context.nCtx, NativeObject); } } /// /// The number of significand bits. /// public uint SBits { get { return Native.Z3_fpa_get_sbits(Context.nCtx, NativeObject); } } #region Internal internal FPSort(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal FPSort(Context ctx, uint ebits, uint sbits) : base(ctx, Native.Z3_mk_fpa_sort(ctx.nCtx, ebits, sbits)) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/IntSymbol.cs0000644000175000017500000000307712540347414016760 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: IntSymbol.cs Abstract: Z3 Managed API: Int Symbols Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Runtime.InteropServices; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Numbered symbols /// [ContractVerification(true)] public class IntSymbol : Symbol { /// /// The int value of the symbol. /// /// Throws an exception if the symbol is not of int kind. public int Int { get { if (!IsIntSymbol()) throw new Z3Exception("Int requested from non-Int symbol"); return Native.Z3_get_symbol_int(Context.nCtx, NativeObject); } } #region Internal internal IntSymbol(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal IntSymbol(Context ctx, int i) : base(ctx, Native.Z3_mk_int_symbol(ctx.nCtx, i)) { Contract.Requires(ctx != null); } #if DEBUG internal override void CheckNativeObject(IntPtr obj) { if ((Z3_symbol_kind)Native.Z3_get_symbol_kind(Context.nCtx, obj) != Z3_symbol_kind.Z3_INT_SYMBOL) throw new Z3Exception("Symbol is not of integer kind"); base.CheckNativeObject(obj); } #endif #endregion } } z3-4.4.0/src/api/dotnet/FPRMSort.cs0000644000175000017500000000140612540347414016446 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPRMSort.cs Abstract: Z3 Managed API: Rounding Mode Sort Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// The FloatingPoint RoundingMode sort /// public class FPRMSort : Sort { #region Internal internal FPRMSort(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal FPRMSort(Context ctx) : base(ctx, Native.Z3_mk_fpa_rounding_mode_sort(ctx.nCtx)) { Contract.Requires(ctx != null); } #endregion } }z3-4.4.0/src/api/dotnet/RelationSort.cs0000644000175000017500000000274412540347414017465 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: RelationSort.cs Abstract: Z3 Managed API: Relation Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Relation sorts. /// [ContractVerification(true)] public class RelationSort : Sort { /// /// The arity of the relation sort. /// public uint Arity { get { return Native.Z3_get_relation_arity(Context.nCtx, NativeObject); } } /// /// The sorts of the columns of the relation sort. /// public Sort[] ColumnSorts { get { Contract.Ensures(Contract.Result() != null); if (m_columnSorts != null) return m_columnSorts; uint n = Arity; Sort[] res = new Sort[n]; for (uint i = 0; i < n; i++) res[i] = Sort.Create(Context, Native.Z3_get_relation_column(Context.nCtx, NativeObject, i)); return res; } } #region Internal private Sort[] m_columnSorts = null; internal RelationSort(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/Symbol.cs0000644000175000017500000000463012540347414016301 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Symbol.cs Abstract: Z3 Managed API: Symbols Author: Christoph Wintersteiger (cwinter) 2012-03-16 Notes: --*/ using System; using System.Runtime.InteropServices; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Symbols are used to name several term and type constructors. /// [ContractVerification(true)] public class Symbol : Z3Object { /// /// The kind of the symbol (int or string) /// protected Z3_symbol_kind Kind { get { return (Z3_symbol_kind)Native.Z3_get_symbol_kind(Context.nCtx, NativeObject); } } /// /// Indicates whether the symbol is of Int kind /// public bool IsIntSymbol() { return Kind == Z3_symbol_kind.Z3_INT_SYMBOL; } /// /// Indicates whether the symbol is of string kind. /// public bool IsStringSymbol() { return Kind == Z3_symbol_kind.Z3_STRING_SYMBOL; } /// /// A string representation of the symbol. /// public override string ToString() { if (IsIntSymbol()) return ((IntSymbol)this).Int.ToString(); else if (IsStringSymbol()) return ((StringSymbol)this).String; else throw new Z3Exception("Unknown symbol kind encountered"); } #region Internal /// /// Symbol constructor /// internal protected Symbol(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal static Symbol Create(Context ctx, IntPtr obj) { Contract.Requires(ctx != null); Contract.Ensures(Contract.Result() != null); switch ((Z3_symbol_kind)Native.Z3_get_symbol_kind(ctx.nCtx, obj)) { case Z3_symbol_kind.Z3_INT_SYMBOL: return new IntSymbol(ctx, obj); case Z3_symbol_kind.Z3_STRING_SYMBOL: return new StringSymbol(ctx, obj); default: throw new Z3Exception("Unknown symbol kind encountered"); } } #endregion } } z3-4.4.0/src/api/dotnet/BitVecExpr.cs0000644000175000017500000000157312540347414017052 0ustar michaelmichael/*++ Copyright () 2012 Microsoft Corporation Module Name: BitVecExpr.cs Abstract: Z3 Managed API: BitVec Expressions Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Bit-vector expressions /// public class BitVecExpr : Expr { /// /// The size of the sort of a bit-vector term. /// public uint SortSize { get { return ((BitVecSort)Sort).Size; } } #region Internal /// Constructor for BitVecExpr internal BitVecExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/Statistics.cs0000644000175000017500000001473712540347414017177 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Statistics.cs Abstract: Z3 Managed API: Statistics Author: Christoph Wintersteiger (cwinter) 2012-03-22 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Objects of this class track statistical information about solvers. /// [ContractVerification(true)] public class Statistics : Z3Object { /// /// Statistical data is organized into pairs of [Key, Entry], where every /// Entry is either a DoubleEntry or a UIntEntry /// public class Entry { /// /// The key of the entry. /// readonly public string Key; /// /// The uint-value of the entry. /// public uint UIntValue { get { return m_uint; } } /// /// The double-value of the entry. /// public double DoubleValue { get { return m_double; } } /// /// True if the entry is uint-valued. /// public bool IsUInt { get { return m_is_uint; } } /// /// True if the entry is double-valued. /// public bool IsDouble { get { return m_is_double; } } /// /// The string representation of the the entry's value. /// public string Value { get { Contract.Ensures(Contract.Result() != null); if (IsUInt) return m_uint.ToString(); else if (IsDouble) return m_double.ToString(); else throw new Z3Exception("Unknown statistical entry type"); } } /// /// The string representation of the Entry. /// public override string ToString() { return Key + ": " + Value; } #region Internal readonly private bool m_is_uint = false; readonly private bool m_is_double = false; readonly private uint m_uint = 0; readonly private double m_double = 0.0; internal Entry(string k, uint v) { Key = k; m_is_uint = true; m_uint = v; } internal Entry(string k, double v) { Key = k; m_is_double = true; m_double = v; } #endregion } /// /// A string representation of the statistical data. /// public override string ToString() { return Native.Z3_stats_to_string(Context.nCtx, NativeObject); } /// /// The number of statistical data. /// public uint Size { get { return Native.Z3_stats_size(Context.nCtx, NativeObject); } } /// /// The data entries. /// public Entry[] Entries { get { Contract.Ensures(Contract.Result() != null); Contract.Ensures(Contract.Result().Length == this.Size); Contract.Ensures(Contract.ForAll(0, Contract.Result().Length, j => Contract.Result()[j] != null)); uint n = Size; Entry[] res = new Entry[n]; for (uint i = 0; i < n; i++) { Entry e; string k = Native.Z3_stats_get_key(Context.nCtx, NativeObject, i); if (Native.Z3_stats_is_uint(Context.nCtx, NativeObject, i) != 0) e = new Entry(k, Native.Z3_stats_get_uint_value(Context.nCtx, NativeObject, i)); else if (Native.Z3_stats_is_double(Context.nCtx, NativeObject, i) != 0) e = new Entry(k, Native.Z3_stats_get_double_value(Context.nCtx, NativeObject, i)); else throw new Z3Exception("Unknown data entry value"); res[i] = e; } return res; } } /// /// The statistical counters. /// public string[] Keys { get { Contract.Ensures(Contract.Result() != null); uint n = Size; string[] res = new string[n]; for (uint i = 0; i < n; i++) res[i] = Native.Z3_stats_get_key(Context.nCtx, NativeObject, i); return res; } } /// /// The value of a particular statistical counter. /// /// Returns null if the key is unknown. public Entry this[string key] { get { uint n = Size; Entry[] es = Entries; for (uint i = 0; i < n; i++) if (es[i].Key == key) return es[i]; return null; } } #region Internal internal Statistics(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_stats_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_stats_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Statistics_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Statistics_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-4.4.0/src/api/dotnet/BitVecSort.cs0000644000175000017500000000137312540347414017061 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: BitVecSort.cs Abstract: Z3 Managed API: BitVec Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Bit-vector sorts. /// public class BitVecSort : Sort { /// /// The size of the bit-vector sort. /// public uint Size { get { return Native.Z3_get_bv_sort_size(Context.nCtx, NativeObject); } } #region Internal internal BitVecSort(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion }; } z3-4.4.0/src/api/dotnet/ArithSort.cs0000644000175000017500000000106112540347414016746 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ArithSort.cs Abstract: Z3 Managed API: Arith Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// An arithmetic sort, i.e., Int or Real. /// public class ArithSort : Sort { #region Internal internal ArithSort(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion }; } z3-4.4.0/src/api/dotnet/FiniteDomainSort.cs0000644000175000017500000000234512540347414020253 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: FiniteDomainSort.cs Abstract: Z3 Managed API: Finite Domain Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Finite domain sorts. /// [ContractVerification(true)] public class FiniteDomainSort : Sort { /// /// The size of the finite domain sort. /// public ulong Size { get { ulong res = 0; Native.Z3_get_finite_domain_sort_size(Context.nCtx, NativeObject, ref res); return res; } } #region Internal internal FiniteDomainSort(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal FiniteDomainSort(Context ctx, Symbol name, ulong size) : base(ctx, Native.Z3_mk_finite_domain_sort(ctx.nCtx, name.NativeObject, size)) { Contract.Requires(ctx != null); Contract.Requires(name != null); } #endregion } } z3-4.4.0/src/api/dotnet/Sort.cs0000644000175000017500000001176512540347414015772 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Sort.cs Abstract: Z3 Managed API: Sorts Author: Christoph Wintersteiger (cwinter) 2012-03-15 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// The Sort class implements type information for ASTs. /// [ContractVerification(true)] public class Sort : AST { /// /// Comparison operator. /// /// A Sort /// A Sort /// True if and are from the same context /// and represent the same sort; false otherwise. public static bool operator ==(Sort a, Sort b) { return Object.ReferenceEquals(a, b) || (!Object.ReferenceEquals(a, null) && !Object.ReferenceEquals(b, null) && a.Context == b.Context && Native.Z3_is_eq_sort(a.Context.nCtx, a.NativeObject, b.NativeObject) != 0); } /// /// Comparison operator. /// /// A Sort /// A Sort /// True if and are not from the same context /// or represent different sorts; false otherwise. public static bool operator !=(Sort a, Sort b) { return !(a == b); } /// /// Equality operator for objects of type Sort. /// /// /// public override bool Equals(object o) { Sort casted = o as Sort; if (casted == null) return false; return this == casted; } /// /// Hash code generation for Sorts /// /// A hash code public override int GetHashCode() { return base.GetHashCode(); } /// /// Returns a unique identifier for the sort. /// new public uint Id { get { return Native.Z3_get_sort_id(Context.nCtx, NativeObject); } } /// /// The kind of the sort. /// public Z3_sort_kind SortKind { get { return (Z3_sort_kind)Native.Z3_get_sort_kind(Context.nCtx, NativeObject); } } /// /// The name of the sort /// public Symbol Name { get { Contract.Ensures(Contract.Result() != null); return Symbol.Create(Context, Native.Z3_get_sort_name(Context.nCtx, NativeObject)); } } /// /// A string representation of the sort. /// public override string ToString() { return Native.Z3_sort_to_string(Context.nCtx, NativeObject); } #region Internal /// /// Sort constructor /// internal Sort(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #if DEBUG internal override void CheckNativeObject(IntPtr obj) { if (Native.Z3_get_ast_kind(Context.nCtx, obj) != (uint)Z3_ast_kind.Z3_SORT_AST) throw new Z3Exception("Underlying object is not a sort"); base.CheckNativeObject(obj); } #endif [ContractVerification(true)] new internal static Sort Create(Context ctx, IntPtr obj) { Contract.Requires(ctx != null); Contract.Ensures(Contract.Result() != null); switch ((Z3_sort_kind)Native.Z3_get_sort_kind(ctx.nCtx, obj)) { case Z3_sort_kind.Z3_ARRAY_SORT: return new ArraySort(ctx, obj); case Z3_sort_kind.Z3_BOOL_SORT: return new BoolSort(ctx, obj); case Z3_sort_kind.Z3_BV_SORT: return new BitVecSort(ctx, obj); case Z3_sort_kind.Z3_DATATYPE_SORT: return new DatatypeSort(ctx, obj); case Z3_sort_kind.Z3_INT_SORT: return new IntSort(ctx, obj); case Z3_sort_kind.Z3_REAL_SORT: return new RealSort(ctx, obj); case Z3_sort_kind.Z3_UNINTERPRETED_SORT: return new UninterpretedSort(ctx, obj); case Z3_sort_kind.Z3_FINITE_DOMAIN_SORT: return new FiniteDomainSort(ctx, obj); case Z3_sort_kind.Z3_RELATION_SORT: return new RelationSort(ctx, obj); case Z3_sort_kind.Z3_FLOATING_POINT_SORT: return new FPSort(ctx, obj); case Z3_sort_kind.Z3_ROUNDING_MODE_SORT: return new FPRMSort(ctx, obj); default: throw new Z3Exception("Unknown sort kind"); } } #endregion } } z3-4.4.0/src/api/dotnet/Microsoft.Z3.csproj0000644000175000017500000006056612540347414020201 0ustar michaelmichael Debug AnyCPU 8.0.30703 2.0 {EC3DB697-B734-42F7-9468-5B62821EEB5A} Library Properties Microsoft.Z3 Microsoft.Z3 v4.0 512 Client 0 true full false ..\Debug\ DEBUG;TRACE prompt 4 true ..\Debug\Microsoft.Z3.XML False False True False False True False True True False False False True False False False True False False True True True False False True Full %28none%29 2 pdbonly true ..\external\ prompt 4 true ..\external\Microsoft.Z3.xml AnyCPU ..\external\ true ..\external\Microsoft.Z3.xml true pdbonly AnyCPU bin\Release\Microsoft.Z3.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets true ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true false true ..\x64\Debug\ DEBUG;TRACE true full x64 ..\Debug\Microsoft.Z3.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets true ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true True False True False False False False False False False False False False False False False True False False True False False False False Full %28none%29 0 ..\x64\Debug\Microsoft.Z3.XML ..\x64\external_64\ true ..\x64\external_64\Microsoft.Z3.xml true pdbonly x64 ..\release\Microsoft.Z3.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets true ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules True False True False False True True True False False False True True False False False True False False True True False False -repro True Full %28none%29 2 ..\x64\external\ true ..\x64\external\Microsoft.Z3.XML true pdbonly x64 bin\Release\Microsoft.Z3.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets true ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true false false ..\Release_delaysign\ true ..\Release_delaysign\Microsoft.Z3.XML true pdbonly AnyCPU ..\release\Microsoft.Z3.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets true ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true DELAYSIGN bin\x64\Release_delaysign\ true bin\x64\Release_delaysign\Microsoft.Z3.XML true pdbonly x64 ..\release\Microsoft.Z3.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets true ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true true ..\x86\Debug\ DEBUG;TRACE true full x86 ..\Debug\Microsoft.Z3.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules ..\x86\Debug\Microsoft.Z3.XML bin\x86\Release\ true bin\x86\Release\Microsoft.Z3.xml true pdbonly x86 ..\external\Microsoft.Z3.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules bin\x86\external\ true bin\x86\external\Microsoft.Z3.XML true pdbonly x86 bin\Release\Microsoft.Z3.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets true ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true bin\x86\Release_delaysign\ DELAYSIGN true bin\x86\Release_delaysign\Microsoft.Z3.XML true pdbonly x86 ..\release\Microsoft.Z3.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets true ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true z3-4.4.0/src/api/dotnet/AlgebraicNum.cs0000644000175000017500000000452712540347414017372 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: IntNum.cs Abstract: Z3 Managed API: Int Numerals Author: Christoph Wintersteiger (cwinter) 2012-03-20 Notes: --*/ using System; using System.Diagnostics.Contracts; #if !FRAMEWORK_LT_4 using System.Numerics; #endif namespace Microsoft.Z3 { /// /// Algebraic numbers /// [ContractVerification(true)] public class AlgebraicNum : ArithExpr { /// /// Return a upper bound for a given real algebraic number. /// The interval isolating the number is smaller than 1/10^. /// /// /// the precision of the result /// A numeral Expr of sort Real public RatNum ToUpper(uint precision) { Contract.Ensures(Contract.Result() != null); return new RatNum(Context, Native.Z3_get_algebraic_number_upper(Context.nCtx, NativeObject, precision)); } /// /// Return a lower bound for the given real algebraic number. /// The interval isolating the number is smaller than 1/10^. /// /// /// /// A numeral Expr of sort Real public RatNum ToLower(uint precision) { Contract.Ensures(Contract.Result() != null); return new RatNum(Context, Native.Z3_get_algebraic_number_lower(Context.nCtx, NativeObject, precision)); } /// /// Returns a string representation in decimal notation. /// /// The result has at most decimal places. public string ToDecimal(uint precision) { Contract.Ensures(Contract.Result() != null); return Native.Z3_get_numeral_decimal_string(Context.nCtx, NativeObject, precision); } #region Internal internal AlgebraicNum(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/SetSort.cs0000644000175000017500000000147112540347414016437 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: SetSort.cs Abstract: Z3 Managed API: Set Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Set sorts. /// [ContractVerification(true)] public class SetSort : Sort { #region Internal internal SetSort(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal SetSort(Context ctx, Sort ty) : base(ctx, Native.Z3_mk_set_sort(ctx.nCtx, ty.NativeObject)) { Contract.Requires(ctx != null); Contract.Requires(ty != null); } #endregion } } z3-4.4.0/src/api/dotnet/ArrayExpr.cs0000644000175000017500000000131612540347414016747 0ustar michaelmichael/*++ Copyright () 2012 Microsoft Corporation Module Name: ArrayExpr.cs Abstract: Z3 Managed API: Array Expressions Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Array expressions /// public class ArrayExpr : Expr { #region Internal /// Constructor for ArrayExpr internal ArrayExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/Log.cs0000644000175000017500000000357012540347414015557 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Log.cs Abstract: Z3 Managed API: Log Author: Christoph Wintersteiger (cwinter) 2012-03-15 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Interaction logging for Z3. /// /// /// Note that this is a global, static log and if multiple Context /// objects are created, it logs the interaction with all of them. /// [ContractVerification(true)] public static class Log { private static bool m_is_open = false; /// /// Open an interaction log file. /// /// the name of the file to open /// True if opening the log file succeeds, false otherwise. public static bool Open(string filename) { m_is_open = true; return Native.Z3_open_log(filename) == 1; } /// /// Closes the interaction log. /// public static void Close() { m_is_open = false; Native.Z3_close_log(); } /// /// Appends the user-provided string to the interaction log. /// public static void Append(string s) { Contract.Requires(isOpen()); if (!m_is_open) throw new Z3Exception("Log cannot be closed."); Native.Z3_append_log(s); } /// /// Checks whether the interaction log is opened. /// /// True if the interaction log is open, false otherwise. [Pure] public static bool isOpen() { return m_is_open; } } } z3-4.4.0/src/api/dotnet/FuncInterp.cs0000644000175000017500000001540212540347414017110 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: FuncInterp.cs Abstract: Z3 Managed API: Function Interpretations Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// A function interpretation is represented as a finite map and an 'else' value. /// Each entry in the finite map represents the value of a function given a set of arguments. /// [ContractVerification(true)] public class FuncInterp : Z3Object { /// /// An Entry object represents an element in the finite map used to encode /// a function interpretation. /// public class Entry : Z3Object { /// /// Return the (symbolic) value of this entry. /// public Expr Value { get { Contract.Ensures(Contract.Result() != null); return Expr.Create(Context, Native.Z3_func_entry_get_value(Context.nCtx, NativeObject)); } } /// /// The number of arguments of the entry. /// public uint NumArgs { get { return Native.Z3_func_entry_get_num_args(Context.nCtx, NativeObject); } } /// /// The arguments of the function entry. /// public Expr[] Args { get { Contract.Ensures(Contract.Result() != null); Contract.Ensures(Contract.Result().Length == this.NumArgs); uint n = NumArgs; Expr[] res = new Expr[n]; for (uint i = 0; i < n; i++) res[i] = Expr.Create(Context, Native.Z3_func_entry_get_arg(Context.nCtx, NativeObject, i)); return res; } } /// /// A string representation of the function entry. /// public override string ToString() { uint n = NumArgs; string res = "["; Expr[] args = Args; for (uint i = 0; i < n; i++) res += args[i] + ", "; return res + Value + "]"; } #region Internal internal Entry(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_func_entry_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_func_entry_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.FuncEntry_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.FuncEntry_DRQ.Add(o); base.DecRef(o); } #endregion }; /// /// The number of entries in the function interpretation. /// public uint NumEntries { get { return Native.Z3_func_interp_get_num_entries(Context.nCtx, NativeObject); } } /// /// The entries in the function interpretation /// public Entry[] Entries { get { Contract.Ensures(Contract.Result() != null); Contract.Ensures(Contract.ForAll(0, Contract.Result().Length, j => Contract.Result()[j] != null)); uint n = NumEntries; Entry[] res = new Entry[n]; for (uint i = 0; i < n; i++) res[i] = new Entry(Context, Native.Z3_func_interp_get_entry(Context.nCtx, NativeObject, i)); return res; } } /// /// The (symbolic) `else' value of the function interpretation. /// public Expr Else { get { Contract.Ensures(Contract.Result() != null); return Expr.Create(Context, Native.Z3_func_interp_get_else(Context.nCtx, NativeObject)); } } /// /// The arity of the function interpretation /// public uint Arity { get { return Native.Z3_func_interp_get_arity(Context.nCtx, NativeObject); } } /// /// A string representation of the function interpretation. /// public override string ToString() { string res = ""; res += "["; foreach (Entry e in Entries) { uint n = e.NumArgs; if (n > 1) res += "["; Expr[] args = e.Args; for (uint i = 0; i < n; i++) { if (i != 0) res += ", "; res += args[i]; } if (n > 1) res += "]"; res += " -> " + e.Value + ", "; } res += "else -> " + Else; res += "]"; return res; } #region Internal internal FuncInterp(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_func_interp_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_func_interp_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.FuncInterp_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.FuncInterp_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-4.4.0/src/api/dotnet/Version.cs0000644000175000017500000000462012540347414016460 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Version.cs Abstract: Z3 Managed API: Version information Author: Christoph Wintersteiger (cwinter) 2012-03-16 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Version information. /// /// Note that this class is static. [ContractVerification(true)] public static class Version { static Version() { } /// /// The major version /// public static uint Major { get { uint major = 0, minor = 0, build = 0, revision = 0; Native.Z3_get_version(ref major, ref minor, ref build, ref revision); return major; } } /// /// The minor version /// public static uint Minor { get { uint major = 0, minor = 0, build = 0, revision = 0; Native.Z3_get_version(ref major, ref minor, ref build, ref revision); return minor; } } /// /// The build version /// public static uint Build { get { uint major = 0, minor = 0, build = 0, revision = 0; Native.Z3_get_version(ref major, ref minor, ref build, ref revision); return build; } } /// /// The revision /// public static uint Revision { get { uint major = 0, minor = 0, build = 0, revision = 0; Native.Z3_get_version(ref major, ref minor, ref build, ref revision); return revision; } } /// /// A string representation of the version information. /// new public static string ToString() { Contract.Ensures(Contract.Result() != null); uint major = 0, minor = 0, build = 0, revision = 0; Native.Z3_get_version(ref major, ref minor, ref build, ref revision); return major.ToString() + "." + minor.ToString() + "." + build.ToString() + "." + revision.ToString(); } } } z3-4.4.0/src/api/dotnet/Params.cs0000644000175000017500000001133312540347414016255 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Parameter.cs Abstract: Z3 Managed API: Parameters Author: Christoph Wintersteiger (cwinter) 2012-03-20 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// A Params objects represents a configuration in the form of Symbol/value pairs. /// [ContractVerification(true)] public class Params : Z3Object { /// /// Adds a parameter setting. /// public void Add(Symbol name, bool value) { Contract.Requires(name != null); Native.Z3_params_set_bool(Context.nCtx, NativeObject, name.NativeObject, (value) ? 1 : 0); } /// /// Adds a parameter setting. /// public void Add(Symbol name, uint value) { Contract.Requires(name != null); Native.Z3_params_set_uint(Context.nCtx, NativeObject, name.NativeObject, value); } /// /// Adds a parameter setting. /// public void Add(Symbol name, double value) { Contract.Requires(name != null); Native.Z3_params_set_double(Context.nCtx, NativeObject, name.NativeObject, value); } /// /// Adds a parameter setting. /// public void Add(Symbol name, string value) { Contract.Requires(value != null); Native.Z3_params_set_symbol(Context.nCtx, NativeObject, name.NativeObject, Context.MkSymbol(value).NativeObject); } /// /// Adds a parameter setting. /// public void Add(Symbol name, Symbol value) { Contract.Requires(name != null); Contract.Requires(value != null); Native.Z3_params_set_symbol(Context.nCtx, NativeObject, name.NativeObject, value.NativeObject); } /// /// Adds a parameter setting. /// public void Add(string name, bool value) { Native.Z3_params_set_bool(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, (value) ? 1 : 0); } /// /// Adds a parameter setting. /// public void Add(string name, uint value) { Native.Z3_params_set_uint(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, value); } /// /// Adds a parameter setting. /// public void Add(string name, double value) { Native.Z3_params_set_double(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, value); } /// /// Adds a parameter setting. /// public void Add(string name, Symbol value) { Contract.Requires(value != null); Native.Z3_params_set_symbol(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, value.NativeObject); } /// /// Adds a parameter setting. /// public void Add(string name, string value) { Contract.Requires(name != null); Contract.Requires(value != null); Native.Z3_params_set_symbol(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, Context.MkSymbol(value).NativeObject); } /// /// A string representation of the parameter set. /// public override string ToString() { return Native.Z3_params_to_string(Context.nCtx, NativeObject); } #region Internal internal Params(Context ctx) : base(ctx, Native.Z3_mk_params(ctx.nCtx)) { Contract.Requires(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_params_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_params_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Params_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Params_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-4.4.0/src/api/dotnet/Expr.cs0000644000175000017500000023440112540347414015753 0ustar michaelmichael/*++ Copyright () 2012 Microsoft Corporation Module Name: Expr.cs Abstract: Z3 Managed API: Expressions Author: Christoph Wintersteiger (cwinter) 2012-03-20 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Expressions are terms. /// [ContractVerification(true)] public class Expr : AST { /// /// Returns a simplified version of the expression. /// /// A set of parameters to configure the simplifier /// public Expr Simplify(Params p = null) { Contract.Ensures(Contract.Result() != null); if (p == null) return Expr.Create(Context, Native.Z3_simplify(Context.nCtx, NativeObject)); else return Expr.Create(Context, Native.Z3_simplify_ex(Context.nCtx, NativeObject, p.NativeObject)); } /// /// The function declaration of the function that is applied in this expression. /// public FuncDecl FuncDecl { get { Contract.Ensures(Contract.Result() != null); return new FuncDecl(Context, Native.Z3_get_app_decl(Context.nCtx, NativeObject)); } } /// /// Indicates whether the expression is the true or false expression /// or something else (Z3_L_UNDEF). /// public Z3_lbool BoolValue { get { return (Z3_lbool)Native.Z3_get_bool_value(Context.nCtx, NativeObject); } } /// /// The number of arguments of the expression. /// public uint NumArgs { get { return Native.Z3_get_app_num_args(Context.nCtx, NativeObject); } } /// /// The arguments of the expression. /// public Expr[] Args { get { Contract.Ensures(Contract.Result() != null); uint n = NumArgs; Expr[] res = new Expr[n]; for (uint i = 0; i < n; i++) res[i] = Expr.Create(Context, Native.Z3_get_app_arg(Context.nCtx, NativeObject, i)); return res; } } /// /// Update the arguments of the expression using the arguments /// The number of new arguments should coincide with the current number of arguments. /// public void Update(Expr[] args) { Contract.Requires(args != null); Contract.Requires(Contract.ForAll(args, a => a != null)); Context.CheckContextMatch(args); if (IsApp && args.Length != NumArgs) throw new Z3Exception("Number of arguments does not match"); NativeObject = Native.Z3_update_term(Context.nCtx, NativeObject, (uint)args.Length, Expr.ArrayToNative(args)); } /// /// Substitute every occurrence of from[i] in the expression with to[i], for i smaller than num_exprs. /// /// /// The result is the new expression. The arrays from and to must have size num_exprs. /// For every i smaller than num_exprs, we must have that /// sort of from[i] must be equal to sort of to[i]. /// public Expr Substitute(Expr[] from, Expr[] to) { Contract.Requires(from != null); Contract.Requires(to != null); Contract.Requires(Contract.ForAll(from, f => f != null)); Contract.Requires(Contract.ForAll(to, t => t != null)); Contract.Ensures(Contract.Result() != null); Context.CheckContextMatch(from); Context.CheckContextMatch(to); if (from.Length != to.Length) throw new Z3Exception("Argument sizes do not match"); return Expr.Create(Context, Native.Z3_substitute(Context.nCtx, NativeObject, (uint)from.Length, Expr.ArrayToNative(from), Expr.ArrayToNative(to))); } /// /// Substitute every occurrence of from in the expression with to. /// /// public Expr Substitute(Expr from, Expr to) { Contract.Requires(from != null); Contract.Requires(to != null); Contract.Ensures(Contract.Result() != null); return Substitute(new Expr[] { from }, new Expr[] { to }); } /// /// Substitute the free variables in the expression with the expressions in /// /// /// For every i smaller than num_exprs, the variable with de-Bruijn index i is replaced with term to[i]. /// public Expr SubstituteVars(Expr[] to) { Contract.Requires(to != null); Contract.Requires(Contract.ForAll(to, t => t != null)); Contract.Ensures(Contract.Result() != null); Context.CheckContextMatch(to); return Expr.Create(Context, Native.Z3_substitute_vars(Context.nCtx, NativeObject, (uint)to.Length, Expr.ArrayToNative(to))); } /// /// Translates (copies) the term to the Context . /// /// A context /// A copy of the term which is associated with new public Expr Translate(Context ctx) { Contract.Requires(ctx != null); Contract.Ensures(Contract.Result() != null); if (ReferenceEquals(Context, ctx)) return this; else return Expr.Create(ctx, Native.Z3_translate(Context.nCtx, NativeObject, ctx.nCtx)); } /// /// Returns a string representation of the expression. /// public override string ToString() { return base.ToString(); } /// /// Indicates whether the term is a numeral /// public bool IsNumeral { get { return Native.Z3_is_numeral_ast(Context.nCtx, NativeObject) != 0; } } /// /// Indicates whether the term is well-sorted. /// /// True if the term is well-sorted, false otherwise. public bool IsWellSorted { get { return Native.Z3_is_well_sorted(Context.nCtx, NativeObject) != 0; } } /// /// The Sort of the term. /// public Sort Sort { get { Contract.Ensures(Contract.Result() != null); return Sort.Create(Context, Native.Z3_get_sort(Context.nCtx, NativeObject)); } } #region Constants /// /// Indicates whether the term represents a constant. /// public bool IsConst { get { return IsApp && NumArgs == 0 && FuncDecl.DomainSize == 0; } } #endregion #region Integer Numerals /// /// Indicates whether the term is an integer numeral. /// public bool IsIntNum { get { return IsNumeral && IsInt; } } #endregion #region Real Numerals /// /// Indicates whether the term is a real numeral. /// public bool IsRatNum { get { return IsNumeral && IsReal; } } #endregion #region Algebraic Numbers /// /// Indicates whether the term is an algebraic number /// public bool IsAlgebraicNumber { get { return Native.Z3_is_algebraic_number(Context.nCtx, NativeObject) != 0; } } #endregion #region Term Kind Tests #region Boolean Terms /// /// Indicates whether the term has Boolean sort. /// public bool IsBool { get { return (IsExpr && Native.Z3_is_eq_sort(Context.nCtx, Native.Z3_mk_bool_sort(Context.nCtx), Native.Z3_get_sort(Context.nCtx, NativeObject)) != 0); } } /// /// Indicates whether the term is the constant true. /// public bool IsTrue { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TRUE; } } /// /// Indicates whether the term is the constant false. /// public bool IsFalse { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FALSE; } } /// /// Indicates whether the term is an equality predicate. /// public bool IsEq { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EQ; } } /// /// Indicates whether the term is an n-ary distinct predicate (every argument is mutually distinct). /// public bool IsDistinct { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_DISTINCT; } } /// /// Indicates whether the term is a ternary if-then-else term /// public bool IsITE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ITE; } } /// /// Indicates whether the term is an n-ary conjunction /// public bool IsAnd { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_AND; } } /// /// Indicates whether the term is an n-ary disjunction /// public bool IsOr { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_OR; } } /// /// Indicates whether the term is an if-and-only-if (Boolean equivalence, binary) /// public bool IsIff { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IFF; } } /// /// Indicates whether the term is an exclusive or /// public bool IsXor { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_XOR; } } /// /// Indicates whether the term is a negation /// public bool IsNot { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_NOT; } } /// /// Indicates whether the term is an implication /// public bool IsImplies { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IMPLIES; } } #endregion #region Interpolation /// /// Indicates whether the term is marked for interpolation. /// /// public bool IsInterpolant { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_INTERP; } } #endregion #region Arithmetic Terms /// /// Indicates whether the term is of integer sort. /// public bool IsInt { get { return (Native.Z3_is_numeral_ast(Context.nCtx, NativeObject) != 0 && Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == (uint)Z3_sort_kind.Z3_INT_SORT); } } /// /// Indicates whether the term is of sort real. /// public bool IsReal { get { return Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == (uint)Z3_sort_kind.Z3_REAL_SORT; } } /// /// Indicates whether the term is an arithmetic numeral. /// public bool IsArithmeticNumeral { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ANUM; } } /// /// Indicates whether the term is a less-than-or-equal /// public bool IsLE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LE; } } /// /// Indicates whether the term is a greater-than-or-equal /// public bool IsGE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_GE; } } /// /// Indicates whether the term is a less-than /// public bool IsLT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LT; } } /// /// Indicates whether the term is a greater-than /// public bool IsGT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_GT; } } /// /// Indicates whether the term is addition (binary) /// public bool IsAdd { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ADD; } } /// /// Indicates whether the term is subtraction (binary) /// public bool IsSub { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SUB; } } /// /// Indicates whether the term is a unary minus /// public bool IsUMinus { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UMINUS; } } /// /// Indicates whether the term is multiplication (binary) /// public bool IsMul { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_MUL; } } /// /// Indicates whether the term is division (binary) /// public bool IsDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_DIV; } } /// /// Indicates whether the term is integer division (binary) /// public bool IsIDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IDIV; } } /// /// Indicates whether the term is remainder (binary) /// public bool IsRemainder { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_REM; } } /// /// Indicates whether the term is modulus (binary) /// public bool IsModulus { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_MOD; } } /// /// Indicates whether the term is a coercion of integer to real (unary) /// public bool IsIntToReal { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TO_REAL; } } /// /// Indicates whether the term is a coercion of real to integer (unary) /// public bool IsRealToInt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TO_INT; } } /// /// Indicates whether the term is a check that tests whether a real is integral (unary) /// public bool IsRealIsInt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IS_INT; } } #endregion #region Array Terms /// /// Indicates whether the term is of an array sort. /// public bool IsArray { get { return (Native.Z3_is_app(Context.nCtx, NativeObject) != 0 && (Z3_sort_kind)Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == Z3_sort_kind.Z3_ARRAY_SORT); } } /// /// Indicates whether the term is an array store. /// /// It satisfies select(store(a,i,v),j) = if i = j then v else select(a,j). /// Array store takes at least 3 arguments. public bool IsStore { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_STORE; } } /// /// Indicates whether the term is an array select. /// public bool IsSelect { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SELECT; } } /// /// Indicates whether the term is a constant array. /// /// For example, select(const(v),i) = v holds for every v and i. The function is unary. public bool IsConstantArray { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CONST_ARRAY; } } /// /// Indicates whether the term is a default array. /// /// For example default(const(v)) = v. The function is unary. public bool IsDefaultArray { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ARRAY_DEFAULT; } } /// /// Indicates whether the term is an array map. /// /// It satisfies map[f](a1,..,a_n)[i] = f(a1[i],...,a_n[i]) for every i. public bool IsArrayMap { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ARRAY_MAP; } } /// /// Indicates whether the term is an as-array term. /// /// An as-array term is n array value that behaves as the function graph of the /// function passed as parameter. public bool IsAsArray { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_AS_ARRAY; } } #endregion #region Set Terms /// /// Indicates whether the term is set union /// public bool IsSetUnion { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_UNION; } } /// /// Indicates whether the term is set intersection /// public bool IsSetIntersect { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_INTERSECT; } } /// /// Indicates whether the term is set difference /// public bool IsSetDifference { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_DIFFERENCE; } } /// /// Indicates whether the term is set complement /// public bool IsSetComplement { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_COMPLEMENT; } } /// /// Indicates whether the term is set subset /// public bool IsSetSubset { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_SUBSET; } } #endregion #region Bit-vector terms /// /// Indicates whether the terms is of bit-vector sort. /// public bool IsBV { get { return Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == (uint)Z3_sort_kind.Z3_BV_SORT; } } /// /// Indicates whether the term is a bit-vector numeral /// public bool IsBVNumeral { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNUM; } } /// /// Indicates whether the term is a one-bit bit-vector with value one /// public bool IsBVBitOne { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BIT1; } } /// /// Indicates whether the term is a one-bit bit-vector with value zero /// public bool IsBVBitZero { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BIT0; } } /// /// Indicates whether the term is a bit-vector unary minus /// public bool IsBVUMinus { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNEG; } } /// /// Indicates whether the term is a bit-vector addition (binary) /// public bool IsBVAdd { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BADD; } } /// /// Indicates whether the term is a bit-vector subtraction (binary) /// public bool IsBVSub { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSUB; } } /// /// Indicates whether the term is a bit-vector multiplication (binary) /// public bool IsBVMul { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BMUL; } } /// /// Indicates whether the term is a bit-vector signed division (binary) /// public bool IsBVSDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSDIV; } } /// /// Indicates whether the term is a bit-vector unsigned division (binary) /// public bool IsBVUDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUDIV; } } /// /// Indicates whether the term is a bit-vector signed remainder (binary) /// public bool IsBVSRem { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSREM; } } /// /// Indicates whether the term is a bit-vector unsigned remainder (binary) /// public bool IsBVURem { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUREM; } } /// /// Indicates whether the term is a bit-vector signed modulus /// public bool IsBVSMod { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSMOD; } } /// /// Indicates whether the term is a bit-vector signed division by zero /// internal bool IsBVSDiv0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSDIV0; } } /// /// Indicates whether the term is a bit-vector unsigned division by zero /// internal bool IsBVUDiv0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUDIV0; } } /// /// Indicates whether the term is a bit-vector signed remainder by zero /// internal bool IsBVSRem0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSREM0; } } /// /// Indicates whether the term is a bit-vector unsigned remainder by zero /// internal bool IsBVURem0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUREM0; } } /// /// Indicates whether the term is a bit-vector signed modulus by zero /// internal bool IsBVSMod0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSMOD0; } } /// /// Indicates whether the term is an unsigned bit-vector less-than-or-equal /// public bool IsBVULE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ULEQ; } } /// /// Indicates whether the term is a signed bit-vector less-than-or-equal /// public bool IsBVSLE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SLEQ; } } /// /// Indicates whether the term is an unsigned bit-vector greater-than-or-equal /// public bool IsBVUGE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UGEQ; } } /// /// Indicates whether the term is a signed bit-vector greater-than-or-equal /// public bool IsBVSGE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SGEQ; } } /// /// Indicates whether the term is an unsigned bit-vector less-than /// public bool IsBVULT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ULT; } } /// /// Indicates whether the term is a signed bit-vector less-than /// public bool IsBVSLT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SLT; } } /// /// Indicates whether the term is an unsigned bit-vector greater-than /// public bool IsBVUGT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UGT; } } /// /// Indicates whether the term is a signed bit-vector greater-than /// public bool IsBVSGT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SGT; } } /// /// Indicates whether the term is a bit-wise AND /// public bool IsBVAND { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BAND; } } /// /// Indicates whether the term is a bit-wise OR /// public bool IsBVOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BOR; } } /// /// Indicates whether the term is a bit-wise NOT /// public bool IsBVNOT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNOT; } } /// /// Indicates whether the term is a bit-wise XOR /// public bool IsBVXOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BXOR; } } /// /// Indicates whether the term is a bit-wise NAND /// public bool IsBVNAND { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNAND; } } /// /// Indicates whether the term is a bit-wise NOR /// public bool IsBVNOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNOR; } } /// /// Indicates whether the term is a bit-wise XNOR /// public bool IsBVXNOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BXNOR; } } /// /// Indicates whether the term is a bit-vector concatenation (binary) /// public bool IsBVConcat { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CONCAT; } } /// /// Indicates whether the term is a bit-vector sign extension /// public bool IsBVSignExtension { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SIGN_EXT; } } /// /// Indicates whether the term is a bit-vector zero extension /// public bool IsBVZeroExtension { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ZERO_EXT; } } /// /// Indicates whether the term is a bit-vector extraction /// public bool IsBVExtract { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXTRACT; } } /// /// Indicates whether the term is a bit-vector repetition /// public bool IsBVRepeat { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_REPEAT; } } /// /// Indicates whether the term is a bit-vector reduce OR /// public bool IsBVReduceOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BREDOR; } } /// /// Indicates whether the term is a bit-vector reduce AND /// public bool IsBVReduceAND { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BREDAND; } } /// /// Indicates whether the term is a bit-vector comparison /// public bool IsBVComp { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BCOMP; } } /// /// Indicates whether the term is a bit-vector shift left /// public bool IsBVShiftLeft { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSHL; } } /// /// Indicates whether the term is a bit-vector logical shift right /// public bool IsBVShiftRightLogical { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BLSHR; } } /// /// Indicates whether the term is a bit-vector arithmetic shift left /// public bool IsBVShiftRightArithmetic { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BASHR; } } /// /// Indicates whether the term is a bit-vector rotate left /// public bool IsBVRotateLeft { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ROTATE_LEFT; } } /// /// Indicates whether the term is a bit-vector rotate right /// public bool IsBVRotateRight { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ROTATE_RIGHT; } } /// /// Indicates whether the term is a bit-vector rotate left (extended) /// /// Similar to Z3_OP_ROTATE_LEFT, but it is a binary operator instead of a parametric one. public bool IsBVRotateLeftExtended { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXT_ROTATE_LEFT; } } /// /// Indicates whether the term is a bit-vector rotate right (extended) /// /// Similar to Z3_OP_ROTATE_RIGHT, but it is a binary operator instead of a parametric one. public bool IsBVRotateRightExtended { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXT_ROTATE_RIGHT; } } /// /// Indicates whether the term is a coercion from integer to bit-vector /// /// This function is not supported by the decision procedures. Only the most /// rudimentary simplification rules are applied to this function. public bool IsIntToBV { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_INT2BV; } } /// /// Indicates whether the term is a coercion from bit-vector to integer /// /// This function is not supported by the decision procedures. Only the most /// rudimentary simplification rules are applied to this function. public bool IsBVToInt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BV2INT; } } /// /// Indicates whether the term is a bit-vector carry /// /// Compute the carry bit in a full-adder. The meaning is given by the /// equivalence (carry l1 l2 l3) <=> (or (and l1 l2) (and l1 l3) (and l2 l3))) public bool IsBVCarry { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CARRY; } } /// /// Indicates whether the term is a bit-vector ternary XOR /// /// The meaning is given by the equivalence (xor3 l1 l2 l3) <=> (xor (xor l1 l2) l3) public bool IsBVXOR3 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_XOR3; } } #endregion #region Labels /// /// Indicates whether the term is a label (used by the Boogie Verification condition generator). /// /// The label has two parameters, a string and a Boolean polarity. It takes one argument, a formula. public bool IsLabel { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LABEL; } } /// /// Indicates whether the term is a label literal (used by the Boogie Verification condition generator). /// /// A label literal has a set of string parameters. It takes no arguments. public bool IsLabelLit { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LABEL_LIT; } } #endregion #region Proof Terms /// /// Indicates whether the term is a binary equivalence modulo namings. /// /// This binary predicate is used in proof terms. /// It captures equisatisfiability and equivalence modulo renamings. public bool IsOEQ { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_OEQ; } } /// /// Indicates whether the term is a Proof for the expression 'true'. /// public bool IsProofTrue { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRUE; } } /// /// Indicates whether the term is a proof for a fact asserted by the user. /// public bool IsProofAsserted { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_ASSERTED; } } /// /// Indicates whether the term is a proof for a fact (tagged as goal) asserted by the user. /// public bool IsProofGoal { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_GOAL; } } /// /// Indicates whether the term is proof via modus ponens /// /// /// Given a proof for p and a proof for (implies p q), produces a proof for q. /// T1: p /// T2: (implies p q) /// [mp T1 T2]: q /// The second antecedents may also be a proof for (iff p q). public bool IsProofModusPonens { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS; } } /// /// Indicates whether the term is a proof for (R t t), where R is a reflexive relation. /// /// This proof object has no antecedents. /// The only reflexive relations that are used are /// equivalence modulo namings, equality and equivalence. /// That is, R is either '~', '=' or 'iff'. public bool IsProofReflexivity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REFLEXIVITY; } } /// /// Indicates whether the term is proof by symmetricity of a relation /// /// /// Given an symmetric relation R and a proof for (R t s), produces a proof for (R s t). /// T1: (R t s) /// [symmetry T1]: (R s t) /// T1 is the antecedent of this proof object. /// public bool IsProofSymmetry { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_SYMMETRY; } } /// /// Indicates whether the term is a proof by transitivity of a relation /// /// /// Given a transitive relation R, and proofs for (R t s) and (R s u), produces a proof /// for (R t u). /// T1: (R t s) /// T2: (R s u) /// [trans T1 T2]: (R t u) /// public bool IsProofTransitivity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY; } } /// /// Indicates whether the term is a proof by condensed transitivity of a relation /// /// /// Condensed transitivity proof. This proof object is only used if the parameter PROOF_MODE is 1. /// It combines several symmetry and transitivity proofs. /// Example: /// T1: (R a b) /// T2: (R c b) /// T3: (R c d) /// [trans* T1 T2 T3]: (R a d) /// R must be a symmetric and transitive relation. /// /// Assuming that this proof object is a proof for (R s t), then /// a proof checker must check if it is possible to prove (R s t) /// using the antecedents, symmetry and transitivity. That is, /// if there is a path from s to t, if we view every /// antecedent (R a b) as an edge between a and b. /// public bool IsProofTransitivityStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY_STAR; } } /// /// Indicates whether the term is a monotonicity proof object. /// /// /// T1: (R t_1 s_1) /// ... /// Tn: (R t_n s_n) /// [monotonicity T1 ... Tn]: (R (f t_1 ... t_n) (f s_1 ... s_n)) /// Remark: if t_i == s_i, then the antecedent Ti is suppressed. /// That is, reflexivity proofs are supressed to save space. /// public bool IsProofMonotonicity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MONOTONICITY; } } /// /// Indicates whether the term is a quant-intro proof /// /// /// Given a proof for (~ p q), produces a proof for (~ (forall (x) p) (forall (x) q)). /// T1: (~ p q) /// [quant-intro T1]: (~ (forall (x) p) (forall (x) q)) /// public bool IsProofQuantIntro { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_QUANT_INTRO; } } /// /// Indicates whether the term is a distributivity proof object. /// /// /// Given that f (= or) distributes over g (= and), produces a proof for /// (= (f a (g c d)) /// (g (f a c) (f a d))) /// If f and g are associative, this proof also justifies the following equality: /// (= (f (g a b) (g c d)) /// (g (f a c) (f a d) (f b c) (f b d))) /// where each f and g can have arbitrary number of arguments. /// /// This proof object has no antecedents. /// Remark. This rule is used by the CNF conversion pass and /// instantiated by f = or, and g = and. /// public bool IsProofDistributivity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DISTRIBUTIVITY; } } /// /// Indicates whether the term is a proof by elimination of AND /// /// /// Given a proof for (and l_1 ... l_n), produces a proof for l_i /// T1: (and l_1 ... l_n) /// [and-elim T1]: l_i /// public bool IsProofAndElimination { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_AND_ELIM; } } /// /// Indicates whether the term is a proof by eliminiation of not-or /// /// /// Given a proof for (not (or l_1 ... l_n)), produces a proof for (not l_i). /// T1: (not (or l_1 ... l_n)) /// [not-or-elim T1]: (not l_i) /// public bool IsProofOrElimination { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NOT_OR_ELIM; } } /// /// Indicates whether the term is a proof by rewriting /// /// /// A proof for a local rewriting step (= t s). /// The head function symbol of t is interpreted. /// /// This proof object has no antecedents. /// The conclusion of a rewrite rule is either an equality (= t s), /// an equivalence (iff t s), or equi-satisfiability (~ t s). /// Remark: if f is bool, then = is iff. /// /// Examples: /// (= (+ x 0) x) /// (= (+ x 1 2) (+ 3 x)) /// (iff (or x false) x) /// public bool IsProofRewrite { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REWRITE; } } /// /// Indicates whether the term is a proof by rewriting /// /// /// A proof for rewriting an expression t into an expression s. /// This proof object is used if the parameter PROOF_MODE is 1. /// This proof object can have n antecedents. /// The antecedents are proofs for equalities used as substitution rules. /// The object is also used in a few cases if the parameter PROOF_MODE is 2. /// The cases are: /// - When applying contextual simplification (CONTEXT_SIMPLIFIER=true) /// - When converting bit-vectors to Booleans (BIT2BOOL=true) /// - When pulling ite expression up (PULL_CHEAP_ITE_TREES=true) /// public bool IsProofRewriteStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REWRITE_STAR; } } /// /// Indicates whether the term is a proof for pulling quantifiers out. /// /// /// A proof for (iff (f (forall (x) q(x)) r) (forall (x) (f (q x) r))). This proof object has no antecedents. /// public bool IsProofPullQuant { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PULL_QUANT; } } /// /// Indicates whether the term is a proof for pulling quantifiers out. /// /// /// A proof for (iff P Q) where Q is in prenex normal form. /// This proof object is only used if the parameter PROOF_MODE is 1. /// This proof object has no antecedents /// public bool IsProofPullQuantStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PULL_QUANT_STAR; } } /// /// Indicates whether the term is a proof for pushing quantifiers in. /// /// /// A proof for: /// (iff (forall (x_1 ... x_m) (and p_1[x_1 ... x_m] ... p_n[x_1 ... x_m])) /// (and (forall (x_1 ... x_m) p_1[x_1 ... x_m]) /// ... /// (forall (x_1 ... x_m) p_n[x_1 ... x_m]))) /// This proof object has no antecedents /// public bool IsProofPushQuant { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PUSH_QUANT; } } /// /// Indicates whether the term is a proof for elimination of unused variables. /// /// /// A proof for (iff (forall (x_1 ... x_n y_1 ... y_m) p[x_1 ... x_n]) /// (forall (x_1 ... x_n) p[x_1 ... x_n])) /// /// It is used to justify the elimination of unused variables. /// This proof object has no antecedents. /// public bool IsProofElimUnusedVars { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_ELIM_UNUSED_VARS; } } /// /// Indicates whether the term is a proof for destructive equality resolution /// /// /// A proof for destructive equality resolution: /// (iff (forall (x) (or (not (= x t)) P[x])) P[t]) /// if x does not occur in t. /// /// This proof object has no antecedents. /// /// Several variables can be eliminated simultaneously. /// public bool IsProofDER { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DER; } } /// /// Indicates whether the term is a proof for quantifier instantiation /// /// /// A proof of (or (not (forall (x) (P x))) (P a)) /// public bool IsProofQuantInst { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_QUANT_INST; } } /// /// Indicates whether the term is a hypthesis marker. /// /// Mark a hypothesis in a natural deduction style proof. public bool IsProofHypothesis { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_HYPOTHESIS; } } /// /// Indicates whether the term is a proof by lemma /// /// /// T1: false /// [lemma T1]: (or (not l_1) ... (not l_n)) /// /// This proof object has one antecedent: a hypothetical proof for false. /// It converts the proof in a proof for (or (not l_1) ... (not l_n)), /// when T1 contains the hypotheses: l_1, ..., l_n. /// public bool IsProofLemma { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_LEMMA; } } /// /// Indicates whether the term is a proof by unit resolution /// /// /// T1: (or l_1 ... l_n l_1' ... l_m') /// T2: (not l_1) /// ... /// T(n+1): (not l_n) /// [unit-resolution T1 ... T(n+1)]: (or l_1' ... l_m') /// public bool IsProofUnitResolution { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_UNIT_RESOLUTION; } } /// /// Indicates whether the term is a proof by iff-true /// /// /// T1: p /// [iff-true T1]: (iff p true) /// public bool IsProofIFFTrue { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_TRUE; } } /// /// Indicates whether the term is a proof by iff-false /// /// /// T1: (not p) /// [iff-false T1]: (iff p false) /// public bool IsProofIFFFalse { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_FALSE; } } /// /// Indicates whether the term is a proof by commutativity /// /// /// [comm]: (= (f a b) (f b a)) /// /// f is a commutative operator. /// /// This proof object has no antecedents. /// Remark: if f is bool, then = is iff. /// public bool IsProofCommutativity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_COMMUTATIVITY; } } /// /// Indicates whether the term is a proof for Tseitin-like axioms /// /// /// Proof object used to justify Tseitin's like axioms: /// /// (or (not (and p q)) p) /// (or (not (and p q)) q) /// (or (not (and p q r)) p) /// (or (not (and p q r)) q) /// (or (not (and p q r)) r) /// ... /// (or (and p q) (not p) (not q)) /// (or (not (or p q)) p q) /// (or (or p q) (not p)) /// (or (or p q) (not q)) /// (or (not (iff p q)) (not p) q) /// (or (not (iff p q)) p (not q)) /// (or (iff p q) (not p) (not q)) /// (or (iff p q) p q) /// (or (not (ite a b c)) (not a) b) /// (or (not (ite a b c)) a c) /// (or (ite a b c) (not a) (not b)) /// (or (ite a b c) a (not c)) /// (or (not (not a)) (not a)) /// (or (not a) a) /// /// This proof object has no antecedents. /// Note: all axioms are propositional tautologies. /// Note also that 'and' and 'or' can take multiple arguments. /// You can recover the propositional tautologies by /// unfolding the Boolean connectives in the axioms a small /// bounded number of steps (=3). /// public bool IsProofDefAxiom { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DEF_AXIOM; } } /// /// Indicates whether the term is a proof for introduction of a name /// /// /// Introduces a name for a formula/term. /// Suppose e is an expression with free variables x, and def-intro /// introduces the name n(x). The possible cases are: /// /// When e is of Boolean type: /// [def-intro]: (and (or n (not e)) (or (not n) e)) /// /// or: /// [def-intro]: (or (not n) e) /// when e only occurs positively. /// /// When e is of the form (ite cond th el): /// [def-intro]: (and (or (not cond) (= n th)) (or cond (= n el))) /// /// Otherwise: /// [def-intro]: (= n e) /// public bool IsProofDefIntro { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DEF_INTRO; } } /// /// Indicates whether the term is a proof for application of a definition /// /// /// [apply-def T1]: F ~ n /// F is 'equivalent' to n, given that T1 is a proof that /// n is a name for F. /// public bool IsProofApplyDef { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_APPLY_DEF; } } /// /// Indicates whether the term is a proof iff-oeq /// /// /// T1: (iff p q) /// [iff~ T1]: (~ p q) /// public bool IsProofIFFOEQ { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_OEQ; } } /// /// Indicates whether the term is a proof for a positive NNF step /// /// /// Proof for a (positive) NNF step. Example: /// /// T1: (not s_1) ~ r_1 /// T2: (not s_2) ~ r_2 /// T3: s_1 ~ r_1' /// T4: s_2 ~ r_2' /// [nnf-pos T1 T2 T3 T4]: (~ (iff s_1 s_2) /// (and (or r_1 r_2') (or r_1' r_2))) /// /// The negation normal form steps NNF_POS and NNF_NEG are used in the following cases: /// (a) When creating the NNF of a positive force quantifier. /// The quantifier is retained (unless the bound variables are eliminated). /// Example /// T1: q ~ q_new /// [nnf-pos T1]: (~ (forall (x T) q) (forall (x T) q_new)) /// /// (b) When recursively creating NNF over Boolean formulas, where the top-level /// connective is changed during NNF conversion. The relevant Boolean connectives /// for NNF_POS are 'implies', 'iff', 'xor', 'ite'. /// NNF_NEG furthermore handles the case where negation is pushed /// over Boolean connectives 'and' and 'or'. /// public bool IsProofNNFPos { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_POS; } } /// /// Indicates whether the term is a proof for a negative NNF step /// /// /// Proof for a (negative) NNF step. Examples: /// /// T1: (not s_1) ~ r_1 /// ... /// Tn: (not s_n) ~ r_n /// [nnf-neg T1 ... Tn]: (not (and s_1 ... s_n)) ~ (or r_1 ... r_n) /// and /// T1: (not s_1) ~ r_1 /// ... /// Tn: (not s_n) ~ r_n /// [nnf-neg T1 ... Tn]: (not (or s_1 ... s_n)) ~ (and r_1 ... r_n) /// and /// T1: (not s_1) ~ r_1 /// T2: (not s_2) ~ r_2 /// T3: s_1 ~ r_1' /// T4: s_2 ~ r_2' /// [nnf-neg T1 T2 T3 T4]: (~ (not (iff s_1 s_2)) /// (and (or r_1 r_2) (or r_1' r_2'))) /// public bool IsProofNNFNeg { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_NEG; } } /// /// Indicates whether the term is a proof for (~ P Q) here Q is in negation normal form. /// /// /// A proof for (~ P Q) where Q is in negation normal form. /// /// This proof object is only used if the parameter PROOF_MODE is 1. /// /// This proof object may have n antecedents. Each antecedent is a PR_DEF_INTRO. /// public bool IsProofNNFStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_STAR; } } /// /// Indicates whether the term is a proof for (~ P Q) where Q is in conjunctive normal form. /// /// /// A proof for (~ P Q) where Q is in conjunctive normal form. /// This proof object is only used if the parameter PROOF_MODE is 1. /// This proof object may have n antecedents. Each antecedent is a PR_DEF_INTRO. /// public bool IsProofCNFStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_CNF_STAR; } } /// /// Indicates whether the term is a proof for a Skolemization step /// /// /// Proof for: /// /// [sk]: (~ (not (forall x (p x y))) (not (p (sk y) y))) /// [sk]: (~ (exists x (p x y)) (p (sk y) y)) /// /// This proof object has no antecedents. /// public bool IsProofSkolemize { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_SKOLEMIZE; } } /// /// Indicates whether the term is a proof by modus ponens for equi-satisfiability. /// /// /// Modus ponens style rule for equi-satisfiability. /// T1: p /// T2: (~ p q) /// [mp~ T1 T2]: q /// public bool IsProofModusPonensOEQ { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS_OEQ; } } /// /// Indicates whether the term is a proof for theory lemma /// /// /// Generic proof for theory lemmas. /// /// The theory lemma function comes with one or more parameters. /// The first parameter indicates the name of the theory. /// For the theory of arithmetic, additional parameters provide hints for /// checking the theory lemma. /// The hints for arithmetic are: /// - farkas - followed by rational coefficients. Multiply the coefficients to the /// inequalities in the lemma, add the (negated) inequalities and obtain a contradiction. /// - triangle-eq - Indicates a lemma related to the equivalence: /// (iff (= t1 t2) (and (<= t1 t2) (<= t2 t1))) /// - gcd-test - Indicates an integer linear arithmetic lemma that uses a gcd test. /// public bool IsProofTheoryLemma { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TH_LEMMA; } } #endregion #region Relational Terms /// /// Indicates whether the term is of relation sort. /// public bool IsRelation { get { return (Native.Z3_is_app(Context.nCtx, NativeObject) != 0 && Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == (uint)Z3_sort_kind.Z3_RELATION_SORT); } } /// /// Indicates whether the term is an relation store /// /// /// Insert a record into a relation. /// The function takes n+1 arguments, where the first argument is the relation and the remaining n elements /// correspond to the n columns of the relation. /// public bool IsRelationStore { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_STORE; } } /// /// Indicates whether the term is an empty relation /// public bool IsEmptyRelation { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_EMPTY; } } /// /// Indicates whether the term is a test for the emptiness of a relation /// public bool IsIsEmptyRelation { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_IS_EMPTY; } } /// /// Indicates whether the term is a relational join /// public bool IsRelationalJoin { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_JOIN; } } /// /// Indicates whether the term is the union or convex hull of two relations. /// /// The function takes two arguments. public bool IsRelationUnion { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_UNION; } } /// /// Indicates whether the term is the widening of two relations /// /// The function takes two arguments. public bool IsRelationWiden { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_WIDEN; } } /// /// Indicates whether the term is a projection of columns (provided as numbers in the parameters). /// /// The function takes one argument. public bool IsRelationProject { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_PROJECT; } } /// /// Indicates whether the term is a relation filter /// /// /// Filter (restrict) a relation with respect to a predicate. /// The first argument is a relation. /// The second argument is a predicate with free de-Brujin indices /// corresponding to the columns of the relation. /// So the first column in the relation has index 0. /// public bool IsRelationFilter { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_FILTER; } } /// /// Indicates whether the term is an intersection of a relation with the negation of another. /// /// /// Intersect the first relation with respect to negation /// of the second relation (the function takes two arguments). /// Logically, the specification can be described by a function /// /// target = filter_by_negation(pos, neg, columns) /// /// where columns are pairs c1, d1, .., cN, dN of columns from pos and neg, such that /// target are elements in x in pos, such that there is no y in neg that agrees with /// x on the columns c1, d1, .., cN, dN. /// public bool IsRelationNegationFilter { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_NEGATION_FILTER; } } /// /// Indicates whether the term is the renaming of a column in a relation /// /// /// The function takes one argument. /// The parameters contain the renaming as a cycle. /// public bool IsRelationRename { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_RENAME; } } /// /// Indicates whether the term is the complement of a relation /// public bool IsRelationComplement { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_COMPLEMENT; } } /// /// Indicates whether the term is a relational select /// /// /// Check if a record is an element of the relation. /// The function takes n+1 arguments, where the first argument is a relation, /// and the remaining n arguments correspond to a record. /// public bool IsRelationSelect { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_SELECT; } } /// /// Indicates whether the term is a relational clone (copy) /// /// /// Create a fresh copy (clone) of a relation. /// The function is logically the identity, but /// in the context of a register machine allows /// for terms of kind /// to perform destructive updates to the first argument. /// public bool IsRelationClone { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_CLONE; } } #endregion #region Finite domain terms /// /// Indicates whether the term is of an array sort. /// public bool IsFiniteDomain { get { return (Native.Z3_is_app(Context.nCtx, NativeObject) != 0 && Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == (uint)Z3_sort_kind.Z3_FINITE_DOMAIN_SORT); } } /// /// Indicates whether the term is a less than predicate over a finite domain. /// public bool IsFiniteDomainLT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FD_LT; } } #endregion #region Floating-point terms /// /// Indicates whether the terms is of floating-point sort. /// public bool IsFP { get { return Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == (uint)Z3_sort_kind.Z3_FLOATING_POINT_SORT; } } /// /// Indicates whether the terms is of floating-point rounding mode sort. /// public bool IsFPRM { get { return Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == (uint)Z3_sort_kind.Z3_ROUNDING_MODE_SORT; } } /// /// Indicates whether the term is a floating-point numeral /// public bool IsFPNumeral { get { return IsFP && IsNumeral; } } /// /// Indicates whether the term is a floating-point rounding mode numeral /// public bool IsFPRMNumeral { get { return IsFPRM && IsNumeral; } } /// /// Indicates whether the term is the floating-point rounding numeral roundNearestTiesToEven /// public bool IsFPRMRoundNearestTiesToEven{ get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN; } } /// /// Indicates whether the term is the floating-point rounding numeral roundNearestTiesToAway /// public bool IsFPRMRoundNearestTiesToAway{ get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardNegative /// public bool IsFPRMRoundTowardNegative{ get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_NEGATIVE; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardPositive /// public bool IsFPRMRoundTowardPositive{ get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_POSITIVE; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardZero /// public bool IsFPRMRoundTowardZero{ get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_ZERO; } } /// /// Indicates whether the term is the floating-point rounding numeral roundNearestTiesToEven /// public bool IsFPRMExprRNE{ get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN; } } /// /// Indicates whether the term is the floating-point rounding numeral roundNearestTiesToAway /// public bool IsFPRMExprRNA { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardNegative /// public bool IsFPRMExprRTN { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_NEGATIVE; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardPositive /// public bool IsFPRMExprRTP { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_POSITIVE; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardZero /// public bool IsFPRMExprRTZ { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_ZERO; } } /// /// Indicates whether the term is a floating-point rounding mode numeral /// public bool IsFPRMExpr { get { return IsApp && (FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY|| FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN || FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_POSITIVE || FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_NEGATIVE || FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_ZERO); } } /// /// Indicates whether the term is a floating-point +oo /// public bool IsFPPlusInfinity{ get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_PLUS_INF; } } /// /// Indicates whether the term is a floating-point -oo /// public bool IsFPMinusInfinity{ get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_MINUS_INF; } } /// /// Indicates whether the term is a floating-point NaN /// public bool IsFPNaN { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_NAN; } } /// /// Indicates whether the term is a floating-point +zero /// public bool IsFPPlusZero { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_PLUS_ZERO; } } /// /// Indicates whether the term is a floating-point -zero /// public bool IsFPMinusZero { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_MINUS_ZERO; } } /// /// Indicates whether the term is a floating-point addition term /// public bool IsFPAdd { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_ADD; } } /// /// Indicates whether the term is a floating-point subtraction term /// public bool IsFPSub { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_SUB; } } /// /// Indicates whether the term is a floating-point negation term /// public bool IsFPNeg { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_NEG; } } /// /// Indicates whether the term is a floating-point multiplication term /// public bool IsFPMul { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_MUL; } } /// /// Indicates whether the term is a floating-point divison term /// public bool IsFPDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_DIV; } } /// /// Indicates whether the term is a floating-point remainder term /// public bool IsFPRem { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_REM; } } /// /// Indicates whether the term is a floating-point term absolute value term /// public bool IsFPAbs { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_ABS; } } /// /// Indicates whether the term is a floating-point minimum term /// public bool IsFPMin { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_MIN; } } /// /// Indicates whether the term is a floating-point maximum term /// public bool IsFPMax { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_MAX; } } /// /// Indicates whether the term is a floating-point fused multiply-add term /// public bool IsFPFMA { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_FMA; } } /// /// Indicates whether the term is a floating-point square root term /// public bool IsFPSqrt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_SQRT; } } /// /// Indicates whether the term is a floating-point roundToIntegral term /// public bool IsFPRoundToIntegral { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_ROUND_TO_INTEGRAL; } } /// /// Indicates whether the term is a floating-point equality term /// public bool IsFPEq { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_EQ; } } /// /// Indicates whether the term is a floating-point less-than term /// public bool IsFPLt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_LT; } } /// /// Indicates whether the term is a floating-point greater-than term /// public bool IsFPGt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_GT; } } /// /// Indicates whether the term is a floating-point less-than or equal term /// public bool IsFPLe { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_LE; } } /// /// Indicates whether the term is a floating-point greater-than or erqual term /// public bool IsFPGe { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_GE; } } /// /// Indicates whether the term is a floating-point isNaN predicate term /// public bool IsFPisNaN { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_IS_NAN; } } /// /// Indicates whether the term is a floating-point isInf predicate term /// public bool IsFPisInf { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_IS_INF; } } /// /// Indicates whether the term is a floating-point isZero predicate term /// public bool IsFPisZero { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_IS_ZERO; } } /// /// Indicates whether the term is a floating-point isNormal term /// public bool IsFPisNormal { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_IS_NORMAL; } } /// /// Indicates whether the term is a floating-point isSubnormal predicate term /// public bool IsFPisSubnormal { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_IS_SUBNORMAL; } } /// /// Indicates whether the term is a floating-point isNegative predicate term /// public bool IsFPisNegative { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_IS_NEGATIVE; } } /// /// Indicates whether the term is a floating-point isPositive predicate term /// public bool IsFPisPositive { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_IS_POSITIVE; } } /// /// Indicates whether the term is a floating-point constructor term /// public bool IsFPFP { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_FP; } } /// /// Indicates whether the term is a floating-point conversion term /// public bool IsFPToFp { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_TO_FP; } } /// /// Indicates whether the term is a floating-point conversion from unsigned bit-vector term /// public bool IsFPToFpUnsigned { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_TO_FP_UNSIGNED; } } /// /// Indicates whether the term is a floating-point conversion to unsigned bit-vector term /// public bool IsFPToUBV { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_TO_UBV; } } /// /// Indicates whether the term is a floating-point conversion to signed bit-vector term /// public bool IsFPToSBV { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_TO_SBV; } } /// /// Indicates whether the term is a floating-point conversion to real term /// public bool IsFPToReal { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_TO_REAL; } } /// /// Indicates whether the term is a floating-point conversion to IEEE-754 bit-vector term /// public bool IsFPToIEEEBV { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_TO_IEEE_BV; } } #endregion #endregion #region Bound Variables /// /// The de-Burijn index of a bound variable. /// /// /// Bound variables are indexed by de-Bruijn indices. It is perhaps easiest to explain /// the meaning of de-Bruijn indices by indicating the compilation process from /// non-de-Bruijn formulas to de-Bruijn format. /// /// abs(forall (x1) phi) = forall (x1) abs1(phi, x1, 0) /// abs(forall (x1, x2) phi) = abs(forall (x1) abs(forall (x2) phi)) /// abs1(x, x, n) = b_n /// abs1(y, x, n) = y /// abs1(f(t1,...,tn), x, n) = f(abs1(t1,x,n), ..., abs1(tn,x,n)) /// abs1(forall (x1) phi, x, n) = forall (x1) (abs1(phi, x, n+1)) /// /// The last line is significant: the index of a bound variable is different depending /// on the scope in which it appears. The deeper x appears, the higher is its /// index. /// public uint Index { get { if (!IsVar) throw new Z3Exception("Term is not a bound variable."); Contract.EndContractBlock(); return Native.Z3_get_index_value(Context.nCtx, NativeObject); } } #endregion #region Internal /// /// Constructor for Expr /// internal protected Expr(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #if DEBUG [Pure] internal override void CheckNativeObject(IntPtr obj) { if (Native.Z3_is_app(Context.nCtx, obj) == 0 && Native.Z3_get_ast_kind(Context.nCtx, obj) != (uint)Z3_ast_kind.Z3_VAR_AST && Native.Z3_get_ast_kind(Context.nCtx, obj) != (uint)Z3_ast_kind.Z3_QUANTIFIER_AST) throw new Z3Exception("Underlying object is not a term"); base.CheckNativeObject(obj); } #endif [Pure] internal static Expr Create(Context ctx, FuncDecl f, params Expr[] arguments) { Contract.Requires(ctx != null); Contract.Requires(f != null); Contract.Ensures(Contract.Result() != null); IntPtr obj = Native.Z3_mk_app(ctx.nCtx, f.NativeObject, AST.ArrayLength(arguments), AST.ArrayToNative(arguments)); return Create(ctx, obj); } [Pure] new internal static Expr Create(Context ctx, IntPtr obj) { Contract.Requires(ctx != null); Contract.Ensures(Contract.Result() != null); Z3_ast_kind k = (Z3_ast_kind)Native.Z3_get_ast_kind(ctx.nCtx, obj); if (k == Z3_ast_kind.Z3_QUANTIFIER_AST) return new Quantifier(ctx, obj); IntPtr s = Native.Z3_get_sort(ctx.nCtx, obj); Z3_sort_kind sk = (Z3_sort_kind)Native.Z3_get_sort_kind(ctx.nCtx, s); if (Native.Z3_is_algebraic_number(ctx.nCtx, obj) != 0) // is this a numeral ast? return new AlgebraicNum(ctx, obj); if (Native.Z3_is_numeral_ast(ctx.nCtx, obj) != 0) { switch (sk) { case Z3_sort_kind.Z3_INT_SORT: return new IntNum(ctx, obj); case Z3_sort_kind.Z3_REAL_SORT: return new RatNum(ctx, obj); case Z3_sort_kind.Z3_BV_SORT: return new BitVecNum(ctx, obj); case Z3_sort_kind.Z3_FLOATING_POINT_SORT: return new FPNum(ctx, obj); case Z3_sort_kind.Z3_ROUNDING_MODE_SORT: return new FPRMNum(ctx, obj); } } switch (sk) { case Z3_sort_kind.Z3_BOOL_SORT: return new BoolExpr(ctx, obj); case Z3_sort_kind.Z3_INT_SORT: return new IntExpr(ctx, obj); case Z3_sort_kind.Z3_REAL_SORT: return new RealExpr(ctx, obj); case Z3_sort_kind.Z3_BV_SORT: return new BitVecExpr(ctx, obj); case Z3_sort_kind.Z3_ARRAY_SORT: return new ArrayExpr(ctx, obj); case Z3_sort_kind.Z3_DATATYPE_SORT: return new DatatypeExpr(ctx, obj); case Z3_sort_kind.Z3_FLOATING_POINT_SORT: return new FPExpr(ctx, obj); case Z3_sort_kind.Z3_ROUNDING_MODE_SORT: return new FPRMExpr(ctx, obj); } return new Expr(ctx, obj); } #endregion } } z3-4.4.0/src/api/dotnet/Solver.cs0000644000175000017500000002626612540347414016317 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Solver.cs Abstract: Z3 Managed API: Solvers Author: Christoph Wintersteiger (cwinter) 2012-03-22 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Solvers. /// [ContractVerification(true)] public class Solver : Z3Object { /// /// A string that describes all available solver parameters. /// public string Help { get { Contract.Ensures(Contract.Result() != null); return Native.Z3_solver_get_help(Context.nCtx, NativeObject); } } /// /// Sets the solver parameters. /// public Params Parameters { set { Contract.Requires(value != null); Context.CheckContextMatch(value); Native.Z3_solver_set_params(Context.nCtx, NativeObject, value.NativeObject); } } /// /// Retrieves parameter descriptions for solver. /// public ParamDescrs ParameterDescriptions { get { return new ParamDescrs(Context, Native.Z3_solver_get_param_descrs(Context.nCtx, NativeObject)); } } /// /// The current number of backtracking points (scopes). /// /// /// public uint NumScopes { get { return Native.Z3_solver_get_num_scopes(Context.nCtx, NativeObject); } } /// /// Creates a backtracking point. /// /// public void Push() { Native.Z3_solver_push(Context.nCtx, NativeObject); } /// /// Backtracks backtracking points. /// /// Note that an exception is thrown if is not smaller than NumScopes /// public void Pop(uint n = 1) { Native.Z3_solver_pop(Context.nCtx, NativeObject, n); } /// /// Resets the Solver. /// /// This removes all assertions from the solver. public void Reset() { Native.Z3_solver_reset(Context.nCtx, NativeObject); } /// /// Assert a constraint (or multiple) into the solver. /// public void Assert(params BoolExpr[] constraints) { Contract.Requires(constraints != null); Contract.Requires(Contract.ForAll(constraints, c => c != null)); Context.CheckContextMatch(constraints); foreach (BoolExpr a in constraints) { Native.Z3_solver_assert(Context.nCtx, NativeObject, a.NativeObject); } } /// /// Alias for Assert. /// public void Add(params BoolExpr[] constraints) { Assert(constraints); } /// /// Assert multiple constraints into the solver, and track them (in the unsat) core /// using the Boolean constants in ps. /// /// /// This API is an alternative to with assumptions for extracting unsat cores. /// Both APIs can be used in the same solver. The unsat core will contain a combination /// of the Boolean variables provided using /// and the Boolean literals /// provided using with assumptions. /// public void AssertAndTrack(BoolExpr[] constraints, BoolExpr[] ps) { Contract.Requires(constraints != null); Contract.Requires(Contract.ForAll(constraints, c => c != null)); Contract.Requires(Contract.ForAll(ps, c => c != null)); Context.CheckContextMatch(constraints); Context.CheckContextMatch(ps); if (constraints.Length != ps.Length) throw new Z3Exception("Argument size mismatch"); for (int i = 0 ; i < constraints.Length; i++) Native.Z3_solver_assert_and_track(Context.nCtx, NativeObject, constraints[i].NativeObject, ps[i].NativeObject); } /// /// Assert a constraint into the solver, and track it (in the unsat) core /// using the Boolean constant p. /// /// /// This API is an alternative to with assumptions for extracting unsat cores. /// Both APIs can be used in the same solver. The unsat core will contain a combination /// of the Boolean variables provided using /// and the Boolean literals /// provided using with assumptions. /// public void AssertAndTrack(BoolExpr constraint, BoolExpr p) { Contract.Requires(constraint != null); Contract.Requires(p != null); Context.CheckContextMatch(constraint); Context.CheckContextMatch(p); Native.Z3_solver_assert_and_track(Context.nCtx, NativeObject, constraint.NativeObject, p.NativeObject); } /// /// The number of assertions in the solver. /// public uint NumAssertions { get { ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_assertions(Context.nCtx, NativeObject)); return assertions.Size; } } /// /// The set of asserted formulas. /// public BoolExpr[] Assertions { get { Contract.Ensures(Contract.Result() != null); ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_assertions(Context.nCtx, NativeObject)); return assertions.ToBoolExprArray(); } } /// /// Checks whether the assertions in the solver are consistent or not. /// /// /// /// /// /// public Status Check(params Expr[] assumptions) { Z3_lbool r; if (assumptions == null || assumptions.Length == 0) r = (Z3_lbool)Native.Z3_solver_check(Context.nCtx, NativeObject); else r = (Z3_lbool)Native.Z3_solver_check_assumptions(Context.nCtx, NativeObject, (uint)assumptions.Length, AST.ArrayToNative(assumptions)); switch (r) { case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE; case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } /// /// The model of the last Check. /// /// /// The result is null if Check was not invoked before, /// if its results was not SATISFIABLE, or if model production is not enabled. /// public Model Model { get { IntPtr x = Native.Z3_solver_get_model(Context.nCtx, NativeObject); if (x == IntPtr.Zero) return null; else return new Model(Context, x); } } /// /// The proof of the last Check. /// /// /// The result is null if Check was not invoked before, /// if its results was not UNSATISFIABLE, or if proof production is disabled. /// public Expr Proof { get { IntPtr x = Native.Z3_solver_get_proof(Context.nCtx, NativeObject); if (x == IntPtr.Zero) return null; else return Expr.Create(Context, x); } } /// /// The unsat core of the last Check. /// /// /// The unsat core is a subset of Assertions /// The result is empty if Check was not invoked before, /// if its results was not UNSATISFIABLE, or if core production is disabled. /// public BoolExpr[] UnsatCore { get { Contract.Ensures(Contract.Result() != null); ASTVector core = new ASTVector(Context, Native.Z3_solver_get_unsat_core(Context.nCtx, NativeObject)); return core.ToBoolExprArray(); } } /// /// A brief justification of why the last call to Check returned UNKNOWN. /// public string ReasonUnknown { get { Contract.Ensures(Contract.Result() != null); return Native.Z3_solver_get_reason_unknown(Context.nCtx, NativeObject); } } /// /// Solver statistics. /// public Statistics Statistics { get { Contract.Ensures(Contract.Result() != null); return new Statistics(Context, Native.Z3_solver_get_statistics(Context.nCtx, NativeObject)); } } /// /// A string representation of the solver. /// public override string ToString() { return Native.Z3_solver_to_string(Context.nCtx, NativeObject); } #region Internal internal Solver(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_solver_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_solver_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Solver_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Solver_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-4.4.0/src/api/dotnet/Deprecated.cs0000644000175000017500000000676412540347414017106 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Deprecated.cs Abstract: Expose deprecated features for use from the managed API those who use them for experiments. Author: Christoph Wintersteiger (cwinter) 2012-03-15 Notes: --*/ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// The main interaction with Z3 happens via the Context. /// [ContractVerification(true)] public class Deprecated { /// /// Creates a backtracking point. /// /// public static void Push(Context ctx) { Native.Z3_push(ctx.nCtx); } /// /// Backtracks backtracking points. /// /// Note that an exception is thrown if is not smaller than NumScopes /// public static void Pop(Context ctx, uint n = 1) { Native.Z3_pop(ctx.nCtx, n); } /// /// Assert a constraint (or multiple) into the solver. /// public static void Assert(Context ctx, params BoolExpr[] constraints) { Contract.Requires(constraints != null); Contract.Requires(Contract.ForAll(constraints, c => c != null)); ctx.CheckContextMatch(constraints); foreach (BoolExpr a in constraints) { Native.Z3_assert_cnstr(ctx.nCtx, a.NativeObject); } } /// /// Checks whether the assertions in the context are consistent or not. /// public static Status Check(Context ctx, List core, ref Model model, ref Expr proof, params Expr[] assumptions) { Z3_lbool r; model = null; proof = null; if (assumptions == null || assumptions.Length == 0) r = (Z3_lbool)Native.Z3_check(ctx.nCtx); else { IntPtr mdl = IntPtr.Zero, prf = IntPtr.Zero; uint core_size = 0; IntPtr[] native_core = new IntPtr[assumptions.Length]; r = (Z3_lbool)Native.Z3_check_assumptions(ctx.nCtx, (uint)assumptions.Length, AST.ArrayToNative(assumptions), ref mdl, ref prf, ref core_size, native_core); for (uint i = 0; i < core_size; i++) core.Add((BoolExpr)Expr.Create(ctx, native_core[i])); if (mdl != IntPtr.Zero) { model = new Model(ctx, mdl); } if (prf != IntPtr.Zero) { proof = Expr.Create(ctx, prf); } } switch (r) { case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE; case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } /// /// Retrieves an assignment to atomic propositions for a satisfiable context. /// public static BoolExpr GetAssignment(Context ctx) { IntPtr x = Native.Z3_get_context_assignment(ctx.nCtx); return (BoolExpr)Expr.Create(ctx, x); } } }z3-4.4.0/src/api/dotnet/BoolExpr.cs0000644000175000017500000000124612540347414016566 0ustar michaelmichael/*++ Copyright () 2012 Microsoft Corporation Module Name: BoolExpr.cs Abstract: Z3 Managed API: Boolean Expressions Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Boolean expressions /// public class BoolExpr : Expr { #region Internal /// Constructor for BoolExpr internal BoolExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/Quantifier.cs0000644000175000017500000002220212540347414017136 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Quantifier.cs Abstract: Z3 Managed API: Quantifiers Author: Christoph Wintersteiger (cwinter) 2012-03-19 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Quantifier expressions. /// [ContractVerification(true)] public class Quantifier : BoolExpr { /// /// Indicates whether the quantifier is universal. /// public bool IsUniversal { get { return Native.Z3_is_quantifier_forall(Context.nCtx, NativeObject) != 0; } } /// /// Indicates whether the quantifier is existential. /// public bool IsExistential { get { return !IsUniversal; } } /// /// The weight of the quantifier. /// public uint Weight { get { return Native.Z3_get_quantifier_weight(Context.nCtx, NativeObject); } } /// /// The number of patterns. /// public uint NumPatterns { get { return Native.Z3_get_quantifier_num_patterns(Context.nCtx, NativeObject); } } /// /// The patterns. /// public Pattern[] Patterns { get { Contract.Ensures(Contract.Result() != null); uint n = NumPatterns; Pattern[] res = new Pattern[n]; for (uint i = 0; i < n; i++) res[i] = new Pattern(Context, Native.Z3_get_quantifier_pattern_ast(Context.nCtx, NativeObject, i)); return res; } } /// /// The number of no-patterns. /// public uint NumNoPatterns { get { return Native.Z3_get_quantifier_num_no_patterns(Context.nCtx, NativeObject); } } /// /// The no-patterns. /// public Pattern[] NoPatterns { get { Contract.Ensures(Contract.Result() != null); uint n = NumNoPatterns; Pattern[] res = new Pattern[n]; for (uint i = 0; i < n; i++) res[i] = new Pattern(Context, Native.Z3_get_quantifier_no_pattern_ast(Context.nCtx, NativeObject, i)); return res; } } /// /// The number of bound variables. /// public uint NumBound { get { return Native.Z3_get_quantifier_num_bound(Context.nCtx, NativeObject); } } /// /// The symbols for the bound variables. /// public Symbol[] BoundVariableNames { get { Contract.Ensures(Contract.Result() != null); uint n = NumBound; Symbol[] res = new Symbol[n]; for (uint i = 0; i < n; i++) res[i] = Symbol.Create(Context, Native.Z3_get_quantifier_bound_name(Context.nCtx, NativeObject, i)); return res; } } /// /// The sorts of the bound variables. /// public Sort[] BoundVariableSorts { get { Contract.Ensures(Contract.Result() != null); uint n = NumBound; Sort[] res = new Sort[n]; for (uint i = 0; i < n; i++) res[i] = Sort.Create(Context, Native.Z3_get_quantifier_bound_sort(Context.nCtx, NativeObject, i)); return res; } } /// /// The body of the quantifier. /// public BoolExpr Body { get { Contract.Ensures(Contract.Result() != null); return new BoolExpr(Context, Native.Z3_get_quantifier_body(Context.nCtx, NativeObject)); } } #region Internal [ContractVerification(false)] // F: Clousot ForAll decompilation gets confused below. Setting verification off until I fixed the bug internal Quantifier(Context ctx, bool isForall, Sort[] sorts, Symbol[] names, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) : base(ctx, IntPtr.Zero) { Contract.Requires(ctx != null); Contract.Requires(sorts != null); Contract.Requires(names != null); Contract.Requires(body != null); Contract.Requires(sorts.Length == names.Length); Contract.Requires(Contract.ForAll(sorts, s => s != null)); Contract.Requires(Contract.ForAll(names, n => n != null)); Contract.Requires(patterns == null || Contract.ForAll(patterns, p => p != null)); Contract.Requires(noPatterns == null || Contract.ForAll(noPatterns, np => np != null)); Context.CheckContextMatch(patterns); Context.CheckContextMatch(noPatterns); Context.CheckContextMatch(sorts); Context.CheckContextMatch(names); Context.CheckContextMatch(body); if (sorts.Length != names.Length) throw new Z3Exception("Number of sorts does not match number of names"); if (noPatterns == null && quantifierID == null && skolemID == null) { NativeObject = Native.Z3_mk_quantifier(ctx.nCtx, (isForall) ? 1 : 0, weight, AST.ArrayLength(patterns), AST.ArrayToNative(patterns), AST.ArrayLength(sorts), AST.ArrayToNative(sorts), Symbol.ArrayToNative(names), body.NativeObject); } else { NativeObject = Native.Z3_mk_quantifier_ex(ctx.nCtx, (isForall) ? 1 : 0, weight, AST.GetNativeObject(quantifierID), AST.GetNativeObject(skolemID), AST.ArrayLength(patterns), AST.ArrayToNative(patterns), AST.ArrayLength(noPatterns), AST.ArrayToNative(noPatterns), AST.ArrayLength(sorts), AST.ArrayToNative(sorts), Symbol.ArrayToNative(names), body.NativeObject); } } [ContractVerification(false)] // F: Clousot ForAll decompilation gets confused below. Setting verification off until I fixed the bug internal Quantifier(Context ctx, bool isForall, Expr[] bound, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) : base(ctx, IntPtr.Zero) { Contract.Requires(ctx != null); Contract.Requires(body != null); Contract.Requires(patterns == null || Contract.ForAll(patterns, p => p != null)); Contract.Requires(noPatterns == null || Contract.ForAll(noPatterns, np => np != null)); Contract.Requires(bound == null || Contract.ForAll(bound, n => n != null)); Context.CheckContextMatch(noPatterns); Context.CheckContextMatch(patterns); //Context.CheckContextMatch(bound); Context.CheckContextMatch(body); if (noPatterns == null && quantifierID == null && skolemID == null) { NativeObject = Native.Z3_mk_quantifier_const(ctx.nCtx, (isForall) ? 1 : 0, weight, AST.ArrayLength(bound), AST.ArrayToNative(bound), AST.ArrayLength(patterns), AST.ArrayToNative(patterns), body.NativeObject); } else { NativeObject = Native.Z3_mk_quantifier_const_ex(ctx.nCtx, (isForall) ? 1 : 0, weight, AST.GetNativeObject(quantifierID), AST.GetNativeObject(skolemID), AST.ArrayLength(bound), AST.ArrayToNative(bound), AST.ArrayLength(patterns), AST.ArrayToNative(patterns), AST.ArrayLength(noPatterns), AST.ArrayToNative(noPatterns), body.NativeObject); } } internal Quantifier(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #if DEBUG internal override void CheckNativeObject(IntPtr obj) { if ((Z3_ast_kind)Native.Z3_get_ast_kind(Context.nCtx, obj) != Z3_ast_kind.Z3_QUANTIFIER_AST) throw new Z3Exception("Underlying object is not a quantifier"); base.CheckNativeObject(obj); } #endif #endregion } } z3-4.4.0/src/api/dotnet/Model.cs0000644000175000017500000002627612540347414016106 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Model.cs Abstract: Z3 Managed API: Models Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// A Model contains interpretations (assignments) of constants and functions. /// [ContractVerification(true)] public class Model : Z3Object { /// /// Retrieves the interpretation (the assignment) of in the model. /// /// A Constant /// An expression if the constant has an interpretation in the model, null otherwise. public Expr ConstInterp(Expr a) { Contract.Requires(a != null); Context.CheckContextMatch(a); return ConstInterp(a.FuncDecl); } /// /// Retrieves the interpretation (the assignment) of in the model. /// /// A function declaration of zero arity /// An expression if the function has an interpretation in the model, null otherwise. public Expr ConstInterp(FuncDecl f) { Contract.Requires(f != null); Context.CheckContextMatch(f); if (f.Arity != 0 || Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_range(Context.nCtx, f.NativeObject)) == (uint)Z3_sort_kind.Z3_ARRAY_SORT) throw new Z3Exception("Non-zero arity functions and arrays have FunctionInterpretations as a model. Use FuncInterp."); IntPtr n = Native.Z3_model_get_const_interp(Context.nCtx, NativeObject, f.NativeObject); if (n == IntPtr.Zero) return null; else return Expr.Create(Context, n); } /// /// Retrieves the interpretation (the assignment) of a non-constant in the model. /// /// A function declaration of non-zero arity /// A FunctionInterpretation if the function has an interpretation in the model, null otherwise. public FuncInterp FuncInterp(FuncDecl f) { Contract.Requires(f != null); Context.CheckContextMatch(f); Z3_sort_kind sk = (Z3_sort_kind)Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_range(Context.nCtx, f.NativeObject)); if (f.Arity == 0) { IntPtr n = Native.Z3_model_get_const_interp(Context.nCtx, NativeObject, f.NativeObject); if (sk == Z3_sort_kind.Z3_ARRAY_SORT) { if (n == IntPtr.Zero) return null; else { if (Native.Z3_is_as_array(Context.nCtx, n) == 0) throw new Z3Exception("Argument was not an array constant"); IntPtr fd = Native.Z3_get_as_array_func_decl(Context.nCtx, n); return FuncInterp(new FuncDecl(Context, fd)); } } else { throw new Z3Exception("Constant functions do not have a function interpretation; use ConstInterp"); } } else { IntPtr n = Native.Z3_model_get_func_interp(Context.nCtx, NativeObject, f.NativeObject); if (n == IntPtr.Zero) return null; else return new FuncInterp(Context, n); } } /// /// The number of constants that have an interpretation in the model. /// public uint NumConsts { get { return Native.Z3_model_get_num_consts(Context.nCtx, NativeObject); } } /// /// The function declarations of the constants in the model. /// public FuncDecl[] ConstDecls { get { Contract.Ensures(Contract.Result() != null); uint n = NumConsts; FuncDecl[] res = new FuncDecl[n]; for (uint i = 0; i < n; i++) res[i] = new FuncDecl(Context, Native.Z3_model_get_const_decl(Context.nCtx, NativeObject, i)); return res; } } /// /// The number of function interpretations in the model. /// public uint NumFuncs { get { return Native.Z3_model_get_num_funcs(Context.nCtx, NativeObject); } } /// /// The function declarations of the function interpretations in the model. /// public FuncDecl[] FuncDecls { get { Contract.Ensures(Contract.Result() != null); uint n = NumFuncs; FuncDecl[] res = new FuncDecl[n]; for (uint i = 0; i < n; i++) res[i] = new FuncDecl(Context, Native.Z3_model_get_func_decl(Context.nCtx, NativeObject, i)); return res; } } /// /// All symbols that have an interpretation in the model. /// public FuncDecl[] Decls { get { Contract.Ensures(Contract.Result() != null); uint nFuncs = NumFuncs; uint nConsts = NumConsts; uint n = nFuncs + nConsts; FuncDecl[] res = new FuncDecl[n]; for (uint i = 0; i < nConsts; i++) res[i] = new FuncDecl(Context, Native.Z3_model_get_const_decl(Context.nCtx, NativeObject, i)); for (uint i = 0; i < nFuncs; i++) res[nConsts + i] = new FuncDecl(Context, Native.Z3_model_get_func_decl(Context.nCtx, NativeObject, i)); return res; } } /// /// A ModelEvaluationFailedException is thrown when an expression cannot be evaluated by the model. /// public class ModelEvaluationFailedException : Z3Exception { /// /// An exception that is thrown when model evaluation fails. /// public ModelEvaluationFailedException() : base() { } } /// /// Evaluates the expression in the current model. /// /// /// This function may fail if contains quantifiers, /// is partial (MODEL_PARTIAL enabled), or if is not well-sorted. /// In this case a ModelEvaluationFailedException is thrown. /// /// An expression /// /// When this flag is enabled, a model value will be assigned to any constant /// or function that does not have an interpretation in the model. /// /// The evaluation of in the model. public Expr Eval(Expr t, bool completion = false) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); IntPtr v = IntPtr.Zero; if (Native.Z3_model_eval(Context.nCtx, NativeObject, t.NativeObject, (completion) ? 1 : 0, ref v) == 0) throw new ModelEvaluationFailedException(); else return Expr.Create(Context, v); } /// /// Alias for Eval. /// public Expr Evaluate(Expr t, bool completion = false) { Contract.Requires(t != null); Contract.Ensures(Contract.Result() != null); return Eval(t, completion); } /// /// The number of uninterpreted sorts that the model has an interpretation for. /// public uint NumSorts { get { return Native.Z3_model_get_num_sorts(Context.nCtx, NativeObject); } } /// /// The uninterpreted sorts that the model has an interpretation for. /// /// /// Z3 also provides an intepretation for uninterpreted sorts used in a formula. /// The interpretation for a sort is a finite set of distinct values. We say this finite set is /// the "universe" of the sort. /// /// /// public Sort[] Sorts { get { Contract.Ensures(Contract.Result() != null); uint n = NumSorts; Sort[] res = new Sort[n]; for (uint i = 0; i < n; i++) res[i] = Sort.Create(Context, Native.Z3_model_get_sort(Context.nCtx, NativeObject, i)); return res; } } /// /// The finite set of distinct values that represent the interpretation for sort . /// /// /// An uninterpreted sort /// An array of expressions, where each is an element of the universe of public Expr[] SortUniverse(Sort s) { Contract.Requires(s != null); Contract.Ensures(Contract.Result() != null); ASTVector av = new ASTVector(Context, Native.Z3_model_get_sort_universe(Context.nCtx, NativeObject, s.NativeObject)); return av.ToExprArray(); } /// /// Conversion of models to strings. /// /// A string representation of the model. public override string ToString() { return Native.Z3_model_to_string(Context.nCtx, NativeObject); } #region Internal internal Model(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_model_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_model_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Model_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Model_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-4.4.0/src/api/dotnet/FPExpr.cs0000644000175000017500000000201412540347414016172 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPExpr.cs Abstract: Z3 Managed API: Floating Point Expressions Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// FloatingPoint Expressions /// public class FPExpr : Expr { /// /// The number of exponent bits. /// public uint EBits { get { return ((FPSort)Sort).EBits; } } /// /// The number of significand bits. /// public uint SBits { get { return ((FPSort)Sort).EBits; } } #region Internal /// Constructor for FPExpr internal FPExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/FuncDecl.cs0000644000175000017500000003050112540347414016513 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: FuncDecl.cs Abstract: Z3 Managed API: Function Declarations Author: Christoph Wintersteiger (cwinter) 2012-03-16 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Function declarations. /// [ContractVerification(true)] public class FuncDecl : AST { /// /// Comparison operator. /// /// True if and share the same context and are equal, false otherwise. public static bool operator ==(FuncDecl a, FuncDecl b) { return Object.ReferenceEquals(a, b) || (!Object.ReferenceEquals(a, null) && !Object.ReferenceEquals(b, null) && a.Context.nCtx == b.Context.nCtx && Native.Z3_is_eq_func_decl(a.Context.nCtx, a.NativeObject, b.NativeObject) != 0); } /// /// Comparison operator. /// /// True if and do not share the same context or are not equal, false otherwise. public static bool operator !=(FuncDecl a, FuncDecl b) { return !(a == b); } /// /// Object comparison. /// public override bool Equals(object o) { FuncDecl casted = o as FuncDecl; if (casted == null) return false; return this == casted; } /// /// A hash code. /// public override int GetHashCode() { return base.GetHashCode(); } /// /// A string representations of the function declaration. /// public override string ToString() { return Native.Z3_func_decl_to_string(Context.nCtx, NativeObject); } /// /// Returns a unique identifier for the function declaration. /// new public uint Id { get { return Native.Z3_get_func_decl_id(Context.nCtx, NativeObject); } } /// /// The arity of the function declaration /// public uint Arity { get { return Native.Z3_get_arity(Context.nCtx, NativeObject); } } /// /// The size of the domain of the function declaration /// /// public uint DomainSize { get { return Native.Z3_get_domain_size(Context.nCtx, NativeObject); } } /// /// The domain of the function declaration /// public Sort[] Domain { get { Contract.Ensures(Contract.Result() != null); uint n = DomainSize; Sort[] res = new Sort[n]; for (uint i = 0; i < n; i++) res[i] = Sort.Create(Context, Native.Z3_get_domain(Context.nCtx, NativeObject, i)); return res; } } /// /// The range of the function declaration /// public Sort Range { get { Contract.Ensures(Contract.Result() != null); return Sort.Create(Context, Native.Z3_get_range(Context.nCtx, NativeObject)); } } /// /// The kind of the function declaration. /// public Z3_decl_kind DeclKind { get { return (Z3_decl_kind)Native.Z3_get_decl_kind(Context.nCtx, NativeObject); } } /// /// The name of the function declaration /// public Symbol Name { get { Contract.Ensures(Contract.Result() != null); return Symbol.Create(Context, Native.Z3_get_decl_name(Context.nCtx, NativeObject)); } } /// /// The number of parameters of the function declaration /// public uint NumParameters { get { return Native.Z3_get_decl_num_parameters(Context.nCtx, NativeObject); } } /// /// The parameters of the function declaration /// public Parameter[] Parameters { get { Contract.Ensures(Contract.Result() != null); uint num = NumParameters; Parameter[] res = new Parameter[num]; for (uint i = 0; i < num; i++) { Z3_parameter_kind k = (Z3_parameter_kind)Native.Z3_get_decl_parameter_kind(Context.nCtx, NativeObject, i); switch (k) { case Z3_parameter_kind.Z3_PARAMETER_INT: res[i] = new Parameter(k, Native.Z3_get_decl_int_parameter(Context.nCtx, NativeObject, i)); break; case Z3_parameter_kind.Z3_PARAMETER_DOUBLE: res[i] = new Parameter(k, Native.Z3_get_decl_double_parameter(Context.nCtx, NativeObject, i)); break; case Z3_parameter_kind.Z3_PARAMETER_SYMBOL: res[i] = new Parameter(k, Symbol.Create(Context, Native.Z3_get_decl_symbol_parameter(Context.nCtx, NativeObject, i))); break; case Z3_parameter_kind.Z3_PARAMETER_SORT: res[i] = new Parameter(k, Sort.Create(Context, Native.Z3_get_decl_sort_parameter(Context.nCtx, NativeObject, i))); break; case Z3_parameter_kind.Z3_PARAMETER_AST: res[i] = new Parameter(k, new AST(Context, Native.Z3_get_decl_ast_parameter(Context.nCtx, NativeObject, i))); break; case Z3_parameter_kind.Z3_PARAMETER_FUNC_DECL: res[i] = new Parameter(k, new FuncDecl(Context, Native.Z3_get_decl_func_decl_parameter(Context.nCtx, NativeObject, i))); break; case Z3_parameter_kind.Z3_PARAMETER_RATIONAL: res[i] = new Parameter(k, Native.Z3_get_decl_rational_parameter(Context.nCtx, NativeObject, i)); break; default: throw new Z3Exception("Unknown function declaration parameter kind encountered"); } } return res; } } /// /// Function declarations can have Parameters associated with them. /// public class Parameter { readonly private Z3_parameter_kind kind; readonly private int i; readonly private double d; readonly private Symbol sym; readonly private Sort srt; readonly private AST ast; readonly private FuncDecl fd; readonly private string r; /// The int value of the parameter. public int Int { get { if (ParameterKind != Z3_parameter_kind.Z3_PARAMETER_INT) throw new Z3Exception("parameter is not an int"); return i; } } /// The double value of the parameter. public double Double { get { if (ParameterKind != Z3_parameter_kind.Z3_PARAMETER_DOUBLE) throw new Z3Exception("parameter is not a double "); return d; } } /// The Symbol value of the parameter. public Symbol Symbol { get { if (ParameterKind != Z3_parameter_kind.Z3_PARAMETER_SYMBOL) throw new Z3Exception("parameter is not a Symbol"); return sym; } } /// The Sort value of the parameter. public Sort Sort { get { if (ParameterKind != Z3_parameter_kind.Z3_PARAMETER_SORT) throw new Z3Exception("parameter is not a Sort"); return srt; } } /// The AST value of the parameter. public AST AST { get { if (ParameterKind != Z3_parameter_kind.Z3_PARAMETER_AST) throw new Z3Exception("parameter is not an AST"); return ast; } } /// The FunctionDeclaration value of the parameter. public FuncDecl FuncDecl { get { if (ParameterKind != Z3_parameter_kind.Z3_PARAMETER_FUNC_DECL) throw new Z3Exception("parameter is not a function declaration"); return fd; } } /// The rational string value of the parameter. public string Rational { get { if (ParameterKind != Z3_parameter_kind.Z3_PARAMETER_RATIONAL) throw new Z3Exception("parameter is not a rational string"); return r; } } /// /// The kind of the parameter. /// public Z3_parameter_kind ParameterKind { get { return kind; } } #region Internal internal Parameter(Z3_parameter_kind k, int i) { this.kind = k; this.i = i; } internal Parameter(Z3_parameter_kind k, double d) { this.kind = k; this.d = d; } internal Parameter(Z3_parameter_kind k, Symbol s) { this.kind = k; this.sym = s; } internal Parameter(Z3_parameter_kind k, Sort s) { this.kind = k; this.srt = s; } internal Parameter(Z3_parameter_kind k, AST a) { this.kind = k; this.ast = a; } internal Parameter(Z3_parameter_kind k, FuncDecl fd) { this.kind = k; this.fd = fd; } internal Parameter(Z3_parameter_kind k, string r) { this.kind = k; this.r = r; } #endregion } #region Internal internal FuncDecl(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal FuncDecl(Context ctx, Symbol name, Sort[] domain, Sort range) : base(ctx, Native.Z3_mk_func_decl(ctx.nCtx, name.NativeObject, AST.ArrayLength(domain), AST.ArrayToNative(domain), range.NativeObject)) { Contract.Requires(ctx != null); Contract.Requires(name != null); Contract.Requires(range != null); } internal FuncDecl(Context ctx, string prefix, Sort[] domain, Sort range) : base(ctx, Native.Z3_mk_fresh_func_decl(ctx.nCtx, prefix, AST.ArrayLength(domain), AST.ArrayToNative(domain), range.NativeObject)) { Contract.Requires(ctx != null); Contract.Requires(range != null); } #if DEBUG internal override void CheckNativeObject(IntPtr obj) { if (Native.Z3_get_ast_kind(Context.nCtx, obj) != (uint)Z3_ast_kind.Z3_FUNC_DECL_AST) throw new Z3Exception("Underlying object is not a function declaration"); base.CheckNativeObject(obj); } #endif #endregion /// /// Create expression that applies function to arguments. /// /// /// public Expr this[params Expr[] args] { get { Contract.Requires(args == null || Contract.ForAll(args, a => a != null)); return Apply(args); } } /// /// Create expression that applies function to arguments. /// /// /// public Expr Apply(params Expr[] args) { Contract.Requires(args == null || Contract.ForAll(args, a => a != null)); Context.CheckContextMatch(args); return Expr.Create(Context, this, args); } } } z3-4.4.0/src/api/dotnet/ParamDescrs.cs0000644000175000017500000000601212540347414017234 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Parameter.cs Abstract: Z3 Managed API: Parameter Descriptions Author: Christoph Wintersteiger (cwinter) 2012-03-20 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// A ParamDescrs describes a set of parameters. /// [ContractVerification(true)] public class ParamDescrs : Z3Object { /// /// validate a set of parameters. /// public void Validate(Params p) { Contract.Requires(p != null); Native.Z3_params_validate(Context.nCtx, p.NativeObject, NativeObject); } /// /// Retrieve kind of parameter. /// public Z3_param_kind GetKind(Symbol name) { Contract.Requires(name != null); return (Z3_param_kind)Native.Z3_param_descrs_get_kind(Context.nCtx, NativeObject, name.NativeObject); } /// /// Retrieve all names of parameters. /// public Symbol[] Names { get { uint sz = Native.Z3_param_descrs_size(Context.nCtx, NativeObject); Symbol[] names = new Symbol[sz]; for (uint i = 0; i < sz; ++i) { names[i] = Symbol.Create(Context, Native.Z3_param_descrs_get_name(Context.nCtx, NativeObject, i)); } return names; } } /// /// The size of the ParamDescrs. /// public uint Size { get { return Native.Z3_param_descrs_size(Context.nCtx, NativeObject); } } /// /// Retrieves a string representation of the ParamDescrs. /// public override string ToString() { return Native.Z3_param_descrs_to_string(Context.nCtx, NativeObject); } #region Internal internal ParamDescrs(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_param_descrs_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_param_descrs_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.ParamDescrs_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.ParamDescrs_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-4.4.0/src/api/dotnet/AST.cs0000644000175000017500000001776412540347414015477 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: AST.cs Abstract: Z3 Managed API: ASTs Author: Christoph Wintersteiger (cwinter) 2012-03-16 Notes: --*/ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// The abstract syntax tree (AST) class. /// [ContractVerification(true)] public class AST : Z3Object, IComparable { /// /// Comparison operator. /// /// An AST /// An AST /// True if and are from the same context /// and represent the same sort; false otherwise. public static bool operator ==(AST a, AST b) { return Object.ReferenceEquals(a, b) || (!Object.ReferenceEquals(a, null) && !Object.ReferenceEquals(b, null) && a.Context.nCtx == b.Context.nCtx && Native.Z3_is_eq_ast(a.Context.nCtx, a.NativeObject, b.NativeObject) != 0); } /// /// Comparison operator. /// /// An AST /// An AST /// True if and are not from the same context /// or represent different sorts; false otherwise. public static bool operator !=(AST a, AST b) { return !(a == b); } /// /// Object comparison. /// public override bool Equals(object o) { AST casted = o as AST; if (casted == null) return false; return this == casted; } /// /// Object Comparison. /// /// Another AST /// Negative if the object should be sorted before , positive if after else zero. public virtual int CompareTo(object other) { if (other == null) return 1; AST oAST = other as AST; if (oAST == null) return 1; else { if (Id < oAST.Id) return -1; else if (Id > oAST.Id) return +1; else return 0; } } /// /// The AST's hash code. /// /// A hash code public override int GetHashCode() { return (int)Native.Z3_get_ast_hash(Context.nCtx, NativeObject); } /// /// A unique identifier for the AST (unique among all ASTs). /// public uint Id { get { return Native.Z3_get_ast_id(Context.nCtx, NativeObject); } } /// /// Translates (copies) the AST to the Context . /// /// A context /// A copy of the AST which is associated with public AST Translate(Context ctx) { Contract.Requires(ctx != null); Contract.Ensures(Contract.Result() != null); if (ReferenceEquals(Context, ctx)) return this; else return new AST(ctx, Native.Z3_translate(Context.nCtx, NativeObject, ctx.nCtx)); } /// /// The kind of the AST. /// public Z3_ast_kind ASTKind { get { return (Z3_ast_kind)Native.Z3_get_ast_kind(Context.nCtx, NativeObject); } } /// /// Indicates whether the AST is an Expr /// public bool IsExpr { get { switch (ASTKind) { case Z3_ast_kind.Z3_APP_AST: case Z3_ast_kind.Z3_NUMERAL_AST: case Z3_ast_kind.Z3_QUANTIFIER_AST: case Z3_ast_kind.Z3_VAR_AST: return true; default: return false; } } } /// /// 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 /// public bool IsVar { get { return this.ASTKind == Z3_ast_kind.Z3_VAR_AST; } } /// /// Indicates whether the AST is a Quantifier /// public bool IsQuantifier { get { return this.ASTKind == Z3_ast_kind.Z3_QUANTIFIER_AST; } } /// /// Indicates whether the AST is a Sort /// public bool IsSort { get { return this.ASTKind == Z3_ast_kind.Z3_SORT_AST; } } /// /// Indicates whether the AST is a FunctionDeclaration /// public bool IsFuncDecl { get { return this.ASTKind == Z3_ast_kind.Z3_FUNC_DECL_AST; } } /// /// A string representation of the AST. /// public override string ToString() { return Native.Z3_ast_to_string(Context.nCtx, NativeObject); } /// /// A string representation of the AST in s-expression notation. /// public string SExpr() { Contract.Ensures(Contract.Result() != null); return Native.Z3_ast_to_string(Context.nCtx, NativeObject); } #region Internal internal AST(Context ctx) : base(ctx) { Contract.Requires(ctx != null); } internal AST(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { // Console.WriteLine("AST IncRef()"); if (Context == null || o == IntPtr.Zero) return; Context.AST_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { // Console.WriteLine("AST DecRef()"); if (Context == null || o == IntPtr.Zero) return; Context.AST_DRQ.Add(o); base.DecRef(o); } internal static AST Create(Context ctx, IntPtr obj) { Contract.Requires(ctx != null); Contract.Ensures(Contract.Result() != null); switch ((Z3_ast_kind)Native.Z3_get_ast_kind(ctx.nCtx, obj)) { case Z3_ast_kind.Z3_FUNC_DECL_AST: return new FuncDecl(ctx, obj); case Z3_ast_kind.Z3_QUANTIFIER_AST: return new Quantifier(ctx, obj); case Z3_ast_kind.Z3_SORT_AST: return Sort.Create(ctx, obj); case Z3_ast_kind.Z3_APP_AST: case Z3_ast_kind.Z3_NUMERAL_AST: case Z3_ast_kind.Z3_VAR_AST: return Expr.Create(ctx, obj); default: throw new Z3Exception("Unknown AST kind"); } } #endregion } } z3-4.4.0/src/api/dotnet/Fixedpoint.cs0000644000175000017500000003065212540347414017150 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Fixedpoints.cs Abstract: Z3 Managed API: Fixedpoints Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Object for managing fixedpoints /// [ContractVerification(true)] public class Fixedpoint : Z3Object { /// /// A string that describes all available fixedpoint solver parameters. /// public string Help { get { Contract.Ensures(Contract.Result() != null); return Native.Z3_fixedpoint_get_help(Context.nCtx, NativeObject); } } /// /// Sets the fixedpoint solver parameters. /// public Params Parameters { set { Contract.Requires(value != null); Context.CheckContextMatch(value); Native.Z3_fixedpoint_set_params(Context.nCtx, NativeObject, value.NativeObject); } } /// /// Retrieves parameter descriptions for Fixedpoint solver. /// public ParamDescrs ParameterDescriptions { get { return new ParamDescrs(Context, Native.Z3_fixedpoint_get_param_descrs(Context.nCtx, NativeObject)); } } /// /// Assert a constraint (or multiple) into the fixedpoint solver. /// public void Assert(params BoolExpr[] constraints) { Contract.Requires(constraints != null); Contract.Requires(Contract.ForAll(constraints, c => c != null)); Context.CheckContextMatch(constraints); foreach (BoolExpr a in constraints) { Native.Z3_fixedpoint_assert(Context.nCtx, NativeObject, a.NativeObject); } } /// /// Alias for Assert. /// public void Add(params BoolExpr[] constraints) { Assert(constraints); } /// /// Register predicate as recursive relation. /// public void RegisterRelation(FuncDecl f) { Contract.Requires(f != null); Context.CheckContextMatch(f); Native.Z3_fixedpoint_register_relation(Context.nCtx, NativeObject, f.NativeObject); } /// /// Add rule into the fixedpoint solver. /// public void AddRule(BoolExpr rule, Symbol name = null) { Contract.Requires(rule != null); Context.CheckContextMatch(rule); Native.Z3_fixedpoint_add_rule(Context.nCtx, NativeObject, rule.NativeObject, AST.GetNativeObject(name)); } /// /// Add table fact to the fixedpoint solver. /// public void AddFact(FuncDecl pred, params uint[] args) { Contract.Requires(pred != null); Contract.Requires(args != null); Context.CheckContextMatch(pred); Native.Z3_fixedpoint_add_fact(Context.nCtx, NativeObject, pred.NativeObject, (uint)args.Length, args); } /// /// Query the fixedpoint solver. /// A query is a conjunction of constraints. The constraints may include the recursively defined relations. /// The query is satisfiable if there is an instance of the query variables and a derivation for it. /// The query is unsatisfiable if there are no derivations satisfying the query variables. /// public Status Query(BoolExpr query) { Contract.Requires(query != null); Context.CheckContextMatch(query); Z3_lbool r = (Z3_lbool)Native.Z3_fixedpoint_query(Context.nCtx, NativeObject, query.NativeObject); switch (r) { case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE; case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } /// /// Query the fixedpoint solver. /// A query is an array of relations. /// The query is satisfiable if there is an instance of some relation that is non-empty. /// The query is unsatisfiable if there are no derivations satisfying any of the relations. /// public Status Query(FuncDecl[] relations) { Contract.Requires(relations != null); Contract.Requires(Contract.ForAll(0, relations.Length, i => relations[i] != null)); Context.CheckContextMatch(relations); Z3_lbool r = (Z3_lbool)Native.Z3_fixedpoint_query_relations(Context.nCtx, NativeObject, AST.ArrayLength(relations), AST.ArrayToNative(relations)); switch (r) { case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE; case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } /// /// Creates a backtracking point. /// /// public void Push() { Native.Z3_fixedpoint_push(Context.nCtx, NativeObject); } /// /// Backtrack one backtracking point. /// /// Note that an exception is thrown if Pop is called without a corresponding Push /// public void Pop() { Native.Z3_fixedpoint_pop(Context.nCtx, NativeObject); } /// /// Update named rule into in the fixedpoint solver. /// public void UpdateRule(BoolExpr rule, Symbol name) { Contract.Requires(rule != null); Context.CheckContextMatch(rule); Native.Z3_fixedpoint_update_rule(Context.nCtx, NativeObject, rule.NativeObject, AST.GetNativeObject(name)); } /// /// Retrieve satisfying instance or instances of solver, /// or definitions for the recursive predicates that show unsatisfiability. /// public Expr GetAnswer() { IntPtr ans = Native.Z3_fixedpoint_get_answer(Context.nCtx, NativeObject); return (ans == IntPtr.Zero) ? null : Expr.Create(Context, ans); } /// /// Retrieve explanation why fixedpoint engine returned status Unknown. /// public string GetReasonUnknown() { Contract.Ensures(Contract.Result() != null); return Native.Z3_fixedpoint_get_reason_unknown(Context.nCtx, NativeObject); } /// /// Retrieve the number of levels explored for a given predicate. /// public uint GetNumLevels(FuncDecl predicate) { return Native.Z3_fixedpoint_get_num_levels(Context.nCtx, NativeObject, predicate.NativeObject); } /// /// Retrieve the cover of a predicate. /// public Expr GetCoverDelta(int level, FuncDecl predicate) { IntPtr res = Native.Z3_fixedpoint_get_cover_delta(Context.nCtx, NativeObject, level, predicate.NativeObject); return (res == IntPtr.Zero) ? null : Expr.Create(Context, res); } /// /// Add property about the predicate. /// The property is added at level. /// public void AddCover(int level, FuncDecl predicate, Expr property) { Native.Z3_fixedpoint_add_cover(Context.nCtx, NativeObject, level, predicate.NativeObject, property.NativeObject); } /// /// Retrieve internal string representation of fixedpoint object. /// public override string ToString() { return Native.Z3_fixedpoint_to_string(Context.nCtx, NativeObject, 0, null); } /// /// Instrument the Datalog engine on which table representation to use for recursive predicate. /// public void SetPredicateRepresentation(FuncDecl f, Symbol[] kinds) { Contract.Requires(f != null); Native.Z3_fixedpoint_set_predicate_representation(Context.nCtx, NativeObject, f.NativeObject, AST.ArrayLength(kinds), Symbol.ArrayToNative(kinds)); } /// /// Convert benchmark given as set of axioms, rules and queries to a string. /// public string ToString(BoolExpr[] queries) { return Native.Z3_fixedpoint_to_string(Context.nCtx, NativeObject, AST.ArrayLength(queries), AST.ArrayToNative(queries)); } /// /// Retrieve set of rules added to fixedpoint context. /// public BoolExpr[] Rules { get { Contract.Ensures(Contract.Result() != null); ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_get_rules(Context.nCtx, NativeObject)); return av.ToBoolExprArray(); } } /// /// Retrieve set of assertions added to fixedpoint context. /// public BoolExpr[] Assertions { get { Contract.Ensures(Contract.Result() != null); ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_get_assertions(Context.nCtx, NativeObject)); return av.ToBoolExprArray(); } } /// /// Fixedpoint statistics. /// public Statistics Statistics { get { Contract.Ensures(Contract.Result() != null); return new Statistics(Context, Native.Z3_fixedpoint_get_statistics(Context.nCtx, NativeObject)); } } /// /// Parse an SMT-LIB2 file with fixedpoint rules. /// Add the rules to the current fixedpoint context. /// Return the set of queries in the file. /// public BoolExpr[] ParseFile(string file) { ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_from_file(Context.nCtx, NativeObject, file)); return av.ToBoolExprArray(); } /// /// Similar to ParseFile. Instead it takes as argument a string. /// public BoolExpr[] ParseString(string s) { ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_from_string(Context.nCtx, NativeObject, s)); return av.ToBoolExprArray(); } #region Internal internal Fixedpoint(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal Fixedpoint(Context ctx) : base(ctx, Native.Z3_mk_fixedpoint(ctx.nCtx)) { Contract.Requires(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_fixedpoint_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_fixedpoint_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Fixedpoint_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Fixedpoint_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-4.4.0/src/api/dotnet/Global.cs0000644000175000017500000001000712540347414016227 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Global.cs Abstract: Z3 Managed API: Global Functions Author: Christoph Wintersteiger (cwinter) 2013-01-15 Notes: --*/ using System; using System.Runtime.InteropServices; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Global functions for Z3. /// /// /// This (static) class contains functions that effect the behaviour of Z3 /// globally across contexts, etc. /// public static class Global { /// /// Set a global (or module) parameter, which is shared by all Z3 contexts. /// /// /// When a Z3 module is initialized it will use the value of these parameters /// when Z3_params objects are not provided. /// The name of parameter can be composed of characters [a-z][A-Z], digits [0-9], '-' and '_'. /// The character '.' is a delimiter (more later). /// The parameter names are case-insensitive. The character '-' should be viewed as an "alias" for '_'. /// Thus, the following parameter names are considered equivalent: "pp.decimal-precision" and "PP.DECIMAL_PRECISION". /// This function can be used to set parameters for a specific Z3 module. /// This can be done by using [module-name].[parameter-name]. /// For example: /// Z3_global_param_set('pp.decimal', 'true') /// will set the parameter "decimal" in the module "pp" to true. /// public static void SetParameter(string id, string value) { Native.Z3_global_param_set(id, value); } /// /// Get a global (or module) parameter. /// /// /// Returns null if the parameter does not exist. /// The caller must invoke #Z3_global_param_del_value to delete the value returned at \c param_value. /// This function cannot be invoked simultaneously from different threads without synchronization. /// The result string stored in param_value is stored in a shared location. /// public static string GetParameter(string id) { IntPtr t; if (Native.Z3_global_param_get(id, out t) == 0) return null; else return Marshal.PtrToStringAnsi(t); } /// /// Restore the value of all global (and module) parameters. /// /// /// This command will not affect already created objects (such as tactics and solvers) /// /// public static void ResetParameters() { Native.Z3_global_param_reset_all(); } /// /// Enable/disable printing of warning messages to the console. /// /// Note that this function is static and effects the behaviour of /// all contexts globally. public static void ToggleWarningMessages(bool enabled) { Native.Z3_toggle_warning_messages((enabled) ? 1 : 0); } /// /// Enable tracing messages tagged as `tag' when Z3 is compiled in debug mode. /// /// /// It is a NOOP otherwise. /// /// trace tag public static void EnableTrace(string tag) { Native.Z3_enable_trace(tag); } /// /// Disable tracing messages tagged as `tag' when Z3 is compiled in debug mode. /// /// /// It is a NOOP otherwise. /// /// trace tag public static void DisableTrace(string tag) { Native.Z3_disable_trace(tag); } } } z3-4.4.0/src/api/dotnet/Tactic.cs0000644000175000017500000000766012540347414016251 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Tactic.cs Abstract: Z3 Managed API: Tactics Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Tactics are the basic building block for creating custom solvers for specific problem domains. /// The complete list of tactics may be obtained using Context.NumTactics /// and Context.TacticNames. /// It may also be obtained using the command (help-tactics) in the SMT 2.0 front-end. /// [ContractVerification(true)] public class Tactic : Z3Object { /// /// A string containing a description of parameters accepted by the tactic. /// public string Help { get { Contract.Ensures(Contract.Result() != null); return Native.Z3_tactic_get_help(Context.nCtx, NativeObject); } } /// /// Retrieves parameter descriptions for Tactics. /// public ParamDescrs ParameterDescriptions { get { return new ParamDescrs(Context, Native.Z3_tactic_get_param_descrs(Context.nCtx, NativeObject)); } } /// /// Execute the tactic over the goal. /// public ApplyResult Apply(Goal g, Params p = null) { Contract.Requires(g != null); Contract.Ensures(Contract.Result() != null); Context.CheckContextMatch(g); if (p == null) return new ApplyResult(Context, Native.Z3_tactic_apply(Context.nCtx, NativeObject, g.NativeObject)); else { Context.CheckContextMatch(p); return new ApplyResult(Context, Native.Z3_tactic_apply_ex(Context.nCtx, NativeObject, g.NativeObject, p.NativeObject)); } } /// /// Apply the tactic to a goal. /// public ApplyResult this[Goal g] { get { Contract.Requires(g != null); Contract.Ensures(Contract.Result() != null); return Apply(g); } } /// /// Creates a solver that is implemented using the given tactic. /// /// public Solver Solver { get { Contract.Ensures(Contract.Result() != null); return Context.MkSolver(this); } } #region Internal internal Tactic(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal Tactic(Context ctx, string name) : base(ctx, Native.Z3_mk_tactic(ctx.nCtx, name)) { Contract.Requires(ctx != null); } /// /// DecRefQueue /// internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_tactic_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_tactic_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Tactic_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Tactic_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-4.4.0/src/api/dotnet/IntExpr.cs0000644000175000017500000000130512540347414016421 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: IntExpr.cs Abstract: Z3 Managed API: Int Expressions Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Int expressions /// public class IntExpr : ArithExpr { #region Internal /// Constructor for IntExpr internal IntExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/BitVecNum.cs0000644000175000017500000000530012540347414016663 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: IntNum.cs Abstract: Z3 Managed API: Int Numerals Author: Christoph Wintersteiger (cwinter) 2012-03-20 Notes: --*/ using System; using System.Diagnostics.Contracts; #if !FRAMEWORK_LT_4 using System.Numerics; #endif namespace Microsoft.Z3 { /// /// Bit-vector numerals /// [ContractVerification(true)] public class BitVecNum : BitVecExpr { /// /// Retrieve the 64-bit unsigned integer value. /// public UInt64 UInt64 { get { UInt64 res = 0; if (Native.Z3_get_numeral_uint64(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not a 64 bit unsigned"); return res; } } /// /// Retrieve the int value. /// public int Int { get { int res = 0; if (Native.Z3_get_numeral_int(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not an int"); return res; } } /// /// Retrieve the 64-bit int value. /// public Int64 Int64 { get { Int64 res = 0; if (Native.Z3_get_numeral_int64(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not an int64"); return res; } } /// /// Retrieve the int value. /// public uint UInt { get { uint res = 0; if (Native.Z3_get_numeral_uint(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not a uint"); return res; } } #if !FRAMEWORK_LT_4 /// /// Retrieve the BigInteger value. /// public BigInteger BigInteger { get { return BigInteger.Parse(this.ToString()); } } #endif /// /// Returns a string representation of the numeral. /// public override string ToString() { return Native.Z3_get_numeral_string(Context.nCtx, NativeObject); } #region Internal internal BitVecNum(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/Pattern.cs0000644000175000017500000000333012540347414016445 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Pattern.cs Abstract: Z3 Managed API: Patterns Author: Christoph Wintersteiger (cwinter) 2012-03-16 Notes: --*/ using System; using System.Runtime.InteropServices; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Patterns comprise a list of terms. The list should be /// non-empty. If the list comprises of more than one term, it is /// also called a multi-pattern. /// [ContractVerification(true)] public class Pattern : AST { /// /// The number of terms in the pattern. /// public uint NumTerms { get { return Native.Z3_get_pattern_num_terms(Context.nCtx, NativeObject); } } /// /// The terms in the pattern. /// public Expr[] Terms { get { Contract.Ensures(Contract.Result() != null); uint n = NumTerms; Expr[] res = new Expr[n]; for (uint i = 0; i < n; i++) res[i] = Expr.Create(Context, Native.Z3_get_pattern(Context.nCtx, NativeObject, i)); return res; } } /// /// A string representation of the pattern. /// public override string ToString() { return Native.Z3_pattern_to_string(Context.nCtx, NativeObject); } #region Internal internal Pattern(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/Properties/0000755000175000017500000000000012540347414016636 5ustar michaelmichaelz3-4.4.0/src/api/dotnet/Properties/AssemblyInfo0000644000175000017500000000277512540347414021167 0ustar michaelmichaelusing System; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Permissions; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Z3 .NET Interface")] [assembly: AssemblyDescription(".NET Interface to the Z3 Theorem Prover")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Microsoft Corporation")] [assembly: AssemblyProduct("Z3")] [assembly: AssemblyCopyright("Copyright (C) 2006-2014 Microsoft Corporation")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("4853ed71-2078-40f4-8117-bc46646bce0e")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("4.2.0.0")] [assembly: AssemblyVersion("4.3.2.0")] [assembly: AssemblyFileVersion("4.3.2.0")] z3-4.4.0/src/api/dotnet/Z3Object.cs0000644000175000017500000000667212540347414016467 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Z3Object.cs Abstract: Z3 Managed API: Internal Z3 Objects Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System; using System.Diagnostics.Contracts; using System.Threading; namespace Microsoft.Z3 { /// /// Internal base class for interfacing with native Z3 objects. /// Should not be used externally. /// [ContractVerification(true)] public class Z3Object : IDisposable { /// /// Finalizer. /// ~Z3Object() { Dispose(); } /// /// Disposes of the underlying native Z3 object. /// public void Dispose() { if (m_n_obj != IntPtr.Zero) { DecRef(m_n_obj); m_n_obj = IntPtr.Zero; } if (m_ctx != null) { if (Interlocked.Decrement(ref m_ctx.refCount) == 0) GC.ReRegisterForFinalize(m_ctx); m_ctx = null; } GC.SuppressFinalize(this); } #region Object Invariant [ContractInvariantMethod] private void ObjectInvariant() { Contract.Invariant(this.m_ctx != null); } #endregion #region Internal private Context m_ctx = null; private IntPtr m_n_obj = IntPtr.Zero; internal Z3Object(Context ctx) { Contract.Requires(ctx != null); Interlocked.Increment(ref ctx.refCount); m_ctx = ctx; } internal Z3Object(Context ctx, IntPtr obj) { Contract.Requires(ctx != null); Interlocked.Increment(ref ctx.refCount); m_ctx = ctx; IncRef(obj); m_n_obj = obj; } internal virtual void IncRef(IntPtr o) { } internal virtual void DecRef(IntPtr o) { } internal virtual void CheckNativeObject(IntPtr obj) { } internal virtual IntPtr NativeObject { get { return m_n_obj; } set { if (value != IntPtr.Zero) { CheckNativeObject(value); IncRef(value); } if (m_n_obj != IntPtr.Zero) { DecRef(m_n_obj); } m_n_obj = value; } } internal static IntPtr GetNativeObject(Z3Object s) { if (s == null) return new IntPtr(); return s.NativeObject; } internal Context Context { get { Contract.Ensures(Contract.Result() != null); return m_ctx; } } [Pure] internal static IntPtr[] ArrayToNative(Z3Object[] a) { Contract.Ensures(a == null || Contract.Result() != null); Contract.Ensures(a == null || Contract.Result().Length == a.Length); if (a == null) return null; IntPtr[] an = new IntPtr[a.Length]; for (uint i = 0; i < a.Length; i++) if (a[i] != null) an[i] = a[i].NativeObject; return an; } [Pure] internal static uint ArrayLength(Z3Object[] a) { return (a == null)?0:(uint)a.Length; } #endregion } } z3-4.4.0/src/api/dotnet/DatatypeSort.cs0000644000175000017500000000623412540347414017461 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: DatatypeSort.cs Abstract: Z3 Managed API: Datatype Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Datatype sorts. /// [ContractVerification(true)] public class DatatypeSort : Sort { /// /// The number of constructors of the datatype sort. /// public uint NumConstructors { get { return Native.Z3_get_datatype_sort_num_constructors(Context.nCtx, NativeObject); } } /// /// The constructors. /// public FuncDecl[] Constructors { get { Contract.Ensures(Contract.Result() != null); uint n = NumConstructors; FuncDecl[] res = new FuncDecl[n]; for (uint i = 0; i < n; i++) res[i] = new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, i)); return res; } } /// /// The recognizers. /// public FuncDecl[] Recognizers { get { Contract.Ensures(Contract.Result() != null); uint n = NumConstructors; FuncDecl[] res = new FuncDecl[n]; for (uint i = 0; i < n; i++) res[i] = new FuncDecl(Context, Native.Z3_get_datatype_sort_recognizer(Context.nCtx, NativeObject, i)); return res; } } /// /// The constructor accessors. /// public FuncDecl[][] Accessors { get { Contract.Ensures(Contract.Result() != null); uint n = NumConstructors; FuncDecl[][] res = new FuncDecl[n][]; for (uint i = 0; i < n; i++) { FuncDecl fd = new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, i)); uint ds = fd.DomainSize; FuncDecl[] tmp = new FuncDecl[ds]; for (uint j = 0; j < ds; j++) tmp[j] = new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor_accessor(Context.nCtx, NativeObject, i, j)); res[i] = tmp; } return res; } } #region Internal internal DatatypeSort(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal DatatypeSort(Context ctx, Symbol name, Constructor[] constructors) : base(ctx, Native.Z3_mk_datatype(ctx.nCtx, name.NativeObject, (uint)constructors.Length, ArrayToNative(constructors))) { Contract.Requires(ctx != null); Contract.Requires(name != null); Contract.Requires(constructors != null); } #endregion }; } z3-4.4.0/src/api/dotnet/EnumSort.cs0000644000175000017500000000746212540347414016616 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: EnumSort.cs Abstract: Z3 Managed API: Enum Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Enumeration sorts. /// [ContractVerification(true)] public class EnumSort : Sort { /// /// The function declarations of the constants in the enumeration. /// public FuncDecl[] ConstDecls { get { Contract.Ensures(Contract.Result() != null); uint n = Native.Z3_get_datatype_sort_num_constructors(Context.nCtx, NativeObject); FuncDecl[] t = new FuncDecl[n]; for (uint i = 0; i < n; i++) t[i] = new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, i)); return t; } } /// /// Retrieves the inx'th constant declaration in the enumeration. /// /// /// public FuncDecl ConstDecl(uint inx) { return new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, inx)); } /// /// The constants in the enumeration. /// public Expr[] Consts { get { Contract.Ensures(Contract.Result() != null); FuncDecl[] cds = ConstDecls; Expr[] t = new Expr[cds.Length]; for (uint i = 0; i < t.Length; i++) t[i] = Context.MkApp(cds[i]); return t; } } /// /// Retrieves the inx'th constant in the enumeration. /// /// /// public Expr Const(uint inx) { return Context.MkApp(ConstDecl(inx)); } /// /// The test predicates (recognizers) for the constants in the enumeration. /// public FuncDecl[] TesterDecls { get { Contract.Ensures(Contract.Result() != null); uint n = Native.Z3_get_datatype_sort_num_constructors(Context.nCtx, NativeObject); FuncDecl[] t = new FuncDecl[n]; for (uint i = 0; i < n; i++) t[i] = new FuncDecl(Context, Native.Z3_get_datatype_sort_recognizer(Context.nCtx, NativeObject, i)); return t; } } /// /// Retrieves the inx'th tester/recognizer declaration in the enumeration. /// /// /// public FuncDecl TesterDecl(uint inx) { return new FuncDecl(Context, Native.Z3_get_datatype_sort_recognizer(Context.nCtx, NativeObject, inx)); } #region Internal internal EnumSort(Context ctx, Symbol name, Symbol[] enumNames) : base(ctx, IntPtr.Zero) { Contract.Requires(ctx != null); Contract.Requires(name != null); Contract.Requires(enumNames != null); int n = enumNames.Length; IntPtr[] n_constdecls = new IntPtr[n]; IntPtr[] n_testers = new IntPtr[n]; NativeObject = Native.Z3_mk_enumeration_sort(ctx.nCtx, name.NativeObject, (uint)n, Symbol.ArrayToNative(enumNames), n_constdecls, n_testers); } #endregion }; } z3-4.4.0/src/api/dotnet/Optimize.cs0000644000175000017500000002136312540347414016636 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Optimize.cs Abstract: Z3 Managed API: Optimizes Author: Nikolaj Bjorner (nbjorner) 2013-12-03 Notes: --*/ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Object for managing optimizization context /// [ContractVerification(true)] public class Optimize : Z3Object { /// /// A string that describes all available optimize solver parameters. /// public string Help { get { Contract.Ensures(Contract.Result() != null); return Native.Z3_optimize_get_help(Context.nCtx, NativeObject); } } /// /// Sets the optimize solver parameters. /// public Params Parameters { set { Contract.Requires(value != null); Context.CheckContextMatch(value); Native.Z3_optimize_set_params(Context.nCtx, NativeObject, value.NativeObject); } } /// /// Retrieves parameter descriptions for Optimize solver. /// public ParamDescrs ParameterDescriptions { get { return new ParamDescrs(Context, Native.Z3_optimize_get_param_descrs(Context.nCtx, NativeObject)); } } /// /// Assert a constraint (or multiple) into the optimize solver. /// public void Assert(params BoolExpr[] constraints) { Contract.Requires(constraints != null); Contract.Requires(Contract.ForAll(constraints, c => c != null)); Context.CheckContextMatch(constraints); foreach (BoolExpr a in constraints) { Native.Z3_optimize_assert(Context.nCtx, NativeObject, a.NativeObject); } } /// /// Alias for Assert. /// public void Add(params BoolExpr[] constraints) { Assert(constraints); } /// /// Handle to objectives returned by objective functions. /// public class Handle { Optimize opt; uint handle; internal Handle(Optimize opt, uint h) { this.opt = opt; this.handle = h; } /// /// Retrieve a lower bound for the objective handle. /// public ArithExpr Lower { get { return opt.GetLower(handle); } } /// /// Retrieve an upper bound for the objective handle. /// public ArithExpr Upper { get { return opt.GetUpper(handle); } } /// /// Retrieve the value of an objective. /// public ArithExpr Value { get { return Lower; } } } /// /// Assert soft constraint /// /// /// Return an objective which associates with the group of constraints. /// public Handle AssertSoft(BoolExpr constraint, uint weight, string group) { Context.CheckContextMatch(constraint); Symbol s = Context.MkSymbol(group); return new Handle(this, Native.Z3_optimize_assert_soft(Context.nCtx, NativeObject, constraint.NativeObject, weight.ToString(), s.NativeObject)); } /// /// Check satisfiability of asserted constraints. /// Produce a model that (when the objectives are bounded and /// don't use strict inequalities) meets the objectives. /// /// public Status Check() { Z3_lbool r = (Z3_lbool)Native.Z3_optimize_check(Context.nCtx, NativeObject); switch (r) { case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE; case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } /// /// Creates a backtracking point. /// /// public void Push() { Native.Z3_optimize_push(Context.nCtx, NativeObject); } /// /// Backtrack one backtracking point. /// /// Note that an exception is thrown if Pop is called without a corresponding Push /// public void Pop() { Native.Z3_optimize_pop(Context.nCtx, NativeObject); } /// /// The model of the last Check. /// /// /// The result is null if Check was not invoked before, /// if its results was not SATISFIABLE, or if model production is not enabled. /// public Model Model { get { IntPtr x = Native.Z3_optimize_get_model(Context.nCtx, NativeObject); if (x == IntPtr.Zero) return null; else return new Model(Context, x); } } /// /// Declare an arithmetical maximization objective. /// Return a handle to the objective. The handle is used as /// to retrieve the values of objectives after calling Check. /// public Handle MkMaximize(ArithExpr e) { return new Handle(this, Native.Z3_optimize_maximize(Context.nCtx, NativeObject, e.NativeObject)); } /// /// Declare an arithmetical minimization objective. /// Similar to MkMaximize. /// public Handle MkMinimize(ArithExpr e) { return new Handle(this, Native.Z3_optimize_minimize(Context.nCtx, NativeObject, e.NativeObject)); } /// /// Retrieve a lower bound for the objective handle. /// private ArithExpr GetLower(uint index) { return (ArithExpr)Expr.Create(Context, Native.Z3_optimize_get_lower(Context.nCtx, NativeObject, index)); } /// /// Retrieve an upper bound for the objective handle. /// private ArithExpr GetUpper(uint index) { return (ArithExpr)Expr.Create(Context, Native.Z3_optimize_get_upper(Context.nCtx, NativeObject, index)); } /// /// Print the context to a string (SMT-LIB parseable benchmark). /// public override string ToString() { return Native.Z3_optimize_to_string(Context.nCtx, NativeObject); } /// /// Optimize statistics. /// public Statistics Statistics { get { Contract.Ensures(Contract.Result() != null); return new Statistics(Context, Native.Z3_optimize_get_statistics(Context.nCtx, NativeObject)); } } #region Internal internal Optimize(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal Optimize(Context ctx) : base(ctx, Native.Z3_mk_optimize(ctx.nCtx)) { Contract.Requires(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_optimize_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_optimize_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Optimize_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Optimize_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-4.4.0/src/api/dotnet/Probe.cs0000644000175000017500000000523112540347414016101 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Probe.cs Abstract: Z3 Managed API: Probes Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System; using System.Runtime.InteropServices; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Probes are used to inspect a goal (aka problem) and collect information that may be used to decide /// which solver and/or preprocessing step will be used. /// The complete list of probes may be obtained using the procedures Context.NumProbes /// and Context.ProbeNames. /// It may also be obtained using the command (help-tactics) in the SMT 2.0 front-end. /// [ContractVerification(true)] public class Probe : Z3Object { /// /// Execute the probe over the goal. /// /// A probe always produce a double value. /// "Boolean" probes return 0.0 for false, and a value different from 0.0 for true. public double Apply(Goal g) { Contract.Requires(g != null); Context.CheckContextMatch(g); return Native.Z3_probe_apply(Context.nCtx, NativeObject, g.NativeObject); } /// /// Apply the probe to a goal. /// public double this[Goal g] { get { Contract.Requires(g != null); return Apply(g); } } #region Internal internal Probe(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal Probe(Context ctx, string name) : base(ctx, Native.Z3_mk_probe(ctx.nCtx, name)) { Contract.Requires(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_probe_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_probe_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Probe_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Probe_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-4.4.0/src/api/dotnet/ArithExpr.cs0000644000175000017500000000133612540347414016742 0ustar michaelmichael/*++ Copyright () 2012 Microsoft Corporation Module Name: ArithExpr.cs Abstract: Z3 Managed API: Arith Expressions Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// Arithmetic expressions (int/real) /// public class ArithExpr : Expr { #region Internal /// Constructor for ArithExpr internal ArithExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion } } z3-4.4.0/src/api/dotnet/ApplyResult.cs0000644000175000017500000000624212540347414017321 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ApplyResult.cs Abstract: Z3 Managed API: Result object for tactic applications Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System; using System.Diagnostics.Contracts; namespace Microsoft.Z3 { /// /// ApplyResult objects represent the result of an application of a /// tactic to a goal. It contains the subgoals that were produced. /// [ContractVerification(true)] public class ApplyResult : Z3Object { /// /// The number of Subgoals. /// public uint NumSubgoals { get { return Native.Z3_apply_result_get_num_subgoals(Context.nCtx, NativeObject); } } /// /// Retrieves the subgoals from the ApplyResult. /// public Goal[] Subgoals { get { Contract.Ensures(Contract.Result() != null); Contract.Ensures(Contract.Result().Length == this.NumSubgoals); uint n = NumSubgoals; Goal[] res = new Goal[n]; for (uint i = 0; i < n; i++) res[i] = new Goal(Context, Native.Z3_apply_result_get_subgoal(Context.nCtx, NativeObject, i)); return res; } } /// /// Convert a model for the subgoal into a model for the original /// goal g, that the ApplyResult was obtained from. /// /// A model for g public Model ConvertModel(uint i, Model m) { Contract.Requires(m != null); Contract.Ensures(Contract.Result() != null); return new Model(Context, Native.Z3_apply_result_convert_model(Context.nCtx, NativeObject, i, m.NativeObject)); } /// /// A string representation of the ApplyResult. /// public override string ToString() { return Native.Z3_apply_result_to_string(Context.nCtx, NativeObject); } #region Internal internal ApplyResult(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_apply_result_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_apply_result_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.ApplyResult_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.ApplyResult_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-4.4.0/src/api/api_context.cpp0000644000175000017500000004211612540347414016232 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_context.cpp Abstract: Interface of Z3 with "external world". It was called _Z3_context Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include"api_context.h" #include"smtparser.h" #include"version.h" #include"ast_pp.h" #include"ast_ll_pp.h" #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); namespace api { static void default_error_handler(Z3_context, Z3_error_code c) { printf("Error: %s\n", Z3_get_error_msg(c)); exit(1); } Z3_search_failure mk_Z3_search_failure(smt::failure f) { switch(f) { case smt::OK: return Z3_NO_FAILURE; case smt::UNKNOWN: return Z3_UNKNOWN; case smt::TIMEOUT: return Z3_TIMEOUT; case smt::MEMOUT: return Z3_MEMOUT_WATERMARK; case smt::CANCELED: return Z3_CANCELED; case smt::NUM_CONFLICTS: return Z3_NUM_CONFLICTS; case smt::THEORY: return Z3_THEORY; case smt::QUANTIFIERS: return Z3_QUANTIFIERS; default: UNREACHABLE(); break; } return static_cast(f); } context::add_plugins::add_plugins(ast_manager & m) { reg_decl_plugins(m); } // ------------------------ // // Core // // ------------------------ context::set_interruptable::set_interruptable(context & ctx, event_handler & i): m_ctx(ctx) { #pragma omp critical (set_interruptable) { SASSERT(m_ctx.m_interruptable == 0); m_ctx.m_interruptable = &i; } } context::set_interruptable::~set_interruptable() { #pragma omp critical (set_interruptable) { m_ctx.m_interruptable = 0; } } context::context(context_params * p, bool user_ref_count): m_params(p != 0 ? *p : context_params()), m_user_ref_count(user_ref_count), m_manager(m_params.mk_ast_manager()), m_plugins(m()), m_arith_util(m()), m_bv_util(m()), m_datalog_util(m()), m_fpa_util(m()), m_dtutil(m()), m_last_result(m()), m_ast_trail(m()), m_replay_stack() { m_solver = 0; m_error_code = Z3_OK; m_print_mode = Z3_PRINT_SMTLIB_FULL; m_searching = false; m_interruptable = 0; m_smtlib_parser = 0; m_smtlib_parser_has_decls = false; z3_bound_num_procs(); m_error_handler = &default_error_handler; m_basic_fid = m().get_basic_family_id(); m_arith_fid = m().mk_family_id("arith"); m_bv_fid = m().mk_family_id("bv"); m_pb_fid = m().mk_family_id("pb"); m_array_fid = m().mk_family_id("array"); m_dt_fid = m().mk_family_id("datatype"); m_datalog_fid = m().mk_family_id("datalog_relation"); m_fpa_fid = m().mk_family_id("fpa"); m_dt_plugin = static_cast(m().get_plugin(m_dt_fid)); if (!m_user_ref_count) { m_replay_stack.push_back(0); } install_tactics(*this); } context::~context() { m_last_obj = 0; if (!m_user_ref_count) { for (unsigned i = 0; i < m_replay_stack.size(); ++i) { dealloc(m_replay_stack[i]); } m_ast_trail.reset(); } reset_parser(); dealloc(m_solver); } void context::interrupt() { #pragma omp critical (set_interruptable) { if (m_interruptable) (*m_interruptable)(); m().set_cancel(true); if (m_rcf_manager.get() != 0) m_rcf_manager->set_cancel(true); } } void context::set_error_code(Z3_error_code err) { m_error_code = err; if (err != Z3_OK) invoke_error_handler(err); } void context::check_searching() { if (m_searching) { set_error_code(Z3_INVALID_USAGE); // TBD: error code could be fixed. } } char * context::mk_external_string(char const * str) { m_string_buffer = str; return const_cast(m_string_buffer.c_str()); } char * context::mk_external_string(std::string const & str) { m_string_buffer = str; return const_cast(m_string_buffer.c_str()); } expr * context::mk_numeral_core(rational const & n, sort * s) { expr* e = 0; family_id fid = s->get_family_id(); if (fid == m_arith_fid) { e = m_arith_util.mk_numeral(n, s); } else if (fid == m_bv_fid) { e = m_bv_util.mk_numeral(n, s); } else if (fid == get_datalog_fid() && n.is_uint64()) { uint64 sz; if (m_datalog_util.try_get_size(s, sz) && sz <= n.get_uint64()) { invoke_error_handler(Z3_INVALID_ARG); } e = m_datalog_util.mk_numeral(n.get_uint64(), s); } else { invoke_error_handler(Z3_INVALID_ARG); } save_ast_trail(e); return e; } expr * context::mk_and(unsigned num_exprs, expr * const * exprs) { switch(num_exprs) { case 0: return m().mk_true(); case 1: save_ast_trail(exprs[0]); return exprs[0]; default: { expr * a = m().mk_and(num_exprs, exprs); save_ast_trail(a); return a; } } } void context::persist_ast(ast * n, unsigned num_scopes) { // persist_ast is irrelevant when m_user_ref_count == true if (m_user_ref_count) return; if (num_scopes > m_ast_lim.size()) { num_scopes = m_ast_lim.size(); } SASSERT(m_replay_stack.size() > num_scopes); unsigned j = m_replay_stack.size() - num_scopes - 1; if (!m_replay_stack[j]) { m_replay_stack[j] = alloc(ast_ref_vector, m()); } m_replay_stack[j]->push_back(n); } void context::save_ast_trail(ast * n) { SASSERT(m().contains(n)); if (m_user_ref_count) { // Corner case bug: n may be in m_last_result, and this is the only reference to n. // When, we execute reset() it is deleted // To avoid this bug, I bump the reference counter before reseting m_last_result m().inc_ref(n); m_last_result.reset(); m_last_result.push_back(n); m().dec_ref(n); } else { m_ast_trail.push_back(n); } } void context::save_multiple_ast_trail(ast * n) { if (m_user_ref_count) m_last_result.push_back(n); else m_ast_trail.push_back(n); } void context::reset_last_result() { if (m_user_ref_count) m_last_result.reset(); m_last_obj = 0; } void context::save_object(object * r) { m_last_obj = r; } void context::handle_exception(z3_exception & ex) { if (ex.has_error_code()) { switch(ex.error_code()) { case ERR_MEMOUT: set_error_code(Z3_MEMOUT_FAIL); break; case ERR_PARSER: set_error_code(Z3_PARSER_ERROR); break; case ERR_INI_FILE: set_error_code(Z3_INVALID_ARG); break; case ERR_OPEN_FILE: set_error_code(Z3_FILE_ACCESS_ERROR); break; default: set_error_code(Z3_INTERNAL_FATAL); break; } } else { m_exception_msg = ex.msg(); set_error_code(Z3_EXCEPTION); } } void context::invoke_error_handler(Z3_error_code c) { if (m_error_handler) { if (g_z3_log) { // error handler can do crazy stuff such as longjmp g_z3_log_enabled = true; } m_error_handler(reinterpret_cast(this), c); } } void context::check_sorts(ast * n) { if (!m().check_sorts(n)) { switch(n->get_kind()) { case AST_APP: { std::ostringstream buffer; app * a = to_app(n); buffer << mk_pp(a->get_decl(), m()) << " applied to: "; if (a->get_num_args() > 1) buffer << "\n"; for (unsigned i = 0; i < a->get_num_args(); ++i) { buffer << mk_bounded_pp(a->get_arg(i), m(), 3) << " of sort "; buffer << mk_pp(m().get_sort(a->get_arg(i)), m()) << "\n"; } warning_msg("%s",buffer.str().c_str()); break; } case AST_VAR: case AST_QUANTIFIER: case AST_SORT: case AST_FUNC_DECL: break; } set_error_code(Z3_SORT_ERROR); } } // ------------------------ // // Solver interface for backward compatibility // // ------------------------ smt::kernel & context::get_smt_kernel() { if (!m_solver) { m_fparams.updt_params(m_params); m_solver = alloc(smt::kernel, m(), m_fparams); } return *m_solver; } void context::assert_cnstr(expr * a) { get_smt_kernel().assert_expr(a); } lbool context::check(model_ref & m) { flet searching(m_searching, true); lbool r; r = get_smt_kernel().check(); if (r != l_false) get_smt_kernel().get_model(m); return r; } void context::push() { get_smt_kernel().push(); m_ast_lim.push_back(m_ast_trail.size()); m_replay_stack.push_back(0); } void context::pop(unsigned num_scopes) { for (unsigned i = 0; i < num_scopes; ++i) { unsigned sz = m_ast_lim.back(); m_ast_lim.pop_back(); dealloc(m_replay_stack.back()); m_replay_stack.pop_back(); while (m_ast_trail.size() > sz) { m_ast_trail.pop_back(); } } SASSERT(num_scopes <= get_smt_kernel().get_scope_level()); get_smt_kernel().pop(num_scopes); } // ------------------------ // // Parser interface for backward compatibility // // ------------------------ void context::reset_parser() { if (m_smtlib_parser) { dealloc(m_smtlib_parser); m_smtlib_parser = 0; m_smtlib_parser_has_decls = false; m_smtlib_parser_decls.reset(); m_smtlib_parser_sorts.reset(); } SASSERT(!m_smtlib_parser_has_decls); } void context::extract_smtlib_parser_decls() { if (m_smtlib_parser) { if (!m_smtlib_parser_has_decls) { smtlib::symtable * table = m_smtlib_parser->get_benchmark()->get_symtable(); table->get_func_decls(m_smtlib_parser_decls); table->get_sorts(m_smtlib_parser_sorts); m_smtlib_parser_has_decls = true; } } else { m_smtlib_parser_decls.reset(); 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()); } }; // ------------------------ // // Context creation API // // ------------------------ extern "C" { Z3_context Z3_API Z3_mk_context(Z3_config c) { Z3_TRY; LOG_Z3_mk_context(c); memory::initialize(UINT_MAX); Z3_context r = reinterpret_cast(alloc(api::context, reinterpret_cast(c), false)); RETURN_Z3(r); Z3_CATCH_RETURN_NO_HANDLE(0); } Z3_context Z3_API Z3_mk_context_rc(Z3_config c) { Z3_TRY; LOG_Z3_mk_context_rc(c); memory::initialize(UINT_MAX); Z3_context r = reinterpret_cast(alloc(api::context, reinterpret_cast(c), true)); RETURN_Z3(r); Z3_CATCH_RETURN_NO_HANDLE(0); } void Z3_API Z3_del_context(Z3_context c) { Z3_TRY; LOG_Z3_del_context(c); RESET_ERROR_CODE(); dealloc(mk_c(c)); Z3_CATCH; } void Z3_API Z3_interrupt(Z3_context c) { Z3_TRY; LOG_Z3_interrupt(c); mk_c(c)->interrupt(); Z3_CATCH; } void Z3_API Z3_toggle_warning_messages(Z3_bool enabled) { LOG_Z3_toggle_warning_messages(enabled); enable_warning_messages(enabled != 0); } void Z3_API Z3_inc_ref(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_inc_ref(c, a); RESET_ERROR_CODE(); mk_c(c)->m().inc_ref(to_ast(a)); Z3_CATCH; } void Z3_API Z3_dec_ref(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_dec_ref(c, a); RESET_ERROR_CODE(); if (to_ast(a)->get_ref_count() == 0) { SET_ERROR_CODE(Z3_DEC_REF_ERROR); return; } mk_c(c)->m().dec_ref(to_ast(a)); Z3_CATCH; } Z3_bool Z3_API Z3_set_logic(Z3_context c, Z3_string logic) { Z3_TRY; LOG_Z3_set_logic(c, logic); RESET_ERROR_CODE(); return mk_c(c)->get_smt_kernel().set_logic(symbol(logic)); Z3_CATCH_RETURN(Z3_FALSE); } void Z3_API Z3_get_version(unsigned * major, unsigned * minor, unsigned * build_number, unsigned * revision_number) { LOG_Z3_get_version(major, minor, build_number, revision_number); *major = Z3_MAJOR_VERSION; *minor = Z3_MINOR_VERSION; *build_number = Z3_BUILD_NUMBER; *revision_number = Z3_REVISION_NUMBER; } void Z3_API Z3_enable_trace(Z3_string tag) { memory::initialize(UINT_MAX); LOG_Z3_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) { LOG_Z3_disable_trace(tag); disable_trace(tag); } void Z3_API Z3_reset_memory(void) { LOG_Z3_reset_memory(); memory::finalize(); memory::initialize(0); } void Z3_API Z3_finalize_memory(void) { LOG_Z3_finalize_memory(); memory::finalize(); } Z3_error_code Z3_API Z3_get_error_code(Z3_context c) { LOG_Z3_get_error_code(c); return mk_c(c)->get_error_code(); } void Z3_API Z3_set_error_handler(Z3_context c, Z3_error_handler h) { RESET_ERROR_CODE(); mk_c(c)->set_error_handler(h); // [Leo]: using exception handling, we don't need global error handlers anymore } void Z3_API Z3_set_error(__in Z3_context c, __in Z3_error_code e) { SET_ERROR_CODE(e); } static char const * _get_error_msg_ex(Z3_context c, Z3_error_code err) { switch(err) { case Z3_OK: return "ok"; case Z3_SORT_ERROR: return "type error"; case Z3_IOB: return "index out of bounds"; case Z3_INVALID_ARG: return "invalid argument"; case Z3_PARSER_ERROR: return "parser error"; case Z3_NO_PARSER: return "parser (data) is not available"; case Z3_INVALID_PATTERN: return "invalid pattern"; case Z3_MEMOUT_FAIL: return "out of memory"; case Z3_FILE_ACCESS_ERROR: return "file access error"; case Z3_INTERNAL_FATAL: return "internal error"; case Z3_INVALID_USAGE: return "invalid usage"; case Z3_DEC_REF_ERROR: return "invalid dec_ref command"; case Z3_EXCEPTION: return c == 0 ? "Z3 exception" : mk_c(c)->get_exception_msg(); default: return "unknown"; } } Z3_API char const * Z3_get_error_msg(Z3_error_code err) { LOG_Z3_get_error_msg(err); return _get_error_msg_ex(0, err); } Z3_API char const * Z3_get_error_msg_ex(Z3_context c, Z3_error_code err) { LOG_Z3_get_error_msg_ex(c, err); return _get_error_msg_ex(c, err); } void Z3_API Z3_persist_ast(Z3_context c, Z3_ast n, unsigned num_scopes) { Z3_TRY; LOG_Z3_persist_ast(c, n, num_scopes); RESET_ERROR_CODE(); CHECK_VALID_AST(to_ast(n), ); mk_c(c)->persist_ast(to_ast(n), num_scopes); Z3_CATCH; } void Z3_API Z3_set_ast_print_mode(Z3_context c, Z3_ast_print_mode mode) { Z3_TRY; LOG_Z3_set_ast_print_mode(c, mode); RESET_ERROR_CODE(); mk_c(c)->set_print_mode(mode); Z3_CATCH; } }; Z3_API ast_manager& Z3_get_manager(__in Z3_context c) { return mk_c(c)->m(); } z3-4.4.0/src/api/api_stats.h0000644000175000017500000000121412540347414015343 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_stats.h Abstract: API for Z3 statistics Author: Leonardo de Moura (leonardo) 2012-03-07. Revision History: --*/ #ifndef _API_STATS_H_ #define _API_STATS_H_ #include"api_util.h" #include"statistics.h" struct Z3_stats_ref : public api::object { statistics m_stats; virtual ~Z3_stats_ref() {} }; inline Z3_stats_ref * to_stats(Z3_stats s) { return reinterpret_cast(s); } inline Z3_stats of_stats(Z3_stats_ref * s) { return reinterpret_cast(s); } inline statistics & to_stats_ref(Z3_stats s) { return to_stats(s)->m_stats; } #endif z3-4.4.0/src/api/z3_private.h0000644000175000017500000000120012540347414015435 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: z3_private.h Abstract: Z3 API. Author: Nikolaj Bjorner (nbjorner) Leonardo de Moura (leonardo) 2007-06-8 Notes: --*/ #include #include"rational.h" #include"z3_macros.h" #ifndef _Z3_PRIVATE__H_ #define _Z3_PRIVATE__H_ #ifndef CAMLIDL #ifdef __cplusplus extern "C" { #endif // __cplusplus #else [pointer_default(ref)] interface Z3 { #endif // CAMLIDL Z3_bool Z3_API Z3_get_numeral_rational(__in Z3_context c, __in Z3_ast a, rational& r); #ifndef CAMLIDL #ifdef __cplusplus }; #endif // __cplusplus #else } #endif // CAMLIDL #endif z3-4.4.0/src/api/api_ast_map.cpp0000644000175000017500000001152312540347414016170 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_ast_map.cpp Abstract: API for creating AST maps Author: Leonardo de Moura (leonardo) 2012-03-09. Revision History: --*/ #include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" #include"api_ast_map.h" #include"api_ast_vector.h" #include"ast_smt2_pp.h" #include"dec_ref_util.h" Z3_ast_map_ref::~Z3_ast_map_ref() { dec_ref_key_values(m, m_map); } extern "C" { Z3_ast_map Z3_API Z3_mk_ast_map(Z3_context c) { Z3_TRY; LOG_Z3_mk_ast_map(c); RESET_ERROR_CODE(); Z3_ast_map_ref * m = alloc(Z3_ast_map_ref, mk_c(c)->m()); mk_c(c)->save_object(m); Z3_ast_map r = of_ast_map(m); RETURN_Z3(r); Z3_CATCH_RETURN(0); } void Z3_API Z3_ast_map_inc_ref(Z3_context c, Z3_ast_map m) { Z3_TRY; LOG_Z3_ast_map_inc_ref(c, m); RESET_ERROR_CODE(); to_ast_map(m)->inc_ref(); Z3_CATCH; } void Z3_API Z3_ast_map_dec_ref(Z3_context c, Z3_ast_map m) { Z3_TRY; LOG_Z3_ast_map_dec_ref(c, m); RESET_ERROR_CODE(); to_ast_map(m)->dec_ref(); Z3_CATCH; } Z3_bool Z3_API Z3_ast_map_contains(Z3_context c, Z3_ast_map m, Z3_ast k) { Z3_TRY; LOG_Z3_ast_map_contains(c, m, k); RESET_ERROR_CODE(); return to_ast_map_ref(m).contains(to_ast(k)); Z3_CATCH_RETURN(Z3_FALSE); } Z3_ast Z3_API Z3_ast_map_find(Z3_context c, Z3_ast_map m, Z3_ast k) { Z3_TRY; LOG_Z3_ast_map_find(c, m, k); RESET_ERROR_CODE(); obj_map::obj_map_entry * entry = to_ast_map_ref(m).find_core(to_ast(k)); if (entry == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } else { ast * r = entry->get_data().m_value; RETURN_Z3(of_ast(r)); } Z3_CATCH_RETURN(0); } void Z3_API Z3_ast_map_insert(Z3_context c, Z3_ast_map m, Z3_ast k, Z3_ast v) { Z3_TRY; LOG_Z3_ast_map_insert(c, m, k, v); RESET_ERROR_CODE(); ast_manager & mng = to_ast_map(m)->m; obj_map::obj_map_entry * entry = to_ast_map_ref(m).insert_if_not_there2(to_ast(k), 0); if (entry->get_data().m_value == 0) { // new entry mng.inc_ref(to_ast(k)); mng.inc_ref(to_ast(v)); entry->get_data().m_value = to_ast(v); } else { // replacing entry mng.inc_ref(to_ast(v)); mng.dec_ref(entry->get_data().m_value); entry->get_data().m_value = to_ast(v); } Z3_CATCH; } void Z3_API Z3_ast_map_reset(Z3_context c, Z3_ast_map m) { Z3_TRY; LOG_Z3_ast_map_reset(c, m); RESET_ERROR_CODE(); dec_ref_key_values(to_ast_map(m)->m, to_ast_map_ref(m)); SASSERT(to_ast_map_ref(m).empty()); Z3_CATCH; } void Z3_API Z3_ast_map_erase(Z3_context c, Z3_ast_map m, Z3_ast k) { Z3_TRY; LOG_Z3_ast_map_erase(c, m, k); RESET_ERROR_CODE(); ast * v = 0; if (to_ast_map_ref(m).find(to_ast(k), v)) { to_ast_map_ref(m).erase(to_ast(k)); ast_manager & mng = to_ast_map(m)->m; mng.dec_ref(to_ast(k)); mng.dec_ref(v); } Z3_CATCH; } unsigned Z3_API Z3_ast_map_size(Z3_context c, Z3_ast_map m) { Z3_TRY; LOG_Z3_ast_map_size(c, m); RESET_ERROR_CODE(); return to_ast_map_ref(m).size(); Z3_CATCH_RETURN(0); } Z3_ast_vector Z3_API Z3_ast_map_keys(Z3_context c, Z3_ast_map m) { Z3_TRY; LOG_Z3_ast_map_keys(c, m); RESET_ERROR_CODE(); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, to_ast_map(m)->m); mk_c(c)->save_object(v); obj_map::iterator it = to_ast_map_ref(m).begin(); obj_map::iterator end = to_ast_map_ref(m).end(); for (; it != end; ++it) { v->m_ast_vector.push_back(it->m_key); } Z3_ast_vector r = of_ast_vector(v); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_ast_map_to_string(Z3_context c, Z3_ast_map m) { Z3_TRY; LOG_Z3_ast_map_to_string(c, m); RESET_ERROR_CODE(); std::ostringstream buffer; ast_manager & mng = to_ast_map(m)->m; buffer << "(ast-map"; obj_map::iterator it = to_ast_map_ref(m).begin(); obj_map::iterator end = to_ast_map_ref(m).end(); for (; it != end; ++it) { buffer << "\n (" << mk_ismt2_pp(it->m_key, mng, 3) << "\n " << mk_ismt2_pp(it->m_value, mng, 3) << ")"; } buffer << ")"; return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(0); } }; z3-4.4.0/src/api/api_fpa.cpp0000644000175000017500000007000312540347414015310 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: api_fpa.cpp Abstract: Additional APIs for floating-point arithmetic (FP). Author: Christoph M. Wintersteiger (cwinter) 2013-06-05 Notes: --*/ #include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" #include"fpa_decl_plugin.h" extern "C" { Z3_sort Z3_API Z3_mk_fpa_rounding_mode_sort(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_rounding_mode_sort(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); sort * s = ctx->fpautil().mk_rm_sort(); mk_c(c)->save_ast_trail(s); RETURN_Z3(of_sort(s)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_even(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_round_nearest_ties_to_even(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_nearest_ties_to_even(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_rne(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_rne(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_nearest_ties_to_even(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_away(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_round_nearest_ties_to_away(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_nearest_ties_to_away(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_rna(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_rna(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_nearest_ties_to_away(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_round_toward_positive(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_round_toward_positive(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_toward_positive(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_rtp(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_rtp(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_toward_positive(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_round_toward_negative(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_round_toward_negative(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_toward_negative(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_rtn(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_rtn(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_toward_negative(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_round_toward_zero(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_round_toward_zero(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_toward_zero(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_rtz(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_rtz(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_toward_zero(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_mk_fpa_sort(Z3_context c, unsigned ebits, unsigned sbits) { Z3_TRY; LOG_Z3_mk_fpa_sort(c, ebits, sbits); RESET_ERROR_CODE(); if (ebits < 2 || sbits < 3) { SET_ERROR_CODE(Z3_INVALID_ARG); } api::context * ctx = mk_c(c); sort * s = ctx->fpautil().mk_float_sort(ebits, sbits); ctx->save_ast_trail(s); RETURN_Z3(of_sort(s)); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_mk_fpa_sort_half(Z3_context c) { return Z3_mk_fpa_sort(c, 5, 11); } Z3_sort Z3_API Z3_mk_fpa_sort_16(Z3_context c) { return Z3_mk_fpa_sort(c, 5, 11); } Z3_sort Z3_API Z3_mk_fpa_sort_single(Z3_context c) { return Z3_mk_fpa_sort(c, 8, 24); } Z3_sort Z3_API Z3_mk_fpa_sort_32(Z3_context c) { return Z3_mk_fpa_sort(c, 8, 24); } Z3_sort Z3_API Z3_mk_fpa_sort_double(Z3_context c) { return Z3_mk_fpa_sort(c, 11, 53); } Z3_sort Z3_API Z3_mk_fpa_sort_64(Z3_context c) { return Z3_mk_fpa_sort(c, 11, 53); } Z3_sort Z3_API Z3_mk_fpa_sort_quadruple(Z3_context c) { return Z3_mk_fpa_sort(c, 15, 113); } Z3_sort Z3_API Z3_mk_fpa_sort_128(Z3_context c) { return Z3_mk_fpa_sort(c, 15, 113); } Z3_ast Z3_API Z3_mk_fpa_nan(Z3_context c, Z3_sort s) { Z3_TRY; LOG_Z3_mk_fpa_nan(c, s); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_nan(to_sort(s)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_inf(Z3_context c, Z3_sort s, Z3_bool negative) { Z3_TRY; LOG_Z3_mk_fpa_inf(c, s, negative); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = negative != 0 ? ctx->fpautil().mk_ninf(to_sort(s)) : ctx->fpautil().mk_pinf(to_sort(s)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_zero(Z3_context c, Z3_sort s, Z3_bool negative) { Z3_TRY; LOG_Z3_mk_fpa_inf(c, s, negative); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = negative != 0 ? ctx->fpautil().mk_nzero(to_sort(s)) : ctx->fpautil().mk_pzero(to_sort(s)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_fp(Z3_context c, Z3_ast sgn, Z3_ast exp, Z3_ast sig) { Z3_TRY; LOG_Z3_mk_fpa_fp(c, sgn, sig, exp); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_fp(to_expr(sgn), to_expr(sig), to_expr(exp)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_numeral_float(Z3_context c, float v, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_fpa_numeral_float(c, v, ty); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); scoped_mpf tmp(ctx->fpautil().fm()); ctx->fpautil().fm().set(tmp, ctx->fpautil().get_ebits(to_sort(ty)), ctx->fpautil().get_sbits(to_sort(ty)), v); expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_numeral_double(Z3_context c, double v, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_fpa_numeral_double(c, v, ty); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); scoped_mpf tmp(ctx->fpautil().fm()); ctx->fpautil().fm().set(tmp, ctx->fpautil().get_ebits(to_sort(ty)), ctx->fpautil().get_sbits(to_sort(ty)), v); expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_numeral_int(Z3_context c, signed v, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_fpa_numeral_int(c, v, ty); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); scoped_mpf tmp(ctx->fpautil().fm()); ctx->fpautil().fm().set(tmp, ctx->fpautil().get_ebits(to_sort(ty)), ctx->fpautil().get_sbits(to_sort(ty)), v); expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_numeral_int_uint(Z3_context c, Z3_bool sgn, signed exp, unsigned sig, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_fpa_numeral_int64_uint64(c, sgn, exp, sig, ty); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); scoped_mpf tmp(ctx->fpautil().fm()); ctx->fpautil().fm().set(tmp, ctx->fpautil().get_ebits(to_sort(ty)), ctx->fpautil().get_sbits(to_sort(ty)), sgn != 0, sig, exp); expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_numeral_int64_uint64(Z3_context c, Z3_bool sgn, __int64 exp, __uint64 sig, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_fpa_numeral_int64_uint64(c, sgn, exp, sig, ty); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); scoped_mpf tmp(ctx->fpautil().fm()); ctx->fpautil().fm().set(tmp, ctx->fpautil().get_ebits(to_sort(ty)), ctx->fpautil().get_sbits(to_sort(ty)), sgn != 0, sig, exp); expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_abs(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_abs(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_abs(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_neg(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_neg(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_neg(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_add(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_add(c, rm, t1, t2); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_add(to_expr(rm), to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_sub(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_add(c, rm, t1, t2); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_sub(to_expr(rm), to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_mul(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_add(c, rm, t1, t2); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_mul(to_expr(rm), to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_div(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_add(c, rm, t1, t2); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_div(to_expr(rm), to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_fma(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2, Z3_ast t3) { Z3_TRY; LOG_Z3_mk_fpa_fma(c, rm, t1, t2, t3); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_fma(to_expr(rm), to_expr(t1), to_expr(t2), to_expr(t3)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_sqrt(Z3_context c, Z3_ast rm, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_sqrt(c, rm, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_sqrt(to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_rem(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_rem(c, t1, t2); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_rem(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_round_to_integral(Z3_context c, Z3_ast rm, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_round_to_integral(c, rm, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_to_integral(to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_min(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_min(c, t1, t2); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_min(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_max(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_max(c, t1, t2); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); Z3_ast r = of_ast(ctx->fpautil().mk_max(to_expr(t1), to_expr(t2))); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_leq(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_leq(c, t1, t2); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_le(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_lt(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_lt(c, t1, t2); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_lt(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_geq(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_geq(c, t1, t2); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_ge(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_gt(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_gt(c, t1, t2); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_gt(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_eq(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_eq(c, t1, t2); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_float_eq(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_is_normal(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_is_normal(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_normal(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_is_subnormal(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_is_subnormal(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_subnormal(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_is_zero(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_is_zero(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_zero(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_is_infinite(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_is_infinite(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_inf(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_is_nan(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_is_nan(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_nan(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_is_negative(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_is_negative(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_negative(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_is_positive(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_is_positive(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_positive(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_to_fp_bv(Z3_context c, Z3_ast bv, Z3_sort s) { Z3_TRY; LOG_Z3_mk_fpa_to_fp_bv(c, bv, s); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!ctx->bvutil().is_bv(to_expr(bv)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(bv)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_to_fp_float(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s) { Z3_TRY; LOG_Z3_mk_fpa_to_fp_float(c, rm, t, s); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!fu.is_rm(to_expr(rm)) || !fu.is_float(to_expr(t)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_to_fp_real(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s) { Z3_TRY; LOG_Z3_mk_fpa_to_fp_real(c, rm, t, s); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!fu.is_rm(to_expr(rm)) || !ctx->autil().is_real(to_expr(t)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_to_fp_signed(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s) { Z3_TRY; LOG_Z3_mk_fpa_to_fp_signed(c, rm, t, s); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!fu.is_rm(to_expr(rm)) || !ctx->bvutil().is_bv(to_expr(t)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_to_fp_unsigned(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s) { Z3_TRY; LOG_Z3_mk_fpa_to_fp_unsigned(c, rm, t, s); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!fu.is_rm(to_expr(rm)) || !ctx->bvutil().is_bv(to_expr(t)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } expr * a = fu.mk_to_fp_unsigned(to_sort(s), to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_to_ubv(Z3_context c, Z3_ast rm, Z3_ast t, unsigned sz) { Z3_TRY; LOG_Z3_mk_fpa_to_ubv(c, rm, t, sz); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_to_ubv(to_expr(rm), to_expr(t), sz); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_to_sbv(Z3_context c, Z3_ast rm, Z3_ast t, unsigned sz) { Z3_TRY; LOG_Z3_mk_fpa_to_sbv(c, rm, t, sz); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_to_sbv(to_expr(rm), to_expr(t), sz); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_to_real(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_to_real(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_to_real(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_fpa_get_ebits(Z3_context c, Z3_sort s) { Z3_TRY; LOG_Z3_fpa_get_ebits(c, s); RESET_ERROR_CODE(); CHECK_NON_NULL(s, 0); return mk_c(c)->fpautil().get_ebits(to_sort(s)); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_fpa_get_sbits(Z3_context c, Z3_sort s) { Z3_TRY; LOG_Z3_fpa_get_sbits(c, s); RESET_ERROR_CODE(); CHECK_NON_NULL(s, 0); return mk_c(c)->fpautil().get_sbits(to_sort(s)); Z3_CATCH_RETURN(0); } Z3_bool Z3_API Z3_fpa_get_numeral_sign(Z3_context c, Z3_ast t, int * sgn) { Z3_TRY; LOG_Z3_fpa_get_numeral_sign(c, t, sgn); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(mk_c(c)->get_fpa_fid()); scoped_mpf val(mpfm); bool r = plugin->is_numeral(to_expr(t), val); if (!r || mpfm.is_nan(val)) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } *sgn = (mpfm.is_nan(val)) ? 0 : mpfm.sgn(val); return r; Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_fpa_get_numeral_significand_string(__in Z3_context c, __in Z3_ast t) { Z3_TRY; LOG_Z3_fpa_get_numeral_significand_string(c, t); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); unsynch_mpz_manager & mpzm = mpfm.mpz_manager(); unsynch_mpq_manager & mpqm = mpfm.mpq_manager(); fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(mk_c(c)->get_fpa_fid()); scoped_mpf val(mpfm); if (!plugin->is_numeral(to_expr(t), val)) { SET_ERROR_CODE(Z3_INVALID_ARG); return ""; } else if (!mpfm.is_regular(val)) { SET_ERROR_CODE(Z3_INVALID_ARG) return ""; } unsigned sbits = val.get().get_sbits(); scoped_mpq q(mpqm); scoped_mpz sn(mpzm); mpfm.sig_normalized(val, sn); mpqm.set(q, sn); mpqm.div(q, mpfm.m_powers2(sbits - 1), q); std::stringstream ss; mpqm.display_decimal(ss, q, sbits); return mk_c(c)->mk_external_string(ss.str()); Z3_CATCH_RETURN(""); } Z3_bool Z3_API Z3_fpa_get_numeral_significand_uint64(__in Z3_context c, __in Z3_ast t, __out __uint64 * n) { Z3_TRY; LOG_Z3_fpa_get_numeral_significand_uint64(c, t, n); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); unsynch_mpz_manager & mpzm = mpfm.mpz_manager(); fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(mk_c(c)->get_fpa_fid()); scoped_mpf val(mpfm); bool r = plugin->is_numeral(to_expr(t), val); if (!r) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } const mpz & z = mpfm.sig(val); if (!mpzm.is_uint64(z)) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } *n = mpzm.get_uint64(z); return 1; Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_fpa_get_numeral_exponent_string(__in Z3_context c, __in Z3_ast t) { Z3_TRY; LOG_Z3_fpa_get_numeral_exponent_string(c, t); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(mk_c(c)->get_fpa_fid()); scoped_mpf val(mpfm); bool r = plugin->is_numeral(to_expr(t), val); if (!r) { SET_ERROR_CODE(Z3_INVALID_ARG); return ""; } else if (!mpfm.is_normal(val) && !mpfm.is_denormal(val)) { SET_ERROR_CODE(Z3_INVALID_ARG) return ""; } mpf_exp_t exp = mpfm.exp_normalized(val); std::stringstream ss; ss << exp; return mk_c(c)->mk_external_string(ss.str()); Z3_CATCH_RETURN(""); } Z3_bool Z3_API Z3_fpa_get_numeral_exponent_int64(__in Z3_context c, __in Z3_ast t, __out __int64 * n) { Z3_TRY; LOG_Z3_fpa_get_numeral_exponent_int64(c, t, n); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(mk_c(c)->get_fpa_fid()); scoped_mpf val(mpfm); bool r = plugin->is_numeral(to_expr(t), val); if (!r) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } *n = mpfm.exp(val); return 1; Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_to_ieee_bv(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_to_ieee_bv(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); Z3_ast r = of_ast(ctx->fpautil().mk_float_to_ieee_bv(to_expr(t))); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_fpa_to_fp_int_real(Z3_context c, Z3_ast rm, Z3_ast exp, Z3_ast sig, Z3_sort s) { Z3_TRY; LOG_Z3_mk_fpa_to_fp_int_real(c, rm, sig, exp, s); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!fu.is_rm(to_expr(rm)) || !ctx->autil().is_real(to_expr(sig)) || !ctx->autil().is_int(to_expr(exp)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(rm), to_expr(sig), to_expr(exp)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(0); } }; z3-4.4.0/src/api/z3_replayer.h0000644000175000017500000000303612540347414015617 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: z3_replayer.h Abstract: Interpreter for Z3 logs Author: Leonardo de Moura (leonardo) 2011-09-22 Notes: --*/ #ifndef _Z3_REPLAYER_H_ #define _Z3_REPLAYER_H_ #include #include"z3.h" #include"z3_exception.h" class z3_replayer; typedef void (*z3_replayer_cmd)(z3_replayer &); typedef default_exception z3_replayer_exception; class z3_replayer { struct imp; imp * m_imp; public: z3_replayer(std::istream & in); ~z3_replayer(); void parse(); unsigned get_line() const; int get_int(unsigned pos) const; unsigned get_uint(unsigned pos) const; __int64 get_int64(unsigned pos) const; __uint64 get_uint64(unsigned pos) const; float get_float(unsigned pos) const; double get_double(unsigned pos) const; bool get_bool(unsigned pos) const; Z3_string get_str(unsigned pos) const; Z3_symbol get_symbol(unsigned pos) const; void * get_obj(unsigned pos) const; unsigned * get_uint_array(unsigned pos) const; int * get_int_array(unsigned pos) const; Z3_symbol * get_symbol_array(unsigned pos) const; void ** get_obj_array(unsigned pos) const; int * get_int_addr(unsigned pos); __int64 * get_int64_addr(unsigned pos); unsigned * get_uint_addr(unsigned pos); __uint64 * get_uint64_addr(unsigned pos); Z3_string * get_str_addr(unsigned pos); void ** get_obj_addr(unsigned pos); void store_result(void * obj); void register_cmd(unsigned id, z3_replayer_cmd cmd); }; #endif z3-4.4.0/src/api/z3.h0000644000175000017500000000105412540347414013712 0ustar michaelmichael/*++ Copyright (c) 2007 Microsoft Corporation Module Name: z3.h Abstract: Z3 API. Author: Nikolaj Bjorner (nbjorner) Leonardo de Moura (leonardo) 2007-06-8 Notes: --*/ #ifndef _Z3__H_ #define _Z3__H_ #include #include"z3_macros.h" #include"z3_api.h" #include"z3_algebraic.h" #include"z3_polynomial.h" #include"z3_rcf.h" #include"z3_interp.h" #include"z3_fpa.h" #undef __in #undef __out #undef __inout #undef __in_z #undef __out_z #undef __ecount #undef __in_ecount #undef __out_ecount #undef __inout_ecount #endif z3-4.4.0/src/api/api_algebraic.cpp0000644000175000017500000003777712540347414016500 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_algebraic.cpp Abstract: Additional APIs for handling Z3 algebraic numbers encoded as Z3_ASTs Author: Leonardo de Moura (leonardo) 2012-12-07 Notes: --*/ #include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" #include"api_ast_vector.h" #include"algebraic_numbers.h" #include"expr2polynomial.h" #include"cancel_eh.h" #include"scoped_timer.h" #define CHECK_IS_ALGEBRAIC(ARG, RET) { \ if (!Z3_algebraic_is_value_core(c, ARG)) { \ SET_ERROR_CODE(Z3_INVALID_ARG); \ return RET; \ } \ } #define CHECK_IS_ALGEBRAIC_X(ARG, RET) { \ if (!Z3_algebraic_is_value_core(c, ARG)) { \ SET_ERROR_CODE(Z3_INVALID_ARG); \ RETURN_Z3(RET); \ } \ } static arith_util & au(Z3_context c) { return mk_c(c)->autil(); } static algebraic_numbers::manager & am(Z3_context c) { return au(c).am(); } static bool is_rational(Z3_context c, Z3_ast a) { return au(c).is_numeral(to_expr(a)); } static bool is_irrational(Z3_context c, Z3_ast a) { return au(c).is_irrational_algebraic_numeral(to_expr(a)); } static rational get_rational(Z3_context c, Z3_ast a) { SASSERT(is_rational(c, a)); rational r; VERIFY(au(c).is_numeral(to_expr(a), r)); return r; } static algebraic_numbers::anum const & get_irrational(Z3_context c, Z3_ast a) { SASSERT(is_irrational(c, a)); return au(c).to_irrational_algebraic_numeral(to_expr(a)); } extern "C" { bool Z3_algebraic_is_value_core(Z3_context c, Z3_ast a) { api::context * _c = mk_c(c); return is_expr(a) && (_c->autil().is_numeral(to_expr(a)) || _c->autil().is_irrational_algebraic_numeral(to_expr(a))); } Z3_bool Z3_API Z3_algebraic_is_value(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_algebraic_is_value(c, a); RESET_ERROR_CODE(); return Z3_algebraic_is_value_core(c, a) ? Z3_TRUE : Z3_FALSE; Z3_CATCH_RETURN(Z3_FALSE); } Z3_bool Z3_API Z3_algebraic_is_pos(Z3_context c, Z3_ast a) { return Z3_algebraic_sign(c, a) > 0; } Z3_bool Z3_API Z3_algebraic_is_neg(Z3_context c, Z3_ast a) { return Z3_algebraic_sign(c, a) < 0; } Z3_bool Z3_API Z3_algebraic_is_zero(Z3_context c, Z3_ast a) { return Z3_algebraic_sign(c, a) == 0; } int Z3_API Z3_algebraic_sign(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_algebraic_sign(c, a); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC(a, 0); if (is_rational(c, a)) { rational v = get_rational(c, a); if (v.is_pos()) return 1; else if (v.is_neg()) return -1; else return 0; } else { algebraic_numbers::anum const & v = get_irrational(c, a); if (am(c).is_pos(v)) return 1; else if (am(c).is_neg(v)) return -1; else return 0; } Z3_CATCH_RETURN(0); } #define BIN_OP(RAT_OP, IRAT_OP) \ algebraic_numbers::manager & _am = am(c); \ ast * r = 0; \ if (is_rational(c, a)) { \ rational av = get_rational(c, a); \ if (is_rational(c, b)) { \ rational bv = get_rational(c, b); \ r = au(c).mk_numeral(av RAT_OP bv, false); \ } \ else { \ algebraic_numbers::anum const & bv = get_irrational(c, b); \ scoped_anum _av(_am); \ _am.set(_av, av.to_mpq()); \ scoped_anum _r(_am); \ _am.IRAT_OP(_av, bv, _r); \ r = au(c).mk_numeral(_r, false); \ } \ } \ else { \ algebraic_numbers::anum const & av = get_irrational(c, a); \ if (is_rational(c, b)) { \ rational bv = get_rational(c, b); \ scoped_anum _bv(_am); \ _am.set(_bv, bv.to_mpq()); \ scoped_anum _r(_am); \ _am.IRAT_OP(av, _bv, _r); \ r = au(c).mk_numeral(_r, false); \ } \ else { \ algebraic_numbers::anum const & bv = get_irrational(c, b); \ scoped_anum _r(_am); \ _am.IRAT_OP(av, bv, _r); \ r = au(c).mk_numeral(_r, false); \ } \ } \ mk_c(c)->save_ast_trail(r); \ RETURN_Z3(of_ast(r)); Z3_ast Z3_API Z3_algebraic_add(Z3_context c, Z3_ast a, Z3_ast b) { Z3_TRY; LOG_Z3_algebraic_add(c, a, b); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC_X(a, 0); CHECK_IS_ALGEBRAIC_X(b, 0); BIN_OP(+,add); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_algebraic_sub(Z3_context c, Z3_ast a, Z3_ast b) { Z3_TRY; LOG_Z3_algebraic_sub(c, a, b); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC_X(a, 0); CHECK_IS_ALGEBRAIC_X(b, 0); BIN_OP(-,sub); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_algebraic_mul(Z3_context c, Z3_ast a, Z3_ast b) { Z3_TRY; LOG_Z3_algebraic_mul(c, a, b); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC_X(a, 0); CHECK_IS_ALGEBRAIC_X(b, 0); BIN_OP(*,mul); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_algebraic_div(Z3_context c, Z3_ast a, Z3_ast b) { Z3_TRY; LOG_Z3_algebraic_div(c, a, b); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC_X(a, 0); CHECK_IS_ALGEBRAIC_X(b, 0); if ((is_rational(c, b) && get_rational(c, b).is_zero()) || (!is_rational(c, b) && am(c).is_zero(get_irrational(c, b)))) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } BIN_OP(/,div); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_algebraic_root(Z3_context c, Z3_ast a, unsigned k) { Z3_TRY; LOG_Z3_algebraic_root(c, a, k); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC_X(a, 0); if (k % 2 == 0) { if ((is_rational(c, a) && get_rational(c, a).is_neg()) || (!is_rational(c, a) && am(c).is_neg(get_irrational(c, a)))) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } } algebraic_numbers::manager & _am = am(c); scoped_anum _r(_am); if (is_rational(c, a)) { scoped_anum av(_am); _am.set(av, get_rational(c, a).to_mpq()); _am.root(av, k, _r); } else { algebraic_numbers::anum const & av = get_irrational(c, a); _am.root(av, k, _r); } expr * r = au(c).mk_numeral(_r, false); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_algebraic_power(Z3_context c, Z3_ast a, unsigned k) { Z3_TRY; LOG_Z3_algebraic_power(c, a, k); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC_X(a, 0); algebraic_numbers::manager & _am = am(c); scoped_anum _r(_am); if (is_rational(c, a)) { scoped_anum av(_am); _am.set(av, get_rational(c, a).to_mpq()); _am.power(av, k, _r); } else { algebraic_numbers::anum const & av = get_irrational(c, a); _am.power(av, k, _r); } expr * r = au(c).mk_numeral(_r, false); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(0); } #define BIN_PRED(RAT_PRED, IRAT_PRED) \ algebraic_numbers::manager & _am = am(c); \ bool r; \ if (is_rational(c, a)) { \ rational av = get_rational(c, a); \ if (is_rational(c, b)) { \ rational bv = get_rational(c, b); \ r = av RAT_PRED bv; \ } \ else { \ algebraic_numbers::anum const & bv = get_irrational(c, b); \ scoped_anum _av(_am); \ _am.set(_av, av.to_mpq()); \ r = _am.IRAT_PRED(_av, bv); \ } \ } \ else { \ algebraic_numbers::anum const & av = get_irrational(c, a); \ if (is_rational(c, b)) { \ rational bv = get_rational(c, b); \ scoped_anum _bv(_am); \ _am.set(_bv, bv.to_mpq()); \ r = _am.IRAT_PRED(av, _bv); \ } \ else { \ algebraic_numbers::anum const & bv = get_irrational(c, b); \ r = _am.IRAT_PRED(av, bv); \ } \ } \ return r ? Z3_TRUE : Z3_FALSE; Z3_bool Z3_API Z3_algebraic_lt(Z3_context c, Z3_ast a, Z3_ast b) { Z3_TRY; LOG_Z3_algebraic_lt(c, a, b); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC(a, 0); CHECK_IS_ALGEBRAIC(b, 0); BIN_PRED(<,lt); Z3_CATCH_RETURN(Z3_FALSE); } Z3_bool Z3_API Z3_algebraic_gt(Z3_context c, Z3_ast a, Z3_ast b) { return Z3_algebraic_lt(c, b, a); } Z3_bool Z3_API Z3_algebraic_le(Z3_context c, Z3_ast a, Z3_ast b) { return !Z3_algebraic_lt(c, b, a); } Z3_bool Z3_API Z3_algebraic_ge(Z3_context c, Z3_ast a, Z3_ast b) { return !Z3_algebraic_lt(c, a, b); } Z3_bool Z3_API Z3_algebraic_eq(Z3_context c, Z3_ast a, Z3_ast b) { Z3_TRY; LOG_Z3_algebraic_eq(c, a, b); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC(a, 0); CHECK_IS_ALGEBRAIC(b, 0); BIN_PRED(==,eq); Z3_CATCH_RETURN(0); } Z3_bool Z3_API Z3_algebraic_neq(Z3_context c, Z3_ast a, Z3_ast b) { return !Z3_algebraic_eq(c, a, b); } static bool to_anum_vector(Z3_context c, unsigned n, Z3_ast a[], scoped_anum_vector & as) { algebraic_numbers::manager & _am = am(c); scoped_anum tmp(_am); for (unsigned i = 0; i < n; i++) { if (is_rational(c, a[i])) { _am.set(tmp, get_rational(c, a[i]).to_mpq()); as.push_back(tmp); } else if (is_irrational(c, a[i])) { as.push_back(get_irrational(c, a[i])); } else { return false; } } return true; } class vector_var2anum : public polynomial::var2anum { scoped_anum_vector const & m_as; public: vector_var2anum(scoped_anum_vector & as):m_as(as) {} virtual ~vector_var2anum() {} virtual algebraic_numbers::manager & m() const { return m_as.m(); } virtual bool contains(polynomial::var x) const { return static_cast(x) < m_as.size(); } virtual algebraic_numbers::anum const & operator()(polynomial::var x) const { return m_as.get(x); } }; Z3_ast_vector Z3_API Z3_algebraic_roots(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]) { Z3_TRY; LOG_Z3_algebraic_roots(c, p, n, a); RESET_ERROR_CODE(); polynomial::manager & pm = mk_c(c)->pm(); polynomial_ref _p(pm); polynomial::scoped_numeral d(pm.m()); expr2polynomial converter(mk_c(c)->m(), pm, 0, true); if (!converter.to_polynomial(to_expr(p), _p, d) || static_cast(max_var(_p)) >= n + 1) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } algebraic_numbers::manager & _am = am(c); scoped_anum_vector as(_am); if (!to_anum_vector(c, n, a, as)) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } scoped_anum_vector roots(_am); { cancel_eh eh(_am); api::context::set_interruptable si(*(mk_c(c)), eh); scoped_timer timer(mk_c(c)->params().m_timeout, &eh); vector_var2anum v2a(as); _am.isolate_roots(_p, v2a, roots); } Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, mk_c(c)->m()); mk_c(c)->save_object(result); for (unsigned i = 0; i < roots.size(); i++) { result->m_ast_vector.push_back(au(c).mk_numeral(roots.get(i), false)); } RETURN_Z3(of_ast_vector(result)); Z3_CATCH_RETURN(0); } int Z3_API Z3_algebraic_eval(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]) { Z3_TRY; LOG_Z3_algebraic_eval(c, p, n, a); RESET_ERROR_CODE(); polynomial::manager & pm = mk_c(c)->pm(); polynomial_ref _p(pm); polynomial::scoped_numeral d(pm.m()); expr2polynomial converter(mk_c(c)->m(), pm, 0, true); if (!converter.to_polynomial(to_expr(p), _p, d) || static_cast(max_var(_p)) >= n) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } algebraic_numbers::manager & _am = am(c); scoped_anum_vector as(_am); if (!to_anum_vector(c, n, a, as)) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } { cancel_eh eh(_am); api::context::set_interruptable si(*(mk_c(c)), eh); scoped_timer timer(mk_c(c)->params().m_timeout, &eh); vector_var2anum v2a(as); int r = _am.eval_sign_at(_p, v2a); if (r > 0) return 1; else if (r < 0) return -1; else return 0; } Z3_CATCH_RETURN(0); } }; z3-4.4.0/src/api/api_polynomial.cpp0000644000175000017500000000422712540347414016732 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_polynomial.cpp Abstract: Polynomial manager and caches for the external API. Author: Leonardo de Moura (leonardo) 2012-12-08 Notes: --*/ #include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" #include"api_polynomial.h" #include"api_ast_vector.h" #include"expr2polynomial.h" #include"cancel_eh.h" #include"scoped_timer.h" #include"expr2var.h" namespace api { pmanager::pmanager(): m_pm(m_nm) { } pmanager::~pmanager() { } void pmanager::set_cancel(bool f) { m_pm.set_cancel(f); } }; extern "C" { Z3_ast_vector Z3_API Z3_polynomial_subresultants(Z3_context c, Z3_ast p, Z3_ast q, Z3_ast x) { Z3_TRY; LOG_Z3_polynomial_subresultants(c, p, q, x); RESET_ERROR_CODE(); polynomial::manager & pm = mk_c(c)->pm(); polynomial_ref _p(pm), _q(pm); polynomial::scoped_numeral d(pm.m()); default_expr2polynomial converter(mk_c(c)->m(), pm); if (!converter.to_polynomial(to_expr(p), _p, d) || !converter.to_polynomial(to_expr(q), _q, d)) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, mk_c(c)->m()); mk_c(c)->save_object(result); if (converter.is_var(to_expr(x))) { expr2var const & mapping = converter.get_mapping(); unsigned v_x = mapping.to_var(to_expr(x)); polynomial_ref_vector rs(pm); polynomial_ref r(pm); expr_ref _r(mk_c(c)->m()); { cancel_eh eh(pm); api::context::set_interruptable si(*(mk_c(c)), eh); scoped_timer timer(mk_c(c)->params().m_timeout, &eh); pm.psc_chain(_p, _q, v_x, rs); } for (unsigned i = 0; i < rs.size(); i++) { r = rs.get(i); converter.to_expr(r, true, _r); result->m_ast_vector.push_back(_r); } } RETURN_Z3(of_ast_vector(result)); Z3_CATCH_RETURN(0); } }; z3-4.4.0/src/api/api_opt.cpp0000644000175000017500000001637712540347414015362 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: api_opt.cpp Abstract: API for optimization Author: Nikolaj Bjorner (nbjorner) 2013-12-3. Revision History: --*/ #include #include"z3.h" #include"api_log_macros.h" #include"api_stats.h" #include"api_context.h" #include"api_util.h" #include"api_model.h" #include"opt_context.h" #include"cancel_eh.h" #include"scoped_timer.h" extern "C" { struct Z3_optimize_ref : public api::object { opt::context* m_opt; Z3_optimize_ref():m_opt(0) {} virtual ~Z3_optimize_ref() { dealloc(m_opt); } }; inline Z3_optimize_ref * to_optimize(Z3_optimize o) { return reinterpret_cast(o); } inline Z3_optimize of_optimize(Z3_optimize_ref * o) { return reinterpret_cast(o); } inline opt::context* to_optimize_ptr(Z3_optimize o) { return to_optimize(o)->m_opt; } Z3_optimize Z3_API Z3_mk_optimize(Z3_context c) { Z3_TRY; LOG_Z3_mk_optimize(c); RESET_ERROR_CODE(); Z3_optimize_ref * o = alloc(Z3_optimize_ref); o->m_opt = alloc(opt::context,mk_c(c)->m()); mk_c(c)->save_object(o); RETURN_Z3(of_optimize(o)); Z3_CATCH_RETURN(0); } void Z3_API Z3_optimize_inc_ref(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_inc_ref(c, o); RESET_ERROR_CODE(); to_optimize(o)->inc_ref(); Z3_CATCH; } void Z3_API Z3_optimize_dec_ref(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_dec_ref(c, o); RESET_ERROR_CODE(); to_optimize(o)->dec_ref(); Z3_CATCH; } void Z3_API Z3_optimize_assert(Z3_context c, Z3_optimize o, Z3_ast a) { Z3_TRY; LOG_Z3_optimize_assert(c, o, a); RESET_ERROR_CODE(); CHECK_FORMULA(a,); to_optimize_ptr(o)->add_hard_constraint(to_expr(a)); Z3_CATCH; } unsigned Z3_API Z3_optimize_assert_soft(Z3_context c, Z3_optimize o, Z3_ast a, Z3_string weight, Z3_symbol id) { Z3_TRY; LOG_Z3_optimize_assert_soft(c, o, a, weight, id); RESET_ERROR_CODE(); CHECK_FORMULA(a,0); rational w(weight); return to_optimize_ptr(o)->add_soft_constraint(to_expr(a), w, to_symbol(id)); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_optimize_maximize(Z3_context c, Z3_optimize o, Z3_ast t) { Z3_TRY; LOG_Z3_optimize_maximize(c, o, t); RESET_ERROR_CODE(); CHECK_VALID_AST(t,0); return to_optimize_ptr(o)->add_objective(to_app(t), true); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_optimize_minimize(Z3_context c, Z3_optimize o, Z3_ast t) { Z3_TRY; LOG_Z3_optimize_minimize(c, o, t); RESET_ERROR_CODE(); CHECK_VALID_AST(t,0); return to_optimize_ptr(o)->add_objective(to_app(t), false); Z3_CATCH_RETURN(0); } void Z3_API Z3_optimize_push(Z3_context c,Z3_optimize d) { Z3_TRY; LOG_Z3_optimize_push(c, d); RESET_ERROR_CODE(); to_optimize_ptr(d)->push(); Z3_CATCH; } void Z3_API Z3_optimize_pop(Z3_context c,Z3_optimize d) { Z3_TRY; LOG_Z3_optimize_pop(c, d); RESET_ERROR_CODE(); to_optimize_ptr(d)->pop(1); Z3_CATCH; } Z3_lbool Z3_API Z3_optimize_check(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_check(c, o); RESET_ERROR_CODE(); lbool r = l_undef; cancel_eh eh(*to_optimize_ptr(o)); unsigned timeout = to_optimize_ptr(o)->get_params().get_uint("timeout", mk_c(c)->get_timeout()); api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_timer timer(timeout, &eh); try { r = to_optimize_ptr(o)->optimize(); } catch (z3_exception& ex) { mk_c(c)->handle_exception(ex); r = l_undef; } // to_optimize_ref(d).cleanup(); } return of_lbool(r); Z3_CATCH_RETURN(Z3_L_UNDEF); } Z3_model Z3_API Z3_optimize_get_model(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_get_model(c, o); RESET_ERROR_CODE(); model_ref _m; to_optimize_ptr(o)->get_model(_m); Z3_model_ref * m_ref = alloc(Z3_model_ref); if (_m) { m_ref->m_model = _m; } else { m_ref->m_model = alloc(model, mk_c(c)->m()); } mk_c(c)->save_object(m_ref); RETURN_Z3(of_model(m_ref)); Z3_CATCH_RETURN(0); } void Z3_API Z3_optimize_set_params(Z3_context c, Z3_optimize o, Z3_params p) { Z3_TRY; LOG_Z3_optimize_set_params(c, o, p); RESET_ERROR_CODE(); param_descrs descrs; to_optimize_ptr(o)->collect_param_descrs(descrs); to_params(p)->m_params.validate(descrs); params_ref pr = to_param_ref(p); to_optimize_ptr(o)->updt_params(pr); Z3_CATCH; } Z3_param_descrs Z3_API Z3_optimize_get_param_descrs(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_get_param_descrs(c, o); RESET_ERROR_CODE(); Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref); mk_c(c)->save_object(d); to_optimize_ptr(o)->collect_param_descrs(d->m_descrs); Z3_param_descrs r = of_param_descrs(d); RETURN_Z3(r); Z3_CATCH_RETURN(0); } // get lower value or current approximation Z3_ast Z3_API Z3_optimize_get_lower(Z3_context c, Z3_optimize o, unsigned idx) { Z3_TRY; LOG_Z3_optimize_get_lower(c, o, idx); RESET_ERROR_CODE(); expr_ref e = to_optimize_ptr(o)->get_lower(idx); mk_c(c)->save_ast_trail(e); RETURN_Z3(of_expr(e)); Z3_CATCH_RETURN(0); } // get upper or current approximation Z3_ast Z3_API Z3_optimize_get_upper(Z3_context c, Z3_optimize o, unsigned idx) { Z3_TRY; LOG_Z3_optimize_get_upper(c, o, idx); RESET_ERROR_CODE(); expr_ref e = to_optimize_ptr(o)->get_upper(idx); mk_c(c)->save_ast_trail(e); RETURN_Z3(of_expr(e)); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_optimize_to_string(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_to_string(c, o); RESET_ERROR_CODE(); return mk_c(c)->mk_external_string(to_optimize_ptr(o)->to_string()); Z3_CATCH_RETURN(""); } Z3_string Z3_API Z3_optimize_get_help(Z3_context c, Z3_optimize d) { Z3_TRY; LOG_Z3_optimize_get_help(c, d); RESET_ERROR_CODE(); std::ostringstream buffer; param_descrs descrs; to_optimize_ptr(d)->collect_param_descrs(descrs); descrs.display(buffer); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } Z3_stats Z3_API Z3_optimize_get_statistics(Z3_context c,Z3_optimize d) { Z3_TRY; LOG_Z3_optimize_get_statistics(c, d); RESET_ERROR_CODE(); Z3_stats_ref * st = alloc(Z3_stats_ref); to_optimize_ptr(d)->collect_statistics(st->m_stats); mk_c(c)->save_object(st); Z3_stats r = of_stats(st); RETURN_Z3(r); Z3_CATCH_RETURN(0); } }; z3-4.4.0/src/api/api_numeral.cpp0000644000175000017500000003106012540347414016205 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_numeral.cpp Abstract: API for handling numerals in Z3 Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" #include"api_util.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"algebraic_numbers.h" #include"fpa_decl_plugin.h" bool is_numeral_sort(Z3_context c, Z3_sort ty) { sort * _ty = to_sort(ty); family_id fid = _ty->get_family_id(); if (fid != mk_c(c)->get_arith_fid() && fid != mk_c(c)->get_bv_fid() && fid != mk_c(c)->get_datalog_fid() && fid != mk_c(c)->get_fpa_fid()) { return false; } return true; } bool check_numeral_sort(Z3_context c, Z3_sort ty) { bool is_num = is_numeral_sort(c, ty); if (!is_num) { SET_ERROR_CODE(Z3_INVALID_ARG); } return is_num; } extern "C" { Z3_ast Z3_API Z3_mk_numeral(Z3_context c, const char* n, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_numeral(c, n, ty); RESET_ERROR_CODE(); if (!check_numeral_sort(c, ty)) { RETURN_Z3(0); } if (!n) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } sort * _ty = to_sort(ty); bool is_float = mk_c(c)->fpautil().is_float(_ty); std::string fixed_num; char const* m = n; while (*m) { if (!(('0' <= *m && *m <= '9') || ('/' == *m) || ('-' == *m) || (' ' == *m) || ('\n' == *m) || ('.' == *m) || ('e' == *m) || ('E' == *m) || ('p' == *m && is_float) || ('P' == *m && is_float))) { SET_ERROR_CODE(Z3_PARSER_ERROR); return 0; } ++m; } ast * a = 0; if (_ty->get_family_id() == mk_c(c)->get_fpa_fid()) { // avoid expanding floats into huge rationals. fpa_util & fu = mk_c(c)->fpautil(); scoped_mpf t(fu.fm()); fu.fm().set(t, fu.get_ebits(_ty), fu.get_sbits(_ty), MPF_ROUND_TOWARD_ZERO, n); a = fu.mk_value(t); mk_c(c)->save_ast_trail(a); } else a = mk_c(c)->mk_numeral_core(rational(n), _ty); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_int(Z3_context c, int value, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_int(c, value, ty); RESET_ERROR_CODE(); if (!check_numeral_sort(c, ty)) { RETURN_Z3(0); } ast * a = mk_c(c)->mk_numeral_core(rational(value), to_sort(ty)); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_unsigned_int(Z3_context c, unsigned value, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_unsigned_int(c, value, ty); RESET_ERROR_CODE(); if (!check_numeral_sort(c, ty)) { RETURN_Z3(0); } ast * a = mk_c(c)->mk_numeral_core(rational(value), to_sort(ty)); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_int64(Z3_context c, long long value, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_int64(c, value, ty); RESET_ERROR_CODE(); if (!check_numeral_sort(c, ty)) { RETURN_Z3(0); } rational n(value, rational::i64()); ast* a = mk_c(c)->mk_numeral_core(n, to_sort(ty)); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_unsigned_int64(Z3_context c, unsigned long long value, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_unsigned_int64(c, value, ty); RESET_ERROR_CODE(); if (!check_numeral_sort(c, ty)) { RETURN_Z3(0); } rational n(value, rational::ui64()); ast * a = mk_c(c)->mk_numeral_core(n, to_sort(ty)); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(0); } Z3_bool Z3_API Z3_is_numeral_ast(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_is_numeral_ast(c, a); RESET_ERROR_CODE(); expr* e = to_expr(a); return mk_c(c)->autil().is_numeral(e) || mk_c(c)->bvutil().is_numeral(e) || mk_c(c)->fpautil().is_numeral(e) || mk_c(c)->fpautil().is_rm_numeral(e); Z3_CATCH_RETURN(Z3_FALSE); } Z3_bool Z3_API Z3_get_numeral_rational(Z3_context c, Z3_ast a, rational& r) { Z3_TRY; // This function is not part of the public API RESET_ERROR_CODE(); expr* e = to_expr(a); if (!e) { SET_ERROR_CODE(Z3_INVALID_ARG); return Z3_FALSE; } if (mk_c(c)->autil().is_numeral(e, r)) { return Z3_TRUE; } unsigned bv_size; if (mk_c(c)->bvutil().is_numeral(e, r, bv_size)) { return Z3_TRUE; } uint64 v; if (mk_c(c)->datalog_util().is_numeral(e, v)) { r = rational(v, rational::ui64()); return Z3_TRUE; } return Z3_FALSE; Z3_CATCH_RETURN(Z3_FALSE); } Z3_string Z3_API Z3_get_numeral_string(Z3_context c, Z3_ast a) { Z3_TRY; // This function invokes Z3_get_numeral_rational, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_string(c, a); RESET_ERROR_CODE(); rational r; Z3_bool ok = Z3_get_numeral_rational(c, a, r); if (ok == Z3_TRUE) { return mk_c(c)->mk_external_string(r.to_string()); } else { // floats are separated from all others to avoid huge rationals. fpa_util & fu = mk_c(c)->fpautil(); scoped_mpf tmp(fu.fm()); mpf_rounding_mode rm; if (mk_c(c)->fpautil().is_rm_numeral(to_expr(a), rm)) { switch (rm) { case OP_FPA_RM_NEAREST_TIES_TO_EVEN: return mk_c(c)->mk_external_string("roundNearestTiesToEven"); break; case OP_FPA_RM_NEAREST_TIES_TO_AWAY: return mk_c(c)->mk_external_string("roundNearestTiesToAway"); break; case OP_FPA_RM_TOWARD_POSITIVE: return mk_c(c)->mk_external_string("roundTowardPositive"); break; case OP_FPA_RM_TOWARD_NEGATIVE: return mk_c(c)->mk_external_string("roundTowardNegative"); break; case OP_FPA_RM_TOWARD_ZERO: default: return mk_c(c)->mk_external_string("roundTowardZero"); break; } } else if (mk_c(c)->fpautil().is_numeral(to_expr(a), tmp)) { return mk_c(c)->mk_external_string(fu.fm().to_string(tmp)); } else { SET_ERROR_CODE(Z3_INVALID_ARG); return ""; } } Z3_CATCH_RETURN(""); } Z3_string Z3_API Z3_get_numeral_decimal_string(Z3_context c, Z3_ast a, unsigned precision) { Z3_TRY; LOG_Z3_get_numeral_decimal_string(c, a, precision); RESET_ERROR_CODE(); expr* e = to_expr(a); if (!e) { SET_ERROR_CODE(Z3_INVALID_ARG); return ""; } rational r; arith_util & u = mk_c(c)->autil(); if (u.is_numeral(e, r) && !r.is_int()) { std::ostringstream buffer; r.display_decimal(buffer, precision); return mk_c(c)->mk_external_string(buffer.str()); } if (u.is_irrational_algebraic_numeral(e)) { algebraic_numbers::anum const & n = u.to_irrational_algebraic_numeral(e); algebraic_numbers::manager & am = u.am(); std::ostringstream buffer; am.display_decimal(buffer, n, precision); return mk_c(c)->mk_external_string(buffer.str()); } Z3_bool ok = Z3_get_numeral_rational(c, a, r); if (ok == Z3_TRUE) { return mk_c(c)->mk_external_string(r.to_string()); } else { SET_ERROR_CODE(Z3_INVALID_ARG); return ""; } Z3_CATCH_RETURN(""); } Z3_bool Z3_API Z3_get_numeral_small(Z3_context c, Z3_ast a, long long* num, long long* den) { Z3_TRY; // This function invokes Z3_get_numeral_rational, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_small(c, a, num, den); RESET_ERROR_CODE(); rational r; Z3_bool ok = Z3_get_numeral_rational(c, a, r); if (ok == Z3_TRUE) { rational n = numerator(r); rational d = denominator(r); if (n.is_int64() && d.is_int64()) { *num = n.get_int64(); *den = d.get_int64(); return Z3_TRUE; } else { return Z3_FALSE; } } SET_ERROR_CODE(Z3_INVALID_ARG); return Z3_FALSE; Z3_CATCH_RETURN(Z3_FALSE); } Z3_bool Z3_API Z3_get_numeral_int(Z3_context c, Z3_ast v, int* i) { Z3_TRY; // This function invokes Z3_get_numeral_int64, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_int(c, v, i); RESET_ERROR_CODE(); if (!i) { SET_ERROR_CODE(Z3_INVALID_ARG); return Z3_FALSE; } long long l; if (Z3_get_numeral_int64(c, v, &l) && l >= INT_MIN && l <= INT_MAX) { *i = static_cast(l); return Z3_TRUE; } return Z3_FALSE; Z3_CATCH_RETURN(Z3_FALSE); } Z3_bool Z3_API Z3_get_numeral_uint(Z3_context c, Z3_ast v, unsigned* u) { Z3_TRY; // This function invokes Z3_get_numeral_uint64, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_uint(c, v, u); RESET_ERROR_CODE(); if (!u) { SET_ERROR_CODE(Z3_INVALID_ARG); return Z3_FALSE; } unsigned long long l; if (Z3_get_numeral_uint64(c, v, &l) && (l <= 0xFFFFFFFF)) { *u = static_cast(l); return Z3_TRUE; } return Z3_FALSE; Z3_CATCH_RETURN(Z3_FALSE); } Z3_bool Z3_API Z3_get_numeral_uint64(Z3_context c, Z3_ast v, unsigned long long* u) { Z3_TRY; // This function invokes Z3_get_numeral_rational, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_uint64(c, v, u); RESET_ERROR_CODE(); if (!u) { SET_ERROR_CODE(Z3_INVALID_ARG); return Z3_FALSE; } rational r; Z3_bool ok = Z3_get_numeral_rational(c, v, r); SASSERT(u); if (ok == Z3_TRUE && r.is_uint64()) { *u = r.get_uint64(); return ok; } return Z3_FALSE; Z3_CATCH_RETURN(Z3_FALSE); } Z3_bool Z3_API Z3_get_numeral_int64(Z3_context c, Z3_ast v, long long* i) { Z3_TRY; // This function invokes Z3_get_numeral_rational, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_int64(c, v, i); RESET_ERROR_CODE(); if (!i) { SET_ERROR_CODE(Z3_INVALID_ARG); return Z3_FALSE; } rational r; Z3_bool ok = Z3_get_numeral_rational(c, v, r); if (ok == Z3_TRUE && r.is_int64()) { *i = r.get_int64(); return ok; } return Z3_FALSE; Z3_CATCH_RETURN(Z3_FALSE); } Z3_bool Z3_API Z3_get_numeral_rational_int64(Z3_context c, Z3_ast v, long long* num, long long* den) { Z3_TRY; // This function invokes Z3_get_numeral_rational, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_rational_int64(c, v, num, den); RESET_ERROR_CODE(); if (!num || !den) { SET_ERROR_CODE(Z3_INVALID_ARG); return Z3_FALSE; } rational r; Z3_bool ok = Z3_get_numeral_rational(c, v, r); if (ok != Z3_TRUE) { return ok; } rational n = numerator(r); rational d = denominator(r); if (n.is_int64() && d.is_int64()) { *num = n.get_int64(); *den = d.get_int64(); return ok; } return Z3_FALSE; Z3_CATCH_RETURN(Z3_FALSE); } }; z3-4.4.0/src/api/c++/0000755000175000017500000000000012540347414013555 5ustar michaelmichaelz3-4.4.0/src/api/c++/z3++.h0000644000175000017500000025067312540347414014425 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Thin C++ layer on top of the Z3 C API. Main features: - Smart pointers for all Z3 objects. - Object-Oriented interface. - Operator overloading. - Exceptions for signining Z3 errors The C API can be used simultaneously with the C++ layer. However, if you use the C API directly, you will have to check the error conditions manually. Of course, you can invoke the method check_error() of the context object. Author: Leonardo (leonardo) 2012-03-28 Notes: --*/ #ifndef __Z3PP_H_ #define __Z3PP_H_ #include #include #include #include #include #include /** \defgroup cppapi C++ API */ /*@{*/ /** @name C++ API classes and functions */ /*@{*/ /** \brief Z3 C++ namespace */ namespace z3 { class exception; class config; class context; class symbol; class params; class ast; class sort; class func_decl; class expr; class solver; class goal; class tactic; class probe; class model; class func_interp; class func_entry; class statistics; class apply_result; class fixedpoint; template class ast_vector_tpl; typedef ast_vector_tpl ast_vector; typedef ast_vector_tpl expr_vector; typedef ast_vector_tpl sort_vector; typedef ast_vector_tpl func_decl_vector; inline void set_param(char const * param, char const * value) { Z3_global_param_set(param, value); } inline void set_param(char const * param, bool value) { Z3_global_param_set(param, value ? "true" : "false"); } inline void set_param(char const * param, int value) { std::ostringstream oss; oss << value; Z3_global_param_set(param, oss.str().c_str()); } inline void reset_params() { Z3_global_param_reset_all(); } /** \brief Exception used to sign API usage errors. */ class exception { std::string m_msg; public: exception(char const * msg):m_msg(msg) {} char const * msg() const { return m_msg.c_str(); } friend std::ostream & operator<<(std::ostream & out, exception const & e) { out << e.msg(); return out; } }; /** \brief Z3 global configuration object. */ class config { Z3_config m_cfg; config(config const & s); config & operator=(config const & s); public: config() { m_cfg = Z3_mk_config(); } ~config() { Z3_del_config(m_cfg); } operator Z3_config() const { return m_cfg; } /** \brief Set global parameter \c param with string \c value. */ void set(char const * param, char const * value) { Z3_set_param_value(m_cfg, param, value); } /** \brief Set global parameter \c param with Boolean \c value. */ void set(char const * param, bool value) { Z3_set_param_value(m_cfg, param, value ? "true" : "false"); } /** \brief Set global parameter \c param with integer \c value. */ void set(char const * param, int value) { std::ostringstream oss; oss << value; Z3_set_param_value(m_cfg, param, oss.str().c_str()); } }; /** \brief A Context manages all other Z3 objects, global configuration options, etc. */ class context { Z3_context m_ctx; static void error_handler(Z3_context c, Z3_error_code e) { /* do nothing */ } void init(config & c) { m_ctx = Z3_mk_context_rc(c); Z3_set_error_handler(m_ctx, error_handler); Z3_set_ast_print_mode(m_ctx, Z3_PRINT_SMTLIB2_COMPLIANT); } context(context const & s); context & operator=(context const & s); public: context() { config c; init(c); } context(config & c) { init(c); } ~context() { Z3_del_context(m_ctx); } operator Z3_context() const { return m_ctx; } /** \brief Auxiliary method used to check for API usage errors. */ void check_error() const { Z3_error_code e = Z3_get_error_code(m_ctx); if (e != Z3_OK) throw exception(Z3_get_error_msg_ex(m_ctx, e)); } /** \brief Update global parameter \c param with string \c value. */ void set(char const * param, char const * value) { Z3_update_param_value(m_ctx, param, value); } /** \brief Update global parameter \c param with Boolean \c value. */ void set(char const * param, bool value) { Z3_update_param_value(m_ctx, param, value ? "true" : "false"); } /** \brief Update global parameter \c param with Integer \c value. */ void set(char const * param, int value) { std::ostringstream oss; oss << value; Z3_update_param_value(m_ctx, param, oss.str().c_str()); } /** \brief Interrupt the current procedure being executed by any object managed by this context. This is a soft interruption: there is no guarantee the object will actualy stop. */ void interrupt() { Z3_interrupt(m_ctx); } /** \brief Create a Z3 symbol based on the given string. */ symbol str_symbol(char const * s); /** \brief Create a Z3 symbol based on the given integer. */ symbol int_symbol(int n); /** \brief Return the Boolean sort. */ sort bool_sort(); /** \brief Return the integer sort. */ sort int_sort(); /** \brief Return the Real sort. */ sort real_sort(); /** \brief Return the Bit-vector sort of size \c sz. That is, the sort for bit-vectors of size \c sz. */ sort bv_sort(unsigned sz); /** \brief Return an array sort for arrays from \c d to \c r. Example: Given a context \c c, c.array_sort(c.int_sort(), c.bool_sort()) is an array sort from integer to Boolean. */ sort array_sort(sort d, sort r); /** \brief Return an enumeration sort: enum_names[0], ..., enum_names[n-1]. \c cs and \c ts are output parameters. The method stores in \c cs the constants corresponding to the enumerated elements, and in \c ts the predicates for testing if terms of the enumeration sort correspond to an enumeration. */ sort enumeration_sort(char const * name, unsigned n, char const * const * enum_names, func_decl_vector & cs, func_decl_vector & ts); /** \brief create an uninterpreted sort with the name given by the string or symbol. */ sort uninterpreted_sort(char const* name); sort uninterpreted_sort(symbol const& name); func_decl function(symbol const & name, unsigned arity, sort const * domain, sort const & range); func_decl function(char const * name, unsigned arity, sort const * domain, sort const & range); func_decl function(symbol const& name, sort_vector const& domain, sort const& range); func_decl function(char const * name, sort_vector const& domain, sort const& range); func_decl function(char const * name, sort const & domain, sort const & range); func_decl function(char const * name, sort const & d1, sort const & d2, sort const & range); func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range); func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range); func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range); expr constant(symbol const & name, sort const & s); expr constant(char const * name, sort const & s); expr bool_const(char const * name); expr int_const(char const * name); expr real_const(char const * name); expr bv_const(char const * name, unsigned sz); expr bool_val(bool b); expr int_val(int n); expr int_val(unsigned n); expr int_val(__int64 n); expr int_val(__uint64 n); expr int_val(char const * n); expr real_val(int n, int d); expr real_val(int n); expr real_val(unsigned n); expr real_val(__int64 n); expr real_val(__uint64 n); expr real_val(char const * n); expr bv_val(int n, unsigned sz); expr bv_val(unsigned n, unsigned sz); expr bv_val(__int64 n, unsigned sz); expr bv_val(__uint64 n, unsigned sz); expr bv_val(char const * n, unsigned sz); expr num_val(int n, sort const & s); }; template class array { T * m_array; unsigned m_size; array(array const & s); array & operator=(array const & s); public: array(unsigned sz):m_size(sz) { m_array = new T[sz]; } template array(ast_vector_tpl const & v); ~array() { delete[] m_array; } unsigned size() const { return m_size; } T & operator[](int i) { assert(0 <= i); assert(static_cast(i) < m_size); return m_array[i]; } T const & operator[](int i) const { assert(0 <= i); assert(static_cast(i) < m_size); return m_array[i]; } T const * ptr() const { return m_array; } T * ptr() { return m_array; } }; class object { protected: context * m_ctx; public: object(context & c):m_ctx(&c) {} object(object const & s):m_ctx(s.m_ctx) {} context & ctx() const { return *m_ctx; } void check_error() const { m_ctx->check_error(); } friend void check_context(object const & a, object const & b); }; inline void check_context(object const & a, object const & b) { assert(a.m_ctx == b.m_ctx); } class symbol : public object { Z3_symbol m_sym; public: symbol(context & c, Z3_symbol s):object(c), m_sym(s) {} symbol(symbol const & s):object(s), m_sym(s.m_sym) {} symbol & operator=(symbol const & s) { m_ctx = s.m_ctx; m_sym = s.m_sym; return *this; } operator Z3_symbol() const { return m_sym; } Z3_symbol_kind kind() const { return Z3_get_symbol_kind(ctx(), m_sym); } std::string str() const { assert(kind() == Z3_STRING_SYMBOL); return Z3_get_symbol_string(ctx(), m_sym); } int to_int() const { assert(kind() == Z3_INT_SYMBOL); return Z3_get_symbol_int(ctx(), m_sym); } friend std::ostream & operator<<(std::ostream & out, symbol const & s) { if (s.kind() == Z3_INT_SYMBOL) out << "k!" << s.to_int(); else out << s.str().c_str(); return out; } }; class params : public object { Z3_params m_params; public: params(context & c):object(c) { m_params = Z3_mk_params(c); Z3_params_inc_ref(ctx(), m_params); } params(params const & s):object(s), m_params(s.m_params) { Z3_params_inc_ref(ctx(), m_params); } ~params() { Z3_params_dec_ref(ctx(), m_params); } operator Z3_params() const { return m_params; } params & operator=(params const & s) { Z3_params_inc_ref(s.ctx(), s.m_params); Z3_params_dec_ref(ctx(), m_params); m_ctx = s.m_ctx; m_params = s.m_params; return *this; } void set(char const * k, bool b) { Z3_params_set_bool(ctx(), m_params, ctx().str_symbol(k), b); } void set(char const * k, unsigned n) { Z3_params_set_uint(ctx(), m_params, ctx().str_symbol(k), n); } void set(char const * k, double n) { Z3_params_set_double(ctx(), m_params, ctx().str_symbol(k), n); } void set(char const * k, symbol const & s) { Z3_params_set_symbol(ctx(), m_params, ctx().str_symbol(k), s); } friend std::ostream & operator<<(std::ostream & out, params const & p) { out << Z3_params_to_string(p.ctx(), p); return out; } }; class ast : public object { protected: Z3_ast m_ast; public: ast(context & c):object(c), m_ast(0) {} ast(context & c, Z3_ast n):object(c), m_ast(n) { Z3_inc_ref(ctx(), m_ast); } ast(ast const & s):object(s), m_ast(s.m_ast) { Z3_inc_ref(ctx(), m_ast); } ~ast() { if (m_ast) Z3_dec_ref(*m_ctx, m_ast); } operator Z3_ast() const { return m_ast; } operator bool() const { return m_ast != 0; } ast & operator=(ast const & s) { Z3_inc_ref(s.ctx(), s.m_ast); if (m_ast) Z3_dec_ref(ctx(), m_ast); m_ctx = s.m_ctx; m_ast = s.m_ast; return *this; } Z3_ast_kind kind() const { Z3_ast_kind r = Z3_get_ast_kind(ctx(), m_ast); check_error(); return r; } unsigned hash() const { unsigned r = Z3_get_ast_hash(ctx(), m_ast); check_error(); return r; } friend std::ostream & operator<<(std::ostream & out, ast const & n) { out << Z3_ast_to_string(n.ctx(), n.m_ast); return out; } /** \brief Return true if the ASTs are structurally identical. */ friend bool eq(ast const & a, ast const & b); }; inline bool eq(ast const & a, ast const & b) { return Z3_is_eq_ast(a.ctx(), a, b) != 0; } /** \brief A Z3 sort (aka type). Every expression (i.e., formula or term) in Z3 has a sort. */ class sort : public ast { public: sort(context & c):ast(c) {} sort(context & c, Z3_sort s):ast(c, reinterpret_cast(s)) {} sort(sort const & s):ast(s) {} operator Z3_sort() const { return reinterpret_cast(m_ast); } /** \brief Return true if this sort and \c s are equal. */ sort & operator=(sort const & s) { return static_cast(ast::operator=(s)); } /** \brief Return the internal sort kind. */ Z3_sort_kind sort_kind() const { return Z3_get_sort_kind(*m_ctx, *this); } /** \brief Return true if this sort is the Boolean sort. */ bool is_bool() const { return sort_kind() == Z3_BOOL_SORT; } /** \brief Return true if this sort is the Integer sort. */ bool is_int() const { return sort_kind() == Z3_INT_SORT; } /** \brief Return true if this sort is the Real sort. */ bool is_real() const { return sort_kind() == Z3_REAL_SORT; } /** \brief Return true if this sort is the Integer or Real sort. */ bool is_arith() const { return is_int() || is_real(); } /** \brief Return true if this sort is a Bit-vector sort. */ bool is_bv() const { return sort_kind() == Z3_BV_SORT; } /** \brief Return true if this sort is a Array sort. */ bool is_array() const { return sort_kind() == Z3_ARRAY_SORT; } /** \brief Return true if this sort is a Datatype sort. */ bool is_datatype() const { return sort_kind() == Z3_DATATYPE_SORT; } /** \brief Return true if this sort is a Relation sort. */ bool is_relation() const { return sort_kind() == Z3_RELATION_SORT; } /** \brief Return true if this sort is a Finite domain sort. */ bool is_finite_domain() const { return sort_kind() == Z3_FINITE_DOMAIN_SORT; } /** \brief Return the size of this Bit-vector sort. \pre is_bv() */ unsigned bv_size() const { assert(is_bv()); unsigned r = Z3_get_bv_sort_size(ctx(), *this); check_error(); return r; } /** \brief Return the domain of this Array sort. \pre is_array() */ sort array_domain() const { assert(is_array()); Z3_sort s = Z3_get_array_sort_domain(ctx(), *this); check_error(); return sort(ctx(), s); } /** \brief Return the range of this Array sort. \pre is_array() */ sort array_range() const { assert(is_array()); Z3_sort s = Z3_get_array_sort_range(ctx(), *this); check_error(); return sort(ctx(), s); } }; /** \brief Function declaration (aka function definition). It is the signature of interpreted and uninterpreted functions in Z3. The basic building block in Z3 is the function application. */ class func_decl : public ast { public: func_decl(context & c):ast(c) {} func_decl(context & c, Z3_func_decl n):ast(c, reinterpret_cast(n)) {} func_decl(func_decl const & s):ast(s) {} operator Z3_func_decl() const { return reinterpret_cast(m_ast); } func_decl & operator=(func_decl const & s) { return static_cast(ast::operator=(s)); } unsigned arity() const { return Z3_get_arity(ctx(), *this); } sort domain(unsigned i) const { assert(i < arity()); Z3_sort r = Z3_get_domain(ctx(), *this, i); check_error(); return sort(ctx(), r); } sort range() const { Z3_sort r = Z3_get_range(ctx(), *this); check_error(); return sort(ctx(), r); } symbol name() const { Z3_symbol s = Z3_get_decl_name(ctx(), *this); check_error(); return symbol(ctx(), s); } Z3_decl_kind decl_kind() const { return Z3_get_decl_kind(ctx(), *this); } bool is_const() const { return arity() == 0; } expr operator()() const; expr operator()(unsigned n, expr const * args) const; expr operator()(expr_vector const& v) const; expr operator()(expr const & a) const; expr operator()(int a) const; expr operator()(expr const & a1, expr const & a2) const; expr operator()(expr const & a1, int a2) const; expr operator()(int a1, expr const & a2) const; expr operator()(expr const & a1, expr const & a2, expr const & a3) const; expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const; expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const; }; /** \brief A Z3 expression is used to represent formulas and terms. For Z3, a formula is any expression of sort Boolean. Every expression has a sort. */ class expr : public ast { public: expr(context & c):ast(c) {} expr(context & c, Z3_ast n):ast(c, reinterpret_cast(n)) {} expr(expr const & n):ast(n) {} expr & operator=(expr const & n) { return static_cast(ast::operator=(n)); } /** \brief Return the sort of this expression. */ sort get_sort() const { Z3_sort s = Z3_get_sort(*m_ctx, m_ast); check_error(); return sort(*m_ctx, s); } /** \brief Return true if this is a Boolean expression. */ bool is_bool() const { return get_sort().is_bool(); } /** \brief Return true if this is an integer expression. */ bool is_int() const { return get_sort().is_int(); } /** \brief Return true if this is a real expression. */ bool is_real() const { return get_sort().is_real(); } /** \brief Return true if this is an integer or real expression. */ bool is_arith() const { return get_sort().is_arith(); } /** \brief Return true if this is a Bit-vector expression. */ bool is_bv() const { return get_sort().is_bv(); } /** \brief Return true if this is a Array expression. */ bool is_array() const { return get_sort().is_array(); } /** \brief Return true if this is a Datatype expression. */ bool is_datatype() const { return get_sort().is_datatype(); } /** \brief Return true if this is a Relation expression. */ bool is_relation() const { return get_sort().is_relation(); } /** \brief Return true if this is a Finite-domain expression. \remark Finite-domain is special kind of interpreted sort: is_bool(), is_bv() and is_finite_domain() are mutually exclusive. */ bool is_finite_domain() const { return get_sort().is_finite_domain(); } /** \brief Return true if this expression is a numeral. */ bool is_numeral() const { return kind() == Z3_NUMERAL_AST; } /** \brief Return true if this expression is an application. */ bool is_app() const { return kind() == Z3_APP_AST || kind() == Z3_NUMERAL_AST; } /** \brief Return true if this expression is a constant (i.e., an application with 0 arguments). */ bool is_const() const { return is_app() && num_args() == 0; } /** \brief Return true if this expression is a quantifier. */ bool is_quantifier() const { return kind() == Z3_QUANTIFIER_AST; } /** \brief Return true if this expression is a variable. */ bool is_var() const { return kind() == Z3_VAR_AST; } /** \brief Return true if this expression is well sorted (aka type correct). */ bool is_well_sorted() const { bool r = Z3_is_well_sorted(ctx(), m_ast) != 0; check_error(); return r; } operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } /** \brief Return the declaration associated with this application. This method assumes the expression is an application. \pre is_app() */ func_decl decl() const { Z3_func_decl f = Z3_get_app_decl(ctx(), *this); check_error(); return func_decl(ctx(), f); } /** \brief Return the number of arguments in this application. This method assumes the expression is an application. \pre is_app() */ unsigned num_args() const { unsigned r = Z3_get_app_num_args(ctx(), *this); check_error(); return r; } /** \brief Return the i-th argument of this application. This method assumes the expression is an application. \pre is_app() \pre i < num_args() */ expr arg(unsigned i) const { Z3_ast r = Z3_get_app_arg(ctx(), *this, i); check_error(); return expr(ctx(), r); } /** \brief Return the 'body' of this quantifier. \pre is_quantifier() */ expr body() const { assert(is_quantifier()); Z3_ast r = Z3_get_quantifier_body(ctx(), *this); check_error(); return expr(ctx(), r); } /** \brief Return an expression representing not(a). \pre a.is_bool() */ friend expr operator!(expr const & a) { assert(a.is_bool()); Z3_ast r = Z3_mk_not(a.ctx(), a); a.check_error(); return expr(a.ctx(), r); } /** \brief Return an expression representing a and b. \pre a.is_bool() \pre b.is_bool() */ friend expr operator&&(expr const & a, expr const & b) { check_context(a, b); assert(a.is_bool() && b.is_bool()); Z3_ast args[2] = { a, b }; Z3_ast r = Z3_mk_and(a.ctx(), 2, args); a.check_error(); return expr(a.ctx(), r); } /** \brief Return an expression representing a and b. The C++ Boolean value \c b is automatically converted into a Z3 Boolean constant. \pre a.is_bool() */ friend expr operator&&(expr const & a, bool b) { return a && a.ctx().bool_val(b); } /** \brief Return an expression representing a and b. The C++ Boolean value \c a is automatically converted into a Z3 Boolean constant. \pre b.is_bool() */ friend expr operator&&(bool a, expr const & b) { return b.ctx().bool_val(a) && b; } /** \brief Return an expression representing a or b. \pre a.is_bool() \pre b.is_bool() */ friend expr operator||(expr const & a, expr const & b) { check_context(a, b); assert(a.is_bool() && b.is_bool()); Z3_ast args[2] = { a, b }; Z3_ast r = Z3_mk_or(a.ctx(), 2, args); a.check_error(); return expr(a.ctx(), r); } /** \brief Return an expression representing a or b. The C++ Boolean value \c b is automatically converted into a Z3 Boolean constant. \pre a.is_bool() */ friend expr operator||(expr const & a, bool b) { return a || a.ctx().bool_val(b); } /** \brief Return an expression representing a or b. The C++ Boolean value \c a is automatically converted into a Z3 Boolean constant. \pre b.is_bool() */ friend expr operator||(bool a, expr const & b) { return b.ctx().bool_val(a) || b; } friend expr implies(expr const & a, expr const & b) { check_context(a, b); assert(a.is_bool() && b.is_bool()); Z3_ast r = Z3_mk_implies(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } friend expr implies(expr const & a, bool b); friend expr implies(bool a, expr const & b); friend expr ite(expr const & c, expr const & t, expr const & e); friend expr distinct(expr_vector const& args); friend expr concat(expr const& a, expr const& b); friend expr operator==(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = Z3_mk_eq(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } friend expr operator==(expr const & a, int b) { assert(a.is_arith() || a.is_bv()); return a == a.ctx().num_val(b, a.get_sort()); } friend expr operator==(int a, expr const & b) { assert(b.is_arith() || b.is_bv()); return b.ctx().num_val(a, b.get_sort()) == b; } friend expr operator!=(expr const & a, expr const & b) { check_context(a, b); Z3_ast args[2] = { a, b }; Z3_ast r = Z3_mk_distinct(a.ctx(), 2, args); a.check_error(); return expr(a.ctx(), r); } friend expr operator!=(expr const & a, int b) { assert(a.is_arith() || a.is_bv()); return a != a.ctx().num_val(b, a.get_sort()); } friend expr operator!=(int a, expr const & b) { assert(b.is_arith() || b.is_bv()); return b.ctx().num_val(a, b.get_sort()) != b; } friend expr operator+(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { Z3_ast args[2] = { a, b }; r = Z3_mk_add(a.ctx(), 2, args); } else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvadd(a.ctx(), a, b); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } friend expr operator+(expr const & a, int b) { return a + a.ctx().num_val(b, a.get_sort()); } friend expr operator+(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) + b; } friend expr operator*(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { Z3_ast args[2] = { a, b }; r = Z3_mk_mul(a.ctx(), 2, args); } else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvmul(a.ctx(), a, b); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } friend expr operator*(expr const & a, int b) { return a * a.ctx().num_val(b, a.get_sort()); } friend expr operator*(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) * b; } /** \brief Power operator */ friend expr pw(expr const & a, expr const & b); friend expr pw(expr const & a, int b); friend expr pw(int a, expr const & b); friend expr operator/(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { r = Z3_mk_div(a.ctx(), a, b); } else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvsdiv(a.ctx(), a, b); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } friend expr operator/(expr const & a, int b) { return a / a.ctx().num_val(b, a.get_sort()); } friend expr operator/(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) / b; } friend expr operator-(expr const & a) { Z3_ast r = 0; if (a.is_arith()) { r = Z3_mk_unary_minus(a.ctx(), a); } else if (a.is_bv()) { r = Z3_mk_bvneg(a.ctx(), a); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } friend expr operator-(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { Z3_ast args[2] = { a, b }; r = Z3_mk_sub(a.ctx(), 2, args); } else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvsub(a.ctx(), a, b); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } friend expr operator-(expr const & a, int b) { return a - a.ctx().num_val(b, a.get_sort()); } friend expr operator-(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) - b; } friend expr operator<=(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { r = Z3_mk_le(a.ctx(), a, b); } else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvsle(a.ctx(), a, b); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } friend expr operator<=(expr const & a, int b) { return a <= a.ctx().num_val(b, a.get_sort()); } friend expr operator<=(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) <= b; } friend expr operator>=(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { r = Z3_mk_ge(a.ctx(), a, b); } else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvsge(a.ctx(), a, b); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } friend expr operator>=(expr const & a, int b) { return a >= a.ctx().num_val(b, a.get_sort()); } friend expr operator>=(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) >= b; } friend expr operator<(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { r = Z3_mk_lt(a.ctx(), a, b); } else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvslt(a.ctx(), a, b); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } friend expr operator<(expr const & a, int b) { return a < a.ctx().num_val(b, a.get_sort()); } friend expr operator<(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) < b; } friend expr operator>(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { r = Z3_mk_gt(a.ctx(), a, b); } else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvsgt(a.ctx(), a, b); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } friend expr operator>(expr const & a, int b) { return a > a.ctx().num_val(b, a.get_sort()); } friend expr operator>(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) > b; } friend expr operator&(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = Z3_mk_bvand(a.ctx(), a, b); return expr(a.ctx(), r); } friend expr operator&(expr const & a, int b) { return a & a.ctx().num_val(b, a.get_sort()); } friend expr operator&(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) & b; } friend expr operator^(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = Z3_mk_bvxor(a.ctx(), a, b); return expr(a.ctx(), r); } friend expr operator^(expr const & a, int b) { return a ^ a.ctx().num_val(b, a.get_sort()); } friend expr operator^(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) ^ b; } friend expr operator|(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = Z3_mk_bvor(a.ctx(), a, b); return expr(a.ctx(), r); } friend expr operator|(expr const & a, int b) { return a | a.ctx().num_val(b, a.get_sort()); } friend expr operator|(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) | b; } friend expr operator~(expr const & a) { Z3_ast r = Z3_mk_bvnot(a.ctx(), a); return expr(a.ctx(), r); } expr extract(unsigned hi, unsigned lo) const { Z3_ast r = Z3_mk_extract(ctx(), hi, lo, *this); return expr(ctx(), r); } unsigned lo() const { assert (is_app() && Z3_get_decl_num_parameters(ctx(), decl()) == 2); return static_cast(Z3_get_decl_int_parameter(ctx(), decl(), 1)); } unsigned hi() const { assert (is_app() && Z3_get_decl_num_parameters(ctx(), decl()) == 2); return static_cast(Z3_get_decl_int_parameter(ctx(), decl(), 0)); } /** \brief Return a simplified version of this expression. */ expr simplify() const { Z3_ast r = Z3_simplify(ctx(), m_ast); check_error(); return expr(ctx(), r); } /** \brief Return a simplified version of this expression. The parameter \c p is a set of parameters for the Z3 simplifier. */ expr simplify(params const & p) const { Z3_ast r = Z3_simplify_ex(ctx(), m_ast, p); check_error(); return expr(ctx(), r); } /** \brief Apply substitution. Replace src expressions by dst. */ expr substitute(expr_vector const& src, expr_vector const& dst); /** \brief Apply substitution. Replace bound variables by expressions. */ expr substitute(expr_vector const& dst); }; inline expr implies(expr const & a, bool b) { return implies(a, a.ctx().bool_val(b)); } inline expr implies(bool a, expr const & b) { return implies(b.ctx().bool_val(a), b); } inline expr pw(expr const & a, expr const & b) { assert(a.is_arith() && b.is_arith()); check_context(a, b); Z3_ast r = Z3_mk_power(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } inline expr pw(expr const & a, int b) { return pw(a, a.ctx().num_val(b, a.get_sort())); } inline expr pw(int a, expr const & b) { return pw(b.ctx().num_val(a, b.get_sort()), b); } /** \brief Create the if-then-else expression ite(c, t, e) \pre c.is_bool() */ inline 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); } /** \brief Wraps a Z3_ast as an expr object. It also checks for errors. This function allows the user to use the whole C API with the C++ layer defined in this file. */ inline expr to_expr(context & c, Z3_ast a) { c.check_error(); assert(Z3_get_ast_kind(c, a) == Z3_APP_AST || Z3_get_ast_kind(c, a) == Z3_NUMERAL_AST || Z3_get_ast_kind(c, a) == Z3_VAR_AST || Z3_get_ast_kind(c, a) == Z3_QUANTIFIER_AST); return expr(c, a); } inline sort to_sort(context & c, Z3_sort s) { c.check_error(); return sort(c, s); } inline func_decl to_func_decl(context & c, Z3_func_decl f) { c.check_error(); return func_decl(c, f); } /** \brief unsigned less than or equal to operator for bitvectors. */ inline expr ule(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvule(a.ctx(), a, b)); } inline expr ule(expr const & a, int b) { return ule(a, a.ctx().num_val(b, a.get_sort())); } inline expr ule(int a, expr const & b) { return ule(b.ctx().num_val(a, b.get_sort()), b); } /** \brief unsigned less than operator for bitvectors. */ inline expr ult(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvult(a.ctx(), a, b)); } inline expr ult(expr const & a, int b) { return ult(a, a.ctx().num_val(b, a.get_sort())); } inline expr ult(int a, expr const & b) { return ult(b.ctx().num_val(a, b.get_sort()), b); } /** \brief unsigned greater than or equal to operator for bitvectors. */ inline expr uge(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvuge(a.ctx(), a, b)); } inline expr uge(expr const & a, int b) { return uge(a, a.ctx().num_val(b, a.get_sort())); } inline expr uge(int a, expr const & b) { return uge(b.ctx().num_val(a, b.get_sort()), b); } /** \brief unsigned greater than operator for bitvectors. */ inline expr ugt(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvugt(a.ctx(), a, b)); } inline expr ugt(expr const & a, int b) { return ugt(a, a.ctx().num_val(b, a.get_sort())); } inline expr ugt(int a, expr const & b) { return ugt(b.ctx().num_val(a, b.get_sort()), b); } /** \brief unsigned division operator for bitvectors. */ inline expr udiv(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvudiv(a.ctx(), a, b)); } inline expr udiv(expr const & a, int b) { return udiv(a, a.ctx().num_val(b, a.get_sort())); } inline expr udiv(int a, expr const & b) { return udiv(b.ctx().num_val(a, b.get_sort()), b); } template class cast_ast; template<> class cast_ast { public: ast operator()(context & c, Z3_ast a) { return ast(c, a); } }; template<> class cast_ast { public: expr operator()(context & c, Z3_ast a) { assert(Z3_get_ast_kind(c, a) == Z3_NUMERAL_AST || Z3_get_ast_kind(c, a) == Z3_APP_AST || Z3_get_ast_kind(c, a) == Z3_QUANTIFIER_AST || Z3_get_ast_kind(c, a) == Z3_VAR_AST); return expr(c, a); } }; template<> class cast_ast { public: sort operator()(context & c, Z3_ast a) { assert(Z3_get_ast_kind(c, a) == Z3_SORT_AST); return sort(c, reinterpret_cast(a)); } }; template<> class cast_ast { public: func_decl operator()(context & c, Z3_ast a) { assert(Z3_get_ast_kind(c, a) == Z3_FUNC_DECL_AST); return func_decl(c, reinterpret_cast(a)); } }; template class ast_vector_tpl : public object { Z3_ast_vector m_vector; void init(Z3_ast_vector v) { Z3_ast_vector_inc_ref(ctx(), v); m_vector = v; } public: ast_vector_tpl(context & c):object(c) { init(Z3_mk_ast_vector(c)); } ast_vector_tpl(context & c, Z3_ast_vector v):object(c) { init(v); } ast_vector_tpl(ast_vector_tpl const & s):object(s), m_vector(s.m_vector) { Z3_ast_vector_inc_ref(ctx(), m_vector); } ~ast_vector_tpl() { Z3_ast_vector_dec_ref(ctx(), m_vector); } operator Z3_ast_vector() const { return m_vector; } unsigned size() const { return Z3_ast_vector_size(ctx(), m_vector); } T operator[](int i) const { assert(0 <= i); Z3_ast r = Z3_ast_vector_get(ctx(), m_vector, i); check_error(); return cast_ast()(ctx(), r); } void push_back(T const & e) { Z3_ast_vector_push(ctx(), m_vector, e); check_error(); } void resize(unsigned sz) { Z3_ast_vector_resize(ctx(), m_vector, sz); check_error(); } T back() const { return operator[](size() - 1); } void pop_back() { assert(size() > 0); resize(size() - 1); } bool empty() const { return size() == 0; } ast_vector_tpl & operator=(ast_vector_tpl const & s) { Z3_ast_vector_inc_ref(s.ctx(), s.m_vector); Z3_ast_vector_dec_ref(ctx(), m_vector); m_ctx = s.m_ctx; m_vector = s.m_vector; return *this; } friend std::ostream & operator<<(std::ostream & out, ast_vector_tpl const & v) { out << Z3_ast_vector_to_string(v.ctx(), v); return out; } }; template template array::array(ast_vector_tpl const & v) { m_array = new T[v.size()]; m_size = v.size(); for (unsigned i = 0; i < m_size; i++) { m_array[i] = v[i]; } } // Basic functions for creating quantified formulas. // The C API should be used for creating quantifiers with patterns, weights, many variables, etc. inline expr forall(expr const & x, expr const & b) { check_context(x, b); Z3_app vars[] = {(Z3_app) x}; Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 1, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr forall(expr const & x1, expr const & x2, expr const & b) { check_context(x1, b); check_context(x2, b); Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 2, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr forall(expr const & x1, expr const & x2, expr const & x3, expr const & b) { check_context(x1, b); check_context(x2, b); check_context(x3, b); Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 3, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr forall(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 4, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr forall(expr_vector const & xs, expr const & b) { array vars(xs); Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, vars.size(), vars.ptr(), 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr exists(expr const & x, expr const & b) { check_context(x, b); Z3_app vars[] = {(Z3_app) x}; Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 1, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr exists(expr const & x1, expr const & x2, expr const & b) { check_context(x1, b); check_context(x2, b); Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 2, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr exists(expr const & x1, expr const & x2, expr const & x3, expr const & b) { check_context(x1, b); check_context(x2, b); check_context(x3, b); Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 3, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr exists(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 4, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr exists(expr_vector const & xs, expr const & b) { array vars(xs); Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, vars.size(), vars.ptr(), 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr distinct(expr_vector const& args) { assert(args.size() > 0); context& ctx = args[0].ctx(); array _args(args); Z3_ast r = Z3_mk_distinct(ctx, _args.size(), _args.ptr()); ctx.check_error(); return expr(ctx, r); } inline expr concat(expr const& a, expr const& b) { check_context(a, b); Z3_ast r = Z3_mk_concat(a.ctx(), a, b); a.ctx().check_error(); return expr(a.ctx(), r); } class func_entry : public object { Z3_func_entry m_entry; void init(Z3_func_entry e) { m_entry = e; Z3_func_entry_inc_ref(ctx(), m_entry); } public: func_entry(context & c, Z3_func_entry e):object(c) { init(e); } func_entry(func_entry const & s):object(s) { init(s.m_entry); } ~func_entry() { Z3_func_entry_dec_ref(ctx(), m_entry); } operator Z3_func_entry() const { return m_entry; } func_entry & operator=(func_entry const & s) { Z3_func_entry_inc_ref(s.ctx(), s.m_entry); Z3_func_entry_dec_ref(ctx(), m_entry); m_ctx = s.m_ctx; m_entry = s.m_entry; return *this; } expr value() const { Z3_ast r = Z3_func_entry_get_value(ctx(), m_entry); check_error(); return expr(ctx(), r); } unsigned num_args() const { unsigned r = Z3_func_entry_get_num_args(ctx(), m_entry); check_error(); return r; } expr arg(unsigned i) const { Z3_ast r = Z3_func_entry_get_arg(ctx(), m_entry, i); check_error(); return expr(ctx(), r); } }; class func_interp : public object { Z3_func_interp m_interp; void init(Z3_func_interp e) { m_interp = e; Z3_func_interp_inc_ref(ctx(), m_interp); } public: func_interp(context & c, Z3_func_interp e):object(c) { init(e); } func_interp(func_interp const & s):object(s) { init(s.m_interp); } ~func_interp() { Z3_func_interp_dec_ref(ctx(), m_interp); } operator Z3_func_interp() const { return m_interp; } func_interp & operator=(func_interp const & s) { Z3_func_interp_inc_ref(s.ctx(), s.m_interp); Z3_func_interp_dec_ref(ctx(), m_interp); m_ctx = s.m_ctx; m_interp = s.m_interp; return *this; } expr else_value() const { Z3_ast r = Z3_func_interp_get_else(ctx(), m_interp); check_error(); return expr(ctx(), r); } unsigned num_entries() const { unsigned r = Z3_func_interp_get_num_entries(ctx(), m_interp); check_error(); return r; } func_entry entry(unsigned i) const { Z3_func_entry e = Z3_func_interp_get_entry(ctx(), m_interp, i); check_error(); return func_entry(ctx(), e); } }; class model : public object { Z3_model m_model; void init(Z3_model m) { m_model = m; Z3_model_inc_ref(ctx(), m); } public: model(context & c, Z3_model m):object(c) { init(m); } model(model const & s):object(s) { init(s.m_model); } ~model() { Z3_model_dec_ref(ctx(), m_model); } operator Z3_model() const { return m_model; } model & operator=(model const & s) { Z3_model_inc_ref(s.ctx(), s.m_model); Z3_model_dec_ref(ctx(), m_model); m_ctx = s.m_ctx; m_model = s.m_model; return *this; } expr eval(expr const & n, bool model_completion=false) const { check_context(*this, n); Z3_ast r = 0; Z3_bool status = Z3_model_eval(ctx(), m_model, n, model_completion, &r); check_error(); if (status == Z3_FALSE) throw exception("failed to evaluate expression"); return expr(ctx(), r); } unsigned num_consts() const { return Z3_model_get_num_consts(ctx(), m_model); } unsigned num_funcs() const { return Z3_model_get_num_funcs(ctx(), m_model); } func_decl get_const_decl(unsigned i) const { Z3_func_decl r = Z3_model_get_const_decl(ctx(), m_model, i); check_error(); return func_decl(ctx(), r); } func_decl get_func_decl(unsigned i) const { Z3_func_decl r = Z3_model_get_func_decl(ctx(), m_model, i); check_error(); return func_decl(ctx(), r); } unsigned size() const { return num_consts() + num_funcs(); } func_decl operator[](int i) const { assert(0 <= i); return static_cast(i) < num_consts() ? get_const_decl(i) : get_func_decl(i - num_consts()); } expr get_const_interp(func_decl c) const { check_context(*this, c); Z3_ast r = Z3_model_get_const_interp(ctx(), m_model, c); check_error(); return expr(ctx(), r); } func_interp get_func_interp(func_decl f) const { check_context(*this, f); Z3_func_interp r = Z3_model_get_func_interp(ctx(), m_model, f); check_error(); return func_interp(ctx(), r); } friend std::ostream & operator<<(std::ostream & out, model const & m) { out << Z3_model_to_string(m.ctx(), m); return out; } }; class stats : public object { Z3_stats m_stats; void init(Z3_stats e) { m_stats = e; Z3_stats_inc_ref(ctx(), m_stats); } public: stats(context & c):object(c), m_stats(0) {} stats(context & c, Z3_stats e):object(c) { init(e); } stats(stats const & s):object(s) { init(s.m_stats); } ~stats() { if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); } operator Z3_stats() const { return m_stats; } stats & operator=(stats const & s) { Z3_stats_inc_ref(s.ctx(), s.m_stats); if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); m_ctx = s.m_ctx; m_stats = s.m_stats; return *this; } unsigned size() const { return Z3_stats_size(ctx(), m_stats); } std::string key(unsigned i) const { Z3_string s = Z3_stats_get_key(ctx(), m_stats, i); check_error(); return s; } bool is_uint(unsigned i) const { Z3_bool r = Z3_stats_is_uint(ctx(), m_stats, i); check_error(); return r != 0; } bool is_double(unsigned i) const { Z3_bool r = Z3_stats_is_double(ctx(), m_stats, i); check_error(); return r != 0; } unsigned uint_value(unsigned i) const { unsigned r = Z3_stats_get_uint_value(ctx(), m_stats, i); check_error(); return r; } double double_value(unsigned i) const { double r = Z3_stats_get_double_value(ctx(), m_stats, i); check_error(); return r; } friend std::ostream & operator<<(std::ostream & out, stats const & s) { out << Z3_stats_to_string(s.ctx(), s); return out; } }; enum check_result { unsat, sat, unknown }; inline std::ostream & operator<<(std::ostream & out, check_result r) { if (r == unsat) out << "unsat"; else if (r == sat) out << "sat"; else out << "unknown"; return out; } inline check_result to_check_result(Z3_lbool l) { if (l == Z3_L_TRUE) return sat; else if (l == Z3_L_FALSE) return unsat; return unknown; } class solver : public object { Z3_solver m_solver; void init(Z3_solver s) { m_solver = s; Z3_solver_inc_ref(ctx(), s); } public: solver(context & c):object(c) { init(Z3_mk_solver(c)); } solver(context & c, Z3_solver s):object(c) { init(s); } solver(context & c, char const * logic):object(c) { init(Z3_mk_solver_for_logic(c, c.str_symbol(logic))); } solver(solver const & s):object(s) { init(s.m_solver); } ~solver() { Z3_solver_dec_ref(ctx(), m_solver); } operator Z3_solver() const { return m_solver; } solver & operator=(solver const & s) { Z3_solver_inc_ref(s.ctx(), s.m_solver); Z3_solver_dec_ref(ctx(), m_solver); m_ctx = s.m_ctx; m_solver = s.m_solver; return *this; } void set(params const & p) { Z3_solver_set_params(ctx(), m_solver, p); check_error(); } void push() { Z3_solver_push(ctx(), m_solver); check_error(); } void pop(unsigned n = 1) { Z3_solver_pop(ctx(), m_solver, n); check_error(); } void reset() { Z3_solver_reset(ctx(), m_solver); check_error(); } void add(expr const & e) { assert(e.is_bool()); Z3_solver_assert(ctx(), m_solver, e); check_error(); } void add(expr const & e, expr const & p) { assert(e.is_bool()); assert(p.is_bool()); assert(p.is_const()); Z3_solver_assert_and_track(ctx(), m_solver, e, p); check_error(); } void add(expr const & e, char const * p) { add(e, ctx().bool_const(p)); } check_result check() { Z3_lbool r = Z3_solver_check(ctx(), m_solver); check_error(); return to_check_result(r); } check_result check(unsigned n, expr * const assumptions) { array _assumptions(n); for (unsigned i = 0; i < n; i++) { check_context(*this, assumptions[i]); _assumptions[i] = assumptions[i]; } Z3_lbool r = Z3_solver_check_assumptions(ctx(), m_solver, n, _assumptions.ptr()); check_error(); return to_check_result(r); } check_result check(expr_vector assumptions) { unsigned n = assumptions.size(); array _assumptions(n); for (unsigned i = 0; i < n; i++) { check_context(*this, assumptions[i]); _assumptions[i] = assumptions[i]; } Z3_lbool r = Z3_solver_check_assumptions(ctx(), m_solver, n, _assumptions.ptr()); check_error(); return to_check_result(r); } model get_model() const { Z3_model m = Z3_solver_get_model(ctx(), m_solver); check_error(); return model(ctx(), m); } std::string reason_unknown() const { Z3_string r = Z3_solver_get_reason_unknown(ctx(), m_solver); check_error(); return r; } stats statistics() const { Z3_stats r = Z3_solver_get_statistics(ctx(), m_solver); check_error(); return stats(ctx(), r); } expr_vector unsat_core() const { Z3_ast_vector r = Z3_solver_get_unsat_core(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } expr_vector assertions() const { Z3_ast_vector r = Z3_solver_get_assertions(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } expr proof() const { Z3_ast r = Z3_solver_get_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); } friend std::ostream & operator<<(std::ostream & out, solver const & s) { out << Z3_solver_to_string(s.ctx(), s); return out; } std::string to_smt2(char const* status = "unknown") { array es(assertions()); Z3_ast const* fmls = es.ptr(); Z3_ast fml = 0; unsigned sz = es.size(); if (sz > 0) { --sz; fml = fmls[sz]; } else { fml = ctx().bool_val(true); } return std::string(Z3_benchmark_to_smtlib_string( ctx(), "", "", status, "", sz, fmls, fml)); } }; class goal : public object { Z3_goal m_goal; void init(Z3_goal s) { m_goal = s; Z3_goal_inc_ref(ctx(), s); } public: goal(context & c, bool models=true, bool unsat_cores=false, bool proofs=false):object(c) { init(Z3_mk_goal(c, models, unsat_cores, proofs)); } goal(context & c, Z3_goal s):object(c) { init(s); } goal(goal const & s):object(s) { init(s.m_goal); } ~goal() { Z3_goal_dec_ref(ctx(), m_goal); } operator Z3_goal() const { return m_goal; } goal & operator=(goal const & s) { Z3_goal_inc_ref(s.ctx(), s.m_goal); Z3_goal_dec_ref(ctx(), m_goal); m_ctx = s.m_ctx; m_goal = s.m_goal; return *this; } void add(expr const & f) { check_context(*this, f); Z3_goal_assert(ctx(), m_goal, f); check_error(); } unsigned size() const { return Z3_goal_size(ctx(), m_goal); } expr operator[](int i) const { assert(0 <= i); Z3_ast r = Z3_goal_formula(ctx(), m_goal, i); check_error(); return expr(ctx(), r); } Z3_goal_prec precision() const { return Z3_goal_precision(ctx(), m_goal); } bool inconsistent() const { return Z3_goal_inconsistent(ctx(), m_goal) != 0; } unsigned depth() const { return Z3_goal_depth(ctx(), m_goal); } void reset() { Z3_goal_reset(ctx(), m_goal); } unsigned num_exprs() const { return Z3_goal_num_exprs(ctx(), m_goal); } bool is_decided_sat() const { return Z3_goal_is_decided_sat(ctx(), m_goal) != 0; } bool is_decided_unsat() const { return Z3_goal_is_decided_unsat(ctx(), m_goal) != 0; } expr as_expr() const { unsigned n = size(); if (n == 0) return ctx().bool_val(true); else if (n == 1) return operator[](0); else { array args(n); for (unsigned i = 0; i < n; i++) args[i] = operator[](i); return expr(ctx(), Z3_mk_and(ctx(), n, args.ptr())); } } friend std::ostream & operator<<(std::ostream & out, goal const & g) { out << Z3_goal_to_string(g.ctx(), g); return out; } }; class apply_result : public object { Z3_apply_result m_apply_result; void init(Z3_apply_result s) { m_apply_result = s; Z3_apply_result_inc_ref(ctx(), s); } public: apply_result(context & c, Z3_apply_result s):object(c) { init(s); } apply_result(apply_result const & s):object(s) { init(s.m_apply_result); } ~apply_result() { Z3_apply_result_dec_ref(ctx(), m_apply_result); } operator Z3_apply_result() const { return m_apply_result; } apply_result & operator=(apply_result const & s) { Z3_apply_result_inc_ref(s.ctx(), s.m_apply_result); Z3_apply_result_dec_ref(ctx(), m_apply_result); m_ctx = s.m_ctx; m_apply_result = s.m_apply_result; return *this; } unsigned size() const { return Z3_apply_result_get_num_subgoals(ctx(), m_apply_result); } goal operator[](int i) const { assert(0 <= i); Z3_goal r = Z3_apply_result_get_subgoal(ctx(), m_apply_result, i); check_error(); return goal(ctx(), r); } model convert_model(model const & m, unsigned i = 0) const { check_context(*this, m); Z3_model new_m = Z3_apply_result_convert_model(ctx(), m_apply_result, i, m); check_error(); return model(ctx(), new_m); } friend std::ostream & operator<<(std::ostream & out, apply_result const & r) { out << Z3_apply_result_to_string(r.ctx(), r); return out; } }; class tactic : public object { Z3_tactic m_tactic; void init(Z3_tactic s) { m_tactic = s; Z3_tactic_inc_ref(ctx(), s); } public: tactic(context & c, char const * name):object(c) { Z3_tactic r = Z3_mk_tactic(c, name); check_error(); init(r); } tactic(context & c, Z3_tactic s):object(c) { init(s); } tactic(tactic const & s):object(s) { init(s.m_tactic); } ~tactic() { Z3_tactic_dec_ref(ctx(), m_tactic); } operator Z3_tactic() const { return m_tactic; } tactic & operator=(tactic const & s) { Z3_tactic_inc_ref(s.ctx(), s.m_tactic); Z3_tactic_dec_ref(ctx(), m_tactic); m_ctx = s.m_ctx; m_tactic = s.m_tactic; return *this; } solver mk_solver() const { Z3_solver r = Z3_mk_solver_from_tactic(ctx(), m_tactic); check_error(); return solver(ctx(), r); } apply_result apply(goal const & g) const { check_context(*this, g); Z3_apply_result r = Z3_tactic_apply(ctx(), m_tactic, g); check_error(); return apply_result(ctx(), r); } apply_result operator()(goal const & g) const { return apply(g); } std::string help() const { char const * r = Z3_tactic_get_help(ctx(), m_tactic); check_error(); return r; } friend tactic operator&(tactic const & t1, tactic const & t2) { check_context(t1, t2); Z3_tactic r = Z3_tactic_and_then(t1.ctx(), t1, t2); t1.check_error(); return tactic(t1.ctx(), r); } friend tactic operator|(tactic const & t1, tactic const & t2) { check_context(t1, t2); Z3_tactic r = Z3_tactic_or_else(t1.ctx(), t1, t2); t1.check_error(); return tactic(t1.ctx(), r); } friend tactic repeat(tactic const & t, unsigned max); friend tactic with(tactic const & t, params const & p); friend tactic try_for(tactic const & t, unsigned ms); }; inline tactic repeat(tactic const & t, unsigned max=UINT_MAX) { Z3_tactic r = Z3_tactic_repeat(t.ctx(), t, max); t.check_error(); return tactic(t.ctx(), r); } inline tactic with(tactic const & t, params const & p) { Z3_tactic r = Z3_tactic_using_params(t.ctx(), t, p); t.check_error(); return tactic(t.ctx(), r); } inline tactic try_for(tactic const & t, unsigned ms) { Z3_tactic r = Z3_tactic_try_for(t.ctx(), t, ms); t.check_error(); return tactic(t.ctx(), r); } class probe : public object { Z3_probe m_probe; void init(Z3_probe s) { m_probe = s; Z3_probe_inc_ref(ctx(), s); } public: probe(context & c, char const * name):object(c) { Z3_probe r = Z3_mk_probe(c, name); check_error(); init(r); } probe(context & c, double val):object(c) { Z3_probe r = Z3_probe_const(c, val); check_error(); init(r); } probe(context & c, Z3_probe s):object(c) { init(s); } probe(probe const & s):object(s) { init(s.m_probe); } ~probe() { Z3_probe_dec_ref(ctx(), m_probe); } operator Z3_probe() const { return m_probe; } probe & operator=(probe const & s) { Z3_probe_inc_ref(s.ctx(), s.m_probe); Z3_probe_dec_ref(ctx(), m_probe); m_ctx = s.m_ctx; m_probe = s.m_probe; return *this; } double apply(goal const & g) const { double r = Z3_probe_apply(ctx(), m_probe, g); check_error(); return r; } double operator()(goal const & g) const { return apply(g); } friend probe operator<=(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_le(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } friend probe operator<=(probe const & p1, double p2) { return p1 <= probe(p1.ctx(), p2); } friend probe operator<=(double p1, probe const & p2) { return probe(p2.ctx(), p1) <= p2; } friend probe operator>=(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_ge(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } friend probe operator>=(probe const & p1, double p2) { return p1 >= probe(p1.ctx(), p2); } friend probe operator>=(double p1, probe const & p2) { return probe(p2.ctx(), p1) >= p2; } friend probe operator<(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_lt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } friend probe operator<(probe const & p1, double p2) { return p1 < probe(p1.ctx(), p2); } friend probe operator<(double p1, probe const & p2) { return probe(p2.ctx(), p1) < p2; } friend probe operator>(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_gt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } friend probe operator>(probe const & p1, double p2) { return p1 > probe(p1.ctx(), p2); } friend probe operator>(double p1, probe const & p2) { return probe(p2.ctx(), p1) > p2; } friend probe operator==(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_eq(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } friend probe operator==(probe const & p1, double p2) { return p1 == probe(p1.ctx(), p2); } friend probe operator==(double p1, probe const & p2) { return probe(p2.ctx(), p1) == p2; } friend probe operator&&(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_and(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } friend probe operator||(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_or(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } friend probe operator!(probe const & p) { Z3_probe r = Z3_probe_not(p.ctx(), p); p.check_error(); return probe(p.ctx(), r); } }; class optimize : public object { Z3_optimize m_opt; public: class handle { unsigned m_h; public: handle(unsigned h): m_h(h) {} unsigned h() const { return m_h; } }; optimize(context& c):object(c) { m_opt = Z3_mk_optimize(c); Z3_optimize_inc_ref(c, m_opt); } ~optimize() { Z3_optimize_dec_ref(ctx(), m_opt); } operator Z3_optimize() const { return m_opt; } void add(expr const& e) { assert(e.is_bool()); Z3_optimize_assert(ctx(), m_opt, e); } handle add(expr const& e, unsigned weight) { assert(e.is_bool()); std::stringstream strm; strm << weight; return handle(Z3_optimize_assert_soft(ctx(), m_opt, e, strm.str().c_str(), 0)); } handle add(expr const& e, char const* weight) { assert(e.is_bool()); return handle(Z3_optimize_assert_soft(ctx(), m_opt, e, weight, 0)); } handle maximize(expr const& e) { return handle(Z3_optimize_maximize(ctx(), m_opt, e)); } handle minimize(expr const& e) { return handle(Z3_optimize_minimize(ctx(), m_opt, e)); } void push() { Z3_optimize_push(ctx(), m_opt); } void pop() { Z3_optimize_pop(ctx(), m_opt); } check_result check() { Z3_lbool r = Z3_optimize_check(ctx(), m_opt); check_error(); return to_check_result(r); } model get_model() const { Z3_model m = Z3_optimize_get_model(ctx(), m_opt); check_error(); return model(ctx(), m); } void set(params const & p) { Z3_optimize_set_params(ctx(), m_opt, p); check_error(); } expr lower(handle const& h) { Z3_ast r = Z3_optimize_get_lower(ctx(), m_opt, h.h()); check_error(); return expr(ctx(), r); } expr upper(handle const& h) { Z3_ast r = Z3_optimize_get_upper(ctx(), m_opt, h.h()); check_error(); return expr(ctx(), r); } stats statistics() const { Z3_stats r = Z3_optimize_get_statistics(ctx(), m_opt); check_error(); return stats(ctx(), r); } friend std::ostream & operator<<(std::ostream & out, optimize const & s) { out << Z3_optimize_to_string(s.ctx(), s.m_opt); return out; } std::string help() const { char const * r = Z3_optimize_get_help(ctx(), m_opt); check_error(); return r; } }; inline tactic fail_if(probe const & p) { Z3_tactic r = Z3_tactic_fail_if(p.ctx(), p); p.check_error(); return tactic(p.ctx(), r); } inline tactic when(probe const & p, tactic const & t) { check_context(p, t); Z3_tactic r = Z3_tactic_when(t.ctx(), p, t); t.check_error(); return tactic(t.ctx(), r); } inline tactic cond(probe const & p, tactic const & t1, tactic const & t2) { check_context(p, t1); check_context(p, t2); Z3_tactic r = Z3_tactic_cond(t1.ctx(), p, t1, t2); t1.check_error(); return tactic(t1.ctx(), r); } inline symbol context::str_symbol(char const * s) { Z3_symbol r = Z3_mk_string_symbol(m_ctx, s); check_error(); return symbol(*this, r); } inline symbol context::int_symbol(int n) { Z3_symbol r = Z3_mk_int_symbol(m_ctx, n); check_error(); return symbol(*this, r); } inline sort context::bool_sort() { Z3_sort s = Z3_mk_bool_sort(m_ctx); check_error(); return sort(*this, s); } inline sort context::int_sort() { Z3_sort s = Z3_mk_int_sort(m_ctx); check_error(); return sort(*this, s); } inline sort context::real_sort() { Z3_sort s = Z3_mk_real_sort(m_ctx); check_error(); return sort(*this, s); } inline sort context::bv_sort(unsigned sz) { Z3_sort s = Z3_mk_bv_sort(m_ctx, sz); check_error(); return sort(*this, s); } inline sort context::array_sort(sort d, sort r) { Z3_sort s = Z3_mk_array_sort(m_ctx, d, r); check_error(); return sort(*this, s); } inline sort context::enumeration_sort(char const * name, unsigned n, char const * const * enum_names, func_decl_vector & cs, func_decl_vector & ts) { array _enum_names(n); for (unsigned i = 0; i < n; i++) { _enum_names[i] = Z3_mk_string_symbol(*this, enum_names[i]); } array _cs(n); array _ts(n); Z3_symbol _name = Z3_mk_string_symbol(*this, name); sort s = to_sort(*this, Z3_mk_enumeration_sort(*this, _name, n, _enum_names.ptr(), _cs.ptr(), _ts.ptr())); check_error(); for (unsigned i = 0; i < n; i++) { cs.push_back(func_decl(*this, _cs[i])); ts.push_back(func_decl(*this, _ts[i])); } return s; } inline sort context::uninterpreted_sort(char const* name) { Z3_symbol _name = Z3_mk_string_symbol(*this, name); return to_sort(*this, Z3_mk_uninterpreted_sort(*this, _name)); } inline sort context::uninterpreted_sort(symbol const& name) { return to_sort(*this, Z3_mk_uninterpreted_sort(*this, name)); } inline func_decl context::function(symbol const & name, unsigned arity, sort const * domain, sort const & range) { array args(arity); for (unsigned i = 0; i < arity; i++) { check_context(domain[i], range); args[i] = domain[i]; } Z3_func_decl f = Z3_mk_func_decl(m_ctx, name, arity, args.ptr(), range); check_error(); return func_decl(*this, f); } inline func_decl context::function(char const * name, unsigned arity, sort const * domain, sort const & range) { return function(range.ctx().str_symbol(name), arity, domain, range); } inline func_decl context::function(symbol const& name, sort_vector const& domain, sort const& range) { array args(domain.size()); for (unsigned i = 0; i < domain.size(); i++) { check_context(domain[i], range); args[i] = domain[i]; } Z3_func_decl f = Z3_mk_func_decl(m_ctx, name, domain.size(), args.ptr(), range); check_error(); return func_decl(*this, f); } inline func_decl context::function(char const * name, sort_vector const& domain, sort const& range) { return function(range.ctx().str_symbol(name), domain, range); } inline func_decl context::function(char const * name, sort const & domain, sort const & range) { check_context(domain, range); Z3_sort args[1] = { domain }; Z3_func_decl f = Z3_mk_func_decl(m_ctx, str_symbol(name), 1, args, range); check_error(); return func_decl(*this, f); } inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & range) { check_context(d1, range); check_context(d2, range); Z3_sort args[2] = { d1, d2 }; Z3_func_decl f = Z3_mk_func_decl(m_ctx, str_symbol(name), 2, args, range); check_error(); return func_decl(*this, f); } inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range) { check_context(d1, range); check_context(d2, range); check_context(d3, range); Z3_sort args[3] = { d1, d2, d3 }; Z3_func_decl f = Z3_mk_func_decl(m_ctx, str_symbol(name), 3, args, range); check_error(); return func_decl(*this, f); } inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range) { check_context(d1, range); check_context(d2, range); check_context(d3, range); check_context(d4, range); Z3_sort args[4] = { d1, d2, d3, d4 }; Z3_func_decl f = Z3_mk_func_decl(m_ctx, str_symbol(name), 4, args, range); check_error(); return func_decl(*this, f); } inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range) { check_context(d1, range); check_context(d2, range); check_context(d3, range); check_context(d4, range); check_context(d5, range); Z3_sort args[5] = { d1, d2, d3, d4, d5 }; Z3_func_decl f = Z3_mk_func_decl(m_ctx, str_symbol(name), 5, args, range); check_error(); return func_decl(*this, f); } inline expr context::constant(symbol const & name, sort const & s) { Z3_ast r = Z3_mk_const(m_ctx, name, s); check_error(); return expr(*this, r); } inline expr context::constant(char const * name, sort const & s) { return constant(str_symbol(name), s); } inline expr context::bool_const(char const * name) { return constant(name, bool_sort()); } inline expr context::int_const(char const * name) { return constant(name, int_sort()); } inline expr context::real_const(char const * name) { return constant(name, real_sort()); } inline expr context::bv_const(char const * name, unsigned sz) { return constant(name, bv_sort(sz)); } inline expr context::bool_val(bool b) { return b ? expr(*this, Z3_mk_true(m_ctx)) : expr(*this, Z3_mk_false(m_ctx)); } inline expr context::int_val(int n) { Z3_ast r = Z3_mk_int(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } inline expr context::int_val(unsigned n) { Z3_ast r = Z3_mk_unsigned_int(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } inline expr context::int_val(__int64 n) { Z3_ast r = Z3_mk_int64(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } inline expr context::int_val(__uint64 n) { Z3_ast r = Z3_mk_unsigned_int64(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } inline expr context::int_val(char const * n) { Z3_ast r = Z3_mk_numeral(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(int n, int d) { Z3_ast r = Z3_mk_real(m_ctx, n, d); check_error(); return expr(*this, r); } inline expr context::real_val(int n) { Z3_ast r = Z3_mk_int(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(unsigned n) { Z3_ast r = Z3_mk_unsigned_int(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(__int64 n) { Z3_ast r = Z3_mk_int64(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(__uint64 n) { Z3_ast r = Z3_mk_unsigned_int64(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(char const * n) { Z3_ast r = Z3_mk_numeral(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } inline expr context::bv_val(int n, unsigned sz) { Z3_ast r = Z3_mk_int(m_ctx, n, bv_sort(sz)); check_error(); return expr(*this, r); } inline expr context::bv_val(unsigned n, unsigned sz) { Z3_ast r = Z3_mk_unsigned_int(m_ctx, n, bv_sort(sz)); check_error(); return expr(*this, r); } inline expr context::bv_val(__int64 n, unsigned sz) { Z3_ast r = Z3_mk_int64(m_ctx, n, bv_sort(sz)); check_error(); return expr(*this, r); } inline expr context::bv_val(__uint64 n, unsigned sz) { Z3_ast r = Z3_mk_unsigned_int64(m_ctx, n, bv_sort(sz)); check_error(); return expr(*this, r); } inline expr context::bv_val(char const * n, unsigned sz) { Z3_ast r = Z3_mk_numeral(m_ctx, n, bv_sort(sz)); check_error(); return expr(*this, r); } inline expr context::num_val(int n, sort const & s) { Z3_ast r = Z3_mk_int(m_ctx, n, s); check_error(); return expr(*this, r); } inline expr func_decl::operator()(unsigned n, expr const * args) const { array _args(n); for (unsigned i = 0; i < n; i++) { check_context(*this, args[i]); _args[i] = args[i]; } Z3_ast r = Z3_mk_app(ctx(), *this, n, _args.ptr()); check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(expr_vector const& args) const { array _args(args.size()); for (unsigned i = 0; i < args.size(); i++) { check_context(*this, args[i]); _args[i] = args[i]; } Z3_ast r = Z3_mk_app(ctx(), *this, args.size(), _args.ptr()); check_error(); return expr(ctx(), r); } inline expr func_decl::operator()() const { Z3_ast r = Z3_mk_app(ctx(), *this, 0, 0); ctx().check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(expr const & a) const { check_context(*this, a); Z3_ast args[1] = { a }; Z3_ast r = Z3_mk_app(ctx(), *this, 1, args); ctx().check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(int a) const { Z3_ast args[1] = { ctx().num_val(a, domain(0)) }; Z3_ast r = Z3_mk_app(ctx(), *this, 1, args); ctx().check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(expr const & a1, expr const & a2) const { check_context(*this, a1); check_context(*this, a2); Z3_ast args[2] = { a1, a2 }; Z3_ast r = Z3_mk_app(ctx(), *this, 2, args); ctx().check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(expr const & a1, int a2) const { check_context(*this, a1); Z3_ast args[2] = { a1, ctx().num_val(a2, domain(1)) }; Z3_ast r = Z3_mk_app(ctx(), *this, 2, args); ctx().check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(int a1, expr const & a2) const { check_context(*this, a2); Z3_ast args[2] = { ctx().num_val(a1, domain(0)), a2 }; Z3_ast r = Z3_mk_app(ctx(), *this, 2, args); ctx().check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3) const { check_context(*this, a1); check_context(*this, a2); check_context(*this, a3); Z3_ast args[3] = { a1, a2, a3 }; Z3_ast r = Z3_mk_app(ctx(), *this, 3, args); ctx().check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const { check_context(*this, a1); check_context(*this, a2); check_context(*this, a3); check_context(*this, a4); Z3_ast args[4] = { a1, a2, a3, a4 }; Z3_ast r = Z3_mk_app(ctx(), *this, 4, args); ctx().check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const { check_context(*this, a1); check_context(*this, a2); check_context(*this, a3); check_context(*this, a4); check_context(*this, a5); Z3_ast args[5] = { a1, a2, a3, a4, a5 }; Z3_ast r = Z3_mk_app(ctx(), *this, 5, args); ctx().check_error(); return expr(ctx(), r); } inline expr to_real(expr const & a) { Z3_ast r = Z3_mk_int2real(a.ctx(), a); a.check_error(); return expr(a.ctx(), r); } inline func_decl function(symbol const & name, unsigned arity, sort const * domain, sort const & range) { return range.ctx().function(name, arity, domain, range); } inline func_decl function(char const * name, unsigned arity, sort const * domain, sort const & range) { return range.ctx().function(name, arity, domain, range); } inline func_decl function(char const * name, sort const & domain, sort const & range) { return range.ctx().function(name, domain, range); } inline func_decl function(char const * name, sort const & d1, sort const & d2, sort const & range) { return range.ctx().function(name, d1, d2, range); } inline func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range) { return range.ctx().function(name, d1, d2, d3, range); } inline func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range) { return range.ctx().function(name, d1, d2, d3, d4, range); } inline func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range) { return range.ctx().function(name, d1, d2, d3, d4, d5, range); } inline expr select(expr const & a, expr const & i) { check_context(a, i); Z3_ast r = Z3_mk_select(a.ctx(), a, i); a.check_error(); return expr(a.ctx(), r); } inline expr select(expr const & a, int i) { return select(a, a.ctx().num_val(i, a.get_sort().array_domain())); } inline expr store(expr const & a, expr const & i, expr const & v) { check_context(a, i); check_context(a, v); Z3_ast r = Z3_mk_store(a.ctx(), a, i, v); a.check_error(); return expr(a.ctx(), r); } inline expr store(expr const & a, int i, expr const & v) { return store(a, a.ctx().num_val(i, a.get_sort().array_domain()), v); } inline expr store(expr const & a, expr i, int v) { return store(a, i, a.ctx().num_val(v, a.get_sort().array_range())); } inline expr store(expr const & a, int i, int v) { return store(a, a.ctx().num_val(i, a.get_sort().array_domain()), a.ctx().num_val(v, a.get_sort().array_range())); } inline expr const_array(sort const & d, expr const & v) { check_context(d, v); Z3_ast r = Z3_mk_const_array(d.ctx(), d, v); d.check_error(); return expr(d.ctx(), r); } inline expr expr::substitute(expr_vector const& src, expr_vector const& dst) { assert(src.size() == dst.size()); array _src(src.size()); array _dst(dst.size()); for (unsigned i = 0; i < src.size(); ++i) { _src[i] = src[i]; _dst[i] = dst[i]; } Z3_ast r = Z3_substitute(ctx(), m_ast, src.size(), _src.ptr(), _dst.ptr()); check_error(); return expr(ctx(), r); } inline expr expr::substitute(expr_vector const& dst) { array _dst(dst.size()); for (unsigned i = 0; i < dst.size(); ++i) { _dst[i] = dst[i]; } Z3_ast r = Z3_substitute_vars(ctx(), m_ast, dst.size(), _dst.ptr()); check_error(); return expr(ctx(), r); } }; /*@}*/ /*@}*/ #endif z3-4.4.0/src/api/api_array.cpp0000644000175000017500000002013712540347414015663 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_array.cpp Abstract: API for array theory Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" #include"api_util.h" #include"array_decl_plugin.h" extern "C" { Z3_sort Z3_API Z3_mk_array_sort(Z3_context c, Z3_sort domain, Z3_sort range) { Z3_TRY; LOG_Z3_mk_array_sort(c, domain, range); RESET_ERROR_CODE(); parameter params[2] = { parameter(to_sort(domain)), parameter(to_sort(range)) }; sort * ty = mk_c(c)->m().mk_sort(mk_c(c)->get_array_fid(), ARRAY_SORT, 2, params); mk_c(c)->save_ast_trail(ty); RETURN_Z3(of_sort(ty)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_select(Z3_context c, Z3_ast a, Z3_ast i) { Z3_TRY; LOG_Z3_mk_select(c, a, i); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * _a = to_expr(a); expr * _i = to_expr(i); sort * a_ty = m.get_sort(_a); sort * i_ty = m.get_sort(_i); if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { SET_ERROR_CODE(Z3_SORT_ERROR); RETURN_Z3(0); } sort * domain[2] = {a_ty, i_ty}; func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_SELECT, 2, a_ty->get_parameters(), 2, domain); expr * args[2] = {_a, _i}; app * r = m.mk_app(d, 2, args); mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_store(Z3_context c, Z3_ast a, Z3_ast i, Z3_ast v) { Z3_TRY; LOG_Z3_mk_store(c, a, i, v); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * _a = to_expr(a); expr * _i = to_expr(i); expr * _v = to_expr(v); sort * a_ty = m.get_sort(_a); sort * i_ty = m.get_sort(_i); sort * v_ty = m.get_sort(_v); if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { SET_ERROR_CODE(Z3_SORT_ERROR); RETURN_Z3(0); } sort * domain[3] = {a_ty, i_ty, v_ty}; func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_STORE, 2, a_ty->get_parameters(), 3, domain); expr * args[3] = {_a, _i, _v}; app * r = m.mk_app(d, 3, args); mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_map(__in Z3_context c, __in Z3_func_decl f, unsigned n, __in Z3_ast const* args) { Z3_TRY; LOG_Z3_mk_map(c, f, n, args); RESET_ERROR_CODE(); if (n == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } ast_manager & m = mk_c(c)->m(); func_decl* _f = to_func_decl(f); expr* const* _args = to_exprs(args); ptr_vector domain; for (unsigned i = 0; i < n; ++i) { domain.push_back(m.get_sort(_args[i])); } parameter param(_f); func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_ARRAY_MAP, 1, ¶m, n, domain.c_ptr()); app* r = m.mk_app(d, n, _args); mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_const_array(__in Z3_context c, __in Z3_sort domain, __in Z3_ast v) { Z3_TRY; LOG_Z3_mk_const_array(c, domain, v); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * _v = to_expr(v); sort * _range = m.get_sort(_v); sort * _domain = to_sort(domain); parameter params[2] = { parameter(_domain), parameter(_range) }; sort * a_ty = mk_c(c)->m().mk_sort(mk_c(c)->get_array_fid(), ARRAY_SORT, 2, params); parameter param(a_ty); func_decl* cd = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_CONST_ARRAY, 1, ¶m, 1, &_range); app * r = m.mk_app(cd, 1, &_v); mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_array_default(__in Z3_context c, __in Z3_ast array) { Z3_TRY; LOG_Z3_mk_array_default(c, array); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * _a = to_expr(array); func_decl * f = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_ARRAY_DEFAULT, 0, 0, 1, &_a); app * r = m.mk_app(f, 1, &_a); mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(0); } Z3_ast mk_app_array_core(__in Z3_context c, __in Z3_sort domain, __in Z3_ast v) { RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * _v = to_expr(v); sort * _range = m.get_sort(_v); sort * _domain = to_sort(domain); parameter params[2] = { parameter(_domain), parameter(_range) }; sort * a_ty = mk_c(c)->m().mk_sort(mk_c(c)->get_array_fid(), ARRAY_SORT, 2, params); parameter param(a_ty); func_decl * cd = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_CONST_ARRAY, 1, ¶m, 1, &_range); app * r = m.mk_app(cd, 1, &_v); mk_c(c)->save_ast_trail(r); check_sorts(c, r); return of_ast(r); } Z3_sort Z3_API Z3_mk_set_sort(__in Z3_context c, __in Z3_sort ty) { Z3_TRY; return Z3_mk_array_sort(c, ty, Z3_mk_bool_sort(c)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_empty_set(__in Z3_context c, __in Z3_sort domain) { Z3_TRY; LOG_Z3_mk_empty_set(c, domain); RESET_ERROR_CODE(); Z3_ast r = mk_app_array_core(c, domain, Z3_mk_false(c)); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_full_set(__in Z3_context c, __in Z3_sort domain) { Z3_TRY; LOG_Z3_mk_full_set(c, domain); RESET_ERROR_CODE(); Z3_ast r = mk_app_array_core(c, domain, Z3_mk_true(c)); RETURN_Z3(r); Z3_CATCH_RETURN(0); } MK_NARY(Z3_mk_set_union, mk_c(c)->get_array_fid(), OP_SET_UNION, SKIP); MK_NARY(Z3_mk_set_intersect, mk_c(c)->get_array_fid(), OP_SET_INTERSECT, SKIP); MK_BINARY(Z3_mk_set_difference, mk_c(c)->get_array_fid(), OP_SET_DIFFERENCE, SKIP); MK_UNARY(Z3_mk_set_complement, mk_c(c)->get_array_fid(), OP_SET_COMPLEMENT, SKIP); MK_BINARY(Z3_mk_set_subset, mk_c(c)->get_array_fid(), OP_SET_SUBSET, SKIP); Z3_ast Z3_mk_set_member(__in Z3_context c, __in Z3_ast elem, __in Z3_ast set) { return Z3_mk_select(c, set, elem); } Z3_ast Z3_mk_set_add(__in Z3_context c, __in Z3_ast set, __in Z3_ast elem) { return Z3_mk_store(c, set, elem, Z3_mk_true(c)); } Z3_ast Z3_mk_set_del(__in Z3_context c, __in Z3_ast set, __in Z3_ast elem) { return Z3_mk_store(c, set, elem, Z3_mk_false(c)); } Z3_sort Z3_API Z3_get_array_sort_domain(Z3_context c, Z3_sort t) { Z3_TRY; LOG_Z3_get_array_sort_domain(c, t); RESET_ERROR_CODE(); CHECK_VALID_AST(t, 0); if (to_sort(t)->get_family_id() == mk_c(c)->get_array_fid() && to_sort(t)->get_decl_kind() == ARRAY_SORT) { Z3_sort r = reinterpret_cast(to_sort(t)->get_parameter(0).get_ast()); RETURN_Z3(r); } SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_get_array_sort_range(Z3_context c, Z3_sort t) { Z3_TRY; LOG_Z3_get_array_sort_range(c, t); RESET_ERROR_CODE(); CHECK_VALID_AST(t, 0); if (to_sort(t)->get_family_id() == mk_c(c)->get_array_fid() && to_sort(t)->get_decl_kind() == ARRAY_SORT) { Z3_sort r = reinterpret_cast(to_sort(t)->get_parameter(1).get_ast()); RETURN_Z3(r); } SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); Z3_CATCH_RETURN(0); } }; z3-4.4.0/src/api/api_ast_vector.h0000644000175000017500000000143112540347414016357 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_ast_vector.h Abstract: API for creating AST vectors Author: Leonardo de Moura (leonardo) 2012-03-09. Revision History: --*/ #ifndef _API_AST_VECTOR_H_ #define _API_AST_VECTOR_H_ #include"api_util.h" struct Z3_ast_vector_ref : public api::object { ast_ref_vector m_ast_vector; Z3_ast_vector_ref(ast_manager & m):m_ast_vector(m) {} virtual ~Z3_ast_vector_ref() {} }; inline Z3_ast_vector_ref * to_ast_vector(Z3_ast_vector v) { return reinterpret_cast(v); } inline Z3_ast_vector of_ast_vector(Z3_ast_vector_ref * v) { return reinterpret_cast(v); } inline ast_ref_vector & to_ast_vector_ref(Z3_ast_vector v) { return to_ast_vector(v)->m_ast_vector; } #endif z3-4.4.0/src/api/api_goal.cpp0000644000175000017500000001204312540347414015464 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_goal.cpp Abstract: API for creating goals Author: Leonardo de Moura (leonardo) 2012-03-06. Revision History: --*/ #include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" #include"api_goal.h" #include"ast_translation.h" extern "C" { Z3_goal Z3_API Z3_mk_goal(Z3_context c, Z3_bool models, Z3_bool unsat_cores, Z3_bool proofs) { Z3_TRY; LOG_Z3_mk_goal(c, models, unsat_cores, proofs); RESET_ERROR_CODE(); if (proofs != 0 && !mk_c(c)->m().proofs_enabled()) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } Z3_goal_ref * g = alloc(Z3_goal_ref); g->m_goal = alloc(goal, mk_c(c)->m(), proofs != 0, models != 0, unsat_cores != 0); mk_c(c)->save_object(g); Z3_goal r = of_goal(g); RETURN_Z3(r); Z3_CATCH_RETURN(0); } void Z3_API Z3_goal_inc_ref(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_inc_ref(c, g); RESET_ERROR_CODE(); to_goal(g)->inc_ref(); Z3_CATCH; } void Z3_API Z3_goal_dec_ref(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_dec_ref(c, g); RESET_ERROR_CODE(); to_goal(g)->dec_ref(); Z3_CATCH; } Z3_goal_prec Z3_API Z3_goal_precision(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_precision(c, g); RESET_ERROR_CODE(); switch (to_goal_ref(g)->prec()) { case goal::PRECISE: return Z3_GOAL_PRECISE; case goal::UNDER: return Z3_GOAL_UNDER; case goal::OVER: return Z3_GOAL_OVER; case goal::UNDER_OVER: return Z3_GOAL_UNDER_OVER; default: UNREACHABLE(); return Z3_GOAL_UNDER_OVER; } Z3_CATCH_RETURN(Z3_GOAL_UNDER_OVER); } void Z3_API Z3_goal_assert(Z3_context c, Z3_goal g, Z3_ast a) { Z3_TRY; LOG_Z3_goal_assert(c, g, a); RESET_ERROR_CODE(); CHECK_FORMULA(a,); to_goal_ref(g)->assert_expr(to_expr(a)); Z3_CATCH; } Z3_bool Z3_API Z3_goal_inconsistent(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_inconsistent(c, g); RESET_ERROR_CODE(); return to_goal_ref(g)->inconsistent(); Z3_CATCH_RETURN(Z3_FALSE); } unsigned Z3_API Z3_goal_depth(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_depth(c, g); RESET_ERROR_CODE(); return to_goal_ref(g)->depth(); Z3_CATCH_RETURN(0); } void Z3_API Z3_goal_reset(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_reset(c, g); RESET_ERROR_CODE(); to_goal_ref(g)->reset(); Z3_CATCH; } unsigned Z3_API Z3_goal_size(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_size(c, g); RESET_ERROR_CODE(); return to_goal_ref(g)->size(); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_goal_formula(Z3_context c, Z3_goal g, unsigned idx) { Z3_TRY; LOG_Z3_goal_formula(c, g, idx); RESET_ERROR_CODE(); if (idx >= to_goal_ref(g)->size()) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } expr * result = to_goal_ref(g)->form(idx); mk_c(c)->save_ast_trail(result); RETURN_Z3(of_ast(result)); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_goal_num_exprs(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_num_exprs(c, g); RESET_ERROR_CODE(); return to_goal_ref(g)->num_exprs(); Z3_CATCH_RETURN(0); } Z3_bool Z3_API Z3_goal_is_decided_sat(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_is_decided_sat(c, g); RESET_ERROR_CODE(); return to_goal_ref(g)->is_decided_sat(); Z3_CATCH_RETURN(Z3_FALSE); } Z3_bool Z3_API Z3_goal_is_decided_unsat(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_is_decided_unsat(c, g); RESET_ERROR_CODE(); return to_goal_ref(g)->is_decided_unsat(); Z3_CATCH_RETURN(Z3_FALSE); } Z3_goal Z3_API Z3_goal_translate(Z3_context c, Z3_goal g, Z3_context target) { Z3_TRY; LOG_Z3_goal_translate(c, g, target); RESET_ERROR_CODE(); ast_translation translator(mk_c(c)->m(), mk_c(target)->m()); Z3_goal_ref * _r = alloc(Z3_goal_ref); _r->m_goal = to_goal_ref(g)->translate(translator); mk_c(target)->save_object(_r); Z3_goal r = of_goal(_r); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_goal_to_string(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_to_string(c, g); RESET_ERROR_CODE(); std::ostringstream buffer; to_goal_ref(g)->display(buffer); // Hack for removing the trailing '\n' std::string result = buffer.str(); SASSERT(result.size() > 0); result.resize(result.size()-1); return mk_c(c)->mk_external_string(result); Z3_CATCH_RETURN(""); } }; z3-4.4.0/src/api/api_params.cpp0000644000175000017500000001354612540347414016036 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_params.cpp Abstract: API for creating parameter sets. This is essentially a wrapper for params_ref. Author: Leonardo de Moura (leonardo) 2012-03-05. Revision History: --*/ #include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" #include"api_util.h" #include"params.h" extern "C" { Z3_params Z3_API Z3_mk_params(Z3_context c) { Z3_TRY; LOG_Z3_mk_params(c); RESET_ERROR_CODE(); Z3_params_ref * p = alloc(Z3_params_ref); mk_c(c)->save_object(p); Z3_params r = of_params(p); RETURN_Z3(r); Z3_CATCH_RETURN(0); } /** \brief Increment the reference counter of the given parameter set. */ void Z3_API Z3_params_inc_ref(Z3_context c, Z3_params p) { Z3_TRY; LOG_Z3_params_inc_ref(c, p); RESET_ERROR_CODE(); to_params(p)->inc_ref(); Z3_CATCH; } /** \brief Decrement the reference counter of the given parameter set. */ void Z3_API Z3_params_dec_ref(Z3_context c, Z3_params p) { Z3_TRY; LOG_Z3_params_dec_ref(c, p); RESET_ERROR_CODE(); to_params(p)->dec_ref(); Z3_CATCH; } /** \brief Add a Boolean parameter \c k with value \c v to the parameter set \c p. */ void Z3_API Z3_params_set_bool(Z3_context c, Z3_params p, Z3_symbol k, Z3_bool v) { Z3_TRY; LOG_Z3_params_set_bool(c, p, k, v); RESET_ERROR_CODE(); to_params(p)->m_params.set_bool(norm_param_name(to_symbol(k)).c_str(), v != 0); Z3_CATCH; } /** \brief Add a unsigned parameter \c k with value \c v to the parameter set \c p. */ void Z3_API Z3_params_set_uint(Z3_context c, Z3_params p, Z3_symbol k, unsigned v) { Z3_TRY; LOG_Z3_params_set_uint(c, p, k, v); RESET_ERROR_CODE(); to_params(p)->m_params.set_uint(norm_param_name(to_symbol(k)).c_str(), v); Z3_CATCH; } /** \brief Add a double parameter \c k with value \c v to the parameter set \c p. */ void Z3_API Z3_params_set_double(Z3_context c, Z3_params p, Z3_symbol k, double v) { Z3_TRY; LOG_Z3_params_set_double(c, p, k, v); RESET_ERROR_CODE(); to_params(p)->m_params.set_double(norm_param_name(to_symbol(k)).c_str(), v); Z3_CATCH; } /** \brief Add a symbol parameter \c k with value \c v to the parameter set \c p. */ void Z3_API Z3_params_set_symbol(Z3_context c, Z3_params p, Z3_symbol k, Z3_symbol v) { Z3_TRY; LOG_Z3_params_set_symbol(c, p, k, v); RESET_ERROR_CODE(); to_params(p)->m_params.set_sym(norm_param_name(to_symbol(k)).c_str(), to_symbol(v)); Z3_CATCH; } /** \brief Convert a parameter set into a string. This function is mainly used for printing the contents of a parameter set. */ Z3_string Z3_API Z3_params_to_string(Z3_context c, Z3_params p) { Z3_TRY; LOG_Z3_params_to_string(c, p); RESET_ERROR_CODE(); std::ostringstream buffer; to_params(p)->m_params.display(buffer); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } void Z3_API Z3_params_validate(Z3_context c, Z3_params p, Z3_param_descrs d) { Z3_TRY; LOG_Z3_params_validate(c, p, d); RESET_ERROR_CODE(); to_params(p)->m_params.validate(*to_param_descrs_ptr(d)); Z3_CATCH; } void Z3_API Z3_param_descrs_inc_ref(Z3_context c, Z3_param_descrs p) { Z3_TRY; LOG_Z3_param_descrs_inc_ref(c, p); RESET_ERROR_CODE(); to_param_descrs(p)->inc_ref(); Z3_CATCH; } void Z3_API Z3_param_descrs_dec_ref(Z3_context c, Z3_param_descrs p) { Z3_TRY; LOG_Z3_param_descrs_dec_ref(c, p); RESET_ERROR_CODE(); to_param_descrs(p)->dec_ref(); Z3_CATCH; } Z3_param_kind Z3_API Z3_param_descrs_get_kind(Z3_context c, Z3_param_descrs p, Z3_symbol n) { Z3_TRY; LOG_Z3_param_descrs_get_kind(c, p, n); RESET_ERROR_CODE(); param_kind k = to_param_descrs_ptr(p)->get_kind(to_symbol(n)); switch (k) { case CPK_UINT: return Z3_PK_UINT; case CPK_BOOL: return Z3_PK_BOOL; case CPK_DOUBLE: return Z3_PK_DOUBLE; case CPK_STRING: return Z3_PK_STRING; case CPK_SYMBOL: return Z3_PK_SYMBOL; case CPK_INVALID: return Z3_PK_INVALID; default: return Z3_PK_OTHER; } Z3_CATCH_RETURN(Z3_PK_INVALID); } unsigned Z3_API Z3_param_descrs_size(Z3_context c, Z3_param_descrs p) { Z3_TRY; LOG_Z3_param_descrs_size(c, p); RESET_ERROR_CODE(); return to_param_descrs_ptr(p)->size(); Z3_CATCH_RETURN(0); } Z3_symbol Z3_API Z3_param_descrs_get_name(Z3_context c, Z3_param_descrs p, unsigned i) { Z3_TRY; LOG_Z3_param_descrs_get_name(c, p, i); RESET_ERROR_CODE(); if (i >= to_param_descrs_ptr(p)->size()) { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } Z3_symbol result = of_symbol(to_param_descrs_ptr(p)->get_param_name(i)); return result; Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_param_descrs_to_string(Z3_context c, Z3_param_descrs p) { Z3_TRY; LOG_Z3_param_descrs_to_string(c, p); RESET_ERROR_CODE(); std::ostringstream buffer; buffer << "("; unsigned sz = to_param_descrs_ptr(p)->size(); for (unsigned i = 0; i < sz; i++) { if (i > 0) buffer << ", "; buffer << to_param_descrs_ptr(p)->get_param_name(i); } buffer << ")"; return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } }; z3-4.4.0/src/api/api_pb.cpp0000644000175000017500000000271212540347414015145 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: api_pb.cpp Abstract: API for pb theory Author: Nikolaj Bjorner (nbjorner) 2013-11-13. Revision History: --*/ #include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" #include"api_util.h" #include"pb_decl_plugin.h" extern "C" { Z3_ast Z3_API Z3_mk_atmost(Z3_context c, unsigned num_args, Z3_ast const args[], unsigned k) { Z3_TRY; LOG_Z3_mk_atmost(c, num_args, args, k); RESET_ERROR_CODE(); parameter param(k); pb_util util(mk_c(c)->m()); ast* a = util.mk_at_most_k(num_args, to_exprs(args), k); mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_pble(Z3_context c, unsigned num_args, Z3_ast const args[], int _coeffs[], int k) { Z3_TRY; LOG_Z3_mk_pble(c, num_args, args, _coeffs, k); RESET_ERROR_CODE(); pb_util util(mk_c(c)->m()); vector coeffs; for (unsigned i = 0; i < num_args; ++i) { coeffs.push_back(rational(_coeffs[i])); } ast* a = util.mk_le(num_args, coeffs.c_ptr(), to_exprs(args), rational(k)); mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(0); } }; z3-4.4.0/src/api/z3_logger.h0000644000175000017500000000531012540347414015250 0ustar michaelmichael/*++ Copyright (c) 2011 Microsoft Corporation Module Name: z3_logger.h Abstract: Goodies for log generation Author: Leonardo de Moura (leonardo) 2011-09-22 Notes: --*/ #include #include"symbol.h" struct ll_escaped { char const * m_str; ll_escaped(char const * str):m_str(str) {} }; static std::ostream & operator<<(std::ostream & out, ll_escaped const & d); static void __declspec(noinline) R() { *g_z3_log << "R\n"; g_z3_log->flush(); } static void __declspec(noinline) P(void * obj) { *g_z3_log << "P " << obj << "\n"; g_z3_log->flush(); } static void __declspec(noinline) I(__int64 i) { *g_z3_log << "I " << i << "\n"; g_z3_log->flush(); } static void __declspec(noinline) U(__uint64 u) { *g_z3_log << "U " << u << "\n"; g_z3_log->flush(); } static void __declspec(noinline) D(double d) { *g_z3_log << "D " << d << "\n"; g_z3_log->flush(); } static void __declspec(noinline) S(Z3_string str) { *g_z3_log << "S \"" << ll_escaped(str) << "\"\n"; g_z3_log->flush(); } static void __declspec(noinline) Sy(Z3_symbol sym) { symbol s = symbol::mk_symbol_from_c_ptr(reinterpret_cast(sym)); if (s == symbol::null) { *g_z3_log << "N\n"; } else if (s.is_numerical()) { *g_z3_log << "# " << s.get_num() << "\n"; } else { *g_z3_log << "$ |" << ll_escaped(s.bare_str()) << "|\n"; } g_z3_log->flush(); } static void __declspec(noinline) Ap(unsigned sz) { *g_z3_log << "p " << sz << "\n"; g_z3_log->flush(); } static void __declspec(noinline) Au(unsigned sz) { *g_z3_log << "u " << sz << "\n"; g_z3_log->flush(); } static void __declspec(noinline) Asy(unsigned sz) { *g_z3_log << "s " << sz << "\n"; g_z3_log->flush(); } static void __declspec(noinline) C(unsigned id) { *g_z3_log << "C " << id << "\n"; g_z3_log->flush(); } void __declspec(noinline) _Z3_append_log(char const * msg) { *g_z3_log << "M \"" << ll_escaped(msg) << "\"\n"; g_z3_log->flush(); } static std::ostream & operator<<(std::ostream & out, ll_escaped const & d) { char const * s = d.m_str; while (*s) { char c = *s; if (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '~' || c == '!' || c == '@' || c == '#' || c == '$' || c == '%' || c == '^' || c == '&' || c == '*' || c == '-' || c == '_' || c == '+' || c == '.' || c == '?' || c == '/' || c == ' ' || c == '<' || c == '>') { out << c; } else { char str[4] = {'0', '0', '0', 0}; str[2] = '0' + (c % 10); c /= 10; str[1] = '0' + (c % 10); c /= 10; str[0] = '0' + c; out << '\\' << str; } s++; } return out; } z3-4.4.0/src/api/api_datalog.cpp0000644000175000017500000005104112540347414016156 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_datalog.cpp Abstract: Datalog API Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include"api_datalog.h" #include"api_context.h" #include"api_util.h" #include"ast_pp.h" #include"api_ast_vector.h" #include"api_log_macros.h" #include"api_stats.h" #include"datalog_parser.h" #include"cancel_eh.h" #include"scoped_timer.h" #include"dl_cmds.h" #include"cmd_context.h" #include"smt2parser.h" #include"dl_context.h" #include"dl_register_engine.h" #include"dl_external_relation.h" #include"dl_decl_plugin.h" #include"rel_context.h" namespace api { class fixedpoint_context : public datalog::external_relation_context { void * m_state; reduce_app_callback_fptr m_reduce_app; reduce_assign_callback_fptr m_reduce_assign; datalog::register_engine m_register_engine; datalog::context m_context; ast_ref_vector m_trail; public: fixedpoint_context(ast_manager& m, smt_params& p): m_state(0), m_reduce_app(0), m_reduce_assign(0), m_context(m, m_register_engine, p), m_trail(m) {} virtual ~fixedpoint_context() {} family_id get_family_id() const { return const_cast(m_context).get_decl_util().get_family_id(); } void set_state(void* state) { SASSERT(!m_state); m_state = state; symbol name("datalog_relation"); ast_manager& m = m_context.get_manager(); if (!m.has_plugin(name)) { m.register_plugin(name, alloc(datalog::dl_decl_plugin)); } datalog::rel_context_base* rel = m_context.get_rel_context(); if (rel) { datalog::relation_manager& r = rel->get_rmanager(); r.register_plugin(alloc(datalog::external_relation_plugin, *this, r)); } } void set_reduce_app(reduce_app_callback_fptr f) { m_reduce_app = f; } void set_reduce_assign(reduce_assign_callback_fptr f) { m_reduce_assign = f; } virtual void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) { expr* r = 0; if (m_reduce_app) { m_reduce_app(m_state, f, num_args, args, &r); result = r; m_trail.push_back(f); for (unsigned i = 0; i < num_args; ++i) { m_trail.push_back(args[i]); } m_trail.push_back(r); } // allow fallthrough. if (r == 0) { ast_manager& m = m_context.get_manager(); result = m.mk_app(f, num_args, args); } } virtual void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) { if (m_reduce_assign) { m_trail.push_back(f); for (unsigned i = 0; i < num_args; ++i) { m_trail.push_back(args[i]); } m_reduce_assign(m_state, f, num_args, args, num_out, outs); } } datalog::context& ctx() { return m_context; } void add_rule(expr* rule, symbol const& name) { m_context.add_rule(rule, name); } void update_rule(expr* rule, symbol const& name) { m_context.update_rule(rule, name); } void add_table_fact(func_decl* r, unsigned num_args, unsigned args[]) { m_context.add_table_fact(r, num_args, args); } std::string get_last_status() { datalog::execution_result status = m_context.get_status(); switch(status) { case datalog::INPUT_ERROR: return "input error"; case datalog::OK: return "ok"; case datalog::TIMEOUT: return "timeout"; case datalog::APPROX: return "approximated"; default: UNREACHABLE(); return "unknown"; } } std::string to_string(unsigned num_queries, expr* const* queries) { std::stringstream str; m_context.display_smt2(num_queries, queries, str); return str.str(); } void cancel() { m_context.cancel(); } void reset_cancel() { m_context.reset_cancel(); } unsigned get_num_levels(func_decl* pred) { return m_context.get_num_levels(pred); } expr_ref get_cover_delta(int level, func_decl* pred) { return m_context.get_cover_delta(level, pred); } void add_cover(int level, func_decl* pred, expr* predicate) { m_context.add_cover(level, pred, predicate); } void collect_param_descrs(param_descrs & p) { m_context.collect_params(p); } void updt_params(params_ref const& p) { m_context.updt_params(p); } }; }; extern "C" { //////////////////////////////////// // Datalog utilities // unsigned Z3_API Z3_get_relation_arity(Z3_context c, Z3_sort s) { Z3_TRY; LOG_Z3_get_relation_arity(c, s); RESET_ERROR_CODE(); sort * r = to_sort(s); if (Z3_get_sort_kind(c, s) != Z3_RELATION_SORT) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } return r->get_num_parameters(); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_get_relation_column(Z3_context c, Z3_sort s, unsigned col) { Z3_TRY; LOG_Z3_get_relation_column(c, s, col); RESET_ERROR_CODE(); sort * r = to_sort(s); if (Z3_get_sort_kind(c, s) != Z3_RELATION_SORT) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } if (col >= r->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } parameter const& p = r->get_parameter(col); if (!p.is_ast() || !is_sort(p.get_ast())) { UNREACHABLE(); warning_msg("Sort parameter expected at %d", col); SET_ERROR_CODE(Z3_INTERNAL_FATAL); RETURN_Z3(0); } Z3_sort res = of_sort(to_sort(p.get_ast())); RETURN_Z3(res); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_mk_finite_domain_sort(Z3_context c, Z3_symbol name, unsigned __int64 size) { Z3_TRY; LOG_Z3_mk_finite_domain_sort(c, name, size); RESET_ERROR_CODE(); sort* s = mk_c(c)->datalog_util().mk_sort(to_symbol(name), size); mk_c(c)->save_ast_trail(s); RETURN_Z3(of_sort(s)); Z3_CATCH_RETURN(0); } Z3_bool Z3_API Z3_get_finite_domain_sort_size(Z3_context c, Z3_sort s, unsigned __int64 * out) { Z3_TRY; if (out) { *out = 0; } if (Z3_get_sort_kind(c, s) != Z3_FINITE_DOMAIN_SORT) { return Z3_FALSE; } if (!out) { return Z3_FALSE; } // must start loggging here, since function uses Z3_get_sort_kind above LOG_Z3_get_finite_domain_sort_size(c, s, out); RESET_ERROR_CODE(); VERIFY(mk_c(c)->datalog_util().try_get_size(to_sort(s), *out)); return Z3_TRUE; Z3_CATCH_RETURN(Z3_FALSE); } Z3_fixedpoint Z3_API Z3_mk_fixedpoint(Z3_context c) { Z3_TRY; LOG_Z3_mk_fixedpoint(c); RESET_ERROR_CODE(); Z3_fixedpoint_ref * d = alloc(Z3_fixedpoint_ref); d->m_datalog = alloc(api::fixedpoint_context, mk_c(c)->m(), mk_c(c)->fparams()); mk_c(c)->save_object(d); Z3_fixedpoint r = of_datalog(d); RETURN_Z3(r); Z3_CATCH_RETURN(0); } void Z3_API Z3_fixedpoint_inc_ref(Z3_context c, Z3_fixedpoint s) { Z3_TRY; LOG_Z3_fixedpoint_inc_ref(c, s); RESET_ERROR_CODE(); to_fixedpoint(s)->inc_ref(); Z3_CATCH; } void Z3_API Z3_fixedpoint_dec_ref(Z3_context c, Z3_fixedpoint s) { Z3_TRY; LOG_Z3_fixedpoint_dec_ref(c, s); RESET_ERROR_CODE(); to_fixedpoint(s)->dec_ref(); Z3_CATCH; } void Z3_API Z3_fixedpoint_assert(Z3_context c, Z3_fixedpoint d, Z3_ast a) { Z3_TRY; LOG_Z3_fixedpoint_assert(c, d, a); RESET_ERROR_CODE(); CHECK_FORMULA(a,); to_fixedpoint_ref(d)->ctx().assert_expr(to_expr(a)); Z3_CATCH; } void Z3_API Z3_fixedpoint_add_rule(Z3_context c, Z3_fixedpoint d, Z3_ast a, Z3_symbol name) { Z3_TRY; LOG_Z3_fixedpoint_add_rule(c, d, a, name); RESET_ERROR_CODE(); CHECK_FORMULA(a,); to_fixedpoint_ref(d)->add_rule(to_expr(a), to_symbol(name)); Z3_CATCH; } void Z3_API Z3_fixedpoint_add_fact(Z3_context c, Z3_fixedpoint d, Z3_func_decl r, unsigned num_args, unsigned args[]) { Z3_TRY; LOG_Z3_fixedpoint_add_fact(c, d, r, num_args, args); RESET_ERROR_CODE(); to_fixedpoint_ref(d)->add_table_fact(to_func_decl(r), num_args, args); Z3_CATCH; } Z3_lbool Z3_API Z3_fixedpoint_query(Z3_context c,Z3_fixedpoint d, Z3_ast q) { Z3_TRY; LOG_Z3_fixedpoint_query(c, d, q); RESET_ERROR_CODE(); lbool r = l_undef; cancel_eh eh(*to_fixedpoint_ref(d)); unsigned timeout = to_fixedpoint(d)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_timer timer(timeout, &eh); try { r = to_fixedpoint_ref(d)->ctx().query(to_expr(q)); } catch (z3_exception& ex) { mk_c(c)->handle_exception(ex); r = l_undef; } to_fixedpoint_ref(d)->ctx().cleanup(); } return of_lbool(r); Z3_CATCH_RETURN(Z3_L_UNDEF); } Z3_lbool Z3_API Z3_fixedpoint_query_relations( __in Z3_context c,__in Z3_fixedpoint d, __in unsigned num_relations, Z3_func_decl const relations[]) { Z3_TRY; LOG_Z3_fixedpoint_query_relations(c, d, num_relations, relations); RESET_ERROR_CODE(); lbool r = l_undef; unsigned timeout = to_fixedpoint(d)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); cancel_eh eh(*to_fixedpoint_ref(d)); api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_timer timer(timeout, &eh); try { r = to_fixedpoint_ref(d)->ctx().rel_query(num_relations, to_func_decls(relations)); } catch (z3_exception& ex) { mk_c(c)->handle_exception(ex); r = l_undef; } to_fixedpoint_ref(d)->ctx().cleanup(); } return of_lbool(r); Z3_CATCH_RETURN(Z3_L_UNDEF); } Z3_ast Z3_API Z3_fixedpoint_get_answer(Z3_context c, Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_get_answer(c, d); RESET_ERROR_CODE(); expr* e = to_fixedpoint_ref(d)->ctx().get_answer_as_formula(); mk_c(c)->save_ast_trail(e); RETURN_Z3(of_expr(e)); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_fixedpoint_get_reason_unknown(Z3_context c,Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_get_reason_unknown(c, d); RESET_ERROR_CODE(); return mk_c(c)->mk_external_string(to_fixedpoint_ref(d)->get_last_status()); Z3_CATCH_RETURN(""); } Z3_string Z3_API Z3_fixedpoint_to_string( Z3_context c, Z3_fixedpoint d, unsigned num_queries, Z3_ast _queries[]) { Z3_TRY; expr*const* queries = to_exprs(_queries); LOG_Z3_fixedpoint_to_string(c, d, num_queries, _queries); RESET_ERROR_CODE(); return mk_c(c)->mk_external_string(to_fixedpoint_ref(d)->to_string(num_queries, queries)); Z3_CATCH_RETURN(""); } Z3_ast_vector Z3_fixedpoint_from_stream( Z3_context c, Z3_fixedpoint d, std::istream& s) { ast_manager& m = mk_c(c)->m(); dl_collected_cmds coll(m); cmd_context ctx(false, &m); install_dl_collect_cmds(coll, ctx); ctx.set_ignore_check(true); if (!parse_smt2_commands(ctx, s)) { SET_ERROR_CODE(Z3_PARSER_ERROR); return 0; } Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, m); mk_c(c)->save_object(v); for (unsigned i = 0; i < coll.m_queries.size(); ++i) { v->m_ast_vector.push_back(coll.m_queries[i].get()); } for (unsigned i = 0; i < coll.m_rels.size(); ++i) { to_fixedpoint_ref(d)->ctx().register_predicate(coll.m_rels[i].get(), true); } for (unsigned i = 0; i < coll.m_rules.size(); ++i) { to_fixedpoint_ref(d)->add_rule(coll.m_rules[i].get(), coll.m_names[i]); } ptr_vector::const_iterator it = ctx.begin_assertions(); ptr_vector::const_iterator end = ctx.end_assertions(); for (; it != end; ++it) { to_fixedpoint_ref(d)->ctx().assert_expr(*it); } return of_ast_vector(v); } Z3_ast_vector Z3_API Z3_fixedpoint_from_string( Z3_context c, Z3_fixedpoint d, Z3_string s) { Z3_TRY; LOG_Z3_fixedpoint_from_string(c, d, s); std::string str(s); std::istringstream is(str); RETURN_Z3(Z3_fixedpoint_from_stream(c, d, is)); Z3_CATCH_RETURN(0); } Z3_ast_vector Z3_API Z3_fixedpoint_from_file( Z3_context c, Z3_fixedpoint d, Z3_string s) { Z3_TRY; LOG_Z3_fixedpoint_from_file(c, d, s); std::ifstream is(s); if (!is) { SET_ERROR_CODE(Z3_PARSER_ERROR); RETURN_Z3(0); } RETURN_Z3(Z3_fixedpoint_from_stream(c, d, is)); Z3_CATCH_RETURN(0); } Z3_stats Z3_API Z3_fixedpoint_get_statistics(Z3_context c,Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_get_statistics(c, d); RESET_ERROR_CODE(); Z3_stats_ref * st = alloc(Z3_stats_ref); to_fixedpoint_ref(d)->ctx().collect_statistics(st->m_stats); mk_c(c)->save_object(st); Z3_stats r = of_stats(st); RETURN_Z3(r); Z3_CATCH_RETURN(0); } void Z3_API Z3_fixedpoint_register_relation(Z3_context c,Z3_fixedpoint d, Z3_func_decl f) { Z3_TRY; LOG_Z3_fixedpoint_register_relation(c, d, f); to_fixedpoint_ref(d)->ctx().register_predicate(to_func_decl(f), true); Z3_CATCH; } void Z3_API Z3_fixedpoint_set_predicate_representation( Z3_context c, Z3_fixedpoint d, Z3_func_decl f, unsigned num_relations, Z3_symbol const relation_kinds[]) { Z3_TRY; LOG_Z3_fixedpoint_set_predicate_representation(c, d, f, num_relations, relation_kinds); svector kinds; for (unsigned i = 0; i < num_relations; ++i) { kinds.push_back(to_symbol(relation_kinds[i])); } to_fixedpoint_ref(d)->ctx().set_predicate_representation(to_func_decl(f), num_relations, kinds.c_ptr()); Z3_CATCH; } Z3_ast_vector Z3_API Z3_fixedpoint_get_rules( Z3_context c, Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_get_rules(c, d); ast_manager& m = mk_c(c)->m(); Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, m); mk_c(c)->save_object(v); expr_ref_vector rules(m), queries(m); svector names; to_fixedpoint_ref(d)->ctx().get_rules_as_formulas(rules, queries, names); for (unsigned i = 0; i < rules.size(); ++i) { v->m_ast_vector.push_back(rules[i].get()); } for (unsigned i = 0; i < queries.size(); ++i) { v->m_ast_vector.push_back(m.mk_not(queries[i].get())); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(0); } Z3_ast_vector Z3_API Z3_fixedpoint_get_assertions( Z3_context c, Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_get_assertions(c, d); ast_manager& m = mk_c(c)->m(); Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, m); mk_c(c)->save_object(v); unsigned num_asserts = to_fixedpoint_ref(d)->ctx().get_num_assertions(); for (unsigned i = 0; i < num_asserts; ++i) { v->m_ast_vector.push_back(to_fixedpoint_ref(d)->ctx().get_assertion(i)); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(0); } void Z3_API Z3_fixedpoint_set_reduce_assign_callback( Z3_context c, Z3_fixedpoint d, Z3_fixedpoint_reduce_assign_callback_fptr f) { Z3_TRY; // no logging to_fixedpoint_ref(d)->set_reduce_assign((reduce_assign_callback_fptr)f); Z3_CATCH; } void Z3_API Z3_fixedpoint_set_reduce_app_callback( Z3_context c, Z3_fixedpoint d, Z3_fixedpoint_reduce_app_callback_fptr f) { Z3_TRY; // no logging to_fixedpoint_ref(d)->set_reduce_app((reduce_app_callback_fptr)f); Z3_CATCH; } void Z3_API Z3_fixedpoint_init(Z3_context c,Z3_fixedpoint d, void* state) { Z3_TRY; // not logged to_fixedpoint_ref(d)->set_state(state); Z3_CATCH; } void Z3_API Z3_fixedpoint_update_rule(Z3_context c, Z3_fixedpoint d, Z3_ast a, Z3_symbol name) { Z3_TRY; LOG_Z3_fixedpoint_update_rule(c, d, a, name); RESET_ERROR_CODE(); CHECK_FORMULA(a,); to_fixedpoint_ref(d)->update_rule(to_expr(a), to_symbol(name)); Z3_CATCH; } unsigned Z3_API Z3_fixedpoint_get_num_levels(Z3_context c, Z3_fixedpoint d, Z3_func_decl pred) { Z3_TRY; LOG_Z3_fixedpoint_get_num_levels(c, d, pred); RESET_ERROR_CODE(); return to_fixedpoint_ref(d)->get_num_levels(to_func_decl(pred)); Z3_CATCH_RETURN(0) } Z3_ast Z3_API Z3_fixedpoint_get_cover_delta(Z3_context c, Z3_fixedpoint d, int level, Z3_func_decl pred) { Z3_TRY; LOG_Z3_fixedpoint_get_cover_delta(c, d, level, pred); RESET_ERROR_CODE(); expr_ref r = to_fixedpoint_ref(d)->get_cover_delta(level, to_func_decl(pred)); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r.get())); Z3_CATCH_RETURN(0); } void Z3_API Z3_fixedpoint_add_cover(Z3_context c, Z3_fixedpoint d, int level, Z3_func_decl pred, Z3_ast property) { Z3_TRY; LOG_Z3_fixedpoint_add_cover(c, d, level, pred, property); RESET_ERROR_CODE(); to_fixedpoint_ref(d)->add_cover(level, to_func_decl(pred), to_expr(property)); Z3_CATCH; } Z3_string Z3_API Z3_fixedpoint_get_help(Z3_context c, Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_get_help(c, d); RESET_ERROR_CODE(); std::ostringstream buffer; param_descrs descrs; to_fixedpoint_ref(d)->collect_param_descrs(descrs); descrs.display(buffer); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } Z3_param_descrs Z3_API Z3_fixedpoint_get_param_descrs(Z3_context c, Z3_fixedpoint f) { Z3_TRY; LOG_Z3_fixedpoint_get_param_descrs(c, f); RESET_ERROR_CODE(); Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref); mk_c(c)->save_object(d); to_fixedpoint_ref(f)->collect_param_descrs(d->m_descrs); Z3_param_descrs r = of_param_descrs(d); RETURN_Z3(r); Z3_CATCH_RETURN(0); } void Z3_API Z3_fixedpoint_set_params(Z3_context c, Z3_fixedpoint d, Z3_params p) { Z3_TRY; LOG_Z3_fixedpoint_set_params(c, d, p); RESET_ERROR_CODE(); param_descrs descrs; to_fixedpoint_ref(d)->collect_param_descrs(descrs); to_params(p)->m_params.validate(descrs); to_fixedpoint_ref(d)->updt_params(to_param_ref(p)); to_fixedpoint(d)->m_params = to_param_ref(p); Z3_CATCH; } void Z3_API Z3_fixedpoint_push(Z3_context c,Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_push(c, d); RESET_ERROR_CODE(); to_fixedpoint_ref(d)->ctx().push(); Z3_CATCH; } void Z3_API Z3_fixedpoint_pop(Z3_context c,Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_pop(c, d); RESET_ERROR_CODE(); to_fixedpoint_ref(d)->ctx().pop(); Z3_CATCH; } }; z3-4.4.0/src/api/api_tactic.h0000644000175000017500000000304312540347414015456 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_tactic.h Abstract: API for creating tactics and goals. Author: Leonardo de Moura (leonardo) 2012-03-06. Revision History: --*/ #ifndef _API_TACTIC_H_ #define _API_TACTIC_H_ #include"api_goal.h" #include"tactical.h" struct Z3_tactic_ref : public api::object { tactic_ref m_tactic; virtual ~Z3_tactic_ref() {} }; struct Z3_probe_ref : public api::object { probe_ref m_probe; virtual ~Z3_probe_ref() {} }; inline Z3_tactic_ref * to_tactic(Z3_tactic g) { return reinterpret_cast(g); } inline Z3_tactic of_tactic(Z3_tactic_ref * g) { return reinterpret_cast(g); } inline tactic * to_tactic_ref(Z3_tactic g) { return g == 0 ? 0 : to_tactic(g)->m_tactic.get(); } inline Z3_probe_ref * to_probe(Z3_probe g) { return reinterpret_cast(g); } inline Z3_probe of_probe(Z3_probe_ref * g) { return reinterpret_cast(g); } inline probe * to_probe_ref(Z3_probe g) { return g == 0 ? 0 : to_probe(g)->m_probe.get(); } struct Z3_apply_result_ref : public api::object { goal_ref_buffer m_subgoals; model_converter_ref m_mc; proof_converter_ref m_pc; expr_dependency_ref m_core; Z3_apply_result_ref(ast_manager & m); virtual ~Z3_apply_result_ref() {} }; inline Z3_apply_result_ref * to_apply_result(Z3_apply_result g) { return reinterpret_cast(g); } inline Z3_apply_result of_apply_result(Z3_apply_result_ref * g) { return reinterpret_cast(g); } #endif z3-4.4.0/src/api/api_arith.cpp0000644000175000017500000001530212540347414015652 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_arith.cpp Abstract: API for arith theory Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" #include"api_util.h" #include"arith_decl_plugin.h" #include"algebraic_numbers.h" #define MK_ARITH_OP(NAME, OP) MK_NARY(NAME, mk_c(c)->get_arith_fid(), OP, SKIP) #define MK_BINARY_ARITH_OP(NAME, OP) MK_BINARY(NAME, mk_c(c)->get_arith_fid(), OP, SKIP) #define MK_ARITH_PRED(NAME, OP) MK_BINARY(NAME, mk_c(c)->get_arith_fid(), OP, SKIP) extern "C" { Z3_sort Z3_API Z3_mk_int_sort(Z3_context c) { Z3_TRY; LOG_Z3_mk_int_sort(c); RESET_ERROR_CODE(); Z3_sort r = of_sort(mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), INT_SORT)); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_mk_real_sort(Z3_context c) { Z3_TRY; LOG_Z3_mk_real_sort(c); RESET_ERROR_CODE(); Z3_sort r = of_sort(mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), REAL_SORT)); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_real(__in Z3_context c, int num, int den) { Z3_TRY; LOG_Z3_mk_real(c, num, den); RESET_ERROR_CODE(); if (den == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } sort* s = mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), REAL_SORT); ast* a = mk_c(c)->mk_numeral_core(rational(num, den), s); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(0); } MK_ARITH_OP(Z3_mk_add, OP_ADD); MK_ARITH_OP(Z3_mk_mul, OP_MUL); MK_BINARY_ARITH_OP(Z3_mk_power, OP_POWER); MK_BINARY_ARITH_OP(Z3_mk_mod, OP_MOD); MK_BINARY_ARITH_OP(Z3_mk_rem, OP_REM); Z3_ast Z3_API Z3_mk_div(Z3_context c, Z3_ast n1, Z3_ast n2) { Z3_TRY; LOG_Z3_mk_div(c, n1, n2); RESET_ERROR_CODE(); decl_kind k = OP_IDIV; sort* ty = mk_c(c)->m().get_sort(to_expr(n1)); sort* real_ty = mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), REAL_SORT); if (ty == real_ty) { k = OP_DIV; } expr * args[2] = { to_expr(n1), to_expr(n2) }; ast* a = mk_c(c)->m().mk_app(mk_c(c)->get_arith_fid(), k, 0, 0, 2, args); mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(0); } MK_ARITH_PRED(Z3_mk_lt, OP_LT); MK_ARITH_PRED(Z3_mk_gt, OP_GT); MK_ARITH_PRED(Z3_mk_le, OP_LE); MK_ARITH_PRED(Z3_mk_ge, OP_GE); MK_UNARY(Z3_mk_int2real, mk_c(c)->get_arith_fid(), OP_TO_REAL, SKIP); MK_UNARY(Z3_mk_real2int, mk_c(c)->get_arith_fid(), OP_TO_INT, SKIP); MK_UNARY(Z3_mk_is_int, mk_c(c)->get_arith_fid(), OP_IS_INT, SKIP); Z3_ast Z3_API Z3_mk_sub(Z3_context c, unsigned num_args, Z3_ast const args[]) { Z3_TRY; LOG_Z3_mk_sub(c, num_args, args); RESET_ERROR_CODE(); if (num_args == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } expr* r = to_expr(args[0]); for (unsigned i = 1; i < num_args; ++i) { expr* args1[2] = { r, to_expr(args[i]) }; r = mk_c(c)->m().mk_app(mk_c(c)->get_arith_fid(), OP_SUB, 0, 0, 2, args1); check_sorts(c, r); } mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_mk_unary_minus(Z3_context c, Z3_ast n) { Z3_TRY; LOG_Z3_mk_unary_minus(c, n); RESET_ERROR_CODE(); MK_UNARY_BODY(Z3_mk_unary_minus, mk_c(c)->get_arith_fid(), OP_UMINUS, SKIP); Z3_CATCH_RETURN(0); } Z3_bool Z3_API Z3_is_algebraic_number(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_is_algebraic_number(c, a); RESET_ERROR_CODE(); expr * e = to_expr(a); return mk_c(c)->autil().is_irrational_algebraic_numeral(e); Z3_CATCH_RETURN(Z3_FALSE); } Z3_ast Z3_API Z3_get_algebraic_number_lower(Z3_context c, Z3_ast a, unsigned precision) { Z3_TRY; LOG_Z3_get_algebraic_number_lower(c, a, precision); RESET_ERROR_CODE(); if (!Z3_is_algebraic_number(c, a)) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } expr * e = to_expr(a); algebraic_numbers::anum const & val = mk_c(c)->autil().to_irrational_algebraic_numeral(e); rational l; mk_c(c)->autil().am().get_lower(val, l, precision); expr * r = mk_c(c)->autil().mk_numeral(l, false); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_get_algebraic_number_upper(Z3_context c, Z3_ast a, unsigned precision) { Z3_TRY; LOG_Z3_get_algebraic_number_upper(c, a, precision); RESET_ERROR_CODE(); if (!Z3_is_algebraic_number(c, a)) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } expr * e = to_expr(a); algebraic_numbers::anum const & val = mk_c(c)->autil().to_irrational_algebraic_numeral(e); rational l; mk_c(c)->autil().am().get_upper(val, l, precision); expr * r = mk_c(c)->autil().mk_numeral(l, false); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_get_numerator(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_numerator(c, a); RESET_ERROR_CODE(); rational val; ast * _a = to_ast(a); if (!is_expr(_a) || !mk_c(c)->autil().is_numeral(to_expr(_a), val)) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } expr * r = mk_c(c)->autil().mk_numeral(numerator(val), true); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_get_denominator(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_denominator(c, a); RESET_ERROR_CODE(); rational val; ast * _a = to_ast(a); if (!is_expr(_a) || !mk_c(c)->autil().is_numeral(to_expr(_a), val)) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } expr * r = mk_c(c)->autil().mk_numeral(denominator(val), true); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(0); } }; z3-4.4.0/src/api/api_ast_vector.cpp0000644000175000017500000000765512540347414016730 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_ast_vector.cpp Abstract: API for creating AST vectors Author: Leonardo de Moura (leonardo) 2012-03-09. Revision History: --*/ #include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" #include"api_ast_vector.h" #include"ast_translation.h" #include"ast_smt2_pp.h" extern "C" { Z3_ast_vector Z3_API Z3_mk_ast_vector(Z3_context c) { Z3_TRY; LOG_Z3_mk_ast_vector(c); RESET_ERROR_CODE(); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, mk_c(c)->m()); mk_c(c)->save_object(v); Z3_ast_vector r = of_ast_vector(v); RETURN_Z3(r); Z3_CATCH_RETURN(0); } void Z3_API Z3_ast_vector_inc_ref(Z3_context c, Z3_ast_vector v) { Z3_TRY; LOG_Z3_ast_vector_inc_ref(c, v); RESET_ERROR_CODE(); to_ast_vector(v)->inc_ref(); Z3_CATCH; } void Z3_API Z3_ast_vector_dec_ref(Z3_context c, Z3_ast_vector v) { Z3_TRY; LOG_Z3_ast_vector_dec_ref(c, v); RESET_ERROR_CODE(); to_ast_vector(v)->dec_ref(); Z3_CATCH; } unsigned Z3_API Z3_ast_vector_size(Z3_context c, Z3_ast_vector v) { Z3_TRY; LOG_Z3_ast_vector_size(c, v); RESET_ERROR_CODE(); return to_ast_vector_ref(v).size(); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_ast_vector_get(Z3_context c, Z3_ast_vector v, unsigned i) { Z3_TRY; LOG_Z3_ast_vector_get(c, v, i); RESET_ERROR_CODE(); if (i >= to_ast_vector_ref(v).size()) { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } // Remark: Don't need to invoke save_object. ast * r = to_ast_vector_ref(v).get(i); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(0); } void Z3_API Z3_ast_vector_set(Z3_context c, Z3_ast_vector v, unsigned i, Z3_ast a) { Z3_TRY; LOG_Z3_ast_vector_set(c, v, i, a); RESET_ERROR_CODE(); if (i >= to_ast_vector_ref(v).size()) { SET_ERROR_CODE(Z3_IOB); return; } to_ast_vector_ref(v).set(i, to_ast(a)); Z3_CATCH; } void Z3_API Z3_ast_vector_resize(Z3_context c, Z3_ast_vector v, unsigned n) { Z3_TRY; LOG_Z3_ast_vector_resize(c, v, n); RESET_ERROR_CODE(); to_ast_vector_ref(v).resize(n); Z3_CATCH; } void Z3_API Z3_ast_vector_push(Z3_context c, Z3_ast_vector v, Z3_ast a) { Z3_TRY; LOG_Z3_ast_vector_push(c, v, a); RESET_ERROR_CODE(); to_ast_vector_ref(v).push_back(to_ast(a)); Z3_CATCH; } Z3_ast_vector Z3_API Z3_ast_vector_translate(Z3_context c, Z3_ast_vector v, Z3_context t) { Z3_TRY; LOG_Z3_ast_vector_translate(c, v, t); RESET_ERROR_CODE(); if (c == t) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } ast_translation translator(mk_c(c)->m(), mk_c(t)->m()); Z3_ast_vector_ref * new_v = alloc(Z3_ast_vector_ref, mk_c(t)->m()); mk_c(t)->save_object(new_v); unsigned sz = to_ast_vector_ref(v).size(); for (unsigned i = 0; i < sz; i++) { ast * new_ast = translator(to_ast_vector_ref(v).get(i)); new_v->m_ast_vector.push_back(new_ast); } RETURN_Z3(of_ast_vector(new_v)); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_ast_vector_to_string(Z3_context c, Z3_ast_vector v) { Z3_TRY; LOG_Z3_ast_vector_to_string(c, v); RESET_ERROR_CODE(); std::ostringstream buffer; buffer << "(ast-vector"; unsigned sz = to_ast_vector_ref(v).size(); for (unsigned i = 0; i < sz; i++) { buffer << "\n " << mk_ismt2_pp(to_ast_vector_ref(v).get(i), mk_c(c)->m(), 2); } buffer << ")"; return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(0); } }; z3-4.4.0/src/api/api_solver.cpp0000644000175000017500000003147612540347414016067 0ustar michaelmichael/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_solver.cpp Abstract: New solver API Author: Leonardo de Moura (leonardo) 2012-03-07. Revision History: --*/ #include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" #include"api_tactic.h" #include"api_solver.h" #include"api_model.h" #include"api_stats.h" #include"api_ast_vector.h" #include"tactic2solver.h" #include"scoped_ctrl_c.h" #include"cancel_eh.h" #include"scoped_timer.h" #include"smt_strategic_solver.h" #include"smt_solver.h" #include"smt_implied_equalities.h" extern "C" { static void init_solver_core(Z3_context c, Z3_solver _s) { Z3_solver_ref * s = to_solver(_s); bool proofs_enabled, models_enabled, unsat_core_enabled; params_ref p = s->m_params; mk_c(c)->params().get_solver_params(mk_c(c)->m(), p, proofs_enabled, models_enabled, unsat_core_enabled); s->m_solver = (*(s->m_solver_factory))(mk_c(c)->m(), p, proofs_enabled, models_enabled, unsat_core_enabled, s->m_logic); param_descrs r; s->m_solver->collect_param_descrs(r); context_params::collect_solver_param_descrs(r); p.validate(r); s->m_solver->updt_params(p); } static void init_solver(Z3_context c, Z3_solver s) { if (to_solver(s)->m_solver.get() == 0) init_solver_core(c, s); } Z3_solver Z3_API Z3_mk_simple_solver(Z3_context c) { Z3_TRY; LOG_Z3_mk_simple_solver(c); RESET_ERROR_CODE(); Z3_solver_ref * s = alloc(Z3_solver_ref, mk_smt_solver_factory()); mk_c(c)->save_object(s); Z3_solver r = of_solver(s); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_solver Z3_API Z3_mk_solver(Z3_context c) { Z3_TRY; LOG_Z3_mk_solver(c); RESET_ERROR_CODE(); Z3_solver_ref * s = alloc(Z3_solver_ref, mk_smt_strategic_solver_factory()); mk_c(c)->save_object(s); Z3_solver r = of_solver(s); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_solver Z3_API Z3_mk_solver_for_logic(Z3_context c, Z3_symbol logic) { Z3_TRY; LOG_Z3_mk_solver_for_logic(c, logic); RESET_ERROR_CODE(); Z3_solver_ref * s = alloc(Z3_solver_ref, mk_smt_strategic_solver_factory(to_symbol(logic))); mk_c(c)->save_object(s); Z3_solver r = of_solver(s); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_solver Z3_API Z3_mk_solver_from_tactic(Z3_context c, Z3_tactic t) { Z3_TRY; LOG_Z3_mk_solver_from_tactic(c, t); RESET_ERROR_CODE(); Z3_solver_ref * s = alloc(Z3_solver_ref, mk_tactic2solver_factory(to_tactic_ref(t))); mk_c(c)->save_object(s); Z3_solver r = of_solver(s); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_solver_get_help(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_help(c, s); RESET_ERROR_CODE(); std::ostringstream buffer; param_descrs descrs; bool initialized = to_solver(s)->m_solver.get() != 0; if (!initialized) init_solver(c, s); to_solver_ref(s)->collect_param_descrs(descrs); context_params::collect_solver_param_descrs(descrs); if (!initialized) to_solver(s)->m_solver = 0; descrs.display(buffer); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } Z3_param_descrs Z3_API Z3_solver_get_param_descrs(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_param_descrs(c, s); RESET_ERROR_CODE(); Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref); mk_c(c)->save_object(d); bool initialized = to_solver(s)->m_solver.get() != 0; if (!initialized) init_solver(c, s); to_solver_ref(s)->collect_param_descrs(d->m_descrs); context_params::collect_solver_param_descrs(d->m_descrs); if (!initialized) to_solver(s)->m_solver = 0; Z3_param_descrs r = of_param_descrs(d); RETURN_Z3(r); Z3_CATCH_RETURN(0); } void Z3_API Z3_solver_set_params(Z3_context c, Z3_solver s, Z3_params p) { Z3_TRY; LOG_Z3_solver_set_params(c, s, p); RESET_ERROR_CODE(); if (to_solver(s)->m_solver) { bool old_model = to_solver(s)->m_params.get_bool("model", true); bool new_model = to_param_ref(p).get_bool("model", true); if (old_model != new_model) to_solver_ref(s)->set_produce_models(new_model); param_descrs r; to_solver_ref(s)->collect_param_descrs(r); context_params::collect_solver_param_descrs(r); to_param_ref(p).validate(r); to_solver_ref(s)->updt_params(to_param_ref(p)); } to_solver(s)->m_params.append(to_param_ref(p)); Z3_CATCH; } void Z3_API Z3_solver_inc_ref(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_inc_ref(c, s); RESET_ERROR_CODE(); to_solver(s)->inc_ref(); Z3_CATCH; } void Z3_API Z3_solver_dec_ref(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_dec_ref(c, s); RESET_ERROR_CODE(); to_solver(s)->dec_ref(); Z3_CATCH; } void Z3_API Z3_solver_push(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_push(c, s); RESET_ERROR_CODE(); init_solver(c, s); to_solver_ref(s)->push(); Z3_CATCH; } void Z3_API Z3_solver_pop(Z3_context c, Z3_solver s, unsigned n) { Z3_TRY; LOG_Z3_solver_pop(c, s, n); RESET_ERROR_CODE(); init_solver(c, s); if (n > to_solver_ref(s)->get_scope_level()) { SET_ERROR_CODE(Z3_IOB); return; } if (n > 0) to_solver_ref(s)->pop(n); Z3_CATCH; } void Z3_API Z3_solver_reset(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_reset(c, s); RESET_ERROR_CODE(); to_solver(s)->m_solver = 0; Z3_CATCH; } unsigned Z3_API Z3_solver_get_num_scopes(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_num_scopes(c, s); RESET_ERROR_CODE(); init_solver(c, s); return to_solver_ref(s)->get_scope_level(); Z3_CATCH_RETURN(0); } void Z3_API Z3_solver_assert(Z3_context c, Z3_solver s, Z3_ast a) { Z3_TRY; LOG_Z3_solver_assert(c, s, a); RESET_ERROR_CODE(); init_solver(c, s); CHECK_FORMULA(a,); to_solver_ref(s)->assert_expr(to_expr(a)); Z3_CATCH; } void Z3_API Z3_solver_assert_and_track(Z3_context c, Z3_solver s, Z3_ast a, Z3_ast p) { Z3_TRY; LOG_Z3_solver_assert_and_track(c, s, a, p); RESET_ERROR_CODE(); init_solver(c, s); CHECK_FORMULA(a,); CHECK_FORMULA(p,); to_solver_ref(s)->assert_expr(to_expr(a), to_expr(p)); Z3_CATCH; } Z3_ast_vector Z3_API Z3_solver_get_assertions(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_assertions(c, s); RESET_ERROR_CODE(); init_solver(c, s); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, mk_c(c)->m()); mk_c(c)->save_object(v); unsigned sz = to_solver_ref(s)->get_num_assertions(); for (unsigned i = 0; i < sz; i++) { v->m_ast_vector.push_back(to_solver_ref(s)->get_assertion(i)); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(0); } static Z3_lbool _solver_check(Z3_context c, Z3_solver s, unsigned num_assumptions, Z3_ast const assumptions[]) { for (unsigned i = 0; i < num_assumptions; i++) { if (!is_expr(to_ast(assumptions[i]))) { SET_ERROR_CODE(Z3_INVALID_ARG); return Z3_L_UNDEF; } } expr * const * _assumptions = to_exprs(assumptions); unsigned timeout = to_solver(s)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); bool use_ctrl_c = to_solver(s)->m_params.get_bool("ctrl_c", false); cancel_eh eh(*to_solver_ref(s)); api::context::set_interruptable si(*(mk_c(c)), eh); lbool result; { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); scoped_timer timer(timeout, &eh); try { result = to_solver_ref(s)->check_sat(num_assumptions, _assumptions); } catch (z3_exception & ex) { mk_c(c)->handle_exception(ex); return Z3_L_UNDEF; } } return static_cast(result); } Z3_lbool Z3_API Z3_solver_check(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_check(c, s); RESET_ERROR_CODE(); init_solver(c, s); return _solver_check(c, s, 0, 0); Z3_CATCH_RETURN(Z3_L_UNDEF); } Z3_lbool Z3_API Z3_solver_check_assumptions(Z3_context c, Z3_solver s, unsigned num_assumptions, Z3_ast const assumptions[]) { Z3_TRY; LOG_Z3_solver_check_assumptions(c, s, num_assumptions, assumptions); RESET_ERROR_CODE(); init_solver(c, s); return _solver_check(c, s, num_assumptions, assumptions); Z3_CATCH_RETURN(Z3_L_UNDEF); } Z3_model Z3_API Z3_solver_get_model(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_model(c, s); RESET_ERROR_CODE(); init_solver(c, s); model_ref _m; to_solver_ref(s)->get_model(_m); if (!_m) { SET_ERROR_CODE(Z3_INVALID_USAGE); RETURN_Z3(0); } Z3_model_ref * m_ref = alloc(Z3_model_ref); m_ref->m_model = _m; mk_c(c)->save_object(m_ref); RETURN_Z3(of_model(m_ref)); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_solver_get_proof(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_proof(c, s); RESET_ERROR_CODE(); init_solver(c, s); proof * p = to_solver_ref(s)->get_proof(); if (!p) { SET_ERROR_CODE(Z3_INVALID_USAGE); RETURN_Z3(0); } mk_c(c)->save_ast_trail(p); RETURN_Z3(of_ast(p)); Z3_CATCH_RETURN(0); } Z3_ast_vector Z3_API Z3_solver_get_unsat_core(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_unsat_core(c, s); RESET_ERROR_CODE(); init_solver(c, s); ptr_vector core; to_solver_ref(s)->get_unsat_core(core); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, mk_c(c)->m()); mk_c(c)->save_object(v); for (unsigned i = 0; i < core.size(); i++) { v->m_ast_vector.push_back(core[i]); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_solver_get_reason_unknown(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_reason_unknown(c, s); RESET_ERROR_CODE(); init_solver(c, s); return mk_c(c)->mk_external_string(to_solver_ref(s)->reason_unknown()); Z3_CATCH_RETURN(""); } Z3_stats Z3_API Z3_solver_get_statistics(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_statistics(c, s); RESET_ERROR_CODE(); init_solver(c, s); Z3_stats_ref * st = alloc(Z3_stats_ref); to_solver_ref(s)->collect_statistics(st->m_stats); unsigned long long max_mem = memory::get_max_used_memory(); unsigned long long mem = memory::get_allocation_size(); st->m_stats.update("max memory", static_cast(max_mem)/(1024.0*1024.0)); st->m_stats.update("memory", static_cast(mem)/(1024.0*1024.0)); mk_c(c)->save_object(st); Z3_stats r = of_stats(st); RETURN_Z3(r); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_solver_to_string(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_to_string(c, s); RESET_ERROR_CODE(); init_solver(c, s); std::ostringstream buffer; to_solver_ref(s)->display(buffer); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } Z3_lbool Z3_API Z3_get_implied_equalities(Z3_context c, Z3_solver s, unsigned num_terms, Z3_ast const terms[], unsigned class_ids[]) { Z3_TRY; LOG_Z3_get_implied_equalities(c, s, num_terms, terms, class_ids); ast_manager& m = mk_c(c)->m(); RESET_ERROR_CODE(); CHECK_SEARCHING(c); init_solver(c, s); lbool result = smt::implied_equalities(m, *to_solver_ref(s), num_terms, to_exprs(terms), class_ids); return static_cast(result); Z3_CATCH_RETURN(Z3_L_UNDEF); } }; z3-4.4.0/src/api/java/0000755000175000017500000000000012540347414014126 5ustar michaelmichaelz3-4.4.0/src/api/java/AlgebraicNum.java0000644000175000017500000000320312540347414017320 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: AlgebraicNum.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Algebraic numbers **/ public class AlgebraicNum extends ArithExpr { /** * Return a upper bound for a given real algebraic number. The interval * isolating the number is smaller than 1/10^{@code precision}. * * @see Expr#isAlgebraicNumber * @param precision the precision of the result * * @return A numeral Expr of sort Real * @throws Z3Exception on error **/ public RatNum toUpper(int precision) { return new RatNum(getContext(), Native.getAlgebraicNumberUpper(getContext() .nCtx(), getNativeObject(), precision)); } /** * Return a lower bound for the given real algebraic number. The interval * isolating the number is smaller than 1/10^{@code precision}. * * @see Expr#isAlgebraicNumber * @param precision precision * * @return A numeral Expr of sort Real * @throws Z3Exception on error **/ public RatNum toLower(int precision) { return new RatNum(getContext(), Native.getAlgebraicNumberLower(getContext() .nCtx(), getNativeObject(), precision)); } /** * Returns a string representation in decimal notation. * Remarks: The result has at most {@code precision} decimal places. * @param precision precision * @return String * @throws Z3Exception on error **/ public String toDecimal(int precision) { return Native.getNumeralDecimalString(getContext().nCtx(), getNativeObject(), precision); } AlgebraicNum(Context ctx, long obj) { super(ctx, obj); } } z3-4.4.0/src/api/java/DatatypeSort.java0000644000175000017500000000514612540347414017422 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: DatatypeSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Datatype sorts. **/ public class DatatypeSort extends Sort { /** * The number of constructors of the datatype sort. * @throws Z3Exception on error * @return an int **/ public int getNumConstructors() { return Native.getDatatypeSortNumConstructors(getContext().nCtx(), getNativeObject()); } /** * The constructors. * * @throws Z3Exception * @throws Z3Exception on error **/ public FuncDecl[] getConstructors() { int n = getNumConstructors(); FuncDecl[] res = new FuncDecl[n]; for (int i = 0; i < n; i++) res[i] = new FuncDecl(getContext(), Native.getDatatypeSortConstructor( getContext().nCtx(), getNativeObject(), i)); return res; } /** * The recognizers. * * @throws Z3Exception * @throws Z3Exception on error **/ public FuncDecl[] getRecognizers() { int n = getNumConstructors(); FuncDecl[] res = new FuncDecl[n]; for (int i = 0; i < n; i++) res[i] = new FuncDecl(getContext(), Native.getDatatypeSortRecognizer( getContext().nCtx(), getNativeObject(), i)); return res; } /** * The constructor accessors. * * @throws Z3Exception * @throws Z3Exception on error **/ public FuncDecl[][] getAccessors() { int n = getNumConstructors(); FuncDecl[][] res = new FuncDecl[n][]; for (int i = 0; i < n; i++) { FuncDecl fd = new FuncDecl(getContext(), Native.getDatatypeSortConstructor(getContext().nCtx(), getNativeObject(), i)); int ds = fd.getDomainSize(); FuncDecl[] tmp = new FuncDecl[ds]; for (int j = 0; j < ds; j++) tmp[j] = new FuncDecl(getContext(), Native.getDatatypeSortConstructorAccessor(getContext() .nCtx(), getNativeObject(), i, j)); res[i] = tmp; } return res; } DatatypeSort(Context ctx, long obj) { super(ctx, obj); } DatatypeSort(Context ctx, Symbol name, Constructor[] constructors) { super(ctx, Native.mkDatatype(ctx.nCtx(), name.getNativeObject(), (int) constructors.length, arrayToNative(constructors))); } }; z3-4.4.0/src/api/java/Log.java0000644000175000017500000000265212540347414015517 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Log.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Interaction logging for Z3. * Remarks: Note that this is a global, static log * and if multiple Context objects are created, it logs the interaction with all * of them. **/ public final class Log { private static boolean m_is_open = false; /** * Open an interaction log file. * @param filename the name of the file to open * * @return True if opening the log file succeeds, false otherwise. **/ public static boolean open(String filename) { m_is_open = true; return Native.openLog(filename) == 1; } /** * Closes the interaction log. **/ public static void close() { m_is_open = false; Native.closeLog(); } /** * Appends the user-provided string {@code s} to the interaction * log. * @throws Z3Exception **/ public static void append(String s) { if (!m_is_open) throw new Z3Exception("Log cannot be closed."); Native.appendLog(s); } /** * Checks whether the interaction log is opened. * * @return True if the interaction log is open, false otherwise. **/ public static boolean isOpen() { return m_is_open; } } z3-4.4.0/src/api/java/GoalDecRefQueue.java0000644000175000017500000000145612540347414017737 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: GoalDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class GoalDecRefQueue extends IDecRefQueue { public GoalDecRefQueue() { super(); } public GoalDecRefQueue(int move_limit) { super(move_limit); } protected void incRef(Context ctx, long obj) { try { Native.goalIncRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } protected void decRef(Context ctx, long obj) { try { Native.goalDecRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } }; z3-4.4.0/src/api/java/ModelDecRefQueue.java0000644000175000017500000000146412540347414020114 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ModelDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class ModelDecRefQueue extends IDecRefQueue { public ModelDecRefQueue() { super(); } public ModelDecRefQueue(int move_limit) { super(move_limit); } protected void incRef(Context ctx, long obj) { try { Native.modelIncRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } protected void decRef(Context ctx, long obj) { try { Native.modelDecRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } }; z3-4.4.0/src/api/java/BitVecNum.java0000644000175000017500000000303612540347414016627 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: BitVecNum.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import java.math.BigInteger; /** * Bit-vector numerals **/ public class BitVecNum extends BitVecExpr { /** * Retrieve the int value. * * @throws Z3Exception **/ public int getInt() { Native.IntPtr res = new Native.IntPtr(); if (Native.getNumeralInt(getContext().nCtx(), getNativeObject(), res) ^ true) throw new Z3Exception("Numeral is not an int"); return res.value; } /** * Retrieve the 64-bit int value. * * @throws Z3Exception **/ public long getLong() { Native.LongPtr res = new Native.LongPtr(); if (Native.getNumeralInt64(getContext().nCtx(), getNativeObject(), res) ^ true) throw new Z3Exception("Numeral is not a long"); return res.value; } /** * Retrieve the BigInteger value. **/ public BigInteger getBigInteger() { return new BigInteger(this.toString()); } /** * Returns a string representation of the numeral. **/ public String toString() { try { return Native.getNumeralString(getContext().nCtx(), getNativeObject()); } catch (Z3Exception e) { return "Z3Exception: " + e.getMessage(); } } BitVecNum(Context ctx, long obj) { super(ctx, obj); } } z3-4.4.0/src/api/java/RealSort.java0000644000175000017500000000067112540347414016530 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: RealSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * A real sort **/ public class RealSort extends ArithSort { RealSort(Context ctx, long obj) { super(ctx, obj); } RealSort(Context ctx) { super(ctx, Native.mkRealSort(ctx.nCtx())); } } z3-4.4.0/src/api/java/FPRMExpr.java0000644000175000017500000000056312540347414016400 0ustar michaelmichael/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPRMExpr.java Abstract: Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ package com.microsoft.z3; /** * FloatingPoint RoundingMode Expressions */ public class FPRMExpr extends Expr { public FPRMExpr(Context ctx, long obj) { super(ctx, obj); } } z3-4.4.0/src/api/java/ProbeDecRefQueue.java0000644000175000017500000000146412540347414020123 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ProbeDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class ProbeDecRefQueue extends IDecRefQueue { public ProbeDecRefQueue() { super(); } public ProbeDecRefQueue(int move_limit) { super(move_limit); } protected void incRef(Context ctx, long obj) { try { Native.probeIncRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } protected void decRef(Context ctx, long obj) { try { Native.probeDecRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } }; z3-4.4.0/src/api/java/BitVecExpr.java0000644000175000017500000000122412540347414017003 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: BitVecExpr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Bit-vector expressions **/ public class BitVecExpr extends Expr { /** * The size of the sort of a bit-vector term. * @throws Z3Exception * @throws Z3Exception on error * @return an int **/ public int getSortSize() { return ((BitVecSort) getSort()).getSize(); } /** * Constructor for BitVecExpr **/ BitVecExpr(Context ctx, long obj) { super(ctx, obj); } } z3-4.4.0/src/api/java/RealExpr.java0000644000175000017500000000062512540347414016516 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: RealExpr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Real expressions **/ public class RealExpr extends ArithExpr { /** * Constructor for RealExpr **/ RealExpr(Context ctx, long obj) { super(ctx, obj); } } z3-4.4.0/src/api/java/Symbol.java0000644000175000017500000000400512540347414016235 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Symbol.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_symbol_kind; /** * Symbols are used to name several term and type constructors. **/ public class Symbol extends Z3Object { /** * The kind of the symbol (int or string) **/ protected Z3_symbol_kind getKind() { return Z3_symbol_kind.fromInt(Native.getSymbolKind(getContext().nCtx(), getNativeObject())); } /** * Indicates whether the symbol is of Int kind **/ public boolean isIntSymbol() { return getKind() == Z3_symbol_kind.Z3_INT_SYMBOL; } /** * Indicates whether the symbol is of string kind. **/ public boolean isStringSymbol() { return getKind() == Z3_symbol_kind.Z3_STRING_SYMBOL; } /** * A string representation of the symbol. **/ public String toString() { try { if (isIntSymbol()) return Integer.toString(((IntSymbol) this).getInt()); else if (isStringSymbol()) return ((StringSymbol) this).getString(); else return new String( "Z3Exception: Unknown symbol kind encountered."); } catch (Z3Exception ex) { return new String("Z3Exception: " + ex.getMessage()); } } /** * Symbol constructor **/ protected Symbol(Context ctx, long obj) { super(ctx, obj); } static Symbol create(Context ctx, long obj) { switch (Z3_symbol_kind.fromInt(Native.getSymbolKind(ctx.nCtx(), obj))) { case Z3_INT_SYMBOL: return new IntSymbol(ctx, obj); case Z3_STRING_SYMBOL: return new StringSymbol(ctx, obj); default: throw new Z3Exception("Unknown symbol kind encountered"); } } } z3-4.4.0/src/api/java/IntExpr.java0000644000175000017500000000066412540347414016370 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: IntExpr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Int expressions **/ public class IntExpr extends ArithExpr { /** * Constructor for IntExpr * @throws Z3Exception on error **/ IntExpr(Context ctx, long obj) { super(ctx, obj); } } z3-4.4.0/src/api/java/RelationSort.java0000644000175000017500000000203512540347414017416 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: RelationSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Relation sorts. **/ public class RelationSort extends Sort { /** * The arity of the relation sort. **/ public int getArity() { return Native.getRelationArity(getContext().nCtx(), getNativeObject()); } /** * The sorts of the columns of the relation sort. * @throws Z3Exception **/ public Sort[] getColumnSorts() { if (m_columnSorts != null) return m_columnSorts; int n = getArity(); Sort[] res = new Sort[n]; for (int i = 0; i < n; i++) res[i] = Sort.create(getContext(), Native.getRelationColumn(getContext() .nCtx(), getNativeObject(), i)); return res; } private Sort[] m_columnSorts = null; RelationSort(Context ctx, long obj) { super(ctx, obj); } } z3-4.4.0/src/api/java/Fixedpoint.java0000644000175000017500000002355612540347414017115 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Fixedpoint.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_lbool; /** * Object for managing fixedpoints **/ public class Fixedpoint extends Z3Object { /** * A string that describes all available fixedpoint solver parameters. **/ public String getHelp() { return Native.fixedpointGetHelp(getContext().nCtx(), getNativeObject()); } /** * Sets the fixedpoint solver parameters. * * @throws Z3Exception **/ public void setParameters(Params value) { getContext().checkContextMatch(value); Native.fixedpointSetParams(getContext().nCtx(), getNativeObject(), value.getNativeObject()); } /** * Retrieves parameter descriptions for Fixedpoint solver. * * @throws Z3Exception **/ public ParamDescrs getParameterDescriptions() { return new ParamDescrs(getContext(), Native.fixedpointGetParamDescrs( getContext().nCtx(), getNativeObject())); } /** * Assert a constraint (or multiple) into the fixedpoint solver. * * @throws Z3Exception **/ public void add(BoolExpr ... constraints) { getContext().checkContextMatch(constraints); for (BoolExpr a : constraints) { Native.fixedpointAssert(getContext().nCtx(), getNativeObject(), a.getNativeObject()); } } /** * Register predicate as recursive relation. * * @throws Z3Exception **/ public void registerRelation(FuncDecl f) { getContext().checkContextMatch(f); Native.fixedpointRegisterRelation(getContext().nCtx(), getNativeObject(), f.getNativeObject()); } /** * Add rule into the fixedpoint solver. * * @throws Z3Exception **/ public void addRule(BoolExpr rule, Symbol name) { getContext().checkContextMatch(rule); Native.fixedpointAddRule(getContext().nCtx(), getNativeObject(), rule.getNativeObject(), AST.getNativeObject(name)); } /** * Add table fact to the fixedpoint solver. * * @throws Z3Exception **/ public void addFact(FuncDecl pred, int ... args) { getContext().checkContextMatch(pred); Native.fixedpointAddFact(getContext().nCtx(), getNativeObject(), pred.getNativeObject(), (int) args.length, args); } /** * Query the fixedpoint solver. A query is a conjunction of constraints. The * constraints may include the recursively defined relations. The query is * satisfiable if there is an instance of the query variables and a * derivation for it. The query is unsatisfiable if there are no derivations * satisfying the query variables. * * @throws Z3Exception **/ public Status query(BoolExpr query) { getContext().checkContextMatch(query); Z3_lbool r = Z3_lbool.fromInt(Native.fixedpointQuery(getContext().nCtx(), getNativeObject(), query.getNativeObject())); switch (r) { case Z3_L_TRUE: return Status.SATISFIABLE; case Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } /** * Query the fixedpoint solver. A query is an array of relations. The query * is satisfiable if there is an instance of some relation that is * non-empty. The query is unsatisfiable if there are no derivations * satisfying any of the relations. * * @throws Z3Exception **/ public Status query(FuncDecl[] relations) { getContext().checkContextMatch(relations); Z3_lbool r = Z3_lbool.fromInt(Native.fixedpointQueryRelations(getContext() .nCtx(), getNativeObject(), AST.arrayLength(relations), AST .arrayToNative(relations))); switch (r) { case Z3_L_TRUE: return Status.SATISFIABLE; case Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } /** * Creates a backtracking point. * @see pop **/ public void push() { Native.fixedpointPush(getContext().nCtx(), getNativeObject()); } /** * Backtrack one backtracking point. * Remarks: Note that an exception is thrown if {@code pop} * is called without a corresponding {@code push} * * @see push **/ public void pop() { Native.fixedpointPop(getContext().nCtx(), getNativeObject()); } /** * Update named rule into in the fixedpoint solver. * * @throws Z3Exception **/ public void updateRule(BoolExpr rule, Symbol name) { getContext().checkContextMatch(rule); Native.fixedpointUpdateRule(getContext().nCtx(), getNativeObject(), rule.getNativeObject(), AST.getNativeObject(name)); } /** * Retrieve satisfying instance or instances of solver, or definitions for * the recursive predicates that show unsatisfiability. * * @throws Z3Exception **/ public Expr getAnswer() { long ans = Native.fixedpointGetAnswer(getContext().nCtx(), getNativeObject()); return (ans == 0) ? null : Expr.create(getContext(), ans); } /** * Retrieve explanation why fixedpoint engine returned status Unknown. **/ public String getReasonUnknown() { return Native.fixedpointGetReasonUnknown(getContext().nCtx(), getNativeObject()); } /** * Retrieve the number of levels explored for a given predicate. **/ public int getNumLevels(FuncDecl predicate) { return Native.fixedpointGetNumLevels(getContext().nCtx(), getNativeObject(), predicate.getNativeObject()); } /** * Retrieve the cover of a predicate. * * @throws Z3Exception **/ public Expr getCoverDelta(int level, FuncDecl predicate) { long res = Native.fixedpointGetCoverDelta(getContext().nCtx(), getNativeObject(), level, predicate.getNativeObject()); return (res == 0) ? null : Expr.create(getContext(), res); } /** * Add property about the predicate. The property is added * at level. **/ public void addCover(int level, FuncDecl predicate, Expr property) { Native.fixedpointAddCover(getContext().nCtx(), getNativeObject(), level, predicate.getNativeObject(), property.getNativeObject()); } /** * Retrieve internal string representation of fixedpoint object. **/ public String toString() { try { return Native.fixedpointToString(getContext().nCtx(), getNativeObject(), 0, null); } catch (Z3Exception e) { return "Z3Exception: " + e.getMessage(); } } /** * Instrument the Datalog engine on which table representation to use for * recursive predicate. **/ public void setPredicateRepresentation(FuncDecl f, Symbol[] kinds) { Native.fixedpointSetPredicateRepresentation(getContext().nCtx(), getNativeObject(), f.getNativeObject(), AST.arrayLength(kinds), Symbol.arrayToNative(kinds)); } /** * Convert benchmark given as set of axioms, rules and queries to a string. **/ public String toString(BoolExpr[] queries) { return Native.fixedpointToString(getContext().nCtx(), getNativeObject(), AST.arrayLength(queries), AST.arrayToNative(queries)); } /** * Retrieve set of rules added to fixedpoint context. * * @throws Z3Exception **/ public BoolExpr[] getRules() { ASTVector v = new ASTVector(getContext(), Native.fixedpointGetRules(getContext().nCtx(), getNativeObject())); return v.ToBoolExprArray(); } /** * Retrieve set of assertions added to fixedpoint context. * * @throws Z3Exception **/ public BoolExpr[] getAssertions() { ASTVector v = new ASTVector(getContext(), Native.fixedpointGetAssertions(getContext().nCtx(), getNativeObject())); return v.ToBoolExprArray(); } /** * Fixedpoint statistics. * * @throws Z3Exception **/ public Statistics getStatistics() { return new Statistics(getContext(), Native.fixedpointGetStatistics( getContext().nCtx(), getNativeObject())); } /** * Parse an SMT-LIB2 file with fixedpoint rules. * Add the rules to the current fixedpoint context. * Return the set of queries in the file. **/ public BoolExpr[] ParseFile(String file) { ASTVector av = new ASTVector(getContext(), Native.fixedpointFromFile(getContext().nCtx(), getNativeObject(), file)); return av.ToBoolExprArray(); } /** * Parse an SMT-LIB2 string with fixedpoint rules. * Add the rules to the current fixedpoint context. * Return the set of queries in the file. **/ public BoolExpr[] ParseString(String s) { ASTVector av = new ASTVector(getContext(), Native.fixedpointFromString(getContext().nCtx(), getNativeObject(), s)); return av.ToBoolExprArray(); } Fixedpoint(Context ctx, long obj) throws Z3Exception { super(ctx, obj); } Fixedpoint(Context ctx) { super(ctx, Native.mkFixedpoint(ctx.nCtx())); } void incRef(long o) { getContext().getFixedpointDRQ().incAndClear(getContext(), o); super.incRef(o); } void decRef(long o) { getContext().getFixedpointDRQ().add(o); super.decRef(o); } } z3-4.4.0/src/api/java/ASTVector.java0000644000175000017500000001412212540347414016603 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ASTVector.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Vectors of ASTs. **/ public class ASTVector extends Z3Object { /** * The size of the vector **/ public int size() { return Native.astVectorSize(getContext().nCtx(), getNativeObject()); } /** * Retrieves the i-th object in the vector. * Remarks: May throw an {@code IndexOutOfBoundsException} when * {@code i} is out of range. * @param i Index * * @return An AST * @throws Z3Exception **/ public AST get(int i) { return new AST(getContext(), Native.astVectorGet(getContext().nCtx(), getNativeObject(), i)); } public void set(int i, AST value) { Native.astVectorSet(getContext().nCtx(), getNativeObject(), i, value.getNativeObject()); } /** * Resize the vector to {@code newSize}. * @param newSize The new size of the vector. **/ public void resize(int newSize) { Native.astVectorResize(getContext().nCtx(), getNativeObject(), newSize); } /** * Add the AST {@code a} to the back of the vector. The size is * increased by 1. * @param a An AST **/ public void push(AST a) { Native.astVectorPush(getContext().nCtx(), getNativeObject(), a.getNativeObject()); } /** * Translates all ASTs in the vector to {@code ctx}. * @param ctx A context * * @return A new ASTVector * @throws Z3Exception **/ public ASTVector translate(Context ctx) { return new ASTVector(getContext(), Native.astVectorTranslate(getContext() .nCtx(), getNativeObject(), ctx.nCtx())); } /** * Retrieves a string representation of the vector. **/ public String toString() { try { return Native.astVectorToString(getContext().nCtx(), getNativeObject()); } catch (Z3Exception e) { return "Z3Exception: " + e.getMessage(); } } ASTVector(Context ctx, long obj) { super(ctx, obj); } ASTVector(Context ctx) { super(ctx, Native.mkAstVector(ctx.nCtx())); } void incRef(long o) { getContext().getASTVectorDRQ().incAndClear(getContext(), o); super.incRef(o); } void decRef(long o) { getContext().getASTVectorDRQ().add(o); super.decRef(o); } /** * Translates the AST vector into an AST[] * */ public AST[] ToArray() { int n = size(); AST[] res = new AST[n]; for (int i = 0; i < n; i++) res[i] = AST.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an Expr[] * */ public Expr[] ToExprArray() { int n = size(); Expr[] res = new Expr[n]; for (int i = 0; i < n; i++) res[i] = Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an BoolExpr[] * */ public BoolExpr[] ToBoolExprArray() { int n = size(); BoolExpr[] res = new BoolExpr[n]; for (int i = 0; i < n; i++) res[i] = (BoolExpr) Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an BitVecExpr[] * */ public BitVecExpr[] ToBitVecExprArray() { int n = size(); BitVecExpr[] res = new BitVecExpr[n]; for (int i = 0; i < n; i++) res[i] = (BitVecExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an ArithExpr[] * */ public ArithExpr[] ToArithExprExprArray() { int n = size(); ArithExpr[] res = new ArithExpr[n]; for (int i = 0; i < n; i++) res[i] = (ArithExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an ArrayExpr[] * */ public ArrayExpr[] ToArrayExprArray() { int n = size(); ArrayExpr[] res = new ArrayExpr[n]; for (int i = 0; i < n; i++) res[i] = (ArrayExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an DatatypeExpr[] * */ public DatatypeExpr[] ToDatatypeExprArray() { int n = size(); DatatypeExpr[] res = new DatatypeExpr[n]; for (int i = 0; i < n; i++) res[i] = (DatatypeExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an FPExpr[] * */ public FPExpr[] ToFPExprArray() { int n = size(); FPExpr[] res = new FPExpr[n]; for (int i = 0; i < n; i++) res[i] = (FPExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an FPRMExpr[] * */ public FPRMExpr[] ToFPRMExprArray() { int n = size(); FPRMExpr[] res = new FPRMExpr[n]; for (int i = 0; i < n; i++) res[i] = (FPRMExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an IntExpr[] * */ public IntExpr[] ToIntExprArray() { int n = size(); IntExpr[] res = new IntExpr[n]; for (int i = 0; i < n; i++) res[i] = (IntExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an RealExpr[] * */ public RealExpr[] ToRealExprArray() { int n = size(); RealExpr[] res = new RealExpr[n]; for (int i = 0; i < n; i++) res[i] = (RealExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } }z3-4.4.0/src/api/java/Pattern.java0000644000175000017500000000251512540347414016411 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Pattern.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Patterns comprise a list of terms. The list should be non-empty. If the list * comprises of more than one term, it is also called a multi-pattern. **/ public class Pattern extends AST { /** * The number of terms in the pattern. **/ public int getNumTerms() { return Native.getPatternNumTerms(getContext().nCtx(), getNativeObject()); } /** * The terms in the pattern. * * @throws Z3Exception **/ public Expr[] getTerms() { int n = getNumTerms(); Expr[] res = new Expr[n]; for (int i = 0; i < n; i++) res[i] = Expr.create(getContext(), Native.getPattern(getContext().nCtx(), getNativeObject(), i)); return res; } /** * A string representation of the pattern. **/ public String toString() { try { return Native.patternToString(getContext().nCtx(), getNativeObject()); } catch (Z3Exception e) { return "Z3Exception: " + e.getMessage(); } } Pattern(Context ctx, long obj) { super(ctx, obj); } } z3-4.4.0/src/api/java/StringSymbol.java0000644000175000017500000000204512540347414017426 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: StringSymbol.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_symbol_kind; /** * Named symbols **/ public class StringSymbol extends Symbol { /** * The string value of the symbol. * Remarks: Throws an exception if the * symbol is not of string kind. **/ public String getString() { return Native.getSymbolString(getContext().nCtx(), getNativeObject()); } StringSymbol(Context ctx, long obj) { super(ctx, obj); } StringSymbol(Context ctx, String s) { super(ctx, Native.mkStringSymbol(ctx.nCtx(), s)); } void checkNativeObject(long obj) { if (Native.getSymbolKind(getContext().nCtx(), obj) != Z3_symbol_kind.Z3_STRING_SYMBOL .toInt()) throw new Z3Exception("Symbol is not of String kind"); super.checkNativeObject(obj); } } z3-4.4.0/src/api/java/TupleSort.java0000644000175000017500000000277312540347414016743 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: TupleSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Tuple sorts. **/ public class TupleSort extends Sort { /** * The constructor function of the tuple. * @throws Z3Exception **/ public FuncDecl mkDecl() { return new FuncDecl(getContext(), Native.getTupleSortMkDecl(getContext() .nCtx(), getNativeObject())); } /** * The number of fields in the tuple. **/ public int getNumFields() { return Native.getTupleSortNumFields(getContext().nCtx(), getNativeObject()); } /** * The field declarations. * @throws Z3Exception **/ public FuncDecl[] getFieldDecls() { int n = getNumFields(); FuncDecl[] res = new FuncDecl[n]; for (int i = 0; i < n; i++) res[i] = new FuncDecl(getContext(), Native.getTupleSortFieldDecl( getContext().nCtx(), getNativeObject(), i)); return res; } TupleSort(Context ctx, Symbol name, int numFields, Symbol[] fieldNames, Sort[] fieldSorts) { super(ctx, 0); Native.LongPtr t = new Native.LongPtr(); setNativeObject(Native.mkTupleSort(ctx.nCtx(), name.getNativeObject(), numFields, Symbol.arrayToNative(fieldNames), AST.arrayToNative(fieldSorts), t, new long[numFields])); } }; z3-4.4.0/src/api/java/StatisticsDecRefQueue.java0000644000175000017500000000151012540347414021176 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: StatisticsDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class StatisticsDecRefQueue extends IDecRefQueue { public StatisticsDecRefQueue() { super(); } public StatisticsDecRefQueue(int move_limit) { super(move_limit); } protected void incRef(Context ctx, long obj) { try { Native.statsIncRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } protected void decRef(Context ctx, long obj) { try { Native.statsDecRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } }; z3-4.4.0/src/api/java/Goal.java0000644000175000017500000001445412540347414015663 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Goal.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_goal_prec; /** * A goal (aka problem). A goal is essentially a set of formulas, that can be * solved and/or transformed using tactics and solvers. **/ public class Goal extends Z3Object { /** * The precision of the goal. * Remarks: Goals can be transformed using over * and under approximations. An under approximation is applied when the * objective is to find a model for a given goal. An over approximation is * applied when the objective is to find a proof for a given goal. * **/ public Z3_goal_prec getPrecision() { return Z3_goal_prec.fromInt(Native.goalPrecision(getContext().nCtx(), getNativeObject())); } /** * Indicates whether the goal is precise. **/ public boolean isPrecise() { return getPrecision() == Z3_goal_prec.Z3_GOAL_PRECISE; } /** * Indicates whether the goal is an under-approximation. **/ public boolean isUnderApproximation() { return getPrecision() == Z3_goal_prec.Z3_GOAL_UNDER; } /** * Indicates whether the goal is an over-approximation. **/ public boolean isOverApproximation() { return getPrecision() == Z3_goal_prec.Z3_GOAL_OVER; } /** * Indicates whether the goal is garbage (i.e., the product of over- and * under-approximations). **/ public boolean isGarbage() { return getPrecision() == Z3_goal_prec.Z3_GOAL_UNDER_OVER; } /** * Adds the {@code constraints} to the given goal. * * @throws Z3Exception **/ public void add(BoolExpr ... constraints) { getContext().checkContextMatch(constraints); for (BoolExpr c : constraints) { Native.goalAssert(getContext().nCtx(), getNativeObject(), c.getNativeObject()); } } /** * Indicates whether the goal contains `false'. **/ public boolean inconsistent() { return Native.goalInconsistent(getContext().nCtx(), getNativeObject()); } /** * The depth of the goal. * Remarks: This tracks how many transformations * were applied to it. **/ public int getDepth() { return Native.goalDepth(getContext().nCtx(), getNativeObject()); } /** * Erases all formulas from the given goal. **/ public void reset() { Native.goalReset(getContext().nCtx(), getNativeObject()); } /** * The number of formulas in the goal. **/ public int size() { return Native.goalSize(getContext().nCtx(), getNativeObject()); } /** * The formulas in the goal. * * @throws Z3Exception **/ public BoolExpr[] getFormulas() { int n = size(); BoolExpr[] res = new BoolExpr[n]; for (int i = 0; i < n; i++) res[i] = new BoolExpr(getContext(), Native.goalFormula(getContext() .nCtx(), getNativeObject(), i)); return res; } /** * The number of formulas, subformulas and terms in the goal. **/ public int getNumExprs() { return Native.goalNumExprs(getContext().nCtx(), getNativeObject()); } /** * Indicates whether the goal is empty, and it is precise or the product of * an under approximation. **/ public boolean isDecidedSat() { return Native.goalIsDecidedSat(getContext().nCtx(), getNativeObject()); } /** * Indicates whether the goal contains `false', and it is precise or the * product of an over approximation. **/ public boolean isDecidedUnsat() { return Native .goalIsDecidedUnsat(getContext().nCtx(), getNativeObject()); } /** * Translates (copies) the Goal to the target Context {@code ctx}. * * @throws Z3Exception **/ public Goal translate(Context ctx) { return new Goal(ctx, Native.goalTranslate(getContext().nCtx(), getNativeObject(), ctx.nCtx())); } /** * Simplifies the goal. * Remarks: Essentially invokes the `simplify' tactic * on the goal. **/ public Goal simplify() { Tactic t = getContext().mkTactic("simplify"); ApplyResult res = t.apply(this); if (res.getNumSubgoals() == 0) throw new Z3Exception("No subgoals"); else return res.getSubgoals()[0]; } /** * Simplifies the goal. * Remarks: Essentially invokes the `simplify' tactic * on the goal. **/ public Goal simplify(Params p) { Tactic t = getContext().mkTactic("simplify"); ApplyResult res = t.apply(this, p); if (res.getNumSubgoals() == 0) throw new Z3Exception("No subgoals"); else return res.getSubgoals()[0]; } /** * Goal to string conversion. * * @return A string representation of the Goal. **/ public String toString() { try { return Native.goalToString(getContext().nCtx(), getNativeObject()); } catch (Z3Exception e) { return "Z3Exception: " + e.getMessage(); } } /** * Goal to BoolExpr conversion. * * Returns a string representation of the Goal. **/ public BoolExpr AsBoolExpr() { int n = size(); if (n == 0) return getContext().mkTrue(); else if (n == 1) return getFormulas()[0]; else { return getContext().mkAnd(getFormulas()); } } Goal(Context ctx, long obj) { super(ctx, obj); } Goal(Context ctx, boolean models, boolean unsatCores, boolean proofs) { super(ctx, Native.mkGoal(ctx.nCtx(), (models) ? true : false, (unsatCores) ? true : false, (proofs) ? true : false)); } void incRef(long o) { getContext().getGoalDRQ().incAndClear(getContext(), o); super.incRef(o); } void decRef(long o) { getContext().getGoalDRQ().add(o); super.decRef(o); } } z3-4.4.0/src/api/java/ParamDescrs.java0000644000175000017500000000413412540347414017177 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ParamDescrs.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_param_kind; /** * A ParamDescrs describes a set of parameters. **/ public class ParamDescrs extends Z3Object { /** * validate a set of parameters. **/ public void validate(Params p) { Native.paramsValidate(getContext().nCtx(), p.getNativeObject(), getNativeObject()); } /** * Retrieve kind of parameter. **/ public Z3_param_kind getKind(Symbol name) { return Z3_param_kind.fromInt(Native.paramDescrsGetKind( getContext().nCtx(), getNativeObject(), name.getNativeObject())); } /** * Retrieve all names of parameters. * * @throws Z3Exception **/ public Symbol[] getNames() { int sz = Native.paramDescrsSize(getContext().nCtx(), getNativeObject()); Symbol[] names = new Symbol[sz]; for (int i = 0; i < sz; ++i) { names[i] = Symbol.create(getContext(), Native.paramDescrsGetName( getContext().nCtx(), getNativeObject(), i)); } return names; } /** * The size of the ParamDescrs. **/ public int size() { return Native.paramDescrsSize(getContext().nCtx(), getNativeObject()); } /** * Retrieves a string representation of the ParamDescrs. **/ public String toString() { try { return Native.paramDescrsToString(getContext().nCtx(), getNativeObject()); } catch (Z3Exception e) { return "Z3Exception: " + e.getMessage(); } } ParamDescrs(Context ctx, long obj) { super(ctx, obj); } void incRef(long o) { getContext().getParamDescrsDRQ().incAndClear(getContext(), o); super.incRef(o); } void decRef(long o) { getContext().getParamDescrsDRQ().add(o); super.decRef(o); } } z3-4.4.0/src/api/java/AstVectorDecRefQueue.java0000644000175000017500000000151312540347414020761 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: AstVectorDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class ASTVectorDecRefQueue extends IDecRefQueue { public ASTVectorDecRefQueue() { super(); } public ASTVectorDecRefQueue(int move_limit) { super(move_limit); } protected void incRef(Context ctx, long obj) { try { Native.astVectorIncRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } protected void decRef(Context ctx, long obj) { try { Native.astVectorDecRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } }; z3-4.4.0/src/api/java/ArrayExpr.java0000644000175000017500000000062512540347414016711 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ArrayExpr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Array expressions **/ public class ArrayExpr extends Expr { /** * Constructor for ArrayExpr **/ ArrayExpr(Context ctx, long obj) { super(ctx, obj); } } z3-4.4.0/src/api/java/Expr.java0000644000175000017500000020543012540347414015713 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Expr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_ast_kind; import com.microsoft.z3.enumerations.Z3_decl_kind; import com.microsoft.z3.enumerations.Z3_lbool; import com.microsoft.z3.enumerations.Z3_sort_kind; /* using System; */ /** * Expressions are terms. **/ public class Expr extends AST { /** * Returns a simplified version of the expression * @return Expr * @throws Z3Exception on error * @return an Expr **/ public Expr simplify() { return simplify(null); } /** * Returns a simplified version of the expression * A set of * parameters * @param p a Params object to configure the simplifier * @see Context#SimplifyHelp * @return an Expr * @throws Z3Exception on error * @return an Expr **/ public Expr simplify(Params p) { if (p == null) return Expr.create(getContext(), Native.simplify(getContext().nCtx(), getNativeObject())); else return Expr.create( getContext(), Native.simplifyEx(getContext().nCtx(), getNativeObject(), p.getNativeObject())); } /** * The function declaration of the function that is applied in this * expression. * @return a FuncDecl * @throws Z3Exception on error **/ public FuncDecl getFuncDecl() { return new FuncDecl(getContext(), Native.getAppDecl(getContext().nCtx(), getNativeObject())); } /** * Indicates whether the expression is the true or false expression or * something else (Z3_L_UNDEF). * @throws Z3Exception on error * @return a Z3_lbool **/ public Z3_lbool getBoolValue() { return Z3_lbool.fromInt(Native.getBoolValue(getContext().nCtx(), getNativeObject())); } /** * The number of arguments of the expression. * @throws Z3Exception on error * @return an int **/ public int getNumArgs() { return Native.getAppNumArgs(getContext().nCtx(), getNativeObject()); } /** * The arguments of the expression. * @throws Z3Exception on error * @return an Expr[] **/ public Expr[] getArgs() { int n = getNumArgs(); Expr[] res = new Expr[n]; for (int i = 0; i < n; i++) res[i] = Expr.create(getContext(), Native.getAppArg(getContext().nCtx(), getNativeObject(), i)); return res; } /** * Update the arguments of the expression using the arguments {@code args} * The number of new arguments should coincide with the * current number of arguments. * @param args arguments * @throws Z3Exception on error **/ public void update(Expr[] args) { getContext().checkContextMatch(args); if (isApp() && args.length != getNumArgs()) throw new Z3Exception("Number of arguments does not match"); setNativeObject(Native.updateTerm(getContext().nCtx(), getNativeObject(), (int) args.length, Expr.arrayToNative(args))); } /** * Substitute every occurrence of {@code from[i]} in the expression * with {@code to[i]}, for {@code i} smaller than * {@code num_exprs}. * Remarks: The result is the new expression. The * arrays {@code from} and {@code to} must have size * {@code num_exprs}. For every {@code i} smaller than * {@code num_exprs}, we must have that sort of {@code from[i]} * must be equal to sort of {@code to[i]}. * @throws Z3Exception on error * @return an Expr **/ public Expr substitute(Expr[] from, Expr[] to) { getContext().checkContextMatch(from); getContext().checkContextMatch(to); if (from.length != to.length) throw new Z3Exception("Argument sizes do not match"); return Expr.create(getContext(), Native.substitute(getContext().nCtx(), getNativeObject(), (int) from.length, Expr.arrayToNative(from), Expr.arrayToNative(to))); } /** * Substitute every occurrence of {@code from} in the expression with * {@code to}. * @see Expr#substitute(Expr[],Expr[]) * @throws Z3Exception on error * @return an Expr **/ public Expr substitute(Expr from, Expr to) { return substitute(new Expr[] { from }, new Expr[] { to }); } /** * Substitute the free variables in the expression with the expressions in * {@code to} * Remarks: For every {@code i} smaller than * {@code num_exprs}, the * variable with de-Bruijn index {@code i} * is replaced with term * {@code to[i]}. * @throws Z3Exception on error * @throws Z3Exception on error * @return an Expr **/ public Expr substituteVars(Expr[] to) { getContext().checkContextMatch(to); return Expr.create(getContext(), Native.substituteVars(getContext().nCtx(), getNativeObject(), (int) to.length, Expr.arrayToNative(to))); } /** * Translates (copies) the term to the Context {@code ctx}. * * @param ctx A context * * @return A copy of the term which is associated with {@code ctx} * @throws Z3Exception on error * @return an Expr **/ public Expr translate(Context ctx) { if (getContext() == ctx) return this; else return Expr.create( ctx, Native.translate(getContext().nCtx(), getNativeObject(), ctx.nCtx())); } /** * Returns a string representation of the expression. **/ public String toString() { return super.toString(); } /** * Indicates whether the term is a numeral * @throws Z3Exception on error * @return a boolean **/ public boolean isNumeral() { return Native.isNumeralAst(getContext().nCtx(), getNativeObject()); } /** * Indicates whether the term is well-sorted. * * @return True if the term is well-sorted, false otherwise. * @throws Z3Exception on error * @return a boolean **/ public boolean isWellSorted() { return Native.isWellSorted(getContext().nCtx(), getNativeObject()); } /** * The Sort of the term. * @throws Z3Exception on error * @return a sort **/ public Sort getSort() { return Sort.create(getContext(), Native.getSort(getContext().nCtx(), getNativeObject())); } /** * Indicates whether the term represents a constant. * @throws Z3Exception on error * @return a boolean **/ public boolean isConst() { return isApp() && getNumArgs() == 0 && getFuncDecl().getDomainSize() == 0; } /** * Indicates whether the term is an integer numeral. * @throws Z3Exception on error * @return a boolean **/ public boolean isIntNum() { return isNumeral() && isInt(); } /** * Indicates whether the term is a real numeral. * @throws Z3Exception on error * @return a boolean **/ public boolean isRatNum() { return isNumeral() && isReal(); } /** * Indicates whether the term is an algebraic number * @throws Z3Exception on error * @return a boolean **/ public boolean isAlgebraicNumber() { return Native.isAlgebraicNumber(getContext().nCtx(), getNativeObject()); } /** * Indicates whether the term has Boolean sort. * @throws Z3Exception on error * @return a boolean **/ public boolean isBool() { return (isExpr() && Native.isEqSort(getContext().nCtx(), Native.mkBoolSort(getContext().nCtx()), Native.getSort(getContext().nCtx(), getNativeObject()))); } /** * Indicates whether the term is the constant true. * @throws Z3Exception on error * @return a boolean **/ public boolean isTrue() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TRUE; } /** * Indicates whether the term is the constant false. * @throws Z3Exception on error * @return a boolean **/ public boolean isFalse() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FALSE; } /** * Indicates whether the term is an equality predicate. * @throws Z3Exception on error * @return a boolean **/ public boolean isEq() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EQ; } /** * Indicates whether the term is an n-ary distinct predicate (every argument * is mutually distinct). * @throws Z3Exception on error * @return a boolean **/ public boolean isDistinct() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_DISTINCT; } /** * Indicates whether the term is a ternary if-then-else term * @throws Z3Exception on error * @return a boolean **/ public boolean isITE() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ITE; } /** * Indicates whether the term is an n-ary conjunction * @throws Z3Exception on error * @return a boolean **/ public boolean isAnd() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_AND; } /** * Indicates whether the term is an n-ary disjunction * @throws Z3Exception on error * @return a boolean **/ public boolean isOr() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_OR; } /** * Indicates whether the term is an if-and-only-if (Boolean equivalence, * binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isIff() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IFF; } /** * Indicates whether the term is an exclusive or * @throws Z3Exception on error * @return a boolean **/ public boolean isXor() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_XOR; } /** * Indicates whether the term is a negation * @throws Z3Exception on error * @return a boolean **/ public boolean isNot() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_NOT; } /** * Indicates whether the term is an implication * @throws Z3Exception on error * @return a boolean **/ public boolean isImplies() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IMPLIES; } /** * Indicates whether the term is of integer sort. * @throws Z3Exception on error * @return a boolean **/ public boolean isInt() { return (Native.isNumeralAst(getContext().nCtx(), getNativeObject()) && Native .getSortKind(getContext().nCtx(), Native.getSort(getContext().nCtx(), getNativeObject())) == Z3_sort_kind.Z3_INT_SORT .toInt()); } /** * Indicates whether the term is of sort real. * @throws Z3Exception on error * @return a boolean **/ public boolean isReal() { return Native.getSortKind(getContext().nCtx(), Native.getSort(getContext().nCtx(), getNativeObject())) == Z3_sort_kind.Z3_REAL_SORT .toInt(); } /** * Indicates whether the term is an arithmetic numeral. * @throws Z3Exception on error * @return a boolean **/ public boolean isArithmeticNumeral() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ANUM; } /** * Indicates whether the term is a less-than-or-equal * @throws Z3Exception on error * @return a boolean **/ public boolean isLE() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LE; } /** * Indicates whether the term is a greater-than-or-equal * @throws Z3Exception on error * @return a boolean **/ public boolean isGE() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_GE; } /** * Indicates whether the term is a less-than * @throws Z3Exception on error * @return a boolean **/ public boolean isLT() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LT; } /** * Indicates whether the term is a greater-than * @throws Z3Exception on error * @return a boolean **/ public boolean isGT() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_GT; } /** * Indicates whether the term is addition (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isAdd() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ADD; } /** * Indicates whether the term is subtraction (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isSub() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SUB; } /** * Indicates whether the term is a unary minus * @throws Z3Exception on error * @return a boolean **/ public boolean isUMinus() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UMINUS; } /** * Indicates whether the term is multiplication (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isMul() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_MUL; } /** * Indicates whether the term is division (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isDiv() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_DIV; } /** * Indicates whether the term is integer division (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isIDiv() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IDIV; } /** * Indicates whether the term is remainder (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isRemainder() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_REM; } /** * Indicates whether the term is modulus (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isModulus() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_MOD; } /** * Indicates whether the term is a coercion of integer to real (unary) * @throws Z3Exception on error * @return a boolean **/ public boolean isIntToReal() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TO_REAL; } /** * Indicates whether the term is a coercion of real to integer (unary) * @throws Z3Exception on error * @return a boolean **/ public boolean isRealToInt() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TO_INT; } /** * Indicates whether the term is a check that tests whether a real is * integral (unary) * @throws Z3Exception on error * @return a boolean **/ public boolean isRealIsInt() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IS_INT; } /** * Indicates whether the term is of an array sort. * @throws Z3Exception on error * @return a boolean **/ public boolean isArray() { return (Native.isApp(getContext().nCtx(), getNativeObject()) && Z3_sort_kind .fromInt(Native.getSortKind(getContext().nCtx(), Native.getSort(getContext().nCtx(), getNativeObject()))) == Z3_sort_kind.Z3_ARRAY_SORT); } /** * Indicates whether the term is an array store. * Remarks: It satisfies * select(store(a,i,v),j) = if i = j then v else select(a,j). Array store * takes at least 3 arguments. * @throws Z3Exception on error * @return a boolean **/ public boolean isStore() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_STORE; } /** * Indicates whether the term is an array select. * @throws Z3Exception on error * @return a boolean **/ public boolean isSelect() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SELECT; } /** * Indicates whether the term is a constant array. * Remarks: For example, * select(const(v),i) = v holds for every v and i. The function is * unary. * @throws Z3Exception on error * @return a boolean **/ public boolean isConstantArray() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CONST_ARRAY; } /** * Indicates whether the term is a default array. * Remarks: For example default(const(v)) = v. The function is unary. * @throws Z3Exception on error * @return a boolean **/ public boolean isDefaultArray() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ARRAY_DEFAULT; } /** * Indicates whether the term is an array map. * Remarks: It satisfies * map[f](a1,..,a_n)[i] = f(a1[i],...,a_n[i]) for every i. * @throws Z3Exception on error * @return a boolean **/ public boolean isArrayMap() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ARRAY_MAP; } /** * Indicates whether the term is an as-array term. * Remarks: An as-array term * is n array value that behaves as the function graph of the function * passed as parameter. * @throws Z3Exception on error * @return a boolean **/ public boolean isAsArray() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_AS_ARRAY; } /** * Indicates whether the term is set union * @throws Z3Exception on error * @return a boolean **/ public boolean isSetUnion() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_UNION; } /** * Indicates whether the term is set intersection * @throws Z3Exception on error * @return a boolean **/ public boolean isSetIntersect() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_INTERSECT; } /** * Indicates whether the term is set difference * @throws Z3Exception on error * @return a boolean **/ public boolean isSetDifference() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_DIFFERENCE; } /** * Indicates whether the term is set complement * @throws Z3Exception on error * @return a boolean **/ public boolean isSetComplement() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_COMPLEMENT; } /** * Indicates whether the term is set subset * @throws Z3Exception on error * @return a boolean **/ public boolean isSetSubset() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_SUBSET; } /** * Indicates whether the terms is of bit-vector sort. * @throws Z3Exception on error * @return a boolean **/ public boolean isBV() { return Native.getSortKind(getContext().nCtx(), Native.getSort(getContext().nCtx(), getNativeObject())) == Z3_sort_kind.Z3_BV_SORT .toInt(); } /** * Indicates whether the term is a bit-vector numeral * @throws Z3Exception on error * @return a boolean **/ public boolean isBVNumeral() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNUM; } /** * Indicates whether the term is a one-bit bit-vector with value one * @throws Z3Exception on error * @return a boolean **/ public boolean isBVBitOne() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BIT1; } /** * Indicates whether the term is a one-bit bit-vector with value zero * @throws Z3Exception on error * @return a boolean **/ public boolean isBVBitZero() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BIT0; } /** * Indicates whether the term is a bit-vector unary minus * @throws Z3Exception on error * @return a boolean **/ public boolean isBVUMinus() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNEG; } /** * Indicates whether the term is a bit-vector addition (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVAdd() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BADD; } /** * Indicates whether the term is a bit-vector subtraction (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSub() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSUB; } /** * Indicates whether the term is a bit-vector multiplication (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVMul() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BMUL; } /** * Indicates whether the term is a bit-vector signed division (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSDiv() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSDIV; } /** * Indicates whether the term is a bit-vector unsigned division (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVUDiv() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUDIV; } /** * Indicates whether the term is a bit-vector signed remainder (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSRem() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSREM; } /** * Indicates whether the term is a bit-vector unsigned remainder (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVURem() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUREM; } /** * Indicates whether the term is a bit-vector signed modulus * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSMod() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSMOD; } /** * Indicates whether the term is a bit-vector signed division by zero * @return a boolean * @throws Z3Exception on error **/ boolean isBVSDiv0() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSDIV0; } /** * Indicates whether the term is a bit-vector unsigned division by zero * @return a boolean * @throws Z3Exception on error **/ boolean isBVUDiv0() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUDIV0; } /** * Indicates whether the term is a bit-vector signed remainder by zero * @return a boolean * @throws Z3Exception on error **/ boolean isBVSRem0() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSREM0; } /** * Indicates whether the term is a bit-vector unsigned remainder by zero * @return a boolean * @throws Z3Exception on error **/ boolean isBVURem0() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUREM0; } /** * Indicates whether the term is a bit-vector signed modulus by zero * @return a boolean * @throws Z3Exception on error **/ boolean isBVSMod0() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSMOD0; } /** * Indicates whether the term is an unsigned bit-vector less-than-or-equal * @throws Z3Exception on error * @return a boolean **/ public boolean isBVULE() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ULEQ; } /** * Indicates whether the term is a signed bit-vector less-than-or-equal * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSLE() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SLEQ; } /** * Indicates whether the term is an unsigned bit-vector * greater-than-or-equal * @throws Z3Exception on error * @return a boolean **/ public boolean isBVUGE() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UGEQ; } /** * Indicates whether the term is a signed bit-vector greater-than-or-equal * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSGE() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SGEQ; } /** * Indicates whether the term is an unsigned bit-vector less-than * @throws Z3Exception on error * @return a boolean **/ public boolean isBVULT() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ULT; } /** * Indicates whether the term is a signed bit-vector less-than * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSLT() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SLT; } /** * Indicates whether the term is an unsigned bit-vector greater-than * @throws Z3Exception on error * @return a boolean **/ public boolean isBVUGT() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UGT; } /** * Indicates whether the term is a signed bit-vector greater-than * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSGT() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SGT; } /** * Indicates whether the term is a bit-wise AND * @throws Z3Exception on error * @return a boolean **/ public boolean isBVAND() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BAND; } /** * Indicates whether the term is a bit-wise OR * @throws Z3Exception on error * @return a boolean **/ public boolean isBVOR() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BOR; } /** * Indicates whether the term is a bit-wise NOT * @throws Z3Exception on error * @return a boolean **/ public boolean isBVNOT() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNOT; } /** * Indicates whether the term is a bit-wise XOR * @throws Z3Exception on error * @return a boolean **/ public boolean isBVXOR() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BXOR; } /** * Indicates whether the term is a bit-wise NAND * @throws Z3Exception on error * @return a boolean **/ public boolean isBVNAND() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNAND; } /** * Indicates whether the term is a bit-wise NOR * @throws Z3Exception on error * @return a boolean **/ public boolean isBVNOR() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNOR; } /** * Indicates whether the term is a bit-wise XNOR * @throws Z3Exception on error * @return a boolean **/ public boolean isBVXNOR() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BXNOR; } /** * Indicates whether the term is a bit-vector concatenation (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVConcat() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CONCAT; } /** * Indicates whether the term is a bit-vector sign extension * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSignExtension() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SIGN_EXT; } /** * Indicates whether the term is a bit-vector zero extension * @throws Z3Exception on error * @return a boolean **/ public boolean isBVZeroExtension() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ZERO_EXT; } /** * Indicates whether the term is a bit-vector extraction * @throws Z3Exception on error * @return a boolean **/ public boolean isBVExtract() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXTRACT; } /** * Indicates whether the term is a bit-vector repetition * @throws Z3Exception on error * @return a boolean **/ public boolean isBVRepeat() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_REPEAT; } /** * Indicates whether the term is a bit-vector reduce OR * @throws Z3Exception on error * @return a boolean **/ public boolean isBVReduceOR() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BREDOR; } /** * Indicates whether the term is a bit-vector reduce AND * @throws Z3Exception on error * @return a boolean **/ public boolean isBVReduceAND() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BREDAND; } /** * Indicates whether the term is a bit-vector comparison * @throws Z3Exception on error * @return a boolean **/ public boolean isBVComp() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BCOMP; } /** * Indicates whether the term is a bit-vector shift left * @throws Z3Exception on error * @return a boolean **/ public boolean isBVShiftLeft() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSHL; } /** * Indicates whether the term is a bit-vector logical shift right * @throws Z3Exception on error * @return a boolean **/ public boolean isBVShiftRightLogical() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BLSHR; } /** * Indicates whether the term is a bit-vector arithmetic shift left * @throws Z3Exception on error * @return a boolean **/ public boolean isBVShiftRightArithmetic() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BASHR; } /** * Indicates whether the term is a bit-vector rotate left * @throws Z3Exception on error * @return a boolean **/ public boolean isBVRotateLeft() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ROTATE_LEFT; } /** * Indicates whether the term is a bit-vector rotate right * @throws Z3Exception on error * @return a boolean **/ public boolean isBVRotateRight() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ROTATE_RIGHT; } /** * Indicates whether the term is a bit-vector rotate left (extended) * Remarks: Similar to Z3_OP_ROTATE_LEFT, but it is a binary operator * instead of a parametric one. * @throws Z3Exception on error * @return a boolean **/ public boolean isBVRotateLeftExtended() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXT_ROTATE_LEFT; } /** * Indicates whether the term is a bit-vector rotate right (extended) * Remarks: Similar to Z3_OP_ROTATE_RIGHT, but it is a binary operator * instead of a parametric one. * @throws Z3Exception on error * @return a boolean **/ public boolean isBVRotateRightExtended() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXT_ROTATE_RIGHT; } /** * Indicates whether the term is a coercion from integer to bit-vector * * Remarks: This function is not supported by the decision procedures. Only * the most rudimentary simplification rules are applied to this * function. * @throws Z3Exception on error * @return a boolean **/ public boolean isIntToBV() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_INT2BV; } /** * Indicates whether the term is a coercion from bit-vector to integer * * Remarks: This function is not supported by the decision procedures. Only * the most rudimentary simplification rules are applied to this * function. * @throws Z3Exception on error * @return a boolean **/ public boolean isBVToInt() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BV2INT; } /** * Indicates whether the term is a bit-vector carry * Remarks: Compute the * carry bit in a full-adder. The meaning is given by the equivalence (carry * l1 l2 l3) <=> (or (and l1 l2) (and l1 l3) (and l2 l3))) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVCarry() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CARRY; } /** * Indicates whether the term is a bit-vector ternary XOR * Remarks: The * meaning is given by the equivalence (xor3 l1 l2 l3) <=> (xor (xor * l1 l2) l3) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVXOR3() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_XOR3; } /** * Indicates whether the term is a label (used by the Boogie Verification * condition generator). * Remarks: The label has two parameters, a string and * a Boolean polarity. It takes one argument, a formula. * @throws Z3Exception on error * @return a boolean **/ public boolean isLabel() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LABEL; } /** * Indicates whether the term is a label literal (used by the Boogie * Verification condition generator). * Remarks: A label literal has a set of * string parameters. It takes no arguments. * @throws Z3Exception on error * @return a boolean **/ public boolean isLabelLit() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LABEL_LIT; } /** * Indicates whether the term is a binary equivalence modulo namings. * Remarks: This binary predicate is used in proof terms. It captures * equisatisfiability and equivalence modulo renamings. * @throws Z3Exception on error * @return a boolean **/ public boolean isOEQ() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_OEQ; } /** * Indicates whether the term is a Proof for the expression 'true'. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofTrue() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRUE; } /** * Indicates whether the term is a proof for a fact asserted by the user. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofAsserted() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_ASSERTED; } /** * Indicates whether the term is a proof for a fact (tagged as goal) * asserted by the user. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofGoal() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_GOAL; } /** * Indicates whether the term is proof via modus ponens * Remarks: Given a * proof for p and a proof for (implies p q), produces a proof for q. T1: p * T2: (implies p q) [mp T1 T2]: q The second antecedents may also be a * proof for (iff p q). * @throws Z3Exception on error * @return a boolean **/ public boolean isProofModusPonens() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS; } /** * Indicates whether the term is a proof for (R t t), where R is a reflexive * relation. * Remarks: This proof object has no antecedents. The only * reflexive relations that are used are equivalence modulo namings, * equality and equivalence. That is, R is either '~', '=' or * 'iff'. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofReflexivity() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REFLEXIVITY; } /** * Indicates whether the term is proof by symmetricity of a relation * * Remarks: Given an symmetric relation R and a proof for (R t s), produces * a proof for (R s t). T1: (R t s) [symmetry T1]: (R s t) T1 is the * antecedent of this proof object. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofSymmetry() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_SYMMETRY; } /** * Indicates whether the term is a proof by transitivity of a relation * * Remarks: Given a transitive relation R, and proofs for (R t s) and (R s * u), produces a proof for (R t u). T1: (R t s) T2: (R s u) [trans T1 T2]: * (R t u) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofTransitivity() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY; } /** * Indicates whether the term is a proof by condensed transitivity of a * relation * Remarks: Condensed transitivity proof. This proof object is * only used if the parameter PROOF_MODE is 1. It combines several symmetry * and transitivity proofs. Example: T1: (R a b) T2: (R c b) T3: (R c d) * [trans* T1 T2 T3]: (R a d) R must be a symmetric and transitive relation. * * Assuming that this proof object is a proof for (R s t), then a proof * checker must check if it is possible to prove (R s t) using the * antecedents, symmetry and transitivity. That is, if there is a path from * s to t, if we view every antecedent (R a b) as an edge between a and b. * * @throws Z3Exception on error * @return a boolean **/ public boolean isProofTransitivityStar() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY_STAR; } /** * Indicates whether the term is a monotonicity proof object. * Remarks: T1: * (R t_1 s_1) ... Tn: (R t_n s_n) [monotonicity T1 ... Tn]: (R (f t_1 ... * t_n) (f s_1 ... s_n)) Remark: if t_i == s_i, then the antecedent Ti is * suppressed. That is, reflexivity proofs are supressed to save space. * * @throws Z3Exception on error * @return a boolean **/ public boolean isProofMonotonicity() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MONOTONICITY; } /** * Indicates whether the term is a quant-intro proof * Remarks: Given a proof * for (~ p q), produces a proof for (~ (forall (x) p) (forall (x) q)). T1: * (~ p q) [quant-intro T1]: (~ (forall (x) p) (forall (x) q)) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofQuantIntro() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_QUANT_INTRO; } /** * Indicates whether the term is a distributivity proof object. * Remarks: * Given that f (= or) distributes over g (= and), produces a proof for (= * (f a (g c d)) (g (f a c) (f a d))) If f and g are associative, this proof * also justifies the following equality: (= (f (g a b) (g c d)) (g (f a c) * (f a d) (f b c) (f b d))) where each f and g can have arbitrary number of * arguments. * * This proof object has no antecedents. Remark. This rule is used by the * CNF conversion pass and instantiated by f = or, and g = and. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofDistributivity() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DISTRIBUTIVITY; } /** * Indicates whether the term is a proof by elimination of AND * Remarks: * Given a proof for (and l_1 ... l_n), produces a proof for l_i T1: (and * l_1 ... l_n) [and-elim T1]: l_i * @throws Z3Exception on error * @return a boolean **/ public boolean isProofAndElimination() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_AND_ELIM; } /** * Indicates whether the term is a proof by eliminiation of not-or * Remarks: * Given a proof for (not (or l_1 ... l_n)), produces a proof for (not l_i). * T1: (not (or l_1 ... l_n)) [not-or-elim T1]: (not l_i) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofOrElimination() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NOT_OR_ELIM; } /** * Indicates whether the term is a proof by rewriting * Remarks: A proof for * a local rewriting step (= t s). The head function symbol of t is * interpreted. * * This proof object has no antecedents. The conclusion of a rewrite rule is * either an equality (= t s), an equivalence (iff t s), or * equi-satisfiability (~ t s). Remark: if f is bool, then = is iff. * * Examples: (= (+ x 0) x) (= (+ x 1 2) (+ 3 x)) (iff (or x false) x) * * @throws Z3Exception on error * @return a boolean **/ public boolean isProofRewrite() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REWRITE; } /** * Indicates whether the term is a proof by rewriting * Remarks: A proof for * rewriting an expression t into an expression s. This proof object is used * if the parameter PROOF_MODE is 1. This proof object can have n * antecedents. The antecedents are proofs for equalities used as * substitution rules. The object is also used in a few cases if the * parameter PROOF_MODE is 2. The cases are: - When applying contextual * simplification (CONTEXT_SIMPLIFIER=true) - When converting bit-vectors to * Booleans (BIT2BOOL=true) - When pulling ite expression up * (PULL_CHEAP_ITE_TREES=true) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofRewriteStar() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REWRITE_STAR; } /** * Indicates whether the term is a proof for pulling quantifiers out. * Remarks: A proof for (iff (f (forall (x) q(x)) r) (forall (x) (f (q x) * r))). This proof object has no antecedents. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofPullQuant() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PULL_QUANT; } /** * Indicates whether the term is a proof for pulling quantifiers out. * * Remarks: A proof for (iff P Q) where Q is in prenex normal form. This * proof object is only used if the parameter PROOF_MODE is 1. This proof * object has no antecedents * @throws Z3Exception on error * @return a boolean **/ public boolean isProofPullQuantStar() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PULL_QUANT_STAR; } /** * Indicates whether the term is a proof for pushing quantifiers in. * Remarks: A proof for: (iff (forall (x_1 ... x_m) (and p_1[x_1 ... x_m] * ... p_n[x_1 ... x_m])) (and (forall (x_1 ... x_m) p_1[x_1 ... x_m]) ... * (forall (x_1 ... x_m) p_n[x_1 ... x_m]))) This proof object has no * antecedents * @throws Z3Exception on error * @return a boolean **/ public boolean isProofPushQuant() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PUSH_QUANT; } /** * Indicates whether the term is a proof for elimination of unused * variables. * Remarks: A proof for (iff (forall (x_1 ... x_n y_1 ... y_m) * p[x_1 ... x_n]) (forall (x_1 ... x_n) p[x_1 ... x_n])) * * It is used to justify the elimination of unused variables. This proof * object has no antecedents. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofElimUnusedVars() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_ELIM_UNUSED_VARS; } /** * Indicates whether the term is a proof for destructive equality resolution * Remarks: A proof for destructive equality resolution: (iff (forall (x) * (or (not (= x t)) P[x])) P[t]) if x does not occur in t. * * This proof object has no antecedents. * * Several variables can be eliminated simultaneously. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofDER() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DER; } /** * Indicates whether the term is a proof for quantifier instantiation * * Remarks: A proof of (or (not (forall (x) (P x))) (P a)) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofQuantInst() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_QUANT_INST; } /** * Indicates whether the term is a hypthesis marker. * Remarks: Mark a * hypothesis in a natural deduction style proof. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofHypothesis() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_HYPOTHESIS; } /** * Indicates whether the term is a proof by lemma * Remarks: T1: false [lemma * T1]: (or (not l_1) ... (not l_n)) * * This proof object has one antecedent: a hypothetical proof for false. It * converts the proof in a proof for (or (not l_1) ... (not l_n)), when T1 * contains the hypotheses: l_1, ..., l_n. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofLemma() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_LEMMA; } /** * Indicates whether the term is a proof by unit resolution * Remarks: T1: * (or l_1 ... l_n l_1' ... l_m') T2: (not l_1) ... T(n+1): (not l_n) * [unit-resolution T1 ... T(n+1)]: (or l_1' ... l_m') * @throws Z3Exception on error * @return a boolean **/ public boolean isProofUnitResolution() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_UNIT_RESOLUTION; } /** * Indicates whether the term is a proof by iff-true * Remarks: T1: p * [iff-true T1]: (iff p true) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofIFFTrue() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_TRUE; } /** * Indicates whether the term is a proof by iff-false * Remarks: T1: (not p) * [iff-false T1]: (iff p false) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofIFFFalse() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_FALSE; } /** * Indicates whether the term is a proof by commutativity * Remarks: [comm]: * (= (f a b) (f b a)) * * f is a commutative operator. * * This proof object has no antecedents. Remark: if f is bool, then = is * iff. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofCommutativity() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_COMMUTATIVITY; } /** * Indicates whether the term is a proof for Tseitin-like axioms * Remarks: * Proof object used to justify Tseitin's like axioms: * * (or (not (and p q)) p) (or (not (and p q)) q) (or (not (and p q r)) p) * (or (not (and p q r)) q) (or (not (and p q r)) r) ... (or (and p q) (not * p) (not q)) (or (not (or p q)) p q) (or (or p q) (not p)) (or (or p q) * (not q)) (or (not (iff p q)) (not p) q) (or (not (iff p q)) p (not q)) * (or (iff p q) (not p) (not q)) (or (iff p q) p q) (or (not (ite a b c)) * (not a) b) (or (not (ite a b c)) a c) (or (ite a b c) (not a) (not b)) * (or (ite a b c) a (not c)) (or (not (not a)) (not a)) (or (not a) a) * * This proof object has no antecedents. Note: all axioms are propositional * tautologies. Note also that 'and' and 'or' can take multiple arguments. * You can recover the propositional tautologies by unfolding the Boolean * connectives in the axioms a small bounded number of steps (=3). * * @throws Z3Exception on error * @return a boolean **/ public boolean isProofDefAxiom() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DEF_AXIOM; } /** * Indicates whether the term is a proof for introduction of a name * Remarks: Introduces a name for a formula/term. Suppose e is an * expression with free variables x, and def-intro introduces the name n(x). * The possible cases are: * * When e is of Boolean type: [def-intro]: (and (or n (not e)) (or (not n) * e)) * * or: [def-intro]: (or (not n) e) when e only occurs positively. * * When e is of the form (ite cond th el): [def-intro]: (and (or (not cond) * (= n th)) (or cond (= n el))) * * Otherwise: [def-intro]: (= n e) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofDefIntro() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DEF_INTRO; } /** * Indicates whether the term is a proof for application of a definition * Remarks: [apply-def T1]: F ~ n F is 'equivalent' to n, given that T1 is * a proof that n is a name for F. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofApplyDef() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_APPLY_DEF; } /** * Indicates whether the term is a proof iff-oeq * Remarks: T1: (iff p q) * [iff~ T1]: (~ p q) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofIFFOEQ() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_OEQ; } /** * Indicates whether the term is a proof for a positive NNF step * Remarks: * Proof for a (positive) NNF step. Example: * * T1: (not s_1) ~ r_1 T2: (not s_2) ~ r_2 T3: s_1 ~ r_1' T4: s_2 ~ r_2' * [nnf-pos T1 T2 T3 T4]: (~ (iff s_1 s_2) (and (or r_1 r_2') (or r_1' * r_2))) * * The negation normal form steps NNF_POS and NNF_NEG are used in the * following cases: (a) When creating the NNF of a positive force * quantifier. The quantifier is retained (unless the bound variables are * eliminated). Example T1: q ~ q_new [nnf-pos T1]: (~ (forall (x T) q) * (forall (x T) q_new)) * * (b) When recursively creating NNF over Boolean formulas, where the * top-level connective is changed during NNF conversion. The relevant * Boolean connectives for NNF_POS are 'implies', 'iff', 'xor', 'ite'. * NNF_NEG furthermore handles the case where negation is pushed over * Boolean connectives 'and' and 'or'. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofNNFPos() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_POS; } /** * Indicates whether the term is a proof for a negative NNF step * Remarks: * Proof for a (negative) NNF step. Examples: * * T1: (not s_1) ~ r_1 ... Tn: (not s_n) ~ r_n [nnf-neg T1 ... Tn]: (not * (and s_1 ... s_n)) ~ (or r_1 ... r_n) and T1: (not s_1) ~ r_1 ... Tn: * (not s_n) ~ r_n [nnf-neg T1 ... Tn]: (not (or s_1 ... s_n)) ~ (and r_1 * ... r_n) and T1: (not s_1) ~ r_1 T2: (not s_2) ~ r_2 T3: s_1 ~ r_1' T4: * s_2 ~ r_2' [nnf-neg T1 T2 T3 T4]: (~ (not (iff s_1 s_2)) (and (or r_1 * r_2) (or r_1' r_2'))) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofNNFNeg() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_NEG; } /** * Indicates whether the term is a proof for (~ P Q) here Q is in negation * normal form. * Remarks: A proof for (~ P Q) where Q is in negation normal * form. * * This proof object is only used if the parameter PROOF_MODE is 1. * * This proof object may have n antecedents. Each antecedent is a * PR_DEF_INTRO. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofNNFStar() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_STAR; } /** * Indicates whether the term is a proof for (~ P Q) where Q is in * conjunctive normal form. * Remarks: A proof for (~ P Q) where Q is in * conjunctive normal form. This proof object is only used if the parameter * PROOF_MODE is 1. This proof object may have n antecedents. Each * antecedent is a PR_DEF_INTRO. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofCNFStar() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_CNF_STAR; } /** * Indicates whether the term is a proof for a Skolemization step * Remarks: * Proof for: * * [sk]: (~ (not (forall x (p x y))) (not (p (sk y) y))) [sk]: (~ (exists x * (p x y)) (p (sk y) y)) * * This proof object has no antecedents. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofSkolemize() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_SKOLEMIZE; } /** * Indicates whether the term is a proof by modus ponens for * equi-satisfiability. * Remarks: Modus ponens style rule for * equi-satisfiability. T1: p T2: (~ p q) [mp~ T1 T2]: q * @throws Z3Exception on error * @return a boolean **/ public boolean isProofModusPonensOEQ() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS_OEQ; } /** * Indicates whether the term is a proof for theory lemma * Remarks: Generic * proof for theory lemmas. * * The theory lemma function comes with one or more parameters. The first * parameter indicates the name of the theory. For the theory of arithmetic, * additional parameters provide hints for checking the theory lemma. The * hints for arithmetic are: - farkas - followed by rational coefficients. * Multiply the coefficients to the inequalities in the lemma, add the * (negated) inequalities and obtain a contradiction. - triangle-eq - * Indicates a lemma related to the equivalence: (iff (= t1 t2) (and (<= * t1 t2) (<= t2 t1))) - gcd-test - Indicates an integer linear * arithmetic lemma that uses a gcd test. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofTheoryLemma() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TH_LEMMA; } /** * Indicates whether the term is of an array sort. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelation() { return (Native.isApp(getContext().nCtx(), getNativeObject()) && Native .getSortKind(getContext().nCtx(), Native.getSort(getContext().nCtx(), getNativeObject())) == Z3_sort_kind.Z3_RELATION_SORT .toInt()); } /** * Indicates whether the term is an relation store * Remarks: Insert a record * into a relation. The function takes {@code n+1} arguments, where the * first argument is the relation and the remaining {@code n} elements * correspond to the {@code n} columns of the relation. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationStore() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_STORE; } /** * Indicates whether the term is an empty relation * @throws Z3Exception on error * @return a boolean **/ public boolean isEmptyRelation() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_EMPTY; } /** * Indicates whether the term is a test for the emptiness of a relation * @throws Z3Exception on error * @return a boolean **/ public boolean isIsEmptyRelation() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_IS_EMPTY; } /** * Indicates whether the term is a relational join * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationalJoin() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_JOIN; } /** * Indicates whether the term is the union or convex hull of two relations. * * Remarks: The function takes two arguments. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationUnion() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_UNION; } /** * Indicates whether the term is the widening of two relations * Remarks: The * function takes two arguments. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationWiden() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_WIDEN; } /** * Indicates whether the term is a projection of columns (provided as * numbers in the parameters). * Remarks: The function takes one * argument. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationProject() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_PROJECT; } /** * Indicates whether the term is a relation filter * Remarks: Filter * (restrict) a relation with respect to a predicate. The first argument is * a relation. The second argument is a predicate with free de-Brujin * indices corresponding to the columns of the relation. So the first column * in the relation has index 0. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationFilter() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_FILTER; } /** * Indicates whether the term is an intersection of a relation with the * negation of another. * Remarks: Intersect the first relation with respect * to negation of the second relation (the function takes two arguments). * Logically, the specification can be described by a function * * target = filter_by_negation(pos, neg, columns) * * where columns are pairs c1, d1, .., cN, dN of columns from pos and neg, * such that target are elements in x in pos, such that there is no y in neg * that agrees with x on the columns c1, d1, .., cN, dN. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationNegationFilter() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_NEGATION_FILTER; } /** * Indicates whether the term is the renaming of a column in a relation * Remarks: The function takes one argument. The parameters contain the * renaming as a cycle. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationRename() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_RENAME; } /** * Indicates whether the term is the complement of a relation * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationComplement() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_COMPLEMENT; } /** * Indicates whether the term is a relational select * Remarks: Check if a * record is an element of the relation. The function takes {@code n+1} * arguments, where the first argument is a relation, and the remaining * {@code n} arguments correspond to a record. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationSelect() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_SELECT; } /** * Indicates whether the term is a relational clone (copy) * Remarks: Create * a fresh copy (clone) of a relation. The function is logically the * identity, but in the context of a register machine allows for terms of * kind {@code isRelationUnion} to perform destructive updates to * the first argument. * @see isRelationUnion * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationClone() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_CLONE; } /** * Indicates whether the term is of an array sort. * @throws Z3Exception on error * @return a boolean **/ public boolean isFiniteDomain() { return (Native.isApp(getContext().nCtx(), getNativeObject()) && Native .getSortKind(getContext().nCtx(), Native.getSort(getContext().nCtx(), getNativeObject())) == Z3_sort_kind.Z3_FINITE_DOMAIN_SORT .toInt()); } /** * Indicates whether the term is a less than predicate over a finite domain. * @throws Z3Exception on error * @return a boolean **/ public boolean isFiniteDomainLT() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FD_LT; } /** * The de-Burijn index of a bound variable. * Remarks: Bound variables are * indexed by de-Bruijn indices. It is perhaps easiest to explain the * meaning of de-Bruijn indices by indicating the compilation process from * non-de-Bruijn formulas to de-Bruijn format. {@code * abs(forall (x1) phi) = forall (x1) abs1(phi, x1, 0) * abs(forall (x1, x2) phi) = abs(forall (x1) abs(forall (x2) phi)) * abs1(x, x, n) = b_n * abs1(y, x, n) = y * abs1(f(t1,...,tn), x, n) = f(abs1(t1,x,n), ..., abs1(tn,x,n)) * abs1(forall (x1) phi, x, n) = forall (x1) (abs1(phi, x, n+1)) * } The last line is significant: the index of a bound variable is * different depending on the scope in which it appears. The deeper x * appears, the higher is its index. * @throws Z3Exception on error * @return an int **/ public int getIndex() { if (!isVar()) throw new Z3Exception("Term is not a bound variable."); return Native.getIndexValue(getContext().nCtx(), getNativeObject()); } /** * Constructor for Expr **/ protected Expr(Context ctx) { super(ctx); { } } /** * Constructor for Expr * @throws Z3Exception on error **/ protected Expr(Context ctx, long obj) { super(ctx, obj); { } } void checkNativeObject(long obj) { if (!Native.isApp(getContext().nCtx(), obj) && Native.getAstKind(getContext().nCtx(), obj) != Z3_ast_kind.Z3_VAR_AST.toInt() && Native.getAstKind(getContext().nCtx(), obj) != Z3_ast_kind.Z3_QUANTIFIER_AST.toInt()) throw new Z3Exception("Underlying object is not a term"); super.checkNativeObject(obj); } static Expr create(Context ctx, FuncDecl f, Expr ... arguments) { long obj = Native.mkApp(ctx.nCtx(), f.getNativeObject(), AST.arrayLength(arguments), AST.arrayToNative(arguments)); return create(ctx, obj); } static Expr create(Context ctx, long obj) { Z3_ast_kind k = Z3_ast_kind.fromInt(Native.getAstKind(ctx.nCtx(), obj)); if (k == Z3_ast_kind.Z3_QUANTIFIER_AST) return new Quantifier(ctx, obj); long s = Native.getSort(ctx.nCtx(), obj); Z3_sort_kind sk = Z3_sort_kind .fromInt(Native.getSortKind(ctx.nCtx(), s)); if (Native.isAlgebraicNumber(ctx.nCtx(), obj)) // is this a numeral ast? return new AlgebraicNum(ctx, obj); if (Native.isNumeralAst(ctx.nCtx(), obj)) { switch (sk) { case Z3_INT_SORT: return new IntNum(ctx, obj); case Z3_REAL_SORT: return new RatNum(ctx, obj); case Z3_BV_SORT: return new BitVecNum(ctx, obj); case Z3_FLOATING_POINT_SORT: return new FPNum(ctx, obj); case Z3_ROUNDING_MODE_SORT: return new FPRMNum(ctx, obj); default: ; } } switch (sk) { case Z3_BOOL_SORT: return new BoolExpr(ctx, obj); case Z3_INT_SORT: return new IntExpr(ctx, obj); case Z3_REAL_SORT: return new RealExpr(ctx, obj); case Z3_BV_SORT: return new BitVecExpr(ctx, obj); case Z3_ARRAY_SORT: return new ArrayExpr(ctx, obj); case Z3_DATATYPE_SORT: return new DatatypeExpr(ctx, obj); case Z3_FLOATING_POINT_SORT: return new FPExpr(ctx, obj); case Z3_ROUNDING_MODE_SORT: return new FPRMExpr(ctx, obj); default: ; } return new Expr(ctx, obj); } } z3-4.4.0/src/api/java/FiniteDomainSort.java0000644000175000017500000000152312540347414020210 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: FiniteDomainSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Finite domain sorts. **/ public class FiniteDomainSort extends Sort { /** * The size of the finite domain sort. * @throws Z3Exception on error **/ public long getSize() { Native.LongPtr res = new Native.LongPtr(); Native.getFiniteDomainSortSize(getContext().nCtx(), getNativeObject(), res); return res.value; } FiniteDomainSort(Context ctx, long obj) { super(ctx, obj); } FiniteDomainSort(Context ctx, Symbol name, long size) { super(ctx, Native.mkFiniteDomainSort(ctx.nCtx(), name.getNativeObject(), size)); } } z3-4.4.0/src/api/java/IntSymbol.java0000644000175000017500000000215112540347414016710 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: IntSymbol.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_symbol_kind; /** * Numbered symbols **/ public class IntSymbol extends Symbol { /** * The int value of the symbol. * Remarks: Throws an exception if the symbol * is not of int kind. **/ public int getInt() { if (!isIntSymbol()) throw new Z3Exception("Int requested from non-Int symbol"); return Native.getSymbolInt(getContext().nCtx(), getNativeObject()); } IntSymbol(Context ctx, long obj) { super(ctx, obj); } IntSymbol(Context ctx, int i) { super(ctx, Native.mkIntSymbol(ctx.nCtx(), i)); } void checkNativeObject(long obj) { if (Native.getSymbolKind(getContext().nCtx(), obj) != Z3_symbol_kind.Z3_INT_SYMBOL .toInt()) throw new Z3Exception("Symbol is not of integer kind"); super.checkNativeObject(obj); } } z3-4.4.0/src/api/java/Solver.java0000644000175000017500000002134312540347414016246 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Solver.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_lbool; /** * Solvers. **/ public class Solver extends Z3Object { /** * A string that describes all available solver parameters. **/ public String getHelp() { return Native.solverGetHelp(getContext().nCtx(), getNativeObject()); } /** * Sets the solver parameters. * * @throws Z3Exception **/ public void setParameters(Params value) { getContext().checkContextMatch(value); Native.solverSetParams(getContext().nCtx(), getNativeObject(), value.getNativeObject()); } /** * Retrieves parameter descriptions for solver. * * @throws Z3Exception **/ public ParamDescrs getParameterDescriptions() { return new ParamDescrs(getContext(), Native.solverGetParamDescrs( getContext().nCtx(), getNativeObject())); } /** * The current number of backtracking points (scopes). * @see pop * @see push **/ public int getNumScopes() { return Native .solverGetNumScopes(getContext().nCtx(), getNativeObject()); } /** * Creates a backtracking point. * @see pop **/ public void push() { Native.solverPush(getContext().nCtx(), getNativeObject()); } /** * Backtracks one backtracking point. * Remarks: . **/ public void pop() { pop(1); } /** * Backtracks {@code n} backtracking points. * Remarks: Note that * an exception is thrown if {@code n} is not smaller than * {@code NumScopes} * @see push **/ public void pop(int n) { Native.solverPop(getContext().nCtx(), getNativeObject(), n); } /** * Resets the Solver. * Remarks: This removes all assertions from the * solver. **/ public void reset() { Native.solverReset(getContext().nCtx(), getNativeObject()); } /** * Assert a multiple constraints into the solver. * * @throws Z3Exception **/ public void add(BoolExpr... constraints) { getContext().checkContextMatch(constraints); for (BoolExpr a : constraints) { Native.solverAssert(getContext().nCtx(), getNativeObject(), a.getNativeObject()); } } /** * Assert multiple constraints into the solver, and track them (in the * unsat) core * using the Boolean constants in ps. * * Remarks: * This API is an alternative to with assumptions for * extracting unsat cores. * Both APIs can be used in the same solver. The unsat core will contain a * combination * of the Boolean variables provided using * and the Boolean literals * provided using with assumptions. **/ public void assertAndTrack(BoolExpr[] constraints, BoolExpr[] ps) { getContext().checkContextMatch(constraints); getContext().checkContextMatch(ps); if (constraints.length != ps.length) throw new Z3Exception("Argument size mismatch"); for (int i = 0; i < constraints.length; i++) Native.solverAssertAndTrack(getContext().nCtx(), getNativeObject(), constraints[i].getNativeObject(), ps[i].getNativeObject()); } /** * Assert a constraint into the solver, and track it (in the unsat) core * using the Boolean constant p. * * Remarks: * This API is an alternative to with assumptions for * extracting unsat cores. * Both APIs can be used in the same solver. The unsat core will contain a * combination * of the Boolean variables provided using * and the Boolean literals * provided using with assumptions. */ public void assertAndTrack(BoolExpr constraint, BoolExpr p) { getContext().checkContextMatch(constraint); getContext().checkContextMatch(p); Native.solverAssertAndTrack(getContext().nCtx(), getNativeObject(), constraint.getNativeObject(), p.getNativeObject()); } /** * The number of assertions in the solver. * * @throws Z3Exception **/ public int getNumAssertions() { ASTVector assrts = new ASTVector(getContext(), Native.solverGetAssertions(getContext().nCtx(), getNativeObject())); return assrts.size(); } /** * The set of asserted formulas. * * @throws Z3Exception **/ public BoolExpr[] getAssertions() { ASTVector assrts = new ASTVector(getContext(), Native.solverGetAssertions(getContext().nCtx(), getNativeObject())); return assrts.ToBoolExprArray(); } /** * Checks whether the assertions in the solver are consistent or not. * Remarks: * @see getModel * @see getUnsatCore * @see getProof **/ public Status check(Expr... assumptions) { Z3_lbool r; if (assumptions == null) r = Z3_lbool.fromInt(Native.solverCheck(getContext().nCtx(), getNativeObject())); else r = Z3_lbool.fromInt(Native.solverCheckAssumptions(getContext() .nCtx(), getNativeObject(), (int) assumptions.length, AST .arrayToNative(assumptions))); switch (r) { case Z3_L_TRUE: return Status.SATISFIABLE; case Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } /** * Checks whether the assertions in the solver are consistent or not. * Remarks: * @see getModel * @see getUnsatCore * @see getProof **/ public Status check() { return check((Expr[]) null); } /** * The model of the last {@code Check}. * Remarks: The result is * {@code null} if {@code Check} was not invoked before, if its * results was not {@code SATISFIABLE}, or if model production is not * enabled. * * @throws Z3Exception **/ public Model getModel() { long x = Native.solverGetModel(getContext().nCtx(), getNativeObject()); if (x == 0) return null; else return new Model(getContext(), x); } /** * The proof of the last {@code Check}. * Remarks: The result is * {@code null} if {@code Check} was not invoked before, if its * results was not {@code UNSATISFIABLE}, or if proof production is * disabled. * * @throws Z3Exception **/ public Expr getProof() { long x = Native.solverGetProof(getContext().nCtx(), getNativeObject()); if (x == 0) return null; else return Expr.create(getContext(), x); } /** * The unsat core of the last {@code Check}. * Remarks: The unsat core * is a subset of {@code Assertions} The result is empty if * {@code Check} was not invoked before, if its results was not * {@code UNSATISFIABLE}, or if core production is disabled. * * @throws Z3Exception **/ public BoolExpr[] getUnsatCore() { ASTVector core = new ASTVector(getContext(), Native.solverGetUnsatCore(getContext().nCtx(), getNativeObject())); return core.ToBoolExprArray(); } /** * A brief justification of why the last call to {@code Check} returned * {@code UNKNOWN}. **/ public String getReasonUnknown() { return Native.solverGetReasonUnknown(getContext().nCtx(), getNativeObject()); } /** * Solver statistics. * * @throws Z3Exception **/ public Statistics getStatistics() { return new Statistics(getContext(), Native.solverGetStatistics( getContext().nCtx(), getNativeObject())); } /** * A string representation of the solver. **/ public String toString() { try { return Native .solverToString(getContext().nCtx(), getNativeObject()); } catch (Z3Exception e) { return "Z3Exception: " + e.getMessage(); } } Solver(Context ctx, long obj) { super(ctx, obj); } void incRef(long o) { getContext().getSolverDRQ().incAndClear(getContext(), o); super.incRef(o); } void decRef(long o) { getContext().getSolverDRQ().add(o); super.decRef(o); } } z3-4.4.0/src/api/java/FuncInterpEntryDecRefQueue.java0000644000175000017500000000154312540347414022151 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: FuncInterpEntryDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class FuncInterpEntryDecRefQueue extends IDecRefQueue { public FuncInterpEntryDecRefQueue() { super(); } public FuncInterpEntryDecRefQueue(int move_limit) { super(move_limit); } protected void incRef(Context ctx, long obj) { try { Native.funcEntryIncRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } protected void decRef(Context ctx, long obj) { try { Native.funcEntryDecRef(ctx.nCtx(), obj); } catch (Z3Exception e) { // OK. } } }; z3-4.4.0/src/api/java/ArithExpr.java0000644000175000017500000000064412540347414016703 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ArithExpr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Arithmetic expressions (int/real) **/ public class ArithExpr extends Expr { /** * Constructor for ArithExpr **/ ArithExpr(Context ctx, long obj) { super(ctx, obj); } } z3-4.4.0/src/api/java/README0000644000175000017500000000035212540347414015006 0ustar michaelmichaelJava bindings ------------- The Java bindings will be included in the Z3 build if it is configured with the option --java to python scripts/mk_make.py. This will produce the com.microsoft.z3.jar package in the build directory. z3-4.4.0/src/api/java/Quantifier.java0000644000175000017500000001466412540347414017113 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Quantifier.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_ast_kind; /** * Quantifier expressions. **/ public class Quantifier extends BoolExpr { /** * Indicates whether the quantifier is universal. **/ public boolean isUniversal() { return Native.isQuantifierForall(getContext().nCtx(), getNativeObject()); } /** * Indicates whether the quantifier is existential. **/ public boolean isExistential() { return !isUniversal(); } /** * The weight of the quantifier. **/ public int getWeight() { return Native.getQuantifierWeight(getContext().nCtx(), getNativeObject()); } /** * The number of patterns. **/ public int getNumPatterns() { return Native .getQuantifierNumPatterns(getContext().nCtx(), getNativeObject()); } /** * The patterns. * * @throws Z3Exception **/ public Pattern[] getPatterns() { int n = getNumPatterns(); Pattern[] res = new Pattern[n]; for (int i = 0; i < n; i++) res[i] = new Pattern(getContext(), Native.getQuantifierPatternAst( getContext().nCtx(), getNativeObject(), i)); return res; } /** * The number of no-patterns. **/ public int getNumNoPatterns() { return Native.getQuantifierNumNoPatterns(getContext().nCtx(), getNativeObject()); } /** * The no-patterns. * * @throws Z3Exception **/ public Pattern[] getNoPatterns() { int n = getNumNoPatterns(); Pattern[] res = new Pattern[n]; for (int i = 0; i < n; i++) res[i] = new Pattern(getContext(), Native.getQuantifierNoPatternAst( getContext().nCtx(), getNativeObject(), i)); return res; } /** * The number of bound variables. **/ public int getNumBound() { return Native.getQuantifierNumBound(getContext().nCtx(), getNativeObject()); } /** * The symbols for the bound variables. * * @throws Z3Exception **/ public Symbol[] getBoundVariableNames() { int n = getNumBound(); Symbol[] res = new Symbol[n]; for (int i = 0; i < n; i++) res[i] = Symbol.create(getContext(), Native.getQuantifierBoundName( getContext().nCtx(), getNativeObject(), i)); return res; } /** * The sorts of the bound variables. * * @throws Z3Exception **/ public Sort[] getBoundVariableSorts() { int n = getNumBound(); Sort[] res = new Sort[n]; for (int i = 0; i < n; i++) res[i] = Sort.create(getContext(), Native.getQuantifierBoundSort( getContext().nCtx(), getNativeObject(), i)); return res; } /** * The body of the quantifier. * * @throws Z3Exception **/ public BoolExpr getBody() { return new BoolExpr(getContext(), Native.getQuantifierBody(getContext() .nCtx(), getNativeObject())); } Quantifier(Context ctx, boolean isForall, Sort[] sorts, Symbol[] names, Expr body, int weight, Pattern[] patterns, Expr[] noPatterns, Symbol quantifierID, Symbol skolemID) { super(ctx, 0); getContext().checkContextMatch(patterns); getContext().checkContextMatch(noPatterns); getContext().checkContextMatch(sorts); getContext().checkContextMatch(names); getContext().checkContextMatch(body); if (sorts.length != names.length) throw new Z3Exception( "Number of sorts does not match number of names"); if (noPatterns == null && quantifierID == null && skolemID == null) { setNativeObject(Native.mkQuantifier(ctx.nCtx(), (isForall) ? true : false, weight, AST.arrayLength(patterns), AST .arrayToNative(patterns), AST.arrayLength(sorts), AST .arrayToNative(sorts), Symbol.arrayToNative(names), body .getNativeObject())); } else { setNativeObject(Native.mkQuantifierEx(ctx.nCtx(), (isForall) ? true : false, weight, AST.getNativeObject(quantifierID), AST.getNativeObject(skolemID), AST.arrayLength(patterns), AST.arrayToNative(patterns), AST.arrayLength(noPatterns), AST.arrayToNative(noPatterns), AST.arrayLength(sorts), AST.arrayToNative(sorts), Symbol.arrayToNative(names), body.getNativeObject())); } } Quantifier(Context ctx, boolean isForall, Expr[] bound, Expr body, int weight, Pattern[] patterns, Expr[] noPatterns, Symbol quantifierID, Symbol skolemID) { super(ctx, 0); getContext().checkContextMatch(noPatterns); getContext().checkContextMatch(patterns); // Context().CheckContextMatch(bound); getContext().checkContextMatch(body); if (noPatterns == null && quantifierID == null && skolemID == null) { setNativeObject(Native.mkQuantifierConst(ctx.nCtx(), (isForall) ? true : false, weight, AST.arrayLength(bound), AST.arrayToNative(bound), AST.arrayLength(patterns), AST.arrayToNative(patterns), body.getNativeObject())); } else { setNativeObject(Native.mkQuantifierConstEx(ctx.nCtx(), (isForall) ? true : false, weight, AST.getNativeObject(quantifierID), AST.getNativeObject(skolemID), AST.arrayLength(bound), AST.arrayToNative(bound), AST.arrayLength(patterns), AST.arrayToNative(patterns), AST.arrayLength(noPatterns), AST.arrayToNative(noPatterns), body.getNativeObject())); } } Quantifier(Context ctx, long obj) { super(ctx, obj); } void checkNativeObject(long obj) { if (Native.getAstKind(getContext().nCtx(), obj) != Z3_ast_kind.Z3_QUANTIFIER_AST .toInt()) throw new Z3Exception("Underlying object is not a quantifier"); super.checkNativeObject(obj); } } z3-4.4.0/src/api/java/InterpolationContext.java0000644000175000017500000001561312540347414021173 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: InterpolationContext.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import java.util.Map; import java.lang.String; import com.microsoft.z3.enumerations.Z3_lbool; /** * The InterpolationContext is suitable for generation of interpolants. * * Remarks: For more information on interpolation please refer * too the C/C++ API, which is well documented. **/ public class InterpolationContext extends Context { /** * Constructor. **/ public InterpolationContext() { m_ctx = Native.mkInterpolationContext(0); initContext(); } /** * Constructor. * * * Remarks: * @see Context#Context **/ public InterpolationContext(Map settings) { long cfg = Native.mkConfig(); for (Map.Entry kv : settings.entrySet()) Native.setParamValue(cfg, kv.getKey(), kv.getValue()); m_ctx = Native.mkInterpolationContext(cfg); Native.delConfig(cfg); initContext(); } /** * Create an expression that marks a formula position for interpolation. * @throws Z3Exception **/ public BoolExpr MkInterpolant(BoolExpr a) { checkContextMatch(a); return new BoolExpr(this, Native.mkInterpolant(nCtx(), a.getNativeObject())); } /** * Computes an interpolant. * Remarks: For more information on interpolation please refer * too the function Z3_get_interpolant in the C/C++ API, which is * well documented. * @throws Z3Exception **/ public BoolExpr[] GetInterpolant(Expr pf, Expr pat, Params p) { checkContextMatch(pf); checkContextMatch(pat); checkContextMatch(p); ASTVector seq = new ASTVector(this, Native.getInterpolant(nCtx(), pf.getNativeObject(), pat.getNativeObject(), p.getNativeObject())); return seq.ToBoolExprArray(); } public class ComputeInterpolantResult { public Z3_lbool status = Z3_lbool.Z3_L_UNDEF; public BoolExpr[] interp = null; public Model model = null; }; /** * Computes an interpolant. * Remarks: For more information on interpolation please refer * too the function Z3_compute_interpolant in the C/C++ API, which is * well documented. * @throws Z3Exception **/ public ComputeInterpolantResult ComputeInterpolant(Expr pat, Params p) { checkContextMatch(pat); checkContextMatch(p); ComputeInterpolantResult res = new ComputeInterpolantResult(); Native.LongPtr n_i = new Native.LongPtr(); Native.LongPtr n_m = new Native.LongPtr(); res.status = Z3_lbool.fromInt(Native.computeInterpolant(nCtx(), pat.getNativeObject(), p.getNativeObject(), n_i, n_m)); if (res.status == Z3_lbool.Z3_L_FALSE) res.interp = (new ASTVector(this, n_i.value)).ToBoolExprArray(); if (res.status == Z3_lbool.Z3_L_TRUE) res.model = new Model(this, n_m.value); return res; } /// /// Return a string summarizing cumulative time used for interpolation. /// /// Remarks: For more information on interpolation please refer /// too the function Z3_interpolation_profile in the C/C++ API, which is /// well documented. public String InterpolationProfile() { return Native.interpolationProfile(nCtx()); } public class CheckInterpolantResult { public int return_value = 0; public String error = null; } /// /// Checks the correctness of an interpolant. /// /// Remarks: For more information on interpolation please refer /// too the function Z3_check_interpolant in the C/C++ API, which is /// well documented. public CheckInterpolantResult CheckInterpolant(Expr[] cnsts, int[] parents, BoolExpr[] interps, String error, Expr[] theory) { CheckInterpolantResult res = new CheckInterpolantResult(); Native.StringPtr n_err_str = new Native.StringPtr(); res.return_value = Native.checkInterpolant(nCtx(), cnsts.length, Expr.arrayToNative(cnsts), parents, Expr.arrayToNative(interps), n_err_str, theory.length, Expr.arrayToNative(theory)); res.error = n_err_str.value; return res; } public class ReadInterpolationProblemResult { public int return_value = 0; public Expr[] cnsts; public int[] parents; public String error; public Expr[] theory; }; /// /// Reads an interpolation problem from a file. /// /// Remarks: For more information on interpolation please refer /// too the function Z3_read_interpolation_problem in the C/C++ API, which is /// well documented. public ReadInterpolationProblemResult ReadInterpolationProblem(String filename, Expr[] cnsts, int[] parents, String error, Expr[] theory) { ReadInterpolationProblemResult res = new ReadInterpolationProblemResult(); Native.IntPtr n_num = new Native.IntPtr(); Native.IntPtr n_num_theory = new Native.IntPtr(); Native.ObjArrayPtr n_cnsts = new Native.ObjArrayPtr(); Native.UIntArrayPtr n_parents = new Native.UIntArrayPtr(); Native.ObjArrayPtr n_theory = new Native.ObjArrayPtr(); Native.StringPtr n_err_str = new Native.StringPtr(); res.return_value = Native.readInterpolationProblem(nCtx(), n_num, n_cnsts, n_parents, filename, n_err_str, n_num_theory, n_theory); int num = n_num.value; int num_theory = n_num_theory.value; res.error = n_err_str.value; res.cnsts = new Expr[num]; res.parents = new int[num]; theory = new Expr[num_theory]; for (int i = 0; i < num; i++) { res.cnsts[i] = Expr.create(this, n_cnsts.value[i]); res.parents[i] = n_parents.value[i]; } for (int i = 0; i < num_theory; i++) res.theory[i] = Expr.create(this, n_theory.value[i]); return res; } /// /// Writes an interpolation problem to a file. /// /// Remarks: For more information on interpolation please refer /// too the function Z3_write_interpolation_problem in the C/C++ API, which is /// well documented. public void WriteInterpolationProblem(String filename, Expr[] cnsts, int[] parents, String error, Expr[] theory) { Native.writeInterpolationProblem(nCtx(), cnsts.length, Expr.arrayToNative(cnsts), parents, filename, theory.length, Expr.arrayToNative(theory)); } } z3-4.4.0/src/api/java/AST.java0000644000175000017500000001316612540347414015427 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: AST.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_ast_kind; /** * The abstract syntax tree (AST) class. **/ public class AST extends Z3Object { /* Overloaded operators are not translated. */ /** * Object comparison. * * @param o another AST **/ public boolean equals(Object o) { AST casted = null; try { casted = AST.class.cast(o); } catch (ClassCastException e) { return false; } return this.getNativeObject() == casted.getNativeObject(); } /** * Object Comparison. * @param other Another AST * * @return Negative if the object should be sorted before {@code other}, * positive if after else zero. * @throws Z3Exception on error **/ public int compareTo(Object other) { if (other == null) return 1; AST oAST = null; try { oAST = AST.class.cast(other); } catch (ClassCastException e) { return 1; } if (getId() < oAST.getId()) return -1; else if (getId() > oAST.getId()) return +1; else return 0; } /** * The AST's hash code. * * @return A hash code **/ public int hashCode() { int r = 0; try { Native.getAstHash(getContext().nCtx(), getNativeObject()); } catch (Z3Exception ex) {} return r; } /** * A unique identifier for the AST (unique among all ASTs). * @throws Z3Exception on error **/ public int getId() { return Native.getAstId(getContext().nCtx(), getNativeObject()); } /** * Translates (copies) the AST to the Context {@code ctx}. * @param ctx A context * * @return A copy of the AST which is associated with {@code ctx} * @throws Z3Exception on error **/ public AST translate(Context ctx) { if (getContext() == ctx) return this; else return new AST(ctx, Native.translate(getContext().nCtx(), getNativeObject(), ctx.nCtx())); } /** * The kind of the AST. * @throws Z3Exception on error **/ public Z3_ast_kind getASTKind() { return Z3_ast_kind.fromInt(Native.getAstKind(getContext().nCtx(), getNativeObject())); } /** * Indicates whether the AST is an Expr * @throws Z3Exception on error * @throws Z3Exception on error **/ public boolean isExpr() { switch (getASTKind()) { case Z3_APP_AST: case Z3_NUMERAL_AST: case Z3_QUANTIFIER_AST: case Z3_VAR_AST: return true; default: return false; } } /** * Indicates whether the AST is an application * @return a boolean * @throws Z3Exception on error **/ public boolean isApp() { return this.getASTKind() == Z3_ast_kind.Z3_APP_AST; } /** * Indicates whether the AST is a BoundVariable. * @return a boolean * @throws Z3Exception on error **/ public boolean isVar() { return this.getASTKind() == Z3_ast_kind.Z3_VAR_AST; } /** * Indicates whether the AST is a Quantifier * @return a boolean * @throws Z3Exception on error **/ public boolean isQuantifier() { return this.getASTKind() == Z3_ast_kind.Z3_QUANTIFIER_AST; } /** * Indicates whether the AST is a Sort **/ public boolean isSort() { return this.getASTKind() == Z3_ast_kind.Z3_SORT_AST; } /** * Indicates whether the AST is a FunctionDeclaration **/ public boolean isFuncDecl() { return this.getASTKind() == Z3_ast_kind.Z3_FUNC_DECL_AST; } /** * A string representation of the AST. **/ public String toString() { try { return Native.astToString(getContext().nCtx(), getNativeObject()); } catch (Z3Exception e) { return "Z3Exception: " + e.getMessage(); } } /** * A string representation of the AST in s-expression notation. **/ public String getSExpr() { return Native.astToString(getContext().nCtx(), getNativeObject()); } AST(Context ctx) { super(ctx); } AST(Context ctx, long obj) { super(ctx, obj); } void incRef(long o) { // Console.WriteLine("AST IncRef()"); if (getContext() == null || o == 0) return; getContext().getASTDRQ().incAndClear(getContext(), o); super.incRef(o); } void decRef(long o) { // Console.WriteLine("AST DecRef()"); if (getContext() == null || o == 0) return; getContext().getASTDRQ().add(o); super.decRef(o); } static AST create(Context ctx, long obj) { switch (Z3_ast_kind.fromInt(Native.getAstKind(ctx.nCtx(), obj))) { case Z3_FUNC_DECL_AST: return new FuncDecl(ctx, obj); case Z3_QUANTIFIER_AST: return new Quantifier(ctx, obj); case Z3_SORT_AST: return Sort.create(ctx, obj); case Z3_APP_AST: case Z3_NUMERAL_AST: case Z3_VAR_AST: return Expr.create(ctx, obj); default: throw new Z3Exception("Unknown AST kind"); } } } z3-4.4.0/src/api/java/Context.java0000644000175000017500000034121212540347414016420 0ustar michaelmichael/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Context.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import java.util.Map; import com.microsoft.z3.enumerations.Z3_ast_print_mode; /** * The main interaction with Z3 happens via the Context. **/ public class Context extends IDisposable { /** * Constructor. **/ public Context() { super(); m_ctx = Native.mkContextRc(0); initContext(); } /** * Constructor. * Remarks: * The following parameters can be set: * - proof (Boolean) Enable proof generation * - debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting * - trace (Boolean) Tracing support for VCC * - trace_file_name (String) Trace out file for VCC traces * - timeout (unsigned) default timeout (in milliseconds) used for solvers * - well_sorted_check type checker * - auto_config use heuristics to automatically select solver and configure it * - model model generation for solvers, this parameter can be overwritten when creating a solver * - model_validate validate models produced by solvers * - unsat_core unsat-core generation for solvers, this parameter can be overwritten when creating a solver * Note that in previous versions of Z3, this constructor was also used to set global and * module parameters. For this purpose we should now use {@code Global.setParameter} **/ public Context(Map settings) { super(); long cfg = Native.mkConfig(); for (Map.Entry kv : settings.entrySet()) Native.setParamValue(cfg, kv.getKey(), kv.getValue()); m_ctx = Native.mkContextRc(cfg); Native.delConfig(cfg); initContext(); } /** * Creates a new symbol using an integer. * Remarks: Not all integers can be passed to this function. * The legal range of unsigned integers is 0 to 2^30-1. **/ public IntSymbol mkSymbol(int i) { return new IntSymbol(this, i); } /** * Create a symbol using a string. **/ public StringSymbol mkSymbol(String name) { return new StringSymbol(this, name); } /** * Create an array of symbols. **/ Symbol[] mkSymbols(String[] names) { if (names == null) return null; Symbol[] result = new Symbol[names.length]; for (int i = 0; i < names.length; ++i) result[i] = mkSymbol(names[i]); return result; } private BoolSort m_boolSort = null; private IntSort m_intSort = null; private RealSort m_realSort = null; /** * Retrieves the Boolean sort of the context. **/ public BoolSort getBoolSort() { if (m_boolSort == null) m_boolSort = new BoolSort(this); return m_boolSort; } /** * Retrieves the Integer sort of the context. **/ public IntSort getIntSort() { if (m_intSort == null) m_intSort = new IntSort(this); return m_intSort; } /** * Retrieves the Real sort of the context. **/ public RealSort getRealSort() { if (m_realSort == null) m_realSort = new RealSort(this); return m_realSort; } /** * Create a new Boolean sort. **/ public BoolSort mkBoolSort() { return new BoolSort(this); } /** * Create a new uninterpreted sort. **/ public UninterpretedSort mkUninterpretedSort(Symbol s) { checkContextMatch(s); return new UninterpretedSort(this, s); } /** * Create a new uninterpreted sort. **/ public UninterpretedSort mkUninterpretedSort(String str) { return mkUninterpretedSort(mkSymbol(str)); } /** * Create a new integer sort. **/ public IntSort mkIntSort() { return new IntSort(this); } /** * Create a real sort. **/ public RealSort mkRealSort() { return new RealSort(this); } /** * Create a new bit-vector sort. **/ public BitVecSort mkBitVecSort(int size) { return new BitVecSort(this, Native.mkBvSort(nCtx(), size)); } /** * Create a new array sort. **/ public ArraySort mkArraySort(Sort domain, Sort range) { checkContextMatch(domain); checkContextMatch(range); return new ArraySort(this, domain, range); } /** * Create a new tuple sort. **/ public TupleSort mkTupleSort(Symbol name, Symbol[] fieldNames, Sort[] fieldSorts) { checkContextMatch(name); checkContextMatch(fieldNames); checkContextMatch(fieldSorts); return new TupleSort(this, name, (int) fieldNames.length, fieldNames, fieldSorts); } /** * Create a new enumeration sort. **/ public EnumSort mkEnumSort(Symbol name, Symbol... enumNames) { checkContextMatch(name); checkContextMatch(enumNames); return new EnumSort(this, name, enumNames); } /** * Create a new enumeration sort. **/ public EnumSort mkEnumSort(String name, String... enumNames) { return new EnumSort(this, mkSymbol(name), mkSymbols(enumNames)); } /** * Create a new list sort. **/ public ListSort mkListSort(Symbol name, Sort elemSort) { checkContextMatch(name); checkContextMatch(elemSort); return new ListSort(this, name, elemSort); } /** * Create a new list sort. **/ public ListSort mkListSort(String name, Sort elemSort) { checkContextMatch(elemSort); return new ListSort(this, mkSymbol(name), elemSort); } /** * Create a new finite domain sort. **/ public FiniteDomainSort mkFiniteDomainSort(Symbol name, long size) { checkContextMatch(name); return new FiniteDomainSort(this, name, size); } /** * Create a new finite domain sort. **/ public FiniteDomainSort mkFiniteDomainSort(String name, long size) { return new FiniteDomainSort(this, mkSymbol(name), size); } /** * Create a datatype constructor. * @param name constructor name * @param recognizer name of recognizer function. * @param fieldNames names of the constructor fields. * @param sorts field sorts, 0 if the field sort refers to a recursive sort. * @param sortRefs reference to datatype sort that is an argument to the * constructor; if the corresponding sort reference is 0, then the value in sort_refs should be * an index referring to one of the recursive datatypes that is * declared. **/ public Constructor mkConstructor(Symbol name, Symbol recognizer, Symbol[] fieldNames, Sort[] sorts, int[] sortRefs) { return new Constructor(this, name, recognizer, fieldNames, sorts, sortRefs); } /** * Create a datatype constructor. * @param name * @param recognizer * @param fieldNames * @param sorts * @param sortRefs * * @return **/ public Constructor mkConstructor(String name, String recognizer, String[] fieldNames, Sort[] sorts, int[] sortRefs) { return new Constructor(this, mkSymbol(name), mkSymbol(recognizer), mkSymbols(fieldNames), sorts, sortRefs); } /** * Create a new datatype sort. **/ public DatatypeSort mkDatatypeSort(Symbol name, Constructor[] constructors) { checkContextMatch(name); checkContextMatch(constructors); return new DatatypeSort(this, name, constructors); } /** * Create a new datatype sort. **/ public DatatypeSort mkDatatypeSort(String name, Constructor[] constructors) { checkContextMatch(constructors); return new DatatypeSort(this, mkSymbol(name), constructors); } /** * Create mutually recursive datatypes. * @param names names of datatype sorts * @param c list of constructors, one list per sort. **/ public DatatypeSort[] mkDatatypeSorts(Symbol[] names, Constructor[][] c) { checkContextMatch(names); int n = (int) names.length; ConstructorList[] cla = new ConstructorList[n]; long[] n_constr = new long[n]; for (int i = 0; i < n; i++) { Constructor[] constructor = c[i]; checkContextMatch(constructor); cla[i] = new ConstructorList(this, constructor); n_constr[i] = cla[i].getNativeObject(); } long[] n_res = new long[n]; Native.mkDatatypes(nCtx(), n, Symbol.arrayToNative(names), n_res, n_constr); DatatypeSort[] res = new DatatypeSort[n]; for (int i = 0; i < n; i++) res[i] = new DatatypeSort(this, n_res[i]); return res; } /** * Create mutually recursive data-types. * @param names * @param c * * @return **/ public DatatypeSort[] mkDatatypeSorts(String[] names, Constructor[][] c) { return mkDatatypeSorts(mkSymbols(names), c); } /** * Update a datatype field at expression t with value v. * The function performs a record update at t. The field * that is passed in as argument is updated with value v, * the remainig fields of t are unchanged. **/ public Expr MkUpdateField(FuncDecl field, Expr t, Expr v) throws Z3Exception { return Expr.create (this, Native.datatypeUpdateField (nCtx(), field.getNativeObject(), t.getNativeObject(), v.getNativeObject())); } /** * Creates a new function declaration. **/ public FuncDecl mkFuncDecl(Symbol name, Sort[] domain, Sort range) { checkContextMatch(name); checkContextMatch(domain); checkContextMatch(range); return new FuncDecl(this, name, domain, range); } /** * Creates a new function declaration. **/ public FuncDecl mkFuncDecl(Symbol name, Sort domain, Sort range) { checkContextMatch(name); checkContextMatch(domain); checkContextMatch(range); Sort[] q = new Sort[] { domain }; return new FuncDecl(this, name, q, range); } /** * Creates a new function declaration. **/ public FuncDecl mkFuncDecl(String name, Sort[] domain, Sort range) { checkContextMatch(domain); checkContextMatch(range); return new FuncDecl(this, mkSymbol(name), domain, range); } /** * Creates a new function declaration. **/ public FuncDecl mkFuncDecl(String name, Sort domain, Sort range) { checkContextMatch(domain); checkContextMatch(range); Sort[] q = new Sort[] { domain }; return new FuncDecl(this, mkSymbol(name), q, range); } /** * Creates a fresh function declaration with a name prefixed with * {@code prefix}. * @see mkFuncDecl(String,Sort,Sort) * @see mkFuncDecl(String,Sort[],Sort) **/ public FuncDecl mkFreshFuncDecl(String prefix, Sort[] domain, Sort range) { checkContextMatch(domain); checkContextMatch(range); return new FuncDecl(this, prefix, domain, range); } /** * Creates a new constant function declaration. **/ public FuncDecl mkConstDecl(Symbol name, Sort range) { checkContextMatch(name); checkContextMatch(range); return new FuncDecl(this, name, null, range); } /** * Creates a new constant function declaration. **/ public FuncDecl mkConstDecl(String name, Sort range) { checkContextMatch(range); return new FuncDecl(this, mkSymbol(name), null, range); } /** * Creates a fresh constant function declaration with a name prefixed with * {@code prefix"}. * @see mkFuncDecl(String,Sort,Sort) * @see mkFuncDecl(String,Sort[],Sort) **/ public FuncDecl mkFreshConstDecl(String prefix, Sort range) { checkContextMatch(range); return new FuncDecl(this, prefix, null, range); } /** * Creates a new bound variable. * @param index The de-Bruijn index of the variable * @param ty The sort of the variable **/ public Expr mkBound(int index, Sort ty) { return Expr.create(this, Native.mkBound(nCtx(), index, ty.getNativeObject())); } /** * Create a quantifier pattern. **/ public Pattern mkPattern(Expr... terms) { if (terms.length == 0) throw new Z3Exception("Cannot create a pattern from zero terms"); long[] termsNative = AST.arrayToNative(terms); return new Pattern(this, Native.mkPattern(nCtx(), (int) terms.length, termsNative)); } /** * Creates a new Constant of sort {@code range} and named * {@code name}. **/ public Expr mkConst(Symbol name, Sort range) { checkContextMatch(name); checkContextMatch(range); return Expr.create( this, Native.mkConst(nCtx(), name.getNativeObject(), range.getNativeObject())); } /** * Creates a new Constant of sort {@code range} and named * {@code name}. **/ public Expr mkConst(String name, Sort range) { return mkConst(mkSymbol(name), range); } /** * Creates a fresh Constant of sort {@code range} and a name * prefixed with {@code prefix}. **/ public Expr mkFreshConst(String prefix, Sort range) { checkContextMatch(range); return Expr.create(this, Native.mkFreshConst(nCtx(), prefix, range.getNativeObject())); } /** * Creates a fresh constant from the FuncDecl {@code f}. * @param f A decl of a 0-arity function **/ public Expr mkConst(FuncDecl f) { return mkApp(f, (Expr[]) null); } /** * Create a Boolean constant. **/ public BoolExpr mkBoolConst(Symbol name) { return (BoolExpr) mkConst(name, getBoolSort()); } /** * Create a Boolean constant. **/ public BoolExpr mkBoolConst(String name) { return (BoolExpr) mkConst(mkSymbol(name), getBoolSort()); } /** * Creates an integer constant. **/ public IntExpr mkIntConst(Symbol name) { return (IntExpr) mkConst(name, getIntSort()); } /** * Creates an integer constant. **/ public IntExpr mkIntConst(String name) { return (IntExpr) mkConst(name, getIntSort()); } /** * Creates a real constant. **/ public RealExpr mkRealConst(Symbol name) { return (RealExpr) mkConst(name, getRealSort()); } /** * Creates a real constant. **/ public RealExpr mkRealConst(String name) { return (RealExpr) mkConst(name, getRealSort()); } /** * Creates a bit-vector constant. **/ public BitVecExpr mkBVConst(Symbol name, int size) { return (BitVecExpr) mkConst(name, mkBitVecSort(size)); } /** * Creates a bit-vector constant. **/ public BitVecExpr mkBVConst(String name, int size) { return (BitVecExpr) mkConst(name, mkBitVecSort(size)); } /** * Create a new function application. **/ public Expr mkApp(FuncDecl f, Expr... args) { checkContextMatch(f); checkContextMatch(args); return Expr.create(this, f, args); } /** * The true Term. **/ public BoolExpr mkTrue() { return new BoolExpr(this, Native.mkTrue(nCtx())); } /** * The false Term. **/ public BoolExpr mkFalse() { return new BoolExpr(this, Native.mkFalse(nCtx())); } /** * Creates a Boolean value. **/ public BoolExpr mkBool(boolean value) { return value ? mkTrue() : mkFalse(); } /** * Creates the equality {@code x"/> = is the weight, to every subgoal produced by to every subgoal produced by evaluates to true and